def take_action(self, parsed_args): # Load what we can from credentials cache. try: ag_context = Agave._read_current(agave_kwargs=True) except FileNotFoundError: # A very common case (all new tapis_cli installs, for instance), will # find NO cache file on disk. This must be recoverable, so set the # context to an empty dict raise FileNotFoundError( 'Auth configuration was not loaded. Try running "tapis auth init".' ) # Formulate a table view of key values for current session headers = [ 'tenant_id', 'username', 'api_key', 'access_token', 'expires_at', 'refresh_token' ] data = [ # Coerce to string to avoid failures where a deepcopy # operation in Python's implementation of tuple() is # unable to accomodate copying properties of an Agave object ag_context['tenant_id'], ag_context['username'], ag_context['api_key'], ag_context['token'], ag_context['expires_at'], ag_context['refresh_token'] ] return (tuple(headers), tuple(data))
def init_clients(self, parsed_args): """Override CommandBase to set up client with passed token """ # client = Agave.restore() if parsed_args.api_server is not None and parsed_args.access_token is not None: self.tapis_client = Agave(api_server=parsed_args.api_server, token=parsed_args.access_token) elif parsed_args.access_token is not None: try: client = Agave._read_current(agave_kwargs=True) except Exception: raise AgaveError( 'Tapis API server was not discoverable. Exiting.') self.tapis_client = Agave(api_server=client['api_server'], token=parsed_args.access_token) else: try: client = Agave.restore() self.tapis_client = client self.tapis_client.refresh() # self.requests_client = self._get_direct(self.tapis_client) except Exception: raise AgaveError(constants.TAPIS_AUTH_FAIL) try: self.requests_client = self._get_direct(self.tapis_client) except Exception: raise AgaveError(constants.TAPIS_AUTH_FAIL) return self
def init_clients(self, parsed_args): """Override CommandBase to set up client with passed token """ api_server = getattr(parsed_args, 'api_server', None) token = getattr(parsed_args, 'access_token', None) nonce = getattr(parsed_args, 'nonce', None) verify_ssl = getattr(parsed_args, 'verify_ssl', True) if (token is not None or nonce is not None) and api_server is None: try: client = Agave._read_current(agave_kwargs=True) api_server = client['api_server'] except Exception: raise AgaveError('Unable to discover Tapis API server URL.') # Initialize the AgavePy client try: if api_server is not None and token is not None: self.tapis_client = Agave(api_server=api_server, token=token, verify=verify_ssl) elif api_server is not None and nonce is not None: self.tapis_client = Agave(api_server=api_server, use_nonce=True, verify=verify_ssl) self.client_extra_args['nonce'] = nonce else: # Load from disk cache # client = Agave.restore() clients = Agave._read_clients() client0 = clients[0] # Override SSL verification from stored client client0['verify'] = verify_ssl client = Agave(**client0) self.tapis_client = client self.tapis_client.refresh() except Exception: raise AgaveError(constants.TAPIS_AUTH_FAIL) # Initialize the direct requests client try: # Direct client will inherit SSL check behavior from Tapis client self.requests_client = self._get_direct(self.tapis_client) except Exception: raise AgaveError(constants.TAPIS_AUTH_FAIL) return self
def take_action(self, parsed_args): # Load what we can from credentials cache. Ultimately, if no # overrides are specified, the cached contents will be used to # populate the Tapis client. firstrun() try: logger.debug('Read from local Tapis cache...') ag_context = Agave._read_current(agave_kwargs=True) except FileNotFoundError: # A very common case (all new tapis_cli installs, for instance), will # find NO cache file on disk. This must be recoverable, so set the # context to an empty dict ag_context = {} # Inject a password field (which may be filled later) into the context ag_context['password'] = None # Process overrides to ag_context provided by parsed_args mandate_username = False mandate_password = False # Process tenant override parsed_tenant_id = getattr(parsed_args, 'tapis_tenant_id', None) if parsed_tenant_id is not None: if ag_context.get('tenant_id', None) != parsed_tenant_id: mandate_username = True mandate_password = True logger.warning( 'Tenant changed. Username and password must be specified.') ag_context['tenant_id'] = parsed_tenant_id # Process username override parsed_username = getattr(parsed_args, 'tapis_username', None) if parsed_username is not None: if ag_context.get('username', None) != parsed_username: mandate_password = True logger.warning('Username changed. Password must be specified.') ag_context['username'] = parsed_username # If interactive OR cannot establish tenant_id, prompt for it temp_tenant_id = ag_context.get('tenant_id', None) if temp_tenant_id is None or parsed_args.interactive: if temp_tenant_id is None: temp_tenant_id = TAPIS_DEFAULT_TENANT_ID tl = [t.get('code') for t in agavepy.tenants.list_tenants()] print('Available Tenants\n=================') print(fmtcols(tl, 5)) ag_context['tenant_id'] = prompt('Enter a tenant name', temp_tenant_id) ag_context['api_server'] = agavepy.tenants.api_server_by_id( ag_context['tenant_id']) # Prompt when interactive or username is reset or unavailable if mandate_username or ag_context.get( 'username', None) is None or parsed_args.interactive: mandate_password = True ag_context['username'] = prompt('Username', ag_context.get('username', None)) # Set client name now that we have tenant and user ag_context['client_name'] = '{0}-{1}-{2}-{3}'.format( CLIENT_PREFIX, ag_context['tenant_id'], ag_context['username'], get_hostname()) # Prompt when interactive or tenant/username is reset or unavailable if mandate_password or parsed_args.interactive: temp_password = getattr(parsed_args, 'tapis_password', None) if temp_password is None or parsed_args.interactive: temp_password = prompt('Password for {0}'.format( ag_context['username']), temp_password, secret=True) # Remove extant api_key, api_secret, and tokens to ensure the # HTTP Basic Auth processor will be loaded when the context is # sent to an Agave instance. ag_context['password'] = temp_password ag_context['api_key'] = None ag_context['api_secret'] = None ag_context['access_token'] = None ag_context['refresh_token'] = None api_key = ag_context.get('api_key', None) ag = None # No client was loadable from the local system if api_key is None or api_key == '': logger.debug('clients.create: {0}'.format( ag_context['client_name'])) create_context = { 'api_server': ag_context['api_server'], 'username': ag_context['username'], 'password': ag_context['password'], 'verify': True } ag = Agave(**create_context) # Preflight activity: Get rid of existing client try: logger.debug('clients.delete()...') ag.clients.delete(clientName=ag_context['client_name']) except Exception: logger.warning('Client was not deleted') pass logger.info('clients.create: {0}'.format( ag_context['client_name'])) ag.clients.create( body={ 'clientName': ag_context['client_name'], 'description': 'Generated by {0}@{1} at {2}'.format( get_local_username(), get_public_ip(), datetime.datetime.utcnow().strftime( "%Y-%m-%dT%H:%M:%SZ")) }) else: # Client was cached - load it up logger.debug('Loading client from cache...') ag = Agave(**ag_context) # Hit profiles service to check client try: logger.debug('Verify Tapis client is active...') ag.profiles.listByUsername(username=ag_context['username']) logger.debug('Verified') except Exception as err: raise AgaveError('Tapis client appears invalid: {0}'.format(err)) # Formulate a table view of key values for current session headers = [ 'tenant_id', 'username', 'client_name', 'api_key', 'access_token', 'expires_at' ] data = [ # Coerce to string to avoid failures where a deepcopy # operation in Python's implementation of tuple() is # unable to accomodate copying properties of an Agave object str(ag.tenant_id), str(ag.username), str(ag_context['client_name']), str(ag.api_key), str(ag._token), str(ag.expires_at) ] et.phone_home() return (tuple(headers), tuple(data))
def take_action(self, parsed_args): # Load what we can from credentials cache. Ultimately, if no # overrides are specified, the cached contents will be used to # populate the Tapis client. firstrun() interactive = parsed_args.interactive # Process overrides to ag_context provided by parsed_args mandate_username = False mandate_password = False mandate_git_reg = interactive if interactive: print('Configure Tapis API access:') print('===========================') # Fetch current values stored on disk into ag_context try: logger.debug('Reading from local Tapis environment') ag_context = Agave._read_current(agave_kwargs=True) except FileNotFoundError: # A very common case (all new tapis_cli installs, for instance), will # find NO cache file on disk. This must be recoverable, so set the # context to an empty dict logger.debug('Read from environment was unsuccessful') ag_context = {} mandate_username = True mandate_password = True # Inject optional password key ag_context['password'] = None # Ensure context has a setting for 'verify'. Default to env setting if 'verify' not in ag_context: ag_context['verify'] = TAPIS_CLI_VERIFY_SSL # Read in from parsed_args, updating ag_context # Process tenant override # Allow this to happen by either specifying --tenant-id or # --api-server. This will trigger the CLI to require user # credentials, which can be passed by argument or entered # interactively parsed_tenant_id = getattr(parsed_args, 'tenant_id', None) parsed_api_server = getattr(parsed_args, 'api_server', None) parsed_username = getattr(parsed_args, 'username', None) parsed_password = getattr(parsed_args, 'password', None) # Allow tenant id OR api server to be provided, updating ag_context as appropriate # Regarding SSL verification: If the default behavior is to not verify, we will # not do verification when accessing the tenants API if parsed_tenant_id is not None: if ag_context.get('tenant_id', None) != parsed_tenant_id: # mandate_git_reg = True mandate_username = True mandate_password = True logger.info('Tenant changed. Credentials will be requested.') ag_context['tenant_id'] = parsed_tenant_id ag_context['api_server'] = agavepy.tenants.api_server_by_id( ag_context['tenant_id'], verify_ssl=TAPIS_CLI_VERIFY_SSL) elif parsed_api_server is not None: if ag_context.get('api_server', None) != parsed_api_server: # mandate_git_reg = True mandate_username = True mandate_password = True logger.info( 'API server changed. Credentials will be requested.') ag_context['api_server'] = parsed_api_server ag_context['tenant_id'] = agavepy.tenants.id_by_api_server( ag_context['api_server'], verify_ssl=TAPIS_CLI_VERIFY_SSL) # If interactive OR cannot establish tenant_id from cache # or args, prompt user to select one temp_tenant_id = ag_context.get('tenant_id', None) if temp_tenant_id is None or interactive: # Pick up default value for tenant_id from settings.TAPIS_DEFAULT_TENANT_ID if temp_tenant_id is None: temp_tenant_id = TAPIS_DEFAULT_TENANT_ID # Get list of tenants # tl = [t.get('code') for t in agavepy.tenants.list_tenants()] th = ['Name', 'Description', 'URL'] tr = [[t.get('code'), t.get('name'), t.get('baseUrl')] for t in agavepy.tenants.list_tenants( verify_ssl=TAPIS_CLI_VERIFY_SSL)] tl = [t[0] for t in tr] pt = PrettyTable() pt.field_names = ['Name', 'Description', 'URL'] for rec in tr: pt.add_row(rec) print(pt) ag_context['tenant_id'] = prompt('Enter a tenant name', temp_tenant_id) if ag_context['tenant_id'] not in tl: raise ValueError( 'Error: "{0}" is not a valid tenant name'.format( ag_context['tenant_id'])) # Look up API server from tenant ID ag_context['api_server'] = agavepy.tenants.api_server_by_id( ag_context['tenant_id'], verify_ssl=TAPIS_CLI_VERIFY_SSL) # Prompt for SSL verification # From here on out in the workflow the user has indiciated a # specific preference re: SSL verification. So, we will use # their preference when communicating with APIs (clients, profiles) # # NOTE: Switching SSL behaviors might cause timeouts due to stale sessions if interactive: verify_ssl = ag_context.get('verify', TAPIS_CLI_VERIFY_SSL) ag_context['verify'] = prompt_boolean('Verify SSL connections', verify_ssl) # Process --username argument if parsed_username is not None: # Force re-capture of password via argument or # interactive entry if username differs if ag_context.get( 'username', None) != parsed_username and parsed_args.password is None: mandate_password = True logger.info('Username changed. Password will be required.') ag_context['username'] = parsed_username # Process --password argument if parsed_password is not None: ag_context['password'] = parsed_password # Prompt when interactive or username is reset or unavailable if interactive or (mandate_username and parsed_username is None): mandate_password = True ag_context['username'] = prompt('{0} username'.format( ag_context['tenant_id'], ag_context.get('username', None)), secret=False, allow_empty=False) # Prompt when interactive or username is reset or unavailable if interactive or (mandate_password and parsed_password is None): temp_password = prompt('{0} password for {1}'.format( ag_context['tenant_id'], ag_context['username']), parsed_password, secret=True, allow_empty=False) ag_context['password'] = temp_password # Password was provided. This indicates the Agave client should NOT # pass in any cached api_key, api_secret, and token, as it will be # interacting with the /clients service, which accepts Basic auth if ag_context['password'] is not None: ag_context['api_key'] = None ag_context['api_secret'] = None ag_context['access_token'] = None ag_context['refresh_token'] = None api_key = ag_context.get('api_key', None) ag = None # Generate client name ag_context['client_name'] = '{0}-{1}-{2}-{3}'.format( CLIENT_PREFIX, ag_context['tenant_id'], ag_context['username'], get_hostname()) # No client was loadable from the local system if api_key is None or api_key == '': logger.debug('clients.create: {0}'.format( ag_context['client_name'])) create_context = { 'api_server': ag_context['api_server'], 'username': ag_context['username'], 'password': ag_context['password'], 'verify': ag_context['verify'] } ag = Agave(**create_context) # Preflight activity: Get rid of existing client try: ag.clients.delete(clientName=ag_context['client_name']) except Exception: logger.debug('Tapis client was not deleted') pass try: ag.clients.create( body={ 'clientName': ag_context['client_name'], 'description': 'Generated by {0}@{1} at {2}'.format( get_local_username(), get_public_ip(), datetime.datetime.utcnow().strftime( "%Y-%m-%dT%H:%M:%SZ")) }) except Exception as err: logger.error('Tapis client was not created') raise AgaveError(TAPIS_AUTH_REJECT) else: # Client was cached - load it up logger.debug('Loading client from cache...') try: ag = Agave(**ag_context) except Exception as err: logger.error( 'Tapis client was not loaded from cache: {0}'.format(err)) raise AgaveError(TAPIS_AUTH_FAIL) # Hit profiles service to check client try: ag.profiles.get() except Exception as err: logger.error( 'Tapis client was unable to make an authenticated API call.') raise AgaveError(TAPIS_AUTH_FAIL) # Formulate a table view of key values for current session headers = [ 'tenant_id', 'username', 'client_name', 'api_key', 'access_token', 'expires_at', 'verify' ] data = [ # Coerce to string to avoid failures where a deepcopy # operation in Python's implementation of tuple() is # unable to accomodate copying properties of an Agave object str(ag.tenant_id), str(ag.username), str(ag_context['client_name']), str(ag.api_key), str(ag._token), str(ag.expires_at), str(ag.verify) ] # Extend headers and data with docker and git workflows (headers, data) = registry.init.interactive(parsed_args, headers, data, mandate_git_reg) (headers, data) = gitserver.init.interactive(parsed_args, headers, data, mandate_git_reg) et.phone_home() return (tuple(headers), tuple(data))