From 6929801831ffe4374088663502388d16855a8eb5 Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Mon, 15 Nov 2021 16:08:13 +0100 Subject: [PATCH 1/3] [IMP] auth_oidc: allow assign groups from token claims --- auth_oidc/__manifest__.py | 5 ++- auth_oidc/demo/local_keycloak.xml | 5 +++ auth_oidc/models/auth_oauth_provider.py | 36 ++++++++++++++++++++- auth_oidc/models/res_users.py | 25 ++++++++++++++ auth_oidc/security/ir.model.access.csv | 2 ++ auth_oidc/tests/test_auth_oidc_auth_code.py | 6 ++++ auth_oidc/views/auth_oauth_provider.xml | 10 ++++++ 7 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 auth_oidc/security/ir.model.access.csv diff --git a/auth_oidc/__manifest__.py b/auth_oidc/__manifest__.py index 2fb0660cdd..6cdae6ea34 100644 --- a/auth_oidc/__manifest__.py +++ b/auth_oidc/__manifest__.py @@ -16,6 +16,9 @@ "summary": "Allow users to login through OpenID Connect Provider", "external_dependencies": {"python": ["jose"]}, "depends": ["auth_oauth"], - "data": ["views/auth_oauth_provider.xml"], + "data": [ + "security/ir.model.access.csv", + "views/auth_oauth_provider.xml", + ], "demo": ["demo/local_keycloak.xml"], } diff --git a/auth_oidc/demo/local_keycloak.xml b/auth_oidc/demo/local_keycloak.xml index 919754db99..92588dc952 100644 --- a/auth_oidc/demo/local_keycloak.xml +++ b/auth_oidc/demo/local_keycloak.xml @@ -17,4 +17,9 @@ name="jwks_uri" >http://localhost:8080/auth/realms/master/protocol/openid-connect/certs + + + + token['name'] == 'test' + diff --git a/auth_oidc/models/auth_oauth_provider.py b/auth_oidc/models/auth_oauth_provider.py index 6a40e87ed6..c414160f43 100644 --- a/auth_oidc/models/auth_oauth_provider.py +++ b/auth_oidc/models/auth_oauth_provider.py @@ -2,12 +2,13 @@ # Copyright 2021 ACSONE SA/NV # License: AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) +import collections import logging import secrets import requests -from odoo import fields, models, tools +from odoo import api, exceptions, fields, models, tools try: from jose import jwt @@ -45,6 +46,9 @@ class AuthOauthProvider(models.Model): string="Token URL", help="Required for OpenID Connect authorization code flow." ) jwks_uri = fields.Char(string="JWKS URL", help="Required for OpenID Connect.") + group_line_ids = fields.One2many( + "auth.oauth.provider.group_line", "provider_id", string="Group mappings", + ) @tools.ormcache("self.jwks_uri", "kid") def _get_key(self, kid): @@ -80,3 +84,33 @@ def _parse_id_token(self, id_token, access_token): res.update(self._map_token_values(res)) return res + + +class AuthOauthProviderGroupLine(models.Model): + _name = 'auth.oauth.provider.group_line' + + provider_id = fields.Many2one('auth.oauth.provider', required=True) + group_id = fields.Many2one('res.groups', required=True) + expression = fields.Char(required=True, help="Variables: user, token") + + @api.constrains('expression') + def _check_expression(self): + for this in self: + try: + this._eval_expression(self.env.user, {}) + except (AttributeError, KeyError, NameError) as e: + raise exceptions.ValidationError('\n'.join(e.args)) + + def _eval_expression(self, user, token): + self.ensure_one() + + class Defaultdict2(collections.defaultdict): + def __init__(self, *args, **kwargs): + super().__init__(Defaultdict2, *args, **kwargs) + + return tools.safe_eval( + self.expression, { + 'user': user, + 'token': Defaultdict2(token), + } + ) diff --git a/auth_oidc/models/res_users.py b/auth_oidc/models/res_users.py index c487504e2a..56e92fbdc9 100644 --- a/auth_oidc/models/res_users.py +++ b/auth_oidc/models/res_users.py @@ -63,6 +63,12 @@ def auth_oauth(self, provider, params): _logger.error("No id_token in response.") raise AccessDenied() validation = oauth_provider._parse_id_token(id_token, access_token) + if oauth_provider.data_endpoint: + data = requests.get( + oauth_provider.data_endpoint, + headers={'Authorization': 'Bearer %s' % access_token} + ).json() + validation.update(data) # required check if not validation.get("user_id"): _logger.error("user_id claim not found in id_token (after mapping).") @@ -74,3 +80,22 @@ def auth_oauth(self, provider, params): raise AccessDenied() # return user credentials return (self.env.cr.dbname, login, access_token) + + @api.model + def _auth_oauth_signin(self, provider, validation, params): + login = super()._auth_oauth_signin(provider, validation, params) + user = self.search([('login', '=', login)]) + if user: + group_updates = [] + for group_line in self.env['auth.oauth.provider'].browse( + provider + ).group_line_ids: + if group_line._eval_expression(user, validation): + if group_line.group_id not in user.groups_id: + group_updates.append((4, group_line.group_id.id)) + else: + if group_line.group_id in user.groups_id: + group_updates.append((3, group_line.group_id.id)) + if group_updates: + user.write({'groups_id': group_updates}) + return login diff --git a/auth_oidc/security/ir.model.access.csv b/auth_oidc/security/ir.model.access.csv new file mode 100644 index 0000000000..503e4c7529 --- /dev/null +++ b/auth_oidc/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_auth_oauth_provider_group_line,auth_oauth_provider,model_auth_oauth_provider_group_line,base.group_system,1,1,1,1 diff --git a/auth_oidc/tests/test_auth_oidc_auth_code.py b/auth_oidc/tests/test_auth_oidc_auth_code.py index afe165927c..c3c89c37cf 100644 --- a/auth_oidc/tests/test_auth_oidc_auth_code.py +++ b/auth_oidc/tests/test_auth_oidc_auth_code.py @@ -74,6 +74,12 @@ def test_auth_link(self): self.assertTrue(params["state"]) self.assertEqual(params["redirect_uri"], [BASE_URL + "/auth_oauth/signin"]) + def test_group_expression(self): + """Test that group expressions evaluate correctly""" + group_line = self.env.ref('auth_oidc.local_keycloak').group_line_ids[:1] + group_line.expression = 'token["test"]["test"] == 1' + self.assertFalse(group_line._eval_expression(self.env.user, {})) + @responses.activate def test_login(self): """Test that login works""" diff --git a/auth_oidc/views/auth_oauth_provider.xml b/auth_oidc/views/auth_oauth_provider.xml index 90c931b417..dbdeadd8ef 100644 --- a/auth_oidc/views/auth_oauth_provider.xml +++ b/auth_oidc/views/auth_oauth_provider.xml @@ -19,6 +19,16 @@ + + + + + + + + + + From 234f67d120b0afa32c31d30f88738aa95d0963a2 Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Mon, 15 Nov 2021 16:14:55 +0100 Subject: [PATCH 2/3] [IMP] regenerate readme --- auth_oidc/static/description/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auth_oidc/static/description/index.html b/auth_oidc/static/description/index.html index bdb24cf91b..58ca3cd71c 100644 --- a/auth_oidc/static/description/index.html +++ b/auth_oidc/static/description/index.html @@ -3,7 +3,7 @@ - + Authentication OpenID Connect