Skip to content

Commit

Permalink
Merge 31f80d7 into df5d5a1
Browse files Browse the repository at this point in the history
  • Loading branch information
thedrow committed Feb 11, 2016
2 parents df5d5a1 + 31f80d7 commit 2715754
Show file tree
Hide file tree
Showing 18 changed files with 278 additions and 66 deletions.
6 changes: 5 additions & 1 deletion oauth2_provider/admin.py
@@ -1,12 +1,16 @@
from django.contrib import admin

from .models import Grant, AccessToken, RefreshToken, get_application_model
from .models import (get_grant_model, get_access_token_model,
get_refersh_token_model, get_application_model)


class RawIDAdmin(admin.ModelAdmin):
raw_id_fields = ('user',)

Application = get_application_model()
Grant = get_grant_model()
AccessToken = get_access_token_model()
RefreshToken = get_refersh_token_model()

admin.site.register(Application, RawIDAdmin)
admin.site.register(Grant, RawIDAdmin)
Expand Down
17 changes: 16 additions & 1 deletion oauth2_provider/migrations/0001_initial.py
Expand Up @@ -13,6 +13,9 @@ class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
migrations.swappable_dependency(oauth2_settings.APPLICATION_MODEL),
migrations.swappable_dependency(oauth2_settings.GRANT_MODEL),
migrations.swappable_dependency(oauth2_settings.ACCESS_TOKEN_MODEL),
migrations.swappable_dependency(oauth2_settings.REFRESH_TOKEN_MODEL),
]

operations = [
Expand Down Expand Up @@ -43,6 +46,10 @@ class Migration(migrations.Migration):
('application', models.ForeignKey(to=oauth2_settings.APPLICATION_MODEL)),
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
],
options={
'abstract': False,
'swappable': 'OAUTH2_PROVIDER_ACCESS_TOKEN_MODEL',
},
),
migrations.CreateModel(
name='Grant',
Expand All @@ -55,15 +62,23 @@ class Migration(migrations.Migration):
('application', models.ForeignKey(to=oauth2_settings.APPLICATION_MODEL)),
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
],
options={
'abstract': False,
'swappable': 'OAUTH2_PROVIDER_GRANT_MODEL',
},
),
migrations.CreateModel(
name='RefreshToken',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('token', models.CharField(max_length=255, db_index=True)),
('access_token', models.OneToOneField(related_name='refresh_token', to='oauth2_provider.AccessToken')),
('access_token', models.OneToOneField(related_name='refresh_token', to=oauth2_settings.ACCESS_TOKEN_MODEL)),
('application', models.ForeignKey(to=oauth2_settings.APPLICATION_MODEL)),
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
],
options={
'abstract': False,
'swappable': 'OAUTH2_PROVIDER_REFRESH_TOKEN_MODEL',
},
),
]
79 changes: 72 additions & 7 deletions oauth2_provider/models.py
Expand Up @@ -127,11 +127,11 @@ class Application(AbstractApplication):
pass

# Add swappable like this to not break django 1.4 compatibility
Application._meta.swappable = 'OAUTH2_PROVIDER_APPLICATION_MODEL'
Application._meta.swappable = 'OAUTH2_PROVIDER_ACCESS_TOKEN_MODEL'


@python_2_unicode_compatible
class Grant(models.Model):
class AbstractGrant(models.Model):
"""
A Grant instance represents a token with a short lifetime that can
be swapped for an access token, as described in :rfc:`4.1.2`
Expand All @@ -153,6 +153,9 @@ class Grant(models.Model):
redirect_uri = models.CharField(max_length=255)
scope = models.TextField(blank=True)

class Meta:
abstract = True

def is_expired(self):
"""
Check token expiration with timezone awareness
Expand All @@ -168,9 +171,14 @@ def redirect_uri_allowed(self, uri):
def __str__(self):
return self.code

class Grant(AbstractGrant):
pass

# Add swappable like this to not break django 1.4 compatibility
Grant._meta.swappable = 'OAUTH2_PROVIDER_GRANT_MODEL'

@python_2_unicode_compatible
class AccessToken(models.Model):
class AbstractAccessToken(models.Model):
"""
An AccessToken instance represents the actual access token to
access user's resources, as in :rfc:`5`.
Expand All @@ -189,6 +197,9 @@ class AccessToken(models.Model):
expires = models.DateTimeField()
scope = models.TextField(blank=True)

class Meta:
abstract = True

def is_valid(self, scopes=None):
"""
Checks if the access token is valid.
Expand Down Expand Up @@ -237,9 +248,15 @@ def scopes(self):
def __str__(self):
return self.token

class AccessToken(AbstractAccessToken):
pass

# Add swappable like this to not break django 1.4 compatibility
AccessToken._meta.swappable = 'OAUTH2_PROVIDER_ACCESS_TOKEN_MODEL'


@python_2_unicode_compatible
class RefreshToken(models.Model):
class AbstractRefreshToken(models.Model):
"""
A RefreshToken instance represents a token that can be swapped for a new
access token when it expires.
Expand All @@ -255,9 +272,12 @@ class RefreshToken(models.Model):
user = models.ForeignKey(AUTH_USER_MODEL)
token = models.CharField(max_length=255, db_index=True)
application = models.ForeignKey(oauth2_settings.APPLICATION_MODEL)
access_token = models.OneToOneField(AccessToken,
access_token = models.OneToOneField(oauth2_settings.ACCESS_TOKEN_MODEL,
related_name='refresh_token')

class Meta:
abstract = True

def revoke(self):
"""
Delete this refresh token along with related access token
Expand All @@ -268,20 +288,65 @@ def revoke(self):
def __str__(self):
return self.token

class RefreshToken(AbstractRefreshToken):
pass

# Add swappable like this to not break django 1.4 compatibility
RefreshToken._meta.swappable = 'OAUTH2_PROVIDER_REFRESH_TOKEN_MODEL'

def get_application_model():
""" Return the Application model that is active in this project. """
try:
app_label, model_name = oauth2_settings.APPLICATION_MODEL.split('.')
except ValueError:
e = "APPLICATION_MODEL must be of the form 'app_label.model_name'"
e = "ACCESS_TOKEN_MODEL must be of the form 'app_label.model_name'"
raise ImproperlyConfigured(e)
app_model = get_model(app_label, model_name)
if app_model is None:
e = "APPLICATION_MODEL refers to model {0} that has not been installed"
e = "ACCESS_TOKEN_MODEL refers to model {0} that has not been installed"
raise ImproperlyConfigured(e.format(oauth2_settings.APPLICATION_MODEL))
return app_model

def get_grant_model():
""" Return the Grant model that is active in this project. """
try:
app_label, model_name = oauth2_settings.GRANT_MODEL.split('.')
except ValueError:
e = "GRANT_MODEL must be of the form 'app_label.model_name'"
raise ImproperlyConfigured(e)
app_model = get_model(app_label, model_name)
if app_model is None:
e = "GRANT_MODEL refers to model {0} that has not been installed"
raise ImproperlyConfigured(e.format(oauth2_settings.GRANT_MODEL))
return app_model


def get_access_token_model():
""" Return the AccessToken model that is active in this project. """
try:
app_label, model_name = oauth2_settings.ACCESS_TOKEN_MODEL.split('.')
except ValueError:
e = "ACCESS_TOKEN_MODEL must be of the form 'app_label.model_name'"
raise ImproperlyConfigured(e)
app_model = get_model(app_label, model_name)
if app_model is None:
e = "ACCESS_TOKEN_MODEL refers to model {0} that has not been installed"
raise ImproperlyConfigured(e.format(oauth2_settings.ACCESS_TOKEN_MODEL))
return app_model


def get_refersh_token_model():
""" Return the Application model that is active in this project. """
try:
app_label, model_name = oauth2_settings.REFRESH_TOKEN_MODEL.split('.')
except ValueError:
e = "REFRESH_TOKEN_MODEL must be of the form 'app_label.model_name'"
raise ImproperlyConfigured(e)
app_model = get_model(app_label, model_name)
if app_model is None:
e = "REFRESH_TOKEN_MODEL refers to model {0} that has not been installed"
raise ImproperlyConfigured(e.format(oauth2_settings.REFRESH_TOKEN_MODEL))
return app_model

def clear_expired():
now = timezone.now()
Expand Down
18 changes: 17 additions & 1 deletion oauth2_provider/oauth2_validators.py
Expand Up @@ -12,7 +12,9 @@
from oauthlib.oauth2 import RequestValidator

from .compat import unquote_plus
from .models import Grant, AccessToken, RefreshToken, get_application_model, AbstractApplication
from .models import (get_grant_model, get_access_token_model,
get_refersh_token_model, get_application_model,
AbstractApplication)
from .settings import oauth2_settings

log = logging.getLogger('oauth2_provider')
Expand Down Expand Up @@ -198,13 +200,15 @@ def confirm_redirect_uri(self, client_id, code, redirect_uri, client, *args, **k
"""
Ensure the redirect_uri is listed in the Application instance redirect_uris field
"""
Grant = get_grant_model()
grant = Grant.objects.get(code=code, application=client)
return grant.redirect_uri_allowed(redirect_uri)

def invalidate_authorization_code(self, client_id, code, request, *args, **kwargs):
"""
Remove the temporary grant used to swap the authorization token
"""
Grant = get_grant_model()
grant = Grant.objects.get(code=code, application=request.client)
grant.delete()

Expand All @@ -225,6 +229,8 @@ def validate_bearer_token(self, token, scopes, request):
if not token:
return False

AccessToken = get_access_token_model()

try:
access_token = AccessToken.objects.select_related("application", "user").get(
token=token)
Expand All @@ -241,6 +247,7 @@ def validate_bearer_token(self, token, scopes, request):
return False

def validate_code(self, client_id, code, client, request, *args, **kwargs):
Grant = get_grant_model()
try:
grant = Grant.objects.get(code=code, application=client)
if not grant.is_expired():
Expand Down Expand Up @@ -286,6 +293,7 @@ def validate_redirect_uri(self, client_id, redirect_uri, request, *args, **kwarg
def save_authorization_code(self, client_id, code, request, *args, **kwargs):
expires = timezone.now() + timedelta(
seconds=oauth2_settings.AUTHORIZATION_CODE_EXPIRE_SECONDS)
Grant = get_grant_model()
g = Grant(application=request.client, user=request.user, code=code['code'],
expires=expires, redirect_uri=request.redirect_uri,
scope=' '.join(request.scopes))
Expand All @@ -296,6 +304,8 @@ def save_bearer_token(self, token, request, *args, **kwargs):
Save access and refresh token, If refresh token is issued, remove old refresh tokens as
in rfc:`6`
"""
RefreshToken = get_refersh_token_model()

if request.refresh_token:
# remove used refresh token
try:
Expand All @@ -307,6 +317,8 @@ def save_bearer_token(self, token, request, *args, **kwargs):
if request.grant_type == 'client_credentials':
request.user = None

AccessToken = get_access_token_model()

access_token = AccessToken(
user=request.user,
scope=token['scope'],
Expand Down Expand Up @@ -338,6 +350,9 @@ def revoke_token(self, token, token_type_hint, request, *args, **kwargs):
if token_type_hint not in ['access_token', 'refresh_token']:
token_type_hint = None

AccessToken = get_access_token_model()
RefreshToken = get_refersh_token_model()

token_types = {
'access_token': AccessToken,
'refresh_token': RefreshToken,
Expand Down Expand Up @@ -372,6 +387,7 @@ def validate_refresh_token(self, refresh_token, client, request, *args, **kwargs
Check refresh_token exists and refers to the right client.
Also attach User instance to the request object
"""
RefreshToken = get_refersh_token_model()
try:
rt = RefreshToken.objects.get(token=refresh_token)
request.user = rt.user
Expand Down
3 changes: 3 additions & 0 deletions oauth2_provider/settings.py
Expand Up @@ -44,6 +44,9 @@
'ACCESS_TOKEN_EXPIRE_SECONDS': 36000,
'REFRESH_TOKEN_EXPIRE_SECONDS': None,
'APPLICATION_MODEL': getattr(settings, 'OAUTH2_PROVIDER_APPLICATION_MODEL', 'oauth2_provider.Application'),
'GRANT_MODEL': getattr(settings, 'OAUTH2_PROVIDER_GRANT_MODEL', 'oauth2_provider.Grant'),
'ACCESS_TOKEN_MODEL': getattr(settings, 'OAUTH2_PROVIDER_ACCESS_TOKEN_MODEL', 'oauth2_provider.AccessToken'),
'REFRESH_TOKEN_MODEL': getattr(settings, 'OAUTH2_PROVIDER_REFRESH_TOKEN_MODEL', 'oauth2_provider.RefreshToken'),
'REQUEST_APPROVAL_PROMPT': 'force',
'ALLOWED_REDIRECT_URI_SCHEMES': ['http', 'https'],

Expand Down
27 changes: 16 additions & 11 deletions oauth2_provider/south_migrations/0001_initial.py
Expand Up @@ -11,9 +11,14 @@
else:
User = get_user_model()

from oauth2_provider.models import get_application_model
from oauth2_provider.models import (get_application_model,
get_access_token_model,
get_grant_model,
get_refersh_token_model)
ApplicationModel = get_application_model()

GrantModel = get_grant_model()
AccessTokenModel = get_access_token_model()
RefreshTokenModel = get_refersh_token_model

class Migration(SchemaMigration):

Expand Down Expand Up @@ -60,7 +65,7 @@ def forwards(self, orm):
('user', self.gf('django.db.models.fields.related.ForeignKey')(to=orm["%s.%s" % (User._meta.app_label, User._meta.object_name)])),
('token', self.gf('django.db.models.fields.CharField')(max_length=255)),
('application', self.gf('django.db.models.fields.related.ForeignKey')(to=orm["%s.%s" % (ApplicationModel._meta.app_label, ApplicationModel._meta.object_name)])),
('access_token', self.gf('django.db.models.fields.related.OneToOneField')(related_name='refresh_token', unique=True, to=orm['oauth2_provider.AccessToken'])),
('access_token', self.gf('django.db.models.fields.related.OneToOneField')(related_name='refresh_token', unique=True, to=ormorm["%s.%s" % (AccessTokenModel._meta.app_label, AccessTokenModel._meta.object_name)])),
))
db.send_create_signal(u'oauth2_provider', ['RefreshToken'])

Expand Down Expand Up @@ -116,8 +121,8 @@ def backwards(self, orm):
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
u'oauth2_provider.accesstoken': {
'Meta': {'object_name': 'AccessToken'},
u"%s.%s" % (AccessTokenModel._meta.app_label, AccessTokenModel._meta.object_name): {
'Meta': {'object_name': AccessTokenModel.__name__},
'application': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['%s.%s']"% (ApplicationModel._meta.app_label, ApplicationModel._meta.object_name)}),
'expires': ('django.db.models.fields.DateTimeField', [], {}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
Expand All @@ -136,8 +141,8 @@ def backwards(self, orm):
'redirect_uris': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['%s.%s']"% (User._meta.app_label, User._meta.object_name)})
},
u'oauth2_provider.grant': {
'Meta': {'object_name': 'Grant'},
u"%s.%s" % (GrantModel._meta.app_label, GrantModel._meta.object_name)': {
'Meta': {'object_name': GrantModel.__name__},
'application': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['%s.%s']"% (ApplicationModel._meta.app_label, ApplicationModel._meta.object_name)}),
'code': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'expires': ('django.db.models.fields.DateTimeField', [], {}),
Expand All @@ -146,14 +151,14 @@ def backwards(self, orm):
'scope': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['%s.%s']"% (User._meta.app_label, User._meta.object_name)})
},
u'oauth2_provider.refreshtoken': {
'Meta': {'object_name': 'RefreshToken'},
'access_token': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'refresh_token'", 'unique': 'True', 'to': u"orm['oauth2_provider.AccessToken']"}),
u"%s.%s" % (RefreshTokenModel._meta.app_label, RefreshTokenModel._meta.object_name)': {
'Meta': {'object_name': RefreshTokenModel.__name__},
'access_token': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'refresh_token'", 'unique': 'True', 'to': {'to': u"orm['%s.%s']"% (AccessTokenModel._meta.app_label, AccessTokenModel._meta.object_name)}}),
'application': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['%s.%s']"% (ApplicationModel._meta.app_label, ApplicationModel._meta.object_name)}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'token': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['%s.%s']"% (User._meta.app_label, User._meta.object_name)})
}
}

complete_apps = ['oauth2_provider']
complete_apps = ['oauth2_provider']

0 comments on commit 2715754

Please sign in to comment.