2
2
#include < ydb/library/actors/core/log.h>
3
3
#include < ydb/core/base/ticket_parser.h>
4
4
#include < ydb/core/security/ticket_parser_log.h>
5
+ #include < ydb/core/util/address_classifier.h>
6
+ #include < queue>
5
7
#include " ldap_auth_provider.h"
6
8
#include " ldap_utils.h"
7
9
@@ -69,6 +71,7 @@ class TLdapAuthProvider : public NActors::TActorBootstrapped<TLdapAuthProvider>
69
71
TLdapAuthProvider (const NKikimrProto::TLdapAuthentication& settings)
70
72
: Settings(settings)
71
73
, FilterCreator(Settings)
74
+ , UrisCreator(Settings, Settings.GetPort() != 0 ? Settings.GetPort() : NKikimrLdap::GetPort(Settings.GetScheme()))
72
75
{
73
76
const TString& requestedGroupAttribute = Settings.GetRequestedGroupAttribute ();
74
77
RequestedAttributes[0 ] = const_cast <char *>(requestedGroupAttribute.empty () ? " memberOf" : requestedGroupAttribute.c_str ());
@@ -135,18 +138,33 @@ class TLdapAuthProvider : public NActors::TActorBootstrapped<TLdapAuthProvider>
135
138
}
136
139
LDAPMessage* entry = NKikimrLdap::FirstEntry (ld, searchUserResponse.SearchMessage );
137
140
BerElement* ber = nullptr ;
138
- std::vector<TString> groupsDn ;
141
+ std::vector<TString> directUserGroups ;
139
142
char * attribute = NKikimrLdap::FirstAttribute (ld, entry, &ber);
140
143
if (attribute != nullptr ) {
141
- groupsDn = NKikimrLdap::GetAllValuesOfAttribute (ld, entry, attribute);
144
+ directUserGroups = NKikimrLdap::GetAllValuesOfAttribute (ld, entry, attribute);
142
145
NKikimrLdap::MemFree (attribute);
143
146
}
144
147
if (ber) {
145
148
NKikimrLdap::BerFree (ber, 0 );
146
149
}
150
+ std::vector<TString> allUserGroups;
151
+ auto & extendedSettings = Settings.GetExtendedSettings ();
152
+ if (extendedSettings.GetEnableNestedGroupsSearch () && !directUserGroups.empty ()) {
153
+ // Active Directory has special matching rule to fetch nested groups in one request it is MatchingRuleInChain
154
+ // We don`t know what is ldap server. Is it Active Directory or OpenLdap or other server?
155
+ // If using MatchingRuleInChain return empty list of groups it means that ldap server isn`t Active Directory
156
+ // but it is known that there are groups and we are trying to do tree traversal
157
+ allUserGroups = TryToGetGroupsUseMatchingRuleInChain (ld, entry);
158
+ if (allUserGroups.empty ()) {
159
+ allUserGroups = std::move (directUserGroups);
160
+ GetNestedGroups (ld, &allUserGroups);
161
+ }
162
+ } else {
163
+ allUserGroups = std::move (directUserGroups);
164
+ }
147
165
NKikimrLdap::MsgFree (entry);
148
166
NKikimrLdap::Unbind (ld);
149
- Send (ev->Sender , new TEvLdapAuthProvider::TEvEnrichGroupsResponse (request->Key , request->User , groupsDn ));
167
+ Send (ev->Sender , new TEvLdapAuthProvider::TEvEnrichGroupsResponse (request->Key , request->User , allUserGroups ));
150
168
}
151
169
152
170
TInitAndBindResponse InitAndBind (LDAP** ld, std::function<THolder<IEventBase>(const TEvLdapAuthProvider::EStatus&, const TEvLdapAuthProvider::TError&)> eventFabric) {
@@ -173,7 +191,7 @@ class TLdapAuthProvider : public NActors::TActorBootstrapped<TLdapAuthProvider>
173
191
result = NKikimrLdap::Bind (*ld, Settings.GetBindDn (), Settings.GetBindPassword ());
174
192
if (!NKikimrLdap::IsSuccess (result)) {
175
193
TEvLdapAuthProvider::TError error {
176
- .Message = " Could not perform initial LDAP bind for dn " + Settings.GetBindDn () + " on server " + UrisList + " \n "
194
+ .Message = " Could not perform initial LDAP bind for dn " + Settings.GetBindDn () + " on server " + UrisCreator. GetUris () + " \n "
177
195
+ NKikimrLdap::ErrorToString (result),
178
196
.Retryable = NKikimrLdap::IsRetryableError (result)
179
197
};
@@ -202,12 +220,10 @@ class TLdapAuthProvider : public NActors::TActorBootstrapped<TLdapAuthProvider>
202
220
}
203
221
}
204
222
205
- const ui32 port = Settings.GetPort () != 0 ? Settings.GetPort () : NKikimrLdap::GetPort (Settings.GetScheme ());
206
- UrisList = GetUris (port);
207
- result = NKikimrLdap::Init (ld, Settings.GetScheme (), UrisList, port);
223
+ result = NKikimrLdap::Init (ld, Settings.GetScheme (), UrisCreator.GetUris (), UrisCreator.GetConfiguredPort ());
208
224
if (!NKikimrLdap::IsSuccess (result)) {
209
225
return {{TEvLdapAuthProvider::EStatus::UNAVAILABLE,
210
- {.Message = " Could not initialize LDAP connection for uris: " + UrisList + " . " + NKikimrLdap::LdapError (*ld),
226
+ {.Message = " Could not initialize LDAP connection for uris: " + UrisCreator. GetUris () + " . " + NKikimrLdap::LdapError (*ld),
211
227
.Retryable = false }}};
212
228
}
213
229
@@ -237,14 +253,14 @@ class TLdapAuthProvider : public NActors::TActorBootstrapped<TLdapAuthProvider>
237
253
char * dn = NKikimrLdap::GetDn (*request.Ld , request.Entry );
238
254
if (dn == nullptr ) {
239
255
return {{TEvLdapAuthProvider::EStatus::UNAUTHORIZED,
240
- {.Message = " Could not get dn for the first entry matching " + FilterCreator.GetFilter (request.Login ) + " on server " + UrisList + " \n "
256
+ {.Message = " Could not get dn for the first entry matching " + FilterCreator.GetFilter (request.Login ) + " on server " + UrisCreator. GetUris () + " \n "
241
257
+ NKikimrLdap::LdapError (*request.Ld ),
242
258
.Retryable = false }}};
243
259
}
244
260
TEvLdapAuthProvider::TError error;
245
261
int result = NKikimrLdap::Bind (*request.Ld , dn, request.Password );
246
262
if (!NKikimrLdap::IsSuccess (result)) {
247
- error.Message = " LDAP login failed for user " + TString (dn) + " on server " + UrisList + " \n "
263
+ error.Message = " LDAP login failed for user " + TString (dn) + " on server " + UrisCreator. GetUris () + " \n "
248
264
+ NKikimrLdap::ErrorToString ((result));
249
265
error.Retryable = NKikimrLdap::IsRetryableError (result);
250
266
}
@@ -266,7 +282,7 @@ class TLdapAuthProvider : public NActors::TActorBootstrapped<TLdapAuthProvider>
266
282
TSearchUserResponse response;
267
283
if (!NKikimrLdap::IsSuccess (result)) {
268
284
response.Status = NKikimrLdap::ErrorToStatus (result);
269
- response.Error = {.Message = " Could not search for filter " + searchFilter + " on server " + UrisList + " \n "
285
+ response.Error = {.Message = " Could not search for filter " + searchFilter + " on server " + UrisCreator. GetUris () + " \n "
270
286
+ NKikimrLdap::ErrorToString (result),
271
287
.Retryable = NKikimrLdap::IsRetryableError (result)};
272
288
return response;
@@ -275,11 +291,11 @@ class TLdapAuthProvider : public NActors::TActorBootstrapped<TLdapAuthProvider>
275
291
if (countEntries != 1 ) {
276
292
if (countEntries == 0 ) {
277
293
response.Error = {.Message = " LDAP user " + request.User + " does not exist. "
278
- " LDAP search for filter " + searchFilter + " on server " + UrisList + " return no entries" ,
294
+ " LDAP search for filter " + searchFilter + " on server " + UrisCreator. GetUris () + " return no entries" ,
279
295
.Retryable = false };
280
296
} else {
281
297
response.Error = {.Message = " LDAP user " + request.User + " is not unique. "
282
- " LDAP search for filter " + searchFilter + " on server " + UrisList + " return " + countEntries + " entries" ,
298
+ " LDAP search for filter " + searchFilter + " on server " + UrisCreator. GetUris () + " return " + countEntries + " entries" ,
283
299
.Retryable = false };
284
300
}
285
301
response.Status = TEvLdapAuthProvider::EStatus::UNAUTHORIZED;
@@ -290,6 +306,85 @@ class TLdapAuthProvider : public NActors::TActorBootstrapped<TLdapAuthProvider>
290
306
return response;
291
307
}
292
308
309
+ std::vector<TString> TryToGetGroupsUseMatchingRuleInChain (LDAP* ld, LDAPMessage* entry) const {
310
+ static const TString matchingRuleInChain = " 1.2.840.113556.1.4.1941" ; // Only Active Directory supports
311
+ TStringBuilder filter;
312
+ char * dn = NKikimrLdap::GetDn (ld, entry);
313
+ filter << " (member:" << matchingRuleInChain << " :=" << dn << ' )' ;
314
+ NKikimrLdap::MemFree (dn);
315
+ dn = nullptr ;
316
+ LDAPMessage* searchMessage = nullptr ;
317
+ int result = NKikimrLdap::Search (ld, Settings.GetBaseDn (), NKikimrLdap::EScope::SUBTREE, filter, NKikimrLdap::noAttributes, 0 , &searchMessage);
318
+ if (!NKikimrLdap::IsSuccess (result)) {
319
+ return {};
320
+ }
321
+ const int countEntries = NKikimrLdap::CountEntries (ld, searchMessage);
322
+ if (countEntries == 0 ) {
323
+ NKikimrLdap::MsgFree (searchMessage);
324
+ return {};
325
+ }
326
+ std::vector<TString> groups;
327
+ groups.reserve (countEntries);
328
+ for (LDAPMessage* groupEntry = NKikimrLdap::FirstEntry (ld, searchMessage); groupEntry != nullptr ; groupEntry = NKikimrLdap::NextEntry (ld, groupEntry)) {
329
+ dn = NKikimrLdap::GetDn (ld, groupEntry);
330
+ groups.push_back (dn);
331
+ NKikimrLdap::MemFree (dn);
332
+ dn = nullptr ;
333
+ }
334
+ NKikimrLdap::MsgFree (searchMessage);
335
+ return groups;
336
+ }
337
+
338
+ void GetNestedGroups (LDAP* ld, std::vector<TString>* groups) {
339
+ std::unordered_set<TString> viewedGroups (groups->cbegin (), groups->cend ());
340
+ std::queue<TString> queue;
341
+ for (const auto & group : *groups) {
342
+ queue.push (group);
343
+ }
344
+ while (!queue.empty ()) {
345
+ TStringBuilder filter;
346
+ filter << " (|" ;
347
+ filter << " (entryDn=" << queue.front () << ' )' ;
348
+ queue.pop ();
349
+ // should filter string is separated into several batches
350
+ while (!queue.empty ()) {
351
+ // entryDn specific for OpenLdap, may get this value from config
352
+ filter << " (entryDn=" << queue.front () << ' )' ;
353
+ queue.pop ();
354
+ }
355
+ filter << ' )' ;
356
+ LDAPMessage* searchMessage = nullptr ;
357
+ int result = NKikimrLdap::Search (ld, Settings.GetBaseDn (), NKikimrLdap::EScope::SUBTREE, filter, RequestedAttributes, 0 , &searchMessage);
358
+ if (!NKikimrLdap::IsSuccess (result)) {
359
+ return ;
360
+ }
361
+ if (NKikimrLdap::CountEntries (ld, searchMessage) == 0 ) {
362
+ NKikimrLdap::MsgFree (searchMessage);
363
+ return ;
364
+ }
365
+ for (LDAPMessage* groupEntry = NKikimrLdap::FirstEntry (ld, searchMessage); groupEntry != nullptr ; groupEntry = NKikimrLdap::NextEntry (ld, groupEntry)) {
366
+ BerElement* ber = nullptr ;
367
+ std::vector<TString> foundGroups;
368
+ char * attribute = NKikimrLdap::FirstAttribute (ld, groupEntry, &ber);
369
+ if (attribute != nullptr ) {
370
+ foundGroups = NKikimrLdap::GetAllValuesOfAttribute (ld, groupEntry, attribute);
371
+ NKikimrLdap::MemFree (attribute);
372
+ }
373
+ if (ber) {
374
+ NKikimrLdap::BerFree (ber, 0 );
375
+ }
376
+ for (const auto & newGroup : foundGroups) {
377
+ if (!viewedGroups.contains (newGroup)) {
378
+ viewedGroups.insert (newGroup);
379
+ queue.push (newGroup);
380
+ groups->push_back (newGroup);
381
+ }
382
+ }
383
+ }
384
+ NKikimrLdap::MsgFree (searchMessage);
385
+ }
386
+ }
387
+
293
388
TInitializeLdapConnectionResponse CheckRequiredSettingsParameters () const {
294
389
if (Settings.GetHosts ().empty () && Settings.GetHost ().empty ()) {
295
390
return {TEvLdapAuthProvider::EStatus::UNAVAILABLE, {.Message = " List of ldap server hosts is empty" , .Retryable = false }};
@@ -306,42 +401,11 @@ class TLdapAuthProvider : public NActors::TActorBootstrapped<TLdapAuthProvider>
306
401
return {TEvLdapAuthProvider::EStatus::SUCCESS, {}};
307
402
}
308
403
309
- TString GetUris (ui32 port) const {
310
- TStringBuilder uris;
311
- if (Settings.HostsSize () > 0 ) {
312
- for (const auto & host : Settings.GetHosts ()) {
313
- uris << CreateUri (host, port) << " " ;
314
- }
315
- uris.remove (uris.size () - 1 );
316
- } else {
317
- uris << CreateUri (Settings.GetHost (), port);
318
- }
319
- return uris;
320
- }
321
-
322
- TString CreateUri (const TString& endpoint, ui32 port) const {
323
- TStringBuilder uri;
324
- uri << Settings.GetScheme () << " ://" << endpoint;
325
- if (!HasEndpointPort (endpoint)) {
326
- uri << ' :' << port;
327
- }
328
- return uri;
329
- }
330
-
331
- static bool HasEndpointPort (const TString& endpoint) {
332
- size_t colonPos = endpoint.rfind (' :' );
333
- if (colonPos == TString::npos) {
334
- return false ;
335
- }
336
- ++colonPos;
337
- return (endpoint.size () - colonPos) > 0 ;
338
- }
339
-
340
404
private:
341
405
const NKikimrProto::TLdapAuthentication Settings;
342
406
const TSearchFilterCreator FilterCreator;
407
+ const TLdapUrisCreator UrisCreator;
343
408
char * RequestedAttributes[2 ];
344
- TString UrisList;
345
409
};
346
410
347
411
IActor* CreateLdapAuthProvider (const NKikimrProto::TLdapAuthentication& settings) {
0 commit comments