def aws_kms_master_key_provider(**kwargs): # type: (**List[Union[Text, str]]) -> KMSMasterKeyProvider """Apply post-processing to transform ``KMSMasterKeyProvider``-specific values from CLI arguments to valid ``KMSMasterKeyProvider`` parameters, then call ``KMSMasterKeyprovider`` with those parameters. :param dict kwargs: Named parameters collected from CLI arguments as prepared in aws_encryption_sdk_cli.internal.master_key_parsing._parse_master_key_providers_from_args :rtype: aws_encryption_sdk.key_providers.kms.KMSMasterKeyProvider """ kwargs = copy.deepcopy(kwargs) try: profile_names = kwargs.pop("profile") if len(profile_names) != 1: raise BadUserArgumentError( "Only one profile may be specified per master key provider configuration. {} provided." .format(len(profile_names))) profile_name = profile_names[0] except KeyError: profile_name = None botocore_session = botocore.session.Session(profile=profile_name) botocore_session.user_agent_extra = USER_AGENT_SUFFIX kwargs["botocore_session"] = botocore_session try: region_name = kwargs.pop("region") if len(region_name) != 1: raise BadUserArgumentError( "Only one region may be specified per master key provider configuration. {} provided." .format(len(region_name))) kwargs["region_names"] = region_name except KeyError: pass return KMSMasterKeyProvider(**kwargs)
def aws_kms_master_key_provider( discovery=True, # type: bool **kwargs # type: List[Union[Text, str]] ): # type: (...) -> Union[MRKAwareDiscoveryAwsKmsMasterKeyProvider, MRKAwareStrictAwsKmsMasterKeyProvider] """Apply post-processing to transform ``KMSMasterKeyProvider``-specific values from CLI arguments to valid ``KMSMasterKeyProvider`` parameters, then call ``KMSMasterKeyProvider`` with those parameters. :param bool discovery: Return a MRKAwareDiscoveryAwsKmsMasterKeyProvider :param dict kwargs: Named parameters collected from CLI arguments as prepared in aws_encryption_sdk_cli.internal.master_key_parsing._parse_master_key_providers_from_args :rtype: aws_encryption_sdk.key_providers.kms.BaseKMSMasterKeyProvider """ kwargs = copy.deepcopy(kwargs) try: profile_names = kwargs.pop("profile") if len(profile_names) != 1: raise BadUserArgumentError( "Only one profile may be specified per master key provider configuration. {} provided." .format(len(profile_names))) profile_name = profile_names[0] # type: Optional[Text] except KeyError: profile_name = None botocore_session = botocore.session.Session(profile=profile_name) botocore_session.user_agent_extra = USER_AGENT_SUFFIX kwargs["botocore_session"] = botocore_session try: region_name = kwargs.pop("region") if len(region_name) != 1: raise BadUserArgumentError( "Only one region may be specified per master key provider configuration. {} provided." .format(len(region_name))) kwargs["region_names"] = region_name except KeyError: pass if discovery: accounts = kwargs.pop("discovery-account", None) partition = kwargs.pop("discovery-partition", None) if accounts and partition: discovery_filter = DiscoveryFilter(account_ids=accounts, partition=partition) kwargs["discovery_filter"] = discovery_filter # type: ignore return MRKAwareDiscoveryAwsKmsMasterKeyProvider(**kwargs) return MRKAwareStrictAwsKmsMasterKeyProvider(**kwargs)
def __call__(self, output_file=None): # type: (Optional[str]) -> MetadataWriter """Set the output file target and validate init and call arguments. .. note:: Separated from ``__init__`` to make use as an argparse type simpler. :param str output_file: Path to file to write to, or "-" for stdout (optional) """ self.output_file = output_file if self.suppress_output: return self if self.output_file is None: raise TypeError( "output_file cannot be None when suppress_output is False") if self.output_file == "-": self._output_mode = "w" return self if not os.path.isdir( os.path.dirname(os.path.realpath(self.output_file))): raise BadUserArgumentError( "Parent directory for requested metdata file does not exist.") self._output_mode = "ab" self.output_file = os.path.abspath(self.output_file) attr.validate(self) return self
def _catch_bad_stdin_stdout_requests(source, destination): # type: (str, str) -> None """Catches bad requests based on characteristics of source and destination when source might be stdin or stdout. :param str source: Identifier for the source (filesystem path or ``-`` for stdin) :param str destination: Identifier for the destination (filesystem path or ``-`` for stdout) :raises BadUserArgument: if source and destination are the same :raises BadUserArgument: if source is stdin and destination is a directory """ acting_as_pipe = destination == "-" and source == "-" if not acting_as_pipe and os.path.realpath(source) == os.path.realpath(destination): raise BadUserArgumentError("Destination and source cannot be the same") if source == "-" and os.path.isdir(destination): raise BadUserArgumentError("Destination may not be a directory when source is stdin")
def _load_master_key_provider(name): # type: (str) -> Callable """Find the correct master key provider entry point for the specified name. :param str name: Name for which to look up entry point :returns: Loaded entry point :rtype: callable :raises BadUserArgumentError: if entry point cannot be found """ if PLUGIN_NAMESPACE_DIVIDER in name: package_name, entry_point_name = name.split(PLUGIN_NAMESPACE_DIVIDER, 1) else: package_name = "" entry_point_name = name entry_points = _entry_points()[entry_point_name] if not entry_points: raise BadUserArgumentError( 'Requested master key provider not found: "{}"'.format( entry_point_name)) if not package_name: if len(entry_points) == 1: return list(entry_points.values())[0].load() raise BadUserArgumentError( "Multiple entry points discovered and no package specified. Packages discovered registered by: ({})" .format(", ".join( [str(entry.dist) for entry in entry_points.values()]))) try: return entry_points[package_name].load() except KeyError: raise BadUserArgumentError(( 'Requested master key provider not found: "{requested}".' ' Packages discovered for "{entry_point}" registered by: ({discovered})' ).format( requested=name, entry_point=entry_point_name, discovered=", ".join( [str(entry.dist) for entry in entry_points.values()]), ))
def _catch_bad_file_and_directory_requests(expanded_sources, destination): # type: (List[str], str) -> None """Catches bad requests based on characteristics of source and destination when source contains files or directories. :param list expanded_sources: List of source paths :param str destination: Identifier for the destination (filesystem path or ``-`` for stdout) :raises BadUserArgumentError: if source contains multiple files and destination is not an existing directory :raises BadUserArgumentError: if source contains a directory and destination is not an existing directory """ if len(expanded_sources) > 1 and not os.path.isdir(destination): raise BadUserArgumentError("If operating on multiple sources, destination must be an existing directory") for _source in expanded_sources: if os.path.isdir(_source): if not os.path.isdir(destination): raise BadUserArgumentError( "If operating on a source directory, destination must be an existing directory" )
def _catch_bad_destination_requests(destination): # type: (str) -> None """Catches bad requests based on characteristics of destination. :param str destination: Identifier for the destination (filesystem path or ``-`` for stdout) :raises BadUserArgument: if destination is a file in a directory that does not already exist """ if destination != "-" and not os.path.isdir(destination): if not os.path.isdir(os.path.realpath(os.path.dirname(destination))): raise BadUserArgumentError("If destination is a file, the immediate parent directory must already exist.")
def _catch_bad_metadata_file_requests(metadata_output, source, destination): # type: (MetadataWriter, str, str) -> None """Catches bad requests based on characteristics of source, destination, and metadata output target. :raises BadUserArgumentError: if output file and metadata file are both ``stdout`` :raises BadUserArgumentError: if metadata file would overwrite input file :raises BadUserArgumentError: if metadata file would overwrite output file :raises BadUserArgumentError: if metadata file is a directory :raises BadUserArgumentError: if input is a directory and contains metadata file :raises BadUserArgumentError: if output is a directory and contains metadata file """ if metadata_output.suppress_output: return if metadata_output.output_file == "-": if destination == "-": raise BadUserArgumentError( "Metadata output cannot be stdout when output is stdout") return real_source = os.path.realpath(source) real_destination = os.path.realpath(destination) real_metadata = os.path.realpath(metadata_output.output_file) if os.path.isdir(real_metadata): raise BadUserArgumentError("Metadata output cannot be a directory") if real_metadata in (real_source, real_destination): raise BadUserArgumentError( "Metadata output file cannot be the input or output") if os.path.isdir(real_destination) and real_metadata.startswith( real_destination): raise BadUserArgumentError( "Metadata output file cannot be in the output directory") if os.path.isdir(real_source) and real_metadata.startswith(real_source): raise BadUserArgumentError( "Metadata output file cannot be in the input directory")
def _is_decrypt_mode(mode): # type: (str) -> bool """ Determines whether the provided mode does decryption :param str filepath: Full file path to file in question :rtype: bool """ if mode in ("decrypt", "decrypt-unsigned"): return True if mode == "encrypt": return False raise BadUserArgumentError( "Mode {mode} has not been implemented".format(mode=mode))
def _expand_sources(source): # type: (str) -> List[str] """Expands source using pathname patterns. https://docs.python.org/3/library/glob.html :param str source: Source pattern :returns: List of source paths :rtype: list """ all_sources = glob.glob(source) if not all_sources: raise BadUserArgumentError("Invalid source. Must be a valid pathname pattern or stdin (-)") _LOGGER.debug("Requested source: %s", source) _LOGGER.debug("Expanded source: %s", all_sources) return all_sources
def process_cli_request(stream_args, parsed_args): # noqa: C901 # type: (STREAM_KWARGS, Namespace) -> None """Maps the operation request to the appropriate function based on the type of input and output provided. :param dict stream_args: kwargs to pass to `aws_encryption_sdk.stream` :param args: Parsed arguments from argparse :type args: argparse.Namespace """ _catch_bad_destination_requests(parsed_args.output) _catch_bad_metadata_file_requests( metadata_output=parsed_args.metadata_output, source=parsed_args.input, destination=parsed_args.output) _catch_bad_stdin_stdout_requests(parsed_args.input, parsed_args.output) if not parsed_args.commitment_policy: commitment_policy = CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT elif parsed_args.commitment_policy == CommitmentPolicyArgs.forbid_encrypt_allow_decrypt: commitment_policy = CommitmentPolicy.FORBID_ENCRYPT_ALLOW_DECRYPT elif parsed_args.commitment_policy == CommitmentPolicyArgs.require_encrypt_allow_decrypt: commitment_policy = CommitmentPolicy.REQUIRE_ENCRYPT_ALLOW_DECRYPT elif parsed_args.commitment_policy == CommitmentPolicyArgs.require_encrypt_require_decrypt: commitment_policy = CommitmentPolicy.REQUIRE_ENCRYPT_REQUIRE_DECRYPT else: raise BadUserArgumentError("Invalid commitment policy.") handler = IOHandler( metadata_writer=parsed_args.metadata_output, interactive=parsed_args.interactive, no_overwrite=parsed_args.no_overwrite, decode_input=parsed_args.decode, encode_output=parsed_args.encode, required_encryption_context=parsed_args.encryption_context, required_encryption_context_keys=parsed_args. required_encryption_context_keys, commitment_policy=commitment_policy, ) if parsed_args.input == "-": # read from stdin handler.process_single_operation(stream_args=stream_args, source=parsed_args.input, destination=parsed_args.output) return expanded_sources = _expand_sources(parsed_args.input) _catch_bad_file_and_directory_requests(expanded_sources, parsed_args.output) for _source in expanded_sources: _destination = copy.copy(parsed_args.output) if os.path.isdir(_source): if not parsed_args.recursive: _LOGGER.warning( "Skipping %s because it is a directory and -r/-R/--recursive is not set", _source) continue handler.process_dir(stream_args=stream_args, source=_source, destination=_destination, suffix=parsed_args.suffix) elif os.path.isfile(_source): if os.path.isdir(parsed_args.output): # create new filename _destination = output_filename( source_filename=_source, destination_dir=_destination, mode=str(stream_args["mode"]), suffix=parsed_args.suffix, ) # write to file handler.process_single_file(stream_args=stream_args, source=_source, destination=_destination)