connector/saml: add 'FilterGroups' setting
This should make AllowedGroups equivalent to an LDAP group filter: When set to true, only the groups from AllowedGroups will be included in the user's identity. Signed-off-by: Stephan Renatus <srenatus@chef.io>
This commit is contained in:
		| @@ -18,6 +18,8 @@ The connector doesn't support signed AuthnRequests or encrypted attributes. | ||||
|  | ||||
| The SAML Connector supports providing a whitelist of SAML Groups to filter access based on, and when the `groupsattr` is set with a scope including groups, Dex will check for membership based on configured groups in the `allowedGroups` config setting for the SAML connector. | ||||
|  | ||||
| If `filterGroups` is set to true, any groups _not_ part of `allowedGroups` will be excluded. | ||||
|  | ||||
| ## Configuration | ||||
|  | ||||
| ```yaml | ||||
|   | ||||
| @@ -101,6 +101,7 @@ type Config struct { | ||||
| 	// used split the groups string. | ||||
| 	GroupsDelim   string   `json:"groupsDelim"` | ||||
| 	AllowedGroups []string `json:"allowedGroups"` | ||||
| 	FilterGroups  bool     `json:"filterGroups"` | ||||
| 	RedirectURI   string   `json:"redirectURI"` | ||||
|  | ||||
| 	// Requested format of the NameID. The NameID value is is mapped to the ID Token | ||||
| @@ -165,6 +166,7 @@ func (c *Config) openConnector(logger log.Logger) (*provider, error) { | ||||
| 		groupsAttr:    c.GroupsAttr, | ||||
| 		groupsDelim:   c.GroupsDelim, | ||||
| 		allowedGroups: c.AllowedGroups, | ||||
| 		filterGroups:  c.FilterGroups, | ||||
| 		redirectURI:   c.RedirectURI, | ||||
| 		logger:        logger, | ||||
|  | ||||
| @@ -240,6 +242,7 @@ type provider struct { | ||||
| 	groupsAttr    string | ||||
| 	groupsDelim   string | ||||
| 	allowedGroups []string | ||||
| 	filterGroups  bool | ||||
|  | ||||
| 	redirectURI string | ||||
|  | ||||
| @@ -430,6 +433,10 @@ func (p *provider) HandlePOST(s connector.Scopes, samlResponse, inResponseTo str | ||||
| 		return ident, fmt.Errorf("user not a member of allowed groups") | ||||
| 	} | ||||
|  | ||||
| 	if p.filterGroups { | ||||
| 		ident.Groups = groupMatches | ||||
| 	} | ||||
|  | ||||
| 	// Otherwise, we're good | ||||
| 	return ident, nil | ||||
| } | ||||
|   | ||||
| @@ -53,6 +53,7 @@ type responseTest struct { | ||||
| 	emailAttr     string | ||||
| 	groupsAttr    string | ||||
| 	allowedGroups []string | ||||
| 	filterGroups  bool | ||||
|  | ||||
| 	// Expected outcome of the test. | ||||
| 	wantErr   bool | ||||
| @@ -121,6 +122,29 @@ func TestGroupsWhitelist(t *testing.T) { | ||||
| 	test.run(t) | ||||
| } | ||||
|  | ||||
| func TestGroupsWhitelistWithFiltering(t *testing.T) { | ||||
| 	test := responseTest{ | ||||
| 		caFile:        "testdata/ca.crt", | ||||
| 		respFile:      "testdata/good-resp.xml", | ||||
| 		now:           "2017-04-04T04:34:59.330Z", | ||||
| 		usernameAttr:  "Name", | ||||
| 		emailAttr:     "email", | ||||
| 		groupsAttr:    "groups", | ||||
| 		allowedGroups: []string{"Admins"}, | ||||
| 		filterGroups:  true, | ||||
| 		inResponseTo:  "6zmm5mguyebwvajyf2sdwwcw6m", | ||||
| 		redirectURI:   "http://127.0.0.1:5556/dex/callback", | ||||
| 		wantIdent: connector.Identity{ | ||||
| 			UserID:        "eric.chiang+okta@coreos.com", | ||||
| 			Username:      "Eric", | ||||
| 			Email:         "eric.chiang+okta@coreos.com", | ||||
| 			EmailVerified: true, | ||||
| 			Groups:        []string{"Admins"}, // "Everyone" is filtered | ||||
| 		}, | ||||
| 	} | ||||
| 	test.run(t) | ||||
| } | ||||
|  | ||||
| func TestGroupsWhitelistEmpty(t *testing.T) { | ||||
| 	test := responseTest{ | ||||
| 		caFile:        "testdata/ca.crt", | ||||
| @@ -388,6 +412,7 @@ func (r responseTest) run(t *testing.T) { | ||||
| 		RedirectURI:   r.redirectURI, | ||||
| 		EntityIssuer:  r.entityIssuer, | ||||
| 		AllowedGroups: r.allowedGroups, | ||||
| 		FilterGroups:  r.filterGroups, | ||||
| 		// Never logging in, don't need this. | ||||
| 		SSOURL: "http://foo.bar/", | ||||
| 	} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user