github.com/cs3org/reva/v2@v2.27.7/pkg/user/manager/ldap/ldap.go (about) 1 // Copyright 2018-2021 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 "context" 23 "fmt" 24 "strconv" 25 26 userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" 27 "github.com/cs3org/reva/v2/pkg/appctx" 28 "github.com/cs3org/reva/v2/pkg/errtypes" 29 "github.com/cs3org/reva/v2/pkg/user" 30 "github.com/cs3org/reva/v2/pkg/user/manager/registry" 31 "github.com/cs3org/reva/v2/pkg/utils" 32 ldapIdentity "github.com/cs3org/reva/v2/pkg/utils/ldap" 33 "github.com/go-ldap/ldap/v3" 34 "github.com/google/uuid" 35 "github.com/mitchellh/mapstructure" 36 "github.com/pkg/errors" 37 ) 38 39 func init() { 40 registry.Register("ldap", New) 41 } 42 43 type manager struct { 44 c *config 45 ldapClient ldap.Client 46 } 47 48 type config struct { 49 utils.LDAPConn `mapstructure:",squash"` 50 LDAPIdentity ldapIdentity.Identity `mapstructure:",squash"` 51 Idp string `mapstructure:"idp"` 52 // Nobody specifies the fallback uid number for users that don't have a uidNumber set in LDAP 53 Nobody int64 `mapstructure:"nobody"` 54 } 55 56 func parseConfig(m map[string]interface{}) (*config, error) { 57 c := config{ 58 LDAPIdentity: ldapIdentity.New(), 59 } 60 if err := mapstructure.Decode(m, &c); err != nil { 61 err = errors.Wrap(err, "error decoding conf") 62 return nil, err 63 } 64 65 return &c, nil 66 } 67 68 // New returns a user manager implementation that connects to a LDAP server to provide user metadata. 69 func New(m map[string]interface{}) (user.Manager, error) { 70 mgr := &manager{} 71 err := mgr.Configure(m) 72 if err != nil { 73 return nil, err 74 } 75 76 mgr.ldapClient, err = utils.GetLDAPClientWithReconnect(&mgr.c.LDAPConn) 77 return mgr, err 78 } 79 80 // Configure initializes the configuration of the user manager from the supplied config map 81 func (m *manager) Configure(ml map[string]interface{}) error { 82 c, err := parseConfig(ml) 83 if err != nil { 84 return err 85 } 86 if c.Nobody == 0 { 87 c.Nobody = 99 88 } 89 90 if err = c.LDAPIdentity.Setup(); err != nil { 91 return fmt.Errorf("error setting up Identity config: %w", err) 92 } 93 m.c = c 94 return nil 95 } 96 97 // GetUser implements the user.Manager interface. Looks up a user by Id and return the user 98 func (m *manager) GetUser(ctx context.Context, uid *userpb.UserId, skipFetchingGroups bool) (*userpb.User, error) { 99 log := appctx.GetLogger(ctx) 100 101 log.Debug().Interface("id", uid).Msg("GetUser") 102 // If the Idp value in the uid does not match our config, we can't answer this request 103 if uid.Idp != "" && uid.Idp != m.c.Idp { 104 return nil, errtypes.NotFound("idp mismatch") 105 } 106 107 userEntry, err := m.c.LDAPIdentity.GetLDAPUserByID(log, m.ldapClient, uid.OpaqueId) 108 if err != nil { 109 return nil, err 110 } 111 112 log.Debug().Interface("entry", userEntry).Msg("entries") 113 114 u, err := m.ldapEntryToUser(userEntry) 115 if err != nil { 116 return nil, err 117 } 118 119 if skipFetchingGroups { 120 return u, nil 121 } 122 123 groups, err := m.c.LDAPIdentity.GetLDAPUserGroups(log, m.ldapClient, userEntry) 124 if err != nil { 125 return nil, err 126 } 127 128 u.Groups = groups 129 return u, nil 130 } 131 132 // GetUserByClaim implements the user.Manager interface. Looks up a user by 133 // claim ('mail', 'username', 'userid') and returns the user. 134 func (m *manager) GetUserByClaim(ctx context.Context, claim, value string, skipFetchingGroups bool) (*userpb.User, error) { 135 log := appctx.GetLogger(ctx) 136 137 log.Debug().Str("claim", claim).Str("value", value).Msg("GetUserByClaim") 138 userEntry, err := m.c.LDAPIdentity.GetLDAPUserByAttribute(log, m.ldapClient, claim, value) 139 if err != nil { 140 log.Debug().Err(err).Msg("GetUserByClaim") 141 return nil, err 142 } 143 144 log.Debug().Interface("entry", userEntry).Msg("entries") 145 146 u, err := m.ldapEntryToUser(userEntry) 147 if err != nil { 148 return nil, err 149 } 150 151 if m.c.LDAPIdentity.IsLDAPUserInDisabledGroup(log, m.ldapClient, userEntry) { 152 return nil, errtypes.NotFound("user is locally disabled") 153 } 154 155 if skipFetchingGroups { 156 return u, nil 157 } 158 159 groups, err := m.c.LDAPIdentity.GetLDAPUserGroups(log, m.ldapClient, userEntry) 160 if err != nil { 161 return nil, err 162 } 163 164 u.Groups = groups 165 166 return u, nil 167 } 168 169 // FindUser implements the user.Manager interface. Searches for users using a prefix-substring search on 170 // the user attributes ('mail', 'username', 'displayname', 'userid') and returns the users. 171 func (m *manager) FindUsers(ctx context.Context, query string, skipFetchingGroups bool) ([]*userpb.User, error) { 172 log := appctx.GetLogger(ctx) 173 entries, err := m.c.LDAPIdentity.GetLDAPUsers(log, m.ldapClient, query) 174 if err != nil { 175 return nil, err 176 } 177 users := []*userpb.User{} 178 179 for _, entry := range entries { 180 u, err := m.ldapEntryToUser(entry) 181 if err != nil { 182 return nil, err 183 } 184 185 if !skipFetchingGroups { 186 groups, err := m.c.LDAPIdentity.GetLDAPUserGroups(log, m.ldapClient, entry) 187 if err != nil { 188 return nil, err 189 } 190 u.Groups = groups 191 } 192 193 users = append(users, u) 194 } 195 196 return users, nil 197 } 198 199 // GetUserGroups implements the user.Manager interface. Looks up all group membership of 200 // the user with the supplied Id. Returns a string slice with the group ids 201 func (m *manager) GetUserGroups(ctx context.Context, uid *userpb.UserId) ([]string, error) { 202 log := appctx.GetLogger(ctx) 203 if uid.Idp != "" && uid.Idp != m.c.Idp { 204 log.Debug().Str("useridp", uid.Idp).Str("configured idp", m.c.Idp).Msg("IDP mismatch") 205 return nil, errtypes.NotFound("idp mismatch") 206 } 207 userEntry, err := m.c.LDAPIdentity.GetLDAPUserByID(log, m.ldapClient, uid.OpaqueId) 208 if err != nil { 209 log.Debug().Err(err).Interface("userid", uid).Msg("Failed to lookup user") 210 return []string{}, err 211 } 212 return m.c.LDAPIdentity.GetLDAPUserGroups(log, m.ldapClient, userEntry) 213 } 214 215 func (m *manager) ldapEntryToUser(entry *ldap.Entry) (*userpb.User, error) { 216 id, err := m.ldapEntryToUserID(entry) 217 if err != nil { 218 return nil, err 219 } 220 221 gidNumber := m.c.Nobody 222 gidValue := entry.GetEqualFoldAttributeValue(m.c.LDAPIdentity.User.Schema.GIDNumber) 223 if gidValue != "" { 224 gidNumber, err = strconv.ParseInt(gidValue, 10, 64) 225 if err != nil { 226 return nil, err 227 } 228 } 229 uidNumber := m.c.Nobody 230 uidValue := entry.GetEqualFoldAttributeValue(m.c.LDAPIdentity.User.Schema.UIDNumber) 231 if uidValue != "" { 232 uidNumber, err = strconv.ParseInt(uidValue, 10, 64) 233 if err != nil { 234 return nil, err 235 } 236 } 237 u := &userpb.User{ 238 Id: id, 239 Username: entry.GetEqualFoldAttributeValue(m.c.LDAPIdentity.User.Schema.Username), 240 Mail: entry.GetEqualFoldAttributeValue(m.c.LDAPIdentity.User.Schema.Mail), 241 DisplayName: entry.GetEqualFoldAttributeValue(m.c.LDAPIdentity.User.Schema.DisplayName), 242 GidNumber: gidNumber, 243 UidNumber: uidNumber, 244 } 245 return u, nil 246 } 247 248 func (m *manager) ldapEntryToUserID(entry *ldap.Entry) (*userpb.UserId, error) { 249 var uid string 250 if m.c.LDAPIdentity.User.Schema.IDIsOctetString { 251 rawValue := entry.GetEqualFoldRawAttributeValue(m.c.LDAPIdentity.User.Schema.ID) 252 if value, err := uuid.FromBytes(rawValue); err == nil { 253 uid = value.String() 254 } else { 255 return nil, err 256 } 257 } else { 258 uid = entry.GetEqualFoldAttributeValue(m.c.LDAPIdentity.User.Schema.ID) 259 } 260 261 return &userpb.UserId{ 262 Idp: m.c.Idp, 263 OpaqueId: uid, 264 Type: m.c.LDAPIdentity.GetUserType(entry), 265 }, nil 266 }