Change setting OIDC_USERINFO.

This commit is contained in:
Ignacio Fiorentino 2016-07-07 12:50:27 -03:00
parent ab65aab0e1
commit dc9ec1863e
9 changed files with 99 additions and 98 deletions

View file

@ -12,17 +12,17 @@ List of all the attributes grouped by scopes:
+--------------------+----------------+-----------------------+------------------------+ +--------------------+----------------+-----------------------+------------------------+
| profile | email | phone | address | | profile | email | phone | address |
+====================+================+=======================+========================+ +====================+================+=======================+========================+
| name | email | phone_number | address_formatted | | name | email | phone_number | formatted |
+--------------------+----------------+-----------------------+------------------------+ +--------------------+----------------+-----------------------+------------------------+
| given_name | email_verified | phone_number_verified | address_street_address | | given_name | email_verified | phone_number_verified | street_address |
+--------------------+----------------+-----------------------+------------------------+ +--------------------+----------------+-----------------------+------------------------+
| family_name | | | address_locality | | family_name | | | locality |
+--------------------+----------------+-----------------------+------------------------+ +--------------------+----------------+-----------------------+------------------------+
| middle_name | | | address_region | | middle_name | | | region |
+--------------------+----------------+-----------------------+------------------------+ +--------------------+----------------+-----------------------+------------------------+
| nickname | | | address_postal_code | | nickname | | | postal_code |
+--------------------+----------------+-----------------------+------------------------+ +--------------------+----------------+-----------------------+------------------------+
| preferred_username | | | address_country | | preferred_username | | | country |
+--------------------+----------------+-----------------------+------------------------+ +--------------------+----------------+-----------------------+------------------------+
| profile | | | | | profile | | | |
+--------------------+----------------+-----------------------+------------------------+ +--------------------+----------------+-----------------------+------------------------+
@ -41,35 +41,22 @@ List of all the attributes grouped by scopes:
| updated_at | | | | | updated_at | | | |
+--------------------+----------------+-----------------------+------------------------+ +--------------------+----------------+-----------------------+------------------------+
Example using a django model:: Somewhere in your Django ``settings.py``::
from django.conf import settings OIDC_USERINFO = 'myproject.oidc_provider_settings.userinfo'
from django.db import models
class UserInfo(models.Model): Then create the function for the ``OIDC_USERINFO`` setting::
GENDER_CHOICES = [ def userinfo(claims, user):
('F', 'Female'),
('M', 'Male'),
]
user = models.OneToOneField(settings.AUTH_USER_MODEL, primary_key=True) claims['name'] = '{0} {1}'.format(user.first_name, user.last_name)
claims['given_name'] = user.first_name
claims['family_name'] = user.last_name
claims['email'] = user.email
claims['address']['street_address'] = '...'
given_name = models.CharField(max_length=255, blank=True, null=True) return claims
family_name = models.CharField(max_length=255, blank=True, null=True)
gender = models.CharField(max_length=100, choices=GENDER_CHOICES, null=True)
birthdate = models.DateField(null=True)
updated_at = models.DateTimeField(auto_now=True, null=True)
email_verified = models.NullBooleanField(default=False) .. note::
Please **DO NOT** add extra keys or delete the existing ones in the ``claims`` dict. If you want to add extra claims to some scopes you can use the ``OIDC_EXTRA_SCOPE_CLAIMS`` setting.
phone_number = models.CharField(max_length=255, blank=True, null=True)
phone_number_verified = models.NullBooleanField(default=False)
address_locality = models.CharField(max_length=255, blank=True, null=True)
address_country = models.CharField(max_length=255, blank=True, null=True)
@classmethod
def get_by_user(cls, user):
return cls.objects.get(user=user)

View file

@ -162,4 +162,21 @@ Expressed in seconds. Default is ``60*60``.
OIDC_USERINFO OIDC_USERINFO
============= =============
OPTIONAL. ``str``. A string with the location of your class. Read **Standard Claims** section. OPTIONAL. ``str``. A string with the location of your function. Read **Standard Claims** section.
The function receives a ``claims`` dictionary with all the standard claims and ``user`` instance. Must returns the ``claims`` dict again.
Example usage::
def userinfo(claims, user):
claims['name'] = '{0} {1}'.format(user.first_name, user.last_name)
claims['given_name'] = user.first_name
claims['family_name'] = user.last_name
claims['email'] = user.email
claims['address']['street_address'] = '...'
return claims
.. note::
Please **DO NOT** add extra keys or delete the existing ones in the ``claims`` dict. If you want to add extra claims to some scopes you can use the ``OIDC_EXTRA_SCOPE_CLAIMS`` setting.

View file

@ -3,11 +3,20 @@ from django.utils.translation import ugettext as _
from oidc_provider import settings from oidc_provider import settings
STANDARD_CLAIMS = {
'name': '', 'given_name': '', 'family_name': '', 'middle_name': '', 'nickname': '',
'preferred_username': '', 'profile': '', 'picture': '', 'website': '', 'gender': '',
'birthdate': '', 'zoneinfo': '', 'locale': '', 'updated_at': '', 'email': '', 'email_verified': '',
'phone_number': '', 'phone_number_verified': '', 'address': { 'formatted': '',
'street_address': '', 'locality': '', 'region': '', 'postal_code': '', 'country': '', },
}
class ScopeClaims(object): class ScopeClaims(object):
def __init__(self, user, scopes): def __init__(self, user, scopes):
self.user = user self.user = user
self.userinfo = settings.get('OIDC_USERINFO', import_str=True).get_by_user(self.user) self.userinfo = settings.get('OIDC_USERINFO', import_str=True)(STANDARD_CLAIMS, self.user)
self.scopes = scopes self.scopes = scopes
def create_response_dic(self): def create_response_dic(self):
@ -85,20 +94,20 @@ class StandardScopeClaims(ScopeClaims):
) )
def scope_profile(self): def scope_profile(self):
dic = { dic = {
'name': getattr(self.userinfo, 'name', None), 'name': self.userinfo.get('name'),
'given_name': getattr(self.userinfo, 'given_name', None), 'given_name': self.userinfo.get('given_name'),
'family_name': getattr(self.userinfo, 'family_name', None), 'family_name': self.userinfo.get('family_name'),
'middle_name': getattr(self.userinfo, 'middle_name', None), 'middle_name': self.userinfo.get('middle_name'),
'nickname': getattr(self.userinfo, 'nickname', None), 'nickname': self.userinfo.get('nickname'),
'preferred_username': getattr(self.userinfo, 'preferred_username', None), 'preferred_username': self.userinfo.get('preferred_username'),
'profile': getattr(self.userinfo, 'profile', None), 'profile': self.userinfo.get('profile'),
'picture': getattr(self.userinfo, 'picture', None), 'picture': self.userinfo.get('picture'),
'website': getattr(self.userinfo, 'website', None), 'website': self.userinfo.get('website'),
'gender': getattr(self.userinfo, 'gender', None), 'gender': self.userinfo.get('gender'),
'birthdate': getattr(self.userinfo, 'birthdate', None), 'birthdate': self.userinfo.get('birthdate'),
'zoneinfo': getattr(self.userinfo, 'zoneinfo', None), 'zoneinfo': self.userinfo.get('zoneinfo'),
'locale': getattr(self.userinfo, 'locale', None), 'locale': self.userinfo.get('locale'),
'updated_at': getattr(self.userinfo, 'updated_at', None), 'updated_at': self.userinfo.get('updated_at'),
} }
return dic return dic
@ -109,8 +118,8 @@ class StandardScopeClaims(ScopeClaims):
) )
def scope_email(self): def scope_email(self):
dic = { dic = {
'email': getattr(self.user, 'email', None), 'email': self.userinfo.get('email'),
'email_verified': getattr(self.userinfo, 'email_verified', None), 'email_verified': self.userinfo.get('email_verified'),
} }
return dic return dic
@ -121,8 +130,8 @@ class StandardScopeClaims(ScopeClaims):
) )
def scope_phone(self): def scope_phone(self):
dic = { dic = {
'phone_number': getattr(self.userinfo, 'phone_number', None), 'phone_number': self.userinfo.get('phone_number'),
'phone_number_verified': getattr(self.userinfo, 'phone_number_verified', None), 'phone_number_verified': self.userinfo.get('phone_number_verified'),
} }
return dic return dic
@ -134,12 +143,12 @@ class StandardScopeClaims(ScopeClaims):
def scope_address(self): def scope_address(self):
dic = { dic = {
'address': { 'address': {
'formatted': getattr(self.userinfo, 'address_formatted', None), 'formatted': self.userinfo.get('address', {}).get('formatted'),
'street_address': getattr(self.userinfo, 'address_street_address', None), 'street_address': self.userinfo.get('address', {}).get('street_address'),
'locality': getattr(self.userinfo, 'address_locality', None), 'locality': self.userinfo.get('address', {}).get('locality'),
'region': getattr(self.userinfo, 'address_region', None), 'region': self.userinfo.get('address', {}).get('region'),
'postal_code': getattr(self.userinfo, 'address_postal_code', None), 'postal_code': self.userinfo.get('address', {}).get('postal_code'),
'country': getattr(self.userinfo, 'address_country', None), 'country': self.userinfo.get('address', {}).get('country'),
} }
} }

View file

@ -214,10 +214,13 @@ class AuthorizeEndpoint(object):
Return a list with the description of all the scopes requested. Return a list with the description of all the scopes requested.
""" """
scopes = StandardScopeClaims.get_scopes_info(self.params.scope) scopes = StandardScopeClaims.get_scopes_info(self.params.scope)
if settings.get('OIDC_EXTRA_SCOPE_CLAIMS'):
scopes_extra = settings.get('OIDC_EXTRA_SCOPE_CLAIMS', import_str=True).get_scopes_info(self.params.scope) scopes_extra = settings.get('OIDC_EXTRA_SCOPE_CLAIMS', import_str=True).get_scopes_info(self.params.scope)
for index_extra, scope_extra in enumerate(scopes_extra): for index_extra, scope_extra in enumerate(scopes_extra):
for index, scope in enumerate(scopes[:]): for index, scope in enumerate(scopes[:]):
if scope_extra['scope'] == scope['scope']: if scope_extra['scope'] == scope['scope']:
del scopes[index] del scopes[index]
else:
scopes_extra = []
return scopes + scopes_extra return scopes + scopes_extra

View file

@ -45,14 +45,12 @@ def get_issuer(site_url=None, request=None):
return issuer return issuer
class DefaultUserInfo(object): def default_userinfo(claims, user):
""" """
Default class for setting OIDC_USERINFO. Default function for setting OIDC_USERINFO.
`claims` is a dict that contains all the OIDC standard claims.
""" """
return claims
@classmethod
def get_by_user(cls, user):
return None
def default_sub_generator(user): def default_sub_generator(user):

View file

@ -43,7 +43,7 @@ class DefaultSettings(object):
OPTIONAL. A string with the location of your class. OPTIONAL. A string with the location of your class.
Used to add extra scopes specific for your app. Used to add extra scopes specific for your app.
""" """
return 'oidc_provider.lib.claims.ScopeClaims' return None
@property @property
def OIDC_IDTOKEN_EXPIRE(self): def OIDC_IDTOKEN_EXPIRE(self):
@ -95,10 +95,10 @@ class DefaultSettings(object):
@property @property
def OIDC_USERINFO(self): def OIDC_USERINFO(self):
""" """
OPTIONAL. A string with the location of your class. OPTIONAL. A string with the location of your function.
Used to add extra scopes specific for your app. Used to populate standard claims with your user information.
""" """
return 'oidc_provider.lib.utils.common.DefaultUserInfo' return 'oidc_provider.lib.utils.common.default_userinfo'
@property @property
def OIDC_IDTOKEN_PROCESSING_HOOK(self): def OIDC_IDTOKEN_PROCESSING_HOOK(self):

View file

@ -58,4 +58,4 @@ USE_TZ = True
# OIDC Provider settings. # OIDC Provider settings.
SITE_URL = 'http://localhost:8000' SITE_URL = 'http://localhost:8000'
OIDC_USERINFO = 'oidc_provider.tests.app.utils.FakeUserInfo' OIDC_USERINFO = 'oidc_provider.tests.app.utils.userinfo'

View file

@ -73,27 +73,16 @@ def is_code_valid(url, user, client):
return is_code_ok return is_code_ok
class FakeUserInfo(object): def userinfo(claims, user):
""" """
Fake class for setting OIDC_USERINFO. Fake function for setting OIDC_USERINFO.
""" """
claims['given_name'] = 'John'
given_name = 'John' claims['family_name'] = 'Doe'
family_name = 'Doe' claims['name'] = '{0} {1}'.format(claims['given_name'], claims['family_name'])
nickname = 'johndoe' claims['email'] = user.email
website = 'http://johndoe.com' claims['address']['country'] = 'Argentina'
return claims
phone_number = '+49-89-636-48018'
phone_number_verified = True
address_street_address = 'Evergreen 742'
address_locality = 'Glendive'
address_region = 'Montana'
address_country = 'United States'
@classmethod
def get_by_user(cls, user):
return cls()
def fake_sub_generator(user): def fake_sub_generator(user):

View file

@ -157,12 +157,10 @@ def userinfo(request, *args, **kwargs):
} }
standard_claims = StandardScopeClaims(token.user, token.scope) standard_claims = StandardScopeClaims(token.user, token.scope)
dic.update(standard_claims.create_response_dic()) dic.update(standard_claims.create_response_dic())
extra_claims = settings.get('OIDC_EXTRA_SCOPE_CLAIMS', import_str=True)( if settings.get('OIDC_EXTRA_SCOPE_CLAIMS'):
token.user, token.scope) extra_claims = settings.get('OIDC_EXTRA_SCOPE_CLAIMS', import_str=True)(token.user, token.scope)
dic.update(extra_claims.create_response_dic()) dic.update(extra_claims.create_response_dic())
response = JsonResponse(dic, status=200) response = JsonResponse(dic, status=200)