mirror of
https://github.com/bringout/oca-ocb-security.git
synced 2026-04-23 00:12:09 +02:00
19.0 vanilla
This commit is contained in:
parent
20ddc1b4a3
commit
c0efcc53f5
1162 changed files with 125577 additions and 105287 deletions
|
|
@ -1,16 +1,16 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import base64
|
||||
import json
|
||||
import logging
|
||||
import werkzeug
|
||||
|
||||
from werkzeug.exceptions import Forbidden
|
||||
|
||||
from odoo import http
|
||||
from odoo import _, http
|
||||
from odoo.exceptions import UserError
|
||||
from odoo.http import request
|
||||
from odoo.tools import consteq
|
||||
from odoo.tools import consteq, email_normalize
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -24,9 +24,12 @@ class MicrosoftOutlookController(http.Controller):
|
|||
We will fetch the refresh token and the access token thanks to this authorization
|
||||
code and save those values on the given mail server.
|
||||
"""
|
||||
if not request.env.user.has_group('base.group_system'):
|
||||
_logger.error('Microsoft Outlook: Non system user try to link an Outlook account.')
|
||||
raise Forbidden()
|
||||
if error_description:
|
||||
_logger.warning("Microsoft Outlook: an error occurred %s", error_description)
|
||||
return request.render('microsoft_outlook.microsoft_outlook_oauth_error', {
|
||||
'error': error_description,
|
||||
'redirect_url': '/odoo',
|
||||
})
|
||||
|
||||
try:
|
||||
state = json.loads(state)
|
||||
|
|
@ -37,40 +40,97 @@ class MicrosoftOutlookController(http.Controller):
|
|||
_logger.error('Microsoft Outlook: Wrong state value %r.', state)
|
||||
raise Forbidden()
|
||||
|
||||
if error_description:
|
||||
record_sudo = self._get_outlook_record(model_name, rec_id, csrf_token)
|
||||
|
||||
try:
|
||||
refresh_token, access_token, expiration = record_sudo._fetch_outlook_refresh_token(code)
|
||||
except UserError as e:
|
||||
return request.render('microsoft_outlook.microsoft_outlook_oauth_error', {
|
||||
'error': error_description,
|
||||
'model_name': model_name,
|
||||
'rec_id': rec_id,
|
||||
'error': str(e),
|
||||
'redirect_url': self._get_redirect_url(record_sudo),
|
||||
})
|
||||
|
||||
return self._check_email_and_redirect_to_outlook_record(access_token, expiration, refresh_token, record_sudo)
|
||||
|
||||
@http.route('/microsoft_outlook/iap_confirm', type='http', auth='user')
|
||||
def microsoft_outlook_iap_callback(self, model, rec_id, csrf_token, access_token, refresh_token, expiration):
|
||||
"""Receive back the refresh token and access token from IAP.
|
||||
|
||||
The authentication process with IAP is done in 4 steps;
|
||||
1. User database make a request to `<IAP>/api/mail_oauth/1/outlook`
|
||||
2. User browser is redirected to the URL we received from IAP
|
||||
3. User browser is redirected to `<IAP>/api/mail_oauth/1/outlook_callback`
|
||||
with the authorization_code
|
||||
4. User browser is redirected to `<DB>/microsoft_outlook/iap_confirm`
|
||||
"""
|
||||
record = self._get_outlook_record(model, rec_id, csrf_token)
|
||||
return self._check_email_and_redirect_to_outlook_record(access_token, expiration, refresh_token, record)
|
||||
|
||||
def _get_outlook_record(self, model_name, rec_id, csrf_token):
|
||||
"""Return the given record after checking the CSRF token."""
|
||||
model = request.env[model_name]
|
||||
|
||||
if not isinstance(model, request.env.registry['microsoft.outlook.mixin']):
|
||||
# The model must inherits from the "microsoft.outlook.mixin" mixin
|
||||
_logger.error('Microsoft Outlook: Wrong model %r.', model_name)
|
||||
raise Forbidden()
|
||||
|
||||
record = model.browse(rec_id).exists()
|
||||
record = model.browse(int(rec_id)).exists().sudo()
|
||||
if not record:
|
||||
_logger.error('Microsoft Outlook: Record not found.')
|
||||
raise Forbidden()
|
||||
|
||||
if not csrf_token or not consteq(csrf_token, record._get_outlook_csrf_token()):
|
||||
_logger.error('Microsoft Outlook: Wrong CSRF token during Outlook authentication.')
|
||||
raise Forbidden()
|
||||
|
||||
try:
|
||||
refresh_token, access_token, expiration = record._fetch_outlook_refresh_token(code)
|
||||
except UserError as e:
|
||||
return request.render('microsoft_outlook.microsoft_outlook_oauth_error', {
|
||||
'error': str(e.name),
|
||||
'model_name': model_name,
|
||||
'rec_id': rec_id,
|
||||
})
|
||||
return record
|
||||
|
||||
def _check_email_and_redirect_to_outlook_record(self, access_token, expiration, refresh_token, record):
|
||||
if (record._name == 'ir.mail_server' and (record.owner_user_id or not request.env.user.has_group('base.group_system'))):
|
||||
# Verify the token information (that the email set on the
|
||||
# server is the email used to login on Outlook)
|
||||
# We can not directly get the id_token from the response, even if we verify the signature
|
||||
# because it comes from the user's browser redirection, and he could give an id_token of one account
|
||||
# and the refresh_token of a different account.
|
||||
# So we ask a new token to the outlook API to check the email address
|
||||
# Because we received the JWT token from the API (or from IAP, with a direct HTTP request),
|
||||
# we don't even need to check the signature
|
||||
refresh_token, access_token, id_token, expiration = record._fetch_outlook_access_token(refresh_token)
|
||||
id_token_data = id_token.split(".")[1]
|
||||
id_token_data += '=' * (-len(id_token_data) % 4) # `=` padding can be missing
|
||||
email = json.loads(base64.b64decode(id_token_data)).get('email')
|
||||
if not email or email_normalize(email) != email_normalize(record[record._email_field]):
|
||||
_logger.error('Microsoft Outlook: Invalid email address: %r != %s.', email, record[record._email_field])
|
||||
return request.render('microsoft_outlook.microsoft_outlook_oauth_error', {
|
||||
'error': _(
|
||||
"Oops, you're creating an authorization to send from %(email_login)s but your address is %(email_server)s. Make sure your addresses match!",
|
||||
email_login=email,
|
||||
email_server=record[record._email_field],
|
||||
),
|
||||
'redirect_url': self._get_redirect_url(record),
|
||||
})
|
||||
|
||||
record.write({
|
||||
'active': True,
|
||||
'microsoft_outlook_refresh_token': refresh_token,
|
||||
'microsoft_outlook_access_token': access_token,
|
||||
'microsoft_outlook_access_token_expiration': expiration,
|
||||
})
|
||||
return request.redirect(self._get_redirect_url(record))
|
||||
|
||||
return request.redirect(f'/web?#id={rec_id}&model={model_name}&view_type=form')
|
||||
def _get_redirect_url(self, record):
|
||||
"""Return the redirect URL for the given record.
|
||||
|
||||
If the user configured a personal mail server, we redirect him
|
||||
to the user preference view. If it's an admin and that he
|
||||
configured a standard incoming / outgoing mail server, then we
|
||||
redirect it to the mail server form view.
|
||||
"""
|
||||
if (
|
||||
(record._name != 'ir.mail_server'
|
||||
or record != request.env.user.outgoing_mail_server_id)
|
||||
and request.env.user.has_group('base.group_system')
|
||||
):
|
||||
return f'/odoo/{record._name}/{record.id}'
|
||||
return f'/odoo/my-preferences/{request.env.user.id}'
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue