Skip to content

Commit

Permalink
Set LDAP schema attributes (#216)
Browse files Browse the repository at this point in the history
* mapping via config file

* use objectGUID instead of objectSID default

* added test package

* provided example ...ldap.schema configuration

* added test coverage to ldap schema default tags

* cleanup test cases

* correct spaces

* test invalid attribute provided

* no reflection

* remove external deps
  • Loading branch information
refs authored and labkode committed Aug 27, 2019
1 parent 4488022 commit ad7c4dc
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 23 deletions.
63 changes: 40 additions & 23 deletions pkg/user/manager/ldap/ldap.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,24 +45,44 @@ type manager struct {
filter string
bindUsername string
bindPassword string
schema attributes
}

type config struct {
Hostname string `mapstructure:"hostname"`
Port int `mapstructure:"port"`
BaseDN string `mapstructure:"base_dn"`
Filter string `mapstructure:"filter"`
BindUsername string `mapstructure:"bind_username"`
BindPassword string `mapstructure:"bind_password"`
Hostname string `mapstructure:"hostname"`
Port int `mapstructure:"port"`
BaseDN string `mapstructure:"base_dn"`
Filter string `mapstructure:"filter"`
BindUsername string `mapstructure:"bind_username"`
BindPassword string `mapstructure:"bind_password"`
Schema attributes `mapstructure:"schema"`
}

type attributes struct {
Mail string `mapstructure:"mail"`
UID string `mapstructure:"uid"`
DisplayName string `mapstructure:"displayName"`
DN string `mapstructure:"dn"`
}

// Default attributes (Active Directory)
var ldapDefaults = attributes{
Mail: "mail",
UID: "objectGUID",
DisplayName: "displayName",
DN: "dn",
}

func parseConfig(m map[string]interface{}) (*config, error) {
c := &config{}
if err := mapstructure.Decode(m, c); err != nil {
c := config{
Schema: ldapDefaults,
}
if err := mapstructure.Decode(m, &c); err != nil {
err = errors.Wrap(err, "error decoding conf")
return nil, err
}
return c, nil

return &c, nil
}

// New returns a user manager implementation that connects to a LDAP server to provide user metadata.
Expand All @@ -79,6 +99,7 @@ func New(m map[string]interface{}) (user.Manager, error) {
filter: c.Filter,
bindUsername: c.BindUsername,
bindPassword: c.BindPassword,
schema: c.Schema,
}, nil
}

Expand All @@ -100,8 +121,8 @@ func (m *manager) GetUser(ctx context.Context, uid *typespb.UserId) (*authv0alph
searchRequest := ldap.NewSearchRequest(
m.baseDN,
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf(m.filter, uid.OpaqueId), // TODO this is screaming for errors if filter contains >1 %s
[]string{"dn", "uid", "mail", "displayName"}, // TODO mapping
fmt.Sprintf(m.filter, uid.OpaqueId), // TODO this is screaming for errors if filter contains >1 %s
[]string{m.schema.DN, m.schema.UID, m.schema.Mail, m.schema.DisplayName},
nil,
)

Expand All @@ -117,12 +138,10 @@ func (m *manager) GetUser(ctx context.Context, uid *typespb.UserId) (*authv0alph
log.Debug().Interface("entries", sr.Entries).Msg("entries")

return &authv0alphapb.User{
// TODO map uuid, userPrincipalName as sub? -> actually objectSID for AD is recommended by MS. is also used for ACLs on NTFS
// TODO map base dn as iss?
Username: sr.Entries[0].GetAttributeValue("uid"),
Username: sr.Entries[0].GetAttributeValue(m.schema.UID),
Groups: []string{},
Mail: sr.Entries[0].GetAttributeValue("mail"),
DisplayName: sr.Entries[0].GetAttributeValue("displayName"),
Mail: sr.Entries[0].GetAttributeValue(m.schema.Mail),
DisplayName: sr.Entries[0].GetAttributeValue(m.schema.DisplayName),
}, nil
}

Expand All @@ -143,8 +162,8 @@ func (m *manager) FindUsers(ctx context.Context, query string) ([]*authv0alphapb
searchRequest := ldap.NewSearchRequest(
m.baseDN,
ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf(m.filter, query), // TODO this is screaming for errors if filter contains >1 %s
[]string{"dn", "uid", "mail", "displayName"}, // TODO mapping
fmt.Sprintf(m.filter, query), // TODO this is screaming for errors if filter contains >1 %s
[]string{m.schema.DN, m.schema.UID, m.schema.Mail, m.schema.DisplayName},
nil,
)

Expand All @@ -157,12 +176,10 @@ func (m *manager) FindUsers(ctx context.Context, query string) ([]*authv0alphapb

for _, entry := range sr.Entries {
user := &authv0alphapb.User{
// TODO map uuid, userPrincipalName as sub? -> actually objectSID for AD is recommended by MS. is also used for ACLs on NTFS
// TODO map base dn as iss?
Username: entry.GetAttributeValue("uid"),
Username: entry.GetAttributeValue(m.schema.UID),
Groups: []string{},
Mail: entry.GetAttributeValue("mail"),
DisplayName: entry.GetAttributeValue("displayName"),
Mail: sr.Entries[0].GetAttributeValue(m.schema.Mail),
DisplayName: sr.Entries[0].GetAttributeValue(m.schema.DisplayName),
}
users = append(users, user)
}
Expand Down
77 changes: 77 additions & 0 deletions pkg/user/manager/ldap/ldap_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright 2018-2019 CERN
//
// 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.
//
// In applying this license, CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.

package ldap

import (
"strings"
"testing"

configreader "github.com/cs3org/reva/cmd/revad/config"
)

var conf = `
[schema]
mail = "email"
dn = "dn"
`

func TestUserManager(t *testing.T) {
// negative test for parseConfig
_, err := New(map[string]interface{}{"hostname": 42})
if err == nil {
t.Fatal("expected error but got none")
}

r := strings.NewReader(conf)
config, err := configreader.Read(r)
if err != nil {
t.Fatal("error reading config")
}

c, err := parseConfig(config)
if err != nil {
t.Fatal("error parsing config")
}

// UID not provided in config file. should not modify defaults
if c.Schema.UID != ldapDefaults.UID {
t.Fatalf("expected default UID to be: %v, got %v", ldapDefaults.UID, c.Schema.UID)
}

// DisplayName not provided in config file. should not modify defaults
if c.Schema.DisplayName != ldapDefaults.DisplayName {
t.Fatalf("expected DisplayName to be: %v, got %v", ldapDefaults.DisplayName, c.Schema.DisplayName)
}

// Mail provided in config file
if c.Schema.Mail != "email" {
t.Fatalf("expected default UID to be: %v, got %v", "email", c.Schema.Mail)
}

// DN provided in config file
if c.Schema.DN != ldapDefaults.DN {
t.Fatalf("expected DisplayName to be: %v, got %v", ldapDefaults.DN, c.Schema.DN)
}

// possitive tests for New
_, err = New(config)
if err != nil {
t.Fatalf(err.Error())
}
}

0 comments on commit ad7c4dc

Please sign in to comment.