diff --git a/fs_folder/fields.py b/fs_folder/fields.py index f1d92c8916..d539f4eac2 100644 --- a/fs_folder/fields.py +++ b/fs_folder/fields.py @@ -1,5 +1,6 @@ # Copyright 2024 ACSONE SA/NV # License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). +import logging import threading import time import typing @@ -7,7 +8,7 @@ import fsspec -from odoo import SUPERUSER_ID, api, fields, models, registry +from odoo import api, fields, models, registry from odoo.tools.misc import SENTINEL, Sentinel from odoo.tools.sql import pg_varchar @@ -15,6 +16,8 @@ from .models.fs_storage import FsStorage +_logger = logging.getLogger(__name__) + class FsContentValue: def __init__( @@ -352,14 +355,17 @@ def create_value_in_fs(self, records: models.BaseModel) -> list[FsFolderValue]: fs.mkdir(path, **kwargs) - def clean_up_folder(path, storage_code, dbname): + def clean_up_folder(path, storage_code, dbname, user_id): db_registry = registry(dbname) with db_registry.cursor() as cr: - env = api.Environment(cr, SUPERUSER_ID, {}) + env = api.Environment(cr, user_id, {}) fs = env["fs.storage"].get_fs_by_code(storage_code) time.sleep(0.5) # wait creation into the filesystem - fs.rm(path, recursive=True) - # remove created resource in case of rollback + try: + # remove created resource in case of rollback + fs.rm(path, recursive=True) + except Exception as e: + _logger.exception(f"Error cleaning up folder {path}: {e}") test_mode = getattr(threading.current_thread(), "testing", False) if not test_mode: @@ -369,6 +375,7 @@ def clean_up_folder(path, storage_code, dbname): path, storage_code, record.env.cr.dbname, + record.env.user.id, ), ) diff --git a/fs_folder_ms_drive/models/fs_folder_field_value_adapter.py b/fs_folder_ms_drive/models/fs_folder_field_value_adapter.py index a9783d3efd..2afc535266 100644 --- a/fs_folder_ms_drive/models/fs_folder_field_value_adapter.py +++ b/fs_folder_ms_drive/models/fs_folder_field_value_adapter.py @@ -42,9 +42,10 @@ def _parse_fs_folder_value( ref, storage_code = super()._parse_fs_folder_value(stored_value, field, record) if ref: fs = record.env["fs.storage"].sudo().get_fs_by_code(storage_code) - if ( - self._is_msgraph_folder(fs) - and self.env.user.microsoft_drive_status == "connected" + user = record.env.user + if self._is_msgraph_folder(fs) and ( + user.microsoft_drive_oauth2_non_interactive + or user.microsoft_drive_status == "connected" ): fs_info = fs.info(path=ref, item_id=ref, details=False) ref = fs_info.get("name") diff --git a/fs_storage_ms_drive/models/fs_storage.py b/fs_storage_ms_drive/models/fs_storage.py index 5948d18218..24c0619601 100644 --- a/fs_storage_ms_drive/models/fs_storage.py +++ b/fs_storage_ms_drive/models/fs_storage.py @@ -23,15 +23,17 @@ def _get_filesystem(self): while hasattr(root_fs, "fs"): root_fs = fs.fs client = root_fs.client - client.register_compliance_hook( - "refresh_token_response", - partial( - self.update_refresh_token_on_user, - client=client, - db_name=self._cr.dbname, - user_id=self.env.user.id, - ), - ) + if not self.env.user.microsoft_drive_oauth2_non_interactive: + # In interactive mode, we need to update the refresh token on user + client.register_compliance_hook( + "refresh_token_response", + partial( + self.update_refresh_token_on_user, + client=client, + db_name=self._cr.dbname, + user_id=self.env.user.id, + ), + ) return fs @api.model diff --git a/fs_storage_ms_drive/models/res_user.py b/fs_storage_ms_drive/models/res_user.py index df47a4b2be..e599f65232 100644 --- a/fs_storage_ms_drive/models/res_user.py +++ b/fs_storage_ms_drive/models/res_user.py @@ -14,26 +14,31 @@ def _get_oauth2_client_params(self): return { "client_id": get_param("microsoft_drive_client_id"), "client_secret": get_param("microsoft_drive_client_secret"), - "scope": self.env["microsoft.service"]._get_drive_scope(), "token_endpoint": get_param("microsoft_account.token_endpoint"), } def _get_oauth2_params(self): self.ensure_one() - access_token = self.microsoft_drive_token - rtoken = self.microsoft_drive_rtoken - expires_at = -1 - if self.microsoft_drive_token_validity: - # Convert datetime to timestamp - # This is needed for compatibility with authlib - # which expects an integer timestamp. - # If the token validity is not set, we use -1 to indicate no expiry. - expires_at = int(self.microsoft_drive_token_validity.timestamp()) - token = { - "access_token": access_token, - "refresh_token": rtoken, - "expires_at": expires_at, - } params = self._get_oauth2_client_params() - params.update(token=token) + if self.microsoft_drive_oauth2_non_interactive: + params["grant_type"] = "client_credentials" + params["scope"] = "https://graph.microsoft.com/.default" + else: + params["grant_type"] = "refresh_token" + params["scope"] = self.env["microsoft.service"]._get_drive_scope() + access_token = self.microsoft_drive_token + rtoken = self.microsoft_drive_rtoken + expires_at = -1 + if self.microsoft_drive_token_validity: + # Convert datetime to timestamp + # This is needed for compatibility with authlib + # which expects an integer timestamp. + # If the token validity is not set, we use -1 to indicate no expiry. + expires_at = int(self.microsoft_drive_token_validity.timestamp()) + token = { + "access_token": access_token, + "refresh_token": rtoken, + "expires_at": expires_at, + } + params["token"] = token return params diff --git a/fs_storage_ms_drive/static/description/index.html b/fs_storage_ms_drive/static/description/index.html index 42c2977a08..bade2cc725 100644 --- a/fs_storage_ms_drive/static/description/index.html +++ b/fs_storage_ms_drive/static/description/index.html @@ -3,7 +3,7 @@ -README.rst +Filesystem Storage For Microsoft Drives -
+
+

Filesystem Storage For Microsoft Drives

- - -Odoo Community Association - -
-

Filesystem Storage For Microsoft Drives

-

Beta License: LGPL-3 OCA/storage Translate me on Weblate Try me on Runboat

+

Beta License: LGPL-3 OCA/storage Translate me on Weblate Try me on Runboat

This addon extends the functionality of the fs_storage module to allow the system to use Microsoft Drives (OneDrive, @@ -411,7 +406,7 @@

Filesystem Storage For Microsoft Drives

-

Bug Tracker

+

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed @@ -419,29 +414,29 @@

Bug Tracker

Do not contact contributors directly about support or help with technical issues.

-

Credits

+

Credits

-

Authors

+

Authors

  • ACSONE SA/NV
-

Other credits

+

Other credits

The development of this module has been financially supported by:

  • ACSONE SA/NV
-

Maintainers

+

Maintainers

This module is maintained by the OCA.

Odoo Community Association @@ -456,6 +451,5 @@

Maintainers

-
diff --git a/microsoft_drive_account/models/res_users.py b/microsoft_drive_account/models/res_users.py index 584bff0907..49958811bd 100644 --- a/microsoft_drive_account/models/res_users.py +++ b/microsoft_drive_account/models/res_users.py @@ -26,6 +26,16 @@ class ResUsers(models.Model): store=True, ) + microsoft_drive_oauth2_non_interactive = fields.Boolean( + string="Microsoft Drive OAuth2 (non-interactive)", + default=False, + help="If set, this user will not go through the interactive OAuth2 " + "authorization flow; The requested token will be requested to the " + "authentication server directly using client credentials flows. " + "(scope: 'https://graph.microsoft.com/.default', " + "grante_type: 'client_credentials')", + ) + def _set_microsoft_drive_auth_tokens( self, access_token: str, refresh_token: str, ttl: float ): diff --git a/microsoft_drive_account/views/res_users.xml b/microsoft_drive_account/views/res_users.xml index b20ac1fcb8..ea07689c88 100644 --- a/microsoft_drive_account/views/res_users.xml +++ b/microsoft_drive_account/views/res_users.xml @@ -9,8 +9,13 @@ + + + - +