mirror of
https://github.com/bringout/oca-technical.git
synced 2026-04-18 13:52:05 +02:00
Initial commit: OCA Technical packages (595 packages)
This commit is contained in:
commit
2cc02aac6e
24950 changed files with 2318079 additions and 0 deletions
|
|
@ -0,0 +1,209 @@
|
|||
# Copyright 2024 Akretion (http://www.akretion.com).
|
||||
# @author Sébastien BEAU <sebastien.beau@akretion.com>
|
||||
# @author Florian Mounier <florian.mounier@akretion.com>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from secrets import token_urlsafe
|
||||
|
||||
import jwt
|
||||
|
||||
from odoo import _, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
from odoo.addons.queue_job.delay import chain
|
||||
|
||||
|
||||
class AuthDirectory(models.Model):
|
||||
_name = "auth.directory"
|
||||
_description = "Auth Directory"
|
||||
_inherit = "server.env.mixin"
|
||||
|
||||
name = fields.Char(required=True)
|
||||
auth_partner_ids = fields.One2many("auth.partner", "directory_id", "Auth Partners")
|
||||
set_password_token_duration = fields.Integer(
|
||||
default=1440, help="In minute, default 1440 minutes => 24h", required=True
|
||||
)
|
||||
impersonating_token_duration = fields.Integer(
|
||||
default=60, help="In seconds, default 60 seconds", required=True
|
||||
)
|
||||
reset_password_template_id = fields.Many2one(
|
||||
"mail.template",
|
||||
"Mail Template Forget Password",
|
||||
required=True,
|
||||
default=lambda self: self.env.ref(
|
||||
"auth_partner.email_reset_password",
|
||||
raise_if_not_found=False,
|
||||
),
|
||||
)
|
||||
set_password_template_id = fields.Many2one(
|
||||
"mail.template",
|
||||
"Mail Template New Password",
|
||||
required=True,
|
||||
default=lambda self: self.env.ref(
|
||||
"auth_partner.email_set_password",
|
||||
raise_if_not_found=False,
|
||||
),
|
||||
)
|
||||
validate_email_template_id = fields.Many2one(
|
||||
"mail.template",
|
||||
"Mail Template Validate Email",
|
||||
required=True,
|
||||
default=lambda self: self.env.ref(
|
||||
"auth_partner.email_validate_email",
|
||||
raise_if_not_found=False,
|
||||
),
|
||||
)
|
||||
secret_key = fields.Char(
|
||||
groups="base.group_system",
|
||||
required=True,
|
||||
default=lambda self: self._generate_default_secret_key(),
|
||||
)
|
||||
count_partner = fields.Integer(compute="_compute_count_partner")
|
||||
|
||||
impersonating_user_ids = fields.Many2many(
|
||||
"res.users",
|
||||
"auth_directory_impersonating_user_rel",
|
||||
"directory_id",
|
||||
"user_id",
|
||||
string="Impersonating Users",
|
||||
help="These odoo users can impersonate any partner of this directory",
|
||||
default=lambda self: (
|
||||
self.env.ref("base.user_root") | self.env.ref("base.user_admin")
|
||||
).ids,
|
||||
groups="auth_partner.group_auth_partner_manager",
|
||||
)
|
||||
force_verified_email = fields.Boolean(
|
||||
help="If checked, email must be verified to be able to log in"
|
||||
)
|
||||
|
||||
def _generate_default_secret_key(self):
|
||||
# generate random ~64 chars secret key
|
||||
return token_urlsafe(64)
|
||||
|
||||
def action_regenerate_secret_key(self):
|
||||
self.ensure_one()
|
||||
self.secret_key = self._generate_default_secret_key()
|
||||
|
||||
def _compute_count_partner(self):
|
||||
data = self.env["auth.partner"].read_group(
|
||||
[
|
||||
("directory_id", "in", self.ids),
|
||||
],
|
||||
["directory_id"],
|
||||
groupby=["directory_id"],
|
||||
lazy=False,
|
||||
)
|
||||
res = {item["directory_id"][0]: item["__count"] for item in data}
|
||||
|
||||
for record in self:
|
||||
record.count_partner = res.get(record.id, 0)
|
||||
|
||||
def _get_template(self, type_or_template):
|
||||
if isinstance(type_or_template, str):
|
||||
return getattr(self, type_or_template + "_template_id", None)
|
||||
return type_or_template
|
||||
|
||||
def _prepare_mail_context(self, context):
|
||||
return context or {}
|
||||
|
||||
def _send_mail_background(
|
||||
self, type_or_template, auth_partner, callback_job=None, **context
|
||||
):
|
||||
"""
|
||||
Send an email asynchronously to the auth_partner
|
||||
using the template defined in the directory
|
||||
"""
|
||||
self.ensure_one()
|
||||
auth_partner.ensure_one()
|
||||
# Load context synchronously
|
||||
context = self._prepare_mail_context(context)
|
||||
|
||||
job = self.delayable()._send_mail_impl(
|
||||
type_or_template, auth_partner, **context
|
||||
)
|
||||
if callback_job:
|
||||
job = chain(job, callback_job)
|
||||
return job.delay()
|
||||
|
||||
def _send_mail(self, type_or_template, auth_partner, **context):
|
||||
"""Send an email to the auth_partner using the template defined in the directory"""
|
||||
self.ensure_one()
|
||||
auth_partner.ensure_one()
|
||||
context = self._prepare_mail_context(context)
|
||||
|
||||
self._send_mail_impl(type_or_template, auth_partner, **context)
|
||||
|
||||
def _send_mail_impl(self, type_or_template, auth_partner, **context):
|
||||
template = self.sudo()._get_template(type_or_template)
|
||||
if not template:
|
||||
raise UserError(
|
||||
_("No email template defined for %(template)s in %(directory)s")
|
||||
% {"template": type_or_template, "directory": self.name}
|
||||
)
|
||||
template.sudo().with_context(**context).send_mail(
|
||||
auth_partner.id, force_send=True, raise_exception=True
|
||||
)
|
||||
|
||||
return f"Mail {template.name} sent to {auth_partner.login}"
|
||||
|
||||
def _generate_token(self, action, auth_partner, expiration_delta, key_salt=""):
|
||||
# We need to sudo here as secret_key is a protected field
|
||||
self = self.sudo()
|
||||
return jwt.encode(
|
||||
{
|
||||
"exp": datetime.now(tz=timezone.utc) + expiration_delta,
|
||||
"aud": str(self.id),
|
||||
"action": action,
|
||||
"ap": auth_partner.id,
|
||||
},
|
||||
self.secret_key + key_salt,
|
||||
algorithm="HS256",
|
||||
)
|
||||
|
||||
def _decode_token(
|
||||
self,
|
||||
token,
|
||||
action,
|
||||
key_salt=None,
|
||||
):
|
||||
# We need to sudo here as secret_key is a protected field
|
||||
self = self.sudo()
|
||||
key = self.secret_key
|
||||
if key_salt:
|
||||
try:
|
||||
obj = jwt.decode(
|
||||
token, algorithms=["HS256"], options={"verify_signature": False}
|
||||
)
|
||||
except jwt.PyJWTError as e:
|
||||
raise UserError(_("Invalid Token")) from e
|
||||
probable_auth_partner = self.env["auth.partner"].browse(obj["ap"])
|
||||
if not probable_auth_partner:
|
||||
raise UserError(_("Invalid Token"))
|
||||
key += key_salt(probable_auth_partner)
|
||||
|
||||
try:
|
||||
obj = jwt.decode(
|
||||
token,
|
||||
key,
|
||||
audience=str(self.id),
|
||||
options={"require": ["exp", "aud", "ap", "action"]},
|
||||
algorithms=["HS256"],
|
||||
)
|
||||
except jwt.PyJWTError as e:
|
||||
raise UserError(_("Invalid Token")) from e
|
||||
|
||||
auth_partner = self.env["auth.partner"].browse(obj["ap"])
|
||||
|
||||
if (
|
||||
obj["action"] != action
|
||||
or not auth_partner
|
||||
or auth_partner.directory_id != self
|
||||
):
|
||||
raise UserError(_("Invalid token"))
|
||||
|
||||
return auth_partner
|
||||
|
||||
@property
|
||||
def _server_env_fields(self):
|
||||
return {"secret_key": {}}
|
||||
Loading…
Add table
Add a link
Reference in a new issue