예제 #1
0
    def post(self):
        """
        Generate a keypair for user

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

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

        (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"
            }
        """
        user_id = current_token["sub"]
        client_id = current_token.get("azp") or None
        proxy_group_id = get_or_create_proxy_group_id()
        username = current_token.get("context", {}).get("user", {}).get("name")

        r_pays_project = flask.request.args.get("userProject", None)

        key, service_account = create_google_access_key(
            client_id, user_id, username, proxy_group_id
        )

        if config["ENABLE_AUTOMATIC_BILLING_PERMISSION_SA_CREDS"]:
            give_service_account_billing_access_if_necessary(
                key,
                r_pays_project,
                default_billing_project=config["BILLING_PROJECT_FOR_SA_CREDS"],
            )

        if client_id is None:
            self.handle_user_service_account_creds(key, service_account)

        return flask.jsonify(key)
예제 #2
0
파일: google.py 프로젝트: stefan2811/fence
    def get(self):
        """
        List access keys for user

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

               POST /credentials/apis/ HTTP/1.1
               Content-Type: application/json
               Accept: application/json

        Info from Google API /serviceAccounts/<account>/keys endpoint
        TODO: In the future we should probably add in our expiration time, when
              we start monitoring and deleting after x amount of time

        .. code-block:: JavaScript

            {
                "access_keys":
                [
                    {
                        "keyAlgorithm": enum(ServiceAccountKeyAlgorithm),
                        "validBeforeTime": string,
                        "name": string,
                        "validAfterTime": string,
                    },
                    ...
                ]
            }

        """
        client_id = current_token.get("azp") or None
        user_id = current_token["sub"]
        username = current_token.get("context", {}).get("user", {}).get("name")

        with GoogleCloudManager() as g_cloud_manager:
            proxy_group_id = get_or_create_proxy_group_id()
            service_account = get_or_create_service_account(
                client_id=client_id,
                user_id=user_id,
                username=username,
                proxy_group_id=proxy_group_id,
            )

            keys = g_cloud_manager.get_service_account_keys_info(
                service_account.google_unique_id)
            result = {"access_keys": keys}

        return flask.jsonify(result)
예제 #3
0
    def delete(self):
        """
        .. http:get: /google/
        Delete keypair(s) for user
        ?all=true must be specified

        True values are y, yes, t, true, on and 1; false values are n, no, f, false, off and 0

        :statuscode 204 Success
        :statuscode 403 Forbidden to delete access key
        :statuscode 405 Method Not Allowed if ?all=true is not included
        """
        user_id = current_token["sub"]
        username = current_token.get("context", {}).get("user", {}).get("name")

        try:
            all_arg = strtobool(flask.request.args.get("all", "false").lower())
        except ValueError:
            all_arg = False

        if not all_arg:
            flask.abort(
                405,
                "Please include ?all=true to confirm deletion of ALL Google Service account keys.",
            )

        with GoogleCloudManager() as g_cloud:
            client_id = current_token.get("azp") or None
            service_account = get_service_account(client_id, user_id, username=username)

            if service_account:
                keys_for_account = g_cloud.get_service_account_keys_info(
                    service_account.email
                )

                # Only delete the key if is owned by current client's SA
                all_client_keys = [
                    key["name"].split("/")[-1] for key in keys_for_account
                ]

                for key in all_client_keys:
                    _delete_service_account_key(g_cloud, service_account.email, key)
            else:
                flask.abort(404, "Could not find service account for current user.")

        return "", 204
예제 #4
0
    def delete(self, access_key):
        """
        .. http:get: /google/(string: access_key)
        Delete keypair(s) for user

        :param access_key: existing access key that belongs to this user

        :statuscode 204 Success
        :statuscode 403 Forbidden to delete access key
        :statuscode 404 Access key doesn't exist
        """
        user_id = current_token["sub"]
        username = current_token.get("context", {}).get("user", {}).get("name")
        with GoogleCloudManager() as g_cloud:
            client_id = current_token.get("azp") or None
            service_account = get_service_account(client_id, user_id, username=username)

            if service_account:
                keys_for_account = g_cloud.get_service_account_keys_info(
                    service_account.email
                )

                # Only delete the key if is owned by current client's SA
                all_client_keys = [
                    key["name"].split("/")[-1] for key in keys_for_account
                ]

                if access_key in all_client_keys:
                    _delete_service_account_key(
                        g_cloud, service_account.email, access_key
                    )
                else:
                    flask.abort(
                        404,
                        "Could not delete key "
                        + access_key
                        + ". Not found for current user.",
                    )
            else:
                flask.abort(404, "Could not find service account for current user.")

        return "", 204
예제 #5
0
    def post(self):
        """
        Generate a key for user

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

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

        .. code-block:: JavaScript

            {
                "key_id": result,
                "api_key": result
            }
        """
        client_id = current_token.get("azp") or None
        user_id = current_token["sub"]

        # 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 config["USER_ALLOWED_SCOPES"]:
                flask.abort(400, "Scope {} is not supported".format(s))

        # add all scopes from the user's access token;
        # remove any scopes that have been removed from USER_ALLOWED_SCOPES
        scope.extend(
            [s for s in current_token["scope"] if s in config["USER_ALLOWED_SCOPES"]]
        )

        # a token created using an API key cannot be used to create a new API key
        scope = [s for s in set(scope) if s != "credentials"]

        max_ttl = config.get("MAX_API_KEY_TTL", 2592000)
        expires_in = min(int(flask.request.args.get("expires_in", max_ttl)), max_ttl)
        api_key, claims = create_api_key(
            user_id, flask.current_app.keypairs[0], expires_in, scope, client_id
        )
        return flask.jsonify(dict(key_id=claims["jti"], api_key=api_key))
예제 #6
0
def get_users_proxy_group_from_token():
    """
    Return a user's proxy group ID by parsing the
    JWT token in the header.

    Returns:
        str: proxy group ID or None
    """
    return (current_token.get("context",
                              {}).get("user",
                                      {}).get("google",
                                              {}).get("proxy_group", None))
예제 #7
0
def get_users_linked_google_email_from_token():
    """
    Return a user's linked Google Account's email address by parsing the
    JWT token in the header.

    Returns:
        str: email address of account or None
    """
    if current_token:
        return (current_token.get("context", {}).get("user", {}).get(
            "google", {}).get("linked_google_account", None))

    return None
예제 #8
0
파일: google.py 프로젝트: stefan2811/fence
    def delete(self, access_key):
        """
        .. http:get: /google/(string: access_key)
        Delete a keypair for user

        :param access_key: existing access key that belongs to this user

        :statuscode 204 Success
        :statuscode 403 Forbidden to delete access key
        :statuscode 404 Access key doesn't exist
        """
        user_id = current_token["sub"]
        with GoogleCloudManager() as g_cloud:
            client_id = current_token.get("azp") or None
            service_account = get_service_account(client_id, user_id)

            if service_account:
                keys_for_account = g_cloud.get_service_account_keys_info(
                    service_account.google_unique_id)

                # Only delete the key if is owned by current client's SA
                all_client_keys = [
                    key["name"].split("/")[-1] for key in keys_for_account
                ]
                if access_key in all_client_keys:
                    g_cloud.delete_service_account_key(
                        service_account.google_unique_id, access_key)

                    db_entry = (current_session.query(GoogleServiceAccountKey).
                                filter_by(key_id=access_key).first())
                    if db_entry:
                        current_session.delete(db_entry)
                        current_session.commit()
                else:
                    flask.abort(
                        404,
                        "Could not delete key " + access_key +
                        ". Not found for current user.",
                    )
            else:
                flask.abort(
                    404, "Could not find service account for current user.")

        return "", 204
예제 #9
0
파일: google.py 프로젝트: uc-cdis/fence
    def post(self):
        """
        Force the creation of the User's Primary Google Service Account instead of
        relying on lazy creation at first time of Google Data Access.
        """
        user_id = current_token["sub"]
        proxy_group_id = get_or_create_proxy_group_id()
        username = current_token.get("context", {}).get("user", {}).get("name")
        service_account_email = None

        # do the same thing signed URL creation is doing, but don't use the resulting
        # key, just extract the service account email
        sa_private_key, _ = get_or_create_primary_service_account_key(
            user_id=user_id, username=username, proxy_group_id=proxy_group_id
        )
        service_account_email = sa_private_key.get("client_email")

        # NOTE: service_account_from_db.email is what gets populated in the UserInfo endpoint's
        #       "primary_google_service_account" as well, so this remains consistent
        return flask.jsonify({"primary_google_service_account": service_account_email})
예제 #10
0
def get_or_create_proxy_group_id():
    """
    If no username returned from token or database, create a new proxy group
    for the give user. Also, add the access privileges.

    Returns:
        int: id of (possibly newly created) proxy group associated with user
    """
    proxy_group_id = _get_proxy_group_id()
    if not proxy_group_id:
        user_id = current_token["sub"]
        username = current_token.get("context", {}).get("user",
                                                        {}).get("name", "")
        proxy_group_id = _create_proxy_group(user_id, username).id

        privileges = current_session.query(AccessPrivilege).filter(
            AccessPrivilege.user_id == user_id)

        for p in privileges:
            storage_accesses = p.project.storage_access

            for sa in storage_accesses:
                if sa.provider.name == STORAGE_ACCESS_PROVIDER_NAME:

                    flask.current_app.storage_manager.logger.info(
                        "grant {} access {} to {} in {}".format(
                            username, p.privilege, p.project_id,
                            p.auth_provider))

                    flask.current_app.storage_manager.grant_access(
                        provider=(sa.provider.name),
                        username=username,
                        project=p.project,
                        access=p.privilege,
                        session=current_session,
                    )

    return proxy_group_id
예제 #11
0
    def _generate_google_storage_signed_url(self, http_verb, resource_path,
                                            expiration_time):
        set_current_token(validate_request(aud={"user"}))
        user_id = current_token["sub"]
        proxy_group_id = get_or_create_proxy_group_id()
        username = current_token.get("context", {}).get("user", {}).get("name")

        private_key, key_db_entry = get_or_create_primary_service_account_key(
            user_id=user_id, username=username, proxy_group_id=proxy_group_id)

        # Make sure the service account key expiration is later
        # than the expiration for the signed url. If it's not, we need to
        # provision a new service account key.
        #
        # NOTE: This should occur very rarely: only when the service account key
        #       already exists and is very close to expiring.
        #
        #       If our scheduled maintainence script removes the url-signing key
        #       before the expiration of the url then the url will NOT work
        #       (even though the url itself isn't expired)
        if key_db_entry and key_db_entry.expires < expiration_time:
            private_key = create_primary_service_account_key(
                user_id=user_id,
                username=username,
                proxy_group_id=proxy_group_id)

        final_url = cirrus.google_cloud.utils.get_signed_url(
            resource_path,
            http_verb,
            expiration_time,
            extension_headers=None,
            content_type="",
            md5_value="",
            service_account_creds=private_key,
        )
        return final_url
예제 #12
0
    def get(self):
        """
        List access keys for user

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

               POST /credentials/apis/ HTTP/1.1
               Content-Type: application/json
               Accept: application/json

        Info from Google API /serviceAccounts/<account>/keys endpoint
        but get the expiration time from our DB

        .. code-block:: JavaScript

            {
                "access_keys":
                [
                    {
                        "keyAlgorithm": enum(ServiceAccountKeyAlgorithm),
                        "validBeforeTime": string,
                        "name": string,
                        "validAfterTime": string,
                    },
                    ...
                ]
            }

        """
        client_id = current_token.get("azp") or None
        user_id = current_token["sub"]
        username = current_token.get("context", {}).get("user", {}).get("name")

        with GoogleCloudManager() as g_cloud_manager:
            proxy_group_id = get_or_create_proxy_group_id()
            service_account = get_or_create_service_account(
                client_id=client_id,
                user_id=user_id,
                username=username,
                proxy_group_id=proxy_group_id,
            )

            keys = g_cloud_manager.get_service_account_keys_info(
                service_account.email)

            # replace Google's expiration date by the one in our DB
            reg = re.compile(
                ".+\/keys\/(.+)")  # get key_id from xx/keys/key_id
            for i, key in enumerate(keys):
                key_id = reg.findall(key["name"])[0]
                db_entry = (
                    current_session.query(GoogleServiceAccountKey).filter_by(
                        service_account_id=service_account.id).filter_by(
                            key_id=key_id).first())

                if db_entry:
                    # convert timestamp to date - use the same format as Google API
                    exp_date = datetime.utcfromtimestamp(
                        db_entry.expires).strftime("%Y-%m-%dT%H:%M:%SZ")
                    key["validBeforeTime"] = exp_date

                # the key exists in Google but not in our DB. This should not
                # happen! Delete the key from Google
                else:
                    keys.pop(i)
                    logger.warning(
                        "No GoogleServiceAccountKey entry was found in the fence database for service account name {} for key_id {}, which exists in Google. It will now be deleted from Google."
                        .format(username, key_id))
                    with GoogleCloudManager() as g_cloud:
                        g_cloud.delete_service_account_key(
                            service_account.email, key_id)

            result = {"access_keys": keys}

        return flask.jsonify(result)
예제 #13
0
파일: utils.py 프로젝트: uc-cdis/fence
def get_or_create_proxy_group_id(expires=None, user_id=None, username=None):
    """
    If no username returned from token or database, create a new proxy group
    for the given user. Also, add the access privileges.

    Returns:
        int: id of (possibly newly created) proxy group associated with user
    """
    proxy_group_id = _get_proxy_group_id(user_id=user_id, username=username)
    if not proxy_group_id:
        try:
            user_by_id = query_for_user_by_id(current_session, user_id)
            user_by_username = query_for_user(
                session=current_session, username=username
            )
        except Exception:
            user_by_id = None
            user_by_username = None

        if user_by_id:
            user_id = user_id
            username = user_by_id.username
        elif user_by_username:
            user_id = user_by_username.id
            username = username
        elif current_token:
            user_id = current_token["sub"]
            username = current_token.get("context", {}).get("user", {}).get("name", "")
        else:
            raise Exception(
                f"could not find user given input user_id={user_id} or "
                f"username={username}, nor was there a current_token"
            )

        proxy_group_id = _create_proxy_group(user_id, username).id

        privileges = current_session.query(AccessPrivilege).filter(
            AccessPrivilege.user_id == user_id
        )

        for p in privileges:
            storage_accesses = p.project.storage_access

            for sa in storage_accesses:
                if sa.provider.name == STORAGE_ACCESS_PROVIDER_NAME:

                    flask.current_app.storage_manager.logger.info(
                        "grant {} access {} to {} in {}".format(
                            username, p.privilege, p.project_id, p.auth_provider
                        )
                    )

                    flask.current_app.storage_manager.grant_access(
                        provider=(sa.provider.name),
                        username=username,
                        project=p.project,
                        access=p.privilege,
                        session=current_session,
                        expires=expires,
                    )

    return proxy_group_id