Skip to content

Commit

Permalink
Merge pull request #83 from ynput/enhancement/AY-4608_Return-ShotGrid…
Browse files Browse the repository at this point in the history
…-tray-menu-items-for-user-logging

Reimplementation of Tray user logging
  • Loading branch information
jakubjezek001 authored Apr 15, 2024
2 parents 2efbe73 + 8196c08 commit 6711964
Show file tree
Hide file tree
Showing 15 changed files with 756 additions and 178 deletions.
6 changes: 5 additions & 1 deletion client/ayon_shotgrid/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
from .version import __version__
from .addon import (
ShotgridAddon,
)

__all__ = ("ShotgridAddon",)
__all__ = (
"ShotgridAddon",
"__version__",
)
136 changes: 112 additions & 24 deletions client/ayon_shotgrid/addon.py
Original file line number Diff line number Diff line change
@@ -1,37 +1,67 @@
import os

import ayon_api

from ayon_core.addon import (
AYONAddon,
ITrayAddon,
IPluginPaths,
)
from ayon_core.lib import Logger

log = Logger.get_logger(__name__)

SHOTGRID_ADDON_DIR = os.path.dirname(os.path.abspath(__file__))


class ShotgridAddon(AYONAddon, IPluginPaths):
class ShotgridAddon(AYONAddon, ITrayAddon, IPluginPaths):
name = "shotgrid"
tray_wrapper = None

def initialize(self, studio_settings):
addon_settings = studio_settings.get(self.name, dict())
self._shotgrid_server_url = addon_settings.get("shotgrid_server")
addon_settings = studio_settings[self.name]
client_login_info = addon_settings["client_login"]

sg_secret = ayon_api.get_secret(addon_settings["shotgrid_api_secret"])
self._shotgrid_script_name = sg_secret.get("name")
self._shotgrid_api_key = sg_secret.get("value")
self._enable_local_storage = addon_settings.get("enable_shotgrid_local_storage")
self._local_storage_key = addon_settings.get("local_storage_key")
log.debug(
f"Initializing {self.name} addon with "
"settings: {addon_settings}"
)
self._shotgrid_server_url = addon_settings["shotgrid_server"]
self._client_login_type = client_login_info["type"]

self._shotgrid_api_key = None
self._shotgrid_script_name = None

# reconfigure for client user api key since studio might need to
# use a different api key with different permissions access
if self._client_login_type in ["env", "tray_api_key"]:
self._shotgrid_script_name = (
client_login_info
[self._client_login_type]
["client_sg_script_name"]
)
self._shotgrid_api_key = (
client_login_info
[self._client_login_type]
["client_sg_script_key"]
)

self._enable_local_storage = addon_settings.get(
"enable_shotgrid_local_storage")

# ShotGrid local storage entry name
self._local_storage_key = addon_settings.get(
"shotgrid_local_storage_key")

def get_sg_url(self):
return self._shotgrid_server_url if self._shotgrid_server_url else None
return self._shotgrid_server_url or None

def get_sg_script_name(self):
return self._shotgrid_script_name if self._shotgrid_script_name else None
return self._shotgrid_script_name or None

def get_sg_api_key(self):
return self._shotgrid_api_key if self._shotgrid_api_key else None
return self._shotgrid_api_key or None

def get_client_login_type(self):
return self._client_login_type or None

def get_plugin_paths(self):
return {
Expand All @@ -41,21 +71,79 @@ def get_plugin_paths(self):
}

def is_local_storage_enabled(self):
return self._enable_local_storage if self._enable_local_storage else False
return self._enable_local_storage or False

def get_local_storage_key(self):
return self._local_storage_key if self._local_storage_key else None
return self._local_storage_key or None

def create_shotgrid_session(self):
from .lib import credentials
kwargs = {
"shotgrid_url": self._shotgrid_server_url,
}

sg_username = os.getenv("AYON_SG_USERNAME")
proxy = os.environ.get("HTTPS_PROXY", "").replace("https://", "")

return credentials.create_sg_session(
self._shotgrid_server_url,
sg_username,
self._shotgrid_script_name,
self._shotgrid_api_key,
proxy,
)
if proxy:
kwargs["proxy"] = proxy

if self._client_login_type == "env":
sg_username = (
os.getenv("AYON_SG_USERNAME")
# TODO: Remove USER env variable in future once ayon-core deadline
# passing of AYON_SG_USERNAME is solved
or os.getenv("USER")
)
kwargs.update({
"username": sg_username,
"api_key": self._shotgrid_api_key,
"script_name": self._shotgrid_script_name,
})
elif self._client_login_type == "tray_pass":
sg_username, sg_password = credentials.get_local_login()

if not sg_username or not sg_password:
return None

kwargs.update({
"username": sg_username,
"password": sg_password
})

elif self._client_login_type == "tray_api_key":
sg_username, _ = credentials.get_local_login()
kwargs.update({
"username": sg_username,
"api_key": self._shotgrid_api_key,
"script_name": self._shotgrid_script_name,
})

return credentials.create_sg_session(**kwargs)

def tray_init(self):
# do not initialize tray if client login type is not tray related
if self._client_login_type == "env":
return

from .tray.shotgrid_tray import ShotgridTrayWrapper
self.tray_wrapper = ShotgridTrayWrapper(self)

def tray_start(self):
# do not initialize tray if client login type is not tray related
if self._client_login_type == "env":
return

return self.tray_wrapper.set_username_label()

def tray_exit(self, *args, **kwargs):
# do not initialize tray if client login type is not tray related
if self._client_login_type == "env":
return

return self.tray_wrapper

def tray_menu(self, tray_menu):
# do not initialize tray if client login type is not tray related
if self._client_login_type == "env":
return

return self.tray_wrapper.tray_menu(tray_menu)
140 changes: 129 additions & 11 deletions client/ayon_shotgrid/lib/credentials.py
Original file line number Diff line number Diff line change
@@ -1,33 +1,151 @@
import shotgun_api3
from shotgun_api3.shotgun import AuthenticationFault

from ayon_core.lib import AYONSecureRegistry

def create_sg_session(shotgrid_url, username, script_name, api_key, proxy):

def check_user_permissions(
shotgrid_url,
username,
password: str = None,
api_key: str = None,
script_name: str = None,
proxy: str = None,
):
"""Check if the provided user can access the Shotgrid API.
Args:
shotgrid_url (str): The Shotgun server URL.
username (str): The Shotgrid username to use the Session as.
password (Optional[str]): The Shotgrid password to use the Session as.
api_key (Optional[str]): The Shotgrid API key to use the Session as.
script_name (Optional[str]): The Shotgrid API script name to use the
Session as.
proxy (Optional[str]): The proxy to use for the connection.
Returns:
tuple(bool, str): Whether the connection was successful or not, and a
string message with the result.
"""

if not any([shotgrid_url, username]):
return (False, "Missing a field.")

kwargs = {}

if api_key:
if not script_name:
return (
False,
(
"'script_name' input arg should be used in "
"combination with 'api_key'."
)
)
kwargs.update({
"api_key": api_key,
"script_name": script_name,
})
if password:
kwargs["password"] = password
if proxy:
kwargs["proxy"] = proxy

try:
session = create_sg_session(shotgrid_url, username, **kwargs)
session.close()
except AuthenticationFault as e:
return (False, str(e))

return (True, "Successfully logged in.")


def create_sg_session(
shotgrid_url,
username,
password: str = None,
api_key: str = None,
script_name: str = None,
proxy: str = None,
):
"""Attempt to create a Shotgun Session
Args:
shotgrid_url (str): The Shotgun server URL.
username (str): The Shotgrid username to use the Session as.
script_name (str): The Shotgrid API script name.
api_key (str): The Shotgrid API key.
proxy (str): The proxy address to use to connect to SG server.
password (Optional[str]): The Shotgrid password to use the Session as.
api_key (Optional[str]): The Shotgrid API key to use the Session as.
script_name (Optional[str]): The Shotgrid API script name to use the
Session as.
proxy (Optional[str]): The proxy to use for the connection.
Returns:
session (shotgun_api3.Shotgun): A Shotgrid API Session.
Raises:
AuthenticationFault: If the authentication with Shotgrid fails.
"""
if not any([shotgrid_url, username]):
return (False, "Missing a field.")

kwargs = {
"base_url": shotgrid_url
}

session = shotgun_api3.Shotgun(
base_url=shotgrid_url,
script_name=script_name,
http_proxy=proxy,
api_key=api_key,
sudo_as_login=username,
)
if api_key:
if not script_name:
return (
False,
(
"'script_name' input arg should be used in "
"combination with 'api_key'."
)
)
kwargs.update({
"api_key": api_key,
"script_name": script_name,
"sudo_as_login": username,
})
if password:
kwargs.update({
"password": password,
"login": username,
})
if proxy:
kwargs["proxy"] = proxy

session = shotgun_api3.Shotgun(**kwargs)

session.preferences_read()

return session


def get_local_login():
"""Get the Shotgrid Login entry from the local registry. """
try:
reg = AYONSecureRegistry("shotgrid/user")
username = reg.get_item("value")
reg = AYONSecureRegistry("shotgrid/pass")
password = reg.get_item("value")
return username, password
except Exception:
return (None, None)


def save_local_login(username, password):
"""Save the Shotgrid Login entry from the local registry. """
reg = AYONSecureRegistry("shotgrid/user")
reg.set_item("value", username)
reg = AYONSecureRegistry("shotgrid/pass")
reg.set_item("value", password)


def clear_local_login():
"""Clear the Shotgrid Login entry from the local registry. """
reg = AYONSecureRegistry("shotgrid/user")
if reg.get_item("value", None) is not None:
reg.delete_item("value")
reg = AYONSecureRegistry("shotgrid/pass")
if reg.get_item("value", None) is not None:
reg.delete_item("value")
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class CollectShotgridEntities(pyblish.api.ContextPlugin):
def process(self, context):
if not context.data.get("shotgridSession"):
raise KnownPublishError(
"Unable to proceeed without a valid Shotgrid Session."
"Unable to proceed without a valid Shotgrid Session."
)

sg_session = context.data["shotgridSession"]
Expand Down
Loading

0 comments on commit 6711964

Please sign in to comment.