github.com/cs3org/reva/v2@v2.27.7/pkg/utils/ldap/identity.go (about) 1 // Copyright 2022 CERN 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 // In applying this license, CERN does not waive the privileges and immunities 16 // granted to it by virtue of its status as an Intergovernmental Organization 17 // or submit itself to any jurisdiction. 18 19 package ldap 20 21 import ( 22 "fmt" 23 "strings" 24 25 identityUser "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" 26 "github.com/cs3org/reva/v2/pkg/errtypes" 27 "github.com/go-ldap/ldap/v3" 28 "github.com/google/uuid" 29 "github.com/pkg/errors" 30 "github.com/rs/zerolog" 31 ) 32 33 // Identity provides methods to query users and groups from an LDAP server 34 type Identity struct { 35 User userConfig `mapstructure:",squash"` 36 Group groupConfig `mapstructure:",squash"` 37 } 38 39 type userConfig struct { 40 BaseDN string `mapstructure:"user_base_dn"` 41 Scope string `mapstructure:"user_search_scope"` 42 scopeVal int 43 Filter string `mapstructure:"user_filter"` 44 Objectclass string `mapstructure:"user_objectclass"` 45 DisableMechanism string `mapstructure:"user_disable_mechanism"` 46 EnabledProperty string `mapstructure:"user_enabled_property"` 47 UserTypeProperty string `mapstructure:"user_type_property"` 48 Schema userSchema `mapstructure:"user_schema"` 49 SubstringFilterType string `mapstructure:"user_substring_filter_type"` 50 substringFilterVal int 51 } 52 53 type groupConfig struct { 54 BaseDN string `mapstructure:"group_base_dn"` 55 Scope string `mapstructure:"group_search_scope"` 56 scopeVal int 57 Filter string `mapstructure:"group_filter"` 58 Objectclass string `mapstructure:"group_objectclass"` 59 Schema groupSchema `mapstructure:"group_schema"` 60 SubstringFilterType string `mapstructure:"group_substring_filter_type"` 61 substringFilterVal int 62 // LocalDisabledDN contains the full DN of a group that contains disabled users. 63 LocalDisabledDN string `mapstructure:"group_local_disabled_dn"` 64 } 65 66 type groupSchema struct { 67 // GID is an immutable group id, see https://docs.microsoft.com/en-us/azure/active-directory/hybrid/plan-connect-design-concepts 68 ID string `mapstructure:"id"` 69 IDIsOctetString bool `mapstructure:"idIsOctetString"` 70 // CN is the group name, typically `cn`, `gid` or `samaccountname` 71 Groupname string `mapstructure:"groupName"` 72 // Mail is the email address of a group 73 Mail string `mapstructure:"mail"` 74 // Displayname is the Human readable name, e.g. `Database Admins` 75 DisplayName string `mapstructure:"displayName"` 76 // GIDNumber is a numeric id that maps to a filesystem gid, eg. 654321 77 GIDNumber string `mapstructure:"gidNumber"` 78 Member string `mapstructure:"member"` 79 } 80 81 type userSchema struct { 82 // UID is an immutable user id, see https://docs.microsoft.com/en-us/azure/active-directory/hybrid/plan-connect-design-concepts 83 ID string `mapstructure:"id"` 84 // UIDIsOctetString set this to true i the values of the UID attribute are returned as OCTET STRING values (binary byte sequences) 85 // by the Directory Service. This is e.g. the case for the 'objectGUID' and 'ms-DS-ConsistencyGuid' Attributes in AD 86 IDIsOctetString bool `mapstructure:"idIsOctetString"` 87 // Name is the username, typically `cn`, `uid` or `samaccountname` 88 Username string `mapstructure:"userName"` 89 // Mail is the email address of a user 90 Mail string `mapstructure:"mail"` 91 // Displayname is the Human readable name, e.g. `Albert Einstein` 92 DisplayName string `mapstructure:"displayName"` 93 // UIDNumber is a numeric id that maps to a filesystem uid, eg. 123546 94 UIDNumber string `mapstructure:"uidNumber"` 95 // GIDNumber is a numeric id that maps to a filesystem gid, eg. 654321 96 GIDNumber string `mapstructure:"gidNumber"` 97 } 98 99 // Default userConfig (somewhat inspired by Active Directory) 100 var userDefaults = userConfig{ 101 Scope: "sub", 102 Objectclass: "posixAccount", 103 Schema: userSchema{ 104 ID: "ms-DS-ConsistencyGuid", 105 IDIsOctetString: false, 106 Username: "cn", 107 Mail: "mail", 108 DisplayName: "displayName", 109 UIDNumber: "uidNumber", 110 GIDNumber: "gidNumber", 111 }, 112 SubstringFilterType: "initial", 113 } 114 115 // Default groupConfig (Active Directory) 116 var groupDefaults = groupConfig{ 117 Scope: "sub", 118 Objectclass: "posixGroup", 119 Schema: groupSchema{ 120 ID: "objectGUID", 121 IDIsOctetString: false, 122 Groupname: "cn", 123 Mail: "mail", 124 DisplayName: "cn", 125 GIDNumber: "gidNumber", 126 Member: "memberUid", 127 }, 128 SubstringFilterType: "initial", 129 } 130 131 // New initializes the default config 132 func New() Identity { 133 return Identity{ 134 User: userDefaults, 135 Group: groupDefaults, 136 } 137 } 138 139 // Setup initialzes some properties that can't be initialized from the 140 // mapstructure based config. Currently it just converts the LDAP search scope 141 // strings from the config to the integer constants expected by the ldap API 142 func (i *Identity) Setup() error { 143 var err error 144 if i.User.scopeVal, err = stringToScope(i.User.Scope); err != nil { 145 return fmt.Errorf("error configuring user scope: %w", err) 146 } 147 148 if i.Group.scopeVal, err = stringToScope(i.Group.Scope); err != nil { 149 return fmt.Errorf("error configuring group scope: %w", err) 150 } 151 152 if i.User.substringFilterVal, err = stringToFilterType(i.User.SubstringFilterType); err != nil { 153 return fmt.Errorf("error configuring user substring filter type: %w", err) 154 } 155 156 if i.Group.substringFilterVal, err = stringToFilterType(i.Group.SubstringFilterType); err != nil { 157 return fmt.Errorf("error configuring group substring filter type: %w", err) 158 } 159 160 switch i.User.DisableMechanism { 161 case "group": 162 if i.Group.LocalDisabledDN == "" { 163 return fmt.Errorf("error configuring disable mechanism, disabled group DN not set") 164 } 165 case "attribute": 166 if i.User.EnabledProperty == "" { 167 return fmt.Errorf("error configuring disable mechanism, enabled property not set") 168 } 169 case "", "none": 170 default: 171 return fmt.Errorf("invalid disable mechanism setting: %s", i.User.DisableMechanism) 172 } 173 174 return nil 175 } 176 177 // GetLDAPUserByID looks up a user by the supplied Id. Returns the corresponding 178 // ldap.Entry 179 func (i *Identity) GetLDAPUserByID(log *zerolog.Logger, lc ldap.Client, id string) (*ldap.Entry, error) { 180 var filter string 181 var err error 182 if filter, err = i.getUserFilter(id); err != nil { 183 return nil, err 184 } 185 return i.GetLDAPUserByFilter(log, lc, filter) 186 } 187 188 // GetLDAPUserByAttribute looks up a single user by attribute (can be "mail", 189 // "uid", "gid", "username" or "userid"). Returns the corresponding ldap.Entry 190 func (i *Identity) GetLDAPUserByAttribute(log *zerolog.Logger, lc ldap.Client, attribute, value string) (*ldap.Entry, error) { 191 var filter string 192 var err error 193 if filter, err = i.getUserAttributeFilter(attribute, value); err != nil { 194 return nil, err 195 } 196 return i.GetLDAPUserByFilter(log, lc, filter) 197 } 198 199 // GetLDAPUserByFilter looks up a single user by the supplied LDAP filter 200 // returns the corresponding ldap.Entry 201 func (i *Identity) GetLDAPUserByFilter(log *zerolog.Logger, lc ldap.Client, filter string) (*ldap.Entry, error) { 202 searchRequest := ldap.NewSearchRequest( 203 i.User.BaseDN, i.User.scopeVal, ldap.NeverDerefAliases, 1, 0, false, 204 filter, 205 []string{ 206 i.User.Schema.DisplayName, 207 i.User.Schema.ID, 208 i.User.Schema.Mail, 209 i.User.Schema.Username, 210 i.User.Schema.UIDNumber, 211 i.User.Schema.GIDNumber, 212 i.User.EnabledProperty, 213 i.User.UserTypeProperty, 214 }, 215 nil, 216 ) 217 log.Debug().Str("backend", "ldap").Str("basedn", i.User.BaseDN).Str("filter", filter).Int("scope", i.User.scopeVal).Msg("LDAP Search") 218 res, err := lc.Search(searchRequest) 219 if err != nil { 220 log.Debug().Str("backend", "ldap").Err(err).Str("userfilter", filter).Msg("Error looking up user by filter") 221 var errmsg string 222 if lerr, ok := err.(*ldap.Error); ok { 223 if lerr.ResultCode == ldap.LDAPResultSizeLimitExceeded { 224 errmsg = fmt.Sprintf("too many results searching for user '%s'", filter) 225 } 226 } 227 return nil, errtypes.NotFound(errmsg) 228 } 229 if len(res.Entries) == 0 { 230 return nil, errtypes.NotFound(filter) 231 } 232 233 return res.Entries[0], nil 234 } 235 236 // GetLDAPUserByDN looks up a single user by the supplied LDAP DN 237 // returns the corresponding ldap.Entry 238 func (i *Identity) GetLDAPUserByDN(log *zerolog.Logger, lc ldap.Client, dn string) (*ldap.Entry, error) { 239 filter := fmt.Sprintf("(objectclass=%s)", i.User.Objectclass) 240 if i.User.Filter != "" { 241 filter = fmt.Sprintf("(&%s%s)", i.User.Filter, filter) 242 } 243 searchRequest := ldap.NewSearchRequest( 244 dn, i.User.scopeVal, ldap.NeverDerefAliases, 1, 0, false, 245 filter, 246 []string{ 247 i.User.Schema.DisplayName, 248 i.User.Schema.ID, 249 i.User.Schema.Mail, 250 i.User.Schema.Username, 251 i.User.Schema.UIDNumber, 252 i.User.Schema.GIDNumber, 253 i.User.EnabledProperty, 254 }, 255 nil, 256 ) 257 log.Debug().Str("backend", "ldap").Str("basedn", dn).Str("filter", filter).Int("scope", i.User.scopeVal).Msg("LDAP Search") 258 res, err := lc.Search(searchRequest) 259 if err != nil { 260 log.Debug().Str("backend", "ldap").Err(err).Str("dn", dn).Msg("Error looking up user by DN") 261 return nil, errtypes.NotFound(dn) 262 } 263 if len(res.Entries) == 0 { 264 return nil, errtypes.NotFound(dn) 265 } 266 267 return res.Entries[0], nil 268 } 269 270 // GetLDAPUsers searches for users using a prefix-substring match on the user 271 // attributes. Returns a slice of matching ldap.Entries 272 func (i *Identity) GetLDAPUsers(log *zerolog.Logger, lc ldap.Client, query string) ([]*ldap.Entry, error) { 273 filter := i.getUserFindFilter(query) 274 searchRequest := ldap.NewSearchRequest( 275 i.User.BaseDN, 276 i.User.scopeVal, ldap.NeverDerefAliases, 0, 0, false, 277 filter, 278 []string{ 279 i.User.Schema.ID, 280 i.User.Schema.Username, 281 i.User.Schema.Mail, 282 i.User.Schema.DisplayName, 283 i.User.Schema.UIDNumber, 284 i.User.Schema.GIDNumber, 285 i.User.EnabledProperty, 286 i.User.UserTypeProperty, 287 }, 288 nil, 289 ) 290 291 log.Debug().Str("backend", "ldap").Str("basedn", i.User.BaseDN).Str("filter", filter).Int("scope", i.User.scopeVal).Msg("LDAP Search") 292 sr, err := lc.Search(searchRequest) 293 if err != nil { 294 log.Debug().Str("backend", "ldap").Err(err).Str("filter", filter).Msg("Error searching users") 295 return nil, errtypes.NotFound(query) 296 } 297 return sr.Entries, nil 298 } 299 300 // IsLDAPUserInDisabledGroup checkes if the user is in the disabled group. 301 func (i *Identity) IsLDAPUserInDisabledGroup(log *zerolog.Logger, lc ldap.Client, userEntry *ldap.Entry) bool { 302 // Check if we need to do this here because the configuration is local to Identity. 303 if i.User.DisableMechanism != "group" { 304 return false 305 } 306 307 filter := fmt.Sprintf("(&(objectClass=groupOfNames)(%s=%s))", i.Group.Schema.Member, userEntry.DN) 308 searchRequest := ldap.NewSearchRequest( 309 i.Group.LocalDisabledDN, 310 i.Group.scopeVal, 311 ldap.NeverDerefAliases, 0, 0, false, 312 filter, 313 []string{i.Group.Schema.ID}, 314 nil, 315 ) 316 log.Debug().Str("backend", "ldap").Str("basedn", i.Group.LocalDisabledDN).Str("filter", filter).Int("scope", i.Group.scopeVal).Msg("LDAP Search") 317 sr, err := lc.Search(searchRequest) 318 if err != nil { 319 log.Error().Str("backend", "ldap").Err(err).Str("filter", filter).Msg("Error looking up error group") 320 // Err on the side of caution. 321 return true 322 } 323 324 return len(sr.Entries) > 0 325 } 326 327 // GetLDAPUserGroups looks up the group member ship of the supplied LDAP user entry. 328 // Returns a slice of strings with groupids 329 func (i *Identity) GetLDAPUserGroups(log *zerolog.Logger, lc ldap.Client, userEntry *ldap.Entry) ([]string, error) { 330 var memberValue string 331 332 if strings.ToLower(i.Group.Objectclass) == "posixgroup" { 333 // posixGroup usually means that the member attribute just contains the username 334 memberValue = userEntry.GetEqualFoldAttributeValue(i.User.Schema.Username) 335 } else { 336 // In all other case we assume the member Attribute to contain full LDAP DNs 337 memberValue = userEntry.DN 338 } 339 340 filter := i.getGroupMemberFilter(memberValue) 341 searchRequest := ldap.NewSearchRequest( 342 i.Group.BaseDN, i.Group.scopeVal, 343 ldap.NeverDerefAliases, 0, 0, false, 344 filter, 345 []string{i.Group.Schema.ID}, 346 nil, 347 ) 348 349 log.Debug().Str("backend", "ldap").Str("basedn", i.Group.BaseDN).Str("filter", filter).Int("scope", i.Group.scopeVal).Msg("LDAP Search") 350 sr, err := lc.Search(searchRequest) 351 if err != nil { 352 log.Debug().Str("backend", "ldap").Err(err).Str("filter", filter).Msg("Error looking up group memberships") 353 return []string{}, err 354 } 355 356 groups := make([]string, 0, len(sr.Entries)) 357 for _, entry := range sr.Entries { 358 // FIXME this makes the users groups use the cn, not an immutable id 359 // FIXME 1. use the memberof or members attribute of a user to get the groups 360 // FIXME 2. ook up the id for each group 361 var groupID string 362 if i.Group.Schema.IDIsOctetString { 363 raw := entry.GetEqualFoldRawAttributeValue(i.Group.Schema.ID) 364 value, err := uuid.FromBytes(raw) 365 if err != nil { 366 return nil, err 367 } 368 groupID = value.String() 369 } else { 370 groupID = entry.GetEqualFoldAttributeValue(i.Group.Schema.ID) 371 } 372 373 groups = append(groups, groupID) 374 } 375 return groups, nil 376 } 377 378 // GetLDAPGroupByID looks up a group by the supplied Id. Returns the corresponding 379 // ldap.Entry 380 func (i *Identity) GetLDAPGroupByID(log *zerolog.Logger, lc ldap.Client, id string) (*ldap.Entry, error) { 381 var filter string 382 var err error 383 if filter, err = i.getGroupFilter(id); err != nil { 384 return nil, err 385 } 386 return i.GetLDAPGroupByFilter(log, lc, filter) 387 } 388 389 // GetLDAPGroupByAttribute looks up a single group by attribute (can be "mail", "gid_number", 390 // "display_name", "group_name", "group_id"). Returns the corresponding ldap.Entry 391 func (i *Identity) GetLDAPGroupByAttribute(log *zerolog.Logger, lc ldap.Client, attribute, value string) (*ldap.Entry, error) { 392 var filter string 393 var err error 394 if filter, err = i.getGroupAttributeFilter(attribute, value); err != nil { 395 return nil, err 396 } 397 return i.GetLDAPGroupByFilter(log, lc, filter) 398 } 399 400 // GetLDAPGroupByFilter looks up a single group by the supplied LDAP filter 401 // returns the corresponding ldap.Entry 402 func (i *Identity) GetLDAPGroupByFilter(log *zerolog.Logger, lc ldap.Client, filter string) (*ldap.Entry, error) { 403 searchRequest := ldap.NewSearchRequest( 404 i.Group.BaseDN, i.Group.scopeVal, ldap.NeverDerefAliases, 1, 0, false, 405 filter, 406 []string{ 407 i.Group.Schema.DisplayName, 408 i.Group.Schema.ID, 409 i.Group.Schema.Mail, 410 i.Group.Schema.Groupname, 411 i.Group.Schema.Member, 412 i.Group.Schema.GIDNumber, 413 }, 414 nil, 415 ) 416 417 log.Debug().Str("backend", "ldap").Str("basedn", i.Group.BaseDN).Str("filter", filter).Int("scope", i.Group.scopeVal).Msg("LDAP Search") 418 res, err := lc.Search(searchRequest) 419 if err != nil { 420 log.Debug().Str("backend", "ldap").Err(err).Str("filter", filter).Msg("Error looking up group by filter") 421 var errmsg string 422 if lerr, ok := err.(*ldap.Error); ok { 423 if lerr.ResultCode == ldap.LDAPResultSizeLimitExceeded { 424 errmsg = fmt.Sprintf("too many results searching for group '%s'", filter) 425 } 426 } 427 return nil, errtypes.NotFound(errmsg) 428 } 429 if len(res.Entries) == 0 { 430 return nil, errtypes.NotFound(filter) 431 } 432 433 return res.Entries[0], nil 434 } 435 436 // GetLDAPGroups searches for groups using a prefix-substring match on the group 437 // attributes. Returns a slice of matching ldap.Entries 438 func (i *Identity) GetLDAPGroups(log *zerolog.Logger, lc ldap.Client, query string) ([]*ldap.Entry, error) { 439 searchRequest := ldap.NewSearchRequest( 440 i.Group.BaseDN, 441 ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, 442 i.getGroupFindFilter(query), 443 []string{ 444 i.Group.Schema.DisplayName, 445 i.Group.Schema.ID, 446 i.Group.Schema.Mail, 447 i.Group.Schema.Groupname, 448 i.Group.Schema.GIDNumber, 449 }, 450 nil, 451 ) 452 453 sr, err := lc.Search(searchRequest) 454 if err != nil { 455 log.Debug().Str("backend", "ldap").Err(err).Str("query", query).Msg("Error search for groups") 456 return nil, errtypes.NotFound(query) 457 } 458 return sr.Entries, nil 459 } 460 461 // GetLDAPGroupMembers looks up all members of the supplied LDAP group entry and returns the 462 // corresponding LDAP user entries 463 func (i *Identity) GetLDAPGroupMembers(log *zerolog.Logger, lc ldap.Client, group *ldap.Entry) ([]*ldap.Entry, error) { 464 members := group.GetEqualFoldAttributeValues(i.Group.Schema.Member) 465 log.Debug().Str("dn", group.DN).Interface("member", members).Msg("Get Group members") 466 memberEntries := make([]*ldap.Entry, 0, len(members)) 467 for _, member := range members { 468 var e *ldap.Entry 469 var err error 470 if strings.ToLower(i.Group.Objectclass) == "posixgroup" { 471 e, err = i.GetLDAPUserByAttribute(log, lc, "username", member) 472 } else { 473 e, err = i.GetLDAPUserByDN(log, lc, member) 474 } 475 if err != nil { 476 log.Warn().Err(err).Interface("member", member).Msg("Failed read user entry for member") 477 continue 478 } 479 memberEntries = append(memberEntries, e) 480 } 481 482 return memberEntries, nil 483 } 484 485 func filterEscapeBinaryUUID(value uuid.UUID) string { 486 filtered := "" 487 for _, b := range value { 488 filtered = fmt.Sprintf("%s\\%02x", filtered, b) 489 } 490 return filtered 491 } 492 493 func (i *Identity) getUserFilter(uid string) (string, error) { 494 var escapedUUID string 495 if i.User.Schema.IDIsOctetString { 496 id, err := uuid.Parse(uid) 497 if err != nil { 498 err := errors.Wrap(err, fmt.Sprintf("error parsing OpaqueID '%s' as UUID", uid)) 499 return "", err 500 } 501 escapedUUID = filterEscapeBinaryUUID(id) 502 } else { 503 escapedUUID = ldap.EscapeFilter(uid) 504 } 505 506 return fmt.Sprintf("(&%s(objectclass=%s)(%s=%s))", 507 i.User.Filter, 508 i.User.Objectclass, 509 i.User.Schema.ID, 510 escapedUUID, 511 ), nil 512 } 513 514 func (i *Identity) getUserAttributeFilter(attribute, value string) (string, error) { 515 switch attribute { 516 case "mail": 517 attribute = i.User.Schema.Mail 518 case "uid": 519 attribute = i.User.Schema.UIDNumber 520 case "gid": 521 attribute = i.User.Schema.GIDNumber 522 case "username": 523 attribute = i.User.Schema.Username 524 case "userid": 525 attribute = i.User.Schema.ID 526 default: 527 return "", errors.New("ldap: invalid field " + attribute) 528 } 529 if attribute == i.User.Schema.ID && i.User.Schema.IDIsOctetString { 530 id, err := uuid.Parse(value) 531 if err != nil { 532 err := errors.Wrap(err, fmt.Sprintf("error parsing OpaqueID '%s' as UUID", value)) 533 return "", err 534 } 535 value = filterEscapeBinaryUUID(id) 536 } else { 537 value = ldap.EscapeFilter(value) 538 } 539 return fmt.Sprintf("(&%s(objectclass=%s)(%s=%s)%s)", 540 i.User.Filter, 541 i.User.Objectclass, 542 attribute, 543 value, 544 i.disabledFilter(), 545 ), nil 546 } 547 548 func (i *Identity) disabledFilter() string { 549 if i.User.DisableMechanism == "attribute" { 550 return fmt.Sprintf("(!(%s=FALSE))", i.User.EnabledProperty) 551 } 552 return "" 553 } 554 555 // getUserFindFilter construct a LDAP filter to perform a prefix-substring 556 // search for users. 557 func (i *Identity) getUserFindFilter(query string) string { 558 searchAttrs := []string{ 559 i.User.Schema.Mail, 560 i.User.Schema.DisplayName, 561 i.User.Schema.Username, 562 } 563 var filter, squery string 564 switch i.User.substringFilterVal { 565 case ldap.FilterSubstringsInitial: 566 squery = fmt.Sprintf("%s*", ldap.EscapeFilter(query)) 567 case ldap.FilterSubstringsAny: 568 squery = fmt.Sprintf("*%s*", ldap.EscapeFilter(query)) 569 case ldap.FilterSubstringsFinal: 570 squery = fmt.Sprintf("*%s", ldap.EscapeFilter(query)) 571 } 572 for _, attr := range searchAttrs { 573 filter = fmt.Sprintf("%s(%s=%s)", filter, attr, squery) 574 } 575 // substring search for UUID is not possible 576 filter = fmt.Sprintf("%s(%s=%s)", filter, i.User.Schema.ID, ldap.EscapeFilter(query)) 577 578 return fmt.Sprintf("(&%s(objectclass=%s)(|%s))", 579 i.User.Filter, 580 i.User.Objectclass, 581 filter, 582 ) 583 } 584 585 // getGroupFindFilter construct a LDAP filter to perform a prefix-substring 586 // search for groups. 587 func (i *Identity) getGroupFindFilter(query string) string { 588 searchAttrs := []string{ 589 i.Group.Schema.Mail, 590 i.Group.Schema.DisplayName, 591 i.Group.Schema.Groupname, 592 } 593 var filter, squery string 594 switch i.Group.substringFilterVal { 595 case ldap.FilterSubstringsInitial: 596 squery = fmt.Sprintf("%s*", ldap.EscapeFilter(query)) 597 case ldap.FilterSubstringsAny: 598 squery = fmt.Sprintf("*%s*", ldap.EscapeFilter(query)) 599 case ldap.FilterSubstringsFinal: 600 squery = fmt.Sprintf("*%s", ldap.EscapeFilter(query)) 601 } 602 for _, attr := range searchAttrs { 603 filter = fmt.Sprintf("%s(%s=%s)", filter, attr, squery) 604 } 605 // substring search for UUID is not possible 606 filter = fmt.Sprintf("%s(%s=%s)", filter, i.Group.Schema.ID, ldap.EscapeFilter(query)) 607 608 return fmt.Sprintf("(&%s(objectclass=%s)(|%s))", 609 i.Group.Filter, 610 i.Group.Objectclass, 611 filter, 612 ) 613 } 614 615 func stringToScope(scope string) (int, error) { 616 var s int 617 switch scope { 618 case "sub": 619 s = ldap.ScopeWholeSubtree 620 case "one": 621 s = ldap.ScopeSingleLevel 622 case "base": 623 s = ldap.ScopeBaseObject 624 default: 625 return 0, fmt.Errorf("invalid Scope '%s'", scope) 626 } 627 return s, nil 628 } 629 630 func stringToFilterType(t string) (int, error) { 631 var s int 632 switch t { 633 case "initial": 634 s = ldap.FilterSubstringsInitial 635 case "any": 636 s = ldap.FilterSubstringsAny 637 case "final": 638 s = ldap.FilterSubstringsFinal 639 default: 640 return 0, fmt.Errorf("invalid filter type '%s'", t) 641 } 642 return s, nil 643 } 644 645 func (i *Identity) getGroupMemberFilter(memberName string) string { 646 return fmt.Sprintf("(&%s(objectclass=%s)(%s=%s))", 647 i.Group.Filter, 648 i.Group.Objectclass, 649 i.Group.Schema.Member, 650 ldap.EscapeFilter(memberName), 651 ) 652 } 653 654 func (i *Identity) getGroupFilter(id string) (string, error) { 655 var escapedUUID string 656 if i.Group.Schema.IDIsOctetString { 657 id, err := uuid.Parse(id) 658 if err != nil { 659 err := errors.Wrap(err, fmt.Sprintf("error parsing OpaqueID '%s' as UUID", id)) 660 return "", err 661 } 662 escapedUUID = filterEscapeBinaryUUID(id) 663 } else { 664 escapedUUID = ldap.EscapeFilter(id) 665 } 666 667 return fmt.Sprintf("(&%s(objectclass=%s)(%s=%s))", 668 i.Group.Filter, 669 i.Group.Objectclass, 670 i.Group.Schema.ID, 671 escapedUUID, 672 ), nil 673 } 674 675 func (i *Identity) getGroupAttributeFilter(attribute, value string) (string, error) { 676 switch attribute { 677 case "mail": 678 attribute = i.Group.Schema.Mail 679 case "gid_number": 680 attribute = i.Group.Schema.GIDNumber 681 case "display_name": 682 attribute = i.Group.Schema.DisplayName 683 case "group_name": 684 attribute = i.Group.Schema.Groupname 685 case "group_id": 686 attribute = i.Group.Schema.ID 687 default: 688 return "", errors.New("ldap: invalid field " + attribute) 689 } 690 if attribute == i.Group.Schema.ID && i.Group.Schema.IDIsOctetString { 691 id, err := uuid.Parse(value) 692 if err != nil { 693 err := errors.Wrap(err, fmt.Sprintf("error parsing OpaqueID '%s' as UUID", value)) 694 return "", err 695 } 696 value = filterEscapeBinaryUUID(id) 697 } else { 698 value = ldap.EscapeFilter(value) 699 } 700 return fmt.Sprintf("(&%s(objectclass=%s)(%s=%s))", 701 i.Group.Filter, 702 i.Group.Objectclass, 703 attribute, 704 value, 705 ), nil 706 } 707 708 // GetUserType is used to get the proper UserType from ldap entry string 709 func (i *Identity) GetUserType(userEntry *ldap.Entry) identityUser.UserType { 710 userTypeString := userEntry.GetEqualFoldAttributeValue(i.User.UserTypeProperty) 711 switch strings.ToLower(userTypeString) { 712 case "member": 713 return identityUser.UserType_USER_TYPE_PRIMARY 714 case "guest": 715 return identityUser.UserType_USER_TYPE_GUEST 716 default: 717 return identityUser.UserType_USER_TYPE_PRIMARY 718 } 719 }