Merge pull request #17888 from edx/astankiewicz/sso_id_verification
ENT-943 Refactor ID verification models
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.12 on 2018-04-11 15:20
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
import model_utils.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('verify_student', '0005_remove_deprecated_models'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='SSOVerification',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('status', model_utils.fields.StatusField(choices=[(b'created', b'created'), (b'ready', b'ready'), (b'submitted', b'submitted'), (b'must_retry', b'must_retry'), (b'approved', b'approved'), (b'denied', b'denied')], default=b'created', max_length=100, no_check_for_status=True, verbose_name='status')),
|
||||
('status_changed', model_utils.fields.MonitorField(default=django.utils.timezone.now, monitor='status', verbose_name='status changed')),
|
||||
('name', models.CharField(blank=True, max_length=255)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, db_index=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True, db_index=True)),
|
||||
('identity_provider_type', models.CharField(choices=[(b'third_party_auth.models.OAuth2ProviderConfig', b'OAuth2 Provider'), (b'third_party_auth.models.SAMLProviderConfig', b'SAML Provider'), (b'third_party_auth.models.LTIProviderConfig', b'LTI Provider')], default=b'third_party_auth.models.SAMLProviderConfig', help_text=b'Specifies which type of Identity Provider this verification originated from.', max_length=100)),
|
||||
('identity_provider_slug', models.SlugField(default=b'default', help_text=b'The slug uniquely identifying the Identity Provider this verification originated from.', max_length=30)),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
]
|
||||
@@ -88,7 +88,79 @@ def status_before_must_be(*valid_start_statuses):
|
||||
return decorator_func
|
||||
|
||||
|
||||
class PhotoVerification(StatusModel):
|
||||
class IDVerificationAttempt(StatusModel):
|
||||
"""
|
||||
Each IDVerificationAttempt represents a Student's attempt to establish
|
||||
their identity through one of several methods that inherit from this Model,
|
||||
including PhotoVerification and SSOVerification.
|
||||
"""
|
||||
STATUS = Choices('created', 'ready', 'submitted', 'must_retry', 'approved', 'denied')
|
||||
user = models.ForeignKey(User, db_index=True)
|
||||
|
||||
# They can change their name later on, so we want to copy the value here so
|
||||
# we always preserve what it was at the time they requested. We only copy
|
||||
# this value during the mark_ready() step. Prior to that, you should be
|
||||
# displaying the user's name from their user.profile.name.
|
||||
name = models.CharField(blank=True, max_length=255)
|
||||
|
||||
created_at = models.DateTimeField(auto_now_add=True, db_index=True)
|
||||
updated_at = models.DateTimeField(auto_now=True, db_index=True)
|
||||
|
||||
class Meta(object):
|
||||
app_label = "verify_student"
|
||||
abstract = True
|
||||
ordering = ['-created_at']
|
||||
|
||||
@property
|
||||
def expiration_datetime(self):
|
||||
"""Datetime that the verification will expire. """
|
||||
days_good_for = settings.VERIFY_STUDENT["DAYS_GOOD_FOR"]
|
||||
return self.created_at + timedelta(days=days_good_for)
|
||||
|
||||
|
||||
class SSOVerification(IDVerificationAttempt):
|
||||
"""
|
||||
Each SSOVerification represents a Student's attempt to establish their identity
|
||||
by signing in with SSO. ID verification through SSO bypasses the need for
|
||||
photo verification.
|
||||
"""
|
||||
|
||||
OAUTH2 = 'third_party_auth.models.OAuth2ProviderConfig'
|
||||
SAML = 'third_party_auth.models.SAMLProviderConfig'
|
||||
LTI = 'third_party_auth.models.LTIProviderConfig'
|
||||
IDENTITY_PROVIDER_TYPE_CHOICES = (
|
||||
(OAUTH2, 'OAuth2 Provider'),
|
||||
(SAML, 'SAML Provider'),
|
||||
(LTI, 'LTI Provider'),
|
||||
)
|
||||
|
||||
identity_provider_type = models.CharField(
|
||||
max_length=100,
|
||||
blank=False,
|
||||
choices=IDENTITY_PROVIDER_TYPE_CHOICES,
|
||||
default=SAML,
|
||||
help_text=(
|
||||
'Specifies which type of Identity Provider this verification originated from.'
|
||||
)
|
||||
)
|
||||
|
||||
identity_provider_slug = models.SlugField(
|
||||
max_length=30, db_index=True, default='default',
|
||||
help_text=(
|
||||
'The slug uniquely identifying the Identity Provider this verification originated from.'
|
||||
))
|
||||
|
||||
class Meta(object):
|
||||
app_label = "verify_student"
|
||||
|
||||
def __unicode__(self):
|
||||
return 'SSOIDVerification for {name}, status: {status}'.format(
|
||||
name=self.name,
|
||||
status=self.status,
|
||||
)
|
||||
|
||||
|
||||
class PhotoVerification(IDVerificationAttempt):
|
||||
"""
|
||||
Each PhotoVerification represents a Student's attempt to establish
|
||||
their identity by uploading a photo of themselves and a picture ID. An
|
||||
@@ -122,7 +194,8 @@ class PhotoVerification(StatusModel):
|
||||
student cannot re-open this attempt -- they have to create another
|
||||
attempt and submit it instead.
|
||||
|
||||
Because this Model inherits from StatusModel, we can also do things like::
|
||||
Because this Model inherits from IDVerificationAttempt, which inherits
|
||||
from StatusModel, we can also do things like:
|
||||
|
||||
attempt.status == PhotoVerification.STATUS.created
|
||||
attempt.status == "created"
|
||||
@@ -130,15 +203,6 @@ class PhotoVerification(StatusModel):
|
||||
"""
|
||||
######################## Fields Set During Creation ########################
|
||||
# See class docstring for description of status states
|
||||
STATUS = Choices('created', 'ready', 'submitted', 'must_retry', 'approved', 'denied')
|
||||
user = models.ForeignKey(User, db_index=True)
|
||||
|
||||
# They can change their name later on, so we want to copy the value here so
|
||||
# we always preserve what it was at the time they requested. We only copy
|
||||
# this value during the mark_ready() step. Prior to that, you should be
|
||||
# displaying the user's name from their user.profile.name.
|
||||
name = models.CharField(blank=True, max_length=255)
|
||||
|
||||
# Where we place the uploaded image files (e.g. S3 URLs)
|
||||
face_image_url = models.URLField(blank=True, max_length=255)
|
||||
photo_id_image_url = models.URLField(blank=True, max_length=255)
|
||||
@@ -152,9 +216,6 @@ class PhotoVerification(StatusModel):
|
||||
max_length=255,
|
||||
)
|
||||
|
||||
created_at = models.DateTimeField(auto_now_add=True, db_index=True)
|
||||
updated_at = models.DateTimeField(auto_now=True, db_index=True)
|
||||
|
||||
# Indicates whether or not a user wants to see the verification status
|
||||
# displayed on their dash. Right now, only relevant for allowing students
|
||||
# to "dismiss" a failed midcourse reverification message
|
||||
@@ -191,12 +252,6 @@ class PhotoVerification(StatusModel):
|
||||
abstract = True
|
||||
ordering = ['-created_at']
|
||||
|
||||
@property
|
||||
def expiration_datetime(self):
|
||||
"""Datetime that the verification will expire. """
|
||||
days_good_for = settings.VERIFY_STUDENT["DAYS_GOOD_FOR"]
|
||||
return self.created_at + timedelta(days=days_good_for)
|
||||
|
||||
def active_at_datetime(self, deadline):
|
||||
"""Check whether the verification was active at a particular datetime.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user