Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add GitLab provider #515

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
40 changes: 40 additions & 0 deletions config/config.yml_example_gitlab
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@

# 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
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
3 changes: 3 additions & 0 deletions handlers/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,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"
Expand Down Expand Up @@ -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")
Expand Down
24 changes: 23 additions & 1 deletion pkg/cfg/oauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ var (
OpenStax: "openstax",
Nextcloud: "nextcloud",
Alibaba: "alibaba",
GitLab: "gitlab",
}
)

Expand All @@ -59,6 +60,7 @@ type OAuthProviders struct {
OpenStax string
Nextcloud string
Alibaba string
GitLab string
}

// oauth config items endoint for access
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -270,6 +276,22 @@ 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 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{
Expand Down
46 changes: 46 additions & 0 deletions pkg/providers/gitlab/gitlab.go
Original file line number Diff line number Diff line change
@@ -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
}
33 changes: 31 additions & 2 deletions pkg/structs/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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"`
Expand Down Expand Up @@ -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"`
Expand Down