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/_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/login.py b/src/sumo/wrapper/login.py index 5577d7a..2245d4b 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 @@ -7,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") @@ -29,21 +32,12 @@ def get_parser() -> ArgumentParser: ) parser.add_argument( - "-i", - "--interactive", - dest="interactive", - action="store_true", - default=False, - 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( @@ -62,22 +56,48 @@ def main(): args = get_parser().parse_args() 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) - print("Login to Sumo environment: " + env) + if mode not in modes: + print(f"Invalid mode: {mode}") + return 1 + + if mode != "silent": + print("Login to Sumo environment: " + env) + + 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 + is_interactive = False + is_devicecode = True sumo = SumoClient( - args.env, interactive=args.interactive, devicecode=args.devicecode + env, + interactive=is_interactive, + devicecode=is_devicecode, ) token = sumo.authenticate() - if args.print_token: - print(f"TOKEN: {token}") + if mode != "silent": + if args.print_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 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__": 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