Exemplo n.º 1
0
    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
Exemplo n.º 2
0
    def configure(self) -> CLIDefaults:
        """
        Orchestrates the --configure option. Writes selections to a defaults file in user's home dir.

        This default files stores the following information:

        user: User Name for SSO integrations
        role: The user's preferred default role if --role is not specified
        env: The user's preferred eefault environment if --env is not specified
        valid_roles: A list of roles the user has access to based on the returned SAML assertion from the SSO provider
        valid_envs: A list of environments the user has access to based on the returned SAML assertion
        assumable_roles: Maintains a mapping of accountId -> environment name -> role name so the we can authenticate
                         the user with the appropriate AWS accounts based on their returned SAML assertion.
        """
        Utils.wipe_vaults() or Utils.wipe_defaults(
        ) or Utils.wipe_config_cache()
        defaults: CLIDefaults = self._setup.get_defaults()
        defaults = self._setup.configure_auth(defaults)
        defaults = self._setup.configure_extras(defaults)
        self._setup.save_defaults(defaults)
        self.c = TerminalFactory(Utils.is_mac()).instance().get_colors()

        defaults = self._setup.configure_roles(current_defaults=defaults)
        defaults = self._setup.configure_preferences(defaults)
        defaults = self._setup.configure_figgy_defaults(defaults)
        self._setup.save_defaults(defaults)
        print(f"\n{self.c.fg_gr}Setup successful! Enjoy figgy!{self.c.rs}")
        return defaults
Exemplo n.º 3
0
    def _put(self):
        value = Input.input(f"Please input a value to share: ")

        # Safe convert to int or float, then validate
        expires_in_hours = Input.input(
            f"Select # of hours before value auto-expires: ", default="1")
        expires_in_hours = Utils.safe_cast(expires_in_hours, int,
                                           expires_in_hours)
        expires_in_hours = Utils.safe_cast(expires_in_hours, float,
                                           expires_in_hours)
        self._utils.validate(
            isinstance(expires_in_hours, int)
            or isinstance(expires_in_hours, float),
            "You must provide a number of hours for when this secret should expire. No strings accepted."
        )
        self._utils.validate(
            expires_in_hours <= 48,
            "You may not specify an expiration time more than 48 hours in the future."
        )

        secret_id = self._ots.put_ots(value, expires_in_hours)
        self._out.print(
            f"\n\nTo share this secret, recipients will need the following")
        self._out.print(f"\n[[Secret Id]] -> {secret_id}")
        self._out.success(
            f"\n\nValue successfully stored, it will expire in {expires_in_hours} hours, or when retrieved."
        )
Exemplo n.º 4
0
 def __init__(self, command: CliCommand, context: HelpContext, figgy_context: FiggyContext):
     self._command = command
     self._context = context
     self._figgy_context = figgy_context
     self._options = context.options
     self._utils = Utils(False)
     self._setup: FiggySetup = FiggySetup(self._figgy_context)
Exemplo n.º 5
0
 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?")
Exemplo n.º 6
0
    def report_error(self, command: str, e: Exception) -> None:
        """
        If the user chooses to report this exception, ship it off over to the Figgy API to let us know what went wrong!
        :param command: user's command input
        :param e: exception that was thrown
        """

        printable_exception = self.sanitize(e)
        os = Utils.get_os()

        payload = {
            'command': command,
            'os': os,
            'stacktrace': printable_exception,
            'version': VERSION
        }

        result = requests.post(FIGGY_ERROR_REPORTING_URL, json=payload)

        Utils.stc_validate(result.status_code == 200, "Unable to report this error to Figgy. Please consider "
                                                      f"opening a ticket on the figgy github repo: {FIGGY_GITHUB}")

        print(f"We are so sorry you experienced this error! This error has been anonymously reported to the Figgy "
              f"development team. \n\nIf you don't want to be prompted to report errors, you can disable the error "
              f"reporting by running `{CLI_NAME} --configure`.")
        print(f"\n\n{self.c.fg_bl}--------------------------------------------------------{self.c.rs}\n\n")
Exemplo n.º 7
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?")
Exemplo n.º 8
0
 def get_factor_type() -> str:
     factor_type = prompt(f"\nPlease select your OKTA MFA Factor type. Supported Types are "
                          f"{SUPPORTED_OKTA_FACTOR_TYPES}: ",
                          completer=WordCompleter(SUPPORTED_OKTA_FACTOR_TYPES))
     Utils.stc_validate(factor_type in SUPPORTED_OKTA_FACTOR_TYPES,
                        f"You must select a factor type from: {SUPPORTED_OKTA_FACTOR_TYPES}")
     return factor_type
Exemplo n.º 9
0
 def __init__(self, source_ssm: SsmDao, config_completer_init: WordCompleter,
              colors_enabled: bool, config_context: ConfigContext, session_mgr: SessionManager):
     super().__init__(promote, colors_enabled, config_context)
     self.config_context = config_context
     self._source_ssm = source_ssm
     self._session_mgr = session_mgr
     self._config_completer = config_completer_init
     self._utils = Utils(colors_enabled)
     self._out = Output(colors_enabled)
Exemplo n.º 10
0
    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
Exemplo n.º 11
0
 def __init__(self, ssm_init: SsmDao, colors_enabled: bool,
              config_context: ConfigContext,
              config_view: RBACLimitedConfigView,
              config_completer: WordCompleter):
     super().__init__(edit, colors_enabled, config_context)
     self._ssm = ssm_init
     self._config_view = config_view
     self._utils = Utils(colors_enabled)
     self._config_completer = config_completer
Exemplo n.º 12
0
 def __init__(self, command: CliCommand, context: MaintenanceContext,
              figgy_context: FiggyContext, config: Optional[ConfigService]):
     self._command = command
     self._context = context
     self._figgy_context = figgy_context
     self._options = context.options
     self._utils = Utils(False)
     self._setup: FiggySetup = FiggySetup(self._figgy_context)
     self._config: Optional[ConfigService] = config
Exemplo n.º 13
0
    def select_run_env(valid_envs: List[str]) -> RunEnv:
        input_env = prompt(f'Select a RunEnvironment: {valid_envs}: ',
                           completer=WordCompleter(valid_envs))
        Utils.stc_validate(
            input_env in valid_envs,
            f"{input_env} is not a valid Run Environment. Please select from: {valid_envs}"
        )

        return RunEnv(env=input_env)
Exemplo n.º 14
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!")
Exemplo n.º 15
0
 def __init__(self, command: CliCommand,
              context: CommandContext,
              session_manager: SessionManager,
              figgy_context: FiggyContext):
     self._command = command
     self._context = context
     self._utils = Utils(False)
     self._setup: FiggySetup = FiggySetup(figgy_context)
     self._session_manager = session_manager
Exemplo n.º 16
0
    def select_aws_cli_profile() -> str:
        default_value = 'bastion'
        profile = input(
            f'Please input the aws_cli profile name of your first.last_programmatic user in the '
            f'MGMT account (Default: {default_value}): ') or default_value
        Utils.stc_validate(profile != '',
                           "You must input a valid aws_cli profile")

        return profile
Exemplo n.º 17
0
    def __init__(self, ssm_init, repl_init: ReplicationDao,
                 config_completer_init, colors_enabled: bool,
                 config_context: ConfigContext):
        super().__init__(share, colors_enabled, config_context)

        self._ssm = ssm_init
        self._repl = repl_init
        self._config_completer = config_completer_init
        self._utils = Utils(colors_enabled)
        self._out = Output(colors_enabled)
Exemplo n.º 18
0
    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
Exemplo n.º 19
0
 def __init__(self, maintenance_context: MaintenanceContext,
              config_service: Optional[ConfigService]):
     super().__init__(version, maintenance_context.defaults.colors_enabled,
                      maintenance_context)
     self.tracker = VersionTracker(self.context.defaults, config_service)
     self.upgrade_mgr = UpgradeManager(
         maintenance_context.defaults.colors_enabled)
     self._utils = Utils(
         colors_enabled=maintenance_context.defaults.colors_enabled)
     self._out = Output(
         colors_enabled=maintenance_context.defaults.colors_enabled)
Exemplo n.º 20
0
    def is_setup_command(args):
        """
        Returns True for 'special' commands that configure figgy itself or follow non-normal execution paths.
        Needed to skip past steps that are not necessary because figgy isn't set up yet, or to support a special
        use case (like sandbox logins).
        """

        return Utils.is_set_true(configure, args) \
               or Utils.command_set(sandbox, args) \
               or Utils.is_set_true(version, args) \
               or Utils.attr_exists(profile, args) \
               or Utils.is_set_true(upgrade, args)
Exemplo n.º 21
0
 def __init__(self, defaults: CLIDefaults, context: FiggyContext):
     super().__init__(defaults, context)
     self._utils = Utils(defaults.colors_enabled)
     self._sts = boto3.client('sts')
     self._context = context
     keychain_enabled = defaults.extras.get(DISABLE_KEYRING) is not True
     vault = FiggyVault(keychain_enabled=keychain_enabled,
                        secrets_mgr=self._secrets_mgr)
     self._sts_cache: CacheManager = CacheManager(
         file_override=STS_SESSION_CACHE_PATH, vault=vault)
     self._saml_cache: CacheManager = CacheManager(
         file_override=SAML_SESSION_CACHE_PATH, vault=vault)
Exemplo n.º 22
0
 def __init__(self, ssm_init: SsmDao, colors_enabled: bool,
              context: ConfigContext):
     super().__init__(validate, colors_enabled, context)
     self._ssm = ssm_init
     self._config_path = context.ci_config_path if context.ci_config_path else Utils.find_figgy_json(
     )
     self._utils = Utils(colors_enabled)
     self._replication_only = context.replication_only
     self._errors_detected = False
     self.example = f"{self.c.fg_bl}{CLI_NAME} config {self.command_printable} " \
                    f"--env dev --config /path/to/config{self.c.rs}"
     self._FILE_PREFIX = "file://"
     self._out = Output(colors_enabled)
Exemplo n.º 23
0
    def __init__(self, help_context: HelpContext, figgy_setup: FiggySetup,
                 figgy_context: FiggyContext):
        super().__init__(login, Utils.not_windows(), help_context)
        self._setup = figgy_setup
        self._defaults: CLIDefaults = figgy_setup.get_defaults()
        self._figgy_context = figgy_context
        self._utils = Utils(self._defaults.colors_enabled)
        self._aws_cfg = AWSConfig(color=self.c)
        self._out = Output(self._defaults.colors_enabled)

        self.example = f"\n\n{self.c.fg_bl}{CLI_NAME} {login.name} \n" \
                       f"{self.c.rs}{self.c.fg_yl}  --or--{self.c.rs}\n" \
                       f"{self.c.fg_bl}{CLI_NAME} {login.name} {sandbox.name}{self.c.rs}"
Exemplo n.º 24
0
    def __init__(self,
                 ssm: SsmDao,
                 ddb: ConfigDao,
                 repl_dao: ReplicationDao,
                 context: ConfigContext,
                 config_completer_init: WordCompleter,
                 colors_enabled: bool,
                 delete: Delete,
                 args=None):
        super().__init__(prune, colors_enabled, context)
        self._ssm = ssm  # type: SsmDao
        self._config_dao = ddb  # type: ConfigDao
        self._repl = repl_dao
        self._config_completer = config_completer_init  # type: WordCompleter
        self._utils = Utils(colors_enabled)
        self.example = f"{self.c.fg_bl}{CLI_NAME} config {self.command_printable} --env dev " \
            f"--config /path/to/figgy.json{self.c.rs}"
        self._config_path = context.ci_config_path if context.ci_config_path else Utils.find_figgy_json(
        )
        self._out = Output(colors_enabled)

        # If user passes in --info flag, we don't need all of this to be initialized.
        if not hasattr(args, info.name) or args.info is False:
            # Validate & parse figgy.json
            self._config = self._utils.get_ci_config(
                self._config_path)  # type: Dict
            self._shared_names = set(
                self._utils.get_config_key_safe(SHARED_KEY,
                                                self._config,
                                                default=[]))  # type: Set
            self._repl_conf = self._utils.get_config_key_safe(
                REPLICATION_KEY, self._config, default={})  # type: Dict
            self._merge_conf = self._utils.get_config_key_safe(
                MERGE_KEY, self._config, default={})  # type: Dict
            self._config_keys = set(
                self._utils.get_config_key_safe(CONFIG_KEY,
                                                self._config,
                                                default=[]))  # type: Set
            self._merge_keys = set(self._merge_conf.keys())  # type: Set
            self._namespace = self._utils.get_namespace(
                self._config)  # type: str
            self._delete_command = delete
            self._repl_from_conf = self._utils.get_config_key_safe(
                REPL_FROM_KEY, self._config, default={})
            self._repl_conf = KeyUtils.merge_repl_and_repl_from_blocks(
                self._repl_conf, self._repl_from_conf, self._namespace)

            # Build list of all keys found across all config types
            self._all_keys = KeyUtils().find_all_expected_names(
                self._config_keys, self._shared_names, self._merge_conf,
                self._repl_conf, self._repl_from_conf, self._namespace)
Exemplo n.º 25
0
    def __init__(self, ssm_init: SsmDao, colors_enabled: bool,
                 config_context: ConfigContext,
                 config_view: RBACLimitedConfigView, get: Get):
        super().__init__(put, colors_enabled, config_context)
        self._ssm = ssm_init
        self._utils = Utils(colors_enabled)
        self._config_view = config_view
        self._get = get
        self._source_key = Utils.attr_if_exists(copy_from, config_context.args)
        self._out = Output(colors_enabled)

        self._select_name = [('class:', 'Please input a PS Name: ')]

        self._FILE_PREFIX = "file://"
Exemplo n.º 26
0
    def get_mfa(display_hint: bool = False,
                color: Optional[Color] = None) -> str:
        if display_hint and random.randint(0, 10) == 10:
            blue = color.fg_bl if color else ''
            rs = color.rs if color else ''
            print(
                f"{blue}Hint:{rs} Tired of typing in your MFA? Consider saving your MFA secret to your keychain and "
                f"let {CLI_NAME} securely auto-generate tokens for you. \n"
                f"{blue}More info:{rs} http://figgy.dev/docs/getting-started/install.html\n\n"
            )

        mfa = input('Please input the MFA associated with your user: '******'', "You must input a valid mfa")

        return mfa
Exemplo n.º 27
0
 def __init__(self, ssm_init: SsmDao, cfg_svc: ConfigService,
              colors_enabled: bool, context: ConfigContext,
              get_command: Get, delete_command: Delete,
              config_view: RBACLimitedConfigView):
     super().__init__(browse, colors_enabled, context)
     self._ssm = ssm_init
     self._get = get_command
     self._cfg_svc = cfg_svc
     self._config_view = config_view
     self.selected_ps_paths = []
     self.deleted_ps_paths = []
     self.dirs = set()
     self._utils = Utils(colors_enabled)
     self._delete = delete_command
     self.prefix = context.prefix
Exemplo n.º 28
0
    def get_or_refresh(self, cache_key: str, refresher: Callable, *args, max_age: int = DEFAULT_REFRESH_INTERVAL) \
            -> Tuple[int, Any]:
        assert isinstance(
            max_age, int
        ), "Invalid max_age provided for session, it must be of type <int>"

        last_write, val = self.get(cache_key)
        if Utils.millis_since_epoch() - last_write > max_age or not val:
            new_val = refresher(*args)
            self.write(cache_key, new_val)
            log.info(f"{cache_key} not found in cache. It was fetched.")
            return Utils.millis_since_epoch(), new_val
        else:
            log.info(f"Value for key: {cache_key} was found in cache.")
            return last_write, val
Exemplo n.º 29
0
    def __init__(self, keychain_enabled=True, secrets_mgr: SecretsManager = SecretsManager()):
        """
        keychain_enabled: Stores the encryption key in the user's keychain. This will be disabled for Sandbox
        sessions to simplify the user experience.
        """
        self._secrets_mgr = secrets_mgr
        encryption_key = DEFAULT_ENCRYPTION_KEY
        if keychain_enabled:
            encryption_key = secrets_mgr.get_encryption_key()
            if not encryption_key:
                Utils.wipe_vaults()
                encryption_key: str = Fernet.generate_key().decode()
                secrets_mgr.set_encryption_key(encryption_key)

        self.fernet = Fernet(encryption_key)
Exemplo n.º 30
0
    def install_onedir(self, install_path: str, latest_version: str, platform: str):
        old_path = f'{install_path}.OLD'
        zip_path = f"{HOME}/.figgy/figgy.zip"
        install_dir = f'{HOME}/.figgy/installations/{latest_version}/{str(uuid.uuid4())[:4]}'
        url = f'http://www.figgy.dev/releases/cli/{latest_version}/{platform.lower()}/figgy.zip'
        os.makedirs(os.path.dirname(install_dir), exist_ok=True)
        suffix = ".exe" if Utils.is_windows() else ""
        self._cleanup_file(zip_path)

        self.download_with_progress(url, zip_path)

        with ZipFile(zip_path, 'r') as zipObj:
            zipObj.extractall(install_dir)

        if self._utils.file_exists(old_path):
            os.remove(old_path)

        executable_path = f'{install_dir}/figgy/{CLI_NAME}{suffix}'
        st = os.stat(executable_path)
        os.chmod(executable_path, st.st_mode | stat.S_IEXEC)

        if self._utils.file_exists(install_path):
            os.rename(install_path, old_path)

        os.symlink(executable_path, install_path)