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

Teamwhitelist based on claims #523

Closed
wants to merge 18 commits into from
33 changes: 18 additions & 15 deletions handlers/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,26 +139,29 @@ func verifyUser(u interface{}) (bool, error) {
return true, nil

// WhiteList
case len(cfg.Cfg.WhiteList) != 0:
for _, wl := range cfg.Cfg.WhiteList {
if user.Username == wl {
log.Debugf("verifyUser: Success! found user.Username in WhiteList: %s", user.Username)
return true, nil
case len(cfg.Cfg.WhiteList) != 0 || len(cfg.Cfg.TeamWhiteList) != 0:
if len(cfg.Cfg.WhiteList) != 0 {
for _, wl := range cfg.Cfg.WhiteList {
if user.Username == wl {
log.Debugf("verifyUser: Success! found user.Username in WhiteList: %s", user.Username)
return true, nil
}
}
}
return false, fmt.Errorf("verifyUser: user.Username not found in WhiteList: %s", user.Username)

// TeamWhiteList
case len(cfg.Cfg.TeamWhiteList) != 0:
for _, team := range user.TeamMemberships {
for _, wl := range cfg.Cfg.TeamWhiteList {
if team == wl {
log.Debugf("verifyUser: Success! found user.TeamWhiteList in TeamWhiteList: %s for user %s", wl, user.Username)
return true, nil
log.Debug("User not in userwhitelist, checking in teamWhiteList")
// TeamWhiteList - first match between user.teammembership and teamwhitelist in config authenticates the user
if len(cfg.Cfg.TeamWhiteList) != 0 {
for _, team := range user.TeamMemberships {
for _, wl := range cfg.Cfg.TeamWhiteList {
if team == wl {
log.Debugf("verifyUser: Success! found user.TeamWhiteList in TeamWhiteList: %s for user %s", wl, user.Username)
return true, nil
}
}
}
log.Warnf("verifyUser: user.TeamMemberships %s not found in TeamWhiteList: %s for user %s", user.TeamMemberships, cfg.Cfg.TeamWhiteList, user.Username)
}
return false, fmt.Errorf("verifyUser: user.TeamMemberships %s not found in TeamWhiteList: %s for user %s", user.TeamMemberships, cfg.Cfg.TeamWhiteList, user.Username)
return false, fmt.Errorf("verifyUser: user.Username not found in WhiteList or TeamWhiteList: %s", user.Username)

// Domains
case len(cfg.Cfg.Domains) != 0:
Expand Down
31 changes: 16 additions & 15 deletions pkg/cfg/cfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,22 @@ import (
// though most of the time envconfig will use the struct key's name: VOUCH_PORT VOUCH_JWT_MAXAGE
// default values should be set in .defaults.yml
type Config struct {
LogLevel string `mapstructure:"logLevel"`
Listen string `mapstructure:"listen"`
Port int `mapstructure:"port"`
SocketMode int `mapstructure:"socket_mode"`
SocketGroup string `mapstructure:"socket_group"`
DocumentRoot string `mapstructure:"document_root" envconfig:"document_root"`
WriteTimeout int `mapstructure:"writeTimeout"`
ReadTimeout int `mapstructure:"readTimeout"`
IdleTimeout int `mapstructure:"idleTimeout"`
Domains []string `mapstructure:"domains"`
WhiteList []string `mapstructure:"whitelist"`
TeamWhiteList []string `mapstructure:"teamWhitelist"`
AllowAllUsers bool `mapstructure:"allowAllUsers"`
PublicAccess bool `mapstructure:"publicAccess"`
TLS struct {
LogLevel string `mapstructure:"logLevel"`
Listen string `mapstructure:"listen"`
Port int `mapstructure:"port"`
SocketMode int `mapstructure:"socket_mode"`
SocketGroup string `mapstructure:"socket_group"`
DocumentRoot string `mapstructure:"document_root" envconfig:"document_root"`
WriteTimeout int `mapstructure:"writeTimeout"`
ReadTimeout int `mapstructure:"readTimeout"`
IdleTimeout int `mapstructure:"idleTimeout"`
Domains []string `mapstructure:"domains"`
WhiteList []string `mapstructure:"whitelist"`
TeamWhiteList []string `mapstructure:"teamWhitelist"`
TeamWhiteListClaim string `mapstructure:"teamWhitelistclaim"` // claim in UserInfo body that is used for TeamWhitelisting. If the value for TeamWhiteListClaim matches TeamWhiteList, auth is successful.
AllowAllUsers bool `mapstructure:"allowAllUsers"`
PublicAccess bool `mapstructure:"publicAccess"`
TLS struct {
Cert string `mapstructure:"cert"`
Key string `mapstructure:"key"`
Profile string `mapstructure:"profile"`
Expand Down
43 changes: 39 additions & 4 deletions pkg/providers/openid/openid.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,13 @@ package openid

import (
"encoding/json"
"golang.org/x/oauth2"
"io/ioutil"
"net/http"

"github.com/vouch/vouch-proxy/pkg/cfg"
"github.com/vouch/vouch-proxy/pkg/providers/common"
"github.com/vouch/vouch-proxy/pkg/structs"
"go.uber.org/zap"
"golang.org/x/oauth2"
"io/ioutil"
"net/http"
)

// Provider provider specific functions
Expand All @@ -39,6 +38,7 @@ func (Provider) GetUserInfo(r *http.Request, user *structs.User, customClaims *s
return err
}
userinfo, err := client.Get(cfg.GenOAuth.UserInfoURL)

if err != nil {
return err
}
Expand All @@ -57,6 +57,41 @@ func (Provider) GetUserInfo(r *http.Request, user *structs.User, customClaims *s
log.Error(err)
return err
}
if err = appendTeamMembershipsFromCustomClaim(data, user); err != nil {
log.Error(err)
return err
}

user.PrepareUserData()
return nil
}

// appendTeamMembershipsFromCustomClaim appends teammembership values in user. If
// any TeamWhiteListClaim is mentioned in the config file, userinfo body is
// checked for the claim and the claim values are appended to user
// teammemberships. Later, this user data is used for teamwhitelist check in auth.
func appendTeamMembershipsFromCustomClaim(data []byte, user *structs.User) error {
var f interface{}
err := json.Unmarshal(data, &f)
if err != nil {
log.Error(err)
return err
}
if cfg.Cfg.TeamWhiteListClaim != "" {
m := f.(map[string]interface{})
for k := range m {
if k == cfg.Cfg.TeamWhiteListClaim {
claimval, ok := m[k].(string)
if !ok {
log.Error("TeamWhiteList claim sent in openID user body cannot be casted as string")
continue // continue auth with existing teammemberships for user
}

user.TeamMemberships = append(user.TeamMemberships, claimval)
break
}
}
log.Debugf("team memberships present in user: %+v", user.TeamMemberships)
}
return nil
}
52 changes: 52 additions & 0 deletions pkg/providers/openid/openid_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package openid

import (
"github.com/stretchr/testify/assert"
"testing"

"github.com/vouch/vouch-proxy/pkg/cfg"
"github.com/vouch/vouch-proxy/pkg/structs"
)

func TestGetUserInfo(t *testing.T) {
setUp()

user := structs.User{
Username: "test",
CreatedOn: 123,
Email: "[email protected]",
ID: 1,
LastUpdate: 123,
Name: "name",
TeamMemberships: []string{"team1"},
}

// test1
userinfobody := "{\"sub\":\"xx\",\"email\":\"[email protected]\",\"email_address\":\"[email protected]\",\"full_name\":\"ABC DEF\",\"last_name\":\"ABC\",\"CustomClaim1\":\"team2\"}"
data := []byte(userinfobody)
err := appendTeamMembershipsFromCustomClaim(data, &user)
assert.ElementsMatchf(t, err, nil, "Expected error to be nil")
assert.ElementsMatchf(t, user.TeamMemberships, []string{"team1", "team2"}, "Expected team memberships to be appended")

//test2
user.TeamMemberships = nil
userinfobody = "{\"sub\":\"xx\",\"email\":\"[email protected]\",\"email_address\":\"[email protected]\",\"full_name\":\"ABC DEF\",\"last_name\":\"ABC\",\"CustomClaim1\":\"team2\"}"
data = []byte(userinfobody)
err = appendTeamMembershipsFromCustomClaim(data, &user)
assert.ElementsMatchf(t, err, nil, "Expected error to be nil")
assert.ElementsMatchf(t, user.TeamMemberships, []string{"team2"}, "Expected team memberships to be appended")

//test3
user.TeamMemberships = nil
userinfobody = "{\"sub\":\"xx\",\"email\":\"[email protected]\",\"email_address\":\"[email protected]\",\"full_name\":\"ABC DEF\",\"last_name\":\"ABC\",\"CustomClaim1\":[\"team2\",\"team3\"]}"
data = []byte(userinfobody)
err = appendTeamMembershipsFromCustomClaim(data, &user)
assert.ElementsMatchf(t, err, nil, "Expected error to be nil")
assert.ElementsMatchf(t, user.TeamMemberships, nil, "Expected team memberships to be empty due to casting error")

}

func setUp() {
log = cfg.Logging.Logger
cfg.Cfg.TeamWhiteListClaim = "CustomClaim1"
}