diff --git a/.defaults.yml b/.defaults.yml index 5355712d..17f2cd43 100644 --- a/.defaults.yml +++ b/.defaults.yml @@ -34,6 +34,7 @@ vouch: # key: headers: + sub: X-Vouch-Sub jwt: X-Vouch-Token user: X-Vouch-User success: X-Vouch-Success diff --git a/handlers/handlers_test.go b/handlers/handlers_test.go index 6a968940..e3a7f126 100644 --- a/handlers/handlers_test.go +++ b/handlers/handlers_test.go @@ -46,7 +46,12 @@ func setUp(configFile string) { func TestVerifyUserPositiveUserInWhiteList(t *testing.T) { setUp("/config/testing/handler_whitelist.yml") - user := &structs.User{Username: "test@example.com", Email: "test@example.com", Name: "Test Name"} + user := &structs.User{ + Sub: "testsub", + Username: "test@example.com", + Email: "test@example.com", + Name: "Test Name", + } ok, err := verifyUser(*user) assert.True(t, ok) assert.Nil(t, err) @@ -55,7 +60,12 @@ func TestVerifyUserPositiveUserInWhiteList(t *testing.T) { func TestVerifyUserPositiveAllowAllUsers(t *testing.T) { setUp("/config/testing/handler_allowallusers.yml") - user := &structs.User{Username: "testuser", Email: "test@example.com", Name: "Test Name"} + user := &structs.User{ + Sub: "testsub", + Username: "testuser", + Email: "test@example.com", + Name: "Test Name", + } ok, err := verifyUser(*user) assert.True(t, ok) @@ -64,7 +74,12 @@ func TestVerifyUserPositiveAllowAllUsers(t *testing.T) { func TestVerifyUserPositiveByEmail(t *testing.T) { setUp("/config/testing/handler_email.yml") - user := &structs.User{Username: "testuser", Email: "test@example.com", Name: "Test Name"} + user := &structs.User{ + Sub: "testsub", + Username: "testuser", + Email: "test@example.com", + Name: "Test Name", + } ok, err := verifyUser(*user) assert.True(t, ok) assert.Nil(t, err) @@ -74,7 +89,12 @@ func TestVerifyUserPositiveByTeam(t *testing.T) { setUp("/config/testing/handler_teams.yml") // cfg.Cfg.TeamWhiteList = append(cfg.Cfg.TeamWhiteList, "org1/team2", "org1/team1") - user := &structs.User{Username: "testuser", Email: "test@example.com", Name: "Test Name"} + user := &structs.User{ + Sub: "testsub", + Username: "testuser", + Email: "test@example.com", + Name: "Test Name", + } user.TeamMemberships = append(user.TeamMemberships, "org1/team3") user.TeamMemberships = append(user.TeamMemberships, "org1/team1") ok, err := verifyUser(*user) @@ -84,7 +104,12 @@ func TestVerifyUserPositiveByTeam(t *testing.T) { func TestVerifyUserNegativeByTeam(t *testing.T) { setUp("/config/testing/handler_teams.yml") - user := &structs.User{Username: "testuser", Email: "test@example.com", Name: "Test Name"} + user := &structs.User{ + Sub: "testsub", + Username: "testuser", + Email: "test@example.com", + Name: "Test Name", + } // cfg.Cfg.TeamWhiteList = append(cfg.Cfg.TeamWhiteList, "org1/team1") ok, err := verifyUser(*user) @@ -95,7 +120,12 @@ func TestVerifyUserNegativeByTeam(t *testing.T) { func TestVerifyUserPositiveNoDomainsConfigured(t *testing.T) { setUp("/config/testing/handler_nodomains.yml") - user := &structs.User{Username: "testuser", Email: "test@example.com", Name: "Test Name"} + user := &structs.User{ + Sub: "testsub", + Username: "testuser", + Email: "test@example.com", + Name: "Test Name", + } cfg.Cfg.Domains = make([]string, 0) ok, err := verifyUser(*user) @@ -105,7 +135,12 @@ func TestVerifyUserPositiveNoDomainsConfigured(t *testing.T) { func TestVerifyUserNegative(t *testing.T) { setUp("/config/testing/test_config.yml") - user := &structs.User{Username: "testuser", Email: "test@example.com", Name: "Test Name"} + user := &structs.User{ + Sub: "testsub", + Username: "testuser", + Email: "test@example.com", + Name: "Test Name", + } ok, err := verifyUser(*user) assert.False(t, ok) @@ -116,6 +151,7 @@ func TestVerifyUserNegative(t *testing.T) { // it should live there but circular imports are resolved if it lives here var ( u1 = structs.User{ + Sub: "test", Username: "test@testing.com", Name: "Test Name", } @@ -141,6 +177,7 @@ func init() { // log.SetLevel(log.DebugLevel) lc = jwtmanager.VouchClaims{ + u1.Sub, u1.Username, jwtmanager.Sites, customClaims.Claims, diff --git a/handlers/validate.go b/handlers/validate.go index 30a3ce01..a94d7dda 100644 --- a/handlers/validate.go +++ b/handlers/validate.go @@ -25,8 +25,8 @@ import ( ) var ( - errNoJWT = errors.New("no jwt found in request") - errNoUser = errors.New("no User found in jwt") + errNoJWT = errors.New("no jwt found in request") + errNoSub = errors.New("no 'sub' found in jwt") ) // ValidateRequestHandler /validate @@ -45,8 +45,8 @@ func ValidateRequestHandler(w http.ResponseWriter, r *http.Request) { return } - if claims.Username == "" { - send401or200PublicAccess(w, r, errNoUser) + if claims.Sub == "" { + send401or200PublicAccess(w, r, errNoSub) return } @@ -59,7 +59,10 @@ func ValidateRequestHandler(w http.ResponseWriter, r *http.Request) { } generateCustomClaimsHeaders(w, claims) - w.Header().Add(cfg.Cfg.Headers.User, claims.Username) + w.Header().Add(cfg.Cfg.Headers.Sub, claims.Sub) + if claims.Username != "" { + w.Header().Add(cfg.Cfg.Headers.User, claims.Username) + } w.Header().Add(cfg.Cfg.Headers.Success, "true") if cfg.Cfg.Headers.AccessToken != "" && claims.PAccessToken != "" { diff --git a/handlers/validate_test.go b/handlers/validate_test.go index 7d82a3f1..9bf968c2 100644 --- a/handlers/validate_test.go +++ b/handlers/validate_test.go @@ -28,7 +28,12 @@ import ( func BenchmarkValidateRequestHandler(b *testing.B) { setUp("/config/testing/handler_email.yml") - user := &structs.User{Username: "testuser", Email: "test@example.com", Name: "Test Name"} + user := &structs.User{ + Sub: "testsub", + Username: "testuser", + Email: "test@example.com", + Name: "Test Name", + } tokens := structs.PTokens{} customClaims := structs.CustomClaims{} @@ -66,7 +71,12 @@ func TestValidateRequestHandlerPerf(t *testing.T) { } setUp("/config/testing/handler_email.yml") - user := &structs.User{Username: "testuser", Email: "test@example.com", Name: "Test Name"} + user := &structs.User{ + Sub: "testsub", + Username: "testuser", + Email: "test@example.com", + Name: "Test Name", + } tokens := structs.PTokens{} customClaims := structs.CustomClaims{} @@ -153,7 +163,12 @@ func TestValidateRequestHandlerWithGroupClaims(t *testing.T) { tokens := structs.PTokens{} - user := &structs.User{Username: "testuser", Email: "test@example.com", Name: "Test Name"} + user := &structs.User{ + Sub: "testsub", + Username: "testuser", + Email: "test@example.com", + Name: "Test Name", + } userTokenString := jwtmanager.CreateUserTokenString(*user, customClaims, tokens) req, err := http.NewRequest("GET", "/validate", nil) @@ -205,7 +220,12 @@ func TestJWTCacheHandler(t *testing.T) { setUp("/config/testing/handler_logout_url.yml") handler := jwtmanager.JWTCacheHandler(http.HandlerFunc(ValidateRequestHandler)) - user := &structs.User{Username: "testuser", Email: "test@example.com", Name: "Test Name"} + user := &structs.User{ + Sub: "testsub", + Username: "testuser", + Email: "test@example.com", + Name: "Test Name", + } tokens := structs.PTokens{} customClaims := structs.CustomClaims{} diff --git a/pkg/cfg/cfg.go b/pkg/cfg/cfg.go index 3e8def28..6e80dbfb 100644 --- a/pkg/cfg/cfg.go +++ b/pkg/cfg/cfg.go @@ -59,6 +59,7 @@ type Config struct { } Headers struct { + Sub string `mapstructure:"sub"` JWT string `mapstructure:"jwt"` User string `mapstructure:"user"` QueryString string `mapstructure:"querystring"` diff --git a/pkg/jwtmanager/jwtmanager.go b/pkg/jwtmanager/jwtmanager.go index bd761eb8..055d05b4 100644 --- a/pkg/jwtmanager/jwtmanager.go +++ b/pkg/jwtmanager/jwtmanager.go @@ -33,6 +33,7 @@ import ( // VouchClaims jwt Claims specific to vouch type VouchClaims struct { + Sub string `json:"sub"` Username string `json:"username"` Sites []string `json:"sites"` // tempting to make this a map but the array is fewer characters in the jwt CustomClaims map[string]interface{} @@ -78,6 +79,7 @@ func CreateUserTokenString(u structs.User, customClaims structs.CustomClaims, pt // User`token` // u.PrepareUserData() claims := VouchClaims{ + u.Sub, u.Username, Sites, customClaims.Claims, diff --git a/pkg/jwtmanager/jwtmanager_test.go b/pkg/jwtmanager/jwtmanager_test.go index f696e0f2..d36959d1 100644 --- a/pkg/jwtmanager/jwtmanager_test.go +++ b/pkg/jwtmanager/jwtmanager_test.go @@ -22,6 +22,7 @@ import ( var ( u1 = structs.User{ + Sub: "testsub", Username: "test@testing.com", Name: "Test Name", } @@ -49,6 +50,7 @@ func init() { Configure() lc = VouchClaims{ + u1.Sub, u1.Username, Sites, customClaims.Claims, diff --git a/pkg/structs/structs.go b/pkg/structs/structs.go index e42c0d60..ec76a75c 100644 --- a/pkg/structs/structs.go +++ b/pkg/structs/structs.go @@ -22,6 +22,7 @@ type UserI interface { // User is inherited. type User struct { + Sub string `json:"sub"` // TODO: set Provider here so that we can pass it to db // populated by db (via mapstructure) or from provider (via json) // Provider string `json:"provider",mapstructure:"provider"` @@ -47,7 +48,6 @@ func (u *User) PrepareUserData() { // AzureUser is a retrieved and authenticated user from Azure AD type AzureUser struct { User - Sub string `json:"sub"` UPN string `json:"upn"` } @@ -71,7 +71,6 @@ func (u *AzureUser) PrepareUserData() { // https://golang.org/doc/effective_go.html#embedding type GoogleUser struct { User - Sub string `json:"sub"` GivenName string `json:"given_name"` FamilyName string `json:"family_name"` Profile string `json:"profile"` @@ -90,7 +89,6 @@ func (u *GoogleUser) PrepareUserData() { // ADFSUser Active Directory user record type ADFSUser struct { User - Sub string `json:"sub"` UPN string `json:"upn"` // UniqueName string `json:"unique_name"` // PwdExp string `json:"pwd_exp"`