package ldap import ( "errors" "strings" "github.com/vjeantet/goldap/message" ) // entityIDFromDN parses out an ID from a given DN. This only works // for ID's that don't contain an escaped comma, but these aren't // valid in NetAuth anyway, so this is taken as a known defect. func (s *server) entityIDFromDN(dn message.LDAPDN) (string, error) { parts := strings.Split(string(dn), ",") for i := range parts { parts[i] = strings.TrimSpace(parts[i]) } if len(parts) < 2 { return "", errors.New("malformed DN") } if !strings.HasPrefix(parts[0], "uid=") { return "", errors.New("entity DN must start with uid=") } if parts[1] != "ou=entities" { return "", errors.New("entity DN must be underneath ou=entities") } rest := parts[2:] if len(rest) != len(s.nc) { return "", errors.New("queries must be rooted at " + strings.Join(s.nc, ",")) } for i, p := range rest { if p != s.nc[i] { return "", errors.New("queries must be rooted at " + strings.Join(s.nc, ",")) } } return strings.TrimPrefix(parts[0], "uid="), nil } // errUnsupportedFilter marks a filter this bridge cannot evaluate. // Returning it lets callers answer with an explicit error rather than // an accidentally empty result. var errUnsupportedFilter = errors.New("unsupported search filter") type entryKind int const ( kindEntity entryKind = iota kindGroup ) // entryRef names the single entry a DN addresses directly, as opposed // to a container DN that heads a subtree search. type entryRef struct { kind entryKind name string } // entryFromDN parses a DN that names one entity or group outright, // e.g. uid=jsmith,ou=entities,dc=netauth,dc=example,dc=com. Clients // that already know a DN (HashiCorp Vault does this on every login) // use such DNs as the base of a base-object search. func (s *server) entryFromDN(dn message.LDAPDN) (*entryRef, error) { parts := strings.Split(string(dn), ",") for i := range parts { parts[i] = strings.ToLower(strings.TrimSpace(parts[i])) } if len(parts) < 3 { return nil, errors.New("malformed DN") } rest := parts[2:] if len(rest) != len(s.nc) { return nil, errors.New("queries must be rooted at " + strings.Join(s.nc, ",")) } for i, p := range rest { if p != s.nc[i] { return nil, errors.New("queries must be rooted at " + strings.Join(s.nc, ",")) } } switch { case strings.HasPrefix(parts[0], "uid=") && parts[1] == "ou=entities": return &entryRef{kind: kindEntity, name: strings.TrimPrefix(parts[0], "uid=")}, nil case strings.HasPrefix(parts[0], "cn=") && parts[1] == "ou=groups": return &entryRef{kind: kindGroup, name: strings.TrimPrefix(parts[0], "cn=")}, nil } return nil, errors.New("DN does not name an entity or a group") }