Merge pull request #17888 from edx/astankiewicz/sso_id_verification

ENT-943 Refactor ID verification models
This commit is contained in:
Brittney Exline
2018-04-11 11:09:43 -06:00
committed by GitHub
2 changed files with 109 additions and 20 deletions

View File

@@ -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)),
],
),
]

View File

@@ -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.