diff --git a/security/ir.model.access.csv b/security/ir.model.access.csv index 77661d7fe861f8eb42b61ed6a8b475b1529a71c3..f7b609edbde0253e02461b566e05047033216d98 100644 --- a/security/ir.model.access.csv +++ b/security/ir.model.access.csv @@ -5,3 +5,4 @@ id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink "access_acc_actu_group_portal","acc_actu group_portal","acc_actus.model_acc_actu","base.group_portal",1,0,0,0 "access_acc_account_group_portal","acc_account group_portal","acc_account.model_acc_account","base.group_portal",1,0,0,0 "access_acc_contract_group_portal","acc_contract group_portal","acc_operation.model_acc_contract","base.group_portal",1,0,0,0 +"access_mail_template_group_portal","mail_template group_portal","mail.model_mail_template","base.group_portal",1,0,0,0 diff --git a/services/auth_services.py b/services/auth_services.py index 85bab21033c0654fd1bac4350f17ed77dafffc93..b4e5e1d85c8c0a661a72e1aaf7af31b81944e1b1 100644 --- a/services/auth_services.py +++ b/services/auth_services.py @@ -1,6 +1,9 @@ # © 2021 Le Filament (<http://www.le-filament.com>) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +import logging +import werkzeug + from datetime import datetime, timedelta import jwt @@ -8,6 +11,8 @@ import jwt from odoo.addons.base_rest import restapi from odoo.addons.component.core import Component +_logger = logging.getLogger(__name__) + class AuthService(Component): _inherit = "base.rest.service" @@ -68,6 +73,187 @@ class AuthService(Component): } return datas + # A faire pour gestion User + @restapi.method( + [(["/reset-password"], "POST")], + input_param=restapi.CerberusValidator("_validator_reset"), + output_param=restapi.CerberusValidator("_validator_return_reset"), + auth="none", + save_session=True, + cors="*", + crsf=False, + ) + def reset_password(self, **params): + """ + Log user and return user data & JWT token + """ + User = self.env["res.users"].sudo() + login = params.get("login", False) + datas = {} + if login: + user = User.search([("login", "=", login)]) + if user: + jwt_validator = self.env.ref("acc_rest_api.elo_validator").sudo() + exp = datetime.now() + timedelta( + hours=(jwt_validator.token_expiration * 24)) + payload = { + "aud": jwt_validator.audience, + "iss": jwt_validator.issuer, + "exp": exp, + "email": user.login, + "id": user.id, + "partner_id": user.partner_id.id, + } + token = jwt.encode( + payload, + key=jwt_validator.secret_key, + algorithm=jwt_validator.secret_algorithm, + ) + datas = { + # "user": user_datas, + "token": {"access_token": token, + "expiration_date": payload.get("exp")}, + } + # send email to users with their signup url + template = False + if not template: + template = self.env.ref('acc_portal.acc_reset_password_email') + assert template._name == 'mail.template' + + # template.sudo().write(template_values) + try: + + lang = user.lang + + query = dict(db=self.request.env.cr.dbname) + query['login'] = user.login + route = 'signup' + signup_url = "/%s?%s" % (route, werkzeug.urls.url_encode(query)) + signup_url = werkzeug.urls.url_join(user.company_id.url_app, signup_url) + + user.partner_id.sudo().signup_prepare() + + template.sudo().with_context( + portal_url=signup_url, + lang=lang + ).send_mail( + user.id, + force_send=True, + raise_exception=True) + except Exception as e: + datas['error'] = str(e) + _logger.info("Password reset email sent for user <%s> to <%s>", + user.login, user.email) + else: + datas['info'] = "Veuillez réinitialiser votre mot de passe: adresse email incorrecte" + return datas + + @restapi.method( + [(["/get-password"], "GET")], + input_param=restapi.CerberusValidator("_validator_get_password"), + output_param=restapi.CerberusValidator("_validator_return_password"), + cors="*", + crsf=False, + ) + def get_password(self): + user_id = self.env["res.users"].browse(self.request.uid) + user = { + "login": user_id.login, + } + return user + + @restapi.method( + [(["/get-signup"], "GET")], + input_param=restapi.CerberusValidator("_validator_get_password"), + output_param=restapi.CerberusValidator("_validator_return_password"), + cors="*", + crsf=False, + ) + def get_signup(self, **params): + user_id = self.env["res.users"].browse(self.request.uid) + user = { + "login": user_id.login, + } + return user + + @restapi.method( + [(["/set-password"], "POST")], + input_param=restapi.CerberusValidator("_validator_set_password"), + output_param=restapi.CerberusValidator("_validator_return_password"), + auth="none", + save_session=True, + cors="*", + crsf=False, + ) + def set_password(self, **params): + User = self.env["res.users"].sudo() + login = params.get("login", False) + password = params.get("password", False) + confirm_password = params.get("confirm_password", False) + datas = {} + if login: + user = User.search([("login", "=", login)]) + if user: + if password == confirm_password: + user.write({'password': password}) + else: + datas['error'] = "Les mots de passe ne correspondent pas, veuillez les saisir à nouveau." + return datas + + @restapi.method( + [(["/signup"], "POST")], + input_param=restapi.CerberusValidator("_validator_signup"), + output_param=restapi.CerberusValidator("_validator_return_signup"), + auth="none", + save_session=True, + cors="*", + crsf=False, + ) + def signup(self, **params): + User = self.env["res.users"].sudo() + login = params.get("login", False) + password = params.get("password", False) + confirm_password = params.get("confirm_password", False) + datas = {} + if login: + user = User.search([("login", "=", login)]) + if user: + user_datas = user.mapped( + lambda u: { + "userId": u.id, + "userEmail": u.login, + "userName": u.name, + "userCompany": u.parent_id.name if u.parent_id else None, + } + )[0] + if password == confirm_password: + jwt_validator = self.env.ref("acc_rest_api.elo_validator").sudo() + exp = datetime.now() + timedelta( + hours=(jwt_validator.token_expiration * 24)) + payload = { + "aud": jwt_validator.audience, + "iss": jwt_validator.issuer, + "exp": exp, + "email": user.login, + "id": user.id, + "partner_id": user.partner_id.id, + } + token = jwt.encode( + payload, + key=jwt_validator.secret_key, + algorithm=jwt_validator.secret_algorithm, + ) + datas = { + "user": user_datas, + "token": {"access_token": token, + "expiration_date": payload.get("exp")}, + } + user.write({'password': password}) + else: + datas["error"] = "Les mots de passe ne correspondent pas, veuillez les saisir à nouveau." + datas["user"] = user_datas + return datas + @restapi.method( [(["/logout"], "GET")], cors="*", @@ -150,6 +336,31 @@ class AuthService(Component): }, } + def _validator_reset(self): + return { + "login": {"type": "string"}, + } + + def _validator_return_reset(self): + return { + # "user": { + # "type": "dict", + # "schema": { + # "userId": {"type": "integer"}, + # "userEmail": {"type": "string"}, + # "userName": {"type": "string"}, + # "userCompany": {"type": "string", "nullable": True}, + # }, + # }, + "token": { + "type": "dict", + "schema": { + "access_token": {"type": "string", "nullable": True}, + "expiration_date": {"type": "datetime", "nullable": True}, + }, + }, + } + def _validator_logout(self): return {} @@ -162,15 +373,60 @@ class AuthService(Component): def _validator_set_my_account(self): return { "login": {"type": "string"}, - "firstname": {"type": "string"}, - "lastname": {"type": "string"}, + "firstname": {"type": "string", "nullable": True}, + "lastname": {"type": "string", "nullable": True}, + } + + def _validator_get_password(self): + return {} + + def _validator_set_password(self): + return { + "login": {"type": "string"}, + "password": {"type": "string", "nullable": True}, + "confirm_password": {"type": "string", "nullable": True}, + } + + def _validator_signup(self): + return { + "login": {"type": "string"}, + "password": {"type": "string", "nullable": True}, + "confirm_password": {"type": "string", "nullable": True}, } def _validator_return_my_account(self): return { "login": {"type": "string"}, - "firstname": {"type": "string"}, - "lastname": {"type": "string"}, + "firstname": {"type": "string", "nullable": True}, + "lastname": {"type": "string", "nullable": True}, "name": {"type": "string"}, "company": {"type": "string", "nullable": True}, } + + def _validator_return_password(self): + return { + "login": {"type": "string"}, + } + + def _validator_return_signup(self): + return { + "user": { + "type": "dict", + "schema": { + "userId": {"type": "integer"}, + "userEmail": {"type": "string"}, + "userName": {"type": "string"}, + "userCompany": {"type": "string", "nullable": True}, + }, + }, + "login": {"type": "string"}, + "error": {"type": "string"}, + "token": { + "type": "dict", + "schema": { + "access_token": {"type": "string", "nullable": True}, + "expiration_date": {"type": "datetime", "nullable": True}, + }, + "nullable": True + }, + }