Exemple #1
0
    def _generate_google_storage_signed_url(self, http_verb, resource_path,
                                            expiration_time, user_id,
                                            username):
        proxy_group_id = get_or_create_proxy_group_id()

        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
Exemple #2
0
    def _extend_account_expiration():
        user_id = current_token["sub"]
        google_email = get_users_linked_google_email(user_id)
        proxy_group = get_or_create_proxy_group_id()

        access_expiration = _force_update_user_google_account(user_id,
                                                              google_email,
                                                              proxy_group,
                                                              _allow_new=False)

        return {"exp": access_expiration}, 200
Exemple #3
0
    def _generate_google_storage_signed_url(
        self,
        http_verb,
        resource_path,
        expiration_time,
        user_id,
        username,
        r_pays_project=None,
    ):
        proxy_group_id = get_or_create_proxy_group_id()

        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)

        if config["ENABLE_AUTOMATIC_BILLING_PERMISSION_SIGNED_URLS"]:
            give_service_account_billing_access_if_necessary(
                private_key,
                r_pays_project,
                default_billing_project=config[
                    "BILLING_PROJECT_FOR_SIGNED_URLS"],
            )

        # use configured project if it exists and no user project was given
        if config["BILLING_PROJECT_FOR_SIGNED_URLS"] and not r_pays_project:
            r_pays_project = config["BILLING_PROJECT_FOR_SIGNED_URLS"]

        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,
            requester_pays_user_project=r_pays_project,
        )
        return final_url
Exemple #4
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)
Exemple #5
0
    def _link_google_account():
        provided_redirect = flask.request.args.get("redirect")

        if not provided_redirect:
            raise UserError({"error": "No redirect provided."})

        user_id = current_token["sub"]
        google_email = get_users_linked_google_email(user_id)
        proxy_group = get_or_create_proxy_group_id()

        # Set session flag to signify that we're linking and not logging in
        # Save info needed for linking in session since we need to AuthN first
        flask.session["google_link"] = True
        flask.session["user_id"] = user_id
        flask.session["google_proxy_group_id"] = proxy_group
        flask.session["linked_google_email"] = google_email

        if not google_email:
            # save off provided redirect in session and initiate Google AuthN
            flask.session["redirect"] = provided_redirect
            flask.redirect_url = flask.current_app.google_client.get_auth_url()

            # Tell Google to let user select an account
            flask.redirect_url = append_query_params(flask.redirect_url,
                                                     prompt="select_account")
        else:
            # double check that the token isn't stale by hitting db
            linked_email_in_db = get_linked_google_account_email(user_id)

            if linked_email_in_db:
                # skip Google AuthN, already linked, error
                redirect_with_errors = append_query_params(
                    provided_redirect,
                    error="g_acnt_link_error",
                    error_description=
                    "User already has a linked Google account.",
                )
                flask.redirect_url = redirect_with_errors
                _clear_google_link_info_from_session()
            else:
                # TODO can we handle this error?
                redirect_with_errors = append_query_params(
                    provided_redirect,
                    error="g_acnt_link_error",
                    error_description="Stale access token, please refresh.",
                )
                flask.redirect_url = redirect_with_errors
                _clear_google_link_info_from_session()

        return flask.redirect(flask.redirect_url)
Exemple #6
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
        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)
Exemple #7
0
    def _extend_account_expiration():
        user_id = current_token["sub"]
        google_email = get_users_linked_google_email(user_id)
        proxy_group = get_or_create_proxy_group_id()

        # requested time (in seconds) during which the link will be valid
        requested_expires_in = get_valid_expiration_from_request()

        access_expiration = _force_update_user_google_account(
            user_id,
            google_email,
            proxy_group,
            _allow_new=False,
            requested_expires_in=requested_expires_in,
        )

        return {"exp": access_expiration}, 200
Exemple #8
0
    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})
Exemple #9
0
    def _link_google_account():
        provided_redirect = flask.request.args.get("redirect")

        # will raise UserError if invalid
        validate_redirect(provided_redirect)

        if not provided_redirect:
            raise UserError({"error": "No redirect provided."})

        user_id = current_token["sub"]
        google_email = get_users_linked_google_email(user_id)
        proxy_group = get_or_create_proxy_group_id()

        # Set session flag to signify that we're linking and not logging in
        # Save info needed for linking in session since we need to AuthN first
        flask.session["google_link"] = True
        flask.session["user_id"] = user_id
        flask.session["google_proxy_group_id"] = proxy_group
        flask.session["linked_google_email"] = google_email

        if not google_email:
            # save off provided redirect in session and initiate Google AuthN
            flask.session["redirect"] = provided_redirect

            # requested time (in seconds) during which the link will be valid
            requested_expires_in = get_valid_expiration_from_request()
            if requested_expires_in:
                flask.session["google_link_expires_in"] = requested_expires_in

            # if we're mocking Google login, skip to callback
            if config.get("MOCK_GOOGLE_AUTH", False):
                flask.redirect_url = (config["BASE_URL"].strip("/") +
                                      "/link/google/callback?code=abc")
                response = flask.redirect(flask.redirect_url)
                # pass-through the authorization header. The user's username
                # MUST be a Google email for MOCK_GOOGLE_AUTH to actually link that
                # email correctly
                response.headers["Authorization"] = flask.request.headers.get(
                    "Authorization")
                return response

            flask.redirect_url = flask.current_app.google_client.get_auth_url()

            # Tell Google to let user select an account
            flask.redirect_url = append_query_params(flask.redirect_url,
                                                     prompt="select_account")
        else:
            # double check that the token isn't stale by hitting db
            linked_email_in_db = get_linked_google_account_email(user_id)

            if linked_email_in_db:
                # skip Google AuthN, already linked, error
                redirect_with_errors = append_query_params(
                    provided_redirect,
                    error="g_acnt_link_error",
                    error_description=
                    "User already has a linked Google account.",
                )
                flask.redirect_url = redirect_with_errors
                _clear_google_link_info_from_session()
            else:
                # TODO can we handle this error?
                redirect_with_errors = append_query_params(
                    provided_redirect,
                    error="g_acnt_link_error",
                    error_description="Stale access token, please refresh.",
                )
                flask.redirect_url = redirect_with_errors
                _clear_google_link_info_from_session()

        return flask.redirect(flask.redirect_url)
Exemple #10
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)