connector/ldap: backward compatibility with single user to group mapping
Signed-off-by: Vitaliy Dmitriev <vi7alya@gmail.com>
This commit is contained in:
		| @@ -134,6 +134,12 @@ type Config struct { | |||||||
|  |  | ||||||
| 		Scope string `json:"scope"` // Defaults to "sub" | 		Scope string `json:"scope"` // Defaults to "sub" | ||||||
|  |  | ||||||
|  | 		// DEPRECATED config options. Those are left for backward compatibility. | ||||||
|  | 		// See "UserMatchers" below for the current group to user matching implementation | ||||||
|  | 		// TODO: should be eventually removed from the code | ||||||
|  | 		UserAttr  string `json:"userAttr"` | ||||||
|  | 		GroupAttr string `json:"groupAttr"` | ||||||
|  |  | ||||||
| 		// Array of the field pairs used to match a user to a group. | 		// Array of the field pairs used to match a user to a group. | ||||||
| 		// See the "UserMatcher" struct for the exact field names | 		// See the "UserMatcher" struct for the exact field names | ||||||
| 		// | 		// | ||||||
| @@ -175,6 +181,23 @@ func parseScope(s string) (int, bool) { | |||||||
| 	return 0, false | 	return 0, false | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Build a list of group attr name to user attr value matchers. | ||||||
|  | // Function exists here to allow backward compatibility between old and new | ||||||
|  | // group to user matching implementations. | ||||||
|  | // See "Config.GroupSearch.UserMatchers" comments for the details | ||||||
|  | func (c *ldapConnector) userMatchers() []UserMatcher { | ||||||
|  | 	if len(c.GroupSearch.UserMatchers) > 0 && c.GroupSearch.UserMatchers[0].UserAttr != "" { | ||||||
|  | 		return c.GroupSearch.UserMatchers[:] | ||||||
|  | 	} else { | ||||||
|  | 		return []UserMatcher{ | ||||||
|  | 			{ | ||||||
|  | 				UserAttr:  c.GroupSearch.UserAttr, | ||||||
|  | 				GroupAttr: c.GroupSearch.GroupAttr, | ||||||
|  | 			}, | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| // Open returns an authentication strategy using LDAP. | // Open returns an authentication strategy using LDAP. | ||||||
| func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error) { | func (c *Config) Open(id string, logger log.Logger) (connector.Connector, error) { | ||||||
| 	conn, err := c.OpenConnector(logger) | 	conn, err := c.OpenConnector(logger) | ||||||
| @@ -392,7 +415,7 @@ func (c *ldapConnector) userEntry(conn *ldap.Conn, username string) (user ldap.E | |||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for _, matcher := range c.GroupSearch.UserMatchers { | 	for _, matcher := range c.userMatchers() { | ||||||
| 		req.Attributes = append(req.Attributes, matcher.UserAttr) | 		req.Attributes = append(req.Attributes, matcher.UserAttr) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -549,7 +572,7 @@ func (c *ldapConnector) groups(ctx context.Context, user ldap.Entry) ([]string, | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	var groups []*ldap.Entry | 	var groups []*ldap.Entry | ||||||
| 	for _, matcher := range c.GroupSearch.UserMatchers { | 	for _, matcher := range c.userMatchers() { | ||||||
| 		for _, attr := range getAttrs(user, matcher.UserAttr) { | 		for _, attr := range getAttrs(user, matcher.UserAttr) { | ||||||
| 			filter := fmt.Sprintf("(%s=%s)", matcher.GroupAttr, ldap.EscapeFilter(attr)) | 			filter := fmt.Sprintf("(%s=%s)", matcher.GroupAttr, ldap.EscapeFilter(attr)) | ||||||
| 			if c.GroupSearch.Filter != "" { | 			if c.GroupSearch.Filter != "" { | ||||||
|   | |||||||
| @@ -676,6 +676,109 @@ memberUid: janedoe | |||||||
| 	runTests(t, schema, connectLDAP, c, tests) | 	runTests(t, schema, connectLDAP, c, tests) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Test deprecated group to user matching implementation | ||||||
|  | // which was left for backward compatibility. | ||||||
|  | // See "Config.GroupSearch.UserMatchers" comments for the details | ||||||
|  | func TestDeprecatedGroupToUserMatcher(t *testing.T) { | ||||||
|  | 	schema := ` | ||||||
|  | dn: ou=People,dc=example,dc=org | ||||||
|  | objectClass: organizationalUnit | ||||||
|  | ou: People | ||||||
|  |  | ||||||
|  | dn: cn=jane,ou=People,dc=example,dc=org | ||||||
|  | objectClass: person | ||||||
|  | objectClass: inetOrgPerson | ||||||
|  | sn: doe | ||||||
|  | cn: jane | ||||||
|  | mail: janedoe@example.com | ||||||
|  | userpassword: foo | ||||||
|  |  | ||||||
|  | dn: cn=john,ou=People,dc=example,dc=org | ||||||
|  | objectClass: person | ||||||
|  | objectClass: inetOrgPerson | ||||||
|  | sn: doe | ||||||
|  | cn: john | ||||||
|  | mail: johndoe@example.com | ||||||
|  | userpassword: bar | ||||||
|  |  | ||||||
|  | # Group definitions. | ||||||
|  |  | ||||||
|  | dn: ou=Seattle,dc=example,dc=org | ||||||
|  | objectClass: organizationalUnit | ||||||
|  | ou: Seattle | ||||||
|  |  | ||||||
|  | dn: ou=Portland,dc=example,dc=org | ||||||
|  | objectClass: organizationalUnit | ||||||
|  | ou: Portland | ||||||
|  |  | ||||||
|  | dn: ou=Groups,ou=Seattle,dc=example,dc=org | ||||||
|  | objectClass: organizationalUnit | ||||||
|  | ou: Groups | ||||||
|  |  | ||||||
|  | dn: ou=Groups,ou=Portland,dc=example,dc=org | ||||||
|  | objectClass: organizationalUnit | ||||||
|  | ou: Groups | ||||||
|  |  | ||||||
|  | dn: cn=qa,ou=Groups,ou=Portland,dc=example,dc=org | ||||||
|  | objectClass: groupOfNames | ||||||
|  | cn: qa | ||||||
|  | member: cn=john,ou=People,dc=example,dc=org | ||||||
|  |  | ||||||
|  | dn: cn=admins,ou=Groups,ou=Seattle,dc=example,dc=org | ||||||
|  | objectClass: groupOfNames | ||||||
|  | cn: admins | ||||||
|  | member: cn=john,ou=People,dc=example,dc=org | ||||||
|  | member: cn=jane,ou=People,dc=example,dc=org | ||||||
|  |  | ||||||
|  | dn: cn=developers,ou=Groups,ou=Seattle,dc=example,dc=org | ||||||
|  | objectClass: groupOfNames | ||||||
|  | cn: developers | ||||||
|  | member: cn=jane,ou=People,dc=example,dc=org | ||||||
|  | ` | ||||||
|  | 	c := &Config{} | ||||||
|  | 	c.UserSearch.BaseDN = "ou=People,dc=example,dc=org" | ||||||
|  | 	c.UserSearch.NameAttr = "cn" | ||||||
|  | 	c.UserSearch.EmailAttr = "mail" | ||||||
|  | 	c.UserSearch.IDAttr = "DN" | ||||||
|  | 	c.UserSearch.Username = "cn" | ||||||
|  | 	c.GroupSearch.BaseDN = "dc=example,dc=org" | ||||||
|  | 	c.GroupSearch.UserAttr = "DN" | ||||||
|  | 	c.GroupSearch.GroupAttr = "member" | ||||||
|  | 	c.GroupSearch.NameAttr = "cn" | ||||||
|  | 	c.GroupSearch.Filter = "(ou:dn:=Seattle)" // ignore other groups | ||||||
|  |  | ||||||
|  | 	tests := []subtest{ | ||||||
|  | 		{ | ||||||
|  | 			name:     "validpassword", | ||||||
|  | 			username: "jane", | ||||||
|  | 			password: "foo", | ||||||
|  | 			groups:   true, | ||||||
|  | 			want: connector.Identity{ | ||||||
|  | 				UserID:        "cn=jane,ou=People,dc=example,dc=org", | ||||||
|  | 				Username:      "jane", | ||||||
|  | 				Email:         "janedoe@example.com", | ||||||
|  | 				EmailVerified: true, | ||||||
|  | 				Groups:        []string{"admins", "developers"}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			name:     "validpassword2", | ||||||
|  | 			username: "john", | ||||||
|  | 			password: "bar", | ||||||
|  | 			groups:   true, | ||||||
|  | 			want: connector.Identity{ | ||||||
|  | 				UserID:        "cn=john,ou=People,dc=example,dc=org", | ||||||
|  | 				Username:      "john", | ||||||
|  | 				Email:         "johndoe@example.com", | ||||||
|  | 				EmailVerified: true, | ||||||
|  | 				Groups:        []string{"admins"}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	runTests(t, schema, connectLDAP, c, tests) | ||||||
|  | } | ||||||
|  |  | ||||||
| func TestStartTLS(t *testing.T) { | func TestStartTLS(t *testing.T) { | ||||||
| 	schema := ` | 	schema := ` | ||||||
| dn: ou=People,dc=example,dc=org | dn: ou=People,dc=example,dc=org | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user