diff --git a/.oca/oca-port/blacklist/fs_attachment.json b/.oca/oca-port/blacklist/fs_attachment.json new file mode 100644 index 0000000000..b4b5a95c16 --- /dev/null +++ b/.oca/oca-port/blacklist/fs_attachment.json @@ -0,0 +1,5 @@ +{ + "pull_requests": { + "OCA/storage#344": "migration PR" + } +} diff --git a/fs_attachment/README.rst b/fs_attachment/README.rst index bd048c7cff..17cc78b89d 100644 --- a/fs_attachment/README.rst +++ b/fs_attachment/README.rst @@ -7,7 +7,7 @@ Base Attachment Object Store !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:88c623934cf2676ac173c9beaa37d16d7f9e735d3bb149ea5527494a151224d6 + !! source digest: sha256:6138d157ac41f158eee22db8c23bd6b5661c7b63855c3f2248566532c6052587 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png @@ -49,10 +49,10 @@ the filename is based on the pattern: This addon also adds on the attachments 2 new fields to use to retrieve the file content from a URL: -- ``Internal URL``: URL to retrieve the file content from the Odoo's - filestore. -- ``Filesystem URL``: URL to retrieve the file content from the - external storage. +- ``Internal URL``: URL to retrieve the file content from the Odoo's + filestore. +- ``Filesystem URL``: URL to retrieve the file content from the external + storage. Note @@ -94,144 +94,142 @@ In addition to the common fields available to configure a storage, specifics fields are available under the section 'Attachment' to configure the way attachments will be stored in the filesystem. -- ``Optimizes Directory Path``: This option is useful if you need to - prevent having too many files in a single directory. It will create a - directory structure based on the attachment's checksum (with 2 levels - of depth) For example, if the checksum is ``123456789``, the file - will be stored in the directory - ``/path/to/storage/12/34/my_file-1-0.txt``. - -- ``Autovacuum GC``: This is used to automatically remove files from - the filesystem when it's no longer referenced in Odoo. Some storage - backends (like S3) may charge you for the storage of files, so it's - important to remove them when they're no longer needed. In some - cases, this option is not desirable, for example if you're using a - storage backend to store images shared with others systems (like your - website) and you don't want to remove the files from the storage - while they're still referenced into the others systems. This - mechanism is based on a ``fs.file.gc`` model used to collect the - files to remove. This model is automatically populated by the - ``ir.attachment`` model when a file is removed from the database. If - you disable this option, you'll have to manually take care of the - records in the ``fs.file.gc`` for your filesystem storage. - -- ``Use As Default For Attachment``: This options allows you to declare - the storage as the default one for attachments. If you have multiple - filesystem storage configured, you can choose which one will be used - by default for attachments. Once activated, attachments created - without specifying a storage will be stored in this default storage. - -- ``Force DB For Default Attachment Rules``: This option is useful if - you want to force the storage of some attachments in the database, - even if you have a default filesystem storage configured. This is - specially useful when you're using a storage backend like S3, where - the latency of the network can be high. This option is a JSON field - that allows you to define the mimetypes and the size limit below - which the attachments will be stored in the database. - - Small images (128, 256) are used in Odoo in list / kanban views. We - want them to be fast to read. They are generally < 50KB (default - configuration) so they don't take that much space in database, but - they'll be read much faster than from the object storage. - - The assets (application/javascript, text/css) are stored in database - as well whatever their size is: - - - a database doesn't have thousands of them - - of course better for performance - - better portability of a database: when replicating a production - instance for dev, the assets are included - - The default configuration is: - - {"image/": 51200, "application/javascript": 0, "text/css": 0} - - Where the key is the beginning of the mimetype to configure and - the value is the limit in size below which attachments are kept in - DB. 0 means no limit. - - Default configuration means: - - - images mimetypes (image/png, image/jpeg, ...) below 50KB are - stored in database - - application/javascript are stored in database whatever their size - - text/css are stored in database whatever their size - - This option is only available on the filesystem storage that is used - as default for attachments. +- ``Optimizes Directory Path``: This option is useful if you need to + prevent having too many files in a single directory. It will create a + directory structure based on the attachment's checksum (with 2 levels + of depth) For example, if the checksum is ``123456789``, the file will + be stored in the directory ``/path/to/storage/12/34/my_file-1-0.txt``. + +- ``Autovacuum GC``: This is used to automatically remove files from the + filesystem when it's no longer referenced in Odoo. Some storage + backends (like S3) may charge you for the storage of files, so it's + important to remove them when they're no longer needed. In some cases, + this option is not desirable, for example if you're using a storage + backend to store images shared with others systems (like your website) + and you don't want to remove the files from the storage while they're + still referenced into the others systems. This mechanism is based on a + ``fs.file.gc`` model used to collect the files to remove. This model + is automatically populated by the ``ir.attachment`` model when a file + is removed from the database. If you disable this option, you'll have + to manually take care of the records in the ``fs.file.gc`` for your + filesystem storage. + +- ``Use As Default For Attachment``: This options allows you to declare + the storage as the default one for attachments. If you have multiple + filesystem storage configured, you can choose which one will be used + by default for attachments. Once activated, attachments created + without specifying a storage will be stored in this default storage. + +- ``Force DB For Default Attachment Rules``: This option is useful if + you want to force the storage of some attachments in the database, + even if you have a default filesystem storage configured. This is + specially useful when you're using a storage backend like S3, where + the latency of the network can be high. This option is a JSON field + that allows you to define the mimetypes and the size limit below which + the attachments will be stored in the database. + + Small images (128, 256) are used in Odoo in list / kanban views. We + want them to be fast to read. They are generally < 50KB (default + configuration) so they don't take that much space in database, but + they'll be read much faster than from the object storage. + + The assets (application/javascript, text/css) are stored in database + as well whatever their size is: + + - a database doesn't have thousands of them + - of course better for performance + - better portability of a database: when replicating a production + instance for dev, the assets are included + + The default configuration is: + + {"image/": 51200, "application/javascript": 0, "text/css": 0} + + Where the key is the beginning of the mimetype to configure and the + value is the limit in size below which attachments are kept in DB. + 0 means no limit. + + Default configuration means: + + - images mimetypes (image/png, image/jpeg, ...) below 50KB are stored + in database + - application/javascript are stored in database whatever their size + - text/css are stored in database whatever their size + + This option is only available on the filesystem storage that is used + as default for attachments. It is also possible to use different FS storages for attachments linked to different resource fields/models. You can configure it either on the ``fs.storage`` directly, or in a server environment file: -- From the ``fs.storage``: Fields model_ids and field_ids will encode - for which models/fields use this storage as default storage for - attachments having these resource model/field. Note that if an - attachment has both resource model and field, it will first take the - FS storage where the field is explicitely linked, then is not found, - the one where the model is explicitely linked. -- From a server environment file: In this case you just have to provide - a comma-separated list of models (under the model_xmlids key) or - fields (under the field_xmlids key). To do so, use the model/field - XML ids provided by Odoo. See the Server Environment section for a - concrete example. +- From the ``fs.storage``: Fields model_ids and field_ids will encode + for which models/fields use this storage as default storage for + attachments having these resource model/field. Note that if an + attachment has both resource model and field, it will first take the + FS storage where the field is explicitely linked, then is not found, + the one where the model is explicitely linked. +- From a server environment file: In this case you just have to provide + a comma-separated list of models (under the model_xmlids key) or + fields (under the field_xmlids key). To do so, use the model/field XML + ids provided by Odoo. See the Server Environment section for a + concrete example. Another key feature of this module is the ability to get access to the attachments from URLs. -- ``Base URL``: This is the base URL used to access the attachments - from the filesystem storage itself. If your storage doesn't provide a - way to access the files from a URL, you can leave this field empty. - -- ``Is Directory Path In URL``: Normally the directory patch configured - on the storage is not included in the URL. If you want to include it, - you can activate this option. - -- ``Use X-Sendfile To Serve Internal Url``: If checked and odoo is - behind a proxy that supports x-sendfile, the content served by the - attachment's internal URL will be served by the proxy using the - filesystem url path if defined (This field is available on the - attachment if the storage is configured with a base URL) If not, the - file will be served by odoo that will stream the content read from - the filesystem storage. This option is useful to avoid to serve files - from odoo and therefore to avoid to load the odoo process. - - To be fully functional, this option requires the proxy to support - x-sendfile (apache) or x-accel-redirect (nginx). You must also - configure your proxy by adding for each storage a rule to redirect - the url rooted at the 'storagge code' to the server serving the - files. For example, if you have a storage with the code 'my_storage' - and a server serving the files at the url 'http://myserver.com', you - must add the following rule in your proxy configuration: - - .. code:: nginx - - location /my_storage/ { - internal; - proxy_pass http://myserver.com; - } - - With this configuration a call to - '/web/content//" for a file stored - in the 'my_storage' storage will generate a response by odoo with the - URI - ``/my_storage//--`` - in the headers ``X-Accel-Redirect`` and ``X-Sendfile`` and the proxy - will redirect to - ``http://myserver.com//--``. - - see - https://www.nginx.com/resources/wiki/start/topics/examples/x-accel/ - for more information. - -- ``Use Filename Obfuscation``: If checked, the filename used to store - the content into the filesystem storage will be obfuscated. This is - useful to avoid to expose the real filename of the attachments - outside of the Odoo database. The filename will be obfuscated by - using the checksum of the content. This option is to avoid when the - content of your filestore is shared with other systems (like your - website) and you want to keep a meaningful filename to ensure SEO. - This option is disabled by default. +- ``Base URL``: This is the base URL used to access the attachments from + the filesystem storage itself. If your storage doesn't provide a way + to access the files from a URL, you can leave this field empty. + +- ``Is Directory Path In URL``: Normally the directory patch configured + on the storage is not included in the URL. If you want to include it, + you can activate this option. + +- ``Use X-Sendfile To Serve Internal Url``: If checked and odoo is + behind a proxy that supports x-sendfile, the content served by the + attachment's internal URL will be served by the proxy using the + filesystem url path if defined (This field is available on the + attachment if the storage is configured with a base URL) If not, the + file will be served by odoo that will stream the content read from the + filesystem storage. This option is useful to avoid to serve files from + odoo and therefore to avoid to load the odoo process. + + To be fully functional, this option requires the proxy to support + x-sendfile (apache) or x-accel-redirect (nginx). You must also + configure your proxy by adding for each storage a rule to redirect the + url rooted at the 'storagge code' to the server serving the files. For + example, if you have a storage with the code 'my_storage' and a server + serving the files at the url 'http://myserver.com', you must add the + following rule in your proxy configuration: + + .. code:: nginx + + location /my_storage/ { + internal; + proxy_pass http://myserver.com; + } + + With this configuration a call to + '/web/content//" for a file stored in + the 'my_storage' storage will generate a response by odoo with the URI + ``/my_storage//--`` + in the headers ``X-Accel-Redirect`` and ``X-Sendfile`` and the proxy + will redirect to + ``http://myserver.com//--``. + + see + https://www.nginx.com/resources/wiki/start/topics/examples/x-accel/ + for more information. + +- ``Use Filename Obfuscation``: If checked, the filename used to store + the content into the filesystem storage will be obfuscated. This is + useful to avoid to expose the real filename of the attachments outside + of the Odoo database. The filename will be obfuscated by using the + checksum of the content. This option is to avoid when the content of + your filestore is shared with other systems (like your website) and + you want to keep a meaningful filename to ensure SEO. This option is + disabled by default. Server Environment ------------------ @@ -239,16 +237,16 @@ Server Environment When you configure a storage through the use of server environment file, you can provide values for the following keys: -- ``optimizes_directory_path`` -- ``autovacuum_gc`` -- ``base_url`` -- ``is_directory_path_in_url`` -- ``use_x_sendfile_to_serve_internal_url`` -- ``use_as_default_for_attachments`` -- ``force_db_for_default_attachment_rules`` -- ``use_filename_obfuscation`` -- ``model_xmlids`` -- ``field_xmlids`` +- ``optimizes_directory_path`` +- ``autovacuum_gc`` +- ``base_url`` +- ``is_directory_path_in_url`` +- ``use_x_sendfile_to_serve_internal_url`` +- ``use_as_default_for_attachments`` +- ``force_db_for_default_attachment_rules`` +- ``use_filename_obfuscation`` +- ``model_xmlids`` +- ``field_xmlids`` For example, the configuration of my storage with code fsprod used to store the attachments by default could be: @@ -314,20 +312,20 @@ new_version can be passed as False to avoid the creation of a new file. Tips & Tricks ------------- -- When working in multi staging environments, the management of the - attachments can be tricky. For example, if you have a production - instance and a staging instance based on a backup of the production - environment, you may want to have the attachments shared between the - two instances BUT you don't want to have one instance removing or - modifying the attachments of the other instance. - - To do so, you can add on your staging instances a new storage and - declare it as the default storage to use for attachments. This way, - all the new attachments will be stored in this new storage but the - attachments created on the production instance will still be read - from the production storage. Be careful to adapt the configuration of - your storage to the production environment to make it read only. (The - use of server environment files is a good way to do so). +- When working in multi staging environments, the management of the + attachments can be tricky. For example, if you have a production + instance and a staging instance based on a backup of the production + environment, you may want to have the attachments shared between the + two instances BUT you don't want to have one instance removing or + modifying the attachments of the other instance. + + To do so, you can add on your staging instances a new storage and + declare it as the default storage to use for attachments. This way, + all the new attachments will be stored in this new storage but the + attachments created on the production instance will still be read from + the production storage. Be careful to adapt the configuration of your + storage to the production environment to make it read only. (The use + of server environment files is a good way to do so). Changelog ========= @@ -337,67 +335,67 @@ Changelog **Bugfixes** -- Fix the error retrieving attachment files when the storage is set to - optimize directory paths. - (`#312 `__) +- Fix the error retrieving attachment files when the storage is set to + optimize directory paths. + (`#312 `__) 16.0.1.0.6 (2023-12-02) ----------------------- **Bugfixes** -- Improve performance at creation of an attachment or when the - attachment is updated. +- Improve performance at creation of an attachment or when the + attachment is updated. - Before this change, when the fs_url was computed the computed value - was always reassigned to the fs_url attribute even if the value was - the same. In a lot of cases the value was the same and the - reassignment was not necessary. Unfortunately this reassignment has - as side effect to mark the record as dirty and generate a SQL update - statement at the end of the transaction. - (`#307 `__) + Before this change, when the fs_url was computed the computed value + was always reassigned to the fs_url attribute even if the value was + the same. In a lot of cases the value was the same and the + reassignment was not necessary. Unfortunately this reassignment has as + side effect to mark the record as dirty and generate a SQL update + statement at the end of the transaction. + (`#307 `__) 16.0.1.0.5 (2023-11-29) ----------------------- **Bugfixes** -- When manipulating the file system api through a local variable named - *fs*, we observed some strange behavior when it was wrongly redefined - in an enclosing scope as in the following example: *with fs.open(...) - as fs*. This commit fixes this issue by renaming the local variable - and therefore avoiding the name clash. - (`#306 `__) +- When manipulating the file system api through a local variable named + *fs*, we observed some strange behavior when it was wrongly redefined + in an enclosing scope as in the following example: *with fs.open(...) + as fs*. This commit fixes this issue by renaming the local variable + and therefore avoiding the name clash. + (`#306 `__) 16.0.1.0.4 (2023-11-22) ----------------------- **Bugfixes** -- Fix error when an url is computed for an attachment in a storage - configure wihtout directory path. - (`#302 `__) +- Fix error when an url is computed for an attachment in a storage + configure wihtout directory path. + (`#302 `__) 16.0.1.0.3 (2023-10-17) ----------------------- **Bugfixes** -- Fix access to technical models to be able to upload attachments for - users with basic access - (`#289 `__) +- Fix access to technical models to be able to upload attachments for + users with basic access + (`#289 `__) 16.0.1.0.2 (2023-10-09) ----------------------- **Bugfixes** -- Ensures python 3.9 compatibility. - (`#285 `__) -- If a storage is not used to store all the attachments by default, the - call to the get_force_db_for_default_attachment_rules method must - return an empty dictionary. - (`#286 `__) +- Ensures python 3.9 compatibility. + (`#285 `__) +- If a storage is not used to store all the attachments by default, the + call to the get_force_db_for_default_attachment_rules method must + return an empty dictionary. + (`#286 `__) Bug Tracker =========== @@ -421,17 +419,21 @@ Authors Contributors ------------ -Thierry Ducrest Guewen Baconnier - Julien Coux - Akim Juillerat - Thomas Nowicki - Vincent Renaville - Denis Leemann - Patrick Tombez - Don Kendall -Stephane Mangin Laurent Mignon - Marie Lejeune -Wolfgang Pichler Nans Lefebvre +- Thierry Ducrest +- Guewen Baconnier +- Julien Coux +- Akim Juillerat +- Thomas Nowicki +- Vincent Renaville +- Denis Leemann +- Patrick Tombez +- Don Kendall +- Stephane Mangi +- Laurent Mignon +- Marie Lejeune +- Wolfgang Pichler +- Nans Lefebvre +- Mohamed Alkobrosli Maintainers ----------- diff --git a/fs_attachment/fs_stream.py b/fs_attachment/fs_stream.py index f04b6c47a5..70173eb814 100644 --- a/fs_attachment/fs_stream.py +++ b/fs_attachment/fs_stream.py @@ -38,7 +38,13 @@ def read(self): return f.read() return super().read() - def get_response(self, as_attachment=None, immutable=None, **send_file_kwargs): + def get_response( + self, + as_attachment=None, + immutable=None, + content_security_policy="default-src 'none'", + **send_file_kwargs, + ): if self.type != "fs": return super().get_response( as_attachment=as_attachment, immutable=immutable, **send_file_kwargs @@ -57,7 +63,6 @@ def get_response(self, as_attachment=None, immutable=None, **send_file_kwargs): "max_age": STATIC_CACHE_LONG if immutable else self.max_age, "environ": request.httprequest.environ, "response_class": Response, - **send_file_kwargs, } use_x_sendfile = self._fs_use_x_sendfile # The file will be closed by werkzeug... @@ -79,6 +84,12 @@ def get_response(self, as_attachment=None, immutable=None, **send_file_kwargs): if immutable and res.cache_control: res.cache_control["immutable"] = None + + res.headers["X-Content-Type-Options"] = "nosniff" + + if content_security_policy: + res.headers["Content-Security-Policy"] = content_security_policy + return res @classmethod diff --git a/fs_attachment/models/ir_binary.py b/fs_attachment/models/ir_binary.py index 1683dd213c..a784aec9df 100644 --- a/fs_attachment/models/ir_binary.py +++ b/fs_attachment/models/ir_binary.py @@ -16,16 +16,14 @@ class IrBinary(models.AbstractModel): _inherit = "ir.binary" - def _record_to_stream(self, record, field_name): - # Extend base implementation to support attachment stored into a - # filesystem storage - fs_attachment = None + def _get_fs_attachment_for_field(self, record, field_name): if record._name == "ir.attachment" and record.fs_filename: - fs_attachment = record + return record + record.check_field_access_rights("read", [field_name]) field_def = record._fields[field_name] - if field_def.attachment and not field_def.compute and not field_def.related: - field_attachment = ( + if field_def.attachment and field_def.store: + fs_attachment = ( self.env["ir.attachment"] .sudo() .search( @@ -37,8 +35,14 @@ def _record_to_stream(self, record, field_name): limit=1, ) ) - if field_attachment.fs_filename: - fs_attachment = field_attachment + if fs_attachment and fs_attachment.fs_filename: + return fs_attachment + return None + + def _record_to_stream(self, record, field_name): + # Extend base implementation to support attachment stored into a + # filesystem storage + fs_attachment = self._get_fs_attachment_for_field(record, field_name) if fs_attachment: return FsStream.from_fs_attachment(fs_attachment) return super()._record_to_stream(record, field_name) @@ -100,6 +104,11 @@ def _get_image_stream_from( if value: record = value.attachment field_name = "raw" + elif field_def.type in ("binary"): + fs_attachment = self._get_fs_attachment_for_field(record, field_name) + if fs_attachment: + record = fs_attachment + field_name = "raw" stream = super()._get_image_stream_from( record, field_name=field_name, diff --git a/fs_attachment/readme/CONTRIBUTORS.md b/fs_attachment/readme/CONTRIBUTORS.md index c3441031b8..071273090e 100644 --- a/fs_attachment/readme/CONTRIBUTORS.md +++ b/fs_attachment/readme/CONTRIBUTORS.md @@ -1,13 +1,15 @@ -Thierry Ducrest \<\> Guewen Baconnier -\<\> Julien Coux -\<\> Akim Juillerat -\<\> Thomas Nowicki -\<\> Vincent Renaville -\<\> Denis Leemann -\<\> Patrick Tombez -\<\> Don Kendall -\<\> Stephane Mangin -\<\> Laurent Mignon -\<\> Marie Lejeune -\<\> Wolfgang Pichler \<\> -Nans Lefebvre \<\> +* Thierry Ducrest \<\> +* Guewen Baconnier \<\> +* Julien Coux \<\> +* Akim Juillerat \<\> +* Thomas Nowicki \<\> +* Vincent Renaville \<\> +* Denis Leemann \<\> +* Patrick Tombez \<\> +* Don Kendall \<\> +* Stephane Mangi \<\> +* Laurent Mignon \<\> +* Marie Lejeune \<\> +* Wolfgang Pichler \<\> +* Nans Lefebvre \<\> +* Mohamed Alkobrosli \<\> diff --git a/fs_attachment/static/description/index.html b/fs_attachment/static/description/index.html index 76aade8029..a7019ed642 100644 --- a/fs_attachment/static/description/index.html +++ b/fs_attachment/static/description/index.html @@ -367,7 +367,7 @@

Base Attachment Object Store

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:88c623934cf2676ac173c9beaa37d16d7f9e735d3bb149ea5527494a151224d6 +!! source digest: sha256:6138d157ac41f158eee22db8c23bd6b5661c7b63855c3f2248566532c6052587 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

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

In some cases, you need to store attachment in another system that the @@ -390,8 +390,8 @@

Base Attachment Object Store

  • Internal URL: URL to retrieve the file content from the Odoo’s filestore.
  • -
  • Filesystem URL: URL to retrieve the file content from the -external storage.
  • +
  • Filesystem URL: URL to retrieve the file content from the external +storage.

Note

The internal URL is always available, but the filesystem URL is only @@ -453,23 +453,22 @@

Configuration

  • Optimizes Directory Path: This option is useful if you need to prevent having too many files in a single directory. It will create a directory structure based on the attachment’s checksum (with 2 levels -of depth) For example, if the checksum is 123456789, the file -will be stored in the directory -/path/to/storage/12/34/my_file-1-0.txt.

    +of depth) For example, if the checksum is 123456789, the file will +be stored in the directory /path/to/storage/12/34/my_file-1-0.txt.

  • -
  • Autovacuum GC: This is used to automatically remove files from -the filesystem when it’s no longer referenced in Odoo. Some storage +

  • Autovacuum GC: This is used to automatically remove files from the +filesystem when it’s no longer referenced in Odoo. Some storage backends (like S3) may charge you for the storage of files, so it’s -important to remove them when they’re no longer needed. In some -cases, this option is not desirable, for example if you’re using a -storage backend to store images shared with others systems (like your -website) and you don’t want to remove the files from the storage -while they’re still referenced into the others systems. This -mechanism is based on a fs.file.gc model used to collect the -files to remove. This model is automatically populated by the -ir.attachment model when a file is removed from the database. If -you disable this option, you’ll have to manually take care of the -records in the fs.file.gc for your filesystem storage.

    +important to remove them when they’re no longer needed. In some cases, +this option is not desirable, for example if you’re using a storage +backend to store images shared with others systems (like your website) +and you don’t want to remove the files from the storage while they’re +still referenced into the others systems. This mechanism is based on a +fs.file.gc model used to collect the files to remove. This model +is automatically populated by the ir.attachment model when a file +is removed from the database. If you disable this option, you’ll have +to manually take care of the records in the fs.file.gc for your +filesystem storage.

  • Use As Default For Attachment: This options allows you to declare the storage as the default one for attachments. If you have multiple @@ -482,8 +481,8 @@

    Configuration

    even if you have a default filesystem storage configured. This is specially useful when you’re using a storage backend like S3, where the latency of the network can be high. This option is a JSON field -that allows you to define the mimetypes and the size limit below -which the attachments will be stored in the database.

    +that allows you to define the mimetypes and the size limit below which +the attachments will be stored in the database.

    Small images (128, 256) are used in Odoo in list / kanban views. We want them to be fast to read. They are generally < 50KB (default configuration) so they don’t take that much space in database, but @@ -499,14 +498,14 @@

    Configuration

    The default configuration is:

    {“image/”: 51200, “application/javascript”: 0, “text/css”: 0}

    -

    Where the key is the beginning of the mimetype to configure and -the value is the limit in size below which attachments are kept in -DB. 0 means no limit.

    +

    Where the key is the beginning of the mimetype to configure and the +value is the limit in size below which attachments are kept in DB. +0 means no limit.

    Default configuration means:

      -
    • images mimetypes (image/png, image/jpeg, …) below 50KB are -stored in database
    • +
    • images mimetypes (image/png, image/jpeg, …) below 50KB are stored +in database
    • application/javascript are stored in database whatever their size
    • text/css are stored in database whatever their size
    @@ -526,16 +525,16 @@

    Configuration

    the one where the model is explicitely linked.
  • From a server environment file: In this case you just have to provide a comma-separated list of models (under the model_xmlids key) or -fields (under the field_xmlids key). To do so, use the model/field -XML ids provided by Odoo. See the Server Environment section for a +fields (under the field_xmlids key). To do so, use the model/field XML +ids provided by Odoo. See the Server Environment section for a concrete example.
  • Another key feature of this module is the ability to get access to the attachments from URLs.

      -
    • Base URL: This is the base URL used to access the attachments -from the filesystem storage itself. If your storage doesn’t provide a -way to access the files from a URL, you can leave this field empty.

      +
    • Base URL: This is the base URL used to access the attachments from +the filesystem storage itself. If your storage doesn’t provide a way +to access the files from a URL, you can leave this field empty.

    • Is Directory Path In URL: Normally the directory patch configured on the storage is not included in the URL. If you want to include it, @@ -546,16 +545,16 @@

      Configuration

      attachment’s internal URL will be served by the proxy using the filesystem url path if defined (This field is available on the attachment if the storage is configured with a base URL) If not, the -file will be served by odoo that will stream the content read from -the filesystem storage. This option is useful to avoid to serve files -from odoo and therefore to avoid to load the odoo process.

      +file will be served by odoo that will stream the content read from the +filesystem storage. This option is useful to avoid to serve files from +odoo and therefore to avoid to load the odoo process.

      To be fully functional, this option requires the proxy to support x-sendfile (apache) or x-accel-redirect (nginx). You must also -configure your proxy by adding for each storage a rule to redirect -the url rooted at the ‘storagge code’ to the server serving the -files. For example, if you have a storage with the code ‘my_storage’ -and a server serving the files at the url ‘http://myserver.com’, you -must add the following rule in your proxy configuration:

      +configure your proxy by adding for each storage a rule to redirect the +url rooted at the ‘storagge code’ to the server serving the files. For +example, if you have a storage with the code ‘my_storage’ and a server +serving the files at the url ‘http://myserver.com’, you must add the +following rule in your proxy configuration:

       location /my_storage/ {
           internal;
      @@ -563,9 +562,8 @@ 

      Configuration

      }

      With this configuration a call to -‘/web/content/<att.id>/<att.name><att.extension>” for a file stored -in the ‘my_storage’ storage will generate a response by odoo with the -URI +‘/web/content/<att.id>/<att.name><att.extension>” for a file stored in +the ‘my_storage’ storage will generate a response by odoo with the URI /my_storage/<paht_in_storage>/<att.name>-<att.id>-<version><att.extension> in the headers X-Accel-Redirect and X-Sendfile and the proxy will redirect to @@ -576,12 +574,12 @@

      Configuration

    • Use Filename Obfuscation: If checked, the filename used to store the content into the filesystem storage will be obfuscated. This is -useful to avoid to expose the real filename of the attachments -outside of the Odoo database. The filename will be obfuscated by -using the checksum of the content. This option is to avoid when the -content of your filestore is shared with other systems (like your -website) and you want to keep a meaningful filename to ensure SEO. -This option is disabled by default.

      +useful to avoid to expose the real filename of the attachments outside +of the Odoo database. The filename will be obfuscated by using the +checksum of the content. This option is to avoid when the content of +your filestore is shared with other systems (like your website) and +you want to keep a meaningful filename to ensure SEO. This option is +disabled by default.

    @@ -666,10 +664,10 @@

    Tips & Tricks

    To do so, you can add on your staging instances a new storage and declare it as the default storage to use for attachments. This way, all the new attachments will be stored in this new storage but the -attachments created on the production instance will still be read -from the production storage. Be careful to adapt the configuration of -your storage to the production environment to make it read only. (The -use of server environment files is a good way to do so).

    +attachments created on the production instance will still be read from +the production storage. Be careful to adapt the configuration of your +storage to the production environment to make it read only. (The use +of server environment files is a good way to do so).

    @@ -694,8 +692,8 @@

    16.0.1.0.6 (2023-12-02)

    Before this change, when the fs_url was computed the computed value was always reassigned to the fs_url attribute even if the value was the same. In a lot of cases the value was the same and the -reassignment was not necessary. Unfortunately this reassignment has -as side effect to mark the record as dirty and generate a SQL update +reassignment was not necessary. Unfortunately this reassignment has as +side effect to mark the record as dirty and generate a SQL update statement at the end of the transaction. (#307)

    @@ -763,17 +761,23 @@

    Authors

    Contributors

    -

    Thierry Ducrest <thierry.ducrest@camptocamp.com> Guewen Baconnier -<guewen.baconnier@camptocamp.com> Julien Coux -<julien.coux@camptocamp.com> Akim Juillerat -<akim.juillerat@camptocamp.com> Thomas Nowicki -<thomas.nowicki@camptocamp.com> Vincent Renaville -<vincent.renaville@camptocamp.com> Denis Leemann -<denis.leemann@camptocamp.com> Patrick Tombez -<patrick.tombez@camptocamp.com> Don Kendall <kendall@donkendall.com> -Stephane Mangin <stephane.mangin@camptocamp.com> Laurent Mignon -<laurent.mignon@acsone.eu> Marie Lejeune <marie.lejeune@acsone.eu> -Wolfgang Pichler <wpichler@callino.at> Nans Lefebvre <len@lambdao.dev>

    +

    Maintainers

    diff --git a/fs_attachment/tests/test_stream.py b/fs_attachment/tests/test_stream.py index 9d19df60b4..f05bf61e30 100644 --- a/fs_attachment/tests/test_stream.py +++ b/fs_attachment/tests/test_stream.py @@ -1,5 +1,6 @@ # Copyright 2023 ACSONE SA/NV (http://acsone.eu). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +import base64 import io import os import shutil @@ -150,3 +151,44 @@ def test_image_url_with_size(self): }, ) self.assertEqual(Image.open(io.BytesIO(res.content)).size, (64, 64)) + + def test_response_csp_header(self): + self.authenticate("admin", "admin") + url = f"/web/content/{self.attachment_binary.id}" + self.assertDownload( + url, + headers={}, + assert_status_code=200, + assert_headers={ + "X-Content-Type-Options": "nosniff", + "Content-Security-Policy": "default-src 'none'", + }, + ) + + def test_serving_field_image(self): + self.authenticate("admin", "admin") + demo_partner = self.env.ref("base.partner_demo") + demo_partner.with_context( + storage_location=self.temp_backend.code, + ).write({"image_128": base64.encodebytes(self._create_image(128, 128))}) + url = f"/web/image/{demo_partner._name}/{demo_partner.id}/image_128" + res = self.assertDownload( + url, + headers={}, + assert_status_code=200, + assert_headers={ + "Content-Type": "image/png", + }, + ) + self.assertEqual(Image.open(io.BytesIO(res.content)).size, (128, 128)) + + url = f"/web/image/{demo_partner._name}/{demo_partner.id}/avatar_128" + avatar_res = self.assertDownload( + url, + headers={}, + assert_status_code=200, + assert_headers={ + "Content-Type": "image/png", + }, + ) + self.assertEqual(Image.open(io.BytesIO(avatar_res.content)).size, (128, 128))