コード例 #1
0
    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?")
コード例 #2
0
ファイル: provider_config.py プロジェクト: figtools/figgy-cli
 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?")
コード例 #3
0
ファイル: setup.py プロジェクト: figtools/figgy-cli
    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
コード例 #4
0
 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!")
コード例 #5
0
ファイル: setup.py プロジェクト: figtools/figgy-cli
    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
コード例 #6
0
ファイル: cli.py プロジェクト: figtools/figgy-cli
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}")
コード例 #7
0
    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
コード例 #8
0
ファイル: setup.py プロジェクト: figtools/figgy-cli
    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
コード例 #9
0
    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."
                            )