@@ -122,6 +122,7 @@ def __init__(
122122 url : Optional [str ] = None ,
123123 script_name : Optional [str ] = None ,
124124 api_key : Optional [str ] = None ,
125+ session_token : Optional [str ] = None ,
125126 connect : bool = True ,
126127 ):
127128 """Initialize the ShotGrid connection.
@@ -130,17 +131,22 @@ def __init__(
130131 url: ShotGrid server URL. Defaults to SHOTGRID_URL env var.
131132 script_name: API script name. Defaults to SHOTGRID_SCRIPT_NAME env var.
132133 api_key: API key for authentication. Defaults to SHOTGRID_API_KEY env var.
134+ session_token: Session token for user authentication.
133135 """
134136 super ().__init__ ()
135137
136138 self .url = url or os .getenv ("SHOTGRID_URL" )
137139 self .script_name = script_name or os .getenv ("SHOTGRID_SCRIPT_NAME" )
138140 self .api_key = api_key or os .getenv ("SHOTGRID_API_KEY" )
141+ self .session_token = session_token
139142
140- if not all ([self .url , self .script_name , self .api_key ]):
143+ if not self .url :
144+ raise ValueError ("ShotGrid URL not provided." )
145+
146+ if not self .session_token and not (self .script_name and self .api_key ):
141147 raise ValueError (
142- "ShotGrid credentials not provided. Set SHOTGRID_URL, "
143- "SHOTGRID_SCRIPT_NAME, and SHOTGRID_API_KEY environment variables ."
148+ "ShotGrid credentials not provided. Provide either session_token "
149+ "or (script_name and api_key) ."
144150 )
145151
146152 self .sg = None
@@ -149,7 +155,11 @@ def __init__(
149155
150156 def _connect (self ):
151157 """Connect to ShotGrid."""
152- self .sg = Shotgun (self .url , self .script_name , self .api_key )
158+ if self .session_token :
159+ # When using session token, we don't use script credentials
160+ self .sg = Shotgun (self .url , session_token = self .session_token )
161+ else :
162+ self .sg = Shotgun (self .url , self .script_name , self .api_key )
153163
154164 def _convert_sg_entity_to_dna_entity (
155165 self ,
@@ -398,6 +408,35 @@ def get_user_by_email(self, user_email: str) -> User:
398408 sg_user , entity_mapping , "user" , resolve_links = False
399409 )
400410
411+ def get_user_by_login (self , login : str ) -> User :
412+ """Get a user by their login/username.
413+
414+ Args:
415+ login: The login/username of the user
416+
417+ Returns:
418+ User entity
419+
420+ Raises:
421+ ValueError: If user is not found
422+ """
423+ if not self .sg :
424+ raise ValueError ("Not connected to ShotGrid" )
425+
426+ sg_user = self .sg .find_one (
427+ "HumanUser" ,
428+ filters = [["login" , "is" , login ]],
429+ fields = ["id" , "name" , "email" , "login" ],
430+ )
431+
432+ if not sg_user :
433+ raise ValueError (f"User not found: { login } " )
434+
435+ entity_mapping = FIELD_MAPPING ["user" ]
436+ return self ._convert_sg_entity_to_dna_entity (
437+ sg_user , entity_mapping , "user" , resolve_links = False
438+ )
439+
401440 def get_projects_for_user (self , user_email : str ) -> list [Project ]:
402441 """Get projects accessible by a user.
403442
@@ -538,6 +577,41 @@ def get_versions_for_playlist(self, playlist_id: int) -> list[Version]:
538577 return versions
539578
540579
580+ @staticmethod
581+ def authenticate_user (url : str , login : str , password : str ) -> str :
582+ """Authenticate a user with ShotGrid and return a session token.
583+
584+ Args:
585+ url: ShotGrid server URL
586+ login: User login/username
587+ password: User password
588+
589+ Returns:
590+ Session token string
591+
592+ Raises:
593+ ValueError: If authentication fails
594+ """
595+ try :
596+ # Shotgun.authenticate_human_user returns the user object, but we need the session_token.
597+ # However, the standard way to get a token is to just create a connection which validates creds.
598+ # But wait, Shotgun API structure specifically for auth:
599+ # We can use the simple authentication helper or instantiate to get token.
600+ # Actually, standard shotgun_api3 doesn't easily expose 'authenticate_human_user' to get a token string directly
601+ # without internals.
602+ # Let's instantiate a connection to verify and get session_token if available or standard auth flow.
603+ # The pattern usually is: sg = Shotgun(url, login=login, password=password) then sg.get_session_token().
604+
605+ # Note: shotgun_api3 v3.3.0+ supports `login` and `password` in constructor for script-based auth,
606+ # but for human user relying on session token:
607+
608+ sg = Shotgun (url , login = login , password = password )
609+ # This establishes connection. Now implementation detail: how to get the token?
610+ # The 'get_session_token()' method provides it.
611+ return sg .get_session_token ()
612+ except Exception as e :
613+ raise ValueError (f"Authentication failed: { str (e )} " )
614+
541615def _get_dna_entity_type (sg_entity_type : str ) -> str :
542616 """Get the DNA entity type from the ShotGrid entity type."""
543617 for entity_type , entity_data in FIELD_MAPPING .items ():
0 commit comments