| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355 |
- // Copyright 2019 Yunion
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- package ldap
- import (
- "context"
- "database/sql"
- "github.com/go-ldap/ldap/v3"
- "yunion.io/x/jsonutils"
- "yunion.io/x/log"
- "yunion.io/x/pkg/errors"
- api "yunion.io/x/onecloud/pkg/apis/identity"
- "yunion.io/x/onecloud/pkg/cloudcommon/db"
- "yunion.io/x/onecloud/pkg/keystone/models"
- "yunion.io/x/onecloud/pkg/keystone/options"
- "yunion.io/x/onecloud/pkg/util/ldaputils"
- )
- func (drv *SLDAPDriver) Probe(ctx context.Context) error {
- cli, err := drv.getClient()
- if err != nil {
- return errors.Wrap(err, "getClient")
- }
- defer cli.Close()
- return nil
- }
- func (drv *SLDAPDriver) Sync(ctx context.Context) error {
- cli, err := drv.getClient()
- if err != nil {
- return errors.Wrap(err, "getClient")
- }
- defer cli.Close()
- if len(drv.ldapConfig.DomainTreeDN) > 0 {
- return drv.syncDomains(ctx, cli)
- } else {
- return drv.syncSingleDomain(ctx, cli)
- }
- }
- func (drv *SLDAPDriver) syncSingleDomain(ctx context.Context, cli *ldaputils.SLDAPClient) error {
- var domain *models.SDomain
- if len(drv.TargetDomainId) > 0 {
- targetDomain, err := models.DomainManager.FetchDomainById(drv.TargetDomainId)
- if err != nil && err != sql.ErrNoRows {
- return errors.Wrap(err, "models.DomainManager.FetchDomainById")
- }
- if targetDomain == nil {
- log.Warningln("target domain not exist!")
- } else {
- domain = targetDomain
- }
- }
- if domain == nil {
- domainInfo := SDomainInfo{DN: drv.ldapConfig.Suffix, Id: api.DefaultRemoteDomainId, Name: drv.IdpName}
- newDomain, err := drv.syncDomainInfo(ctx, domainInfo)
- if err != nil {
- return errors.Wrap(err, "syncDomainInfo")
- }
- domain = newDomain
- }
- userIdMap, err := drv.syncUsers(ctx, cli, domain.Id, drv.getUserTreeDN())
- if err != nil {
- return errors.Wrap(err, "syncUsers")
- }
- err = drv.syncGroups(ctx, cli, domain.Id, drv.getGroupTreeDN(), userIdMap)
- if err != nil {
- return errors.Wrap(err, "syncGroups")
- }
- return nil
- }
- func (drv *SLDAPDriver) searchDomainEntries(cli *ldaputils.SLDAPClient, domainid string, entryFunc func(*ldap.Entry) error) error {
- attrMap := make(map[string]string)
- if len(domainid) > 0 {
- attrMap[drv.ldapConfig.DomainIdAttribute] = domainid
- }
- _, err := cli.Search(drv.getDomainTreeDN(),
- drv.ldapConfig.DomainObjectclass,
- attrMap,
- drv.ldapConfig.DomainFilter,
- drv.domainAttributeList(),
- drv.domainQueryScope(),
- options.Options.LdapSearchPageSize, 0,
- func(offset uint32, entry *ldap.Entry) error {
- return entryFunc(entry)
- },
- )
- if err != nil {
- return errors.Wrap(err, "Search")
- }
- return nil
- }
- func (drv *SLDAPDriver) syncDomains(ctx context.Context, cli *ldaputils.SLDAPClient) error {
- domainIds := make([]string, 0)
- err := drv.searchDomainEntries(cli, "", func(entry *ldap.Entry) error {
- domainInfo := drv.entry2Domain(entry)
- err := domainInfo.isValid()
- if err != nil {
- log.Errorf("invalid domainInfo: %s, skip", err)
- return nil
- }
- domain, err := drv.syncDomainInfo(ctx, domainInfo)
- if err != nil {
- return errors.Wrap(err, "syncDomainInfo")
- }
- domainIds = append(domainIds, domain.Id)
- userIdMap, err := drv.syncUsers(ctx, cli, domain.Id, domainInfo.DN)
- if err != nil {
- return errors.Wrap(err, "syncUsers")
- }
- err = drv.syncGroups(ctx, cli, domain.Id, domainInfo.DN, userIdMap)
- if err != nil {
- return errors.Wrap(err, "syncGroups")
- }
- return nil
- })
- if err != nil {
- return errors.Wrap(err, "searchDomainEntries")
- }
- // remove any obsolete domains
- obsoleteDomainIds, err := models.IdmappingManager.FetchPublicIdsExcludes(drv.IdpId, api.IdMappingEntityDomain, domainIds)
- if err != nil {
- return errors.Wrap(err, "models.IdmappingManager.FetchPublicIdsExcludes")
- }
- for _, obsoleteDomainId := range obsoleteDomainIds {
- obsoleteDomain, err := models.DomainManager.FetchDomainById(obsoleteDomainId)
- if err != nil {
- log.Errorf("models.DomainManager.FetchDomainById error %s", err)
- continue
- }
- obsoleteDomain.AppendDescription(models.GetDefaultAdminCred(), "domain source removed")
- // unlink with Idp
- err = obsoleteDomain.UnlinkIdp(drv.IdpId)
- if err != nil {
- log.Errorf("obsoleteDomain.UnlinkIdp error %s", err)
- continue
- }
- // remove any user and groups
- err = obsoleteDomain.DeleteUserGroups(ctx, models.GetDefaultAdminCred())
- if err != nil {
- log.Errorf("domain.DeleteUserGroups error %s", err)
- continue
- }
- err = obsoleteDomain.ValidateDeleteCondition(ctx, nil)
- if err != nil {
- log.Errorf("obsoleteDomain.ValidateDeleteCondition error %s", err)
- continue
- }
- err = obsoleteDomain.Delete(ctx, models.GetDefaultAdminCred())
- if err != nil {
- log.Errorf("obsoleteDomain.Delete error %s", err)
- continue
- }
- }
- return nil
- }
- func (drv *SLDAPDriver) syncDomainInfo(ctx context.Context, info SDomainInfo) (*models.SDomain, error) {
- idp, err := models.IdentityProviderManager.FetchIdentityProviderById(drv.IdpId)
- if err != nil {
- return nil, errors.Wrap(err, "drv.GetIdentityProvider")
- }
- return idp.SyncOrCreateDomain(ctx, info.Id, info.Name, info.DN, true)
- }
- func (drv *SLDAPDriver) syncUsers(ctx context.Context, cli *ldaputils.SLDAPClient, domainId string, baseDN string) (map[string]string, error) {
- userIds := make([]string, 0)
- userIdMap := make(map[string]string)
- _, err := cli.Search(baseDN,
- drv.ldapConfig.UserObjectclass,
- nil,
- drv.ldapConfig.UserFilter,
- drv.userAttributeList(),
- drv.userQueryScope(),
- options.Options.LdapSearchPageSize, 0,
- func(offset uint32, entry *ldap.Entry) error {
- userInfo := drv.entry2User(entry)
- err := userInfo.isValid()
- if err != nil {
- log.Debugf("userInfo is invalid: %s, skip", err)
- return nil
- }
- userId, err := drv.syncUserDB(ctx, userInfo, domainId)
- if err != nil {
- return errors.Wrap(err, "syncUserDB")
- }
- userIds = append(userIds, userId)
- if drv.ldapConfig.GroupMembersAreIds {
- userIdMap[userInfo.Id] = userId
- } else {
- userIdMap[userInfo.DN] = userId
- }
- return nil
- },
- )
- if err != nil {
- return nil, errors.Wrap(err, "searchLDAP")
- }
- deleteUsers, err := models.UserManager.FetchUsersInDomain(domainId, userIds)
- if err != nil {
- return nil, errors.Wrap(err, "models.UserManager.FetchUserIdsInDomain")
- }
- for i := range deleteUsers {
- if !deleteUsers[i].LinkedWithIdp(drv.IdpId) {
- continue
- }
- err := deleteUsers[i].UnlinkIdp(drv.IdpId)
- if err != nil {
- log.Errorf("deleteUser.UnlinkIdp error %s", err)
- continue
- }
- err = deleteUsers[i].ValidateDeleteCondition(ctx, nil)
- if err != nil {
- log.Errorf("deleteUser.ValidateDeleteCondition error %s", err)
- continue
- }
- err = deleteUsers[i].Delete(ctx, models.GetDefaultAdminCred())
- if err != nil {
- log.Errorf("deleteUser.Delete error %s", err)
- continue
- }
- }
- return userIdMap, nil
- }
- func (drv *SLDAPDriver) syncUserDB(ctx context.Context, ui SUserInfo, domainId string) (string, error) {
- idp, err := models.IdentityProviderManager.FetchIdentityProviderById(drv.IdpId)
- if err != nil {
- return "", errors.Wrap(err, "models.IdentityProviderManager.FetchIdentityProviderById")
- }
- enableDefault := !drv.ldapConfig.DisableUserOnImport
- if !ui.Enabled {
- enableDefault = false
- }
- usr, err := idp.SyncOrCreateUser(ctx, ui.Id, ui.Name, domainId, enableDefault, func(user *models.SUser) {
- // LDAP user is always enabled
- // if ui.Enabled {
- // user.Enabled = tristate.True
- // } else {
- // user.Enabled = tristate.False
- // }
- if val, ok := ui.Extra["email"]; ok && len(val) > 0 && len(user.Email) == 0 {
- user.Email = val
- }
- if val, ok := ui.Extra["displayname"]; ok && len(val) > 0 && len(user.Displayname) == 0 {
- user.Displayname = val
- }
- if val, ok := ui.Extra["mobile"]; ok && len(val) > 0 && len(user.Mobile) == 0 {
- user.Mobile = val
- }
- tags := make(map[string]interface{})
- for k, v := range ui.Extra {
- tags[db.CLOUD_TAG_PREFIX+k] = v
- }
- err := user.SetAllMetadata(ctx, tags, models.GetDefaultAdminCred())
- if err != nil {
- log.Errorf("SetCloudMetadataAll %s fail %s", jsonutils.Marshal(tags), err)
- }
- })
- if err != nil {
- return "", errors.Wrap(err, "idp.SyncOrCreateUser")
- }
- return usr.Id, nil
- }
- func (drv *SLDAPDriver) syncGroups(ctx context.Context, cli *ldaputils.SLDAPClient, domainId string, baseDN string, userIdMap map[string]string) error {
- groupIds := make([]string, 0)
- _, err := cli.Search(baseDN,
- drv.ldapConfig.GroupObjectclass,
- nil,
- drv.ldapConfig.GroupFilter,
- drv.groupAttributeList(),
- drv.groupQueryScope(),
- options.Options.LdapSearchPageSize, 0,
- func(offset uint32, entry *ldap.Entry) error {
- groupInfo := drv.entry2Group(entry)
- err := groupInfo.isValid()
- if err != nil {
- log.Errorf("invalid group info: %s, skip", err)
- return nil
- }
- groupId, err := drv.syncGroupDB(ctx, groupInfo, domainId, userIdMap)
- if err != nil {
- return errors.Wrap(err, "syncGroupDB")
- }
- groupIds = append(groupIds, groupId)
- return nil
- },
- )
- if err != nil {
- return errors.Wrap(err, "searchLDAP")
- }
- deleteGroups, err := models.GroupManager.FetchGroupsInDomain(domainId, groupIds)
- if err != nil {
- return errors.Wrap(err, "models.GroupManager.FetchGroupsInDomain")
- }
- for i := range deleteGroups {
- if !deleteGroups[i].LinkedWithIdp(drv.IdpId) {
- continue
- }
- err := deleteGroups[i].UnlinkIdp(drv.IdpId)
- if err != nil {
- log.Errorf("deleteGroup.UnlinkIdp error %s", err)
- continue
- }
- err = deleteGroups[i].ValidateDeleteCondition(ctx, nil)
- if err != nil {
- log.Errorf("deleteGroup.ValidateDeleteCondition error %s", err)
- continue
- }
- err = deleteGroups[i].Delete(ctx, models.GetDefaultAdminCred())
- if err != nil {
- log.Errorf("deleteGroup.Delete error %s", err)
- continue
- }
- }
- return nil
- }
- func (drv *SLDAPDriver) syncGroupDB(ctx context.Context, groupInfo SGroupInfo, domainId string, userIdMap map[string]string) (string, error) {
- grp, err := models.GroupManager.RegisterExternalGroup(ctx, drv.IdpId, domainId, groupInfo.Id, groupInfo.Name)
- if err != nil {
- return "", errors.Wrap(err, "GroupManager.RegisterExternalGroup")
- }
- userIds := make([]string, 0)
- for _, userExtId := range groupInfo.Members {
- if uid, ok := userIdMap[userExtId]; ok {
- userIds = append(userIds, uid)
- }
- }
- models.UsergroupManager.SyncGroupUsers(ctx, models.GetDefaultAdminCred(), grp.Id, userIds)
- return grp.Id, nil
- }
|