diff --git a/python/tank/authentication/shotgun_authenticator.py b/python/tank/authentication/shotgun_authenticator.py index 31f2f715d6..ef59d910ea 100644 --- a/python/tank/authentication/shotgun_authenticator.py +++ b/python/tank/authentication/shotgun_authenticator.py @@ -184,7 +184,9 @@ def create_session_user( return user.ShotgunWebUser(impl) return user.ShotgunUser(impl) - def create_script_user(self, api_script, api_key, host=None, http_proxy=None): + def create_script_user( + self, api_script, api_key, host=None, http_proxy=None, sudo_as_login=None + ): """ Create an AuthenticatedUser given a set of script credentials. @@ -194,6 +196,8 @@ def create_script_user(self, api_script, api_key, host=None, http_proxy=None): be used. :param http_proxy: Shotgun proxy to use. If None, the default http proxy will be used. + :param sudo_as_login: A Shotgun user login string for the user whose permissions will be applied + to all actions. If None, api_script permissions will be used. :returns: A :class:`ShotgunUser` derived instance. """ @@ -203,6 +207,7 @@ def create_script_user(self, api_script, api_key, host=None, http_proxy=None): api_script, api_key, http_proxy or self._defaults_manager.get_http_proxy(), + sudo_as_login, ) ) @@ -245,6 +250,7 @@ def get_default_user(self): api_key=credentials.get("api_key"), host=credentials.get("host"), http_proxy=credentials.get("http_proxy"), + sudo_as_login=credentials.get("sudo_as_login"), ) # If this looks like a session user, delegate to create_session_user. # If some of the arguments are missing, don't worry, create_session_user diff --git a/python/tank/authentication/user.py b/python/tank/authentication/user.py index 4c7ea16a83..228af0a874 100644 --- a/python/tank/authentication/user.py +++ b/python/tank/authentication/user.py @@ -79,6 +79,10 @@ def login(self): """ return self._impl.get_login() + @property + def sudo_as_login(self): + return self._impl.get_sudo_as_login() + def resolve_entity(self): """ Resolves the Shotgun entity associated with this user. diff --git a/python/tank/authentication/user_impl.py b/python/tank/authentication/user_impl.py index ada41a3a2e..34a8020977 100644 --- a/python/tank/authentication/user_impl.py +++ b/python/tank/authentication/user_impl.py @@ -111,6 +111,14 @@ def get_login(self): """ self.__class__._not_implemented("get_login") + def get_sudo_as_login(self): + """ + Returns the sudo_as_login for this user. + + :returns: The sudo_as_login string." + """ + self.__class__._not_implemented("get_sudo_as_login") + def get_session_metadata(self): """ Returns the session metadata for this user. @@ -236,6 +244,14 @@ def get_login(self): """ return self._login + def get_sudo_as_login(self): + """ + Returns the user used for sudo_as_login for this user. + + :returns: The sudo_as_login user login string. + """ + return None + def get_session_token(self): """ Returns the session token for this user. @@ -421,7 +437,7 @@ class ScriptUser(ShotgunUserImpl): User that authenticates to the Shotgun server using a api name and api key. """ - def __init__(self, host, api_script, api_key, http_proxy): + def __init__(self, host, api_script, api_key, http_proxy, sudo_as_login): """ Constructor. @@ -437,6 +453,7 @@ def __init__(self, host, api_script, api_key, http_proxy): self._api_script = api_script self._api_key = api_key + self._sudo_as_login = sudo_as_login def create_sg_connection(self): """ @@ -452,6 +469,7 @@ def create_sg_connection(self): self._host, script_name=self._api_script, api_key=self._api_key, + sudo_as_login=self._sudo_as_login, http_proxy=self._http_proxy, connect=False, ) @@ -509,7 +527,7 @@ def get_login(self): :returns: The login name string. """ # Script user has no login. - return None + return self._sudo_as_login def get_session_metadata(self): """ @@ -520,6 +538,14 @@ def get_session_metadata(self): # Script user has no session_metadata. return None + def get_sudo_as_login(self): + """ + Returns the user used for sudo_as_login for this user. + + :returns: The sudo_as_login user login string. + """ + return self._sudo_as_login + def to_dict(self): """ Converts the user into a dictionary object. @@ -529,6 +555,7 @@ def to_dict(self): data = super(ScriptUser, self).to_dict() data["api_script"] = self.get_script() data["api_key"] = self.get_key() + data["sudo_as_login"] = self.get_sudo_as_login() return data def __repr__(self): @@ -537,7 +564,14 @@ def __repr__(self): :returns: A string containing script name and site. """ - return "" % (self._api_script, self._host) + sudo_as_login_repr = ( + " (as %s)" % self._sudo_as_login if self._sudo_as_login else "" + ) + return "" % ( + self._api_script, + sudo_as_login_repr, + self._host, + ) def __str__(self): """ @@ -545,7 +579,10 @@ def __str__(self): :returns: A string. """ - return self._api_script + value = self._api_script + if self._sudo_as_login: + value += " (as %s)" % self._sudo_as_login + return value @staticmethod def from_dict(payload): @@ -560,6 +597,7 @@ def from_dict(payload): host=payload.get("host"), api_script=payload.get("api_script"), api_key=payload.get("api_key"), + sudo_as_login=payload.get("sudo_as_login"), http_proxy=payload.get("http_proxy"), ) diff --git a/python/tank/bootstrap/configuration.py b/python/tank/bootstrap/configuration.py index 320a40239c..fe4a0f1cbb 100644 --- a/python/tank/bootstrap/configuration.py +++ b/python/tank/bootstrap/configuration.py @@ -283,14 +283,15 @@ def _set_authenticated_user( project_user = None # If the project core's authentication code found a user... - # (Note that in the following code, a user with no login is a script user.) + # (Note that in the following code, a user with no login or a user with a sudo_as_login is a script user.) + if default_user: # If the project uses a script user, we'll use that. if not default_user.login: log.debug("User retrieved for the project is a script user.") project_user = default_user # If the project didn't use a script, but the bootstrap did, we'll keep using it. - elif not bootstrap_user_login: + elif not bootstrap_user_login or bootstrap_user.sudo_as_login: # We'll keep using the bootstrap user. This is because configurations like tk- # config-basic or tk-config-default are meant to be used with whatever credentials # were used during bootstrapping when used with a CachedDescriptor. The bootstrap diff --git a/tests/authentication_tests/test_shotgun_authenticator.py b/tests/authentication_tests/test_shotgun_authenticator.py index e16f443182..5e9208749d 100644 --- a/tests/authentication_tests/test_shotgun_authenticator.py +++ b/tests/authentication_tests/test_shotgun_authenticator.py @@ -151,6 +151,15 @@ def test_create_script_user(self, server_caps_mock): self.assertEqual(connection.config.script_name, "api_script") self.assertEqual(connection.config.api_key, "api_key") + # Test using sudo_as_login + user = ShotgunAuthenticator(CustomDefaultManager()).create_script_user( + "api_script", "api_key", "https://host.shotgunstudio.com", None, "sudouser" + ) + connection = user.create_sg_connection() + self.assertEqual(connection.config.script_name, "api_script") + self.assertEqual(connection.config.api_key, "api_key") + self.assertEqual(connection.config.sudo_as_login, "sudouser") + @patch("tank.authentication.session_cache.get_current_host", return_value=None) def test_no_current_host(self, _): """ diff --git a/tests/authentication_tests/test_user.py b/tests/authentication_tests/test_user.py index 8ea0eea7e0..ff9aec7022 100644 --- a/tests/authentication_tests/test_user.py +++ b/tests/authentication_tests/test_user.py @@ -51,6 +51,18 @@ def _create_script_user(self): api_script="api_script", api_key="api_key", http_proxy="http_proxy", + sudo_as_login=None, + ) + ) + + def _create_script_sudo_as_login_user(self): + return user.ShotgunUser( + user_impl.ScriptUser( + host="host", + api_script="api_script", + api_key="api_key", + http_proxy="http_proxy", + sudo_as_login="sudo_as_login", ) ) @@ -96,6 +108,10 @@ def test_login_value(self): script_user = self._create_script_user() self.assertIsNone(script_user.login) + sudo_user = self._create_script_sudo_as_login_user() + self.assertEqual(sudo_user.login, "sudo_as_login") + self.assertEqual(sudo_user.sudo_as_login, "sudo_as_login") + class CustomUser(user_impl.ShotgunUserImpl): def __init__(self): super(CustomUser, self).__init__("https://test.shotgunstudio.com", None)