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:
@@ -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 #########################################
|
||||
|
||||
19
openedx/core/djangoapps/external_user_ids/README.rst
Normal file
19
openedx/core/djangoapps/external_user_ids/README.rst
Normal 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
|
||||
==================
|
||||
@@ -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),
|
||||
),
|
||||
]
|
||||
@@ -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),
|
||||
]
|
||||
38
openedx/core/djangoapps/external_user_ids/models.py
Normal file
38
openedx/core/djangoapps/external_user_ids/models.py
Normal 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()
|
||||
Reference in New Issue
Block a user