2017-04-11 03:32:40 +05:30
|
|
|
package ldap
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2019-12-09 00:51:28 +05:30
|
|
|
"fmt"
|
2021-09-17 11:42:39 +05:30
|
|
|
"io"
|
2017-04-11 03:32:40 +05:30
|
|
|
"os"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/kylelemons/godebug/pretty"
|
2017-07-26 02:15:17 +05:30
|
|
|
"github.com/sirupsen/logrus"
|
2017-04-11 03:32:40 +05:30
|
|
|
|
2018-09-03 12:14:44 +05:30
|
|
|
"github.com/dexidp/dex/connector"
|
2017-04-11 03:32:40 +05:30
|
|
|
)
|
|
|
|
|
2017-04-13 02:43:34 +05:30
|
|
|
// connectionMethod indicates how the test should connect to the LDAP server.
|
|
|
|
type connectionMethod int32
|
|
|
|
|
|
|
|
const (
|
|
|
|
connectStartTLS connectionMethod = iota
|
|
|
|
connectLDAPS
|
|
|
|
connectLDAP
|
2017-10-10 02:57:22 +05:30
|
|
|
connectInsecureSkipVerify
|
2017-04-13 02:43:34 +05:30
|
|
|
)
|
|
|
|
|
2017-04-11 03:32:40 +05:30
|
|
|
// subtest is a login test against a given schema.
|
|
|
|
type subtest struct {
|
|
|
|
// Name of the sub-test.
|
|
|
|
name string
|
|
|
|
|
|
|
|
// Password credentials, and if the connector should request
|
|
|
|
// groups as well.
|
|
|
|
username string
|
|
|
|
password string
|
|
|
|
groups bool
|
|
|
|
|
|
|
|
// Expected result of the login.
|
|
|
|
wantErr bool
|
|
|
|
wantBadPW bool
|
|
|
|
want connector.Identity
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestQuery(t *testing.T) {
|
|
|
|
c := &Config{}
|
2021-02-15 20:54:26 +05:30
|
|
|
c.UserSearch.BaseDN = "ou=People,ou=TestQuery,dc=example,dc=org"
|
2017-04-11 03:32:40 +05:30
|
|
|
c.UserSearch.NameAttr = "cn"
|
|
|
|
c.UserSearch.EmailAttr = "mail"
|
|
|
|
c.UserSearch.IDAttr = "DN"
|
|
|
|
c.UserSearch.Username = "cn"
|
|
|
|
|
|
|
|
tests := []subtest{
|
|
|
|
{
|
|
|
|
name: "validpassword",
|
|
|
|
username: "jane",
|
|
|
|
password: "foo",
|
|
|
|
want: connector.Identity{
|
2021-02-15 20:54:26 +05:30
|
|
|
UserID: "cn=jane,ou=People,ou=TestQuery,dc=example,dc=org",
|
2017-04-11 03:32:40 +05:30
|
|
|
Username: "jane",
|
|
|
|
Email: "janedoe@example.com",
|
|
|
|
EmailVerified: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "validpassword2",
|
|
|
|
username: "john",
|
|
|
|
password: "bar",
|
|
|
|
want: connector.Identity{
|
2021-02-15 20:54:26 +05:30
|
|
|
UserID: "cn=john,ou=People,ou=TestQuery,dc=example,dc=org",
|
2017-04-11 03:32:40 +05:30
|
|
|
Username: "john",
|
|
|
|
Email: "johndoe@example.com",
|
|
|
|
EmailVerified: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "invalidpassword",
|
|
|
|
username: "jane",
|
|
|
|
password: "badpassword",
|
|
|
|
wantBadPW: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "invaliduser",
|
|
|
|
username: "idontexist",
|
|
|
|
password: "foo",
|
|
|
|
wantBadPW: true, // Want invalid password, not a query error.
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2021-02-15 20:54:26 +05:30
|
|
|
runTests(t, connectLDAP, c, tests)
|
2017-04-11 03:32:40 +05:30
|
|
|
}
|
|
|
|
|
2019-01-09 08:31:42 +05:30
|
|
|
func TestQueryWithEmailSuffix(t *testing.T) {
|
|
|
|
c := &Config{}
|
2021-02-15 20:54:26 +05:30
|
|
|
c.UserSearch.BaseDN = "ou=People,ou=TestQueryWithEmailSuffix,dc=example,dc=org"
|
2019-01-09 08:31:42 +05:30
|
|
|
c.UserSearch.NameAttr = "cn"
|
|
|
|
c.UserSearch.EmailSuffix = "test.example.com"
|
|
|
|
c.UserSearch.IDAttr = "DN"
|
|
|
|
c.UserSearch.Username = "cn"
|
|
|
|
|
|
|
|
tests := []subtest{
|
|
|
|
{
|
|
|
|
name: "ignoremailattr",
|
|
|
|
username: "jane",
|
|
|
|
password: "foo",
|
|
|
|
want: connector.Identity{
|
2021-02-15 20:54:26 +05:30
|
|
|
UserID: "cn=jane,ou=People,ou=TestQueryWithEmailSuffix,dc=example,dc=org",
|
2019-01-09 08:31:42 +05:30
|
|
|
Username: "jane",
|
|
|
|
Email: "jane@test.example.com",
|
|
|
|
EmailVerified: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "nomailattr",
|
|
|
|
username: "john",
|
|
|
|
password: "bar",
|
|
|
|
want: connector.Identity{
|
2021-02-15 20:54:26 +05:30
|
|
|
UserID: "cn=john,ou=People,ou=TestQueryWithEmailSuffix,dc=example,dc=org",
|
2019-01-09 08:31:42 +05:30
|
|
|
Username: "john",
|
|
|
|
Email: "john@test.example.com",
|
|
|
|
EmailVerified: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2021-02-15 20:54:26 +05:30
|
|
|
runTests(t, connectLDAP, c, tests)
|
2019-01-09 08:31:42 +05:30
|
|
|
}
|
|
|
|
|
2018-06-14 13:40:15 +05:30
|
|
|
func TestUserFilter(t *testing.T) {
|
|
|
|
c := &Config{}
|
2021-02-15 20:54:26 +05:30
|
|
|
c.UserSearch.BaseDN = "ou=TestUserFilter,dc=example,dc=org"
|
2018-06-14 13:40:15 +05:30
|
|
|
c.UserSearch.NameAttr = "cn"
|
|
|
|
c.UserSearch.EmailAttr = "mail"
|
|
|
|
c.UserSearch.IDAttr = "DN"
|
|
|
|
c.UserSearch.Username = "cn"
|
|
|
|
c.UserSearch.Filter = "(ou:dn:=Seattle)"
|
|
|
|
|
|
|
|
tests := []subtest{
|
|
|
|
{
|
|
|
|
name: "validpassword",
|
|
|
|
username: "jane",
|
|
|
|
password: "foo",
|
|
|
|
want: connector.Identity{
|
2021-02-15 20:54:26 +05:30
|
|
|
UserID: "cn=jane,ou=People,ou=Seattle,ou=TestUserFilter,dc=example,dc=org",
|
2018-06-14 13:40:15 +05:30
|
|
|
Username: "jane",
|
|
|
|
Email: "janedoe@example.com",
|
|
|
|
EmailVerified: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "validpassword2",
|
|
|
|
username: "john",
|
|
|
|
password: "bar",
|
|
|
|
want: connector.Identity{
|
2021-02-15 20:54:26 +05:30
|
|
|
UserID: "cn=john,ou=People,ou=Seattle,ou=TestUserFilter,dc=example,dc=org",
|
2018-06-14 13:40:15 +05:30
|
|
|
Username: "john",
|
|
|
|
Email: "johndoe@example.com",
|
|
|
|
EmailVerified: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "invalidpassword",
|
|
|
|
username: "jane",
|
|
|
|
password: "badpassword",
|
|
|
|
wantBadPW: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "invaliduser",
|
|
|
|
username: "idontexist",
|
|
|
|
password: "foo",
|
|
|
|
wantBadPW: true, // Want invalid password, not a query error.
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2021-02-15 20:54:26 +05:30
|
|
|
runTests(t, connectLDAP, c, tests)
|
2018-06-14 13:40:15 +05:30
|
|
|
}
|
|
|
|
|
2017-04-11 03:32:40 +05:30
|
|
|
func TestGroupQuery(t *testing.T) {
|
|
|
|
c := &Config{}
|
2021-02-15 20:54:26 +05:30
|
|
|
c.UserSearch.BaseDN = "ou=People,ou=TestGroupQuery,dc=example,dc=org"
|
2017-04-11 03:32:40 +05:30
|
|
|
c.UserSearch.NameAttr = "cn"
|
|
|
|
c.UserSearch.EmailAttr = "mail"
|
|
|
|
c.UserSearch.IDAttr = "DN"
|
|
|
|
c.UserSearch.Username = "cn"
|
2021-02-15 20:54:26 +05:30
|
|
|
c.GroupSearch.BaseDN = "ou=Groups,ou=TestGroupQuery,dc=example,dc=org"
|
connector/ldap: add multiple user to group mapping
Add an ability to fetch user's membership from
groups of a different type by specifying multiple
group attribute to user attribute value matchers
in the Dex config:
userMatchers:
- userAttr: uid
groupAttr: memberUid
- userAttr: DN
groupAttr: member
In other words the user's groups can be fetched now from
ldap structure similar to the following:
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
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
Signed-off-by: Vitaliy Dmitriev <vi7alya@gmail.com>
2020-01-03 15:10:08 +05:30
|
|
|
c.GroupSearch.UserMatchers = []UserMatcher{
|
|
|
|
{
|
|
|
|
UserAttr: "DN",
|
|
|
|
GroupAttr: "member",
|
|
|
|
},
|
|
|
|
}
|
2017-04-11 03:32:40 +05:30
|
|
|
c.GroupSearch.NameAttr = "cn"
|
|
|
|
|
|
|
|
tests := []subtest{
|
|
|
|
{
|
|
|
|
name: "validpassword",
|
|
|
|
username: "jane",
|
|
|
|
password: "foo",
|
|
|
|
groups: true,
|
|
|
|
want: connector.Identity{
|
2021-02-15 20:54:26 +05:30
|
|
|
UserID: "cn=jane,ou=People,ou=TestGroupQuery,dc=example,dc=org",
|
2017-04-11 03:32:40 +05:30
|
|
|
Username: "jane",
|
|
|
|
Email: "janedoe@example.com",
|
|
|
|
EmailVerified: true,
|
|
|
|
Groups: []string{"admins", "developers"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "validpassword2",
|
|
|
|
username: "john",
|
|
|
|
password: "bar",
|
|
|
|
groups: true,
|
|
|
|
want: connector.Identity{
|
2021-02-15 20:54:26 +05:30
|
|
|
UserID: "cn=john,ou=People,ou=TestGroupQuery,dc=example,dc=org",
|
2017-04-11 03:32:40 +05:30
|
|
|
Username: "john",
|
|
|
|
Email: "johndoe@example.com",
|
|
|
|
EmailVerified: true,
|
|
|
|
Groups: []string{"admins"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2021-02-15 20:54:26 +05:30
|
|
|
runTests(t, connectLDAP, c, tests)
|
2017-04-11 03:32:40 +05:30
|
|
|
}
|
|
|
|
|
connector/ldap: fix case where groups are listed on the user entity
Support schemas that determine membership by having fields on the
user entity, instead of listing users on a groups entity. E.g. the
following schema is now supported when it wasn't previously:
cn=eric,cn=user,dn=exapmle,dn=com
objectClass=myPerson
cn: eric
uid: eric
email: eric@example.com
memberOf: foo
memberOf: bar
cn=foo,cn=group,dn=exapmle,dn=com
objectClass=myGroup
cn: foo
cn=bar,cn=group,dn=exapmle,dn=com
objectClass=myGroup
cn: bar
2017-04-11 22:18:48 +05:30
|
|
|
func TestGroupsOnUserEntity(t *testing.T) {
|
|
|
|
c := &Config{}
|
2021-02-15 20:54:26 +05:30
|
|
|
c.UserSearch.BaseDN = "ou=People,ou=TestGroupsOnUserEntity,dc=example,dc=org"
|
connector/ldap: fix case where groups are listed on the user entity
Support schemas that determine membership by having fields on the
user entity, instead of listing users on a groups entity. E.g. the
following schema is now supported when it wasn't previously:
cn=eric,cn=user,dn=exapmle,dn=com
objectClass=myPerson
cn: eric
uid: eric
email: eric@example.com
memberOf: foo
memberOf: bar
cn=foo,cn=group,dn=exapmle,dn=com
objectClass=myGroup
cn: foo
cn=bar,cn=group,dn=exapmle,dn=com
objectClass=myGroup
cn: bar
2017-04-11 22:18:48 +05:30
|
|
|
c.UserSearch.NameAttr = "cn"
|
|
|
|
c.UserSearch.EmailAttr = "mail"
|
|
|
|
c.UserSearch.IDAttr = "DN"
|
|
|
|
c.UserSearch.Username = "cn"
|
2021-02-15 20:54:26 +05:30
|
|
|
c.GroupSearch.BaseDN = "ou=Groups,ou=TestGroupsOnUserEntity,dc=example,dc=org"
|
connector/ldap: add multiple user to group mapping
Add an ability to fetch user's membership from
groups of a different type by specifying multiple
group attribute to user attribute value matchers
in the Dex config:
userMatchers:
- userAttr: uid
groupAttr: memberUid
- userAttr: DN
groupAttr: member
In other words the user's groups can be fetched now from
ldap structure similar to the following:
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
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
Signed-off-by: Vitaliy Dmitriev <vi7alya@gmail.com>
2020-01-03 15:10:08 +05:30
|
|
|
c.GroupSearch.UserMatchers = []UserMatcher{
|
|
|
|
{
|
|
|
|
UserAttr: "departmentNumber",
|
|
|
|
GroupAttr: "gidNumber",
|
|
|
|
},
|
|
|
|
}
|
connector/ldap: fix case where groups are listed on the user entity
Support schemas that determine membership by having fields on the
user entity, instead of listing users on a groups entity. E.g. the
following schema is now supported when it wasn't previously:
cn=eric,cn=user,dn=exapmle,dn=com
objectClass=myPerson
cn: eric
uid: eric
email: eric@example.com
memberOf: foo
memberOf: bar
cn=foo,cn=group,dn=exapmle,dn=com
objectClass=myGroup
cn: foo
cn=bar,cn=group,dn=exapmle,dn=com
objectClass=myGroup
cn: bar
2017-04-11 22:18:48 +05:30
|
|
|
c.GroupSearch.NameAttr = "cn"
|
|
|
|
tests := []subtest{
|
|
|
|
{
|
|
|
|
name: "validpassword",
|
|
|
|
username: "jane",
|
|
|
|
password: "foo",
|
|
|
|
groups: true,
|
|
|
|
want: connector.Identity{
|
2021-02-15 20:54:26 +05:30
|
|
|
UserID: "cn=jane,ou=People,ou=TestGroupsOnUserEntity,dc=example,dc=org",
|
connector/ldap: fix case where groups are listed on the user entity
Support schemas that determine membership by having fields on the
user entity, instead of listing users on a groups entity. E.g. the
following schema is now supported when it wasn't previously:
cn=eric,cn=user,dn=exapmle,dn=com
objectClass=myPerson
cn: eric
uid: eric
email: eric@example.com
memberOf: foo
memberOf: bar
cn=foo,cn=group,dn=exapmle,dn=com
objectClass=myGroup
cn: foo
cn=bar,cn=group,dn=exapmle,dn=com
objectClass=myGroup
cn: bar
2017-04-11 22:18:48 +05:30
|
|
|
Username: "jane",
|
|
|
|
Email: "janedoe@example.com",
|
|
|
|
EmailVerified: true,
|
|
|
|
Groups: []string{"admins", "developers"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "validpassword2",
|
|
|
|
username: "john",
|
|
|
|
password: "bar",
|
|
|
|
groups: true,
|
|
|
|
want: connector.Identity{
|
2021-02-15 20:54:26 +05:30
|
|
|
UserID: "cn=john,ou=People,ou=TestGroupsOnUserEntity,dc=example,dc=org",
|
connector/ldap: fix case where groups are listed on the user entity
Support schemas that determine membership by having fields on the
user entity, instead of listing users on a groups entity. E.g. the
following schema is now supported when it wasn't previously:
cn=eric,cn=user,dn=exapmle,dn=com
objectClass=myPerson
cn: eric
uid: eric
email: eric@example.com
memberOf: foo
memberOf: bar
cn=foo,cn=group,dn=exapmle,dn=com
objectClass=myGroup
cn: foo
cn=bar,cn=group,dn=exapmle,dn=com
objectClass=myGroup
cn: bar
2017-04-11 22:18:48 +05:30
|
|
|
Username: "john",
|
|
|
|
Email: "johndoe@example.com",
|
|
|
|
EmailVerified: true,
|
|
|
|
Groups: []string{"admins", "designers"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
2021-02-15 20:54:26 +05:30
|
|
|
runTests(t, connectLDAP, c, tests)
|
2017-04-13 02:43:34 +05:30
|
|
|
}
|
|
|
|
|
2018-06-14 13:40:15 +05:30
|
|
|
func TestGroupFilter(t *testing.T) {
|
|
|
|
c := &Config{}
|
2021-02-15 20:54:26 +05:30
|
|
|
c.UserSearch.BaseDN = "ou=People,ou=TestGroupFilter,dc=example,dc=org"
|
2018-06-14 13:40:15 +05:30
|
|
|
c.UserSearch.NameAttr = "cn"
|
|
|
|
c.UserSearch.EmailAttr = "mail"
|
|
|
|
c.UserSearch.IDAttr = "DN"
|
|
|
|
c.UserSearch.Username = "cn"
|
2021-02-15 20:54:26 +05:30
|
|
|
c.GroupSearch.BaseDN = "ou=TestGroupFilter,dc=example,dc=org"
|
connector/ldap: add multiple user to group mapping
Add an ability to fetch user's membership from
groups of a different type by specifying multiple
group attribute to user attribute value matchers
in the Dex config:
userMatchers:
- userAttr: uid
groupAttr: memberUid
- userAttr: DN
groupAttr: member
In other words the user's groups can be fetched now from
ldap structure similar to the following:
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
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
Signed-off-by: Vitaliy Dmitriev <vi7alya@gmail.com>
2020-01-03 15:10:08 +05:30
|
|
|
c.GroupSearch.UserMatchers = []UserMatcher{
|
|
|
|
{
|
|
|
|
UserAttr: "DN",
|
|
|
|
GroupAttr: "member",
|
|
|
|
},
|
|
|
|
}
|
2018-06-14 13:40:15 +05:30
|
|
|
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{
|
2021-02-15 20:54:26 +05:30
|
|
|
UserID: "cn=jane,ou=People,ou=TestGroupFilter,dc=example,dc=org",
|
2018-06-14 13:40:15 +05:30
|
|
|
Username: "jane",
|
|
|
|
Email: "janedoe@example.com",
|
|
|
|
EmailVerified: true,
|
|
|
|
Groups: []string{"admins", "developers"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "validpassword2",
|
|
|
|
username: "john",
|
|
|
|
password: "bar",
|
|
|
|
groups: true,
|
|
|
|
want: connector.Identity{
|
2021-02-15 20:54:26 +05:30
|
|
|
UserID: "cn=john,ou=People,ou=TestGroupFilter,dc=example,dc=org",
|
2018-06-14 13:40:15 +05:30
|
|
|
Username: "john",
|
|
|
|
Email: "johndoe@example.com",
|
|
|
|
EmailVerified: true,
|
|
|
|
Groups: []string{"admins"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2021-02-15 20:54:26 +05:30
|
|
|
runTests(t, connectLDAP, c, tests)
|
connector/ldap: add multiple user to group mapping
Add an ability to fetch user's membership from
groups of a different type by specifying multiple
group attribute to user attribute value matchers
in the Dex config:
userMatchers:
- userAttr: uid
groupAttr: memberUid
- userAttr: DN
groupAttr: member
In other words the user's groups can be fetched now from
ldap structure similar to the following:
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
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
Signed-off-by: Vitaliy Dmitriev <vi7alya@gmail.com>
2020-01-03 15:10:08 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
func TestGroupToUserMatchers(t *testing.T) {
|
|
|
|
c := &Config{}
|
2021-02-15 20:54:26 +05:30
|
|
|
c.UserSearch.BaseDN = "ou=People,ou=TestGroupToUserMatchers,dc=example,dc=org"
|
connector/ldap: add multiple user to group mapping
Add an ability to fetch user's membership from
groups of a different type by specifying multiple
group attribute to user attribute value matchers
in the Dex config:
userMatchers:
- userAttr: uid
groupAttr: memberUid
- userAttr: DN
groupAttr: member
In other words the user's groups can be fetched now from
ldap structure similar to the following:
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
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
Signed-off-by: Vitaliy Dmitriev <vi7alya@gmail.com>
2020-01-03 15:10:08 +05:30
|
|
|
c.UserSearch.NameAttr = "cn"
|
|
|
|
c.UserSearch.EmailAttr = "mail"
|
|
|
|
c.UserSearch.IDAttr = "DN"
|
|
|
|
c.UserSearch.Username = "cn"
|
2021-02-15 20:54:26 +05:30
|
|
|
c.GroupSearch.BaseDN = "ou=TestGroupToUserMatchers,dc=example,dc=org"
|
connector/ldap: add multiple user to group mapping
Add an ability to fetch user's membership from
groups of a different type by specifying multiple
group attribute to user attribute value matchers
in the Dex config:
userMatchers:
- userAttr: uid
groupAttr: memberUid
- userAttr: DN
groupAttr: member
In other words the user's groups can be fetched now from
ldap structure similar to the following:
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
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
Signed-off-by: Vitaliy Dmitriev <vi7alya@gmail.com>
2020-01-03 15:10:08 +05:30
|
|
|
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{
|
2021-02-15 20:54:26 +05:30
|
|
|
UserID: "cn=jane,ou=People,ou=TestGroupToUserMatchers,dc=example,dc=org",
|
connector/ldap: add multiple user to group mapping
Add an ability to fetch user's membership from
groups of a different type by specifying multiple
group attribute to user attribute value matchers
in the Dex config:
userMatchers:
- userAttr: uid
groupAttr: memberUid
- userAttr: DN
groupAttr: member
In other words the user's groups can be fetched now from
ldap structure similar to the following:
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
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
Signed-off-by: Vitaliy Dmitriev <vi7alya@gmail.com>
2020-01-03 15:10:08 +05:30
|
|
|
Username: "jane",
|
|
|
|
Email: "janedoe@example.com",
|
|
|
|
EmailVerified: true,
|
|
|
|
Groups: []string{"admins", "developers", "frontend"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "validpassword2",
|
|
|
|
username: "john",
|
|
|
|
password: "bar",
|
|
|
|
groups: true,
|
|
|
|
want: connector.Identity{
|
2021-02-15 20:54:26 +05:30
|
|
|
UserID: "cn=john,ou=People,ou=TestGroupToUserMatchers,dc=example,dc=org",
|
connector/ldap: add multiple user to group mapping
Add an ability to fetch user's membership from
groups of a different type by specifying multiple
group attribute to user attribute value matchers
in the Dex config:
userMatchers:
- userAttr: uid
groupAttr: memberUid
- userAttr: DN
groupAttr: member
In other words the user's groups can be fetched now from
ldap structure similar to the following:
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
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
Signed-off-by: Vitaliy Dmitriev <vi7alya@gmail.com>
2020-01-03 15:10:08 +05:30
|
|
|
Username: "john",
|
|
|
|
Email: "johndoe@example.com",
|
|
|
|
EmailVerified: true,
|
2021-02-15 20:54:26 +05:30
|
|
|
Groups: []string{"admins", "qa", "logger"},
|
connector/ldap: add multiple user to group mapping
Add an ability to fetch user's membership from
groups of a different type by specifying multiple
group attribute to user attribute value matchers
in the Dex config:
userMatchers:
- userAttr: uid
groupAttr: memberUid
- userAttr: DN
groupAttr: member
In other words the user's groups can be fetched now from
ldap structure similar to the following:
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
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
Signed-off-by: Vitaliy Dmitriev <vi7alya@gmail.com>
2020-01-03 15:10:08 +05:30
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2021-02-15 20:54:26 +05:30
|
|
|
runTests(t, connectLDAP, c, tests)
|
2020-01-07 19:25:13 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
// 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) {
|
|
|
|
c := &Config{}
|
2021-02-15 20:54:26 +05:30
|
|
|
c.UserSearch.BaseDN = "ou=People,ou=TestDeprecatedGroupToUserMatcher,dc=example,dc=org"
|
2020-01-07 19:25:13 +05:30
|
|
|
c.UserSearch.NameAttr = "cn"
|
|
|
|
c.UserSearch.EmailAttr = "mail"
|
|
|
|
c.UserSearch.IDAttr = "DN"
|
|
|
|
c.UserSearch.Username = "cn"
|
2021-02-15 20:54:26 +05:30
|
|
|
c.GroupSearch.BaseDN = "ou=TestDeprecatedGroupToUserMatcher,dc=example,dc=org"
|
2020-01-07 19:25:13 +05:30
|
|
|
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{
|
2021-02-15 20:54:26 +05:30
|
|
|
UserID: "cn=jane,ou=People,ou=TestDeprecatedGroupToUserMatcher,dc=example,dc=org",
|
2020-01-07 19:25:13 +05:30
|
|
|
Username: "jane",
|
|
|
|
Email: "janedoe@example.com",
|
|
|
|
EmailVerified: true,
|
|
|
|
Groups: []string{"admins", "developers"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "validpassword2",
|
|
|
|
username: "john",
|
|
|
|
password: "bar",
|
|
|
|
groups: true,
|
|
|
|
want: connector.Identity{
|
2021-02-15 20:54:26 +05:30
|
|
|
UserID: "cn=john,ou=People,ou=TestDeprecatedGroupToUserMatcher,dc=example,dc=org",
|
2020-01-07 19:25:13 +05:30
|
|
|
Username: "john",
|
|
|
|
Email: "johndoe@example.com",
|
|
|
|
EmailVerified: true,
|
|
|
|
Groups: []string{"admins"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2021-02-15 20:54:26 +05:30
|
|
|
runTests(t, connectLDAP, c, tests)
|
2018-06-14 13:40:15 +05:30
|
|
|
}
|
|
|
|
|
2017-04-13 02:43:34 +05:30
|
|
|
func TestStartTLS(t *testing.T) {
|
|
|
|
c := &Config{}
|
2021-02-15 20:54:26 +05:30
|
|
|
c.UserSearch.BaseDN = "ou=People,ou=TestStartTLS,dc=example,dc=org"
|
2017-04-13 02:43:34 +05:30
|
|
|
c.UserSearch.NameAttr = "cn"
|
|
|
|
c.UserSearch.EmailAttr = "mail"
|
|
|
|
c.UserSearch.IDAttr = "DN"
|
|
|
|
c.UserSearch.Username = "cn"
|
|
|
|
|
|
|
|
tests := []subtest{
|
|
|
|
{
|
|
|
|
name: "validpassword",
|
|
|
|
username: "jane",
|
|
|
|
password: "foo",
|
|
|
|
want: connector.Identity{
|
2021-02-15 20:54:26 +05:30
|
|
|
UserID: "cn=jane,ou=People,ou=TestStartTLS,dc=example,dc=org",
|
2017-04-13 02:43:34 +05:30
|
|
|
Username: "jane",
|
|
|
|
Email: "janedoe@example.com",
|
|
|
|
EmailVerified: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
2021-02-15 20:54:26 +05:30
|
|
|
runTests(t, connectStartTLS, c, tests)
|
2017-04-13 02:43:34 +05:30
|
|
|
}
|
|
|
|
|
2017-10-10 02:57:22 +05:30
|
|
|
func TestInsecureSkipVerify(t *testing.T) {
|
|
|
|
c := &Config{}
|
2021-02-15 20:54:26 +05:30
|
|
|
c.UserSearch.BaseDN = "ou=People,ou=TestInsecureSkipVerify,dc=example,dc=org"
|
2017-10-10 02:57:22 +05:30
|
|
|
c.UserSearch.NameAttr = "cn"
|
|
|
|
c.UserSearch.EmailAttr = "mail"
|
|
|
|
c.UserSearch.IDAttr = "DN"
|
|
|
|
c.UserSearch.Username = "cn"
|
|
|
|
|
|
|
|
tests := []subtest{
|
|
|
|
{
|
|
|
|
name: "validpassword",
|
|
|
|
username: "jane",
|
|
|
|
password: "foo",
|
|
|
|
want: connector.Identity{
|
2021-02-15 20:54:26 +05:30
|
|
|
UserID: "cn=jane,ou=People,ou=TestInsecureSkipVerify,dc=example,dc=org",
|
2017-10-10 02:57:22 +05:30
|
|
|
Username: "jane",
|
|
|
|
Email: "janedoe@example.com",
|
|
|
|
EmailVerified: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
2021-02-15 20:54:26 +05:30
|
|
|
runTests(t, connectInsecureSkipVerify, c, tests)
|
2017-10-10 02:57:22 +05:30
|
|
|
}
|
|
|
|
|
2017-04-13 02:43:34 +05:30
|
|
|
func TestLDAPS(t *testing.T) {
|
|
|
|
c := &Config{}
|
2021-02-15 20:54:26 +05:30
|
|
|
c.UserSearch.BaseDN = "ou=People,ou=TestLDAPS,dc=example,dc=org"
|
2017-04-13 02:43:34 +05:30
|
|
|
c.UserSearch.NameAttr = "cn"
|
|
|
|
c.UserSearch.EmailAttr = "mail"
|
|
|
|
c.UserSearch.IDAttr = "DN"
|
|
|
|
c.UserSearch.Username = "cn"
|
|
|
|
|
|
|
|
tests := []subtest{
|
|
|
|
{
|
|
|
|
name: "validpassword",
|
|
|
|
username: "jane",
|
|
|
|
password: "foo",
|
|
|
|
want: connector.Identity{
|
2021-02-15 20:54:26 +05:30
|
|
|
UserID: "cn=jane,ou=People,ou=TestLDAPS,dc=example,dc=org",
|
2017-04-13 02:43:34 +05:30
|
|
|
Username: "jane",
|
|
|
|
Email: "janedoe@example.com",
|
|
|
|
EmailVerified: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
2021-02-15 20:54:26 +05:30
|
|
|
runTests(t, connectLDAPS, c, tests)
|
connector/ldap: fix case where groups are listed on the user entity
Support schemas that determine membership by having fields on the
user entity, instead of listing users on a groups entity. E.g. the
following schema is now supported when it wasn't previously:
cn=eric,cn=user,dn=exapmle,dn=com
objectClass=myPerson
cn: eric
uid: eric
email: eric@example.com
memberOf: foo
memberOf: bar
cn=foo,cn=group,dn=exapmle,dn=com
objectClass=myGroup
cn: foo
cn=bar,cn=group,dn=exapmle,dn=com
objectClass=myGroup
cn: bar
2017-04-11 22:18:48 +05:30
|
|
|
}
|
|
|
|
|
2017-11-07 14:58:21 +05:30
|
|
|
func TestUsernamePrompt(t *testing.T) {
|
|
|
|
tests := map[string]struct {
|
|
|
|
config Config
|
|
|
|
expected string
|
|
|
|
}{
|
|
|
|
"with usernamePrompt unset it returns \"\"": {
|
|
|
|
config: Config{},
|
|
|
|
expected: "",
|
|
|
|
},
|
|
|
|
"with usernamePrompt set it returns that": {
|
|
|
|
config: Config{UsernamePrompt: "Email address"},
|
|
|
|
expected: "Email address",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for n, d := range tests {
|
|
|
|
t.Run(n, func(t *testing.T) {
|
|
|
|
conn := &ldapConnector{Config: d.config}
|
|
|
|
if actual := conn.Prompt(); actual != d.expected {
|
|
|
|
t.Errorf("expected %v, got %v", d.expected, actual)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-15 20:54:26 +05:30
|
|
|
func getenv(key, defaultVal string) string {
|
|
|
|
if val := os.Getenv(key); val != "" {
|
|
|
|
return val
|
|
|
|
}
|
|
|
|
return defaultVal
|
|
|
|
}
|
|
|
|
|
|
|
|
// runTests runs a set of tests against an LDAP schema.
|
2017-04-11 03:32:40 +05:30
|
|
|
//
|
2021-02-15 20:54:26 +05:30
|
|
|
// The tests require LDAP to be runnning.
|
|
|
|
// You can use the provided docker-compose file to setup an LDAP server.
|
|
|
|
func runTests(t *testing.T, connMethod connectionMethod, config *Config, tests []subtest) {
|
2021-02-20 18:35:41 +05:30
|
|
|
ldapHost := os.Getenv("DEX_LDAP_HOST")
|
|
|
|
if ldapHost == "" {
|
|
|
|
t.Skipf(`test environment variable "DEX_LDAP_HOST" not set, skipping`)
|
2021-02-20 14:25:52 +05:30
|
|
|
}
|
|
|
|
|
2017-04-11 03:32:40 +05:30
|
|
|
// Shallow copy.
|
|
|
|
c := *config
|
|
|
|
|
|
|
|
// We need to configure host parameters but don't want to overwrite user or
|
|
|
|
// group search configuration.
|
2017-04-13 02:43:34 +05:30
|
|
|
switch connMethod {
|
|
|
|
case connectStartTLS:
|
2021-02-20 18:35:41 +05:30
|
|
|
c.Host = fmt.Sprintf("%s:%s", ldapHost, getenv("DEX_LDAP_PORT", "389"))
|
2019-12-09 00:51:28 +05:30
|
|
|
c.RootCA = "testdata/certs/ca.crt"
|
2017-04-13 02:43:34 +05:30
|
|
|
c.StartTLS = true
|
|
|
|
case connectLDAPS:
|
2021-02-20 18:35:41 +05:30
|
|
|
c.Host = fmt.Sprintf("%s:%s", ldapHost, getenv("DEX_LDAP_TLS_PORT", "636"))
|
2019-12-09 00:51:28 +05:30
|
|
|
c.RootCA = "testdata/certs/ca.crt"
|
2017-10-10 02:57:22 +05:30
|
|
|
case connectInsecureSkipVerify:
|
2021-02-20 18:35:41 +05:30
|
|
|
c.Host = fmt.Sprintf("%s:%s", ldapHost, getenv("DEX_LDAP_TLS_PORT", "636"))
|
2017-10-10 02:57:22 +05:30
|
|
|
c.InsecureSkipVerify = true
|
2017-04-13 02:43:34 +05:30
|
|
|
case connectLDAP:
|
2021-02-20 18:35:41 +05:30
|
|
|
c.Host = fmt.Sprintf("%s:%s", ldapHost, getenv("DEX_LDAP_PORT", "389"))
|
2017-04-13 02:43:34 +05:30
|
|
|
c.InsecureNoSSL = true
|
|
|
|
}
|
|
|
|
|
2017-04-11 03:32:40 +05:30
|
|
|
c.BindDN = "cn=admin,dc=example,dc=org"
|
|
|
|
c.BindPW = "admin"
|
|
|
|
|
2021-09-17 11:42:39 +05:30
|
|
|
l := &logrus.Logger{Out: io.Discard, Formatter: &logrus.TextFormatter{}}
|
2017-04-11 03:32:40 +05:30
|
|
|
|
|
|
|
conn, err := c.openConnector(l)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("open connector: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range tests {
|
|
|
|
if test.name == "" {
|
|
|
|
t.Fatal("go a subtest with no name")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Run the subtest.
|
|
|
|
t.Run(test.name, func(t *testing.T) {
|
|
|
|
s := connector.Scopes{OfflineAccess: true, Groups: test.groups}
|
|
|
|
ident, validPW, err := conn.Login(context.Background(), s, test.username, test.password)
|
|
|
|
if err != nil {
|
|
|
|
if !test.wantErr {
|
|
|
|
t.Fatalf("query failed: %v", err)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if test.wantErr {
|
|
|
|
t.Fatalf("wanted query to fail")
|
|
|
|
}
|
|
|
|
|
|
|
|
if !validPW {
|
|
|
|
if !test.wantBadPW {
|
|
|
|
t.Fatalf("invalid password: %v", err)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if test.wantBadPW {
|
|
|
|
t.Fatalf("wanted invalid password")
|
|
|
|
}
|
|
|
|
got := ident
|
|
|
|
got.ConnectorData = nil
|
|
|
|
|
|
|
|
if diff := pretty.Compare(test.want, got); diff != "" {
|
|
|
|
t.Error(diff)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify that refresh tokens work.
|
|
|
|
ident, err = conn.Refresh(context.Background(), s, ident)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("refresh failed: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
got = ident
|
|
|
|
got.ConnectorData = nil
|
|
|
|
|
|
|
|
if diff := pretty.Compare(test.want, got); diff != "" {
|
|
|
|
t.Errorf("after refresh: %s", diff)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|