mirror of
https://github.com/bringout/oca-server-auth.git
synced 2026-04-18 11:32:07 +02:00
Initial commit: OCA Server Auth packages (29 packages)
This commit is contained in:
commit
3ed80311c4
1325 changed files with 127292 additions and 0 deletions
|
|
@ -0,0 +1,144 @@
|
|||
# Copyright 2021 ACSONE SA/NV
|
||||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
|
||||
|
||||
import logging
|
||||
|
||||
from odoo import SUPERUSER_ID, api, models
|
||||
from odoo.http import request
|
||||
|
||||
from ..exceptions import (
|
||||
ConfigurationError,
|
||||
Unauthorized,
|
||||
UnauthorizedCompositeJwtError,
|
||||
UnauthorizedMissingAuthorizationHeader,
|
||||
UnauthorizedMissingCookie,
|
||||
UnauthorizedSessionMismatch,
|
||||
)
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class IrHttpJwt(models.AbstractModel):
|
||||
|
||||
_inherit = "ir.http"
|
||||
|
||||
@classmethod
|
||||
def _authenticate(cls, endpoint):
|
||||
"""Protect the _authenticate method.
|
||||
|
||||
This is to ensure that the _authenticate method is called
|
||||
in the correct conditions to invoke _auth_method_jwt below.
|
||||
When migrating, review this method carefully by reading the original
|
||||
_authenticate method and make sure the conditions have not changed.
|
||||
"""
|
||||
auth_method = endpoint.routing["auth"]
|
||||
if (
|
||||
auth_method in ("jwt", "public_or_jwt")
|
||||
or auth_method.startswith("jwt_")
|
||||
or auth_method.startswith("public_or_jwt_")
|
||||
):
|
||||
if request.session.uid:
|
||||
_logger.warning(
|
||||
'A route with auth="jwt" must not be used within a user session.'
|
||||
)
|
||||
raise UnauthorizedSessionMismatch()
|
||||
# Odoo calls _authenticate more than once (in v14? why?), so
|
||||
# on the second call we have a request uid and that is not an error
|
||||
# because _authenticate will not call _auth_method_jwt a second time.
|
||||
if request.uid and not hasattr(request, "jwt_payload"):
|
||||
_logger.error(
|
||||
"A route with auth='jwt' should not have a request.uid here."
|
||||
)
|
||||
raise UnauthorizedSessionMismatch()
|
||||
return super()._authenticate(endpoint)
|
||||
|
||||
@classmethod
|
||||
def _get_jwt_payload(cls, validator):
|
||||
"""Obtain and validate the JWT payload from the request authorization header or
|
||||
cookie."""
|
||||
try:
|
||||
token = cls._get_bearer_token()
|
||||
assert token
|
||||
return validator._decode(token)
|
||||
except UnauthorizedMissingAuthorizationHeader:
|
||||
if not validator.cookie_enabled:
|
||||
raise
|
||||
token = cls._get_cookie_token(validator.cookie_name)
|
||||
assert token
|
||||
return validator._decode(token, secret=validator._get_jwt_cookie_secret())
|
||||
|
||||
@classmethod
|
||||
def _auth_method_jwt(cls, validator_name=None):
|
||||
assert not request.uid
|
||||
assert not request.session.uid
|
||||
# # Use request cursor to allow partner creation strategy in validator
|
||||
env = api.Environment(request.cr, SUPERUSER_ID, {})
|
||||
validator = env["auth.jwt.validator"]._get_validator_by_name(validator_name)
|
||||
assert len(validator) == 1
|
||||
|
||||
payload = None
|
||||
exceptions = {}
|
||||
while validator:
|
||||
try:
|
||||
payload = cls._get_jwt_payload(validator)
|
||||
break
|
||||
except Unauthorized as e:
|
||||
exceptions[validator.name] = e
|
||||
validator = validator.next_validator_id
|
||||
|
||||
if not payload:
|
||||
if len(exceptions) == 1:
|
||||
raise list(exceptions.values())[0]
|
||||
raise UnauthorizedCompositeJwtError(exceptions)
|
||||
|
||||
if validator.cookie_enabled:
|
||||
if not validator.cookie_name:
|
||||
_logger.info("Cookie name not set for validator %s", validator.name)
|
||||
raise ConfigurationError()
|
||||
request.future_response.set_cookie(
|
||||
key=validator.cookie_name,
|
||||
value=validator._encode(
|
||||
payload,
|
||||
secret=validator._get_jwt_cookie_secret(),
|
||||
expire=validator.cookie_max_age,
|
||||
),
|
||||
max_age=validator.cookie_max_age,
|
||||
path=validator.cookie_path or "/",
|
||||
secure=validator.cookie_secure,
|
||||
httponly=True,
|
||||
)
|
||||
|
||||
uid = validator._get_and_check_uid(payload)
|
||||
assert uid
|
||||
partner_id = validator._get_and_check_partner_id(payload)
|
||||
request.update_env(user=uid)
|
||||
request.jwt_payload = payload
|
||||
request.jwt_partner_id = partner_id
|
||||
|
||||
@classmethod
|
||||
def _auth_method_public_or_jwt(cls, validator_name=None):
|
||||
if "HTTP_AUTHORIZATION" not in request.httprequest.environ:
|
||||
env = api.Environment(request.cr, SUPERUSER_ID, {})
|
||||
validator = env["auth.jwt.validator"]._get_validator_by_name(validator_name)
|
||||
assert len(validator) == 1
|
||||
if not validator.cookie_enabled or not request.httprequest.cookies.get(
|
||||
validator.cookie_name
|
||||
):
|
||||
return cls._auth_method_public()
|
||||
return cls._auth_method_jwt(validator_name)
|
||||
|
||||
@classmethod
|
||||
def _get_bearer_token(cls):
|
||||
# https://tools.ietf.org/html/rfc2617#section-3.2.2
|
||||
authorization = request.httprequest.environ.get("HTTP_AUTHORIZATION")
|
||||
return request.env["auth.jwt.validator"]._parse_bearer_authorization(
|
||||
authorization
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def _get_cookie_token(cls, cookie_name):
|
||||
token = request.httprequest.cookies.get(cookie_name)
|
||||
if not token:
|
||||
_logger.info("Missing cookie %s.", cookie_name)
|
||||
raise UnauthorizedMissingCookie()
|
||||
return token
|
||||
Loading…
Add table
Add a link
Reference in a new issue