diff --git a/README.md b/README.md index d467fa2b..575c8d64 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,3 @@ -TAS version | Compatible? ---- | --- -2.12 | ![CI](https://ci.cryo.cf-app.com/api/v1/teams/cf-mgmt/pipelines/cf-mgmt/jobs/test-against-tas-2_12/badge) -2.11 | ![CI](https://ci.cryo.cf-app.com/api/v1/teams/cf-mgmt/pipelines/cf-mgmt/jobs/test-against-tas-2_11_lts2/badge) -2.10 | ![CI](https://ci.cryo.cf-app.com/api/v1/teams/cf-mgmt/pipelines/cf-mgmt/jobs/test-against-tas-2_10/badge) -2.7 | ![CI](https://ci.cryo.cf-app.com/api/v1/teams/cf-mgmt/pipelines/cf-mgmt/jobs/test-against-tas-2_7_lts/badge) - # Cloud Foundry Management (cf-mgmt) Go automation for managing orgs, spaces, users (from ldap groups or internal store) mapping to roles, quotas, application security groups and private-domains that can be driven from concourse pipeline and GIT managed metadata @@ -109,6 +102,8 @@ Navigate into a directory in which will become your git repository for cf-mgmt c - [LDAP only config](docs/config/README.md#ldap-configuration) - [SAML with LDAP groups](docs/config/README.md#saml-configuration-with-ldap-group-lookups) - [SAML only](docs/config/README.md#saml-configuration) + - [SAML with LDAP groups and SAMl Groups](docs/config/README.md#saml-configuration-with-saml group-lookups-and-ldap-group-lookups) + 4. [Generate the concourse pipeline](docs/config/generate-concourse-pipeline/README.md) using `cf-mgmt-config` - ```cf-mgmt-config [OPTIONS] generate-concourse-pipeline [generate-concourse-pipeline-OPTIONS]``` @@ -221,7 +216,7 @@ Some portions of this code are autogenerated. To regenerate them, install the p - `go get -u github.com/jteeuwen/go-bindata/...` - `go get -u github.com/maxbrunsfeld/counterfeiter` -And then run `go generate ./...` from the project directory, or `go generate .` +And then run `go generate $(glide nv)` from the project directory, or `go generate .` from a specific directory. ## Contributing diff --git a/config/ldap.go b/config/ldap.go index 74ef8aaa..63300a7f 100644 --- a/config/ldap.go +++ b/config/ldap.go @@ -21,4 +21,9 @@ type LdapConfig struct { UseIDForSAMLUser bool `yaml:"useIDForSAMLUser"` MinTLSVersion string `yaml:"minTLSVersion"` MaxTLSVersion string `yaml:"maxTLSVersion"` + LdapOrigin string `yaml:"ldapOrigin"` + LdapUserFilter string `yaml:"ldapUserFilter"` + SamlUserFilter string `yaml:"samlUserFilter"` + LdapUserFilterMode string `yaml:"ldapUserFilterMode"` + SamlUserFilterMode string `yaml:"samlUserFilterMode"` } diff --git a/config/org.go b/config/org.go index 5bac005c..cdbc37fb 100644 --- a/config/org.go +++ b/config/org.go @@ -125,3 +125,15 @@ func (o *OrgConfig) GetManagerGroups() []string { func (o *OrgConfig) GetAuditorGroups() []string { return o.Auditor.groups(o.AuditorGroup) } + +func (o *OrgConfig) GetBillingManagerSamlGroups() []string { + return o.BillingManager.saml_groups(o.BillingManagerGroup) +} + +func (o *OrgConfig) GetManagerSamlGroups() []string { + return o.Manager.saml_groups(o.ManagerGroup) +} + +func (o *OrgConfig) GetAuditorSamlGroups() []string { + return o.Auditor.saml_groups(o.AuditorGroup) +} diff --git a/config/space.go b/config/space.go index 320c0546..18736e82 100644 --- a/config/space.go +++ b/config/space.go @@ -122,3 +122,15 @@ func (i *SpaceConfig) GetManagerGroups() []string { func (i *SpaceConfig) GetAuditorGroups() []string { return i.Auditor.groups(i.AuditorGroup) } + +func (i *SpaceConfig) GetDeveloperSamlGroups() []string { + return i.Developer.saml_groups(i.DeveloperGroup) +} + +func (i *SpaceConfig) GetManagerSamlGroups() []string { + return i.Manager.saml_groups(i.ManagerGroup) +} + +func (i *SpaceConfig) GetAuditorSamlGroups() []string { + return i.Auditor.saml_groups(i.AuditorGroup) +} diff --git a/config/usermgmt.go b/config/usermgmt.go index aebe844f..d89e2288 100644 --- a/config/usermgmt.go +++ b/config/usermgmt.go @@ -7,6 +7,7 @@ type UserMgmt struct { SamlUsers []string `yaml:"saml_users"` LDAPGroup string `yaml:"ldap_group,omitempty"` LDAPGroups []string `yaml:"ldap_groups"` + SAMLGroups []string `yaml:"saml_groups"` } // UserOrigin is an enum type encoding from what source a user originated. @@ -25,6 +26,26 @@ const ( LDAPOrigin ) +// groups are always defined as ldap groups for compatibility +func (u *UserMgmt) saml_groups(groupName string) []string { + groupMap := make(map[string]string) + for _, group := range u.SAMLGroups { + groupMap[group] = group + } + if groupName != "" { + groupMap[groupName] = groupName + } + + result := make([]string, 0, len(groupMap)) + for k := range groupMap { + result = append(result, k) + } + return result +} + +// ne function to get the saml_groups (which are technically ldap groups, but the contained users will be synced) +// to cf as user with saml origin + func (u *UserMgmt) groups(groupName string) []string { groupMap := make(map[string]string) for _, group := range u.LDAPGroups { diff --git a/config/yaml_config.go b/config/yaml_config.go index 05f5e726..e3d55303 100644 --- a/config/yaml_config.go +++ b/config/yaml_config.go @@ -553,6 +553,11 @@ func (m *yamlManager) LdapConfig(ldapBindUser, ldapBindPassword, ldapServer stri if config.Origin == "" { config.Origin = "ldap" } + + if config.LdapOrigin == "" { + config.LdapOrigin = config.Origin + } + return config, nil } diff --git a/docs/config/README.md b/docs/config/README.md index fce53a7f..94104950 100644 --- a/docs/config/README.md +++ b/docs/config/README.md @@ -411,6 +411,56 @@ minTLSVersion: 1.0 maxTLSVersion: 1.3 ``` +### SAML Configuration with saml group lookups AND ldap group lookups + +LDAP configuration file ```ldap.yml``` is located under the ```config``` folder. To have cf-mgmt create SAML users in UAA need to enable ldap to lookup the user information from an LDAP source to properly create the SAML users. In orgConfig.yml and spaceConfig.yml leverage saml_groups. + +If you provide ldapOrigin in the ldap.yaml in addition to origin (which is the saml origin in this case) ldap_groups and ldap_users the users are created with ldapOrigin. If you do not provide ldapOrigin ldap_users and ldap_groups are created with Origin (in this case saml origin) +```yml +enabled: true +ldapHost: 127.0.0.1 +ldapPort: 10389 +#true/false (default false) +use_tls: true +bindDN: uid=admin,ou=system +userSearchBase: ou=users,dc=example,dc=com +userNameAttribute: uid +# optional added in v1.0.20+ +userObjectClass: +userMailAttribute: mail +groupSearchBase: ou=groups,dc=example,dc=com +groupAttribute: member +# optional added in v1.0.20+ +groupObjectClass: +origin: +ldapOrigin: "ldap" + +# optional added in 1.0.11+ - true/false +insecure_skip_verify: false +# optional added in 1.0.11+ if ldap server is signed by non-public CA provide ca pem here +ca_cert: | + +# optional added in 1.0.37 - true/false. If true it will use userid from ldap group lookup vs email address for userid +useIDForSAMLUser: false + +# optional added in 1.0.47+ if omitted 1.0 is min, 1.3 is max. Valid values 1.0, 1.1, 1.2, 1.3 or blank +minTLSVersion: 1.0 +maxTLSVersion: 1.3 +``` + +### Filtering saml_groups and ldap_groups + +LDAP configuration file ```ldap.yml``` is located under the ```config``` folder. If you leverage ldap_groups or saml_groups in orgConfig.yml and/or spaceConfig.yml, the users found in accordig groups may be filtered through a samlGroupFilter or ldapGroupFilter in ldap.yaml. Filter values may be all valid golang regular expressions. Via ldapUserFilterMode/samlUserFilterMode the filter can be set to include or exclude + +```yml +ldapGroupFilter: "^foo" +samlGroupFilter: ".*" +ldapUserFilterMode: "include" +samlUserFilterMode: "exclude" + +``` + + ### SAML Configuration LDAP configuration file ```ldap.yml``` is located under the ```config``` folder. To have cf-mgmt create SAML users you can disable ldap integration for looking up users in ldap groups with v0.0.66+ as orgConfig.yml and spaceConfig.yml now includes a saml_users array attribute which can contain a list of email addresses. diff --git a/ldap/types.go b/ldap/types.go index 2e1fed02..815cf16a 100644 --- a/ldap/types.go +++ b/ldap/types.go @@ -17,4 +17,5 @@ type User struct { UserDN string UserID string Email string + Origin string } diff --git a/user/ldap_users.go b/user/ldap_users.go index c052c739..e26e02e7 100644 --- a/user/ldap_users.go +++ b/user/ldap_users.go @@ -2,6 +2,7 @@ package user import ( "fmt" + "regexp" "strings" "github.com/pkg/errors" @@ -11,7 +12,7 @@ import ( ) func (m *DefaultManager) SyncLdapUsers(roleUsers *RoleUsers, uaaUsers *uaa.Users, usersInput UsersInput) error { - origin := m.LdapConfig.Origin + if m.LdapConfig.Enabled { ldapUsers, err := m.GetLDAPUsers(uaaUsers, usersInput) if err != nil { @@ -22,16 +23,20 @@ func (m *DefaultManager) SyncLdapUsers(roleUsers *RoleUsers, uaaUsers *uaa.Users userToUse := m.UpdateUserInfo(inputUser) userID := userToUse.UserID userList := uaaUsers.GetByName(userID) + origin := userToUse.Origin + if origin == "" { + return fmt.Errorf("Unable to find user %s for origin %s", userID, origin) + } if len(userList) == 0 { lo.G.Debug("User", userID, "doesn't exist in cloud foundry, so creating user") - if userGUID, err := m.UAAMgr.CreateExternalUser(userID, userToUse.Email, userToUse.UserDN, m.LdapConfig.Origin); err != nil { + if userGUID, err := m.UAAMgr.CreateExternalUser(userID, userToUse.Email, userToUse.UserDN, origin); err != nil { lo.G.Errorf("Unable to create user %s with error %s", userID, err.Error()) continue } else { uaaUsers.Add(uaa.User{ Username: userID, ExternalID: userToUse.UserDN, - Origin: m.LdapConfig.Origin, + Origin: userToUse.Origin, Email: userToUse.Email, GUID: userGUID, }) @@ -56,13 +61,122 @@ func (m *DefaultManager) SyncLdapUsers(roleUsers *RoleUsers, uaaUsers *uaa.Users return nil } +func (m *DefaultManager) FilterUsers(userFilter string, userFilterMode string, dnList []string, groupName string) []string { + filteredDNList := []string{} + filter, _ := regexp.Compile(userFilter) + filterMode := userFilterMode + for _, userDN := range dnList { + if filter.MatchString(userDN) { + if filterMode == "include" { + filteredDNList = append(filteredDNList, userDN) + } else if filterMode == "exclude" { + lo.G.Debugf("Removed user %s from group %s because of filter %s and mode %s", userDN, groupName, filter, filterMode) + } + } else { + if filterMode == "include" { + lo.G.Debugf("Removed user %s from group %s because of filter %s and mode %s", userDN, groupName, filter, filterMode) + } else if filterMode == "exclude" { + filteredDNList = append(filteredDNList, userDN) + } + } + } + return filteredDNList +} + func (m *DefaultManager) GetLDAPUsers(uaaUsers *uaa.Users, usersInput UsersInput) ([]ldap.User, error) { var ldapUsers []ldap.User + + // if saml groups are present, saml group members get the saml origin (LdapConfig.origin) + // ldap_group members and ldap_users get the origin ldap + // ldap_group members than are interpreted with LdapConfig.LdapOrigin + // if LdapOrigin is not present it defaults to origin, in this case the saml origin + if m.LdapConfig.LdapOrigin == "" { + m.LdapConfig.LdapOrigin = m.LdapConfig.Origin + } + + if m.LdapConfig.LdapUserFilterMode == "" { + m.LdapConfig.LdapUserFilterMode = "include" + } + if m.LdapConfig.SamlUserFilterMode == "" { + m.LdapConfig.SamlUserFilterMode = "include" + } + + // only when ldapOriging is set AND SamlGroups are present, ldapOrigin is used + // for migration only - remove when migrated + length := len(usersInput.UniqueSamlGroupNames()) + originForLdapGroups := "" + if length > 0 { + originForLdapGroups = m.LdapConfig.LdapOrigin + lo.G.Debugf("SAML Groups found %s", usersInput.UniqueSamlGroupNames()) + } else { + originForLdapGroups = m.LdapConfig.Origin + } + // remove when migrated + for _, groupName := range usersInput.UniqueLdapGroupNames() { + lo.G.Debugf("LDAP GROUP found %s", groupName) + userDNList, err := m.LdapMgr.GetUserDNs(groupName) + if err != nil { + return nil, err + } + lo.G.Debugf("LDAP group users pre filter %s", userDNList) + + // filter ldap users + lo.G.Debugf("LDAP filter %s", m.LdapConfig.LdapUserFilter) + if m.LdapConfig.LdapUserFilter != "" { + filteredDNList := m.FilterUsers(m.LdapConfig.LdapUserFilter, m.LdapConfig.LdapUserFilterMode, userDNList, groupName) + userDNList = filteredDNList + } + + lo.G.Debugf("LDAP group users post filter %s", userDNList) + + for _, userDN := range userDNList { + lo.G.Debugf("Checking for userDN %s", userDN) + uaaUser := uaaUsers.GetByExternalID(userDN) + if uaaUser != nil { + lo.G.Debugf("UserDN [%s] found in UAA as [%s], skipping ldap lookup", userDN, uaaUser.Username) + ldapUsers = append(ldapUsers, ldap.User{ + UserID: uaaUser.Username, + UserDN: userDN, + Email: uaaUser.Email, + // remove when migrated + Origin: originForLdapGroups, + }) + } else { + lo.G.Debugf("UserDN [%s] not found in UAA, executing ldap lookup", userDN) + user, err := m.LdapMgr.GetUserByDN(userDN) + if err != nil { + return nil, err + } + if user != nil { + // remove when migrated + user.Origin = originForLdapGroups + ldapUsers = append(ldapUsers, *user) + } else { + lo.G.Debugf("UserDN %s not found in ldap", userDN) + } + } + } + } + + for _, groupName := range usersInput.UniqueSamlGroupNames() { + lo.G.Debugf("SAML Group %v", groupName) userDNList, err := m.LdapMgr.GetUserDNs(groupName) if err != nil { return nil, err } + + // filter saml users + lo.G.Debugf("user list before %+v", userDNList) + + if m.LdapConfig.SamlUserFilter != "" { + lo.G.Debugf("SAML filter %s", m.LdapConfig.SamlUserFilter) + filteredDNList := m.FilterUsers(m.LdapConfig.SamlUserFilter, m.LdapConfig.SamlUserFilterMode, userDNList, groupName) + userDNList = filteredDNList + } + + lo.G.Debugf("user list after %v", userDNList) + for _, userDN := range userDNList { lo.G.Debugf("Checking for userDN %s", userDN) uaaUser := uaaUsers.GetByExternalID(userDN) @@ -72,6 +186,7 @@ func (m *DefaultManager) GetLDAPUsers(uaaUsers *uaa.Users, usersInput UsersInput UserID: uaaUser.Username, UserDN: userDN, Email: uaaUser.Email, + Origin: m.LdapConfig.Origin, }) } else { lo.G.Debugf("UserDN [%s] not found in UAA, executing ldap lookup", userDN) @@ -80,6 +195,7 @@ func (m *DefaultManager) GetLDAPUsers(uaaUsers *uaa.Users, usersInput UsersInput return nil, err } if user != nil { + user.Origin = m.LdapConfig.Origin ldapUsers = append(ldapUsers, *user) } else { lo.G.Infof("UserDN %s not found in ldap", userDN) @@ -87,17 +203,19 @@ func (m *DefaultManager) GetLDAPUsers(uaaUsers *uaa.Users, usersInput UsersInput } } } + for _, userID := range usersInput.LdapUsers { userList := uaaUsers.GetByName(userID) if len(userList) > 0 { lo.G.Debugf("UserID [%s] found in UAA, skipping ldap lookup", userID) for _, uaaUser := range userList { lo.G.Debugf("Checking if userID [%s] with origin [%s] and externalID [%s] matches ldap origin", uaaUser.Username, uaaUser.Origin, uaaUser.ExternalID) - if strings.EqualFold(uaaUser.Origin, m.LdapConfig.Origin) { + if strings.EqualFold(uaaUser.Origin, m.LdapConfig.LdapOrigin) { ldapUsers = append(ldapUsers, ldap.User{ UserID: userID, UserDN: uaaUser.ExternalID, Email: uaaUser.Email, + Origin: m.LdapConfig.LdapOrigin, }) } } @@ -108,6 +226,7 @@ func (m *DefaultManager) GetLDAPUsers(uaaUsers *uaa.Users, usersInput UsersInput return nil, err } if user != nil { + user.Origin = m.LdapConfig.LdapOrigin ldapUsers = append(ldapUsers, *user) } else { lo.G.Infof("User %s not found in ldap", userID) @@ -128,6 +247,7 @@ func (m *DefaultManager) GetLDAPUsers(uaaUsers *uaa.Users, usersInput UsersInput for _, uniqueLDAPUser := range uniqueLDAPUsers { ldapUsersToReturn = append(ldapUsersToReturn, uniqueLDAPUser) } + lo.G.Debugf("LdapUsers to return: %+v", ldapUsersToReturn) return ldapUsersToReturn, nil } @@ -135,7 +255,8 @@ func (m *DefaultManager) UpdateUserInfo(user ldap.User) ldap.User { userID := strings.ToLower(user.UserID) externalID := user.UserDN email := user.Email - if m.LdapConfig.Origin != "ldap" { + origin := user.Origin + if origin != "ldap" { if m.LdapConfig.UseIDForSAMLUser { userID = strings.ToLower(user.UserID) externalID = user.UserID @@ -153,5 +274,6 @@ func (m *DefaultManager) UpdateUserInfo(user ldap.User) ldap.User { UserID: userID, UserDN: externalID, Email: email, + Origin: origin, } } diff --git a/user/ldap_users_test.go b/user/ldap_users_test.go index 0d59df33..9fff0765 100644 --- a/user/ldap_users_test.go +++ b/user/ldap_users_test.go @@ -45,15 +45,16 @@ var _ = Describe("given UserSpaces", func() { SpaceMgr: spaceFake, OrgReader: orgFake, Peek: false, - LdapConfig: &config.LdapConfig{Origin: "ldap"}} + LdapConfig: &config.LdapConfig{Origin: "ldap", LdapOrigin: "ldap"}} }) Context("SyncLdapUsers", func() { var roleUsers *RoleUsers var uaaUsers *uaa.Users BeforeEach(func() { userManager.LdapConfig = &config.LdapConfig{ - Origin: "ldap", - Enabled: true, + Origin: "ldap", + LdapOrigin: "ldap", + Enabled: true, } uaaUsers = &uaa.Users{} uaaUsers.Add(uaa.User{Username: "test_ldap", Origin: "ldap", ExternalID: "cn=test_ldap", GUID: "test_ldap-id"}) @@ -95,8 +96,9 @@ var _ = Describe("given UserSpaces", func() { It("Should add ldap user to role", func() { userManager.LdapConfig = &config.LdapConfig{ - Origin: "ldap", - Enabled: true, + Origin: "ldap", + LdapOrigin: "ldap", + Enabled: true, } uaaUsers = &uaa.Users{} uaaUsers.Add(uaa.User{Username: "test_ldap", Origin: "ldap", ExternalID: "cn=test_ldap", GUID: "test_ldap-id"}) @@ -166,6 +168,52 @@ var _ = Describe("given UserSpaces", func() { }) + It("Should add saml ldap group member to role", func() { + + userManager.LdapConfig = &config.LdapConfig{ + Origin: "saml", + UseIDForSAMLUser: true, + Enabled: true, + } + updateUsersInput := UsersInput{ + LdapUsers: []string{}, + LdapGroupNames: []string{"test_group"}, + SpaceGUID: "space_guid", + OrgGUID: "org_guid", + AddUser: userManager.AssociateSpaceAuditor, + } + + uaaFake.CreateExternalUserReturns("test_ldap3-id", nil) + + ldapFake.GetUserDNsReturns([]string{"cn=ldap_test_dn"}, nil) + ldapFake.GetUserByDNReturns( + &ldap.User{ + UserDN: "ldap_test_dn", + UserID: "test_ldap3", + Email: "test@test.com", + }, + nil) + + err := userManager.SyncLdapUsers(roleUsers, uaaUsers, updateUsersInput) + Expect(err).ShouldNot(HaveOccurred()) + Expect(client.AssociateOrgUserCallCount()).Should(Equal(1)) + Expect(client.AssociateSpaceAuditorCallCount()).Should(Equal(1)) + orgGUID, userGUID := client.AssociateOrgUserArgsForCall(0) + Expect(orgGUID).Should(Equal("org_guid")) + Expect(userGUID).Should(Equal("test_ldap3-id")) + + spaceGUID, userGUID := client.AssociateSpaceAuditorArgsForCall(0) + Expect(spaceGUID).Should(Equal("space_guid")) + Expect(userGUID).Should(Equal("test_ldap3-id")) + + arg1, arg2, arg3, origin := uaaFake.CreateExternalUserArgsForCall(0) + Expect(arg1).Should(Equal("test_ldap3")) + Expect(arg2).Should(Equal("test@test.com")) + Expect(arg3).Should(Equal("test_ldap3")) + Expect(origin).Should(Equal("saml")) + + }) + It("Should not add existing ldap user to role", func() { updateUsersInput := UsersInput{ LdapUsers: []string{"test_ldap"}, @@ -209,6 +257,322 @@ var _ = Describe("given UserSpaces", func() { Expect(origin).Should(Equal("ldap")) }) + It("Should create external user when user doesn't exist in uaa 2", func() { + userManager.LdapConfig = &config.LdapConfig{ + Origin: "saml", + LdapOrigin: "ldap", + Enabled: true, + } + updateUsersInput := UsersInput{ + LdapUsers: []string{"test_ldap_new"}, + SpaceGUID: "space_guid", + LdapGroupNames: []string{"test_group"}, + OrgGUID: "org_guid", + AddUser: userManager.AssociateSpaceAuditor, + } + ldapFake.GetUserByIDReturns( + &ldap.User{ + UserDN: "ldap_test_dn", + UserID: "test_ldap_new", + Email: "test@test.com", + }, + nil) + err := userManager.SyncLdapUsers(roleUsers, uaaUsers, updateUsersInput) + Expect(err).ShouldNot(HaveOccurred()) + Expect(uaaFake.CreateExternalUserCallCount()).Should(Equal(1)) + arg1, arg2, arg3, origin := uaaFake.CreateExternalUserArgsForCall(0) + Expect(arg1).Should(Equal("test_ldap_new")) + Expect(arg2).Should(Equal("test@test.com")) + Expect(arg3).Should(Equal("ldap_test_dn")) + Expect(origin).Should(Equal("ldap")) + + }) + + // saml_group tests + It("Should add saml ldap group member to role", func() { + + userManager.LdapConfig = &config.LdapConfig{ + Origin: "saml", + LdapOrigin: "ldap", + UseIDForSAMLUser: true, + Enabled: true, + } + updateUsersInput := UsersInput{ + LdapUsers: []string{}, + SamlGroupNames: []string{"test_group2"}, + SpaceGUID: "space_guid", + OrgGUID: "org_guid", + AddUser: userManager.AssociateSpaceAuditor, + } + + uaaFake.CreateExternalUserReturns("test_ldap3-id", nil) + + ldapFake.GetUserDNsReturns([]string{"cn=ldap_test_dn"}, nil) + ldapFake.GetUserByDNReturns( + &ldap.User{ + UserDN: "ldap_test_dn", + UserID: "test_ldap3", + Email: "test@test.com", + }, + nil) + + err := userManager.SyncLdapUsers(roleUsers, uaaUsers, updateUsersInput) + Expect(err).ShouldNot(HaveOccurred()) + Expect(client.AssociateOrgUserCallCount()).Should(Equal(1)) + Expect(client.AssociateSpaceAuditorCallCount()).Should(Equal(1)) + orgGUID, userGUID := client.AssociateOrgUserArgsForCall(0) + Expect(orgGUID).Should(Equal("org_guid")) + Expect(userGUID).Should(Equal("test_ldap3-id")) + + spaceGUID, userGUID := client.AssociateSpaceAuditorArgsForCall(0) + Expect(spaceGUID).Should(Equal("space_guid")) + Expect(userGUID).Should(Equal("test_ldap3-id")) + + arg1, arg2, arg3, origin := uaaFake.CreateExternalUserArgsForCall(0) + Expect(arg1).Should(Equal("test_ldap3")) + Expect(arg2).Should(Equal("test@test.com")) + Expect(arg3).Should(Equal("test_ldap3")) + Expect(origin).Should(Equal("saml")) + + }) + + // saml group filter test + It("Should not add saml group member to role, because of filter", func() { + + userManager.LdapConfig = &config.LdapConfig{ + Origin: "saml", + LdapOrigin: "ldap", + UseIDForSAMLUser: true, + SamlUserFilter: "bla", + Enabled: true, + } + updateUsersInput := UsersInput{ + LdapUsers: []string{}, + SamlGroupNames: []string{"test_group2"}, + SpaceGUID: "space_guid", + OrgGUID: "org_guid", + AddUser: userManager.AssociateSpaceAuditor, + } + + uaaFake.CreateExternalUserReturns("test_ldap3-id", nil) + + ldapFake.GetUserDNsReturns([]string{"cn=ldap_test_dn"}, nil) + ldapFake.GetUserByDNReturns( + &ldap.User{ + UserDN: "ldap_test_dn", + UserID: "test_ldap3", + Email: "test@test.com", + }, + nil) + + err := userManager.SyncLdapUsers(roleUsers, uaaUsers, updateUsersInput) + Expect(err).ShouldNot(HaveOccurred()) + Expect(client.AssociateOrgUserCallCount()).Should(Equal(0)) + Expect(client.AssociateSpaceAuditorCallCount()).Should(Equal(0)) + }) + + // filter test + It("Should add saml group member to role, because of filter", func() { + + userManager.LdapConfig = &config.LdapConfig{ + Origin: "saml", + LdapOrigin: "ldap", + UseIDForSAMLUser: true, + SamlUserFilter: "ldap_test_dn", + Enabled: true, + } + updateUsersInput := UsersInput{ + LdapUsers: []string{}, + SamlGroupNames: []string{"test_group2"}, + SpaceGUID: "space_guid", + OrgGUID: "org_guid", + AddUser: userManager.AssociateSpaceAuditor, + } + + uaaFake.CreateExternalUserReturns("test_ldap3-id", nil) + + ldapFake.GetUserDNsReturns([]string{"cn=ldap_test_dn"}, nil) + ldapFake.GetUserByDNReturns( + &ldap.User{ + UserDN: "ldap_test_dn", + UserID: "test_ldap3", + Email: "test@test.com", + }, + nil) + + err := userManager.SyncLdapUsers(roleUsers, uaaUsers, updateUsersInput) + Expect(err).ShouldNot(HaveOccurred()) + Expect(client.AssociateOrgUserCallCount()).Should(Equal(1)) + Expect(client.AssociateSpaceAuditorCallCount()).Should(Equal(1)) + orgGUID, userGUID := client.AssociateOrgUserArgsForCall(0) + Expect(orgGUID).Should(Equal("org_guid")) + Expect(userGUID).Should(Equal("test_ldap3-id")) + + spaceGUID, userGUID := client.AssociateSpaceAuditorArgsForCall(0) + Expect(spaceGUID).Should(Equal("space_guid")) + Expect(userGUID).Should(Equal("test_ldap3-id")) + + arg1, arg2, arg3, origin := uaaFake.CreateExternalUserArgsForCall(0) + Expect(arg1).Should(Equal("test_ldap3")) + Expect(arg2).Should(Equal("test@test.com")) + Expect(arg3).Should(Equal("test_ldap3")) + Expect(origin).Should(Equal("saml")) + }) + + // saml group filter and mode test + It("Should not add saml group member to role, because of filter and exclusion mode", func() { + + userManager.LdapConfig = &config.LdapConfig{ + Origin: "saml", + LdapOrigin: "ldap", + UseIDForSAMLUser: true, + SamlUserFilter: "ldap_test_dn", + SamlUserFilterMode: "exclude", + Enabled: true, + } + updateUsersInput := UsersInput{ + LdapUsers: []string{}, + SamlGroupNames: []string{"test_group2"}, + SpaceGUID: "space_guid", + OrgGUID: "org_guid", + AddUser: userManager.AssociateSpaceAuditor, + } + + uaaFake.CreateExternalUserReturns("test_ldap3-id", nil) + + ldapFake.GetUserDNsReturns([]string{"cn=ldap_test_dn"}, nil) + ldapFake.GetUserByDNReturns( + &ldap.User{ + UserDN: "ldap_test_dn", + UserID: "test_ldap3", + Email: "test@test.com", + }, + nil) + + err := userManager.SyncLdapUsers(roleUsers, uaaUsers, updateUsersInput) + Expect(err).ShouldNot(HaveOccurred()) + Expect(client.AssociateOrgUserCallCount()).Should(Equal(0)) + Expect(client.AssociateSpaceAuditorCallCount()).Should(Equal(0)) + }) + + // ldap group filter test + + It("Should not add saml ldap group member to role, because of filter", func() { + + userManager.LdapConfig = &config.LdapConfig{ + Origin: "saml", + UseIDForSAMLUser: true, + LdapUserFilter: "bla", + Enabled: true, + } + updateUsersInput := UsersInput{ + LdapUsers: []string{}, + LdapGroupNames: []string{"test_group2"}, + SpaceGUID: "space_guid", + OrgGUID: "org_guid", + AddUser: userManager.AssociateSpaceAuditor, + } + + uaaFake.CreateExternalUserReturns("test_ldap3-id", nil) + + ldapFake.GetUserDNsReturns([]string{"cn=ldap_test_dn"}, nil) + ldapFake.GetUserByDNReturns( + &ldap.User{ + UserDN: "ldap_test_dn", + UserID: "test_ldap3", + Email: "test@test.com", + }, + nil) + + err := userManager.SyncLdapUsers(roleUsers, uaaUsers, updateUsersInput) + Expect(err).ShouldNot(HaveOccurred()) + Expect(client.AssociateOrgUserCallCount()).Should(Equal(0)) + Expect(client.AssociateSpaceAuditorCallCount()).Should(Equal(0)) + }) + + It("Should not add saml ldap group member to role, because of filter and exclusion mode", func() { + + userManager.LdapConfig = &config.LdapConfig{ + Origin: "saml", + UseIDForSAMLUser: true, + LdapUserFilter: "ldap_test_dn", + LdapUserFilterMode: "exclude", + Enabled: true, + } + updateUsersInput := UsersInput{ + LdapUsers: []string{}, + LdapGroupNames: []string{"test_group2"}, + SpaceGUID: "space_guid", + OrgGUID: "org_guid", + AddUser: userManager.AssociateSpaceAuditor, + } + + uaaFake.CreateExternalUserReturns("test_ldap3-id", nil) + + ldapFake.GetUserDNsReturns([]string{"cn=ldap_test_dn"}, nil) + ldapFake.GetUserByDNReturns( + &ldap.User{ + UserDN: "ldap_test_dn", + UserID: "test_ldap3", + Email: "test@test.com", + }, + nil) + + err := userManager.SyncLdapUsers(roleUsers, uaaUsers, updateUsersInput) + Expect(err).ShouldNot(HaveOccurred()) + Expect(client.AssociateOrgUserCallCount()).Should(Equal(0)) + Expect(client.AssociateSpaceAuditorCallCount()).Should(Equal(0)) + }) + + // filter test + It("Should add saml ldap group member to role, because of filter", func() { + + userManager.LdapConfig = &config.LdapConfig{ + Origin: "saml", + UseIDForSAMLUser: true, + LdapUserFilter: "ldap_test_dn", + Enabled: true, + } + updateUsersInput := UsersInput{ + LdapUsers: []string{}, + LdapGroupNames: []string{"test_group2"}, + SpaceGUID: "space_guid", + OrgGUID: "org_guid", + AddUser: userManager.AssociateSpaceAuditor, + } + + uaaFake.CreateExternalUserReturns("test_ldap3-id", nil) + + ldapFake.GetUserDNsReturns([]string{"cn=ldap_test_dn"}, nil) + ldapFake.GetUserByDNReturns( + &ldap.User{ + UserDN: "ldap_test_dn", + UserID: "test_ldap3", + Email: "test@test.com", + }, + nil) + + err := userManager.SyncLdapUsers(roleUsers, uaaUsers, updateUsersInput) + Expect(err).ShouldNot(HaveOccurred()) + Expect(client.AssociateOrgUserCallCount()).Should(Equal(1)) + Expect(client.AssociateSpaceAuditorCallCount()).Should(Equal(1)) + orgGUID, userGUID := client.AssociateOrgUserArgsForCall(0) + Expect(orgGUID).Should(Equal("org_guid")) + Expect(userGUID).Should(Equal("test_ldap3-id")) + + spaceGUID, userGUID := client.AssociateSpaceAuditorArgsForCall(0) + Expect(spaceGUID).Should(Equal("space_guid")) + Expect(userGUID).Should(Equal("test_ldap3-id")) + + arg1, arg2, arg3, origin := uaaFake.CreateExternalUserArgsForCall(0) + Expect(arg1).Should(Equal("test_ldap3")) + Expect(arg2).Should(Equal("test@test.com")) + Expect(arg3).Should(Equal("test_ldap3")) + Expect(origin).Should(Equal("saml")) + }) + + // + It("Should not error when create external user errors", func() { updateUsersInput := UsersInput{ LdapUsers: []string{"test_ldap3"}, @@ -306,6 +670,7 @@ var _ = Describe("given UserSpaces", func() { userInfo := userManager.UpdateUserInfo(ldap.User{ Email: "test@test.com", UserID: "testUser", + Origin: "ldap", UserDN: "testUserDN", }) Expect(userInfo.Email).Should(Equal("test@test.com")) @@ -319,6 +684,7 @@ var _ = Describe("given UserSpaces", func() { Email: "", UserID: "testUser", UserDN: "testUserDN", + Origin: "ldap", }) Expect(userInfo.Email).Should(Equal("testuser@user.from.ldap.cf")) Expect(userInfo.UserDN).Should(Equal("testUserDN")) @@ -332,6 +698,7 @@ var _ = Describe("given UserSpaces", func() { Email: "test@test.com", UserID: "testUser", UserDN: "testUserDN", + Origin: "foo", }) Expect(userInfo.Email).Should(Equal("test@test.com")) Expect(userInfo.UserDN).Should(Equal("test@test.com")) diff --git a/user/users.go b/user/users.go index c93944a0..15c948aa 100644 --- a/user/users.go +++ b/user/users.go @@ -225,6 +225,7 @@ func (m *DefaultManager) updateSpaceUsers(input *config.SpaceConfig, uaaUsers *u OrgName: input.Org, OrgGUID: space.OrganizationGuid, LdapGroupNames: input.GetDeveloperGroups(), + SamlGroupNames: input.GetDeveloperSamlGroups(), LdapUsers: input.Developer.LDAPUsers, Users: input.Developer.Users, SamlUsers: input.Developer.SamlUsers, @@ -243,6 +244,7 @@ func (m *DefaultManager) updateSpaceUsers(input *config.SpaceConfig, uaaUsers *u OrgGUID: space.OrganizationGuid, OrgName: input.Org, LdapGroupNames: input.GetManagerGroups(), + SamlGroupNames: input.GetManagerSamlGroups(), LdapUsers: input.Manager.LDAPUsers, Users: input.Manager.Users, SamlUsers: input.Manager.SamlUsers, @@ -260,6 +262,7 @@ func (m *DefaultManager) updateSpaceUsers(input *config.SpaceConfig, uaaUsers *u OrgGUID: space.OrganizationGuid, OrgName: input.Org, LdapGroupNames: input.GetAuditorGroups(), + SamlGroupNames: input.GetAuditorSamlGroups(), LdapUsers: input.Auditor.LDAPUsers, Users: input.Auditor.Users, SamlUsers: input.Auditor.SamlUsers, @@ -312,6 +315,7 @@ func (m *DefaultManager) updateOrgUsers(input *config.OrgConfig, uaaUsers *uaa.U OrgName: org.Name, OrgGUID: org.Guid, LdapGroupNames: input.GetBillingManagerGroups(), + SamlGroupNames: input.GetBillingManagerSamlGroups(), LdapUsers: input.BillingManager.LDAPUsers, Users: input.BillingManager.Users, SamlUsers: input.BillingManager.SamlUsers, @@ -329,6 +333,7 @@ func (m *DefaultManager) updateOrgUsers(input *config.OrgConfig, uaaUsers *uaa.U OrgName: org.Name, OrgGUID: org.Guid, LdapGroupNames: input.GetAuditorGroups(), + SamlGroupNames: input.GetAuditorSamlGroups(), LdapUsers: input.Auditor.LDAPUsers, Users: input.Auditor.Users, SamlUsers: input.Auditor.SamlUsers, @@ -346,6 +351,7 @@ func (m *DefaultManager) updateOrgUsers(input *config.OrgConfig, uaaUsers *uaa.U OrgName: org.Name, OrgGUID: org.Guid, LdapGroupNames: input.GetManagerGroups(), + SamlGroupNames: input.GetManagerSamlGroups(), LdapUsers: input.Manager.LDAPUsers, Users: input.Manager.Users, SamlUsers: input.Manager.SamlUsers, diff --git a/user/users_input.go b/user/users_input.go index fbff395e..2c0ebe7b 100644 --- a/user/users_input.go +++ b/user/users_input.go @@ -6,15 +6,15 @@ import ( //UsersInput type UsersInput struct { - SpaceGUID string - OrgGUID string - LdapUsers, Users, LdapGroupNames, SamlUsers []string - SpaceName string - OrgName string - RemoveUsers bool - ListUsers func(updateUserInput UsersInput, uaaUsers *uaa.Users) (*RoleUsers, error) - AddUser func(updateUserInput UsersInput, userName, userGUID string) error - RemoveUser func(updateUserInput UsersInput, userName, userGUID string) error + SpaceGUID string + OrgGUID string + LdapUsers, Users, LdapGroupNames, SamlGroupNames, SamlUsers []string + SpaceName string + OrgName string + RemoveUsers bool + ListUsers func(updateUserInput UsersInput, uaaUsers *uaa.Users) (*RoleUsers, error) + AddUser func(updateUserInput UsersInput, userName, userGUID string) error + RemoveUser func(updateUserInput UsersInput, userName, userGUID string) error } func (u *UsersInput) UniqueUsers() []string { @@ -33,6 +33,10 @@ func (u *UsersInput) UniqueLdapGroupNames() []string { return uniqueSlice(u.LdapGroupNames) } +func (u *UsersInput) UniqueSamlGroupNames() []string { + return uniqueSlice(u.SamlGroupNames) +} + func uniqueSlice(input []string) []string { unique := make(map[string]string) output := []string{}