Exemple #1
0
    def get_signed_url(
        self,
        protocol,
        action,
        expires_in,
        force_signed_url=True,
        r_pays_project=None,
        file_name=None,
    ):
        if self.index_document.get("authz"):
            action_to_permission = {
                "upload": "write-storage",
                "download": "read-storage",
            }
            if not self.check_authz(action_to_permission[action]):
                raise Unauthorized(
                    f"Either you weren't logged in or you don't have "
                    f"{action_to_permission[action]} permission "
                    f"on {self.index_document['authz']} for fence")
        else:
            if self.public_acl and action == "upload":
                raise Unauthorized(
                    "Cannot upload on public files while using acl field")
            # don't check the authorization if the file is public
            # (downloading public files with no auth is fine)
            if not self.public_acl and not self.check_authorization(action):
                raise Unauthorized(
                    f"You don't have access permission on this file: {self.file_id}"
                )

        if action is not None and action not in SUPPORTED_ACTIONS:
            raise NotSupported("action {} is not supported".format(action))
        return self._get_signed_url(protocol, action, expires_in,
                                    force_signed_url, r_pays_project,
                                    file_name)
Exemple #2
0
    def get_signed_url(self, protocol, action, expires_in):
        if not self.public and not self.check_authorization(action):
            raise Unauthorized("You don't have access permission on this file")

        if action is not None and action not in SUPPORTED_ACTIONS:
            raise NotSupported("action {} is not supported".format(action))

        return self._get_signed_url(protocol, action, expires_in)
Exemple #3
0
 def from_url(url):
     protocol = urlparse(url).scheme
     if (protocol is not None) and (protocol not in SUPPORTED_PROTOCOLS):
         raise NotSupported(
             "The specified protocol {} is not supported".format(protocol))
     if protocol == "s3":
         return S3IndexedFileLocation(url)
     elif protocol == "gs":
         return GoogleStorageIndexedFileLocation(url)
     return IndexedFileLocation(url)
Exemple #4
0
def get_prefix_for_google_proxy_groups():
    """
    Return a string prefix for Google proxy groups based on configuration.

    Returns:
        str: prefix for proxy groups
    """
    prefix = config.get("GOOGLE_GROUP_PREFIX")
    if not prefix:
        raise NotSupported(
            "GOOGLE_GROUP_PREFIX must be set in the configuration. "
            "This namespaces the Google groups for security and safety.")
    return prefix
Exemple #5
0
def return_link(action, urls):
    protocol = flask.request.args.get('protocol', None)
    expires = min(int(flask.request.args.get('expires_in', 3600)), 3600)
    if (protocol is not None) and (protocol not in SUPPORTED_PROTOCOLS):
        raise NotSupported("The specified protocol is not supported")
    if len(urls) == 0:
        raise NotFound("Can't find any location for the data")
    for url in urls:
        location = urlparse(url)
        if check_protocol(protocol, location.scheme):
            return resolve_url(url, location, expires, action)
    raise NotFound(
        "Can't find a location for the data with given request arguments.")
Exemple #6
0
def get_google_project_valid_users_and_service_accounts(
    project_id, google_cloud_manager, membership=None
):
    """
    Gets google project members of type
    USER or SERVICE_ACCOUNT and raises an error if it finds a member
    that isn't one of those types.

    Args:
        project_id (str): Google project ID
        google_cloud_manager(GoogleCloudManager): cloud manager instance
        membership (List(GooglePolicyMember): pre-calculated list of members,
            Will make call to Google API if membership is None

    Return:
        List[cirrus.google_cloud.iam.GooglePolicyMember]: Members on the
            google project

    Raises:
        NotSupported: Member is invalid type
    """
    try:
        members = membership or google_cloud_manager.get_project_membership(project_id)
        for member in members:
            if not (
                member.member_type == GooglePolicyMember.SERVICE_ACCOUNT
                or member.member_type == GooglePolicyMember.USER
            ):
                raise NotSupported(
                    "Member {} has invalid type: {}".format(
                        member.email_id, member.member_type
                    )
                )
        users = [
            member
            for member in members
            if member.member_type == GooglePolicyMember.USER
        ]
        service_accounts = [
            member
            for member in members
            if member.member_type == GooglePolicyMember.SERVICE_ACCOUNT
        ]
        return users, service_accounts
    except Exception as exc:
        logger.error(
            "validity of Google Project (id: {}) members "
            "could not complete. Details: {}".format(project_id, exc)
        )

        raise
Exemple #7
0
 def get_signed_url(self,
                    protocol,
                    action,
                    expires_in,
                    force_signed_url=True):
     if self.public and action == "upload":
         raise Unauthorized("Cannot upload on public files")
     # don't check the authorization if the file is public
     # (downloading public files with no auth is fine)
     if not self.public and not self.check_authorization(action):
         raise Unauthorized("You don't have access permission on this file")
     if action is not None and action not in SUPPORTED_ACTIONS:
         raise NotSupported("action {} is not supported".format(action))
     return self._get_signed_url(protocol, action, expires_in,
                                 force_signed_url)
Exemple #8
0
def resolve_url(url, location, expires, action):
    protocol = location.scheme
    if protocol == 's3':
        aws_creds = current_app.config['AWS_CREDENTIALS']
        if 'AWS_CREDENTIALS' in current_app.config and len(aws_creds) > 0:
            buckets = flask.current_app.config['S3_BUCKETS']
            if location.netloc not in buckets.keys():
                raise Unauthorized('permission denied for bucket')
            if buckets[location.netloc] not in aws_creds:
                raise Unauthorized('permission denied for bucket')
        credential_key = buckets[location.netloc]
        url = current_app.boto.presigned_url(
            location.netloc,
            location.path.strip('/'),
            expires,
            aws_creds[credential_key],
            ACTION_DICT[protocol][action],
        )
    elif protocol not in ['http', 'https']:
        raise NotSupported('protocol {} in URL {} is not supported'.format(
            protocol, url))
    return flask.jsonify(dict(url=url))
Exemple #9
0
 def wrapper(self, provider, *args, **kwargs):
     if provider not in self.clients:
         raise NotSupported("This backend is not supported by the system!")
     return f(self, provider, *args, **kwargs)
Exemple #10
0
def give_service_account_billing_access_if_necessary(
    sa_private_key, r_pays_project=None, default_billing_project=None
):
    """
    Give the Service Account (whose key is provided) the privilege to bill to the
    given project. If a project is not provided and there is a configured Google project
    to bill to, we will use that.

    Args:
        sa_private_key (dict): JSON key in Google Credentials File format:

            .. code-block:: JavaScript

                {
                    "type": "service_account",
                    "project_id": "project-id",
                    "private_key_id": "some_number",
                    "private_key": "-----BEGIN PRIVATE KEY-----\n....
                    =\n-----END PRIVATE KEY-----\n",
                    "client_email": "<api-name>[email protected]",
                    "client_id": "...",
                    "auth_uri": "https://accounts.google.com/o/oauth2/auth",
                    "token_uri": "https://accounts.google.com/o/oauth2/token",
                    "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
                    "client_x509_cert_url": "https://www.googleapis.com/...<api-name>api%40project-id.iam.gserviceaccount.com"
                }
        r_pays_project (str, optional): The Google Project identifier to bill to
        default_billing_project (str, optional): the default The Google Project
            identifier to bill to if r_pays_project is None
    """
    if not r_pays_project and not default_billing_project:
        sa_account_id = sa_private_key.get("client_email")
        raise UserError(
            "You did NOT provide a `userProject` for requester pays billing, "
            "so we could not create a custom role in that project to provide "
            "the necessary service account ({}) billing permission. "
            "Our main service account ({}) will need valid permissions in the "
            "project you supplied to create a custom role and change the project "
            "IAM policy. There is no configured default billing project so you must "
            "provide a `userProject` query parameter.".format(
                sa_account_id, config["CIRRUS_CFG"].get("GOOGLE_ADMIN_EMAIL")
            )
        )

    # use configured project if it exists and no user project was given
    is_default_billing = False
    if default_billing_project and not r_pays_project:
        r_pays_project = default_billing_project
        is_default_billing = True

    if r_pays_project:
        sa_account_id = sa_private_key.get("client_email")

        try:
            # attempt to create custom role that gives
            # the SA access to bill the project provided
            # NOTE: this may fail if our fence SA doesn't have the right permissions
            #       to add this role and update the project policy
            with GoogleCloudManager(project_id=r_pays_project) as g_cloud_manager:
                g_cloud_manager.give_service_account_billing_access(
                    sa_account_id, project_id=r_pays_project
                )
        except Exception as exc:
            logger.error(
                "Unable to create a custom role in Google Project {} to "
                "give Google service account {} rights to bill the project. Error: {}".format(
                    r_pays_project, sa_account_id, exc
                )
            )
            if is_default_billing:
                raise InternalError(
                    "Fence has a configured Google Project for requester pays billing ({}), "
                    "but could not create a custom role in that project to provide "
                    "the necessary service account ({}) billing permission. It could be that "
                    "the Fence admin service account ({}) does not have valid permissions in the "
                    "project.".format(
                        r_pays_project,
                        sa_account_id,
                        config["CIRRUS_CFG"].get("GOOGLE_ADMIN_EMAIL"),
                    )
                )
            else:
                raise NotSupported(
                    "You provided {} as a `userProject` for requester pays billing, "
                    "but we could not create a custom role in that project to provide "
                    "the necessary service account ({}) billing permission. It could be that "
                    "our main service account ({}) does not have valid permissions in the "
                    "project you supplied to create a custom role and change the project IAM policy.".format(
                        r_pays_project,
                        sa_account_id,
                        config["CIRRUS_CFG"].get("GOOGLE_ADMIN_EMAIL"),
                    )
                )

        logger.info(
            "Created a custom role in Google Project {} to "
            "give Google service account {} rights to bill the project.".format(
                r_pays_project, sa_account_id
            )
        )
def create_keypairs(provider):
    """
    Generate a keypair for user

    :query expires_in: expiration time in seconds, default and max is 30 days

    **Example:**
    .. code-block:: http

           POST /credentials/cdis/?expires_in=3600 HTTP/1.1
           Content-Type: application/json
           Accept: application/json

    cdis:

    .. code-block:: JavaScript

        {
            "key_id": result,
            "api_key": result
        }

    google:
    (JSON key in Google Credentials File format)

    .. code-block:: JavaScript

        {
            "type": "service_account",
            "project_id": "project-id",
            "private_key_id": "some_number",
            "private_key": "-----BEGIN PRIVATE KEY-----\n....
            =\n-----END PRIVATE KEY-----\n",
            "client_email": "<api-name>[email protected]",
            "client_id": "...",
            "auth_uri": "https://accounts.google.com/o/oauth2/auth",
            "token_uri": "https://accounts.google.com/o/oauth2/token",
            "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
            "client_x509_cert_url": "https://www.googleapis.com/...<api-name>api%40project-id.iam.gserviceaccount.com"
        }

    other:

    .. code-block:: JavaScript

        {
            "access_key": "8DGW9LyC0D4nByoWo6pp",
            "secret_key": "1lnkGScEH8Vr4EC6QnoqLK1PqRWPNqIBJkH6Vpgx"
        }
    """
    client_id = getattr(flask.g, 'client_id', None)
    if provider == 'cdis':
        # requestor is user if client_id is not set
        if client_id is None:
            client_id = str(flask.g.user.id)
        # fence identifies access_token endpoint, openid is the default
        # scope for service endpoints
        default_scope = ['fence', 'openid']
        content_type = flask.request.headers.get('Content-Type')
        if content_type == 'application/x-www-form-urlencoded':
            scope = flask.request.form.getlist('scope')
        else:
            try:
                scope = (
                    json.loads(flask.request.data)
                    .get('scope')
                ) or []
            except ValueError:
                scope = []
        if not isinstance(scope, list):
            scope = scope.split(',')
        scope.extend(default_scope)
        for s in scope:
            if s not in USER_ALLOWED_SCOPES:
                raise NotSupported('Scope {} is not supported'.format(s))
        expires_in = min(
            int(flask.request.args.get('expires_in', 2592000)),
            2592000
        )
        api_key, claims = create_api_key(
            flask.g.user, flask.current_app.keypairs[0], expires_in, scope,
            client_id
        )
        return flask.jsonify(dict(key_id=claims['jti'], api_key=api_key))
    elif provider == 'google':
        with GoogleCloudManager() as g_cloud:
            key = _get_google_access_key(g_cloud)
        return flask.jsonify(key)
    else:
        return flask.jsonify(flask.current_app.storage_manager.create_keypair(
            provider, flask.g.user
        ))