def get_session(self, env: GlobalEnvironment, prompt: bool, exit_on_fail=True, mfa: Optional[str] = None) -> boto3.Session: try: return boto3.Session(profile_name=env.role.profile) except botocore.exceptions.ProfileNotFound: Utils.stc_error_exit(f"The provided profile override of {env.role.profile} is invalid. Are you sure " f"this profile is set in your ~/.aws/credentials and ~/.aws/config files?")
def base_url(self) -> str: result = re.match(r'^.*https://(.*\.com)/.*', self.app_link) if result: return result.group(1) else: Utils.stc_error_exit(f"Unable to parse base url from OKTA application link: {self.app_link}, are you " f"sure this link is valid?")
def stc_get_defaults(skip: bool = False, profile: str = None) -> Optional[CLIDefaults]: """Lookup a user's defaults as configured by --configure option. :param skip - Boolean, if this is true, exit and return none. :param profile - AWS CLI profile to use as an override. If this is passed in all other options are ignored. :return: hydrated CLIDefaults object of default values stored in cache file or None if no cache found """ if profile: return CLIDefaults.from_profile(profile) cache_mgr = CacheManager(file_override=DEFAULTS_FILE_CACHE_PATH) try: last_write, defaults = cache_mgr.get(DEFAULTS_KEY) if not defaults: if skip: return CLIDefaults.unconfigured() else: Utils.stc_error_exit(f'{CLI_NAME} has not been configured.\n\nIf your organization has already ' f'installed Figgy Cloud, please run ' f'`{CLI_NAME} --{configure.name}`.\n\n' f'You may also provide the `--profile` flag, or log-in to our free sandbox with ' f'`figgy login sandbox` to experiment with {CLI_NAME}.') return defaults except JSONDecodeError: return None
def current_cloud_version(self): if self._config: return self._config.get_fig_simple(PS_CLOUD_VERSION_PATH).value else: Utils.stc_error_exit("Before upgrading you must configure Figgy to ensure your version of " f"the Figgy CLI will be compatible your installed version of Figgy Cloud. " f"Please run `{CLI_NAME} --configure` before trying again. If you do not have an installed " f"version of Figgy Cloud, you may log into the Figgy Sandbox and try again. " f"`figgy login sandbox`. Good luck!")
def basic_configure(self, configure_provider=True) -> CLIDefaults: defaults: CLIDefaults = self.get_defaults() if not defaults: Utils.stc_error_exit(f"Please run {CLI_NAME} --{configure.name} to set up Figgy, " f"you've got problems friend!") else: defaults = self.configure_auth(defaults, configure_provider=configure_provider) return defaults
def main(): """ Entrypoint to figgy. Performs generic validation, then routes user down appropriate execution path based on command line parameters """ arguments = sys.argv user = getpass.getuser() Utils.stc_validate(user != ROOT_USER, f"Hey! Stop trying to run {CLI_NAME} as {ROOT_USER}. That's bad!") original_command = ' '.join(arguments) sys.argv = arguments os.makedirs(os.path.dirname(BOTO3_CLIENT_FILE_LOCK_PATH), exist_ok=True) try: # Parse / Validate Args args = FiggyCLI.parse_args() if hasattr(args, 'debug') and args.debug: root_logger.setLevel(logging.INFO) root_logger.addHandler(stdout_handler) cli: FiggyCLI = FiggyCLI(args) command: Command = cli.get_command() if hasattr(args, 'info') and args.info: command.print_help_text() else: command.execute() except AssertionError as e: Utils.stc_error_exit(e.args[0]) except KeyboardInterrupt: pass except SystemExit: pass except (BaseException, Exception) as e: try: error_reporter = FiggyErrorReporter(FiggySetup.stc_get_defaults(skip=True, profile=None)) error_reporter.log_error(original_command, e) except Exception as e: print(e) print(f"\n\nUnable to log or report this exception. Please submit a Github issue to: {FIGGY_GITHUB}")
def __lookup_roles(self) -> List[AssumableRole]: assertion = self.get_saml_assertion(prompt=False) root = ET.fromstring(assertion) prefix_map = {"saml2": "urn:oasis:names:tc:SAML:2.0:assertion"} role_attribute = root.find( ".//saml2:Attribute[@Name='https://aws.amazon.com/SAML/Attributes/Role']", prefix_map) # SAML arns should look something like this: # arn:aws:iam::106481321259:saml-provider/okta,arn:aws:iam::106481321259:role/figgy-dev-data # One exception is the `figgy-default` role. pattern = r'^arn:aws:iam::([0-9]+):saml-provider/(\w+),arn:aws:iam::.*role/(\w+-(\w+)-(\w+))' assumable_roles: List[AssumableRole] = [] for value in role_attribute.findall('.//saml2:AttributeValue', prefix_map): if FIGGY_DEFAULT_ROLE_NAME not in value: result = re.search(pattern, value.text) unparsable_msg = f'{value.text} is of an invalid pattern, it must match: {pattern} for figgy to ' \ f'dynamically map account_id -> run_env -> role for OKTA users. If this is not a figgy role, ' \ f'ignore this message.' if not result: Utils.stc_warn(unparsable_msg) continue result.groups() account_id, provider_name, role_name, run_env, role = result.groups( ) if not account_id or not run_env or not role_name or not role: Utils.stc_error_exit(unparsable_msg) else: assumable_roles.append( AssumableRole(account_id=account_id, role=Role(role=role, full_name=role_name), run_env=RunEnv(env=run_env), provider_name=provider_name, profile=None)) return assumable_roles
def configure_auth(self, current_defaults: CLIDefaults, configure_provider=True) -> CLIDefaults: updated_defaults = current_defaults if configure_provider or current_defaults.provider is Provider.UNSELECTED: provider: Provider = Input.select_provider() updated_defaults.provider = provider else: provider: Provider = current_defaults.provider if provider in Provider.sso_providers(): user: str = Input.get_user(provider=provider.name) password: str = Input.get_password(provider=provider.name) self._secrets_mgr.set_password(user, password) updated_defaults.user = user try: mfa_enabled = Utils.parse_bool(self._config_mgr.get_or_prompt(Config.Section.Figgy.MFA_ENABLED, Input.select_mfa_enabled, desc=MFA_DESC)) if mfa_enabled: auto_mfa = Utils.parse_bool(self._config_mgr.get_or_prompt(Config.Section.Figgy.AUTO_MFA, Input.select_auto_mfa, desc=AUTO_MFA_DESC)) else: auto_mfa = False except ValueError as e: Utils.stc_error_exit(f"Invalid value found in figgy defaults file under " f"{Config.Section.Figgy.MFA_ENABLED.value}. It must be either 'true' or 'false'") else: updated_defaults.mfa_enabled = mfa_enabled updated_defaults.auto_mfa = auto_mfa if updated_defaults.auto_mfa: mfa_secret = Input.get_mfa_secret() self._secrets_mgr.set_mfa_secret(updated_defaults.user, mfa_secret) if configure_provider: provider_config = ProviderConfigFactory().instance(provider, mfa_enabled=updated_defaults.mfa_enabled) updated_defaults.provider_config = provider_config return updated_defaults
def get_sso_session(self, prompt: bool = False, mfa: Optional[str] = None) -> Okta: """ Pulls the last okta session from cache, if cache doesn't exist, generates a new session and writes it to cache. From this session, the OKTA SVC is hydrated and returned. Args: prompt: If supplied, will never get session from cache. Returns: Initialized Okta service. """ count = 0 while True: try: if prompt: raise InvalidSessionError( "Forcing new session due to prompt.") cached_session = self._get_session_from_cache() if not cached_session: raise InvalidSessionError("No session found in cache.") okta = Okta(OktaSessionAuth(self._defaults, cached_session)) return okta except (FileNotFoundError, InvalidSessionError, JSONDecodeError, AttributeError) as e: try: password = self._secrets_mgr.get_password( self._defaults.user) if not mfa: if self._context.command == commands.ui and not self._defaults.auto_mfa: raise CannotRetrieveMFAException( "Cannot retrieve MFA, UI mode is activated.") else: color = Utils.default_colors( ) if self._defaults.colors_enabled else None mfa = self._secrets_mgr.get_next_mfa(self._defaults.user) if self._defaults.auto_mfa else \ Input.get_mfa(display_hint=True, color=color) log.info(f"Getting OKTA primary auth with mfa: {mfa}") primary_auth = OktaPrimaryAuth(self._defaults, password, mfa) self._write_okta_session_to_cache( primary_auth.get_session()) return Okta(primary_auth) except InvalidSessionError as e: prompt = True log.error( f"Caught error when authing with OKTA & caching session: {e}. " ) time.sleep(1) count += 1 if count > 1: if self._context.command == ui: raise InvalidCredentialsException( "Failed OKTA authentication. Invalid user, password, or MFA." ) else: Utils.stc_error_exit( "Unable to autheticate with OKTA with your provided credentials. Perhaps your" f"user, password, or MFA changed? Try rerunning `{CLI_NAME} --configure` again." )