1414# limitations under the License.
1515#
1616import functools
17+ from dataclasses import dataclass
18+
1719from typing import Union , List
1820
1921from labkey .server_context import ServerContext
2022
2123SECURITY_CONTROLLER = "security"
2224USER_CONTROLLER = "user"
25+ LOGIN_CONTROLLER = "login"
2326
2427
2528def activate_users (
@@ -156,7 +159,7 @@ def get_roles(server_context: ServerContext, container_path: str = None):
156159 url = server_context .build_url (
157160 SECURITY_CONTROLLER , "getRoles.api" , container_path = container_path
158161 )
159- return server_context .make_request (url , None )
162+ return server_context .make_request (url )
160163
161164
162165def get_user_by_email (server_context : ServerContext , email : str ):
@@ -167,7 +170,7 @@ def get_user_by_email(server_context: ServerContext, email: str):
167170 :return:
168171 """
169172 url = server_context .build_url (USER_CONTROLLER , "getUsers.api" )
170- payload = dict ( includeDeactivatedAccounts = True )
173+ payload = { " includeDeactivatedAccounts" : True }
171174 result = server_context .make_request (url , payload )
172175
173176 if result is None or result ["users" ] is None :
@@ -184,7 +187,6 @@ def list_groups(
184187 server_context : ServerContext , include_site_groups : bool = False , container_path : str = None
185188):
186189 url = server_context .build_url (SECURITY_CONTROLLER , "listProjectGroups.api" , container_path )
187-
188190 return server_context .make_request (url , {"includeSiteGroups" : include_site_groups })
189191
190192
@@ -242,10 +244,68 @@ def reset_password(server_context: ServerContext, email: str, container_path: st
242244 :return:
243245 """
244246 url = server_context .build_url (SECURITY_CONTROLLER , "adminRotatePassword.api" , container_path )
245-
246247 return server_context .make_request (url , {"email" : email })
247248
248249
250+ def impersonate_user (
251+ server_context : ServerContext ,
252+ user_id : int = None ,
253+ email : str = None ,
254+ container_path : str = None ,
255+ ):
256+ """
257+ For site-admins or project-admins only, start impersonating a user.
258+
259+ Admins may impersonate other users to perform actions on their behalf.
260+ Site users may impersonate any user in any project. Project admins must
261+ execute this command in a project in which they have admin permission
262+ and may impersonate any user that has access to the project.
263+
264+ To finish an impersonation session use `stop_impersonating`.
265+
266+ :param user_id: to impersonate (must supply this or email)
267+ :param email: to impersonate (must supply this or user_id)
268+ :param container_path: in which to impersonate the user
269+ """
270+ if email is None and user_id is None :
271+ raise ValueError ("Must supply either [email] or [user_id]" )
272+
273+ url = server_context .build_url (USER_CONTROLLER , "impersonateUser.api" , container_path )
274+ return server_context .make_request (url , {"userId" : user_id , "email" : email })
275+
276+
277+ def stop_impersonating (server_context : ServerContext ):
278+ """
279+ Stop impersonating a user while keeping the original user logged in.
280+ """
281+ url = server_context .build_url (LOGIN_CONTROLLER , "stopImpersonating.api" )
282+ return server_context .make_request (url )
283+
284+
285+ @dataclass
286+ class WhoAmI :
287+ id : int
288+ email : str
289+ display_name : str
290+ impersonated : str
291+ CSRF : str
292+
293+
294+ def who_am_i (server_context : ServerContext ) -> WhoAmI :
295+ """
296+ Calls the whoami API and returns a WhoAmI object.
297+ """
298+ url = server_context .build_url ("login" , "whoami.api" )
299+ response = server_context .make_request (url )
300+ return WhoAmI (
301+ response ["id" ],
302+ response ["email" ],
303+ response ["displayName" ],
304+ response ["impersonated" ],
305+ response ["CSRF" ],
306+ )
307+
308+
249309def __make_security_group_api_request (
250310 server_context : ServerContext ,
251311 api : str ,
@@ -378,3 +438,15 @@ def remove_from_role(
378438 @functools .wraps (reset_password )
379439 def reset_password (self , email : str , container_path : str = None ):
380440 return reset_password (self .server_context , email , container_path )
441+
442+ @functools .wraps (impersonate_user )
443+ def impersonate_user (self , user_id : int = None , email : str = None , container_path : str = None ):
444+ return impersonate_user (self .server_context , user_id , email , container_path )
445+
446+ @functools .wraps (stop_impersonating )
447+ def stop_impersonating (self ):
448+ return stop_impersonating (self .server_context )
449+
450+ @functools .wraps (who_am_i )
451+ def who_am_i (self ):
452+ return who_am_i (self .server_context )
0 commit comments