sample-client/cloudfunctions/common_lib/utils.py (62 lines of code) (raw):

# Copyright 2019 Google LLC. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may not # use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations under # the License. # # Any software provided by Google hereunder is distributed "AS IS", WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, and is not intended for production use. import json import os import time from google.auth import jwt import googleapiclient.discovery _ENDPOINT = os.getenv('endpoint') _SERVICE_ACCOUNT_EMAIL = os.getenv('FUNCTION_IDENTITY') PROJECT_ID = os.getenv('projectId') RETENTION_RULES_ENDPOINT = '{}:80/retentionrules'.format(_ENDPOINT) EVENTS_ENDPOINT = '{}:80/events'.format(_ENDPOINT) EVENTS_EXECUTION_ENDPOINT = '{}/execution'.format(EVENTS_ENDPOINT) EVENTS_VALIDATION_ENDPOINT = '{}/validation'.format(EVENTS_ENDPOINT) EVENTS_NOTIFICATION_ENDPOINT = '{}/notification'.format(EVENTS_ENDPOINT) JWT = None def parse_rpo_request(re_match, event_attributes, object_id): """Takes the pubsub notification and parses out the required information. An example of a RPO pattern: .rpo/<datetime>_<retention-days> """ retention_period_index = object_id.rfind('_') retention_period = object_id[retention_period_index + 1:] object_prefix = object_id[:re_match.start()] bucket_id = event_attributes['bucketId'] data_storage_name = 'gs://{}/{}'.format(bucket_id, object_prefix) return SdrsRequest(data_storage_name, PROJECT_ID, retention_period) def parse_delete_request(re_match, event_attributes, object_id): """Takes the pubsub notification and parses out the required information. An example of a delete pattern: .delete_this_folder """ object_prefix = object_id[:re_match.start()] bucket_id = event_attributes['bucketId'] data_storage_name = 'gs://{}/{}'.format(bucket_id, object_prefix) return SdrsRequest(data_storage_name, PROJECT_ID, None) def get_auth_header(): """Returns an authorization header that can be attached to a request.""" return {'Authorization': 'Bearer {}'.format(_get_jwt())} def _get_jwt(): """Checks to see if the global JWT is still valid and either returns it or generates a new one.""" global JWT if JWT is None: JWT = _generate_jwt() else: try: # This will throw a ValueError if the JWT is expired by over 5 min decoded = jwt.decode(JWT, verify=False) # Err on the side of caution and just create a new JWT if we're at expiry if time.time() >= decoded['exp']: JWT = _generate_jwt() except ValueError: JWT = _generate_jwt() return JWT def _generate_jwt(): """Generates a signed JWT using the currently running service account.""" service = googleapiclient.discovery.build(serviceName='iam', version='v1', cache_discovery=False) now = int(time.time()) payload_json = json.dumps({ 'iat': now, # expires after one hour 'exp': now + 3600, # iss is the service account email 'iss': _SERVICE_ACCOUNT_EMAIL, # sub is required for cloud endpoints and must match iss 'sub': _SERVICE_ACCOUNT_EMAIL, 'email': _SERVICE_ACCOUNT_EMAIL, # aud is the URL of the target service 'aud': _ENDPOINT }) slist = service.projects().serviceAccounts().signJwt( name='projects/-/serviceAccounts/{}'.format(_SERVICE_ACCOUNT_EMAIL), body={'payload': payload_json}) resp = slist.execute() return resp['signedJwt'] class SdrsRequest: def __init__(self, data_storage_name, project_id, retention_period): self.data_storage_name = data_storage_name self.project_id = project_id self.retention_period = retention_period