Commit Graph
252 Commits
Author SHA1 Message Date
Samuel Degueldre b258e5d294 [FIX] http: force mimetype of .js files to text/javascript
Previously, when the odoo server was running on some Windows
installations, it was possible for javascript files loaded directly from
the static folder of an addon to fail to run because the Content-Type
header was set to text/plain instead of text/javascript. This is because
the mimetypes module from the standard library honors the mimetypes from
the OS, in the case of Windows it reads a key in the registry, which can
be misconfigured to text/plain for .js files.

This commit forces the mimetype of .js files to text/javascript to solve
this issue.

closes odoo/odoo#162313

X-original-commit: 64cbe389e698398eee93ebde9c61b2ee79756380
Signed-off-by: Julien Castiaux (juc) <juc@odoo.com>
Signed-off-by: Samuel Degueldre (sad) <sad@odoo.com>
2024-04-18 05:53:27 +00:00
Julien Castiaux 851133ff06 [FIX] core: NotFound error without warning
The conditionnal `isinstance(exc, NotFound)` is shadowed by the
conditionnal `isinstance(exc, HTTPException)` two lines above. Nobody
ever complained that the warning for NotFound error was gone. Since
werkzeug 1.0.0, the status code in the response log is colored, 404 is
colored yellow which should catch the eye. The explicit warning line
isn't really necessary.

closes odoo/odoo#159895

X-original-commit: 851b91f19b87446662421cb8d801a9472725bc72
Signed-off-by: Julien Castiaux (juc) <juc@odoo.com>
2024-04-08 09:27:27 +00:00
Denis Ledoux b23cb16487 [FIX] core: restrict httprequest attributes
The version of werkzeug installed can vary from one deployment
to another, as we recommend to use the operating system package,
and the version can therefore change according to the operating
system version.

e.g. the werkzeug version installed using
`apt install python3-werkzeug` varies between
Ubuntu 18.04, 20.04, 22.04, 23.10, ...

We want to keep under control the attributes
developers use on werkzeug.wrappers.Request,
to avoid compatibility issues from one
version to another.

Therefore, this revision aims
to subclass werkzeug.wrappers.Request to limit
the attributes which can be used.

task-3734305

Part-of: odoo/odoo#78857

Signed-off-by: Julien Castiaux (juc) <juc@odoo.com>
2023-12-07 12:53:19 +00:00
Carsten Wolff (cawo) 683a51bd27 [FIX] http: close request resources when done
Currently, when a worker (process/thread) has finished processing a request, it
will keep handles on resources held by the werkzeug `Request` object. This
includes open filhandles to temporary files, e.g. those of uploaded files. On
platforms supporting `O_TMPFILE`, these files are not visible in the
filesystem, but keep using up space in `TMPDIR` until werkzeug finally closes
the file handles when the next Request is being handled.

In some contexts, e.g. the upgrade platform, it can happen that there are
multiple workers that only handle rare requests that upload big files (multiple
GiB), kept open after the upload has finished:
```shell
lsof -nP | grep -E 'odoo\/tmp.*(deleted)' | grep -vE 'GeoIP'
python3 213853                odoo 13u REG 252,3 1064251 926275 /home/odoo/tmp/#926275 (deleted)
python3 213853 213865 python3 odoo 13u REG 252,3 1064251 926275 /home/odoo/tmp/#926275 (deleted)
```
This can pose problems, because often the filesystem on `TMPDIR` is not very
large and idle workers holding on to large files can increase the chance for
ENOSPC.

This patch changes the behavior such that the resources held by the werkzeug
`Request` object are being closed[^1] after the response has been sent out.
This also has the advantage that this work is done at potentially idle time
instead of within handling the next request.

[^1]: https://werkzeug.palletsprojects.com/en/2.0.x/wrappers/#werkzeug.wrappers.Request.close

closes odoo/odoo#150029

X-original-commit: a920ddca57e7bf9353611396f1c0b1060c8cb044
Signed-off-by: Julien Castiaux (juc) <juc@odoo.com>
2024-01-23 04:41:40 +00:00
Julien Castiaux 2a8dd60118 [FIX] core: HTTP 413 when restoring large backups
In #126914 a limit on the request size has been enforced, that limit is
by default 128MiB and can be configured via an ir.config_parameter. When
restoring a backup larger than 128MiB via the database manager, the
default limit was used and the request was cancelled with a Request
Entity Too Large (code 413) HTTP error.

It is now possible to define a default max content length per route,
that per-route limit takes over the `web.max_file_upload_size` ICP.

Moved the code from `get_http_params` to `pre_dispatch` to better align
with the httpocalypse new http stack.

Fixes: #144144
opw-3643475

closes odoo/odoo#147506

Signed-off-by: Xavier Dollé (xdo) <xdo@odoo.com>
2024-01-04 12:01:59 +00:00
Julien Castiaux 3f8296a16e [FIX] core: set Content-Security-Policy on static
The Content-Security-Policy[^1] http header was only set on the response
generated by controllers but it was missing from the `/<module>/static/`
route.

It is not strictly necessary to set that header on the responses comming
from that routes as it is not possible to add new static files or edit
existing ones via the interface (not even as admin). Only the developers
and system administrator can access those files.

It is also worth mentionning that using the Odoo internal web server to
deliver static files is suboptimal. Outside of a dev environment, those
files will typically be delivered via a web server[^2] and sysadmins
should configure their web server to set the CSP header on static images.

[^1]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
[^2]: https://www.odoo.com/documentation/master/administration/install/deploy.html#serving-static-files-and-attachments

closes odoo/odoo#146591

X-original-commit: 55e09d504df9bd134afb6e0b38f03457b5c71e8e
Related: odoo/documentation#6953
Signed-off-by: Julien Castiaux (juc) <juc@odoo.com>
2023-12-18 23:32:01 +00:00
Antoine Vandevenne (anv) 1fb7571afb [FIX] *: retarget documentation links to 17.0
closes odoo/odoo#141406

Related: odoo/enterprise#50357
Signed-off-by: Antoine Vandevenne (anv) <anv@odoo.com>
2023-11-07 23:57:41 +00:00
Christophe Monniez f194271823 [FIX] http: comply with rfc6265
Werkzeug changed the behavior of url_quote in
pallets/werkzeug@babfc93b38

Which appeared in Werkzeug 2.2.2 used in Debian Bookworm.

This change broke at least the export feature in Odoo. In summary,
the character set specified by [RFC5987] is more restricted than
that of [RFC3986]. So url_quote now allows invalid characters.

For example, a filename like `Journal Entry (account.move).xlsx`
leads to a crash of the client with Werkzeug 2.2.2.

url_quote is not really made to conform to [RFC6266] but we did not
find any [RFC6266] escaping tool in the standard library. This
commit explicitly specify as unsafe this list of chars.

[RFC6266]: https://datatracker.ietf.org/doc/html/rfc6266/ [RFC5987]:
https://datatracker.ietf.org/doc/html/rfc5987#section-3.2 [RFC3986]:
https://datatracker.ietf.org/doc/html/rfc3986/ [RFC2616]:
https://datatracker.ietf.org/doc/html/rfc2616#section-2

closes odoo/odoo#139483

X-original-commit: d145cb57eaf39fbe7612d4a79f209fc191828a8b
Signed-off-by: Julien Castiaux (juc) <juc@odoo.com>
Signed-off-by: Christophe Monniez (moc) <moc@odoo.com>
2023-10-24 13:55:28 +00:00
Abdelouahab (abla)andJulien Castiaux 5dd1efe3d8 [FIX] auth_oauth: missing user in signin endpoint
Install auth_oauth and via the /web/login, click the "Log in using
Odoo.com" button. You are redirected on odoo.com which ask you for your
odoo.com login and password. When the login form on odoo.com is
submited, you are redirected back on your local database.

The problem is that, in case a new account was created on-the-fly, then
the login fails with a cryptic error. The actual error is that
`request.env.user._is_internal` fails because `user` is an empty
recordset where it should had been the just-authenticated user.

The problem is an inconsistent transaction state between the cursor of
the request, the cursor used with `auth_oauth` (which created a new
user) and the cursor used with `authenticate` (which authenticated the
new user). Yes, there are 3 cursors. The newly created user just isn't
present in the transaction of the request's cursor.

Here is the lifetime of the 3 cursors:

* request.env.cr, it begins when the http request enters Odoo, it is
  commited when a http response exits Odoo.
* /auth_oauth/signin, it begins roughly at the beginning of the
  controller, it is commited once after the user is created (so before
  the authenticate transaction begins but AFTER the request transaction
  begun), it is commited again when the controller exits.
* authenticate, begins when authenticate is called, is commited when it
  returns.

Because the request transaction started before, it cannot access user
created by /auth_oauth/signin.

Because the route is `auth='none'`, if system administrators append the
`auth_oauth` module via `--load` (cli) or `server_wide_modules` (odoorc)
then the controller can be accessed without database. This is the reason
for the explicit registry/cursor/environment inside this controller, we
needed to make sure we are connected to a database, we cannot rely on
request.

The new approach used in this work is to benefit from `ensure_db()`, the
function that is used by various web `auth='none'` controllers such as
/web and /web/login. It makes sure that the database we want to connect
to is already present on the request, otherwise it repeats the request
but this time connecting it to the database. Using this approach we can
have a `auth='none'` controller whose request.env is guaranteed to be
connected on the right database. We can avoid to create explicit new
registry/cursor/environment within the controller and just use request's
ones.

Because the /auth_oauth/signin controller now simply use the request
transaction, the above point:

> Because the request transaction started before, it cannot access user
> created by /auth_oauth/signin.

just doesn't stand anymore as the user is created within the same
transaction. The extra `cr.commit()` must still be present for
`authenticate` to see the newly created user.

opw-3421701

closes odoo/odoo#138051

X-original-commit: e165568f9795af213c8467a7f17948ed781b0799
Signed-off-by: Olivier Dony (odo) <odo@odoo.com>
Co-authored-by: Julien Castiaux <juc@odoo.com>
2023-10-10 01:59:48 +00:00
Xavier Morel beb0c25a94 [IMP] core, bus: future werkzeug compatibility fixes
- `Response.charset` is deprecated since 2.3, `Response.set_cookie`
  accesses the currently-extent internal `_charset` directly until
  this too gets removed in Werkzeug 3.0. Add a `_charset` to
  `FutureResponse` so this does not crash.
- Bytes response headers are deprecated since 2.3, and will get
  removed in 3.0, passing bytes in websocket is completely unnecessary
  happenstance which is trivially fixed.

closes odoo/odoo#137145

X-original-commit: 66e3040d4ad9e03aabefebd4799f699ad30fca45
Signed-off-by: Julien Castiaux (juc) <juc@odoo.com>
Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
2023-10-02 06:54:39 +00:00
std-odoo 91979b0fcf [IMP] http_routing, website_slides: redirect the user on /slides on AccessError
Purpose
=======
When a user tries to reach a course, an AccessError can occur when we
unslug the URL. Instead of the traditional error page, we want to
redirect the users to /slides, and the error will be displayed there.

Task-3477630

closes odoo/odoo#135926

Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
2023-09-25 09:38:33 +00:00
Julien Castiaux cc5a14b6a9 [FIX] core: prevent upload of large files
The limit was only enforced by the front-end, meaning that anybody could
forge a request with a huge file and get it processed by Odoo. According
to the documentation of werkzeug[^1], such limit should be enforced by
the server server instead of the wsgi application. It is the case for
Odoo Online but on-premise customers might not configure their servers.

The `web.max_file_upload_size` system paramter is now enforced upon
parsing the content of the request. It defaults at 128 MiB which is
enough for most documents and images. We do not want to host large
files (e.g. videos) in the Odoo filestore.

[^1]: https://werkzeug.palletsprojects.com/en/2.0.x/request_data/

Fixes: #124646
Part-of: odoo/odoo#126914
2023-08-11 14:32:00 +02:00
Xavier-Do 595aa24843 [IMP] registry: multiple ormcache
One of the main issue with ormcache is that the invalidation clears
everything, meaning that some value, slow to compute but with a long
lifetime, can be removed from the cache because an easy to invalidate
value is cleared, like after writting or creating a product has an
example.

Most example in the code will try to invalidate the cache of the models
doing something like `env['ir.qweb'].clear_caches()` but it is
finally equivalent to `env.registry.clear_cache()`, and cross worker.

The idea is to have multiple cache, maybe with specific sizes for a
specific purpose.

Having one per model is maybe a bad idea because it will be difficult
to size the LRU correcly, and it is too dynamic. Checking invalidation
may be expensive.

The proposed solution is closed allow a limited number of named caches,
using onse sequence per cache. This is actually close to the
cache_longterm.

We want to discourage using a specific cache for one use case in
the buisness code. Adding a cache shouldn't be something easy, doable
in stable.

Note that we could also change the invalisation mecanism using an
insert only table. We an check the sequence of this table, but also
fetch all invalidation messages.
Another possible improvement, especially if we have more than x cache is
to have a global sequence, checking signaling would mean to check the
main sequence, and only the other ones if the main one changed.

Note that this poc is inspired from the long term cache but not all
use case where applie yet.

Part-of: odoo/odoo#119813
2023-07-18 11:42:26 +02:00
Benoit Socias d1292a96a6 [IMP] base,*: support image/webp image format
*: mail, mrp, test_website, web, web_editor

Before this commit '.webp' images could not be used in odoo.

After this commit '.webp' images can be uploaded to odoo.
- can be used in image field
- can be used in HTML field image
- can be used in mails and website
- can be transformed (shape mask, filter effect, crop, rotate, resize,
  adjust quality)

task-2774352

Part-of: odoo/odoo#85494
2023-07-15 05:10:45 +02:00
Julien Castiaux 7d1959febc [FIX] core: reset current_thread.dbname/uid between requests
In [HTTPocalypse] the `odoo/service/wsgi_server.py` file has been
removed and its features has been spread to other files. One of the
feature was reseting those few thread-local variables[^1] before
processing any new request:

    if hasattr(threading.current_thread(), 'uid'):
        del threading.current_thread().uid
    if hasattr(threading.current_thread(), 'dbname'):
        del threading.current_thread().dbname
    if hasattr(threading.current_thread(), 'url'):
        del threading.current_thread().url

In [HTTPocalypse] the `url`[^2] is correctly set at its definitive value
at the beginning of the request so there is no need to delete it before
processing.

On the other hand, `dbname`[^3][^4] and `uid`[^5] are only set when the
request is processed by `_serve_db`, i.e. that the user is connected to
a database already. Those values weren't reset at the begining of the
next request so in case that next request was processed by `_serve_nodb`
or `_serve_static`, the dbname and uid of the previous request would
still be present.

This commit restores both `del uid` and `del dbname` at the beginning of
the http stack, before the request is processed.

[HTTPocalypse]: odoo/odoo#78857
[^1]: https://github.com/odoo/odoo/blob/a1361d6629a829fd622f2a1a1b3e5b025050eaf9/odoo/service/wsgi_server.py#L80-L85
[^2]: https://github.com/odoo/odoo/blob/0e629cd2a1fc3623b579a38ae534fbdfae9b38a3/odoo/http.py#L1987
[^3]: https://github.com/odoo/odoo/blob/0e629cd2a1fc3623b579a38ae534fbdfae9b38a3/odoo/http.py#L1563
[^4]: https://github.com/odoo/odoo/blob/0e629cd2a1fc3623b579a38ae534fbdfae9b38a3/odoo/modules/registry.py#L70
[^5]: https://github.com/odoo/odoo/blob/0e629cd2a1fc3623b579a38ae534fbdfae9b38a3/odoo/http.py#L1582

closes odoo/odoo#125205

X-original-commit: c327c4586243430751100c8980169ae067383b1c
Signed-off-by: Julien Castiaux (juc) <juc@odoo.com>
2023-06-15 19:59:13 +02:00
Julien Castiaux ade2715910 [FIX] core: jsonrpc sends 400 for non json request
Send a request to any json-rpc route with a HTTP header
`Content-Type: application/json-rpc` but send non-json or
non-jsonrpc data in the body. The application crashes with
a 500 Internal Server Error instead of a 400 Bad Request one

closes odoo/odoo#124571

Closes: #122048
X-original-commit: a1b83b07996f3fe4744474ff4023a3a2ce17ac7f
Signed-off-by: Julien Castiaux (juc) <juc@odoo.com>
2023-06-11 00:34:31 +02:00
Olivier Dony cea9150fb7 [FIX] http: make session lifetime consistent and configurable
Before 16.0 and https://github.com/odoo/odoo/pull/78857 the session
cookie duration was set to 3 months, but the server-side garbage
collection of inactive session was reaping them after 7 days of
inactivity. The cookie lifetime was essentially superseded by the
server-side GC.

After https://github.com/odoo/odoo/pull/78857 these limits were made
consistent with each other, but the lifetime value was kept at 3 months,
which is a bit too long as a default.

This commit changes the default SESSION_LIFETIME back to 7 days for both
limits.

In addition, since the server-side GC is now implemented by a
database-specific cron job, this commit introduces an optional system
parameter `sessions.max_inactivity_seconds` that can be set to override
the default server-side GC threshold, to make it shorter.

Note 1: the ICP does not modify the cookie lifetime which will remain set
to the default 7 days. This means normal browser sessions won't stay
alive for longer than 7 days of inactivity. So `sessions.max_inactivity_seconds`
can't be effectively set to a longer expiration time.
This seems like a reasonably safe default.

Note 2: the session GC happens during the execution of the autovacuum
cron job ("Base: Auto-vacuum internal data") which is scheduled once per
day by default. When setting a small `sessions.max_inactivity_seconds`
value, it may be necessary to increase the frequency of that cron job
accordingly.

closes odoo/odoo#122964

X-original-commit: 05ff9a2db32c2fb1afa107ac005423218f452290
Signed-off-by: Julien Castiaux (juc) <juc@odoo.com>
2023-05-31 15:54:05 +02:00
Jeremy Kersten a89af09120 [FIX] http_routing, utm, website: set expiration date on some cookies
The 'frontend_lang' cookie is used to 'cache' the user's preferred lang.
We want to make sure that this language preference is preserved for a
longer period of time than just the life of the browser. This means that
even if you quit your browser and come back into the year, your
preferred language will be used, until you choose to remove your cookies.

The 'utm_*' cookies are used to 'track' where you are coming from on the
instance. The purpose of these cookies is to know the tracking value
to improve the overall user experience or compute the profitability of
some campaigns. Now we keep these cookies for 1 month.

closes odoo/odoo#122573

X-original-commit: 058e0abcf621796bf23d8dcaaf3b2297f632b5fd
Signed-off-by: Romain Derie (rde) <rde@odoo.com>
Signed-off-by: Jérémy Kersten <jke@odoo.com>
2023-05-30 13:03:25 +02:00
Julien Castiaux c207b40b51 [FIX] base: reniew request registry after uninstall
Install and then uninstall the utm module via the web client, you get a
traceback because the ir.http override of the utm module is still
present in the registry altought the module is not installed anymore.

The problem affects all modules that override the _post_dispatch method
of ir.http, it is not limited to UTM.

The problem is that, after the uninstallation, a new registry (without
the uninstalled modules) is created but the old registry was still used
by the HTTP stack.

closes odoo/odoo#122519

Closes: #121755
X-original-commit: 979844600a0bc5ea8b63cf4d58c7aba5133e82f3
Signed-off-by: Julien Castiaux (juc) <juc@odoo.com>
2023-05-25 19:16:40 +02:00
Denis Ledoux 58ea5e7b43 [IMP] http.py: do not inject context by default in JSON routes
Before this revision, when you pass `context` in the arguments
of a JSON routes, this one gets automatically injected
in the environment context.

This is not the case for regular HTTP routes.

It makes sense to propagate the context for the JSONRPC protocol,
JSON routes used by the backend, such as `call_kw`,
but it doesn't make sense to pass this context automatically
for any other kind of routes, such as front-end routes
or routes used by custom Javascript widgets.

This change brings a more unified behavior for routes
of types HTTP and JSON.
In addition, most developers were not aware of this "feautre",
that passing `context` in the arguments of a JSON route leaded
to the injection of this context in the environment context.
This is actually reflected by the diff size this changes required,
only a dozens of routes needed to be adapted, to manually
add the context in their route arguments and to inject it
in their environment context.

closes odoo/odoo#121726

X-original-commit: a7a5655631e6d5b05fd2ba3d0c80617aae6d9cfe
Related: odoo/enterprise#41229
Signed-off-by: Denis Ledoux (dle) <dle@odoo.com>
2023-05-22 11:43:04 +02:00
Xavier-Do f5797774c6 [FIX] profiling, base: non default in session
The current profiler will add default value in the user session
using disk space without valid reason.

This commit makes those parameters optional in the session.

closes odoo/odoo#118762

X-original-commit: eb3bf03b119105f56806d7460fccd4c0133deaab
Signed-off-by: Jérémy Kersten <jke@odoo.com>
Signed-off-by: Xavier Dollé (xdo) <xdo@odoo.com>
2023-04-17 14:14:30 +02:00
lejeune quentin 1981697e02 [FIX] core: try import geoip2 for iot
The geoip2 module is not installed in the iot so this one cannot launch Odoo.
This module is not used by the iot so it is not necessary
to install the module in the iot and therefore to make a new build

From this commit c59750d824

closes odoo/odoo#118240

X-original-commit: 5f251f7c95adf9b67ef1c81ee0084b0b5c396cc3
Signed-off-by: Quentin Lejeune (qle) <qle@odoo.com>
2023-04-13 09:31:26 +02:00
Denis Ledoux 536e670f8c [FIX] http: ensure values of session are serializable when stored
closes odoo/odoo#86015
Signed-off-by: Julien Castiaux <juc@odoo.com>
2023-02-07 09:12:32 +01:00
Xavier Morel e37109c8c3 [REM] core: support for werkzeug interactive debugger
With the special support for postmortem debugging removed, the
likelihood of needing / wanting the werkzeug remote debugger seems
even more remote (as it works in strictly less situations, only for
frontend non-json requests).

So remove that as well.

closes odoo/odoo#115176

X-original-commit: a2022783b652299155c460294c00dbced9b619ac
Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
2023-03-14 13:30:19 +01:00
Julien Castiaux 93b684d3c7 [FIX] base: lang should fallback on english instead of arab
Install a database with many langs, arab, french, english, ... Keep
english as the default lang. Start a shell and validate a sale-order
using the superuser. On the web client, the sale order has been
validated in arab instead of in english.

In 16.0 the `context_get` method was changed to ensure there was always
a lang set in the returned context. It used the following fallback
order: context > request. The solution was partial because in case there
was no request to extract a lang from, no lang was set on the context.

In a recent 16.0 fix (f2523c4a), the mechanism was changed to fix the
previous problem. The fallback order became: context > request > first
installed lang. This solution is sub-optimal because the first installed
lang isn't always the best pick. e.g. when you have a mostly english
company but that arab is installed for some website pages, arab is
selected instead of english (the langs are alphabetically sorted)

In this work, the fallback order is changed once again:

  1. The lang set on the user's profile if activated
  2. The best lang extracted from the user's browser if activated
  3. (new) The lang of the user's current company if activated
  4. (new) English if activated
  5. The first lang (ordered by ISO code) if any
  6. English

The 3rd should cover most of ill-cases. For the 4th step, we assume that
english is prioritaty to other installed langs when no lang standout.

closes odoo/odoo#113186

X-original-commit: 03134bf7cb1e3d63f3be435fbc734e6198ca029b
Signed-off-by: Julien Castiaux (juc) <juc@odoo.com>
2023-02-21 17:02:06 +01:00
Xavier Morel d1a0ac989f [FW][FIX] core: misleading handle_error docstring
`handle_error`'s docstring states that it returns a `Response`, but in most case `HttpDispatcher.handle_error` returns an `HTTPException`.

After discussion, the implementation is correct, `handle_error` should be documented to return a WSGI Application (a callable taking an `environ` and a `start_response` callable) instead.

closes odoo/odoo#112889

Forward-port-of: odoo/odoo#112690
Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
2023-02-16 16:58:49 +01:00
Benoit Socias fb9efde4f3 [FIX] *: replace werkzeug's Response by odoo's Response
*: base, http_routing, mass_mailing, web, web_editor, website_slides

In some situations `werkzeug.wrappers.Response` are used instead of
`odoo.http.Reponse` that extends it.
This is a problem because since [1] the calls to `set_cookie` expect it
to accept the `cookie_type` parameter, which is not the case in the base
werkzeug implementation.

This commit replaces the `werkzeug.wrappers.Response` by
`odoo.http.Response`.

[1]: https://github.com/odoo/odoo/commit/2cbda6c98ee947cea1d06c09880eee8c758304a8

closes odoo/odoo#112827

X-original-commit: 28da08292b7028575e628c5ad846fc05d30498f2
Signed-off-by: Julien Castiaux (juc) <juc@odoo.com>
2023-02-16 09:01:07 +01:00
Florian VranckxandJulien Castiaux 8c30699f2a [FIX] http: no rotate sid for unidentified user
This commit fixes a change in behavior between 15.2 and 15.3.

Previously, if an unidentified user tried to reach a route that had auth='user', it would simply redirect to the login page.

Currently, it redirects and invalidates the session_id.

This is an issue in the latest version of master after this PR https://github.com/odoo/enterprise/pull/36521
This commit changes the route of service-worker.js to auth='user'.

This route is called on the login page, which rotates the sid and therefore invalidates the csrf token. Making it impossible for a user to log in.

This is a race condition, meaning it would only appear if the user stayed on the login page for a few seconds, hence why the automated testing did not block the commit.

closes odoo/odoo#112239

X-original-commit: d5d80d172616afe02bd41934930ea18dc273c739
Signed-off-by: Julien Castiaux (juc) <juc@odoo.com>
Co-authored-by: Julien Castiaux <juc@odoo.com>
2023-02-08 21:45:53 +01:00
Xavier Morel b951cb4422 [IMP] core: mark all responses as nosniff
Doesn't seem like it could hurt, and should only make CORB more
reliable by avoiding sniffing. Worst case scenario requires fixing a
few mimetypes, but aside from CORB it looks like modern (non-IE)
browsers only try to guess document mimetypes in very limited
contexts.

closes odoo/odoo#107957

Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
2023-01-31 12:55:46 +01:00
Victor Feyens 1a1ce16265 [IMP] core,*: check routes decorators
When overriding an existing controller route, developers can
easily c/p the route definition and call super() in the overridden method
when the route attributes are automatically deducted by odoo from the parent route.

Removing those redefined attributes simplifies the routes definition,
clearly highlighting what's changed by the override.
Also reduces unexpected behavior when modifying the base route without
noticing/considering the redefined attributes in a overridden route,
which overrides the changes made to the base route when the sub-module is installed.

This commit adds a test to catch routes attributes redefinition, and clean existing routes.

closes odoo/odoo#108512

Related: odoo/enterprise#35176
Signed-off-by: Victor Feyens (vfe) <vfe@odoo.com>
2023-01-16 19:45:57 +01:00
Julien Castiaux bf4c5015ce [IMP] core: make some cookies expire on logout
Some cookies were left around even when the user logged out

The next user should log into the default company instead of the company
of the last user.

task-3077421

closes odoo/odoo#108998

Related: odoo/enterprise#35685
Signed-off-by: Julien Castiaux <juc@odoo.com>
2023-01-13 16:27:35 +01:00
Julien Castiaux 145b44fa57 [FIX] core: cannot read country geoip on city db
Start odoo-bin shell with `--geoip_country_db='/i/dont/exist'` and type
the following code:

    from odoo.http import GeoIP
    GeoIP('8.8.8.8').country_code

TypeError: The country method cannot be used with the GeoLite2-City
database

There are two "things" here: the raw country and city entries in the
databases and the rich python country and city objects returned when an
entry was found and parsed.

Then there are two "readers", a reader capable of reading and parsing
entries from the city database and **another** reader for the country.
The two readers are **only** compatible with their specific database:
the city reader cannot read or parse information from the country
database and vis-versa. That's the bug reported by the TypeError that
this commit fixes.

But, the two python instances created from parsing the entries of
respecting databases with respecting parsers **do** share a same API:
the city record class inherits from the country record class.

    # Valid
    city_db = geoip2.database.Reader(config['geoip_city_db'])
    city_record = city_db.city('8.8.8.8')
    city_record.country.name  # works, a city record also
                              # holds country attributes

    # Invalid
    city_db = geoip2.database.Reader(config['geoip_city_db'])
    country_record = city_db.country('8.8.8.8')  # TypeError

closes odoo/odoo#109095

Signed-off-by: Julien Castiaux <juc@odoo.com>
2023-01-05 11:35:55 +01:00
Julien Castiaux 3d1f486bcc [IMP] *: update modules to use the new geoip API
request.geoip is no more a dictionnary cached in the session. It is now
a full blown object with lazy and smart geolocalisation capabilities.

Among other things, the previous dictionnary API is now deprecated. The
changes are:

* `request.geoip['country_name']` -> `request.geoip.country_name`
* `request.geoip['country_code']` -> `request.geoip.country_code`
* `request.geoip['city']` -> `request.geoip.city.name`
* `request.geoip['latitude']` -> `request.geoip.location.latitude`
* `request.geoip['longitude']` -> `request.geoip.location.longitude`
* `request.geoip['region']` -> `(request.geoip.subdivisions[0].iso_code if request.geoip.subdivisions else None)`
* `request.geoip['time_zone']` -> `request.geoip.location.time_zone`

It is safe to access all the attributes. Doing `request.geoip.city.name`
when the geolocalization failed (missing db, invalid address, ...)
evaluates to None. It does not raise an AttributeError.

Task: 2848206
Part-of: odoo/odoo#91337
2023-01-03 13:16:02 +01:00
Julien Castiaux c59750d824 [IMP] core: smarter geoip
Maxmind offers multiple ip-geolocalization databases, historically we
have been using the City database which contains records on a
city-basis. Many years later it turns out we are primary using geoip to
know the country of the user. Geolocalization in the City database is
considered slow by our standard and we have been clever in order not to
geolocate each request by saving the info in the session.

On the other hand, the Country database that is offered by Maxmind is
much more lightweight and geoip using that country is considered a fast
operation by our standard.

In this work we make Odoo compatible with both the City and the Country
databases. Using multiple database at the same time, we can be smart and
only query each of the two on-demand. If a user ask for its country,
we'll use the fast Country db. If a user ask for its city/timezone we'll
use the slower City db.

By default it loads both database from the `/usr/share/GeoIP/` folder,
respectively the files `GeoLite2-City.mmdb` and `GeoLite2-Country.mmdb`,
you can provide alternative paths using the `--geoip-city-db` and
`--geoip-country-db` CLI options.

In the same mindset as #86015, geoip is still lazy. It is done on-demand
and the result is cached on the current request. The different with the
related PR is that as we know consider geoip to be fast, we no longer
cache the result in the session.

Task: 2848206
Part-of: odoo/odoo#91337
2023-01-03 13:16:02 +01:00
Julien Castiaux 5502313853 [FIX] web, *: multi-db /web/session/authenticate
*: base_setup, hr_timesheet, mail, partner_autocomplete, web_tour

Start odoo without -d and with a --dbfilter that allows multiple
databases. Via JSON-RPC access the /web/session/authenticate route
providing a non-filtered database and valid credentials. Traceback,
`request.env` is None.

Since httpocalypse the initialization of the ORM (cursor, registry,
environment) is greedy. It means that the connection to the database is
established very early during the request routing or skip altogether in
case no dbname was known at that time. This contrast with prepocalypse
where the various ORM thingies were lazily setup the first time they
were accessed.

This changement has an important implication regarding authentication.

In prepocalypse, thanks to the lazy approache, a cursor/registry/env
would be setup on the database you just login upon using the
`request.env` for the first time. This was very nice in this regard but
had other problems.

Since httpocalypse such operation is no more possible. Devs must
initialize and use their own cursor/registry/env in case they
authenticate on another database than the one `request.cr` is (maybe)
connected to.

The `/web/session/authenticate` controller is an example of such case.
It crates its own cr/registry/environment after authentication. The
problem the controller uses `ir.http.session_info` and that not all
overrides were updated to use `self.env` (=the env created in the web
controller) instead of `request.env` (=the missing env of the request).

closes odoo/odoo#108063

X-original-commit: 7b9bd9d37731fae724dc5d91da656dab70aa9ad4
Related: odoo/enterprise#35012
Signed-off-by: Julien Castiaux <juc@odoo.com>
2022-12-15 15:45:07 +01:00
Vincent Schippefilt 25c6c15a06 [IMP] base,*: remove __last_update from all models
The main goal of this commit is to reduce the size of the registry by
removing the (almost) useless __last_update field.

Statistics # of fields with all modules installed:
before 30184 fields, 1299x last_update (4.30%)

Before this commit, the computed field __last_update was added on every model.
The idea behind this field was to have a computed field that had either
the write_date or the create_date if the write_date was empty. However,
the write_date is always written, even on creation, making it useless
to have the computed field __last_update

After this update, we completely remove from BaseModel:
* __last_update
* CONCURRENCY_CHECK_FIELD that was always defined as "__last_update"
* _compute_concurrency_field that was the compute function for __last_update

closes odoo/odoo#105739

Task-id: 3062140 (part of 3062137 improve registry load time)
Related: odoo/upgrade#4038
Related: odoo/enterprise#33939
Signed-off-by: Raphael Collet <rco@odoo.com>
2022-12-07 18:29:01 +01:00
Julien Castiaux 05a3ad3159 [FIX] core: skip useless controllers in routingmap
The routing-map is a mapping that maps HTTP verbs and paths to python
controller methods (endpoints), e.g. it maps `GET /web/health/` to
`/web:Home.health`. The `_generate_routing_map` function is the function
responsible to generating the werkzeug routing-map in regard to the
controller inheritance mechanism. The mechanism makes it possible to
override an endpoint is various odoo modules to enrich it with new
features, e.g. `/web/login` is overriden in website to change the visual
of the page.

Implementation-wise, the informations from each `@route` decorator must
be merged with the other `@route` info for each endpoint override. When
a method is not overriden in a controller, there is not new info and
that controller should be skipped for that method.

The previous implementation attempted to skip such method using the
following idiom:

    if not hasattr(controller, method_name):
        continue

That idiom doesn't work as `hasattr` will perform a lookup on the full
controller's MRO instead of a lookup only on controller's own methods
and attributes. The controller's own methods and attributes are
actually found in `controller.__dict__`.

closes odoo/odoo#107064

X-original-commit: 42f52d26b6a81a68f1fe28e28971f0e7f4c97d11
Signed-off-by: Julien Castiaux <juc@odoo.com>
2022-12-05 16:06:49 +01:00
Julien Castiaux d9cb0671a6 [FIX] core: converse existing context in JSON-RPC
Define the following controller:

    @route('/get-context', auth='public', type='json', website=True):
        return request.env.context

Access that route via JSON-RPC providing a context:

    this._rpc({
        route: '/get-context',
        params: {
            context: {"a": 1}
        }
    })

Since httpocalypse, it replaces the context entirely instead of updating
it. i.e. before you would get along those lines:

    {"a": 1, "lang": "en_US", "tz": "Europe/Brussels", "uid": 2}

but since you get instead (and that's wrong):

    {"a": 1}

Note that after this commit, this merging-context behavior will be the
same whether or not website=True is defined (while it was only there for
website=True before).

Discovered via opw-2885948

X-original-commit: 7887cfc3f177d9e64f008aabb8bd710f54024dea
Part-of: odoo/odoo#107179
2022-12-05 14:04:00 +01:00
Jeremy Kersten 2c4556efd1 [IMP] http_routing: support redirect of double slash in middle of path
Move code to support only the redirect from url containing double '/' in
the middle of the path.
Keep same behavior than v15 and default Apache behavior.

domain.com//shop/product/1 -> 404
domain.com/shop//product/1 -> 301 -> /shop/product/1

opw-3063387

closes odoo/odoo#106137

X-original-commit: fdd6bf9942e2807b6d1460dba1ca5f61404c7b06
Signed-off-by: Julien Castiaux <juc@odoo.com>
Signed-off-by: Jérémy Kersten <jke@odoo.com>
2022-11-21 11:50:26 +01:00
Julien Castiaux ec4826ef5b [FIX] core: session logout after 16.0 migration
Create a 15.0 database with website, access the home page via your
browser. Stop the server and migrate the database to 16.0. Restart the
server with a `--dbfilter` that rejects the database you created and
refresh your browser. 500 Internal server error, attribute error:
the `request` object as no `session`.

An error could occurs after a migration to 16.0 due to the presence of
the `geoip` key in the session. `request.session.geoip` has been made a
deprecated alias to `request.geoip` between 15.0 and 16.0, see 04e9726.

Because the session was created before 16.0, the session dict does
contain a `geoip` key. Upon logging the session out, the session dict
is cleared. The default implementation of `clear()`[^1] inside of
`collections.abc.MutableMapping` can be summarized for our usecase to:

    for key in self:
        value = self[key]
        del self[key]

There is an extra `__getitem__` call due to `value = self[key]`, in the
case of the `geoip` key, it would access the alias. It is not possible
to accessing that alias inside of the `_get_dbname_and_session` method
of request as the session has not been set on `self` (the request) yet.

Yet inside of that method, we do `session.logout()` which `clear()` the
session which (wrongly) access the alias because `geoip` exists in the
internal dict (`'geoip' in self.keys()  # True`).

The solution has been to implement the `clear()` function ourself
instead of using the mixin of `MutableMapping`.

[^1]: https://github.com/python/cpython/blob/b43496c01a554cf41ae654a0379efae18609ad39/Lib/_collections_abc.py#L925-L931

closes odoo/odoo#105763

X-original-commit: b66e1ffa8e348eedf2de735babbc398290a8bffb
Signed-off-by: Julien Castiaux <juc@odoo.com>
2022-11-15 18:25:40 +01:00
Julien Castiaux 6b0e54ca4c [FIX] base, web: missing borrow_request() for db
The new `borrow_request()` function has been introduced to properly
separate the HTTP layer from the RPC layer. We forgot to protect some
RPC endpoints, mainly inside of `odoo.addons.web.controllers.database`.

This commit moves `odoo.service.dispatch_rpc` to `odoo.http` as we only
permorm RPC from the controllers and that we don't want to import
`borrow_request` inside of `odoo.service` (circular import).

X-original-commit: d0ee8615d8820e02232989c50a6e6ffbc66a9266
Part-of: odoo/odoo#105710
2022-11-15 00:17:59 +01:00
Nils Hamerlinck b92082d494 [IMP] http: bind http.request before calling _get_session_and_dbname() on new request
closes odoo/odoo#104605

X-original-commit: a4a71d34ffa60154f2764e1dfa5b0d688644603c
Signed-off-by: Julien Castiaux <juc@odoo.com>
2022-10-31 13:39:53 +01:00
Julien Castiaux 1e57ad2076 [FIX] base: no request.is_frontend attribute in RPC
Install website then render a qweb template via xmlrpc, attribute error:
`request` has no `is_frontend` attribute.

Inside the qweb's `_prepare_environment` override of the http_routing
module (a dependency of website) is the following code snippet:

    if (not irQweb.env.context.get('minimal_qcontext') and
            request and request.is_frontend):
        return irQweb._prepare_frontend_environment(values)

The conditionnal is about injecting extra informations in the qweb's
context in case we are serving a frontend request. By default, there is
no `is_frontend` attribute on the request object. That attribute is set
by the ir.http's `_match` override of the http_routing module (a website
dependency): it is set True when we don't match any endpoint or that we
match an endpoint that is `website=True`, it is set False otherwise.

The ir.http's `_match` method is called whenever we are serving a http
request whoose session is bound to a specific database. i.e. when there
is a valid database saved in the request's session. When there is not
database in the request's session (or that it is invalid) the matched
endpoint is directly called without going throught ir.http. Most
endpoints are only accessible via ir.http.

The two `/xmlrpc` and `/jsonrpc` endpoints are examples of endpoint that
do not require an established database connection to work. They perform
the request authentication and database connection themselves. It is
possible to call those two endpoints with no database saved in the
session, thus it is possible to call those two endpoints without going
throught ir.http. This is expected.

The two endpoints's duty is to execute public model methods and return
the xml/json serialized result. To do so, a registry is loaded on the
database with all the installed modules, including http_routing.

We fall in a situation where (1) there is a request, (2) we are using a
registry where http_routing is loaded, (3) there is no `is_frontend`
attribute on `request` as we didn't serve the endpoint via ir.http. This
situation is illegale.

To solve the problem, instead of working on the `if request.is_frontend`
bit of the above conditional, we decided to work on the `if request`
bit. ISO-model wise, RPC is an extra 8th layer built on top of HTTP.
HTTP is merely a transparent transport between a RPC client and a RPC
server, any other request-response capable procotol could fit. The
method executed via RPC must be independant from the usage of HTTP as
mean of transportation thus it should not be capable of using the
current request.

The proposed change is to temporary un-expose the current request from
the local-stack during the execution of the RPC method. This fixes the
problem as the code now run like it was executed from the shell or from
a cron. The other benefit is that the pattern used inside the condition:
`if request and request.is_frontend` doesn't need to change.

X-original-commit: f28863bfdaac3f644fcb274c856e0770783ed75c
Part-of: odoo/odoo#104567
2022-10-29 04:51:01 +02:00
Xavier Morel 5d47883262 [REM] core: dead code for JSONRPC HTTP status
When `make_json_response` was added in
034d01b2f3 and `_response` was updated
to use it, the `http_status` extracted from the error object was
removed.

But it's still set by `handle_error` and a local is defined for
it. Drop that.

closes odoo/odoo#104507

X-original-commit: b0ea1cd6a2628a6acd1e2d79cc862976c63b1565
Signed-off-by: Julien Castiaux <juc@odoo.com>
2022-10-28 17:18:10 +02:00
Nicolas Martinelli 5c80ca4d1d [FIX] http: nodb fallback in case of connection error
When calling a `auth=none` route with a non-existing DB name, the server
redirects to `/web/database/selector`. Such a route can be called
without database, therefore it is expected to work if the database
doesn't exist as well.

Before the HTTP refactoring, the fallback was `_dispatch_nodb` [1]. We
roll back to the same behavior since there is no good reason to redirect
to `/web/database/selector`.

Commits 4b330f3173 and de4e67dcc529163 are reintroduced as well.

[1] https://github.com/odoo/odoo/blob/b3199e949ae70307e8181ba0d6237871d6d07af7/odoo/http.py#L1529

closes odoo/odoo#104174

X-original-commit: 1565e85890f1d14e067edd1c198c7199ab536307
Signed-off-by: Nicolas Martinelli (nim) <nim@odoo.com>
Signed-off-by: Olivier Dony <odo@odoo.com>
2022-10-26 16:07:31 +02:00
Victor Feyens 9ded78ede0 [IMP] core: docstring improvements
* clean and improve docstrings in orm
* fix typos found with codespell
* rely on the Environment class docstring instead of doc content (and
therefore move part of the doc inside the class docstring)

closes odoo/odoo#102969

X-original-commit: 8250cd4b210005d223a4cdb8afa4014425ca6fa3
Related: odoo/documentation#2803
Signed-off-by: Victor Feyens (vfe) <vfe@odoo.com>
2022-10-10 19:55:37 +02:00
Benoit Socias 720a8c9940 [IMP] base, web: unify the behavior of cookie setters
Both implementations of `set_cookie` in `http.py` and the one of
`setCookie` in `cookie_utils.js` are slightly different.

This commit unifies both Python implementations and replicates the same
behavior in JS:
- default the cookie type to "required"
- if setting that type is not allowed, delete that cookie if it was
previously set

task-2800976

closes odoo/odoo#102380

X-original-commit: fa8fdab2894607f1f89cdd195f62ec37af7b0342
Signed-off-by: Romain Derie (rde) <rde@odoo.com>
2022-10-06 17:00:33 +02:00
Jeremy KerstenandBenoit Socias 878351d840 [IMP] base, web, website, *: differentiate essential & optional cookies
*: im_livechat, survey, utm, website_crm_iap_reveal, website_forum,
   website_livechat, website_sale, website_sale_comparison

Before this commit all cookies were considered essential.

This commit makes some of them optional. It also makes it possible for
the website visitor to only accept the essential cookies.

task-2800976

X-original-commit: 9a8a9463289a7446e9be0ef62ff895feb37a4de4
Part-of: odoo/odoo#101845
Co-authored-by: Benoit Socias <bso@odoo.com>
2022-10-03 10:56:58 +02:00
Jeremy Kersten 5490fcc27f [FIX] http: convert DEFAULT_SESSION as a function get_default_session
This commit avoid to have a dict by reference that will be global.
Now get_default_session return a new dict each time for the context key.
From this way the session.context['lang'] is not shared between several
users on the same worker.

To reproduce the bug, restart the server with 2 workers, make request in
lang A on these 2 workers. DEFAULT_SESSION['context']['lang'] now is set
to this lang A.
Now, make request to an url without lang in path and without cookies and
withtout session, you should be redirected to lang B (preferred lang
from the request header) but you will be redirect to lang A due to the
dict session.context that is shared for the worker...
When we initialize the new Session, we get the wrong lang A as value for
context.lang, so we don't recompute the expected lang for the end user.

X-original-commit: 62179de74862210fe2a055d15b367b1850c24263

fwd-port of #100102

closes odoo/odoo#100910

X-original-commit: 42e46b2d89dde276f796b980f29e33cc216e7cb2
Signed-off-by: Jérémy Kersten <jke@odoo.com>
2022-09-23 09:21:38 +02:00
Gorash 5410b7c238 [IMP] base/web: XML templates are added into the asset bundles.
XML files are now declared in python module manifests. During the qweb
't-call-asset' directive, assetbundle will fetch the declared xml files,
apply the inheritance (t-inherit) and create a javascript service (for
eg: 'web.assets_backend.bundle.xml') which is added at the end of the
*.js mimifier file.

When the debug mode is activated, comments are added in the template
indicating which file the template comes from as well as the
inheritances applied to it.

****

JavaScript:

assets.js (module @web/core/assets) takes care of loading libraries,
javascripts and styles.
`loadJS(url)` (loads the javascript and returns a resolved promise when
the templates are also loaded via the '*.bundle.xml' service)
`loadCSS(url)` (loads the style a resolved promise when the file is
loaded)
`loadXML(xml, app=assets.defaultApp)` (load template into
application/owl, used by the `*.bundle.xml` services)
`getBundle(bundleName)` (get the bundle descriptor)
`loadBundle(desc)` (load the files and bundle from a descriptor)

templates (XML element content all owl templates)

A new `ready(serviceName)` method on boot.js lets you know when a
service is loaded are the require.

The xmlDependencies attribute no longer exists.

Python:

The xmls taken into account by assetbundle.py, applying `t-inherit`
inheritances and adding an `name_of_the_bundle.bundle.xml` service in
the generated JavaScript file.

****

Every manifest changes is into the next commit, except 'web_tour' in
this current commit as example.

Part-of: odoo/odoo#95500
2022-09-14 20:25:01 +02:00