diff --git a/remote/github/github.go b/remote/github/github.go index 561388b1..d881aa3b 100644 --- a/remote/github/github.go +++ b/remote/github/github.go @@ -26,8 +26,6 @@ type GitHub struct { Secret string PrivateMode bool SkipVerify bool - Orgs []string - Open bool cache *lru.Cache } @@ -40,8 +38,6 @@ func New(service *settings.Service) *GitHub { Secret: service.OAuth.Secret, PrivateMode: service.PrivateMode, SkipVerify: service.SkipVerify, - Orgs: service.Orgs, - Open: service.Open, } var err error github.cache, err = lru.New(1028) @@ -81,6 +77,20 @@ func (g *GitHub) Login(token, secret string) (*common.User, error) { return &user, nil } +// Orgs fetches the organizations for the given user. +func (g *GitHub) Orgs(u *common.User) ([]string, error) { + client := NewClient(g.API, u.Token, g.SkipVerify) + orgs_ := []string{} + orgs, err := GetOrgs(client) + if err != nil { + return orgs_, err + } + for _, org := range orgs { + orgs_ = append(orgs_, *org.Login) + } + return orgs_, nil +} + // Repo fetches the named repository from the remote system. func (g *GitHub) Repo(u *common.User, owner, name string) (*common.Repo, error) { client := NewClient(g.API, u.Token, g.SkipVerify) diff --git a/remote/remote.go b/remote/remote.go index c43a6628..64e5609f 100644 --- a/remote/remote.go +++ b/remote/remote.go @@ -11,6 +11,13 @@ type Remote interface { // remote user details. Login(token, secret string) (*common.User, error) + // Orgs fetches the organizations for the given user. + // + // TODO(bradrydzewski) consider consolidating this to return + // the list of organizations along with + // the user Login info. + Orgs(u *common.User) ([]string, error) + // Repo fetches the named repository from the remote system. Repo(u *common.User, owner, repo string) (*common.Repo, error) diff --git a/server/login.go b/server/login.go index eb00f09f..d804092f 100644 --- a/server/login.go +++ b/server/login.go @@ -23,6 +23,7 @@ import ( func GetLogin(c *gin.Context) { settings := ToSettings(c) session := ToSession(c) + remote := ToRemote(c) store := ToDatastore(c) // when dealing with redirects we may need @@ -49,8 +50,18 @@ func GetLogin(c *gin.Context) { return } - // get the user from the database login := ToUser(c) + + // check organization membership, if applicable + if len(settings.Service.Orgs) != 0 { + orgs, _ := remote.Orgs(login) + if !checkMembership(orgs, settings.Service.Orgs) { + c.Redirect(303, "/login#error=access_denied_org") + return + } + } + + // get the user from the database u, err := store.User(login.Login) if err != nil { count, err := store.UserCount() @@ -193,3 +204,19 @@ func getLoginBasic(c *gin.Context) { // add the user to the request c.Set("user", user) } + +// checkMembership is a helper function that compares the user's +// organization list to a whitelist of organizations that are +// approved to use the system. +func checkMembership(orgs, whitelist []string) bool { + orgs_ := make(map[string]struct{}, len(orgs)) + for _, org := range orgs { + orgs_[org] = struct{}{} + } + for _, org := range whitelist { + if _, ok := orgs_[org]; ok { + return true + } + } + return false +}