From 525cd564a3cc365ce32580adabe244cde26d8d50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=85dne=20Jacobsen?= Date: Tue, 21 May 2024 12:25:13 +0200 Subject: [PATCH 1/5] Add --silent option --- src/sumo/wrapper/_auth_provider.py | 28 +++++++++++------- src/sumo/wrapper/_version.py | 16 ++++++++++ src/sumo/wrapper/login.py | 47 ++++++++++++++++++++++++------ src/sumo/wrapper/sumo_client.py | 23 ++------------- 4 files changed, 73 insertions(+), 41 deletions(-) create mode 100644 src/sumo/wrapper/_version.py diff --git a/src/sumo/wrapper/_auth_provider.py b/src/sumo/wrapper/_auth_provider.py index 0c64bfd..0e35a57 100644 --- a/src/sumo/wrapper/_auth_provider.py +++ b/src/sumo/wrapper/_auth_provider.py @@ -1,5 +1,3 @@ -import platform -from pathlib import Path import msal import os from datetime import datetime, timedelta @@ -71,6 +69,18 @@ def get_authorization(self): pass +class AuthProviderSilent(AuthProvider): + def __init__(self, client_id, authority, resource_id): + super().__init__(resource_id) + cache = get_token_cache(resource_id, ".token") + self._app = msal.PublicClientApplication( + client_id=client_id, authority=authority, token_cache=cache + ) + self._resource_id = resource_id + + self._scope = scope_for_resource(resource_id) + + class AuthProviderAccessToken(AuthProvider): def __init__(self, access_token): self._access_token = access_token @@ -396,6 +406,11 @@ def get_auth_provider( if os.path.exists(get_token_path(resource_id, ".sharedkey")): return AuthProviderSumoToken(resource_id) # ELSE + auth_silent = AuthProviderSilent(client_id, authority, resource_id) + token = auth_silent.get_token() + if token is not None: + return auth_silent + # ELSE if interactive: return AuthProviderInteractive(client_id, authority, resource_id) # ELSE @@ -416,12 +431,3 @@ def get_auth_provider( ] ): return AuthProviderManaged(resource_id) - # ELSE - lockfile_path = Path.home() / ".config/chromium/SingletonLock" - if Path(lockfile_path).is_symlink() and not str( - Path(lockfile_path).resolve() - ).__contains__(platform.node()): - # https://github.com/equinor/sumo-wrapper-python/issues/193 - return AuthProviderDeviceCode(client_id, authority, resource_id) - # ELSE - return AuthProviderInteractive(client_id, authority, resource_id) diff --git a/src/sumo/wrapper/_version.py b/src/sumo/wrapper/_version.py new file mode 100644 index 0000000..16467f7 --- /dev/null +++ b/src/sumo/wrapper/_version.py @@ -0,0 +1,16 @@ +# file generated by setuptools_scm +# don't change, don't track in version control +TYPE_CHECKING = False +if TYPE_CHECKING: + from typing import Tuple, Union + VERSION_TUPLE = Tuple[Union[int, str], ...] +else: + VERSION_TUPLE = object + +version: str +__version__: str +__version_tuple__: VERSION_TUPLE +version_tuple: VERSION_TUPLE + +__version__ = version = '1.0.10.dev1+gb0c8c00.d20240515' +__version_tuple__ = version_tuple = (1, 0, 10, 'dev1', 'gb0c8c00.d20240515') diff --git a/src/sumo/wrapper/login.py b/src/sumo/wrapper/login.py index 5577d7a..ba67f8d 100644 --- a/src/sumo/wrapper/login.py +++ b/src/sumo/wrapper/login.py @@ -1,5 +1,6 @@ import logging - +import platform +from pathlib import Path from argparse import ArgumentParser from sumo.wrapper import SumoClient @@ -33,7 +34,7 @@ def get_parser() -> ArgumentParser: "--interactive", dest="interactive", action="store_true", - default=False, + default=True, help="Login interactively", ) @@ -55,6 +56,15 @@ def get_parser() -> ArgumentParser: help="Print access token", ) + parser.add_argument( + "-s", + "--silent", + dest="silent", + action="store_true", + default=False, + help="Attempt acquire token silently", + ) + return parser @@ -64,20 +74,39 @@ def main(): env = args.env logger.debug("env is %s", env) - print("Login to Sumo environment: " + env) + if args.silent: + args.interactive = False + args.devicecode = False + args.print_token = False + else: + print("Login to Sumo environment: " + env) + + if args.interactive: + lockfile_path = Path.home() / ".config/chromium/SingletonLock" + if Path(lockfile_path).is_symlink() and not str( + Path(lockfile_path).resolve() + ).__contains__(platform.node()): + # https://github.com/equinor/sumo-wrapper-python/issues/193 + args.interactive = False + args.devicecode = True sumo = SumoClient( args.env, interactive=args.interactive, devicecode=args.devicecode ) token = sumo.authenticate() - if args.print_token: - print(f"TOKEN: {token}") - - if token is not None: - print("Successfully logged in to Sumo environment: " + env) + if args.silent: + if token is None: + return 1 + return 0 else: - print("Failed login to Sumo environment: " + env) + if args.print_token: + print(f"TOKEN: {token}") + + if token is not None: + print("Successfully logged in to Sumo environment: " + env) + else: + print("Failed login to Sumo environment: " + env) if __name__ == "__main__": diff --git a/src/sumo/wrapper/sumo_client.py b/src/sumo/wrapper/sumo_client.py index 0bcaa6e..7c788db 100644 --- a/src/sumo/wrapper/sumo_client.py +++ b/src/sumo/wrapper/sumo_client.py @@ -84,27 +84,6 @@ def __init__( access_token=access_token, devicecode=devicecode, ) - if ( - self.auth.get_token() is None - and refresh_token is None - and access_token is None - ): - print("\n \033[31m !!! Falling back to device-code login:\033[0m") - self.auth = get_auth_provider( - client_id=APP_REGISTRATION[env]["CLIENT_ID"], - authority=f"{AUTHORITY_HOST_URI}/{TENANT_ID}", - resource_id=APP_REGISTRATION[env]["RESOURCE_ID"], - interactive=False, - refresh_token=None, - access_token=None, - devicecode=True, - ) - if self.auth.get_token() is None: - print( - "\n\n \033[31m " - + "NOTE! Login failed/timed out. Giving up." - + "\033[0m" - ) if env == "localhost": self.base_url = "http://localhost:8084/api/v1" @@ -114,6 +93,8 @@ def __init__( return def authenticate(self): + if self.auth is None: + return None return self.auth.get_token() @property From bfeb574a0c7afb4051777760a6342de3fd572e79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=85dne=20Jacobsen?= Date: Tue, 21 May 2024 15:07:29 +0200 Subject: [PATCH 2/5] remove _version.py and add to gitignore --- .gitignore | 1 + src/sumo/wrapper/_version.py | 16 ---------------- 2 files changed, 1 insertion(+), 16 deletions(-) delete mode 100644 src/sumo/wrapper/_version.py diff --git a/.gitignore b/.gitignore index f6127cc..1beebc1 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ build /src/testing.py /docs/_build /src/sumo/wrapper/version.py +*_version.py \ No newline at end of file diff --git a/src/sumo/wrapper/_version.py b/src/sumo/wrapper/_version.py deleted file mode 100644 index 16467f7..0000000 --- a/src/sumo/wrapper/_version.py +++ /dev/null @@ -1,16 +0,0 @@ -# file generated by setuptools_scm -# don't change, don't track in version control -TYPE_CHECKING = False -if TYPE_CHECKING: - from typing import Tuple, Union - VERSION_TUPLE = Tuple[Union[int, str], ...] -else: - VERSION_TUPLE = object - -version: str -__version__: str -__version_tuple__: VERSION_TUPLE -version_tuple: VERSION_TUPLE - -__version__ = version = '1.0.10.dev1+gb0c8c00.d20240515' -__version_tuple__ = version_tuple = (1, 0, 10, 'dev1', 'gb0c8c00.d20240515') From 5b698ac12a439c9f303253d30bcf588ad6a7b396 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=85dne=20Jacobsen?= Date: Thu, 23 May 2024 10:02:30 +0200 Subject: [PATCH 3/5] replace --interactive, --devidecode and --silent with --mode --- src/sumo/wrapper/login.py | 52 +++++++++++++++------------------------ 1 file changed, 20 insertions(+), 32 deletions(-) diff --git a/src/sumo/wrapper/login.py b/src/sumo/wrapper/login.py index ba67f8d..3c51028 100644 --- a/src/sumo/wrapper/login.py +++ b/src/sumo/wrapper/login.py @@ -8,6 +8,8 @@ logger = logging.getLogger("sumo.wrapper") logger.setLevel(level="CRITICAL") +modes = ["interactive", "devicecode", "silent"] + def get_parser() -> ArgumentParser: parser = ArgumentParser(description="Login to Sumo on azure") @@ -30,21 +32,12 @@ def get_parser() -> ArgumentParser: ) parser.add_argument( - "-i", - "--interactive", - dest="interactive", - action="store_true", - default=True, - help="Login interactively", - ) - - parser.add_argument( - "-d", - "--devicecode", - dest="devicecode", - action="store_true", - default=False, - help="Login with device-code", + "-m", + "--mode", + dest="mode", + action="store", + default="interactive", + help=f"Valid modes: {', '.join(modes)}", ) parser.add_argument( @@ -56,15 +49,6 @@ def get_parser() -> ArgumentParser: help="Print access token", ) - parser.add_argument( - "-s", - "--silent", - dest="silent", - action="store_true", - default=False, - help="Attempt acquire token silently", - ) - return parser @@ -72,16 +56,18 @@ def main(): args = get_parser().parse_args() logger.setLevel(level=args.verbosity) env = args.env + mode = args.mode + logger.debug("env is %s", env) - if args.silent: - args.interactive = False - args.devicecode = False - args.print_token = False - else: + if mode not in modes: + print(f"Invalid mode: {mode}") + return 1 + + if mode != "silent": print("Login to Sumo environment: " + env) - if args.interactive: + if mode == "interactive": lockfile_path = Path.home() / ".config/chromium/SingletonLock" if Path(lockfile_path).is_symlink() and not str( Path(lockfile_path).resolve() @@ -91,11 +77,13 @@ def main(): args.devicecode = True sumo = SumoClient( - args.env, interactive=args.interactive, devicecode=args.devicecode + args.env, + interactive=mode == "interactive", + devicecode=mode == "devicecode", ) token = sumo.authenticate() - if args.silent: + if mode == "silent": if token is None: return 1 return 0 From b2347639221fa187ef4116be16751159b77fc7ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=85dne=20Jacobsen?= Date: Thu, 23 May 2024 10:08:29 +0200 Subject: [PATCH 4/5] prevent modifying args directly --- src/sumo/wrapper/login.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/sumo/wrapper/login.py b/src/sumo/wrapper/login.py index 3c51028..1fb4fe6 100644 --- a/src/sumo/wrapper/login.py +++ b/src/sumo/wrapper/login.py @@ -57,6 +57,8 @@ def main(): logger.setLevel(level=args.verbosity) env = args.env mode = args.mode + is_interactive = mode == "interactive" + is_devicecode = mode == "devicecode" logger.debug("env is %s", env) @@ -69,17 +71,18 @@ def main(): if mode == "interactive": lockfile_path = Path.home() / ".config/chromium/SingletonLock" + if Path(lockfile_path).is_symlink() and not str( Path(lockfile_path).resolve() ).__contains__(platform.node()): # https://github.com/equinor/sumo-wrapper-python/issues/193 - args.interactive = False - args.devicecode = True + is_interactive = False + is_devicecode = True sumo = SumoClient( - args.env, - interactive=mode == "interactive", - devicecode=mode == "devicecode", + env, + interactive=is_interactive, + devicecode=is_devicecode, ) token = sumo.authenticate() From d0d31624a9f5332e69a200eeb3d678d9849979ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=85dne=20Jacobsen?= Date: Thu, 23 May 2024 12:32:17 +0200 Subject: [PATCH 5/5] + return code --- src/sumo/wrapper/login.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sumo/wrapper/login.py b/src/sumo/wrapper/login.py index 1fb4fe6..2245d4b 100644 --- a/src/sumo/wrapper/login.py +++ b/src/sumo/wrapper/login.py @@ -86,19 +86,19 @@ def main(): ) token = sumo.authenticate() - if mode == "silent": - if token is None: - return 1 - return 0 - else: + if mode != "silent": if args.print_token: - print(f"TOKEN: {token}") + print(token) if token is not None: print("Successfully logged in to Sumo environment: " + env) else: print("Failed login to Sumo environment: " + env) + if token is None: + return 1 + return 0 + if __name__ == "__main__": main()