11import json
2+ import hashlib
23import ldap3
34import asyncio
4- from ldap3 import (
5- Server ,
6- Connection ,
7- MODIFY_REPLACE ,
8- ALL_ATTRIBUTES ,
9- NTLM
10- )
5+ from ldap3 import Server , Connection , MODIFY_REPLACE , ALL_ATTRIBUTES , NTLM
6+
7+ try :
8+ from Crypto .Hash import MD4 as CryptoMD4
9+ except ImportError :
10+ CryptoMD4 = None
1111
12- from ldap3 .extend .microsoft .addMembersToGroups import ad_add_members_to_groups as addUsersInGroups
13- from ldap3 .extend .microsoft .removeMembersFromGroups import ad_remove_members_from_groups as removeUsersFromGroups
12+ from ldap3 .extend .microsoft .addMembersToGroups import (
13+ ad_add_members_to_groups as addUsersInGroups ,
14+ )
15+ from ldap3 .extend .microsoft .removeMembersFromGroups import (
16+ ad_remove_members_from_groups as removeUsersFromGroups ,
17+ )
1418
1519from shuffle_sdk import AppBase
1620
21+
1722class ActiveDirectory (AppBase ):
1823 __version__ = "1.0.1"
1924 app_name = "Active Directory" # this needs to match "name" in api.yaml
@@ -28,11 +33,28 @@ def __init__(self, redis, logger, console_logger=None):
2833 super ().__init__ (redis , logger , console_logger )
2934
3035 def __ldap_connection (self , server , port , domain , login_user , password , use_ssl ):
31- use_SSL = False if use_ssl .lower () == "false" else True
36+ use_SSL = False if use_ssl .lower () == "false" else True
3237 login_dn = domain + "\\ " + login_user
3338
3439 s = Server (server , port = int (port ), use_ssl = use_SSL )
35- c = Connection (s , user = login_dn , password = password , authentication = NTLM , auto_bind = True )
40+
41+ if CryptoMD4 and not getattr (hashlib , "__active_directory_md4_patch__" , False ):
42+ try :
43+ import ldap3 .utils .ntlm as ldap3_ntlm
44+
45+ def _md4_hash (data ):
46+ md4 = CryptoMD4 .new ()
47+ md4 .update (data )
48+ return md4 .digest ()
49+
50+ ldap3_ntlm .hashlib .md4 = _md4_hash
51+ hashlib .__active_directory_md4_patch__ = True
52+ except Exception :
53+ pass
54+
55+ c = Connection (
56+ s , user = login_dn , password = password , authentication = NTLM , auto_bind = True
57+ )
3658 return c
3759
3860 # Decode UserAccountControl code
@@ -137,21 +159,28 @@ def user_attributes(
137159
138160 result = json .loads (c .response_to_json ())
139161 if len (result ["entries" ]) == 0 :
140- return json .dumps ({
141- "success" : False ,
142- "result" : result ,
143- "reason" : "No user found for %s" % samaccountname ,
144- })
162+ return json .dumps (
163+ {
164+ "success" : False ,
165+ "result" : result ,
166+ "reason" : "No user found for %s" % samaccountname ,
167+ }
168+ )
145169
146170 except Exception as e :
147- return json .dumps ({
148- "success" : False ,
149- "reason" : "Failed to get users in user attributes: %s" % e ,
150- })
151-
171+ return json .dumps (
172+ {
173+ "success" : False ,
174+ "reason" : "Failed to get users in user attributes: %s" % e ,
175+ }
176+ )
152177
153178 result = result ["entries" ][0 ]
154- result ["attributes" ]["userAccountControl" ] = self .__getUserAccountControlAttributes (result ["attributes" ]["userAccountControl" ])
179+ result ["attributes" ]["userAccountControl" ] = (
180+ self .__getUserAccountControlAttributes (
181+ result ["attributes" ]["userAccountControl" ]
182+ )
183+ )
155184
156185 return json .dumps (result )
157186
@@ -180,7 +209,19 @@ def set_password(
180209 server , port , domain , login_user , password , use_ssl
181210 )
182211
183- result = json .loads ( self .user_attributes ( server , port , domain , login_user , password , base_dn , use_ssl , samaccountname , search_base ,))
212+ result = json .loads (
213+ self .user_attributes (
214+ server ,
215+ port ,
216+ domain ,
217+ login_user ,
218+ password ,
219+ base_dn ,
220+ use_ssl ,
221+ samaccountname ,
222+ search_base ,
223+ )
224+ )
184225
185226 user_dn = result ["dn" ]
186227 c .extend .microsoft .modify_password (user_dn , new_password )
@@ -243,7 +284,6 @@ def enable_user(
243284 samaccountname ,
244285 search_base ,
245286 ):
246-
247287 if search_base :
248288 base_dn = search_base
249289
@@ -299,7 +339,6 @@ def disable_user(
299339 samaccountname ,
300340 search_base ,
301341 ):
302-
303342 if search_base :
304343 base_dn = search_base
305344
@@ -326,7 +365,6 @@ def disable_user(
326365 "success" : False ,
327366 "reason" : "Failed to get result attributes: %s" % e ,
328367 }
329-
330368
331369 if "ACCOUNTDISABLED" in userAccountControl :
332370 try :
@@ -362,8 +400,18 @@ def disable_user(
362400 "reason" : "Failed adding ACCOUNTDISABLED to user: %s" % e ,
363401 }
364402
365- def lock_user (self ,server ,domain ,port ,login_user ,password ,base_dn ,use_ssl ,samaccountname ,search_base ):
366-
403+ def lock_user (
404+ self ,
405+ server ,
406+ domain ,
407+ port ,
408+ login_user ,
409+ password ,
410+ base_dn ,
411+ use_ssl ,
412+ samaccountname ,
413+ search_base ,
414+ ):
367415 if search_base :
368416 base_dn = search_base
369417
@@ -372,19 +420,29 @@ def lock_user(self,server,domain,port,login_user,password,base_dn,use_ssl,samacc
372420 c .search (base_dn , f"(SAMAccountName={ samaccountname } )" )
373421
374422 if len (c .entries ) == 0 :
375- return {"success" :"false" ,"message" :f"User { samaccountname } not found" }
423+ return {"success" : "false" , "message" : f"User { samaccountname } not found" }
376424
377425 user_dn = c .entries [0 ].entry_dn
378426
379- c .modify (user_dn , {' userAccountControl' : [(MODIFY_REPLACE ,[514 ])]})
427+ c .modify (user_dn , {" userAccountControl" : [(MODIFY_REPLACE , [514 ])]})
380428
381429 result = c .result
382430 result ["success" ] = True
383431
384432 return result
385-
386- def unlock_user (self ,server ,domain ,port ,login_user ,password ,base_dn ,use_ssl ,samaccountname ,search_base ):
387-
433+
434+ def unlock_user (
435+ self ,
436+ server ,
437+ domain ,
438+ port ,
439+ login_user ,
440+ password ,
441+ base_dn ,
442+ use_ssl ,
443+ samaccountname ,
444+ search_base ,
445+ ):
388446 if search_base :
389447 base_dn = search_base
390448
@@ -393,91 +451,136 @@ def unlock_user(self,server,domain,port,login_user,password,base_dn,use_ssl,sama
393451 c .search (base_dn , f"(SAMAccountName={ samaccountname } )" )
394452
395453 if len (c .entries ) == 0 :
396- return {"success" :"false" ,"message" :f"User { samaccountname } not found" }
454+ return {"success" : "false" , "message" : f"User { samaccountname } not found" }
397455
398456 user_dn = c .entries [0 ].entry_dn
399457
400- c .modify (user_dn , {' userAccountControl' : [(MODIFY_REPLACE ,[0 ])]})
458+ c .modify (user_dn , {" userAccountControl" : [(MODIFY_REPLACE , [0 ])]})
401459
402460 result = c .result
403461 result ["success" ] = True
404462
405463 return result
406-
407- def change_user_password_at_next_login (self ,server ,domain ,port ,login_user ,password ,base_dn ,use_ssl ,samaccountname ,search_base ,new_user_password ,repeat_new_user_password ):
408-
464+
465+ def change_user_password_at_next_login (
466+ self ,
467+ server ,
468+ domain ,
469+ port ,
470+ login_user ,
471+ password ,
472+ base_dn ,
473+ use_ssl ,
474+ samaccountname ,
475+ search_base ,
476+ new_user_password ,
477+ repeat_new_user_password ,
478+ ):
409479 if search_base :
410480 base_dn = search_base
411481
412482 if str (new_user_password ) != str (repeat_new_user_password ):
413- return {"success" :"false" ,"message" :"new_user_password and repeat_new_user_password does not match." }
483+ return {
484+ "success" : "false" ,
485+ "message" : "new_user_password and repeat_new_user_password does not match." ,
486+ }
414487
415488 c = self .__ldap_connection (server , port , domain , login_user , password , use_ssl )
416489
417490 c .search (base_dn , f"(SAMAccountName={ samaccountname } )" )
418491
419492 if len (c .entries ) == 0 :
420- return {"success" :"false" ,"message" :f"User { samaccountname } not found" }
493+ return {"success" : "false" , "message" : f"User { samaccountname } not found" }
421494
422495 user_dn = c .entries [0 ].entry_dn
423496
424- c .modify (user_dn , {'pwdLastSet' :(MODIFY_REPLACE , [0 ])})
425- c .extend .microsoft .modify_password (user_dn , new_user_password .encode ('utf-16-le' ))
497+ c .modify (user_dn , {"pwdLastSet" : (MODIFY_REPLACE , [0 ])})
498+ c .extend .microsoft .modify_password (
499+ user_dn , new_user_password .encode ("utf-16-le" )
500+ )
426501
427502 result = c .result
428503 result ["success" ] = True
429504
430505 return result
431506
432- def add_user_to_group (self , server , domain , port , login_user , password , base_dn , use_ssl , samaccountname , search_base , group_name ):
433-
507+ def add_user_to_group (
508+ self ,
509+ server ,
510+ domain ,
511+ port ,
512+ login_user ,
513+ password ,
514+ base_dn ,
515+ use_ssl ,
516+ samaccountname ,
517+ search_base ,
518+ group_name ,
519+ ):
434520 if search_base :
435521 base_dn = search_base
436522
437523 c = self .__ldap_connection (server , port , domain , login_user , password , use_ssl )
438524
439525 c .search (base_dn , f"(SAMAccountName={ samaccountname } )" )
440526 if len (c .entries ) == 0 :
441- return {"success" :"false" ,"message" :f"User { samaccountname } not found" }
527+ return {"success" : "false" , "message" : f"User { samaccountname } not found" }
442528 user_dn = c .entries [0 ].entry_dn
443529
444- search_filter = f' (&(objectClass=group)(cn={ group_name } ))'
530+ search_filter = f" (&(objectClass=group)(cn={ group_name } ))"
445531 c .search (base_dn , search_filter , attributes = ["distinguishedName" ])
446532 if len (c .entries ) == 0 :
447- return {"success" :"false" ,"message" :f"Group { group_name } not found" }
533+ return {"success" : "false" , "message" : f"Group { group_name } not found" }
448534 group_dn = c .entries [0 ]["distinguishedName" ]
449535 print (group_dn )
450536
451- res = addUsersInGroups (c , user_dn , str (group_dn ),fix = True )
537+ res = addUsersInGroups (c , user_dn , str (group_dn ), fix = True )
452538 if res == True :
453- return {"success" :"true" ,"message" :f"User { samaccountname } was added to group { group_name } " }
539+ return {
540+ "success" : "true" ,
541+ "message" : f"User { samaccountname } was added to group { group_name } " ,
542+ }
454543 else :
455- return {"success" :"false" ,"message" :f"Could not add user to group" }
544+ return {"success" : "false" , "message" : f"Could not add user to group" }
456545
457- def remove_user_from_group (self , server , domain , port , login_user , password , base_dn , use_ssl , samaccountname , search_base , group_name ):
458-
546+ def remove_user_from_group (
547+ self ,
548+ server ,
549+ domain ,
550+ port ,
551+ login_user ,
552+ password ,
553+ base_dn ,
554+ use_ssl ,
555+ samaccountname ,
556+ search_base ,
557+ group_name ,
558+ ):
459559 if search_base :
460560 base_dn = search_base
461561
462562 c = self .__ldap_connection (server , port , domain , login_user , password , use_ssl )
463563
464564 c .search (base_dn , f"(SAMAccountName={ samaccountname } )" )
465565 if len (c .entries ) == 0 :
466- return {"success" :"false" ,"message" :f"User { samaccountname } not found" }
566+ return {"success" : "false" , "message" : f"User { samaccountname } not found" }
467567
468568 user_dn = c .entries [0 ].entry_dn
469- search_filter = f' (&(objectClass=group)(cn={ group_name } ))'
569+ search_filter = f" (&(objectClass=group)(cn={ group_name } ))"
470570 c .search (base_dn , search_filter , attributes = ["distinguishedName" ])
471571 if len (c .entries ) == 0 :
472- return {"success" :"false" ,"message" :f"Group { group_name } not found" }
572+ return {"success" : "false" , "message" : f"Group { group_name } not found" }
473573
474574 group_dn = c .entries [0 ]["distinguishedName" ]
475575 print (group_dn )
476- res = removeUsersFromGroups (c , user_dn , str (group_dn ),fix = True )
576+ res = removeUsersFromGroups (c , user_dn , str (group_dn ), fix = True )
477577 if res == True :
478- return {"success" :"true" ,"message" :f"User { samaccountname } was removed from group { group_name } " }
578+ return {
579+ "success" : "true" ,
580+ "message" : f"User { samaccountname } was removed from group { group_name } " ,
581+ }
479582 else :
480- return {"success" :"false" ,"message" :f"Could not remove user to group" }
583+ return {"success" : "false" , "message" : f"Could not remove user to group" }
481584
482585
483586if __name__ == "__main__" :
0 commit comments