Merge pull request #1612 from vi7/multiple-user-to-group-mapping
connector/ldap: add multiple user to group mapping
This commit is contained in:
		| @@ -123,10 +123,11 @@ connectors: | |||||||
|       # Optional filter to apply when searching the directory. |       # Optional filter to apply when searching the directory. | ||||||
|       filter: "(objectClass=group)" |       filter: "(objectClass=group)" | ||||||
|  |  | ||||||
|       # Following two fields are used to match a user to a group. It adds an additional |       # Following list contains field pairs that are used to match a user to a group. It adds an additional | ||||||
|       # requirement to the filter that an attribute in the group must match the user's |       # requirement to the filter that an attribute in the group must match the user's | ||||||
|       # attribute value. |       # attribute value. | ||||||
|       userAttr: uid |       userMatchers: | ||||||
|  |       - userAttr: uid | ||||||
|         groupAttr: member |         groupAttr: member | ||||||
|  |  | ||||||
|       # Represents group name. |       # Represents group name. | ||||||
| @@ -215,7 +216,8 @@ groupSearch: | |||||||
|  |  | ||||||
|   # The group search needs to match the "uid" attribute on |   # The group search needs to match the "uid" attribute on | ||||||
|   # the user with the "memberUid" attribute on the group. |   # the user with the "memberUid" attribute on the group. | ||||||
|   userAttr: uid |   userMatchers: | ||||||
|  |   - userAttr: uid | ||||||
|     groupAttr: memberUid |     groupAttr: memberUid | ||||||
|  |  | ||||||
|   # Unique name of the group. |   # Unique name of the group. | ||||||
| @@ -242,7 +244,26 @@ groupSearch: | |||||||
|   # Optional filter to apply when searching the directory. |   # Optional filter to apply when searching the directory. | ||||||
|   filter: "(objectClass=group)" |   filter: "(objectClass=group)" | ||||||
|  |  | ||||||
|   userAttr: DN # Use "DN" here not "uid" |   userMatchers: | ||||||
|  |   - userAttr: DN # Use "DN" here not "uid" | ||||||
|  |     groupAttr: member | ||||||
|  |  | ||||||
|  |   nameAttr: name | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | There are cases when different types (objectClass) of groups use different attributes to keep a list of members. Below is an example of group query for such case: | ||||||
|  |  | ||||||
|  | ```yaml | ||||||
|  | groupSearch: | ||||||
|  |   baseDN: cn=groups,cn=compat,dc=example,dc=com | ||||||
|  |   # Optional filter to search for different group types | ||||||
|  |   filter: "(|(objectClass=posixGroup)(objectClass=group))" | ||||||
|  |  | ||||||
|  |   # Use multiple user matchers so Dex will know which attribute names should be used to search for group members | ||||||
|  |   userMatchers: | ||||||
|  |   - userAttr: uid | ||||||
|  |     groupAttr: memberUid | ||||||
|  |   - userAttr: DN | ||||||
|     groupAttr: member |     groupAttr: member | ||||||
|  |  | ||||||
|   nameAttr: name |   nameAttr: name | ||||||
| @@ -275,7 +296,8 @@ connectors: | |||||||
|       # Would translate to the query "(&(objectClass=group)(member=<user uid>))". |       # Would translate to the query "(&(objectClass=group)(member=<user uid>))". | ||||||
|       baseDN: cn=groups,dc=freeipa,dc=example,dc=com |       baseDN: cn=groups,dc=freeipa,dc=example,dc=com | ||||||
|       filter: "(objectClass=group)" |       filter: "(objectClass=group)" | ||||||
|       userAttr: uid |       userMatchers: | ||||||
|  |       - userAttr: uid | ||||||
|         groupAttr: member |         groupAttr: member | ||||||
|       nameAttr: name |       nameAttr: name | ||||||
| ``` | ``` | ||||||
| @@ -315,7 +337,8 @@ connectors: | |||||||
|     groupSearch: |     groupSearch: | ||||||
|       baseDN: cn=Users,dc=example,dc=com |       baseDN: cn=Users,dc=example,dc=com | ||||||
|       filter: "(objectClass=group)" |       filter: "(objectClass=group)" | ||||||
|       userAttr: DN |       userMatchers: | ||||||
|  |       - userAttr: DN | ||||||
|         groupAttr: member |         groupAttr: member | ||||||
|       nameAttr: cn |       nameAttr: cn | ||||||
| ``` | ``` | ||||||
|   | |||||||
| @@ -41,16 +41,26 @@ import ( | |||||||
| //         nameAttr: name | //         nameAttr: name | ||||||
| //         preferredUsernameAttr: uid | //         preferredUsernameAttr: uid | ||||||
| //       groupSearch: | //       groupSearch: | ||||||
| //         # Would translate to the query "(&(objectClass=group)(member=<user uid>))" | //         # Would translate to the separate query per user matcher pair and aggregate results into a single group list: | ||||||
|  | //         #  "(&(|(objectClass=posixGroup)(objectClass=groupOfNames))(memberUid=<user uid>))" | ||||||
|  | //         #  "(&(|(objectClass=posixGroup)(objectClass=groupOfNames))(member=<user DN>))" | ||||||
| //         baseDN: cn=groups,dc=example,dc=com | //         baseDN: cn=groups,dc=example,dc=com | ||||||
| //         filter: "(objectClass=group)" | //         filter: "(|(objectClass=posixGroup)(objectClass=groupOfNames))" | ||||||
| //         userAttr: uid | //         userMatchers: | ||||||
|  | //         - userAttr: uid | ||||||
|  | //           groupAttr: memberUid | ||||||
| //           # Use if full DN is needed and not available as any other attribute | //           # Use if full DN is needed and not available as any other attribute | ||||||
| //         # Will only work if "DN" attribute does not exist in the record | //           # Will only work if "DN" attribute does not exist in the record: | ||||||
| //         # userAttr: DN | //         - userAttr: DN | ||||||
| //           groupAttr: member | //           groupAttr: member | ||||||
| //         nameAttr: name | //         nameAttr: name | ||||||
| // | // | ||||||
|  |  | ||||||
|  | type UserMatcher struct { | ||||||
|  | 	UserAttr  string `json:"userAttr"` | ||||||
|  | 	GroupAttr string `json:"groupAttr"` | ||||||
|  | } | ||||||
|  |  | ||||||
| type Config struct { | type Config struct { | ||||||
| 	// The host and optional port of the LDAP server. If port isn't supplied, it will be | 	// The host and optional port of the LDAP server. If port isn't supplied, it will be | ||||||
| 	// guessed based on the TLS configuration. 389 or 636. | 	// guessed based on the TLS configuration. 389 or 636. | ||||||
| @@ -124,16 +134,22 @@ type Config struct { | |||||||
|  |  | ||||||
| 		Scope string `json:"scope"` // Defaults to "sub" | 		Scope string `json:"scope"` // Defaults to "sub" | ||||||
|  |  | ||||||
| 		// These two fields are use to match a user to a group. | 		// 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. | ||||||
|  | 		// See the "UserMatcher" struct for the exact field names | ||||||
| 		// | 		// | ||||||
| 		// It adds an additional requirement to the filter that an attribute in the group | 		// Each pair adds an additional requirement to the filter that an attribute in the group | ||||||
| 		// match the user's attribute value. For example that the "members" attribute of | 		// match the user's attribute value. For example that the "members" attribute of | ||||||
| 		// a group matches the "uid" of the user. The exact filter being added is: | 		// a group matches the "uid" of the user. The exact filter being added is: | ||||||
| 		// | 		// | ||||||
| 		//   (<groupAttr>=<userAttr value>) | 		//   (userMatchers[n].<groupAttr>=userMatchers[n].<userAttr value>) | ||||||
| 		// | 		// | ||||||
| 		UserAttr  string `json:"userAttr"` | 		UserMatchers []UserMatcher `json:"userMatchers"` | ||||||
| 		GroupAttr string `json:"groupAttr"` |  | ||||||
|  |  | ||||||
| 		// The attribute of the group that represents its name. | 		// The attribute of the group that represents its name. | ||||||
| 		NameAttr string `json:"nameAttr"` | 		NameAttr string `json:"nameAttr"` | ||||||
| @@ -165,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) | ||||||
| @@ -378,11 +411,14 @@ func (c *ldapConnector) userEntry(conn *ldap.Conn, username string) (user ldap.E | |||||||
| 		Attributes: []string{ | 		Attributes: []string{ | ||||||
| 			c.UserSearch.IDAttr, | 			c.UserSearch.IDAttr, | ||||||
| 			c.UserSearch.EmailAttr, | 			c.UserSearch.EmailAttr, | ||||||
| 			c.GroupSearch.UserAttr, |  | ||||||
| 			// TODO(ericchiang): what if this contains duplicate values? | 			// TODO(ericchiang): what if this contains duplicate values? | ||||||
| 		}, | 		}, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	for _, matcher := range c.userMatchers() { | ||||||
|  | 		req.Attributes = append(req.Attributes, matcher.UserAttr) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	if c.UserSearch.NameAttr != "" { | 	if c.UserSearch.NameAttr != "" { | ||||||
| 		req.Attributes = append(req.Attributes, c.UserSearch.NameAttr) | 		req.Attributes = append(req.Attributes, c.UserSearch.NameAttr) | ||||||
| 	} | 	} | ||||||
| @@ -536,8 +572,9 @@ func (c *ldapConnector) groups(ctx context.Context, user ldap.Entry) ([]string, | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	var groups []*ldap.Entry | 	var groups []*ldap.Entry | ||||||
| 	for _, attr := range getAttrs(user, c.GroupSearch.UserAttr) { | 	for _, matcher := range c.userMatchers() { | ||||||
| 		filter := fmt.Sprintf("(%s=%s)", c.GroupSearch.GroupAttr, ldap.EscapeFilter(attr)) | 		for _, attr := range getAttrs(user, matcher.UserAttr) { | ||||||
|  | 			filter := fmt.Sprintf("(%s=%s)", matcher.GroupAttr, ldap.EscapeFilter(attr)) | ||||||
| 			if c.GroupSearch.Filter != "" { | 			if c.GroupSearch.Filter != "" { | ||||||
| 				filter = fmt.Sprintf("(&%s%s)", c.GroupSearch.Filter, filter) | 				filter = fmt.Sprintf("(&%s%s)", c.GroupSearch.Filter, filter) | ||||||
| 			} | 			} | ||||||
| @@ -568,6 +605,7 @@ func (c *ldapConnector) groups(ctx context.Context, user ldap.Entry) ([]string, | |||||||
| 				c.logger.Errorf("ldap: groups search with filter %q returned no groups", filter) | 				c.logger.Errorf("ldap: groups search with filter %q returned no groups", filter) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	var groupNames []string | 	var groupNames []string | ||||||
| 	for _, group := range groups { | 	for _, group := range groups { | ||||||
|   | |||||||
| @@ -307,8 +307,12 @@ member: cn=jane,ou=People,dc=example,dc=org | |||||||
| 	c.UserSearch.IDAttr = "DN" | 	c.UserSearch.IDAttr = "DN" | ||||||
| 	c.UserSearch.Username = "cn" | 	c.UserSearch.Username = "cn" | ||||||
| 	c.GroupSearch.BaseDN = "ou=Groups,dc=example,dc=org" | 	c.GroupSearch.BaseDN = "ou=Groups,dc=example,dc=org" | ||||||
| 	c.GroupSearch.UserAttr = "DN" | 	c.GroupSearch.UserMatchers = []UserMatcher{ | ||||||
| 	c.GroupSearch.GroupAttr = "member" | 		{ | ||||||
|  | 			UserAttr:  "DN", | ||||||
|  | 			GroupAttr: "member", | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
| 	c.GroupSearch.NameAttr = "cn" | 	c.GroupSearch.NameAttr = "cn" | ||||||
|  |  | ||||||
| 	tests := []subtest{ | 	tests := []subtest{ | ||||||
| @@ -400,8 +404,12 @@ gidNumber: 1002 | |||||||
| 	c.UserSearch.IDAttr = "DN" | 	c.UserSearch.IDAttr = "DN" | ||||||
| 	c.UserSearch.Username = "cn" | 	c.UserSearch.Username = "cn" | ||||||
| 	c.GroupSearch.BaseDN = "ou=Groups,dc=example,dc=org" | 	c.GroupSearch.BaseDN = "ou=Groups,dc=example,dc=org" | ||||||
| 	c.GroupSearch.UserAttr = "departmentNumber" | 	c.GroupSearch.UserMatchers = []UserMatcher{ | ||||||
| 	c.GroupSearch.GroupAttr = "gidNumber" | 		{ | ||||||
|  | 			UserAttr:  "departmentNumber", | ||||||
|  | 			GroupAttr: "gidNumber", | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
| 	c.GroupSearch.NameAttr = "cn" | 	c.GroupSearch.NameAttr = "cn" | ||||||
| 	tests := []subtest{ | 	tests := []subtest{ | ||||||
| 		{ | 		{ | ||||||
| @@ -485,6 +493,243 @@ cn: admins | |||||||
| member: cn=john,ou=People,dc=example,dc=org | member: cn=john,ou=People,dc=example,dc=org | ||||||
| member: cn=jane,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.UserMatchers = []UserMatcher{ | ||||||
|  | 		{ | ||||||
|  | 			UserAttr:  "DN", | ||||||
|  | 			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 TestGroupToUserMatchers(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 | ||||||
|  | uid: janedoe | ||||||
|  | mail: janedoe@example.com | ||||||
|  | userpassword: foo | ||||||
|  |  | ||||||
|  | dn: cn=john,ou=People,dc=example,dc=org | ||||||
|  | objectClass: person | ||||||
|  | objectClass: inetOrgPerson | ||||||
|  | sn: doe | ||||||
|  | cn: john | ||||||
|  | uid: johndoe | ||||||
|  | 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=UnixGroups,ou=Seattle,dc=example,dc=org | ||||||
|  | objectClass: organizationalUnit | ||||||
|  | ou: UnixGroups | ||||||
|  |  | ||||||
|  | dn: ou=Groups,ou=Portland,dc=example,dc=org | ||||||
|  | objectClass: organizationalUnit | ||||||
|  | ou: Groups | ||||||
|  |  | ||||||
|  | dn: ou=UnixGroups,ou=Portland,dc=example,dc=org | ||||||
|  | objectClass: organizationalUnit | ||||||
|  | ou: UnixGroups | ||||||
|  |  | ||||||
|  | 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=logger,ou=UnixGroups,ou=Portland,dc=example,dc=org | ||||||
|  | objectClass: posixGroup | ||||||
|  | gidNumber: 1000 | ||||||
|  | cn: logger | ||||||
|  | memberUid: johndoe | ||||||
|  |  | ||||||
|  | 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 | ||||||
|  |  | ||||||
|  | dn: cn=frontend,ou=UnixGroups,ou=Seattle,dc=example,dc=org | ||||||
|  | objectClass: posixGroup | ||||||
|  | gidNumber: 1001 | ||||||
|  | cn: frontend | ||||||
|  | memberUid: janedoe | ||||||
|  | ` | ||||||
|  | 	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.UserMatchers = []UserMatcher{ | ||||||
|  | 		{ | ||||||
|  | 			UserAttr:  "DN", | ||||||
|  | 			GroupAttr: "member", | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			UserAttr:  "uid", | ||||||
|  | 			GroupAttr: "memberUid", | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	c.GroupSearch.NameAttr = "cn" | ||||||
|  | 	c.GroupSearch.Filter = "(|(objectClass=posixGroup)(objectClass=groupOfNames))" // search all group types | ||||||
|  |  | ||||||
|  | 	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", "frontend"}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		{ | ||||||
|  | 			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{"qa", "admins", "logger"}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	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 | dn: cn=developers,ou=Groups,ou=Seattle,dc=example,dc=org | ||||||
| objectClass: groupOfNames | objectClass: groupOfNames | ||||||
| cn: developers | cn: developers | ||||||
|   | |||||||
| @@ -41,9 +41,10 @@ connectors: | |||||||
|       baseDN: cn=Users,dc=example,dc=com |       baseDN: cn=Users,dc=example,dc=com | ||||||
|       filter: "(objectClass=group)" |       filter: "(objectClass=group)" | ||||||
|  |  | ||||||
|  |       userMatchers: | ||||||
|       # A user is a member of a group when their DN matches |       # A user is a member of a group when their DN matches | ||||||
|       # the value of a "member" attribute on the group entity. |       # the value of a "member" attribute on the group entity. | ||||||
|       userAttr: DN |       - userAttr: DN | ||||||
|         groupAttr: member |         groupAttr: member | ||||||
|  |  | ||||||
|       # The group name should be the "cn" value. |       # The group name should be the "cn" value. | ||||||
|   | |||||||
| @@ -37,9 +37,10 @@ connectors: | |||||||
|       baseDN: ou=Groups,dc=example,dc=org |       baseDN: ou=Groups,dc=example,dc=org | ||||||
|       filter: "(objectClass=groupOfNames)" |       filter: "(objectClass=groupOfNames)" | ||||||
|  |  | ||||||
|  |       userMatchers: | ||||||
|         # A user is a member of a group when their DN matches |         # A user is a member of a group when their DN matches | ||||||
|         # the value of a "member" attribute on the group entity. |         # the value of a "member" attribute on the group entity. | ||||||
|       userAttr: DN |       - userAttr: DN | ||||||
|         groupAttr: member |         groupAttr: member | ||||||
|  |  | ||||||
|       # The group name should be the "cn" value. |       # The group name should be the "cn" value. | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user