2016-01-25 20:52:24 +00:00
# -*- coding: utf-8 -*-
2016-08-05 19:11:01 +00:00
import base64
import binascii
from hashlib import md5 , sha256
2015-01-28 18:19:36 +00:00
import json
2014-12-19 15:27:43 +00:00
from django . db import models
from django . utils import timezone
2015-12-04 16:55:04 +00:00
from django . utils . translation import ugettext_lazy as _
2015-07-21 13:57:23 +00:00
from django . conf import settings
2014-12-19 15:27:43 +00:00
2016-04-25 17:55:30 +00:00
CLIENT_TYPE_CHOICES = [
( ' confidential ' , ' Confidential ' ) ,
( ' public ' , ' Public ' ) ,
]
RESPONSE_TYPE_CHOICES = [
( ' code ' , ' code (Authorization Code Flow) ' ) ,
( ' id_token ' , ' id_token (Implicit Flow) ' ) ,
( ' id_token token ' , ' id_token token (Implicit Flow) ' ) ,
2016-09-08 19:21:48 +00:00
( ' code token ' , ' code token (Hybrid Flow) ' ) ,
( ' code id_token ' , ' code id_token (Hybrid Flow) ' ) ,
( ' code id_token token ' , ' code id_token token (Hybrid Flow) ' ) ,
2016-04-25 17:55:30 +00:00
]
2014-12-19 15:27:43 +00:00
2016-04-25 17:55:30 +00:00
JWT_ALGS = [
( ' HS256 ' , ' HS256 ' ) ,
( ' RS256 ' , ' RS256 ' ) ,
]
2014-12-19 15:27:43 +00:00
2016-08-08 17:54:40 +00:00
2016-04-25 17:55:30 +00:00
class Client ( models . Model ) :
2016-03-22 19:17:56 +00:00
2016-06-13 15:15:10 +00:00
name = models . CharField ( max_length = 100 , default = ' ' , verbose_name = _ ( u ' Name ' ) )
client_type = models . CharField ( max_length = 30 , choices = CLIENT_TYPE_CHOICES , default = ' confidential ' , verbose_name = _ ( u ' Client Type ' ) , help_text = _ ( u ' <b>Confidential</b> clients are capable of maintaining the confidentiality of their credentials. <b>Public</b> clients are incapable. ' ) )
client_id = models . CharField ( max_length = 255 , unique = True , verbose_name = _ ( u ' Client ID ' ) )
2016-10-05 15:37:49 +00:00
client_secret = models . CharField ( max_length = 255 , blank = True , verbose_name = _ ( u ' Client SECRET ' ) )
2016-06-13 15:15:10 +00:00
response_type = models . CharField ( max_length = 30 , choices = RESPONSE_TYPE_CHOICES , verbose_name = _ ( u ' Response Type ' ) )
2016-09-09 18:57:25 +00:00
jwt_alg = models . CharField ( max_length = 10 , choices = JWT_ALGS , default = ' RS256 ' , verbose_name = _ ( u ' JWT Algorithm ' ) , help_text = _ ( u ' Algorithm used to encode ID Tokens. ' ) )
2016-06-13 15:15:10 +00:00
date_created = models . DateField ( auto_now_add = True , verbose_name = _ ( u ' Date Created ' ) )
2016-09-09 18:57:25 +00:00
website_url = models . CharField ( max_length = 255 , blank = True , default = ' ' , verbose_name = _ ( u ' Website URL ' ) )
terms_url = models . CharField ( max_length = 255 , blank = True , default = ' ' , verbose_name = _ ( u ' Terms URL ' ) , help_text = _ ( u ' External reference to the privacy policy of the client. ' ) )
contact_email = models . CharField ( max_length = 255 , blank = True , default = ' ' , verbose_name = _ ( u ' Contact Email ' ) )
logo = models . FileField ( blank = True , default = ' ' , upload_to = ' oidc_provider/clients ' , verbose_name = _ ( u ' Logo Image ' ) )
2014-12-19 15:27:43 +00:00
2016-06-13 15:15:10 +00:00
_redirect_uris = models . TextField ( default = ' ' , verbose_name = _ ( u ' Redirect URIs ' ) , help_text = _ ( u ' Enter each URI on a new line. ' ) )
2015-12-04 16:55:04 +00:00
class Meta :
verbose_name = _ ( u ' Client ' )
verbose_name_plural = _ ( u ' Clients ' )
2015-05-30 11:54:04 +00:00
def __str__ ( self ) :
2015-11-02 16:19:03 +00:00
return u ' {0} ' . format ( self . name )
2015-05-30 11:54:04 +00:00
def __unicode__ ( self ) :
return self . __str__ ( )
2016-06-13 15:15:10 +00:00
2015-01-06 19:29:31 +00:00
def redirect_uris ( ) :
def fget ( self ) :
return self . _redirect_uris . splitlines ( )
2016-08-08 17:54:40 +00:00
2015-01-06 19:29:31 +00:00
def fset ( self , value ) :
self . _redirect_uris = ' \n ' . join ( value )
2016-08-08 17:54:40 +00:00
2015-01-06 19:29:31 +00:00
return locals ( )
redirect_uris = property ( * * redirect_uris ( ) )
2014-12-19 15:27:43 +00:00
@property
def default_redirect_uri ( self ) :
2015-01-16 18:03:41 +00:00
return self . redirect_uris [ 0 ] if self . redirect_uris else ' '
2014-12-19 15:27:43 +00:00
2015-01-28 18:19:36 +00:00
2015-04-21 18:19:43 +00:00
class BaseCodeTokenModel ( models . Model ) :
2014-12-19 15:27:43 +00:00
2016-06-13 15:15:10 +00:00
user = models . ForeignKey ( settings . AUTH_USER_MODEL , verbose_name = _ ( u ' User ' ) )
client = models . ForeignKey ( Client , verbose_name = _ ( u ' Client ' ) )
expires_at = models . DateTimeField ( verbose_name = _ ( u ' Expiration Date ' ) )
_scope = models . TextField ( default = ' ' , verbose_name = _ ( u ' Scopes ' ) )
2015-05-30 11:54:04 +00:00
2015-04-21 18:19:43 +00:00
def scope ( ) :
def fget ( self ) :
return self . _scope . split ( )
2016-08-08 17:54:40 +00:00
2015-04-21 18:19:43 +00:00
def fset ( self , value ) :
self . _scope = ' ' . join ( value )
2016-08-08 17:54:40 +00:00
2015-04-21 18:19:43 +00:00
return locals ( )
scope = property ( * * scope ( ) )
2014-12-19 15:27:43 +00:00
def has_expired ( self ) :
return timezone . now ( ) > = self . expires_at
2015-05-30 11:54:04 +00:00
def __str__ ( self ) :
2016-06-13 16:26:33 +00:00
return u ' {0} - {1} ' . format ( self . client , self . user . email )
2015-05-30 11:54:04 +00:00
def __unicode__ ( self ) :
return self . __str__ ( )
2016-06-13 15:15:10 +00:00
2015-04-21 18:19:43 +00:00
class Meta :
abstract = True
2015-01-28 18:19:36 +00:00
2014-12-19 15:27:43 +00:00
2015-04-21 18:19:43 +00:00
class Code ( BaseCodeTokenModel ) :
2015-02-06 17:26:34 +00:00
2016-06-13 15:15:10 +00:00
code = models . CharField ( max_length = 255 , unique = True , verbose_name = _ ( u ' Code ' ) )
nonce = models . CharField ( max_length = 255 , blank = True , default = ' ' , verbose_name = _ ( u ' Nonce ' ) )
is_authentication = models . BooleanField ( default = False , verbose_name = _ ( u ' Is Authentication? ' ) )
code_challenge = models . CharField ( max_length = 255 , null = True , verbose_name = _ ( u ' Code Challenge ' ) )
code_challenge_method = models . CharField ( max_length = 255 , null = True , verbose_name = _ ( u ' Code Challenge Method ' ) )
2014-12-19 15:27:43 +00:00
2015-12-04 16:55:04 +00:00
class Meta :
verbose_name = _ ( u ' Authorization Code ' )
verbose_name_plural = _ ( u ' Authorization Codes ' )
2015-01-28 18:19:36 +00:00
2015-04-21 18:19:43 +00:00
class Token ( BaseCodeTokenModel ) :
2016-06-13 15:15:10 +00:00
access_token = models . CharField ( max_length = 255 , unique = True , verbose_name = _ ( u ' Access Token ' ) )
refresh_token = models . CharField ( max_length = 255 , unique = True , null = True , verbose_name = _ ( u ' Refresh Token ' ) )
_id_token = models . TextField ( verbose_name = _ ( u ' ID Token ' ) )
2016-08-08 17:54:40 +00:00
2014-12-19 15:27:43 +00:00
def id_token ( ) :
2016-08-08 17:54:40 +00:00
2014-12-19 15:27:43 +00:00
def fget ( self ) :
return json . loads ( self . _id_token )
2016-08-08 17:54:40 +00:00
2014-12-19 15:27:43 +00:00
def fset ( self , value ) :
self . _id_token = json . dumps ( value )
2016-08-08 17:54:40 +00:00
2014-12-19 15:27:43 +00:00
return locals ( )
id_token = property ( * * id_token ( ) )
2015-12-04 16:55:04 +00:00
class Meta :
verbose_name = _ ( u ' Token ' )
verbose_name_plural = _ ( u ' Tokens ' )
2016-08-05 19:11:01 +00:00
@property
def at_hash ( self ) :
# @@@ d-o-p only supports 256 bits (change this if that changes)
hashed_access_token = sha256 (
self . access_token . encode ( ' ascii ' )
) . hexdigest ( ) . encode ( ' ascii ' )
return base64 . urlsafe_b64encode (
binascii . unhexlify (
hashed_access_token [ : len ( hashed_access_token ) / / 2 ]
)
) . rstrip ( b ' = ' ) . decode ( ' ascii ' )
2015-01-28 18:19:36 +00:00
2015-06-22 21:42:04 +00:00
class UserConsent ( BaseCodeTokenModel ) :
2015-12-04 16:55:04 +00:00
2016-06-13 15:15:10 +00:00
date_given = models . DateTimeField ( verbose_name = _ ( u ' Date Given ' ) )
2015-11-10 10:29:05 +00:00
class Meta :
unique_together = ( ' user ' , ' client ' )
2016-01-25 20:52:24 +00:00
class RSAKey ( models . Model ) :
2016-06-13 15:15:10 +00:00
key = models . TextField ( verbose_name = _ ( u ' Key ' ) , help_text = _ ( u ' Paste your private RSA Key here. ' ) )
2016-01-25 20:52:24 +00:00
class Meta :
verbose_name = _ ( u ' RSA Key ' )
verbose_name_plural = _ ( u ' RSA Keys ' )
def __str__ ( self ) :
return u ' {0} ' . format ( self . kid )
def __unicode__ ( self ) :
return self . __str__ ( )
@property
def kid ( self ) :
2016-08-08 17:54:40 +00:00
return u ' {0} ' . format ( md5 ( self . key . encode ( ' utf-8 ' ) ) . hexdigest ( ) if self . key else ' ' )