2015-05-07 17:54:33 +00:00
package ldap
import (
2018-01-08 18:31:38 +00:00
"context"
2015-05-07 17:54:33 +00:00
"fmt"
2016-05-14 23:56:49 +00:00
"reflect"
2016-05-09 00:21:44 +00:00
"sort"
2015-05-07 17:54:33 +00:00
"testing"
2015-10-06 19:48:10 +00:00
"time"
2015-05-07 17:54:33 +00:00
2020-02-14 18:26:30 +00:00
goldap "github.com/go-ldap/ldap/v3"
2019-07-01 20:16:23 +00:00
"github.com/go-test/deep"
2020-02-14 18:26:30 +00:00
hclog "github.com/hashicorp/go-hclog"
2018-10-17 21:56:51 +00:00
"github.com/hashicorp/vault/helper/namespace"
2019-10-22 17:37:41 +00:00
"github.com/hashicorp/vault/helper/testhelpers/ldap"
2019-04-13 07:44:06 +00:00
logicaltest "github.com/hashicorp/vault/helper/testhelpers/logical"
2019-07-01 20:16:23 +00:00
"github.com/hashicorp/vault/sdk/helper/ldaputil"
2019-04-12 22:08:46 +00:00
"github.com/hashicorp/vault/sdk/helper/policyutil"
2019-07-01 20:16:23 +00:00
"github.com/hashicorp/vault/sdk/helper/tokenutil"
2019-04-12 21:54:35 +00:00
"github.com/hashicorp/vault/sdk/logical"
2015-05-07 17:54:33 +00:00
"github.com/mitchellh/mapstructure"
)
2017-01-23 15:56:43 +00:00
func createBackendWithStorage ( t * testing . T ) ( * backend , logical . Storage ) {
config := logical . TestBackendConfig ( )
config . StorageView = & logical . InmemStorage { }
b := Backend ( )
if b == nil {
t . Fatalf ( "failed to create backend" )
}
2018-01-19 06:44:44 +00:00
err := b . Backend . Setup ( context . Background ( ) , config )
2017-01-23 15:56:43 +00:00
if err != nil {
t . Fatal ( err )
}
return b , config . StorageView
}
2018-10-17 21:56:51 +00:00
func TestLdapAuthBackend_Listing ( t * testing . T ) {
b , storage := createBackendWithStorage ( t )
// Create group "testgroup"
resp , err := b . HandleRequest ( namespace . RootContext ( nil ) , & logical . Request {
Path : "groups/testgroup" ,
Operation : logical . UpdateOperation ,
Storage : storage ,
Data : map [ string ] interface { } {
"policies" : [ ] string { "default" } ,
} ,
} )
if err != nil || ( resp != nil && resp . IsError ( ) ) {
t . Fatalf ( "bad: resp: %#v\nerr: %v" , resp , err )
}
// Create group "nested/testgroup"
resp , err = b . HandleRequest ( namespace . RootContext ( nil ) , & logical . Request {
Path : "groups/nested/testgroup" ,
Operation : logical . UpdateOperation ,
Storage : storage ,
Data : map [ string ] interface { } {
"policies" : [ ] string { "default" } ,
} ,
} )
if err != nil || ( resp != nil && resp . IsError ( ) ) {
t . Fatalf ( "bad: resp: %#v\nerr: %v" , resp , err )
}
// Create user "testuser"
resp , err = b . HandleRequest ( namespace . RootContext ( nil ) , & logical . Request {
Path : "users/testuser" ,
Operation : logical . UpdateOperation ,
Storage : storage ,
Data : map [ string ] interface { } {
"policies" : [ ] string { "default" } ,
"groups" : "testgroup,nested/testgroup" ,
} ,
} )
if err != nil || ( resp != nil && resp . IsError ( ) ) {
t . Fatalf ( "bad: resp: %#v\nerr: %v" , resp , err )
}
// Create user "nested/testuser"
resp , err = b . HandleRequest ( namespace . RootContext ( nil ) , & logical . Request {
Path : "users/nested/testuser" ,
Operation : logical . UpdateOperation ,
Storage : storage ,
Data : map [ string ] interface { } {
"policies" : [ ] string { "default" } ,
"groups" : "testgroup,nested/testgroup" ,
} ,
} )
if err != nil || ( resp != nil && resp . IsError ( ) ) {
t . Fatalf ( "bad: resp: %#v\nerr: %v" , resp , err )
}
// List users
resp , err = b . HandleRequest ( namespace . RootContext ( nil ) , & logical . Request {
Path : "users/" ,
Operation : logical . ListOperation ,
Storage : storage ,
} )
if err != nil || ( resp != nil && resp . IsError ( ) ) {
t . Fatalf ( "bad: resp: %#v\nerr: %v" , resp , err )
}
expected := [ ] string { "testuser" , "nested/testuser" }
if ! reflect . DeepEqual ( expected , resp . Data [ "keys" ] . ( [ ] string ) ) {
t . Fatalf ( "bad: listed users; expected: %#v actual: %#v" , expected , resp . Data [ "keys" ] . ( [ ] string ) )
}
// List groups
resp , err = b . HandleRequest ( namespace . RootContext ( nil ) , & logical . Request {
Path : "groups/" ,
Operation : logical . ListOperation ,
Storage : storage ,
} )
if err != nil || ( resp != nil && resp . IsError ( ) ) {
t . Fatalf ( "bad: resp: %#v\nerr: %v" , resp , err )
}
expected = [ ] string { "testgroup" , "nested/testgroup" }
if ! reflect . DeepEqual ( expected , resp . Data [ "keys" ] . ( [ ] string ) ) {
t . Fatalf ( "bad: listed groups; expected: %#v actual: %#v" , expected , resp . Data [ "keys" ] . ( [ ] string ) )
}
}
2018-04-03 13:52:43 +00:00
func TestLdapAuthBackend_CaseSensitivity ( t * testing . T ) {
var resp * logical . Response
var err error
b , storage := createBackendWithStorage ( t )
ctx := context . Background ( )
testVals := func ( caseSensitive bool ) {
// Clear storage
userList , err := storage . List ( ctx , "user/" )
if err != nil {
t . Fatal ( err )
}
for _ , user := range userList {
err = storage . Delete ( ctx , "user/" + user )
if err != nil {
t . Fatal ( err )
}
}
groupList , err := storage . List ( ctx , "group/" )
if err != nil {
t . Fatal ( err )
}
for _ , group := range groupList {
err = storage . Delete ( ctx , "group/" + group )
if err != nil {
t . Fatal ( err )
}
}
configReq := & logical . Request {
Path : "config" ,
Operation : logical . ReadOperation ,
Storage : storage ,
}
resp , err = b . HandleRequest ( ctx , configReq )
if err != nil || ( resp != nil && resp . IsError ( ) ) {
t . Fatalf ( "err:%v resp:%#v" , err , resp )
}
if resp == nil {
t . Fatal ( "nil response" )
}
if resp . Data [ "case_sensitive_names" ] . ( bool ) != caseSensitive {
t . Fatalf ( "expected case sensitivity %t, got %t" , caseSensitive , resp . Data [ "case_sensitive_names" ] . ( bool ) )
}
groupReq := & logical . Request {
Operation : logical . UpdateOperation ,
Data : map [ string ] interface { } {
"policies" : "grouppolicy" ,
} ,
Path : "groups/EngineerS" ,
Storage : storage ,
}
resp , err = b . HandleRequest ( ctx , groupReq )
if err != nil || ( resp != nil && resp . IsError ( ) ) {
t . Fatalf ( "err:%v resp:%#v" , err , resp )
}
keys , err := storage . List ( ctx , "group/" )
if err != nil {
t . Fatal ( err )
}
switch caseSensitive {
case true :
if keys [ 0 ] != "EngineerS" {
t . Fatalf ( "bad: %s" , keys [ 0 ] )
}
default :
if keys [ 0 ] != "engineers" {
t . Fatalf ( "bad: %s" , keys [ 0 ] )
}
}
userReq := & logical . Request {
Operation : logical . UpdateOperation ,
Data : map [ string ] interface { } {
"groups" : "EngineerS" ,
"policies" : "userpolicy" ,
} ,
2019-10-22 17:37:41 +00:00
Path : "users/hermeS conRad" ,
2018-04-03 13:52:43 +00:00
Storage : storage ,
}
resp , err = b . HandleRequest ( ctx , userReq )
if err != nil || ( resp != nil && resp . IsError ( ) ) {
t . Fatalf ( "err:%v resp:%#v" , err , resp )
}
keys , err = storage . List ( ctx , "user/" )
if err != nil {
t . Fatal ( err )
}
switch caseSensitive {
case true :
2019-10-22 17:37:41 +00:00
if keys [ 0 ] != "hermeS conRad" {
2018-04-03 13:52:43 +00:00
t . Fatalf ( "bad: %s" , keys [ 0 ] )
}
default :
2019-10-22 17:37:41 +00:00
if keys [ 0 ] != "hermes conrad" {
2018-04-03 13:52:43 +00:00
t . Fatalf ( "bad: %s" , keys [ 0 ] )
}
}
if caseSensitive {
// The online test server is actually case sensitive so we need to
// write again so it works
userReq = & logical . Request {
Operation : logical . UpdateOperation ,
Data : map [ string ] interface { } {
"groups" : "EngineerS" ,
"policies" : "userpolicy" ,
} ,
2019-10-22 17:37:41 +00:00
Path : "users/Hermes Conrad" ,
2019-07-03 01:01:34 +00:00
Storage : storage ,
Connection : & logical . Connection { } ,
2018-04-03 13:52:43 +00:00
}
resp , err = b . HandleRequest ( ctx , userReq )
if err != nil || ( resp != nil && resp . IsError ( ) ) {
t . Fatalf ( "err:%v resp:%#v" , err , resp )
}
}
loginReq := & logical . Request {
Operation : logical . UpdateOperation ,
2019-10-22 17:37:41 +00:00
Path : "login/Hermes Conrad" ,
2018-04-03 13:52:43 +00:00
Data : map [ string ] interface { } {
2019-10-22 17:37:41 +00:00
"password" : "hermes" ,
2018-04-03 13:52:43 +00:00
} ,
2019-07-03 01:01:34 +00:00
Storage : storage ,
Connection : & logical . Connection { } ,
2018-04-03 13:52:43 +00:00
}
resp , err = b . HandleRequest ( ctx , loginReq )
if err != nil || ( resp != nil && resp . IsError ( ) ) {
t . Fatalf ( "err:%v resp:%#v" , err , resp )
}
expected := [ ] string { "grouppolicy" , "userpolicy" }
if ! reflect . DeepEqual ( expected , resp . Auth . Policies ) {
t . Fatalf ( "bad: policies: expected: %q, actual: %q" , expected , resp . Auth . Policies )
}
}
2019-10-22 17:37:41 +00:00
cleanup , cfg := ldap . PrepareTestContainer ( t , "latest" )
defer cleanup ( )
2018-04-03 13:52:43 +00:00
configReq := & logical . Request {
Operation : logical . UpdateOperation ,
Path : "config" ,
Data : map [ string ] interface { } {
2019-10-22 17:37:41 +00:00
"url" : cfg . Url ,
"userattr" : cfg . UserAttr ,
"userdn" : cfg . UserDN ,
"groupdn" : cfg . GroupDN ,
"groupattr" : cfg . GroupAttr ,
"binddn" : cfg . BindDN ,
"bindpass" : cfg . BindPassword ,
2018-04-03 13:52:43 +00:00
} ,
Storage : storage ,
}
resp , err = b . HandleRequest ( ctx , configReq )
if err != nil || ( resp != nil && resp . IsError ( ) ) {
t . Fatalf ( "err:%v resp:%#v" , err , resp )
}
testVals ( false )
// Check that if the value is nil, on read it is case sensitive
configEntry , err := b . Config ( ctx , configReq )
if err != nil {
t . Fatal ( err )
}
configEntry . CaseSensitiveNames = nil
entry , err := logical . StorageEntryJSON ( "config" , configEntry )
if err != nil {
t . Fatal ( err )
}
err = configReq . Storage . Put ( ctx , entry )
if err != nil {
t . Fatal ( err )
}
testVals ( true )
}
2017-01-23 15:56:43 +00:00
func TestLdapAuthBackend_UserPolicies ( t * testing . T ) {
var resp * logical . Response
var err error
b , storage := createBackendWithStorage ( t )
2019-10-22 17:37:41 +00:00
cleanup , cfg := ldap . PrepareTestContainer ( t , "latest" )
defer cleanup ( )
2017-01-23 15:56:43 +00:00
configReq := & logical . Request {
Operation : logical . UpdateOperation ,
Path : "config" ,
Data : map [ string ] interface { } {
2019-10-22 17:37:41 +00:00
"url" : cfg . Url ,
"userattr" : cfg . UserAttr ,
"userdn" : cfg . UserDN ,
"groupdn" : cfg . GroupDN ,
"groupattr" : cfg . GroupAttr ,
"binddn" : cfg . BindDN ,
"bindpassword" : cfg . BindPassword ,
2017-01-23 15:56:43 +00:00
} ,
Storage : storage ,
}
2018-01-08 18:31:38 +00:00
resp , err = b . HandleRequest ( context . Background ( ) , configReq )
2017-01-23 15:56:43 +00:00
if err != nil || ( resp != nil && resp . IsError ( ) ) {
t . Fatalf ( "err:%v resp:%#v" , err , resp )
}
groupReq := & logical . Request {
Operation : logical . UpdateOperation ,
Data : map [ string ] interface { } {
"policies" : "grouppolicy" ,
} ,
2019-07-03 01:01:34 +00:00
Path : "groups/engineers" ,
Storage : storage ,
Connection : & logical . Connection { } ,
2017-01-23 15:56:43 +00:00
}
2018-01-08 18:31:38 +00:00
resp , err = b . HandleRequest ( context . Background ( ) , groupReq )
2017-01-23 15:56:43 +00:00
if err != nil || ( resp != nil && resp . IsError ( ) ) {
t . Fatalf ( "err:%v resp:%#v" , err , resp )
}
userReq := & logical . Request {
Operation : logical . UpdateOperation ,
Data : map [ string ] interface { } {
"groups" : "engineers" ,
"policies" : "userpolicy" ,
} ,
2019-10-22 17:37:41 +00:00
Path : "users/hermes conrad" ,
2019-07-03 01:01:34 +00:00
Storage : storage ,
Connection : & logical . Connection { } ,
2017-01-23 15:56:43 +00:00
}
2018-01-08 18:31:38 +00:00
resp , err = b . HandleRequest ( context . Background ( ) , userReq )
2017-01-23 15:56:43 +00:00
if err != nil || ( resp != nil && resp . IsError ( ) ) {
t . Fatalf ( "err:%v resp:%#v" , err , resp )
}
loginReq := & logical . Request {
Operation : logical . UpdateOperation ,
2019-10-22 17:37:41 +00:00
Path : "login/hermes conrad" ,
2017-01-23 15:56:43 +00:00
Data : map [ string ] interface { } {
2019-10-22 17:37:41 +00:00
"password" : "hermes" ,
2017-01-23 15:56:43 +00:00
} ,
2019-07-03 01:01:34 +00:00
Storage : storage ,
Connection : & logical . Connection { } ,
2017-01-23 15:56:43 +00:00
}
2018-01-08 18:31:38 +00:00
resp , err = b . HandleRequest ( context . Background ( ) , loginReq )
2017-01-23 15:56:43 +00:00
if err != nil || ( resp != nil && resp . IsError ( ) ) {
t . Fatalf ( "err:%v resp:%#v" , err , resp )
}
2017-09-13 15:36:52 +00:00
expected := [ ] string { "grouppolicy" , "userpolicy" }
2017-01-23 15:56:43 +00:00
if ! reflect . DeepEqual ( expected , resp . Auth . Policies ) {
t . Fatalf ( "bad: policies: expected: %q, actual: %q" , expected , resp . Auth . Policies )
}
}
2016-05-09 00:21:44 +00:00
/ *
2017-09-13 01:48:52 +00:00
* Acceptance test for LDAP Auth Method
2016-05-09 00:21:44 +00:00
*
2019-10-22 17:37:41 +00:00
* The tests here rely on a docker LDAP server :
* [ https : //github.com/rroemhild/docker-test-openldap]
2016-05-09 00:21:44 +00:00
*
2019-10-22 17:37:41 +00:00
* ... as well as existence of a person object , ` cn=Hermes Conrad,dc=example,dc=com ` ,
* which is a member of a group , ` cn=admin_staff,ou=people,dc=example,dc=com `
2016-05-09 00:21:44 +00:00
*
* Querying the server from the command line :
2019-10-22 17:37:41 +00:00
* $ docker run -- privileged - d - p 389 : 389 -- name ldap -- rm rroemhild / test - openldap
* $ ldapsearch - x - H ldap : //localhost -b dc=planetexpress,dc=com -s sub uid=hermes
* $ ldapsearch - x - H ldap : //localhost -b dc=planetexpress,dc=com -s sub \
' member = cn = Hermes Conrad , ou = people , dc = planetexpress , dc = com '
* /
2015-10-06 19:48:10 +00:00
func factory ( t * testing . T ) logical . Backend {
defaultLeaseTTLVal := time . Hour * 24
2016-09-28 22:32:49 +00:00
maxLeaseTTLVal := time . Hour * 24 * 32
2018-01-19 06:44:44 +00:00
b , err := Factory ( context . Background ( ) , & logical . BackendConfig {
2020-02-14 18:26:30 +00:00
Logger : hclog . New ( & hclog . LoggerOptions {
Name : "FactoryLogger" ,
Level : hclog . Debug ,
} ) ,
2015-10-06 19:48:10 +00:00
System : & logical . StaticSystemView {
DefaultLeaseTTLVal : defaultLeaseTTLVal ,
MaxLeaseTTLVal : maxLeaseTTLVal ,
} ,
} )
if err != nil {
t . Fatalf ( "Unable to create backend: %s" , err )
}
return b
}
2015-05-07 17:54:33 +00:00
func TestBackend_basic ( t * testing . T ) {
2015-10-06 19:48:10 +00:00
b := factory ( t )
2019-10-22 17:37:41 +00:00
cleanup , cfg := ldap . PrepareTestContainer ( t , "latest" )
defer cleanup ( )
2015-05-07 17:54:33 +00:00
logicaltest . Test ( t , logicaltest . TestCase {
2018-11-07 01:21:24 +00:00
CredentialBackend : b ,
2015-05-07 17:54:33 +00:00
Steps : [ ] logicaltest . TestStep {
2019-10-22 17:37:41 +00:00
testAccStepConfigUrl ( t , cfg ) ,
// Map Admin_staff group (from LDAP server) with foo policy
testAccStepGroup ( t , "admin_staff" , "foo" ) ,
2016-05-09 00:21:44 +00:00
// Map engineers group (local) with bar policy
2015-07-17 21:40:06 +00:00
testAccStepGroup ( t , "engineers" , "bar" ) ,
2016-05-09 00:21:44 +00:00
2019-10-22 17:37:41 +00:00
// Map hermes conrad user with local engineers group
testAccStepUser ( t , "hermes conrad" , "engineers" ) ,
2016-05-09 00:21:44 +00:00
// Authenticate
2019-10-22 17:37:41 +00:00
testAccStepLogin ( t , "hermes conrad" , "hermes" ) ,
2016-05-09 00:21:44 +00:00
// Verify both groups mappings can be listed back
2019-10-22 17:37:41 +00:00
testAccStepGroupList ( t , [ ] string { "engineers" , "admin_staff" } ) ,
2016-05-09 00:21:44 +00:00
// Verify user mapping can be listed back
2019-10-22 17:37:41 +00:00
testAccStepUserList ( t , [ ] string { "hermes conrad" } ) ,
2015-05-07 17:54:33 +00:00
} ,
} )
}
2018-06-20 03:00:22 +00:00
func TestBackend_basic_noPolicies ( t * testing . T ) {
b := factory ( t )
2019-10-22 17:37:41 +00:00
cleanup , cfg := ldap . PrepareTestContainer ( t , "latest" )
defer cleanup ( )
2018-06-20 03:00:22 +00:00
logicaltest . Test ( t , logicaltest . TestCase {
2018-11-07 01:21:24 +00:00
CredentialBackend : b ,
2018-06-20 03:00:22 +00:00
Steps : [ ] logicaltest . TestStep {
2019-10-22 17:37:41 +00:00
testAccStepConfigUrl ( t , cfg ) ,
2018-06-20 03:00:22 +00:00
// Create LDAP user
2019-10-22 17:37:41 +00:00
testAccStepUser ( t , "hermes conrad" , "" ) ,
2018-06-20 03:00:22 +00:00
// Authenticate
2019-10-22 17:37:41 +00:00
testAccStepLoginNoAttachedPolicies ( t , "hermes conrad" , "hermes" ) ,
testAccStepUserList ( t , [ ] string { "hermes conrad" } ) ,
2018-06-20 03:00:22 +00:00
} ,
} )
}
func TestBackend_basic_group_noPolicies ( t * testing . T ) {
b := factory ( t )
2019-10-22 17:37:41 +00:00
cleanup , cfg := ldap . PrepareTestContainer ( t , "latest" )
defer cleanup ( )
2018-06-20 03:00:22 +00:00
logicaltest . Test ( t , logicaltest . TestCase {
2018-11-07 01:21:24 +00:00
CredentialBackend : b ,
2018-06-20 03:00:22 +00:00
Steps : [ ] logicaltest . TestStep {
2019-10-22 17:37:41 +00:00
testAccStepConfigUrl ( t , cfg ) ,
2018-06-20 03:00:22 +00:00
// Create engineers group with no policies
testAccStepGroup ( t , "engineers" , "" ) ,
2019-10-22 17:37:41 +00:00
// Map hermes conrad user with local engineers group
testAccStepUser ( t , "hermes conrad" , "engineers" ) ,
2018-06-20 03:00:22 +00:00
// Authenticate
2019-10-22 17:37:41 +00:00
testAccStepLoginNoAttachedPolicies ( t , "hermes conrad" , "hermes" ) ,
2018-06-20 03:00:22 +00:00
// Verify group mapping can be listed back
testAccStepGroupList ( t , [ ] string { "engineers" } ) ,
} ,
} )
}
2016-02-19 16:38:27 +00:00
func TestBackend_basic_authbind ( t * testing . T ) {
b := factory ( t )
2019-10-22 17:37:41 +00:00
cleanup , cfg := ldap . PrepareTestContainer ( t , "latest" )
defer cleanup ( )
2016-02-19 16:38:27 +00:00
logicaltest . Test ( t , logicaltest . TestCase {
2018-11-07 01:21:24 +00:00
CredentialBackend : b ,
2016-02-19 16:38:27 +00:00
Steps : [ ] logicaltest . TestStep {
2019-10-22 17:37:41 +00:00
testAccStepConfigUrlWithAuthBind ( t , cfg ) ,
testAccStepGroup ( t , "admin_staff" , "foo" ) ,
2016-02-19 16:38:27 +00:00
testAccStepGroup ( t , "engineers" , "bar" ) ,
2019-10-22 17:37:41 +00:00
testAccStepUser ( t , "hermes conrad" , "engineers" ) ,
testAccStepLogin ( t , "hermes conrad" , "hermes" ) ,
2016-02-19 16:38:27 +00:00
} ,
} )
}
2020-02-14 18:26:30 +00:00
func TestBackend_basic_authbind_upndomain ( t * testing . T ) {
b := factory ( t )
cleanup , cfg := ldap . PrepareTestContainer ( t , "latest" )
defer cleanup ( )
cfg . UPNDomain = "planetexpress.com"
// Setup connection
client := & ldaputil . Client {
Logger : hclog . New ( & hclog . LoggerOptions {
Name : "LDAPAuthTest" ,
Level : hclog . Debug ,
} ) ,
LDAP : ldaputil . NewLDAP ( ) ,
}
conn , err := client . DialLDAP ( cfg )
if err != nil {
t . Fatal ( err )
}
defer conn . Close ( )
if err := conn . Bind ( "cn=admin,cn=config" , cfg . BindPassword ) ; err != nil {
t . Fatal ( err )
}
// Add userPrincipalName attribute type
userPrincipleNameTypeReq := goldap . NewModifyRequest ( "cn={0}core,cn=schema,cn=config" , nil )
userPrincipleNameTypeReq . Add ( "olcAttributetypes" , [ ] string { "( 2.25.247072656268950430024439664556757516066 NAME ( 'userPrincipalName' ) SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 EQUALITY caseIgnoreMatch SINGLE-VALUE )" } )
if err := conn . Modify ( userPrincipleNameTypeReq ) ; err != nil {
t . Fatal ( err )
}
// Add new object class
userPrincipleNameObjClassReq := goldap . NewModifyRequest ( "cn={0}core,cn=schema,cn=config" , nil )
userPrincipleNameObjClassReq . Add ( "olcObjectClasses" , [ ] string { "( 1.2.840.113556.6.2.6 NAME 'PrincipalNameClass' AUXILIARY MAY ( userPrincipalName ) )" } )
if err := conn . Modify ( userPrincipleNameObjClassReq ) ; err != nil {
t . Fatal ( err )
}
// Re-authenticate with the binddn user
if err := conn . Bind ( cfg . BindDN , cfg . BindPassword ) ; err != nil {
t . Fatal ( err )
}
// Modify professor user and add userPrincipalName attribute
profDN := "cn=Hubert J. Farnsworth,ou=people,dc=planetexpress,dc=com"
modifyUserReq := goldap . NewModifyRequest ( profDN , nil )
modifyUserReq . Add ( "objectClass" , [ ] string { "PrincipalNameClass" } )
modifyUserReq . Add ( "userPrincipalName" , [ ] string { "professor@planetexpress.com" } )
if err := conn . Modify ( modifyUserReq ) ; err != nil {
t . Fatal ( err )
}
logicaltest . Test ( t , logicaltest . TestCase {
CredentialBackend : b ,
Steps : [ ] logicaltest . TestStep {
testAccStepConfigUrlWithAuthBind ( t , cfg ) ,
testAccStepLoginNoAttachedPolicies ( t , "professor" , "professor" ) ,
} ,
} )
}
2016-02-19 16:46:59 +00:00
func TestBackend_basic_discover ( t * testing . T ) {
b := factory ( t )
2019-10-22 17:37:41 +00:00
cleanup , cfg := ldap . PrepareTestContainer ( t , "latest" )
defer cleanup ( )
2016-02-19 16:46:59 +00:00
logicaltest . Test ( t , logicaltest . TestCase {
2018-11-07 01:21:24 +00:00
CredentialBackend : b ,
2016-02-19 16:46:59 +00:00
Steps : [ ] logicaltest . TestStep {
2019-10-22 17:37:41 +00:00
testAccStepConfigUrlWithDiscover ( t , cfg ) ,
testAccStepGroup ( t , "admin_staff" , "foo" ) ,
2016-02-19 16:46:59 +00:00
testAccStepGroup ( t , "engineers" , "bar" ) ,
2019-10-22 17:37:41 +00:00
testAccStepUser ( t , "hermes conrad" , "engineers" ) ,
testAccStepLogin ( t , "hermes conrad" , "hermes" ) ,
2016-02-19 16:46:59 +00:00
} ,
} )
}
2016-04-02 17:11:36 +00:00
func TestBackend_basic_nogroupdn ( t * testing . T ) {
b := factory ( t )
2019-10-22 17:37:41 +00:00
cleanup , cfg := ldap . PrepareTestContainer ( t , "latest" )
defer cleanup ( )
2016-04-02 17:11:36 +00:00
logicaltest . Test ( t , logicaltest . TestCase {
2018-11-07 01:21:24 +00:00
CredentialBackend : b ,
2016-04-02 17:11:36 +00:00
Steps : [ ] logicaltest . TestStep {
2019-10-22 17:37:41 +00:00
testAccStepConfigUrlNoGroupDN ( t , cfg ) ,
testAccStepGroup ( t , "admin_staff" , "foo" ) ,
2016-04-02 17:11:36 +00:00
testAccStepGroup ( t , "engineers" , "bar" ) ,
2019-10-22 17:37:41 +00:00
testAccStepUser ( t , "hermes conrad" , "engineers" ) ,
testAccStepLoginNoGroupDN ( t , "hermes conrad" , "hermes" ) ,
2016-04-02 17:11:36 +00:00
} ,
} )
}
2015-05-07 17:54:33 +00:00
func TestBackend_groupCrud ( t * testing . T ) {
2015-10-06 19:48:10 +00:00
b := factory ( t )
2015-05-07 17:54:33 +00:00
logicaltest . Test ( t , logicaltest . TestCase {
2018-11-07 01:21:24 +00:00
CredentialBackend : b ,
2015-05-07 17:54:33 +00:00
Steps : [ ] logicaltest . TestStep {
testAccStepGroup ( t , "g1" , "foo" ) ,
2017-09-13 15:36:52 +00:00
testAccStepReadGroup ( t , "g1" , "foo" ) ,
2015-05-07 17:54:33 +00:00
testAccStepDeleteGroup ( t , "g1" ) ,
testAccStepReadGroup ( t , "g1" , "" ) ,
} ,
} )
}
2016-05-09 00:21:44 +00:00
/ *
* Test backend configuration defaults are successfully read .
* /
func TestBackend_configDefaultsAfterUpdate ( t * testing . T ) {
b := factory ( t )
logicaltest . Test ( t , logicaltest . TestCase {
2018-11-07 01:21:24 +00:00
CredentialBackend : b ,
2016-05-09 00:21:44 +00:00
Steps : [ ] logicaltest . TestStep {
logicaltest . TestStep {
Operation : logical . UpdateOperation ,
Path : "config" ,
Data : map [ string ] interface { } { } ,
} ,
logicaltest . TestStep {
Operation : logical . ReadOperation ,
Path : "config" ,
Check : func ( resp * logical . Response ) error {
if resp == nil {
return fmt . Errorf ( "bad: %#v" , resp )
}
// Test well-known defaults
cfg := resp . Data
defaultGroupFilter := "(|(memberUid={{.Username}})(member={{.UserDN}})(uniqueMember={{.UserDN}}))"
if cfg [ "groupfilter" ] != defaultGroupFilter {
t . Errorf ( "Default mismatch: groupfilter. Expected: '%s', received :'%s'" , defaultGroupFilter , cfg [ "groupfilter" ] )
}
defaultGroupAttr := "cn"
if cfg [ "groupattr" ] != defaultGroupAttr {
t . Errorf ( "Default mismatch: groupattr. Expected: '%s', received :'%s'" , defaultGroupAttr , cfg [ "groupattr" ] )
}
defaultUserAttr := "cn"
if cfg [ "userattr" ] != defaultUserAttr {
t . Errorf ( "Default mismatch: userattr. Expected: '%s', received :'%s'" , defaultUserAttr , cfg [ "userattr" ] )
}
2016-12-01 18:11:40 +00:00
defaultDenyNullBind := true
if cfg [ "deny_null_bind" ] != defaultDenyNullBind {
2018-02-05 19:26:31 +00:00
t . Errorf ( "Default mismatch: deny_null_bind. Expected: '%t', received :'%s'" , defaultDenyNullBind , cfg [ "deny_null_bind" ] )
2016-12-01 18:11:40 +00:00
}
2016-05-09 00:21:44 +00:00
return nil
} ,
} ,
} ,
} )
}
2019-10-22 17:37:41 +00:00
func testAccStepConfigUrl ( t * testing . T , cfg * ldaputil . ConfigEntry ) logicaltest . TestStep {
2015-05-07 17:54:33 +00:00
return logicaltest . TestStep {
2016-01-07 15:30:47 +00:00
Operation : logical . UpdateOperation ,
2015-05-07 17:54:33 +00:00
Path : "config" ,
Data : map [ string ] interface { } {
2019-10-22 17:37:41 +00:00
"url" : cfg . Url ,
"userattr" : cfg . UserAttr ,
"userdn" : cfg . UserDN ,
"groupdn" : cfg . GroupDN ,
"groupattr" : cfg . GroupAttr ,
"binddn" : cfg . BindDN ,
"bindpass" : cfg . BindPassword ,
2018-04-03 13:52:43 +00:00
"case_sensitive_names" : true ,
2019-07-01 20:16:23 +00:00
"token_policies" : "abc,xyz" ,
2019-11-20 19:26:13 +00:00
"request_timeout" : cfg . RequestTimeout ,
2015-05-07 17:54:33 +00:00
} ,
}
}
2019-10-22 17:37:41 +00:00
func testAccStepConfigUrlWithAuthBind ( t * testing . T , cfg * ldaputil . ConfigEntry ) logicaltest . TestStep {
2016-02-19 16:38:27 +00:00
return logicaltest . TestStep {
Operation : logical . UpdateOperation ,
Path : "config" ,
Data : map [ string ] interface { } {
2017-02-08 22:59:24 +00:00
// In this test we also exercise multiple URL support
2019-10-22 17:37:41 +00:00
"url" : "foobar://ldap.example.com," + cfg . Url ,
"userattr" : cfg . UserAttr ,
"userdn" : cfg . UserDN ,
"groupdn" : cfg . GroupDN ,
"groupattr" : cfg . GroupAttr ,
"binddn" : cfg . BindDN ,
"bindpass" : cfg . BindPassword ,
2020-02-14 18:26:30 +00:00
"upndomain" : cfg . UPNDomain ,
2018-04-03 13:52:43 +00:00
"case_sensitive_names" : true ,
2019-07-01 20:16:23 +00:00
"token_policies" : "abc,xyz" ,
2019-11-20 19:26:13 +00:00
"request_timeout" : cfg . RequestTimeout ,
2016-02-19 16:38:27 +00:00
} ,
}
}
2019-10-22 17:37:41 +00:00
func testAccStepConfigUrlWithDiscover ( t * testing . T , cfg * ldaputil . ConfigEntry ) logicaltest . TestStep {
2016-02-19 16:46:59 +00:00
return logicaltest . TestStep {
Operation : logical . UpdateOperation ,
Path : "config" ,
Data : map [ string ] interface { } {
2019-10-22 17:37:41 +00:00
"url" : cfg . Url ,
"userattr" : cfg . UserAttr ,
"userdn" : cfg . UserDN ,
"groupdn" : cfg . GroupDN ,
"groupattr" : cfg . GroupAttr ,
"binddn" : cfg . BindDN ,
"bindpass" : cfg . BindPassword ,
2018-04-03 13:52:43 +00:00
"discoverdn" : true ,
"case_sensitive_names" : true ,
2019-07-01 20:16:23 +00:00
"token_policies" : "abc,xyz" ,
2019-11-20 19:26:13 +00:00
"request_timeout" : cfg . RequestTimeout ,
2016-02-19 16:46:59 +00:00
} ,
}
}
2019-10-22 17:37:41 +00:00
func testAccStepConfigUrlNoGroupDN ( t * testing . T , cfg * ldaputil . ConfigEntry ) logicaltest . TestStep {
2016-04-02 17:11:36 +00:00
return logicaltest . TestStep {
Operation : logical . UpdateOperation ,
Path : "config" ,
Data : map [ string ] interface { } {
2019-10-22 17:37:41 +00:00
"url" : cfg . Url ,
"userattr" : cfg . UserAttr ,
"userdn" : cfg . UserDN ,
"binddn" : cfg . BindDN ,
"bindpass" : cfg . BindPassword ,
2018-04-03 13:52:43 +00:00
"discoverdn" : true ,
"case_sensitive_names" : true ,
2019-11-20 19:26:13 +00:00
"request_timeout" : cfg . RequestTimeout ,
2016-04-02 17:11:36 +00:00
} ,
}
}
2015-05-07 17:54:33 +00:00
func testAccStepGroup ( t * testing . T , group string , policies string ) logicaltest . TestStep {
2016-05-09 00:21:44 +00:00
t . Logf ( "[testAccStepGroup] - Registering group %s, policy %s" , group , policies )
2015-05-07 17:54:33 +00:00
return logicaltest . TestStep {
2016-01-07 15:30:47 +00:00
Operation : logical . UpdateOperation ,
2015-05-07 17:54:33 +00:00
Path : "groups/" + group ,
Data : map [ string ] interface { } {
"policies" : policies ,
} ,
}
}
func testAccStepReadGroup ( t * testing . T , group string , policies string ) logicaltest . TestStep {
return logicaltest . TestStep {
Operation : logical . ReadOperation ,
Path : "groups/" + group ,
Check : func ( resp * logical . Response ) error {
if resp == nil {
if policies == "" {
return nil
}
return fmt . Errorf ( "bad: %#v" , resp )
}
var d struct {
2017-09-13 15:36:52 +00:00
Policies [ ] string ` mapstructure:"policies" `
2015-05-07 17:54:33 +00:00
}
if err := mapstructure . Decode ( resp . Data , & d ) ; err != nil {
return err
}
2017-09-13 15:36:52 +00:00
if ! reflect . DeepEqual ( d . Policies , policyutil . ParsePolicies ( policies ) ) {
2015-05-07 17:54:33 +00:00
return fmt . Errorf ( "bad: %#v" , resp )
}
return nil
} ,
}
}
func testAccStepDeleteGroup ( t * testing . T , group string ) logicaltest . TestStep {
return logicaltest . TestStep {
Operation : logical . DeleteOperation ,
Path : "groups/" + group ,
}
}
2015-07-14 22:46:15 +00:00
func TestBackend_userCrud ( t * testing . T ) {
b := Backend ( )
logicaltest . Test ( t , logicaltest . TestCase {
2018-11-07 01:21:24 +00:00
CredentialBackend : b ,
2015-07-14 22:46:15 +00:00
Steps : [ ] logicaltest . TestStep {
testAccStepUser ( t , "g1" , "bar" ) ,
testAccStepReadUser ( t , "g1" , "bar" ) ,
testAccStepDeleteUser ( t , "g1" ) ,
testAccStepReadUser ( t , "g1" , "" ) ,
} ,
} )
}
2015-07-17 21:40:06 +00:00
func testAccStepUser ( t * testing . T , user string , groups string ) logicaltest . TestStep {
2015-07-14 22:46:15 +00:00
return logicaltest . TestStep {
2016-01-07 15:30:47 +00:00
Operation : logical . UpdateOperation ,
2015-07-14 22:46:15 +00:00
Path : "users/" + user ,
Data : map [ string ] interface { } {
2015-07-17 21:40:06 +00:00
"groups" : groups ,
2015-07-14 22:46:15 +00:00
} ,
}
}
2015-07-17 21:40:06 +00:00
func testAccStepReadUser ( t * testing . T , user string , groups string ) logicaltest . TestStep {
2015-07-14 22:46:15 +00:00
return logicaltest . TestStep {
Operation : logical . ReadOperation ,
Path : "users/" + user ,
Check : func ( resp * logical . Response ) error {
if resp == nil {
2015-07-17 21:40:06 +00:00
if groups == "" {
2015-07-14 22:46:15 +00:00
return nil
}
return fmt . Errorf ( "bad: %#v" , resp )
}
var d struct {
2015-07-17 21:40:06 +00:00
Groups string ` mapstructure:"groups" `
2015-07-14 22:46:15 +00:00
}
if err := mapstructure . Decode ( resp . Data , & d ) ; err != nil {
return err
}
2015-07-17 21:40:06 +00:00
if d . Groups != groups {
2015-07-14 22:46:15 +00:00
return fmt . Errorf ( "bad: %#v" , resp )
}
return nil
} ,
}
}
func testAccStepDeleteUser ( t * testing . T , user string ) logicaltest . TestStep {
return logicaltest . TestStep {
Operation : logical . DeleteOperation ,
Path : "users/" + user ,
}
}
2015-05-07 17:54:33 +00:00
func testAccStepLogin ( t * testing . T , user string , pass string ) logicaltest . TestStep {
return logicaltest . TestStep {
2016-01-07 15:30:47 +00:00
Operation : logical . UpdateOperation ,
2015-05-09 19:07:52 +00:00
Path : "login/" + user ,
2015-05-07 17:54:33 +00:00
Data : map [ string ] interface { } {
"password" : pass ,
} ,
Unauthenticated : true ,
2019-10-22 17:37:41 +00:00
// Verifies user hermes conrad maps to groups via local group (engineers) as well as remote group (Scientists)
2019-07-01 20:16:23 +00:00
Check : logicaltest . TestCheckAuth ( [ ] string { "abc" , "bar" , "default" , "foo" , "xyz" } ) ,
2018-06-20 03:00:22 +00:00
}
}
func testAccStepLoginNoAttachedPolicies ( t * testing . T , user string , pass string ) logicaltest . TestStep {
return logicaltest . TestStep {
Operation : logical . UpdateOperation ,
Path : "login/" + user ,
Data : map [ string ] interface { } {
"password" : pass ,
} ,
Unauthenticated : true ,
2019-10-22 17:37:41 +00:00
// Verifies user hermes conrad maps to groups via local group (engineers) as well as remote group (Scientists)
2019-07-01 20:16:23 +00:00
Check : logicaltest . TestCheckAuth ( [ ] string { "abc" , "default" , "xyz" } ) ,
2015-05-07 17:54:33 +00:00
}
}
2015-06-29 21:50:55 +00:00
2016-04-02 17:11:36 +00:00
func testAccStepLoginNoGroupDN ( t * testing . T , user string , pass string ) logicaltest . TestStep {
return logicaltest . TestStep {
Operation : logical . UpdateOperation ,
Path : "login/" + user ,
Data : map [ string ] interface { } {
"password" : pass ,
} ,
Unauthenticated : true ,
2018-03-20 18:54:10 +00:00
// Verifies a search without defined GroupDN returns a warning rather than failing
2016-04-02 17:11:36 +00:00
Check : func ( resp * logical . Response ) error {
2017-06-05 14:52:43 +00:00
if len ( resp . Warnings ) != 1 {
return fmt . Errorf ( "expected a warning due to no group dn, got: %#v" , resp . Warnings )
2016-04-02 17:11:36 +00:00
}
return logicaltest . TestCheckAuth ( [ ] string { "bar" , "default" } ) ( resp )
} ,
}
}
2016-05-14 23:56:49 +00:00
func testAccStepGroupList ( t * testing . T , groups [ ] string ) logicaltest . TestStep {
return logicaltest . TestStep {
Operation : logical . ListOperation ,
Path : "groups" ,
Check : func ( resp * logical . Response ) error {
if resp . IsError ( ) {
2018-04-09 18:35:21 +00:00
return fmt . Errorf ( "got error response: %#v" , * resp )
2016-05-14 23:56:49 +00:00
}
2016-05-09 00:21:44 +00:00
expected := make ( [ ] string , len ( groups ) )
copy ( expected , groups )
sort . Strings ( expected )
sortedResponse := make ( [ ] string , len ( resp . Data [ "keys" ] . ( [ ] string ) ) )
copy ( sortedResponse , resp . Data [ "keys" ] . ( [ ] string ) )
sort . Strings ( sortedResponse )
if ! reflect . DeepEqual ( expected , sortedResponse ) {
return fmt . Errorf ( "expected:\n%#v\ngot:\n%#v\n" , expected , sortedResponse )
2016-05-14 23:56:49 +00:00
}
return nil
} ,
}
}
func testAccStepUserList ( t * testing . T , users [ ] string ) logicaltest . TestStep {
return logicaltest . TestStep {
Operation : logical . ListOperation ,
Path : "users" ,
Check : func ( resp * logical . Response ) error {
if resp . IsError ( ) {
2018-04-09 18:35:21 +00:00
return fmt . Errorf ( "got error response: %#v" , * resp )
2016-05-14 23:56:49 +00:00
}
2016-05-09 00:21:44 +00:00
expected := make ( [ ] string , len ( users ) )
copy ( expected , users )
sort . Strings ( expected )
sortedResponse := make ( [ ] string , len ( resp . Data [ "keys" ] . ( [ ] string ) ) )
copy ( sortedResponse , resp . Data [ "keys" ] . ( [ ] string ) )
sort . Strings ( sortedResponse )
if ! reflect . DeepEqual ( expected , sortedResponse ) {
return fmt . Errorf ( "expected:\n%#v\ngot:\n%#v\n" , expected , sortedResponse )
2016-05-14 23:56:49 +00:00
}
return nil
} ,
}
}
2019-07-01 20:16:23 +00:00
func TestLdapAuthBackend_ConfigUpgrade ( t * testing . T ) {
var resp * logical . Response
var err error
b , storage := createBackendWithStorage ( t )
ctx := context . Background ( )
2019-10-22 17:37:41 +00:00
cleanup , cfg := ldap . PrepareTestContainer ( t , "latest" )
defer cleanup ( )
2019-07-01 20:16:23 +00:00
configReq := & logical . Request {
Operation : logical . UpdateOperation ,
Path : "config" ,
Data : map [ string ] interface { } {
2019-10-22 17:37:41 +00:00
"url" : cfg . Url ,
"userattr" : cfg . UserAttr ,
"userdn" : cfg . UserDN ,
"groupdn" : cfg . GroupDN ,
"groupattr" : cfg . GroupAttr ,
"binddn" : cfg . BindDN ,
"bindpass" : cfg . BindPassword ,
2019-07-01 20:16:23 +00:00
"token_period" : "5m" ,
"token_explicit_max_ttl" : "24h" ,
2019-11-20 19:26:13 +00:00
"request_timeout" : cfg . RequestTimeout ,
2019-07-01 20:16:23 +00:00
} ,
2019-07-03 01:01:34 +00:00
Storage : storage ,
Connection : & logical . Connection { } ,
2019-07-01 20:16:23 +00:00
}
resp , err = b . HandleRequest ( ctx , configReq )
if err != nil || ( resp != nil && resp . IsError ( ) ) {
t . Fatalf ( "err:%v resp:%#v" , err , resp )
}
fd , err := b . getConfigFieldData ( )
if err != nil {
t . Fatal ( err )
}
defParams , err := ldaputil . NewConfigEntry ( nil , fd )
if err != nil {
t . Fatal ( err )
}
falseBool := new ( bool )
* falseBool = false
exp := & ldapConfigEntry {
TokenParams : tokenutil . TokenParams {
TokenPeriod : 5 * time . Minute ,
TokenExplicitMaxTTL : 24 * time . Hour ,
} ,
ConfigEntry : & ldaputil . ConfigEntry {
2019-10-22 17:37:41 +00:00
Url : cfg . Url ,
UserAttr : cfg . UserAttr ,
UserDN : cfg . UserDN ,
GroupDN : cfg . GroupDN ,
GroupAttr : cfg . GroupAttr ,
BindDN : cfg . BindDN ,
BindPassword : cfg . BindPassword ,
2019-07-29 21:40:04 +00:00
GroupFilter : defParams . GroupFilter ,
DenyNullBind : defParams . DenyNullBind ,
TLSMinVersion : defParams . TLSMinVersion ,
TLSMaxVersion : defParams . TLSMaxVersion ,
CaseSensitiveNames : falseBool ,
UsePre111GroupCNBehavior : new ( bool ) ,
2019-11-20 19:26:13 +00:00
RequestTimeout : cfg . RequestTimeout ,
2019-07-01 20:16:23 +00:00
} ,
}
configEntry , err := b . Config ( ctx , configReq )
if err != nil {
t . Fatal ( err )
}
if diff := deep . Equal ( exp , configEntry ) ; diff != nil {
t . Fatal ( diff )
}
// Store just the config entry portion, for upgrade testing
entry , err := logical . StorageEntryJSON ( "config" , configEntry . ConfigEntry )
if err != nil {
t . Fatal ( err )
}
err = configReq . Storage . Put ( ctx , entry )
if err != nil {
t . Fatal ( err )
}
configEntry , err = b . Config ( ctx , configReq )
if err != nil {
t . Fatal ( err )
}
// We won't have token params anymore so nil those out
exp . TokenParams = tokenutil . TokenParams { }
if diff := deep . Equal ( exp , configEntry ) ; diff != nil {
t . Fatal ( diff )
}
}