コード例 #1
0
ファイル: figgy.py プロジェクト: figtools/figgy-cli
    def __init__(self, child, extra_args=""):
        self.c = Utils.default_colors()
        self.extra_args = extra_args

        if child:
            c = Utils.default_colors()
            print(f"{c.fg_yl}Testing command: {child.args}{c.rs}")
            self._child = child
            self._child.logfile = sys.stdout
            self._child.delaybeforesend = .5
コード例 #2
0
ファイル: setup.py プロジェクト: figtools/figgy-cli
 def __init__(self, context: FiggyContext):
     self._cache_mgr = CacheManager(file_override=DEFAULTS_FILE_CACHE_PATH)
     self._config_mgr, self.c = ConfigManager.figgy(), Utils.default_colors()
     self._session_mgr = None
     self._session_provider = None
     self._secrets_mgr = SecretsManager()
     self._figgy_context = context
コード例 #3
0
 def __init__(self, command, extra_args=""):
     self.c = Utils.default_colors()
     self.command = command
     self.extra_args = extra_args
     self._child = self.spawn(command)
     print(f"{self.c.fg_yl}Executing action: {self._child.args}{self.c.rs}")
     self._child.logfile = sys.stdout
     self._child.delaybeforesend = .5
コード例 #4
0
    def get_saml_assertion(self,
                           prompt: bool = False,
                           mfa: Optional[str] = None) -> str:
        """
        Lookup OKTA session from cache, if it's valid, use it, otherwise, generate new assertion with MFA
        Args:
            prompt: Used for forcing prompts of username / password and always generating a new assertion
            mfa: MFA to use for generating the new OKTA session with.
            force_new: Forces a new session, abandons one from cache
        """
        log.info(f'Getting SAML assertion. Provided MFA override: {mfa}')
        invalid_session = True
        okta = self.get_sso_session(prompt, mfa)
        failure_count = 0
        # Todo: is this an infinite loop after a request from UI with a bad MFA?
        while invalid_session:
            try:
                assertion = okta.get_assertion()
            except InvalidSessionError as e:
                if failure_count > 0:
                    print(e)
                    print(
                        "Authentication failed with SSO provider, please reauthenticate"
                        " Likely invalid MFA or Password?\r\n")
                    failure_count += 1

                log.debug(f" invalid session: {e}")
                user = self._get_user(prompt)
                password = self._get_password(user, prompt=prompt, save=True)

                if self._defaults.mfa_enabled:
                    color = Utils.default_colors(
                    ) if self._defaults.colors_enabled else None
                    mfa = self._secrets_mgr.get_next_mfa(user) if self._defaults.auto_mfa else \
                        Input.get_mfa(display_hint=True, color=color)
                else:
                    mfa = None

                primary_auth = OktaPrimaryAuth(self._defaults, password, mfa)

                try:
                    print("Trying to write session to cache...")
                    self._write_okta_session_to_cache(
                        primary_auth.get_session())
                except InvalidSessionError as e:
                    print(f"Got invalid session: {e}")
                    return self.get_saml_assertion(prompt=True)
                else:
                    return self.get_saml_assertion(prompt=True)
            else:
                assertion = base64.b64decode(assertion).decode('utf-8')
                self._saml_cache.write(SAML_ASSERTION_CACHE_KEY, assertion)
                return assertion
コード例 #5
0
ファイル: provider_config.py プロジェクト: figtools/figgy-cli
    def configure(mfa_enabled: bool = False) -> "GoogleProviderConfig":
        config, c = ConfigManager(CONFIG_OVERRIDE_FILE_PATH), Utils.default_colors()

        idp_id = config.get_property(Config.Section.Google.IDP_ID)
        sp_id = config.get_property(Config.Section.Google.SP_ID)

        if idp_id and idp_id != FAKE_GOOGLE_IDP_ID:
            idp_id = config.get_or_prompt(Config.Section.Google.IDP_ID, GoogleProviderConfig.get_idp_id)
        else:
            idp_id = config.get_or_prompt(Config.Section.Google.IDP_ID, GoogleProviderConfig.get_idp_id, force_prompt=True)

        if sp_id and sp_id != FAKE_GOOGLE_SP_ID:
            sp_id = config.get_or_prompt(Config.Section.Google.SP_ID, GoogleProviderConfig.get_sp_id)
        else:
            sp_id = config.get_or_prompt(Config.Section.Google.SP_ID, GoogleProviderConfig.get_sp_id, force_prompt=True)

        return GoogleProviderConfig(idp_id=idp_id, sp_id=sp_id)
コード例 #6
0
ファイル: provider_config.py プロジェクト: figtools/figgy-cli
    def configure(mfa_enabled: bool = False) -> "OktaProviderConfig":
        config, c = ConfigManager(CONFIG_OVERRIDE_FILE_PATH), Utils.default_colors()
        app_link = config.get_property(Config.Section.Google.IDP_ID)
        if app_link and app_link != FAKE_OKTA_APP_LINK:
            app_link = config.get_or_prompt(Config.Section.Okta.APP_LINK, OktaProviderConfig.get_app_link,
                                            desc=OKTA_APP_LINK_DESC)
        else:
            app_link = config.get_or_prompt(Config.Section.Okta.APP_LINK, OktaProviderConfig.get_app_link,
                                            force_prompt=True, desc=OKTA_APP_LINK_DESC)

        if mfa_enabled:
            factor_type = config.get_or_prompt(Config.Section.Okta.FACTOR_TYPE, OktaProviderConfig.get_factor_type,
                                               desc=OKTA_MFA_TYPE_DESC)
        else:
            factor_type = None

        return OktaProviderConfig(app_link=app_link, factor_type=factor_type)
コード例 #7
0
ファイル: google.py プロジェクト: figtools/figgy-cli
    def handle_totp(self, sess):
        response_page = BeautifulSoup(sess.text, 'html.parser')
        tl = response_page.find('input', {'name': 'TL'}).get('value')
        gxf = response_page.find('input', {'name': 'gxf'}).get('value')
        challenge_url = sess.url.split("?")[0]
        challenge_id = challenge_url.split("totp/")[1]

        if self._defaults.mfa_enabled:
            color = Utils.default_colors(
            ) if self._defaults.colors_enabled else None
            mfa_token = self._secrets_mgr.get_next_mfa(self._defaults.user) if self._defaults.auto_mfa else \
                Input.get_mfa(display_hint=True, color=color)
        else:
            mfa_token = None

        if not mfa_token:
            raise ValueError(
                "MFA token required for {} but none supplied.".format(
                    self.config.username))

        payload = {
            'challengeId': challenge_id,
            'challengeType': 6,
            'continue': self.cont,
            'scc': 1,
            'sarp': 1,
            'checkedDomains': 'youtube',
            'pstMsg': 0,
            'TL': tl,
            'gxf': gxf,
            'Pin': mfa_token,
            'TrustDevice': 'on',
        }

        # Submit TOTP
        return self.post(challenge_url, data=payload)
コード例 #8
0
ファイル: provider_config.py プロジェクト: figtools/figgy-cli
 def configure(mfa_enabled: bool = False) -> "BastionProviderConfig":
     config, c = ConfigManager(CONFIG_OVERRIDE_FILE_PATH), Utils.default_colors()
     profile = config.get_or_prompt(Config.Section.Bastion.PROFILE, BastionProviderConfig.get_profile)
     return BastionProviderConfig(profile_name=profile)
コード例 #9
0
ファイル: run_tests.py プロジェクト: figtools/figgy-cli
from figcli.test.cli.dev.export import DevExport
from figcli.test.cli.dev.get import DevGet
from figcli.test.cli.dev.list import DevList
from figcli.test.cli.dev.put import DevPut
from figcli.test.cli.dev.restore import DevRestore
from figcli.test.cli.dev.sync import DevSync
from figcli.test.cli.dev.login import DevLogin
from figcli.test.cli.sso.google.configure import ConfigureGoogle
from figcli.utils.utils import Utils
from figcli.test.cli.sso.okta.configure import ConfigureOkta

CACHE_DIR = f'{HOME}/.figgy/cache'
VAULT_DIR = f'{HOME}/.figgy/lockbox'
CONFIG_FILE = f'{HOME}/.figgy/config'
KEYRING_FILE = f'{HOME}/.local/share/python_keyring/keyring_pass.cfg'
c = Utils.default_colors()
AUTH_TYPES = ['google', 'okta', 'bastion', 'profile']

# FYI I know tests are a bit UGLY, but I'm ok with it, it's just tests! :)


## JORDAN: If you get EOF exceptions like this:
## pexpect.exceptions.EOF: End Of File (EOF). Empty string style platform.
## It means you created a TestObj and are calling `.expect()` on it after the child
## process has already exited. For instance, creating a single DevGet() and calling .get() numerous times.
##
## ALSO: You must always have an `child.expect` looking for the _last_ line of output, otherwise pexpect will kill
## the child process even if the child process is still finishing some stuff in the background.
##
##
## If you forget "encoding='utf-8' on a spawn() call logs will break! Use TestUtils going forward.
コード例 #10
0
ファイル: upgrade_manager.py プロジェクト: figtools/figgy-cli
 def __init__(self, colors_enabled: bool):
     self._utils = Utils(colors_enabled=colors_enabled)
     self.c = Utils.default_colors(enabled=colors_enabled)
     self.current_version: FiggyVersionDetails = VersionTracker.get_version()
コード例 #11
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."
                            )
コード例 #12
0
    def get_session(self,
                    env: GlobalEnvironment,
                    prompt: bool,
                    exit_on_fail=True,
                    mfa: Optional[str] = None) -> boto3.Session:
        forced = False
        log.info(
            f"Getting session for role: {env.role.role_arn} in env: {env.role.run_env.env}"
        )
        attempts = 0
        while True:
            try:
                if prompt and not forced:
                    forced = True
                    raise InvalidSessionError(
                        "Forcing new session due to prompt.")

                creds: FiggyAWSSession = self._sts_cache.get_val(
                    env.role.cache_key())

                if creds:
                    session = boto3.Session(
                        aws_access_key_id=creds.access_key,
                        aws_secret_access_key=creds.secret_key,
                        aws_session_token=creds.token,
                        region_name=env.region)

                    if creds.expires_soon(
                    ) or not self._is_valid_session(session):
                        self._utils.validate(
                            attempts < self._MAX_ATTEMPTS,
                            f"Failed to authenticate with AWS after {attempts} attempts. Exiting. "
                        )

                        attempts = attempts + 1
                        log.info(
                            "Invalid session detected in cache. Raising session error."
                        )
                        raise InvalidSessionError("Invalid Session Detected")

                    log.info("Valid bastion SSO session returned from cache.")
                    return session
                else:
                    raise InvalidSessionError(
                        "Forcing new session, cache is empty.")
            except (FileNotFoundError, NoCredentialsError,
                    InvalidSessionError) as e:
                try:
                    if self._defaults.mfa_enabled:
                        self._defaults.mfa_serial = self.get_mfa_serial()
                        color = Utils.default_colors(
                        ) if self._defaults.colors_enabled else None

                        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:
                                mfa = self._secrets_mgr.get_next_mfa(self._defaults.user) if self._defaults.auto_mfa else \
                                                                    Input.get_mfa(display_hint=True, color=color)

                        response = self.__get_sts().assume_role(
                            RoleArn=env.role.role_arn,
                            RoleSessionName=Utils.sanitize_session_name(
                                self._defaults.user),
                            DurationSeconds=self._defaults.session_duration,
                            SerialNumber=self._defaults.mfa_serial,
                            TokenCode=mfa)
                    else:
                        response = self.__get_sts().assume_role(
                            RoleArn=env.role.role_arn,
                            RoleSessionName=Utils.sanitize_session_name(
                                self._defaults.user),
                            DurationSeconds=self._defaults.session_duration)

                    session = FiggyAWSSession(
                        **response.get('Credentials', {}))
                    log.info(f"Got session response: {response}")
                    self._sts_cache.write(env.role.cache_key(), session)
                except (ClientError, ParamValidationError) as e:
                    if isinstance(
                            e, ParamValidationError
                    ) or "AccessDenied" == e.response['Error']['Code']:
                        if exit_on_fail:
                            self._utils.error_exit(
                                f"Error authenticating with AWS from Bastion Profile:"
                                f" {self._defaults.provider_config.profile_name}: {e}"
                            )
                    else:
                        if exit_on_fail:
                            log.error(
                                f"Failed to authenticate due to error: {e}")
                            self._utils.error_exit(
                                f"Error getting session for role: {env.role.role_arn} "
                                f"-- Are you sure you have permissions?")

                    raise e