11from ldap3 import Server , Connection
22from ldap3 .core .exceptions import LDAPBindError
3+ import json
34
45from service import get_tenant_config
56from service .errors import InvalidPasswordError
7+ from service .models import LdapOU , LdapUser
8+
9+ from common .config import conf
10+ from common .errors import DAOError
611
712# get the logger instance -
813from common .logs import get_logger
914logger = get_logger (__name__ )
1015
1116
12- def get_ldap_connection (tenant_id , bind_dn = None , bind_credential = None ):
17+ def get_ldap_connection (ldap_server , ldap_port , bind_dn , bind_password , use_ssl = True ):
1318 """
14- Get an ldap connection to the ldap server corresponding to the tenant_id.
15- :param tenant_id:
19+ Get a connection to an LDAP server.
20+ :param ldap_server: The URI of the ldap server.
21+ :param ldap_port: The port of the ldap server.
22+ :param bind_dn: The DN to use to bind.
23+ :param bind_password: The password associated with the bind DN.
24+ :param use_ssl: Whether to use SSL when connecting to the LDAP server.
25+ :return:
26+ """
27+ server = Server (ldap_server , port = ldap_port , use_ssl = use_ssl )
28+ conn = Connection (server , bind_dn , bind_password , auto_bind = True )
29+ return conn
30+
31+ def get_tapis_ldap_server_info ():
32+ """
33+ Returns dictionary of Tapis LDAP server connection information.
34+ :return: (dict)
35+ """
36+ return {
37+ "server" : conf .dev_ldap_url ,
38+ "port" : conf .dev_ldap_port ,
39+ "bind_dn" : conf .dev_ldap_bind_dn ,
40+ "bind_password" : conf .dev_ldap_bind_credential ,
41+ "base_dn" : conf .dev_ldap_tenants_base_dn ,
42+ "use_ssl" : conf .dev_ldap_use_ssl
43+ }
44+
45+ tapis_ldap = get_tapis_ldap_server_info ()
46+
47+ def get_tapis_ldap_connection ():
48+ """
49+ Convenience wrapper function to get an ldap connection to the Tapis dev ldap server.
50+ :return:
51+ """
52+ try :
53+ return get_ldap_connection (ldap_server = tapis_ldap ['server' ],
54+ ldap_port = tapis_ldap ['port' ],
55+ bind_dn = tapis_ldap ['bind_dn' ],
56+ bind_password = tapis_ldap ['bind_password' ],
57+ use_ssl = tapis_ldap ['use_ssl' ])
58+ except LDAPBindError as e :
59+ logger .debug (f'Invalid Tapis bind credential: { e } ' )
60+ raise InvalidPasswordError ("Invalid username/password combination." )
61+ except Exception as e :
62+ msg = f"Got exception trying to create connection object to Tapis LDAP. e: { e } "
63+ logger .error (msg )
64+ raise DAOError (msg )
65+
66+
67+ def add_tapis_ou (ou ):
68+ """
69+ Add an LDAP record representing an Organizational Unit (ou) to the Tapis LDAP.
70+ :param ou: (LdapOU) The OU object to add.
71+ :return:
72+ """
73+ conn = get_tapis_ldap_connection ()
74+ try :
75+ result = conn .add (ou .dn , ou .object_class )
76+ except Exception as e :
77+ msg = f'got an error trying to add an ou. Exception: { e } ; ou.dn: { ou .dn } ; ou.object_class: { ou .object_class } '
78+ logger .error (msg )
79+ if not result :
80+ msg = f'Got False result trying to add OU to LDAP; error data: { conn .result } '
81+ logger .error (msg )
82+ raise DAOError ("Unable to add OU to LDAP database; "
83+ "Required fields could be missing or improperly formatted." )
84+ return True
85+
86+
87+ def list_tapis_ous ():
88+ """
89+ List the OUs associated with the Tapis LDAP server.
90+ :return:
91+ """
92+ conn = get_tapis_ldap_connection ()
93+ try :
94+ # search for all cn's under the tapis tenants base_dn and pull back all attributes
95+ result = conn .search (conf .dev_ldap_tenants_base_dn , '(ou=*)' , attributes = ['*' ])
96+ except Exception as e :
97+ msg = f'Got an exception trying to list Tapis OUs. Exception: { e } '
98+ logger .error (msg )
99+ raise DAOError (msg )
100+ if not result :
101+ msg = f'Got an error trying to list Tapis OUs. message: { conn .result } '
102+ logger .error (msg )
103+ # return the results -
104+ result = []
105+ for ent in conn .entries :
106+ result .append (ent .entry_attributes_as_dict )
107+ return result
108+
109+
110+ def create_tapis_ldap_tenant_ou (tenant_id ):
111+ """
112+ Create an OU in the Tapis LDAP for a tenant id.
113+ :param tenant_id:
114+ :return:
115+ """
116+ base_dn = tapis_ldap ['base_dn' ]
117+ ou = LdapOU (dn = f'ou=tenants.{ tenant_id } ,{ base_dn } ' )
118+ return add_tapis_ou (ou )
119+
120+
121+ def get_tenant_ldap_connection (tenant_id , bind_dn = None , bind_password = None ):
122+ """
123+ Convenience wrapper function to get an ldap connection to the ldap server corresponding to the tenant_id.
124+ :param tenant_id: (str) The id of the tenant.
125+ :param bind_dn: (str) Optional dn to use to bind. Pass this to check validity of a username/password.
126+ :param bind_password (str) Optional password to use to bind. Pass this to check validity of a username/password.
16127 :return:
17128 """
18129 tenant = get_tenant_config (tenant_id )
19- server = Server (tenant ['ldap_url' ], port = tenant ['ldap_port' ], use_ssl = tenant ['ldap_use_ssl' ])
20- if bind_dn and bind_credential :
21- conn = Connection (server , bind_dn , bind_credential , auto_bind = True )
22- else :
23- conn = Connection (server , tenant ['ldap_bind_dn' ], tenant ['ldap_bind_credential' ], auto_bind = True )
24- return conn
130+ # if we are passed specific bind credentials, use those:
131+ if not bind_dn is None :
132+ return get_ldap_connection (ldap_server = tenant ['ldap_url' ],
133+ ldap_port = tenant ['ldap_port' ],
134+ bind_dn = bind_dn ,
135+ bind_password = bind_password ,
136+ use_ssl = tenant ['ldap_use_ssl' ])
137+ # otherwise, return the connection associated with the tenant's bind credentials -
138+ return get_ldap_connection (ldap_server = tenant ['ldap_url' ],
139+ ldap_port = tenant ['ldap_port' ],
140+ bind_dn = tenant ['ldap_bind_dn' ],
141+ bind_password = tenant ['ldap_bind_credential' ],
142+ use_ssl = tenant ['ldap_use_ssl' ])
143+
144+ def list_tenant_users (tenant_id ):
145+ """
146+ List all users in a tenant
147+ :param tenant_id: (str) the tenant id to use.
148+ :return:
149+ """
150+ tenant = get_tenant_config (tenant_id )
151+ conn = get_tenant_ldap_connection (tenant_id )
152+ result = conn .search (tenant ['ldap_user_dn' ], '(cn=*)' , attributes = ['*' ])
153+ if not result :
154+ msg = f'Error retrieving users; debug information: { conn .result } '
155+ logger .error (msg )
156+ raise DAOError (msg )
157+ result = []
158+ for ent in conn .entries :
159+ result .append (ent .entry_attributes_as_dict )
160+ return result
25161
26162def get_dn (tenant_id , username ):
27163 """
@@ -32,7 +168,7 @@ def get_dn(tenant_id, username):
32168 """
33169 tenant = get_tenant_config (tenant_id )
34170 ldap_user_dn = tenant ['ldap_user_dn' ]
35- return f'uid ={ username } ,{ ldap_user_dn } '
171+ return f'cn ={ username } ,{ ldap_user_dn } '
36172
37173def check_username_password (tenant_id , username , password ):
38174 """
@@ -44,18 +180,36 @@ def check_username_password(tenant_id, username, password):
44180 """
45181 bind_dn = get_dn (tenant_id , username )
46182 try :
47- get_ldap_connection (tenant_id , bind_dn , password )
183+ conn = get_tenant_ldap_connection (tenant_id , bind_dn = bind_dn , bind_password = password )
48184 except LDAPBindError as e :
49- logger .debg (f'got exception checking password: { e } ' )
185+ logger .debug (f'got exception checking password: { e } ' )
50186 raise InvalidPasswordError ("Invalid username/password combination." )
51187
52- def add_user (tenant_id , username , password ):
188+
189+ def add_user (tenant_id , user ):
53190 """
54- Add an LDAP record
55- :param tenant_id:
56- :param username:
57- :param password:
191+ Add an LDAP record representing a user in a specific tenant.
192+ :param tenant_id: (str) The tenant id of the tenant where the user should be added.
193+ :param user: (LdapUser) An LdapUser object containing the details of the user to add.
58194 :return:
59195 """
196+ conn = get_tenant_ldap_connection (tenant_id )
197+ user .save (conn )
60198
61-
199+ def add_test_user (tenant_id , username ):
200+ """
201+ Add a testuser to the Tapis LDAP for tenant id, tenant_id. The username is required and from it, all inetorgperson
202+ attributes are derived.
203+ :param tenant_id: (str) the tenant id.
204+ :param username: (str) the username of the test account.
205+ :return:
206+ """
207+ # first, create an LdapUser object with the appropriate attributes.
208+ base_dn = tapis_ldap ['base_dn' ]
209+ user = LdapUser (dn = f'cn={ username } ,ou=tenants.{ tenant_id } ,{ base_dn } ' ,
210+ givenName = username ,
211+ sn = username ,
212+ mail = f'{ username } @test.tapis.io' ,
213+ userPassword = username )
214+ # now call the generic add user for the tenant id:
215+ add_user (tenant_id , user )
0 commit comments