# Part of Odoo. See LICENSE file for full copyright and licensing details. import json import re from urllib.parse import unquote, quote try: from google.oauth2 import service_account from google.auth.transport.requests import Request except ImportError: service_account = Request = None from odoo import models from odoo.exceptions import ValidationError from ..utils.cloud_storage_google_utils import generate_signed_url_v4 CloudStorageGoogleCredentials = {} # {db_name: (account_info, credential)} def get_cloud_storage_google_credential(env): """ Get the credentials object of currently used account info. This method is cached to because from_service_account_info is slow. """ cached_account_info, cached_credential = CloudStorageGoogleCredentials.get(env.registry.db_name, (None, None)) account_info = json.loads(env['ir.config_parameter'].sudo().get_param('cloud_storage_google_account_info')) if cached_account_info == account_info: return cached_credential credential = service_account.Credentials.from_service_account_info(account_info) CloudStorageGoogleCredentials[env.registry.db_name] = (account_info, credential) return credential class IrAttachment(models.Model): _inherit = 'ir.attachment' _cloud_storage_google_url_pattern = re.compile(r'https://storage\.googleapis\.com/(?P[\w\-.]+)/(?P[^?]+)') def _get_cloud_storage_google_info(self): match = self._cloud_storage_google_url_pattern.match(self.url) if not match: raise ValidationError('%s is not a valid Google Cloud Storage URL.', self.url) return { 'bucket_name': match['bucket_name'], 'blob_name': unquote(match['blob_name']), } def _generate_cloud_storage_google_url(self, blob_name): bucket_name = self.env['ir.config_parameter'].get_param('cloud_storage_google_bucket_name') return f"https://storage.googleapis.com/{bucket_name}/{quote(blob_name)}" def _generate_cloud_storage_google_signed_url(self, bucket_name, blob_name, **kwargs): quote_blob_name = quote(blob_name) return generate_signed_url_v4( credentials=get_cloud_storage_google_credential(self.env), resource=f'/{bucket_name}/{quote_blob_name}', **kwargs, ) # OVERRIDES def _generate_cloud_storage_url(self): if self.env['ir.config_parameter'].sudo().get_param('cloud_storage_provider') != 'google': return super()._generate_cloud_storage_url() blob_name = self._generate_cloud_storage_blob_name() return self._generate_cloud_storage_google_url(blob_name) def _generate_cloud_storage_download_info(self): if self.env['ir.config_parameter'].sudo().get_param('cloud_storage_provider') != 'google': return super()._generate_cloud_storage_download_info() info = self._get_cloud_storage_google_info() return { 'url': self._generate_cloud_storage_google_signed_url(info['bucket_name'], info['blob_name'], method='GET', expiration=self._cloud_storage_download_url_time_to_expiry), 'time_to_expiry': self._cloud_storage_download_url_time_to_expiry, } def _generate_cloud_storage_upload_info(self): if self.env['ir.config_parameter'].sudo().get_param('cloud_storage_provider') != 'google': return super()._generate_cloud_storage_upload_info() info = self._get_cloud_storage_google_info() return { 'url': self._generate_cloud_storage_google_signed_url(info['bucket_name'], info['blob_name'], method='PUT', expiration=self._cloud_storage_upload_url_time_to_expiry), 'method': 'PUT', 'response_status': 200, }