From 7703b5bdaa527455bb8634949baa9060291585f7 Mon Sep 17 00:00:00 2001 From: Christian Bjartli Date: Sun, 18 Dec 2022 22:21:25 +0100 Subject: [PATCH 1/7] add GitLabUser struct --- pkg/structs/structs.go | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/pkg/structs/structs.go b/pkg/structs/structs.go index bccc0180..b96dacd0 100644 --- a/pkg/structs/structs.go +++ b/pkg/structs/structs.go @@ -10,7 +10,9 @@ OR CONDITIONS OF ANY KIND, either express or implied. package structs -import "strconv" +import ( + "strconv" +) // CustomClaims Temporary struct storing custom claims until JWT creation. type CustomClaims struct { @@ -148,7 +150,7 @@ type Contact struct { Verified bool `json:"is_verified"` } -//OpenStaxUser is a retrieved and authenticated user from OpenStax Accounts +// OpenStaxUser is a retrieved and authenticated user from OpenStax Accounts type OpenStaxUser struct { User Contacts []Contact `json:"contact_infos"` @@ -217,6 +219,33 @@ type AliData struct { OuName string `json:"ou_name"` } +// GitLabUser is a user provided by GitLab +// https://docs.gitlab.com/ee/integration/openid_connect_provider.html +type GitLabUser struct { + User + Sub string `json:"sub"` + AuthTime int `json:"auth_time"` + Name string `json:"name"` + Nickname string `json:"nickname"` + Email string `json:"email"` + EmailVerified bool `json:"email_verified"` + Website string `json:"website"` + Profile string `json:"profile"` + Picture string `json:"picture"` + Groups []string `json:"groups"` + GroupsDirect []string `json:"groups_direct"` + OwnerOfGroups []string `json:"https://gitlab.org/claims/groups/owner"` + MaintainerOfGroups []string `json:"https://gitlab.org/claims/groups/maintainer"` + DeveloperOfGroups []string `json:"https://gitlab.org/claims/groups/developer"` +} + +func (g *GitLabUser) PrepareUserData() { + g.User.Name = g.Name + g.User.Username = g.Nickname + g.User.Email = g.Email + g.User.TeamMemberships = g.Groups +} + // Team has members and provides acess to sites type Team struct { Name string `json:"name" mapstructure:"name"` From 95202a3b324320e9bc0a0ce5790d7a93b0e71167 Mon Sep 17 00:00:00 2001 From: Christian Bjartli Date: Sun, 18 Dec 2022 22:27:48 +0100 Subject: [PATCH 2/7] add GitLab provider --- handlers/handlers.go | 3 +++ pkg/cfg/oauth.go | 30 +++++++++++++++++++++- pkg/providers/gitlab/gitlab.go | 46 ++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 pkg/providers/gitlab/gitlab.go diff --git a/handlers/handlers.go b/handlers/handlers.go index 6dae94e3..eabed070 100644 --- a/handlers/handlers.go +++ b/handlers/handlers.go @@ -11,6 +11,7 @@ OR CONDITIONS OF ANY KIND, either express or implied. package handlers import ( + "github.com/vouch/vouch-proxy/pkg/providers/gitlab" "net/http" "github.com/gorilla/sessions" @@ -88,6 +89,8 @@ func getProvider() Provider { return openid.Provider{} case cfg.Providers.Alibaba: return alibaba.Provider{} + case cfg.Providers.GitLab: + return gitlab.Provider{} default: // shouldn't ever reach this since cfg checks for a properly configure `oauth.provider` log.Fatal("oauth.provider appears to be misconfigured, please check your config") diff --git a/pkg/cfg/oauth.go b/pkg/cfg/oauth.go index 56442d64..1642ba87 100644 --- a/pkg/cfg/oauth.go +++ b/pkg/cfg/oauth.go @@ -44,6 +44,7 @@ var ( OpenStax: "openstax", Nextcloud: "nextcloud", Alibaba: "alibaba", + GitLab: "gitlab", } ) @@ -59,6 +60,7 @@ type OAuthProviders struct { OpenStax string Nextcloud string Alibaba string + GitLab string } // oauth config items endoint for access @@ -122,7 +124,8 @@ func oauthBasicTest() error { GenOAuth.Provider != Providers.OIDC && GenOAuth.Provider != Providers.OpenStax && GenOAuth.Provider != Providers.Nextcloud && - GenOAuth.Provider != Providers.Alibaba { + GenOAuth.Provider != Providers.Alibaba && + GenOAuth.Provider != Providers.GitLab { return errors.New("configuration error: Unknown oauth provider: " + GenOAuth.Provider) } // OAuthconfig Checks @@ -188,6 +191,9 @@ func setProviderDefaults() { } else if GenOAuth.Provider == Providers.IndieAuth { GenOAuth.CodeChallengeMethod = "S256" configureOAuthClient() + } else if GenOAuth.Provider == Providers.GitLab { + setDefaultsGitLab() + configureOAuthClient() } else { // OIDC, OpenStax, Nextcloud configureOAuthClient() @@ -270,6 +276,28 @@ func setDefaultsGitHub() { GenOAuth.CodeChallengeMethod = "S256" } +func setDefaultsGitLab() { + if GenOAuth.AuthURL == "" { + GenOAuth.AuthURL = "https://gitlab.com/oauth/authorize" + } + if GenOAuth.TokenURL == "" { + GenOAuth.TokenURL = "https://gitlab.com/oauth/token" + } + if GenOAuth.UserInfoURL == "" { + GenOAuth.UserInfoURL = "https://gitlab.com/oauth/userinfo" + } + if GenOAuth.UserTeamURL == "" { + GenOAuth.UserTeamURL = "https://api.github.com/orgs/:org_id/teams/:team_slug/memberships/:username?access_token=" + } + if GenOAuth.UserOrgURL == "" { + GenOAuth.UserOrgURL = "https://api.github.com/orgs/:org_id/members/:username?access_token=" + } + if len(GenOAuth.Scopes) == 0 { + GenOAuth.Scopes = []string{"openid"} + } + GenOAuth.CodeChallengeMethod = "S256" +} + func configureOAuthClient() { log.Infof("configuring %s OAuth with Endpoint %s", GenOAuth.Provider, GenOAuth.AuthURL) OAuthClient = &oauth2.Config{ diff --git a/pkg/providers/gitlab/gitlab.go b/pkg/providers/gitlab/gitlab.go new file mode 100644 index 00000000..c5c48fc9 --- /dev/null +++ b/pkg/providers/gitlab/gitlab.go @@ -0,0 +1,46 @@ +package gitlab + +import ( + "encoding/json" + "github.com/vouch/vouch-proxy/pkg/cfg" + "github.com/vouch/vouch-proxy/pkg/providers/common" + "github.com/vouch/vouch-proxy/pkg/structs" + "golang.org/x/oauth2" + "io" + "net/http" +) + +type Provider struct{} + +func (Provider) Configure() { +} + +func (Provider) GetUserInfo(r *http.Request, user *structs.User, customClaims *structs.CustomClaims, ptokens *structs.PTokens, opts ...oauth2.AuthCodeOption) (rerr error) { + client, _, err := common.PrepareTokensAndClient(r, ptokens, true, opts...) + if err != nil { + return err + } + userinfo, err := client.Get(cfg.GenOAuth.UserInfoURL) + if err != nil { + return err + } + defer func() { + if err := userinfo.Body.Close(); err != nil { + rerr = err + } + }() + data, _ := io.ReadAll(userinfo.Body) + cfg.Logging.Logger.Infof("GitLab userinfo body: %s", string(data)) + if err = common.MapClaims(data, customClaims); err != nil { + cfg.Logging.Logger.Error(err) + return err + } + var glUser structs.GitLabUser + if err = json.Unmarshal(data, &glUser); err != nil { + cfg.Logging.Logger.Error(err) + return err + } + glUser.PrepareUserData() + *user = glUser.User + return nil +} From 5ffac3ad0f9c5adbd70e340053146340de6725e0 Mon Sep 17 00:00:00 2001 From: Christian Bjartli Date: Sun, 18 Dec 2022 22:41:14 +0100 Subject: [PATCH 3/7] add example config for GitLab --- config/config.yml_example_gitlab | 34 ++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 config/config.yml_example_gitlab diff --git a/config/config.yml_example_gitlab b/config/config.yml_example_gitlab new file mode 100644 index 00000000..be81301a --- /dev/null +++ b/config/config.yml_example_gitlab @@ -0,0 +1,34 @@ + +# Vouch Proxy configuration +# bare minimum to get Vouch Proxy running with gitlab + +vouch: + # domains: + # valid domains that the jwt cookies can be set into + # the callback_urls will be to these domains + # for github that's only one domain since they only allow one callback URL + # https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/#redirect-urls + # each of these domains must serve the url https://login.$domains[0] https://login.$domains[1] ... + domains: + - yourothersite.io + + # set allowAllUsers: true to use Vouch Proxy to just accept anyone who can authenticate at GitHub + # allowAllUsers: true + + cookie: + # allow the jwt/cookie to be set into http://yourdomain.com (defaults to true, requiring https://yourdomain.com) + secure: false + # vouch.cookie.domain must be set when enabling allowAllUsers + # domain: yourdomain.com + + # set teamWhitelist: to list of GitLab group/repositories + # The user is authorized if they have access to the given group or repository. + # teamWhitelist: + # - myGroup + # - myGroup/myRepository + +oauth: + # remember to create a new OAuth application in GitLab + provider: gitlab + client_id: xxxxxxxxxxxxxxxxxxxx + client_secret: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx From 6597440b4d0a5e8286dad63b41824ae06952ad7c Mon Sep 17 00:00:00 2001 From: Christian Bjartli Date: Sun, 18 Dec 2022 22:46:24 +0100 Subject: [PATCH 4/7] add desc. of changes to Unreleased section of CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 629c72fa..64a09453 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## Unreleased -Coming soon! Please document any work in progress here as part of your PR. It will be moved to the next tag when released. +- Add provider for GitLab, and support for using `teamWhiteList` to restrict access based on GitLab group/repository access. ## v0.37.0 From 3a385a5517971747992146a0af66ffe33f678f62 Mon Sep 17 00:00:00 2001 From: Christian Bjartli Date: Sun, 18 Dec 2022 22:59:56 +0100 Subject: [PATCH 5/7] remove (fix) erroneous and extraneous urls in cfg.setDefaultsGitLab --- pkg/cfg/oauth.go | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pkg/cfg/oauth.go b/pkg/cfg/oauth.go index 1642ba87..fd5580a3 100644 --- a/pkg/cfg/oauth.go +++ b/pkg/cfg/oauth.go @@ -286,12 +286,6 @@ func setDefaultsGitLab() { if GenOAuth.UserInfoURL == "" { GenOAuth.UserInfoURL = "https://gitlab.com/oauth/userinfo" } - if GenOAuth.UserTeamURL == "" { - GenOAuth.UserTeamURL = "https://api.github.com/orgs/:org_id/teams/:team_slug/memberships/:username?access_token=" - } - if GenOAuth.UserOrgURL == "" { - GenOAuth.UserOrgURL = "https://api.github.com/orgs/:org_id/members/:username?access_token=" - } if len(GenOAuth.Scopes) == 0 { GenOAuth.Scopes = []string{"openid"} } From d6bf9ffad23751d3f405efdacd07e13180fe7115 Mon Sep 17 00:00:00 2001 From: Christian Bjartli Date: Tue, 20 Dec 2022 10:46:19 +0100 Subject: [PATCH 6/7] fix unconventional import order in handlers.go --- handlers/handlers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/handlers/handlers.go b/handlers/handlers.go index eabed070..297a6936 100644 --- a/handlers/handlers.go +++ b/handlers/handlers.go @@ -11,7 +11,6 @@ OR CONDITIONS OF ANY KIND, either express or implied. package handlers import ( - "github.com/vouch/vouch-proxy/pkg/providers/gitlab" "net/http" "github.com/gorilla/sessions" @@ -25,6 +24,7 @@ import ( "github.com/vouch/vouch-proxy/pkg/providers/azure" "github.com/vouch/vouch-proxy/pkg/providers/common" "github.com/vouch/vouch-proxy/pkg/providers/github" + "github.com/vouch/vouch-proxy/pkg/providers/gitlab" "github.com/vouch/vouch-proxy/pkg/providers/google" "github.com/vouch/vouch-proxy/pkg/providers/homeassistant" "github.com/vouch/vouch-proxy/pkg/providers/indieauth" From 9f80f3d13d2601728b5a00d6b1858897a0dfebe0 Mon Sep 17 00:00:00 2001 From: Christian Bjartli Date: Tue, 20 Dec 2022 11:21:06 +0100 Subject: [PATCH 7/7] modify example config for GitLab with examples for self-hosted instances --- config/config.yml_example_gitlab | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/config/config.yml_example_gitlab b/config/config.yml_example_gitlab index be81301a..0f1dfa1d 100644 --- a/config/config.yml_example_gitlab +++ b/config/config.yml_example_gitlab @@ -32,3 +32,9 @@ oauth: provider: gitlab client_id: xxxxxxxxxxxxxxxxxxxx client_secret: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + callback_url: http://vouch.yourdomain.com:9090/auth + + # if running a self-hosted instance of GitLab, set the following urls to point to the correct domain + # auth_url: https://{yourGitLabDomain}/oauth/authorize + # token_url: https://{yourGitLabDomain}/oauth/token + # user_info_url: https://{yourGitLabDomain}/oauth/userinfo