0001-auth_oidc-factorize-claim-mapping-resolution-72418.patch
src/authentic2_auth_oidc/backends.py | ||
---|---|---|
34 | 34 |
from authentic2.models import Lock |
35 | 35 |
from authentic2.utils import hooks |
36 | 36 |
from authentic2.utils.crypto import base64url_encode |
37 |
from authentic2.utils.template import Template |
|
38 | 37 | |
39 | 38 |
from . import models, utils |
40 | 39 | |
... | ... | |
152 | 151 |
user_ou = provider.ou |
153 | 152 |
user_info = None |
154 | 153 |
save_user = False |
155 |
mappings = [] |
|
156 | 154 |
context = id_token_content.copy() |
157 | 155 |
need_user_info = False |
158 | 156 |
for claim_mapping in provider.claim_mappings.all(): |
... | ... | |
182 | 180 |
logger.debug('auth_oidc: user_info content %s', user_info) |
183 | 181 |
context.update(user_info or {}) |
184 | 182 | |
185 |
for claim_mapping in provider.claim_mappings.all(): |
|
186 |
claim = claim_mapping.claim |
|
187 |
if claim_mapping.idtoken_claim: |
|
188 |
source = id_token |
|
189 |
else: |
|
190 |
source = user_info |
|
191 |
if not source or claim not in source and not ('{{' in claim or '{%' in claim): |
|
192 |
continue |
|
193 |
verified = False |
|
194 |
attribute = claim_mapping.attribute |
|
195 |
if '{{' in claim or '{%' in claim: |
|
196 |
template = Template(claim) |
|
197 |
value = template.render(context=context) |
|
198 |
# xxx missing verification logic for templated claims |
|
199 |
else: |
|
200 |
value = source.get(claim) |
|
201 |
if claim_mapping.verified == models.OIDCClaimMapping.VERIFIED_CLAIM: |
|
202 |
verified = bool(source.get(claim + '_verified', False)) |
|
183 |
mappings = utils.resolve_claim_mappings(provider, context, id_token, user_info) |
|
184 |
for attribute, value, dummy in mappings: |
|
203 | 185 |
if attribute == 'ou__slug' and value in ou_map: |
204 | 186 |
user_ou = ou_map[value] |
205 |
continue |
|
206 |
if claim_mapping.verified == models.OIDCClaimMapping.ALWAYS_VERIFIED: |
|
207 |
verified = True |
|
208 |
mappings.append((attribute, value, verified)) |
|
187 |
break |
|
209 | 188 | |
210 | 189 |
# check for required claims |
211 | 190 |
for claim_mapping in provider.claim_mappings.all(): |
... | ... | |
393 | 372 | |
394 | 373 |
# new style attributes |
395 | 374 |
for attribute, value, verified in mappings: |
396 |
if attribute in ('username', 'email'): |
|
375 |
if attribute in ('username', 'email', 'ou__slug'):
|
|
397 | 376 |
continue |
398 | 377 |
if attribute in ('first_name', 'last_name') and not verified: |
399 | 378 |
continue |
src/authentic2_auth_oidc/models.py | ||
---|---|---|
38 | 38 |
from authentic2.utils.misc import make_url |
39 | 39 |
from authentic2.utils.template import Template, validate_template |
40 | 40 | |
41 |
from . import managers |
|
41 |
from . import managers, utils
|
|
42 | 42 | |
43 | 43 |
if django.VERSION < (3, 1): |
44 | 44 |
from django.contrib.postgres.fields.jsonb import JSONField # noqa pylint: disable=ungrouped-imports |
... | ... | |
334 | 334 |
except OIDCAccount.MultipleObjectsReturned: |
335 | 335 |
continue |
336 | 336 |
had_changes = False |
337 |
for claim in self.claim_mappings.all(): |
|
338 |
if '{{' in claim.claim or '{%' in claim.claim: |
|
339 |
template = Template(claim.claim) |
|
340 |
attribute_value = template.render(context=user_dict) |
|
341 |
else: |
|
342 |
attribute_value = user_dict.get(claim.claim) |
|
337 |
mappings = utils.resolve_claim_mappings(self, user_dict) |
|
338 |
for attribute, value, dummy in mappings: |
|
343 | 339 |
try: |
344 |
old_attribute_value = getattr(account.user, claim.attribute)
|
|
340 |
old_attribute_value = getattr(account.user, attribute) |
|
345 | 341 |
except AttributeError: |
346 | 342 |
try: |
347 |
old_attribute_value = getattr(account.user.attributes, claim.attribute)
|
|
343 |
old_attribute_value = getattr(account.user.attributes, attribute) |
|
348 | 344 |
except AttributeError: |
349 | 345 |
old_attribute_value = None |
350 |
if old_attribute_value == attribute_value:
|
|
346 |
if old_attribute_value == value: |
|
351 | 347 |
continue |
352 | 348 |
had_changes = True |
353 |
setattr(account.user, claim.attribute, attribute_value)
|
|
349 |
setattr(account.user, attribute, value)
|
|
354 | 350 |
try: |
355 |
setattr(account.user.attributes, claim.attribute, attribute_value)
|
|
351 |
setattr(account.user.attributes, attribute, value)
|
|
356 | 352 |
except AttributeError: |
357 | 353 |
pass |
358 | 354 |
if had_changes: |
src/authentic2_auth_oidc/utils.py | ||
---|---|---|
28 | 28 |
from authentic2.a2_rbac.utils import get_default_ou |
29 | 29 |
from authentic2.models import Attribute |
30 | 30 |
from authentic2.utils.cache import GlobalCache |
31 |
from authentic2.utils.template import Template |
|
32 | ||
33 |
from . import models |
|
31 | 34 | |
32 | 35 |
TIMEOUT = 1 |
33 | 36 | |
... | ... | |
73 | 76 |
return json_decode(jwt.claims) |
74 | 77 | |
75 | 78 | |
79 |
def resolve_claim_mappings(provider, context, id_token=None, user_info=None): |
|
80 |
mappings = [] |
|
81 |
for claim_mapping in provider.claim_mappings.all(): |
|
82 |
claim = claim_mapping.claim |
|
83 |
if id_token is None and user_info is None: |
|
84 |
source = context |
|
85 |
elif claim_mapping.idtoken_claim: |
|
86 |
source = id_token |
|
87 |
else: |
|
88 |
source = user_info |
|
89 |
if not source or claim not in source and not ('{{' in claim or '{%' in claim): |
|
90 |
continue |
|
91 |
verified = False |
|
92 |
attribute = claim_mapping.attribute |
|
93 |
if '{{' in claim or '{%' in claim: |
|
94 |
template = Template(claim) |
|
95 |
value = template.render(context=context) |
|
96 |
else: |
|
97 |
value = source.get(claim) |
|
98 |
if claim_mapping.verified == models.OIDCClaimMapping.VERIFIED_CLAIM: |
|
99 |
verified = bool(source.get(claim + '_verified', False)) |
|
100 |
if claim_mapping.verified == models.OIDCClaimMapping.ALWAYS_VERIFIED: |
|
101 |
verified = True |
|
102 |
mappings.append((attribute, value, verified)) |
|
103 |
return mappings |
|
104 | ||
105 | ||
76 | 106 |
REQUIRED_ID_TOKEN_KEYS = {'iss', 'sub', 'aud', 'exp', 'iat'} |
77 | 107 |
KEY_TYPES = { |
78 | 108 |
'iss': str, |
79 |
- |