Add new custom DOT Application model to support OAuth2 per-application scopes.
This also introduces a model for persisting organization-based filters on a per-application basis. See openedx/core/djangoapps/oauth_dispatch/docs/decisions/0007-include-organizations-in-tokens.rst for additional details.
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.13 on 2018-06-20 18:22
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django_mysql.models
|
||||
import oauth2_provider.generators
|
||||
import oauth2_provider.validators
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
migrations.swappable_dependency(settings.OAUTH2_PROVIDER_APPLICATION_MODEL),
|
||||
('oauth_dispatch', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ScopedApplication',
|
||||
fields=[
|
||||
('client_id', models.CharField(db_index=True, default=oauth2_provider.generators.generate_client_id, max_length=100, unique=True)),
|
||||
('redirect_uris', models.TextField(blank=True, help_text='Allowed URIs list, space separated', validators=[oauth2_provider.validators.validate_uris])),
|
||||
('client_type', models.CharField(choices=[('confidential', 'Confidential'), ('public', 'Public')], max_length=32)),
|
||||
('authorization_grant_type', models.CharField(choices=[('authorization-code', 'Authorization code'), ('implicit', 'Implicit'), ('password', 'Resource owner password-based'), ('client-credentials', 'Client credentials')], max_length=32)),
|
||||
('client_secret', models.CharField(blank=True, db_index=True, default=oauth2_provider.generators.generate_client_secret, max_length=255)),
|
||||
('name', models.CharField(blank=True, max_length=255)),
|
||||
('skip_authorization', models.BooleanField(default=False)),
|
||||
('id', models.IntegerField(primary_key=True, serialize=False)),
|
||||
('scopes', django_mysql.models.ListCharField(models.CharField(max_length=32), help_text='Comma-separated list of scopes that this application will be allowed to request.', max_length=825, size=25)),
|
||||
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='oauth_dispatch_scopedapplication', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ScopedApplicationOrganization',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('short_name', models.CharField(help_text='The short_name of an existing Organization.', max_length=255)),
|
||||
('provider_type', models.CharField(choices=[(b'content_org', 'Content Provider')], default=b'content_org', max_length=32)),
|
||||
('application', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='organizations', to=settings.OAUTH2_PROVIDER_APPLICATION_MODEL)),
|
||||
],
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,40 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.13 on 2018-06-05 13:19
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def migrate_application_data(apps, schema_editor):
|
||||
"""
|
||||
Migrate existing DOT Application models to new ScopedApplication models.
|
||||
"""
|
||||
Application = apps.get_model(settings.OAUTH2_PROVIDER_APPLICATION_MODEL)
|
||||
ScopedApplication = apps.get_model('oauth_dispatch', 'ScopedApplication')
|
||||
|
||||
for application in Application.objects.all():
|
||||
ScopedApplication.objects.update_or_create(
|
||||
id=application.id,
|
||||
defaults={
|
||||
'client_id': application.client_id,
|
||||
'user': application.user,
|
||||
'redirect_uris': application.redirect_uris,
|
||||
'client_type': application.client_type,
|
||||
'authorization_grant_type': application.authorization_grant_type,
|
||||
'client_secret': application.client_secret,
|
||||
'name': application.name,
|
||||
'skip_authorization': application.skip_authorization,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('oauth_dispatch', '0002_scopedapplication_scopedapplicationorganization'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(migrate_application_data, reverse_code=migrations.RunPython.noop),
|
||||
]
|
||||
@@ -5,6 +5,9 @@ Specialized models for oauth_dispatch djangoapp
|
||||
from datetime import datetime
|
||||
|
||||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django_mysql.models import ListCharField
|
||||
from oauth2_provider.models import AbstractApplication
|
||||
from oauth2_provider.settings import oauth2_settings
|
||||
from pytz import utc
|
||||
|
||||
@@ -43,3 +46,88 @@ class RestrictedApplication(models.Model):
|
||||
is set at the beginning of the epoch which is Jan. 1, 1970
|
||||
"""
|
||||
return access_token.expires == datetime(1970, 1, 1, tzinfo=utc)
|
||||
|
||||
|
||||
class ScopedApplication(AbstractApplication):
|
||||
"""
|
||||
Custom Django OAuth Toolkit Application model that enables the definition
|
||||
of scopes that are authorized for the given Application.
|
||||
"""
|
||||
FILTER_USER_ME = 'user:me'
|
||||
|
||||
# TODO: Remove the id field once we perform the inital migrations for this model.
|
||||
# We need to copy data over from the oauth2_provider.models.Application model to
|
||||
# this new model with the intial migration and the model IDs will need to match
|
||||
# so that existing AccessTokens will still work when switching over to the new model.
|
||||
# Once we have the data copied over we can move back to an auto-increment primary key.
|
||||
id = models.IntegerField(primary_key=True)
|
||||
scopes = ListCharField(
|
||||
base_field=models.CharField(max_length=32),
|
||||
size=25,
|
||||
max_length=(25 * 33), # 25 * 32 character scopes, plus commas
|
||||
help_text=_('Comma-separated list of scopes that this application will be allowed to request.'),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
app_label = 'oauth_dispatch'
|
||||
|
||||
def __unicode__(self):
|
||||
"""
|
||||
Return a unicode representation of this object.
|
||||
"""
|
||||
return u"<ScopedApplication '{name}'>".format(
|
||||
name=self.name
|
||||
)
|
||||
|
||||
@property
|
||||
def authorization_filters(self):
|
||||
"""
|
||||
Return the list of authorization filters for this application.
|
||||
"""
|
||||
filters = [':'.join([org.provider_type, org.short_name]) for org in self.organizations.all()]
|
||||
if self.authorization_grant_type == self.GRANT_CLIENT_CREDENTIALS:
|
||||
filters.append(self.FILTER_USER_ME)
|
||||
return filters
|
||||
|
||||
|
||||
class ScopedApplicationOrganization(models.Model):
|
||||
"""
|
||||
Associates an organization to a given ScopedApplication including the
|
||||
provider type of the organization so that organization-based filters
|
||||
can be added to access tokens provided to the given Application.
|
||||
|
||||
See openedx/core/djangoapps/oauth_dispatch/docs/decisions/0007-include-organizations-in-tokens.rst
|
||||
for the intended use of this model.
|
||||
"""
|
||||
CONTENT_PROVIDER_TYPE = 'content_org'
|
||||
ORGANIZATION_PROVIDER_TYPES = (
|
||||
(CONTENT_PROVIDER_TYPE, _('Content Provider')),
|
||||
)
|
||||
|
||||
# In practice, short_name should match the short_name of an Organization model.
|
||||
# This is not a foreign key because the organizations app is not installed by default.
|
||||
short_name = models.CharField(
|
||||
max_length=255,
|
||||
help_text=_('The short_name of an existing Organization.'),
|
||||
)
|
||||
provider_type = models.CharField(
|
||||
max_length=32,
|
||||
choices=ORGANIZATION_PROVIDER_TYPES,
|
||||
default=CONTENT_PROVIDER_TYPE,
|
||||
)
|
||||
application = models.ForeignKey(
|
||||
oauth2_settings.APPLICATION_MODEL,
|
||||
related_name='organizations',
|
||||
)
|
||||
|
||||
class Meta:
|
||||
app_label = 'oauth_dispatch'
|
||||
|
||||
def __unicode__(self):
|
||||
"""
|
||||
Return a unicode representation of this object.
|
||||
"""
|
||||
return u"<ScopedApplicationOrganization '{application_name}':'{org}'>".format(
|
||||
application_name=self.application.name,
|
||||
org=self.short_name,
|
||||
)
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#
|
||||
# make upgrade
|
||||
#
|
||||
|
||||
common/lib/calc
|
||||
common/lib/chem
|
||||
common/lib/sandbox-packages
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#
|
||||
# make upgrade
|
||||
#
|
||||
|
||||
-e common/lib/calc
|
||||
-e common/lib/chem
|
||||
-e common/lib/sandbox-packages
|
||||
|
||||
@@ -46,6 +46,7 @@ django-memcached-hashring
|
||||
django-method-override==0.1.0
|
||||
django-model-utils==3.0.0
|
||||
django-mptt>=0.8.6,<0.9
|
||||
django-mysql
|
||||
django-oauth-toolkit==0.12.0
|
||||
django-pyfs
|
||||
django-ratelimit
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#
|
||||
# make upgrade
|
||||
#
|
||||
|
||||
-e git+https://github.com/edx/acid-block.git@e46f9cda8a03e121a00c7e347084d142d22ebfb7#egg=acid-xblock
|
||||
-e common/lib/calc
|
||||
-e common/lib/capa
|
||||
@@ -84,6 +83,7 @@ django-method-override==0.1.0
|
||||
django-model-utils==3.0.0
|
||||
django-mptt==0.8.7
|
||||
django-multi-email-field==0.5.1 # via edx-enterprise
|
||||
django-mysql==2.3.0
|
||||
django-oauth-toolkit==0.12.0
|
||||
django-object-actions==0.10.0 # via edx-enterprise
|
||||
django-pyfs==2.0
|
||||
@@ -93,7 +93,7 @@ django-require==1.0.11
|
||||
django-rest-swagger==2.2.0
|
||||
django-sekizai==0.10.0
|
||||
django-ses==0.8.4
|
||||
django-simple-history==2.1.0
|
||||
django-simple-history==2.1.1
|
||||
django-splash==0.2.2
|
||||
django-statici18n==1.4.0
|
||||
django-storages==1.4.1
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#
|
||||
# make upgrade
|
||||
#
|
||||
|
||||
coverage==4.2
|
||||
diff-cover==0.9.8
|
||||
inflect==0.3.1 # via jinja2-pluralize
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#
|
||||
# make upgrade
|
||||
#
|
||||
|
||||
-e git+https://github.com/edx/acid-block.git@e46f9cda8a03e121a00c7e347084d142d22ebfb7#egg=acid-xblock
|
||||
-e common/lib/calc
|
||||
-e common/lib/capa
|
||||
@@ -44,7 +43,7 @@ git+https://github.com/edx-solutions/xblock-drag-and-drop-v2@v2.1.6#egg=xblock-d
|
||||
git+https://github.com/open-craft/xblock-poll@add89e14558c30f3c8dc7431e5cd6536fff6d941#egg=xblock-poll==1.5.1
|
||||
git+https://github.com/edx/xblock-utils.git@v1.1.1#egg=xblock-utils==1.1.1
|
||||
-e common/lib/xmodule
|
||||
alabaster==0.7.10 # via sphinx
|
||||
alabaster==0.7.11 # via sphinx
|
||||
amqp==1.4.9
|
||||
analytics-python==1.1.0
|
||||
anyjson==0.3.3
|
||||
@@ -104,6 +103,7 @@ django-method-override==0.1.0
|
||||
django-model-utils==3.0.0
|
||||
django-mptt==0.8.7
|
||||
django-multi-email-field==0.5.1
|
||||
django-mysql==2.3.0
|
||||
django-oauth-toolkit==0.12.0
|
||||
django-object-actions==0.10.0
|
||||
django-pyfs==2.0
|
||||
@@ -113,7 +113,7 @@ django-require==1.0.11
|
||||
django-rest-swagger==2.2.0
|
||||
django-sekizai==0.10.0
|
||||
django-ses==0.8.4
|
||||
django-simple-history==2.1.0
|
||||
django-simple-history==2.1.1
|
||||
django-splash==0.2.2
|
||||
django-statici18n==1.4.0
|
||||
django-storages==1.4.1
|
||||
@@ -156,7 +156,7 @@ event-tracking==0.2.4
|
||||
execnet==1.5.0
|
||||
extras==1.0.0
|
||||
factory_boy==2.8.1
|
||||
faker==0.8.15
|
||||
faker==0.8.16
|
||||
feedparser==5.1.3
|
||||
firebase-token-generator==1.3.2
|
||||
first==2.0.1
|
||||
@@ -270,7 +270,7 @@ pytest-django==3.1.2
|
||||
pytest-forked==0.2
|
||||
pytest-randomly==1.2.3
|
||||
pytest-xdist==1.22.2
|
||||
pytest==3.6.1
|
||||
pytest==3.6.2
|
||||
python-dateutil==2.4.0
|
||||
python-levenshtein==0.12.0
|
||||
python-memcached==1.48
|
||||
@@ -316,7 +316,7 @@ sqlparse==0.2.4 # via django-debug-toolbar
|
||||
stevedore==1.10.0
|
||||
sure==1.4.11
|
||||
sympy==0.7.1
|
||||
testfixtures==6.1.0
|
||||
testfixtures==6.2.0
|
||||
testtools==2.3.0
|
||||
text-unidecode==1.2
|
||||
tox-battery==0.5.1
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#
|
||||
# make upgrade
|
||||
#
|
||||
|
||||
argh==0.26.2 # via watchdog
|
||||
argparse==1.4.0 # via stevedore
|
||||
edx-opaque-keys==0.4.4
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#
|
||||
# make upgrade
|
||||
#
|
||||
|
||||
click==6.7 # via pip-tools
|
||||
first==2.0.1 # via pip-tools
|
||||
pip-tools==2.0.2
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#
|
||||
# make upgrade
|
||||
#
|
||||
|
||||
-e git+https://github.com/edx/acid-block.git@e46f9cda8a03e121a00c7e347084d142d22ebfb7#egg=acid-xblock
|
||||
-e common/lib/calc
|
||||
-e common/lib/capa
|
||||
@@ -100,6 +99,7 @@ django-method-override==0.1.0
|
||||
django-model-utils==3.0.0
|
||||
django-mptt==0.8.7
|
||||
django-multi-email-field==0.5.1
|
||||
django-mysql==2.3.0
|
||||
django-oauth-toolkit==0.12.0
|
||||
django-object-actions==0.10.0
|
||||
django-pyfs==2.0
|
||||
@@ -109,7 +109,7 @@ django-require==1.0.11
|
||||
django-rest-swagger==2.2.0
|
||||
django-sekizai==0.10.0
|
||||
django-ses==0.8.4
|
||||
django-simple-history==2.1.0
|
||||
django-simple-history==2.1.1
|
||||
django-splash==0.2.2
|
||||
django-statici18n==1.4.0
|
||||
django-storages==1.4.1
|
||||
@@ -150,7 +150,7 @@ event-tracking==0.2.4
|
||||
execnet==1.5.0 # via pytest-xdist
|
||||
extras==1.0.0 # via python-subunit, testtools
|
||||
factory_boy==2.8.1
|
||||
faker==0.8.15 # via factory-boy
|
||||
faker==0.8.16 # via factory-boy
|
||||
feedparser==5.1.3
|
||||
firebase-token-generator==1.3.2
|
||||
fixtures==3.0.0 # via testtools
|
||||
@@ -259,7 +259,7 @@ pytest-django==3.1.2
|
||||
pytest-forked==0.2 # via pytest-xdist
|
||||
pytest-randomly==1.2.3
|
||||
pytest-xdist==1.22.2
|
||||
pytest==3.6.1
|
||||
pytest==3.6.2
|
||||
python-dateutil==2.4.0
|
||||
python-levenshtein==0.12.0
|
||||
python-memcached==1.48
|
||||
@@ -300,7 +300,7 @@ splinter==0.8.0
|
||||
stevedore==1.10.0
|
||||
sure==1.4.11
|
||||
sympy==0.7.1
|
||||
testfixtures==6.1.0
|
||||
testfixtures==6.2.0
|
||||
testtools==2.3.0 # via fixtures, python-subunit
|
||||
text-unidecode==1.2 # via faker
|
||||
tox-battery==0.5.1
|
||||
|
||||
Reference in New Issue
Block a user