MICROBA-149 Create external user id tables (#23064)

* MICROBA-149 Create user id tables

* Move to openedx

* Rename app in doc

* Update PII tag

* Update doc
This commit is contained in:
Christie Rice
2020-02-11 08:17:28 -05:00
committed by GitHub
parent 36d06e949e
commit 1167ce74c8
7 changed files with 194 additions and 0 deletions

View File

@@ -2512,6 +2512,9 @@ INSTALLED_APPS = [
# signal handlers to capture course dates into edx-when
'openedx.core.djangoapps.course_date_signals',
# Management of external user ids
'openedx.core.djangoapps.external_user_ids',
]
######################### CSRF #########################################

View File

@@ -0,0 +1,19 @@
Status: Active
Responsibilities
================
The external_user_ids app links ids to a user. The internal database id
associated with a user may not be appropriate to send outside of Open edX, so
this app contains external ids for users.
This app can link an internal user id to an external user id of a particular
type, and can also link an external user id to an internal id (and thus to a
particular user).
Intended responsibility: Management of ids associated with a user.
Glossary
========
More Documentation
==================

View File

@@ -0,0 +1,101 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.28 on 2020-02-10 17:53
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
import simple_history.models
import uuid
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='ExternalId',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
('external_user_id', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='ExternalIdType',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
('name', models.CharField(db_index=True, max_length=32, unique=True)),
('description', models.TextField()),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='HistoricalExternalId',
fields=[
('id', models.IntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
('external_user_id', models.UUIDField(db_index=True, default=uuid.uuid4, editable=False)),
('history_id', models.AutoField(primary_key=True, serialize=False)),
('history_date', models.DateTimeField()),
('history_change_reason', models.CharField(max_length=100, null=True)),
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
('external_id_type', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='external_user_ids.ExternalIdType')),
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
('user', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL)),
],
options={
'ordering': ('-history_date', '-history_id'),
'get_latest_by': 'history_date',
'verbose_name': 'historical external id',
},
bases=(simple_history.models.HistoricalChanges, models.Model),
),
migrations.CreateModel(
name='HistoricalExternalIdType',
fields=[
('id', models.IntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
('created', model_utils.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False, verbose_name='created')),
('modified', model_utils.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, verbose_name='modified')),
('name', models.CharField(db_index=True, max_length=32)),
('description', models.TextField()),
('history_id', models.AutoField(primary_key=True, serialize=False)),
('history_date', models.DateTimeField()),
('history_change_reason', models.CharField(max_length=100, null=True)),
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
],
options={
'ordering': ('-history_date', '-history_id'),
'get_latest_by': 'history_date',
'verbose_name': 'historical external id type',
},
bases=(simple_history.models.HistoricalChanges, models.Model),
),
migrations.AddField(
model_name='externalid',
name='external_id_type',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='external_user_ids.ExternalIdType'),
),
migrations.AddField(
model_name='externalid',
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
),
]

View File

@@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.28 on 2020-02-10 17:54
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('external_user_ids', '0001_initial'),
]
coaching_name = 'mb_coaching'
def create_mb_coaching_type(apps, schema_editor):
"""
Add a MicroBachelors (MB) coaching type
"""
ExternalIdType = apps.get_model('external_user_ids', 'ExternalIdType')
ExternalIdType.objects.update_or_create(name=Migration.coaching_name, description='MicroBachelors Coaching')
def delete_mb_coaching_type(apps, schema_editor):
"""
Delete the MicroBachelors (MB) coaching type
"""
ExternalIdType = apps.get_model('external_user_ids', 'ExternalIdType')
ExternalIdType.objects.filter(
name=Migration.coaching_name
).delete()
operations = [
migrations.RunPython(create_mb_coaching_type, reverse_code=delete_mb_coaching_type),
]

View File

@@ -0,0 +1,38 @@
"""
Models for External User Ids that are sent out of Open edX
"""
import uuid as uuid_tools
from django.contrib.auth.models import User
from django.db import models
from model_utils.models import TimeStampedModel
from simple_history.models import HistoricalRecords
class ExternalIdType(TimeStampedModel):
"""
ExternalIdType defines the type (purpose, or expected use) of an external id. A user may have one id that is sent
to Company A and another that is sent to Company B.
.. no_pii:
"""
name = models.CharField(max_length=32, blank=False, unique=True, db_index=True)
description = models.TextField()
history = HistoricalRecords()
class ExternalId(TimeStampedModel):
"""
External ids are sent to systems or companies outside of Open edX. This allows us to limit the exposure of any
given id.
An external id is linked to an internal id, so that users may be re-identified if the external id is sent
back to Open edX.
.. no_pii: We store external_user_id here, but do not consider that PII under OEP-30.
"""
external_user_id = models.UUIDField(default=uuid_tools.uuid4, editable=False, unique=True)
external_id_type = models.ForeignKey(ExternalIdType, db_index=True, on_delete=models.CASCADE)
user = models.ForeignKey(User, db_index=True, on_delete=models.CASCADE)
history = HistoricalRecords()