Esempio n. 1
0
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)
Esempio n. 3
0
    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
Esempio n. 4
0
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()]),
        ))
Esempio n. 6
0
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"
                )
Esempio n. 7
0
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.")
Esempio n. 8
0
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")
Esempio n. 9
0
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))
Esempio n. 10
0
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
Esempio n. 11
0
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)