mirror of
https://github.com/bringout/oca-ocb-security.git
synced 2026-04-19 19:32:08 +02:00
139 lines
6.2 KiB
Python
139 lines
6.2 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
import json
|
|
import logging
|
|
import requests
|
|
|
|
from werkzeug.exceptions import Forbidden
|
|
|
|
from odoo import _, http
|
|
from odoo.exceptions import UserError
|
|
from odoo.http import request
|
|
from odoo.tools import consteq, email_normalize
|
|
from odoo.addons.google_gmail.models.google_gmail_mixin import GMAIL_TOKEN_REQUEST_TIMEOUT
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
class GoogleGmailController(http.Controller):
|
|
@http.route('/google_gmail/confirm', type='http', auth='user')
|
|
def google_gmail_callback(self, code=None, state=None, error=None, **kwargs):
|
|
"""Callback URL during the OAuth process.
|
|
|
|
Gmail redirects the user browser to this endpoint with the authorization code.
|
|
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 error:
|
|
_logger.warning("Google Gmail: an error occurred %s", error)
|
|
return request.render('google_gmail.google_gmail_oauth_error', {
|
|
'error': _('An error occurred during the authentication process.'),
|
|
'redirect_url': '/odoo',
|
|
})
|
|
|
|
try:
|
|
state = json.loads(state)
|
|
model_name = state['model']
|
|
rec_id = state['id']
|
|
csrf_token = state['csrf_token']
|
|
except Exception:
|
|
_logger.error('Google Gmail: Wrong state value %r.', state)
|
|
raise Forbidden()
|
|
|
|
record_sudo = self._get_gmail_record(model_name, rec_id, csrf_token)
|
|
|
|
try:
|
|
refresh_token, access_token, expiration = record_sudo._fetch_gmail_refresh_token(code)
|
|
except UserError as e:
|
|
return request.render('google_gmail.google_gmail_oauth_error', {
|
|
'error': str(e),
|
|
'redirect_url': self._get_redirect_url(record_sudo),
|
|
})
|
|
|
|
return self._check_email_and_redirect_to_gmail_record(access_token, expiration, refresh_token, record_sudo)
|
|
|
|
@http.route('/google_gmail/iap_confirm', type='http', auth='user')
|
|
def google_gmail_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/gmail`
|
|
2. User browser is redirected to the URL we received from IAP
|
|
3. User browser is redirected to `<IAP>/api/mail_oauth/1/gmail_callback`
|
|
with the authorization_code
|
|
4. User browser is redirected to `<DB>/google_gmail/iap_confirm`
|
|
"""
|
|
record = self._get_gmail_record(model, rec_id, csrf_token)
|
|
return self._check_email_and_redirect_to_gmail_record(access_token, expiration, refresh_token, record)
|
|
|
|
def _get_gmail_record(self, model_name, rec_id, csrf_token):
|
|
"""Return the record after checking the CSRF token."""
|
|
model = request.env[model_name]
|
|
|
|
if not isinstance(model, request.env.registry['google.gmail.mixin']):
|
|
# The model must inherits from the "google.gmail.mixin" mixin
|
|
_logger.error('Google Gmail: Wrong model %r.', model_name)
|
|
raise Forbidden()
|
|
|
|
record = model.browse(int(rec_id)).exists().sudo()
|
|
if not record:
|
|
_logger.error('Google Gmail: No record found.')
|
|
raise Forbidden()
|
|
|
|
if not csrf_token or not consteq(csrf_token, record._get_gmail_csrf_token()):
|
|
_logger.error('Google Gmail: Wrong CSRF token during Gmail authentication.')
|
|
raise Forbidden()
|
|
|
|
return record
|
|
|
|
def _check_email_and_redirect_to_gmail_record(self, access_token, expiration, refresh_token, record):
|
|
# Verify the token information (that the email set on the
|
|
# server is the email used to login on Gmail)
|
|
if (record._name == 'ir.mail_server' and (record.owner_user_id or not request.env.user.has_group('base.group_system'))):
|
|
# https://developers.google.com/identity/protocols/oauth2/scopes
|
|
response = requests.get(
|
|
'https://www.googleapis.com/oauth2/v2/userinfo',
|
|
params={'access_token': access_token},
|
|
timeout=GMAIL_TOKEN_REQUEST_TIMEOUT,
|
|
)
|
|
if not response.ok:
|
|
_logger.error('Google Gmail: Could not verify the token information: %s.', response.text)
|
|
raise Forbidden()
|
|
|
|
response = response.json()
|
|
|
|
if not response.get('verified_email') or email_normalize(response.get('email')) != email_normalize(record[record._email_field]):
|
|
_logger.error('Google Gmail: Invalid email address: %r != %s.', response, record[record._email_field])
|
|
return request.render('google_gmail.google_gmail_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=response.get('email'),
|
|
email_server=record[record._email_field],
|
|
),
|
|
'redirect_url': self._get_redirect_url(record),
|
|
})
|
|
|
|
record.write({
|
|
'active': True,
|
|
'google_gmail_access_token': access_token,
|
|
'google_gmail_access_token_expiration': expiration,
|
|
'google_gmail_refresh_token': refresh_token,
|
|
})
|
|
return request.redirect(self._get_redirect_url(record))
|
|
|
|
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}'
|