From ad1e00ab5d75e9c87ab02b397002c211bbdc39c4 Mon Sep 17 00:00:00 2001 From: Maciej Wawro <36930951+maciekwawro@users.noreply.github.com> Date: Sat, 14 Sep 2019 14:47:50 +0200 Subject: [PATCH] Allow restricting clients to groups, APIs to clients, custom API exceptions --- galicea_openid_connect/__manifest__.py | 2 +- galicea_openid_connect/api.py | 33 +++++++++++++--------- galicea_openid_connect/controllers/main.py | 13 ++++++++- galicea_openid_connect/models/client.py | 4 +++ galicea_openid_connect/views/views.xml | 1 + 5 files changed, 37 insertions(+), 16 deletions(-) diff --git a/galicea_openid_connect/__manifest__.py b/galicea_openid_connect/__manifest__.py index bc24662..1cdfcb1 100644 --- a/galicea_openid_connect/__manifest__.py +++ b/galicea_openid_connect/__manifest__.py @@ -9,7 +9,7 @@ 'website': "http://galicea.pl", 'category': 'Technical Settings', - 'version': '10.0.1.1', + 'version': '10.0.1.2', 'depends': ['web', 'galicea_environment_checkup'], diff --git a/galicea_openid_connect/api.py b/galicea_openid_connect/api.py index bf19be2..0de6a39 100644 --- a/galicea_openid_connect/api.py +++ b/galicea_openid_connect/api.py @@ -16,7 +16,13 @@ class ApiException(Exception): super(Exception, self).__init__(message) self.code = code if code else self.INVALID_REQUEST -def resource(path, method, auth='user'): + def to_json(self): + return { + 'error': self.code, + 'error_message': self.message + } + +def resource(path, method, auth='user', clients=None): assert auth in ['user', 'client'] def endpoint_decorator(func): @@ -68,6 +74,10 @@ def resource(path, method, auth='user'): ) req.uid = token.client_id.system_user_id.id + if clients: + if token.client_id.id not in map(lambda c: req.env.ref(c).id, clients): + raise ApiException('Access denied', 'restricted_app') + ctx = req.context.copy() ctx.update({'client_id': token.client_id.id}) req.context = ctx @@ -78,22 +88,17 @@ def resource(path, method, auth='user'): headers=cors_headers, status=200 ) - except ApiException as e: + except Exception as e: + status = 400 + if not isinstance(e, ApiException): + _logger.exception('Unexpected exception while processing API request') + e = ApiException('Unexpected server error', 'server_error') + status = 500 return werkzeug.Response( - response=json.dumps({'error': e.code, 'error_message': e.message}), - status=400, + response=json.dumps(e.to_json()), + status=status, headers=cors_headers ) - except: - _logger.exception('Unexpected exception while processing API request') - return werkzeug.Response( - response=json.dumps({ - 'error': 'server_error', - 'error_message': 'Unexpected server error', - }), - headers=cors_headers, - status=500 - ) return func_wrapper return endpoint_decorator diff --git a/galicea_openid_connect/controllers/main.py b/galicea_openid_connect/controllers/main.py index 2fafaf2..d5419a3 100644 --- a/galicea_openid_connect/controllers/main.py +++ b/galicea_openid_connect/controllers/main.py @@ -47,6 +47,7 @@ class OAuthException(Exception): UNSUPPORTED_RESPONSE_TYPE = 'unsupported_response_type' INVALID_GRANT = 'invalid_grant' UNSUPPORTED_GRANT_TYPE = 'unsupported_grant_type' + RESTRICTED_APP = 'restricted_app' def __init__(self, message, type): super(Exception, self).__init__(message) @@ -103,6 +104,15 @@ class Main(http.Controller): OAuthException.INVALID_CLIENT, ) + def __validate_user(self, client, user): + if not client.user_group_id: + return + if client.user_group_id not in user.groups_id: + raise OAuthException( + 'User is not allowed to use this client', + OAuthException.RESTRICTED_APP + ) + @http.route('/.well-known/openid-configuration', auth='public', type='http') def metadata(self, req, **query): base_url = http.request.httprequest.host_url @@ -206,6 +216,7 @@ class Main(http.Controller): } return self.__redirect('/web/login', params, 'query') + self.__validate_user(client, user) response_types = response_type.split() extra_claims = { @@ -355,7 +366,7 @@ class Main(http.Controller): 'Invalid username or password', OAuthException.INVALID_REQUEST ) - + self.__validate_user(client, req.env['res.users'].sudo().browse(user_id)) scopes = query['scope'].split(' ') if query.get('scope') else [] # Retrieve/generate access token. We currently only store one per user/client token = req.env['galicea_openid_connect.access_token'].sudo().retrieve_or_create( diff --git a/galicea_openid_connect/models/client.py b/galicea_openid_connect/models/client.py index 0c00e20..70f353e 100644 --- a/galicea_openid_connect/models/client.py +++ b/galicea_openid_connect/models/client.py @@ -34,6 +34,10 @@ class Client(models.Model): string='Allow OAuth2 password grant', default=False, ) + user_group_id = fields.Many2one( + 'res.groups', + 'Restrict the client to a group' + ) @api.model def __system_user_name(self, client_name): diff --git a/galicea_openid_connect/views/views.xml b/galicea_openid_connect/views/views.xml index 6e6cc66..facd3e1 100644 --- a/galicea_openid_connect/views/views.xml +++ b/galicea_openid_connect/views/views.xml @@ -28,6 +28,7 @@