# -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. import json import logging as logger from odoo import api, fields, models from ..tools.jwt import generate_vapid_keys _logger = logger.getLogger(__name__) class InvalidVapidError(Exception): pass class PartnerDevice(models.Model): _name = 'mail.partner.device' _description = 'Partner Web Push Device' partner_id = fields.Many2one('res.partner', string='Partner', index=True, required=True, default=lambda self: self.env.user.partner_id) endpoint = fields.Char(string='Browser endpoint', required=True) keys = fields.Char(string='Browser keys', required=True, help=("It's refer to browser keys used by the notification: \n" "- p256dh: It's the subscription public key generated by the browser. The browser will \n" " keep the private key secret and use it for decrypting the payload\n" "- auth: The auth value should be treated as a secret and not shared outside of Odoo")) expiration_time = fields.Datetime(string='Expiration Token Date') _sql_constraints = [('endpoint_unique', 'unique(endpoint)', 'The endpoint must be unique !')] @api.model def get_web_push_vapid_public_key(self): ir_params_sudo = self.env['ir.config_parameter'].sudo() public_key = 'mail.web_push_vapid_public_key' public_key_value = ir_params_sudo.get_param(public_key) # Regenerate new Keys if public key not present if not public_key_value: self.sudo().search([]).unlink() # Reset all devices (ServiceWorker) private_key_value, public_key_value = generate_vapid_keys() ir_params_sudo.set_param('mail.web_push_vapid_private_key', private_key_value) ir_params_sudo.set_param(public_key, public_key_value) _logger.info("WebPush: missing public key, new VAPID keys generated") return public_key_value @api.model def register_devices(self, **kw): sw_vapid_public_key = kw.get('vapid_public_key') valid_sub = self._verify_vapid_public_key(sw_vapid_public_key) if not valid_sub: raise InvalidVapidError("Invalid VAPID public key") endpoint = kw.get('endpoint') browser_keys = kw.get('keys') if not endpoint or not browser_keys: return search_endpoint = kw.get('previousEndpoint', endpoint) user_device = self.sudo().search([('endpoint', '=', search_endpoint)]) if user_device: if user_device.partner_id is not self.env.user.partner_id: user_device.write({ 'endpoint': endpoint, 'expiration_time': kw.get('expirationTime'), 'keys': json.dumps(browser_keys), 'partner_id': self.env.user.partner_id, }) else: self.sudo().create([{ 'endpoint': endpoint, 'expiration_time': kw.get('expirationTime'), 'keys': json.dumps(browser_keys), 'partner_id': self.env.user.partner_id.id, }]) @api.model def unregister_devices(self, **kw): endpoint = kw.get('endpoint') if not endpoint: return user_device = self.sudo().search([ ('endpoint', '=', endpoint) ]) if user_device: user_device.unlink() def _verify_vapid_public_key(self, sw_public_key): ir_params_sudo = self.env['ir.config_parameter'].sudo() db_public_key = ir_params_sudo.get_param('mail.web_push_vapid_public_key') return db_public_key == sw_public_key