Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
D
dex
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
go
dex
Commits
4a93b55c
Commit
4a93b55c
authored
Apr 10, 2017
by
Eric Chiang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
connector/ldap: add LDAP integration tests
parent
943253fe
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
457 additions
and
0 deletions
+457
-0
ldap.go
connector/ldap/ldap.go
+4
-0
ldap_test.go
connector/ldap/ldap_test.go
+453
-0
No files found.
connector/ldap/ldap.go
View file @
4a93b55c
...
...
@@ -154,6 +154,10 @@ func (c *Config) OpenConnector(logger logrus.FieldLogger) (interface {
connector
.
PasswordConnector
connector
.
RefreshConnector
},
error
)
{
return
c
.
openConnector
(
logger
)
}
func
(
c
*
Config
)
openConnector
(
logger
logrus
.
FieldLogger
)
(
*
ldapConnector
,
error
)
{
requiredFields
:=
[]
struct
{
name
string
...
...
connector/ldap/ldap_test.go
0 → 100644
View file @
4a93b55c
package
ldap
import
(
"bytes"
"context"
"io/ioutil"
"net/url"
"os"
"os/exec"
"path/filepath"
"sync"
"testing"
"text/template"
"time"
"github.com/Sirupsen/logrus"
"github.com/kylelemons/godebug/pretty"
"github.com/coreos/dex/connector"
)
const
envVar
=
"DEX_LDAP_TESTS"
// 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
)
{
schema
:=
`
dn: dc=example,dc=org
objectClass: dcObject
objectClass: organization
o: Example Company
dc: example
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
`
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"
tests
:=
[]
subtest
{
{
name
:
"validpassword"
,
username
:
"jane"
,
password
:
"foo"
,
want
:
connector
.
Identity
{
UserID
:
"cn=jane,ou=People,dc=example,dc=org"
,
Username
:
"jane"
,
Email
:
"janedoe@example.com"
,
EmailVerified
:
true
,
},
},
{
name
:
"validpassword2"
,
username
:
"john"
,
password
:
"bar"
,
want
:
connector
.
Identity
{
UserID
:
"cn=john,ou=People,dc=example,dc=org"
,
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.
},
}
runTests
(
t
,
schema
,
c
,
tests
)
}
func
TestGroupQuery
(
t
*
testing
.
T
)
{
schema
:=
`
dn: dc=example,dc=org
objectClass: dcObject
objectClass: organization
o: Example Company
dc: example
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=Groups,dc=example,dc=org
objectClass: organizationalUnit
ou: Groups
dn: cn=admins,ou=Groups,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,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
=
"ou=Groups,dc=example,dc=org"
c
.
GroupSearch
.
UserAttr
=
"DN"
c
.
GroupSearch
.
GroupAttr
=
"member"
c
.
GroupSearch
.
NameAttr
=
"cn"
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
,
c
,
tests
)
}
// runTests runs a set of tests against an LDAP schema. It does this by
// setting up an OpenLDAP server and injecting the provided scheme.
//
// The tests require the slapd and ldapadd binaries available in the host
// machine's PATH.
//
// The DEX_LDAP_TESTS must be set to "1"
func
runTests
(
t
*
testing
.
T
,
schema
string
,
config
*
Config
,
tests
[]
subtest
)
{
if
os
.
Getenv
(
envVar
)
!=
"1"
{
t
.
Skipf
(
"%s not set. Skipping test (run 'export %s=1' to run tests)"
,
envVar
,
envVar
)
}
for
_
,
cmd
:=
range
[]
string
{
"slapd"
,
"ldapadd"
}
{
if
_
,
err
:=
exec
.
LookPath
(
cmd
);
err
!=
nil
{
t
.
Errorf
(
"%s not available"
,
cmd
)
}
}
tempDir
,
err
:=
ioutil
.
TempDir
(
""
,
""
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
defer
os
.
RemoveAll
(
tempDir
)
configBytes
:=
new
(
bytes
.
Buffer
)
if
err
:=
slapdConfigTmpl
.
Execute
(
configBytes
,
tmplData
{
tempDir
,
includes
(
t
)});
err
!=
nil
{
t
.
Fatal
(
err
)
}
configPath
:=
filepath
.
Join
(
tempDir
,
"ldap.conf"
)
if
err
:=
ioutil
.
WriteFile
(
configPath
,
configBytes
.
Bytes
(),
0644
);
err
!=
nil
{
t
.
Fatal
(
err
)
}
schemaPath
:=
filepath
.
Join
(
tempDir
,
"schema.ldap"
)
if
err
:=
ioutil
.
WriteFile
(
schemaPath
,
[]
byte
(
schema
),
0644
);
err
!=
nil
{
t
.
Fatal
(
err
)
}
socketPath
:=
url
.
QueryEscape
(
filepath
.
Join
(
tempDir
,
"ldap.unix"
))
slapdOut
:=
new
(
bytes
.
Buffer
)
cmd
:=
exec
.
Command
(
"slapd"
,
"-d"
,
"any"
,
"-h"
,
"ldap://localhost:10363/ ldaps://localhost:10636/ ldapi://"
+
socketPath
,
"-f"
,
configPath
,
)
cmd
.
Stdout
=
slapdOut
cmd
.
Stderr
=
slapdOut
if
err
:=
cmd
.
Start
();
err
!=
nil
{
t
.
Fatal
(
err
)
}
var
(
// Wait group finishes once slapd has exited.
//
// Use a wait group because multiple goroutines can't listen on
// cmd.Wait(). It triggers the race detector.
wg
=
new
(
sync
.
WaitGroup
)
// Ensure only one condition can set the slapdFailed boolean.
once
=
new
(
sync
.
Once
)
slapdFailed
bool
)
wg
.
Add
(
1
)
go
func
()
{
cmd
.
Wait
();
wg
.
Done
()
}()
defer
func
()
{
if
slapdFailed
{
// If slapd exited before it was killed, print its logs.
t
.
Logf
(
"%s
\n
"
,
slapdOut
)
}
}()
go
func
()
{
wg
.
Wait
()
once
.
Do
(
func
()
{
slapdFailed
=
true
})
}()
defer
func
()
{
once
.
Do
(
func
()
{
slapdFailed
=
false
})
cmd
.
Process
.
Kill
()
wg
.
Wait
()
}()
// Wait for slapd to come up.
time
.
Sleep
(
100
*
time
.
Millisecond
)
ldapadd
:=
exec
.
Command
(
"ldapadd"
,
"-x"
,
"-D"
,
"cn=admin,dc=example,dc=org"
,
"-w"
,
"admin"
,
"-f"
,
schemaPath
,
"-H"
,
"ldap://localhost:10363/"
,
)
if
out
,
err
:=
ldapadd
.
CombinedOutput
();
err
!=
nil
{
t
.
Errorf
(
"ldapadd: %s"
,
out
)
return
}
// Shallow copy.
c
:=
*
config
// We need to configure host parameters but don't want to overwrite user or
// group search configuration.
c
.
Host
=
"localhost:10363"
c
.
InsecureNoSSL
=
true
c
.
BindDN
=
"cn=admin,dc=example,dc=org"
c
.
BindPW
=
"admin"
l
:=
&
logrus
.
Logger
{
Out
:
ioutil
.
Discard
,
Formatter
:
&
logrus
.
TextFormatter
{}}
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
)
}
})
}
}
// Standard OpenLDAP schema files to include.
//
// These are copied from the /etc/openldap/schema directory.
var
includeFiles
=
[]
string
{
"core.schema"
,
"cosine.schema"
,
"inetorgperson.schema"
,
"misc.schema"
,
"nis.schema"
,
"openldap.schema"
,
}
// tmplData is the struct used to execute the SLAPD config template.
type
tmplData
struct
{
// Directory for database to be writen to.
TempDir
string
// List of schema files to include.
Includes
[]
string
}
// Config template copied from:
// http://www.zytrax.com/books/ldap/ch5/index.html#step1-slapd
var
slapdConfigTmpl
=
template
.
Must
(
template
.
New
(
""
)
.
Parse
(
`
{{ range $i, $include := .Includes }}
include {{ $include }}
{{ end }}
# MODULELOAD definitions
# not required (comment out) before version 2.3
moduleload back_bdb.la
database bdb
suffix "dc=example,dc=org"
# root or superuser
rootdn "cn=admin,dc=example,dc=org"
rootpw admin
# The database directory MUST exist prior to running slapd AND
# change path as necessary
directory {{ .TempDir }}
# Indices to maintain for this directory
# unique id so equality match only
index uid eq
# allows general searching on commonname, givenname and email
index cn,gn,mail eq,sub
# allows multiple variants on surname searching
index sn eq,sub
# sub above includes subintial,subany,subfinal
# optimise department searches
index ou eq
# if searches will include objectClass uncomment following
# index objectClass eq
# shows use of default index parameter
index default eq,sub
# indices missing - uses default eq,sub
index telephonenumber
# other database parameters
# read more in slapd.conf reference section
cachesize 10000
checkpoint 128 15
`
))
func
includes
(
t
*
testing
.
T
)
(
paths
[]
string
)
{
wd
,
err
:=
os
.
Getwd
()
if
err
!=
nil
{
t
.
Fatalf
(
"getting working directory: %v"
,
err
)
}
for
_
,
f
:=
range
includeFiles
{
p
:=
filepath
.
Join
(
wd
,
"testdata"
,
f
)
if
_
,
err
:=
os
.
Stat
(
p
);
err
!=
nil
{
t
.
Fatalf
(
"failed to find schema file: %s %v"
,
p
,
err
)
}
paths
=
append
(
paths
,
p
)
}
return
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment