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 |
+====================+================+=======================+========================+
| 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 | | | |
+--------------------+----------------+-----------------------+------------------------+
@ -41,35 +41,22 @@ List of all the attributes grouped by scopes:
| updated_at | | | |
+--------------------+----------------+-----------------------+------------------------+
Example using a django model::
Somewhere in your Django ``settings.py``::
from django.conf import settings
from django.db import models
OIDC_USERINFO = 'myproject.oidc_provider_settings.userinfo'
class UserInfo(models.Model):
Then create the function for the ``OIDC_USERINFO`` setting::
GENDER_CHOICES = [
('F', 'Female'),
('M', 'Male'),
]
def userinfo(claims, user):
user = models.OneToOneField(settings.AUTH_USER_MODEL, primary_key=True)
given_name = models.CharField(max_length=255, blank=True, null=True)
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)
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'] = '...'
email_verified = models.NullBooleanField(default=False)
return claims
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)
.. 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

@ -162,4 +162,21 @@ Expressed in seconds. Default is ``60*60``.
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
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):
def __init__(self, user, scopes):
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
def create_response_dic(self):
@ -85,20 +94,20 @@ class StandardScopeClaims(ScopeClaims):
)
def scope_profile(self):
dic = {
'name': getattr(self.userinfo, 'name', None),
'given_name': getattr(self.userinfo, 'given_name', None),
'family_name': getattr(self.userinfo, 'family_name', None),
'middle_name': getattr(self.userinfo, 'middle_name', None),
'nickname': getattr(self.userinfo, 'nickname', None),
'preferred_username': getattr(self.userinfo, 'preferred_username', None),
'profile': getattr(self.userinfo, 'profile', None),
'picture': getattr(self.userinfo, 'picture', None),
'website': getattr(self.userinfo, 'website', None),
'gender': getattr(self.userinfo, 'gender', None),
'birthdate': getattr(self.userinfo, 'birthdate', None),
'zoneinfo': getattr(self.userinfo, 'zoneinfo', None),
'locale': getattr(self.userinfo, 'locale', None),
'updated_at': getattr(self.userinfo, 'updated_at', None),
'name': self.userinfo.get('name'),
'given_name': self.userinfo.get('given_name'),
'family_name': self.userinfo.get('family_name'),
'middle_name': self.userinfo.get('middle_name'),
'nickname': self.userinfo.get('nickname'),
'preferred_username': self.userinfo.get('preferred_username'),
'profile': self.userinfo.get('profile'),
'picture': self.userinfo.get('picture'),
'website': self.userinfo.get('website'),
'gender': self.userinfo.get('gender'),
'birthdate': self.userinfo.get('birthdate'),
'zoneinfo': self.userinfo.get('zoneinfo'),
'locale': self.userinfo.get('locale'),
'updated_at': self.userinfo.get('updated_at'),
}
return dic
@ -109,8 +118,8 @@ class StandardScopeClaims(ScopeClaims):
)
def scope_email(self):
dic = {
'email': getattr(self.user, 'email', None),
'email_verified': getattr(self.userinfo, 'email_verified', None),
'email': self.userinfo.get('email'),
'email_verified': self.userinfo.get('email_verified'),
}
return dic
@ -121,8 +130,8 @@ class StandardScopeClaims(ScopeClaims):
)
def scope_phone(self):
dic = {
'phone_number': getattr(self.userinfo, 'phone_number', None),
'phone_number_verified': getattr(self.userinfo, 'phone_number_verified', None),
'phone_number': self.userinfo.get('phone_number'),
'phone_number_verified': self.userinfo.get('phone_number_verified'),
}
return dic
@ -134,12 +143,12 @@ class StandardScopeClaims(ScopeClaims):
def scope_address(self):
dic = {
'address': {
'formatted': getattr(self.userinfo, 'address_formatted', None),
'street_address': getattr(self.userinfo, 'address_street_address', None),
'locality': getattr(self.userinfo, 'address_locality', None),
'region': getattr(self.userinfo, 'address_region', None),
'postal_code': getattr(self.userinfo, 'address_postal_code', None),
'country': getattr(self.userinfo, 'address_country', None),
'formatted': self.userinfo.get('address', {}).get('formatted'),
'street_address': self.userinfo.get('address', {}).get('street_address'),
'locality': self.userinfo.get('address', {}).get('locality'),
'region': self.userinfo.get('address', {}).get('region'),
'postal_code': self.userinfo.get('address', {}).get('postal_code'),
'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.
"""
scopes = StandardScopeClaims.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, scope in enumerate(scopes[:]):
if scope_extra['scope'] == scope['scope']:
del scopes[index]
if settings.get('OIDC_EXTRA_SCOPE_CLAIMS'):
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, scope in enumerate(scopes[:]):
if scope_extra['scope'] == scope['scope']:
del scopes[index]
else:
scopes_extra = []
return scopes + scopes_extra

View file

@ -45,14 +45,12 @@ def get_issuer(site_url=None, request=None):
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.
"""
@classmethod
def get_by_user(cls, user):
return None
return claims
def default_sub_generator(user):

View file

@ -41,9 +41,9 @@ class DefaultSettings(object):
def OIDC_EXTRA_SCOPE_CLAIMS(self):
"""
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
def OIDC_IDTOKEN_EXPIRE(self):
@ -95,10 +95,10 @@ class DefaultSettings(object):
@property
def OIDC_USERINFO(self):
"""
OPTIONAL. A string with the location of your class.
Used to add extra scopes specific for your app.
OPTIONAL. A string with the location of your function.
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
def OIDC_IDTOKEN_PROCESSING_HOOK(self):

View file

@ -58,4 +58,4 @@ USE_TZ = True
# OIDC Provider settings.
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
class FakeUserInfo(object):
def userinfo(claims, user):
"""
Fake class for setting OIDC_USERINFO.
Fake function for setting OIDC_USERINFO.
"""
given_name = 'John'
family_name = 'Doe'
nickname = 'johndoe'
website = 'http://johndoe.com'
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()
claims['given_name'] = 'John'
claims['family_name'] = 'Doe'
claims['name'] = '{0} {1}'.format(claims['given_name'], claims['family_name'])
claims['email'] = user.email
claims['address']['country'] = 'Argentina'
return claims
def fake_sub_generator(user):

View file

@ -157,13 +157,11 @@ def userinfo(request, *args, **kwargs):
}
standard_claims = StandardScopeClaims(token.user, token.scope)
dic.update(standard_claims.create_response_dic())
extra_claims = settings.get('OIDC_EXTRA_SCOPE_CLAIMS', import_str=True)(
token.user, token.scope)
dic.update(extra_claims.create_response_dic())
if settings.get('OIDC_EXTRA_SCOPE_CLAIMS'):
extra_claims = settings.get('OIDC_EXTRA_SCOPE_CLAIMS', import_str=True)(token.user, token.scope)
dic.update(extra_claims.create_response_dic())
response = JsonResponse(dic, status=200)
response['Cache-Control'] = 'no-store'