Skip to content

Commit 92b84b5

Browse files
authored
Merge pull request #4789 from kobotoolbox/remove-duplicate-django-allauth-app
Refactor django-allauth migration
2 parents f345911 + b1ce806 commit 92b84b5

File tree

6 files changed

+94
-72
lines changed

6 files changed

+94
-72
lines changed

kobo/apps/accounts/admin.py

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
from django.contrib import admin
21
from allauth.account.models import EmailAddress
2+
from allauth.socialaccount.admin import SocialAppForm, SocialAppAdmin
3+
from allauth.socialaccount.models import SocialApp
4+
from django.contrib import admin
5+
from django.core.exceptions import ValidationError
6+
from django.db.models import Q
37

48
from .models import EmailAddressAdmin, SocialAppCustomData
59
from kobo.apps.accounts.models import EmailContent
@@ -13,4 +17,50 @@ class EmailContentView(admin.ModelAdmin):
1317
admin.site.unregister(EmailAddress)
1418
admin.site.register(EmailAddress, EmailAddressAdmin)
1519

20+
21+
class RequireProviderIdSocialAppForm(SocialAppForm):
22+
def __init__(self, *args, **kwargs):
23+
super(SocialAppForm, self).__init__(*args, **kwargs)
24+
# require the provider_id in the admin, since we can't make it required on allauth's model
25+
self.fields['provider_id'].required = True
26+
27+
def clean_provider_id(self):
28+
reserved_keywords = ['kobo']
29+
provider_id = self.cleaned_data.get('provider_id')
30+
"""
31+
Don't allow `kobo` to be set as the `provider_id` value in `SOCIALACCOUNT_PROVIDERS`
32+
settings because it breaks the login page redirect when language is changed.
33+
"""
34+
if provider_id in reserved_keywords:
35+
raise ValidationError(
36+
f'`{provider_id}` is not a valid value for the `provider_id` setting.'
37+
)
38+
39+
"""
40+
By default, django-allauth only supports showing one provider on the login screen.
41+
But OIDC providers allow multiple subproviders, so kpi has some additional code to display multiple providers.
42+
Because of that, we need to make sure that the `provider` and `provider_id` fields are unique.
43+
django-allauth (as of 0.57.0) technically enforces this on the model level, but in practice it's flawed.
44+
"""
45+
if SocialApp.objects.filter(
46+
Q(provider_id=provider_id) |
47+
Q(provider=provider_id)
48+
).exclude(pk=self.instance.pk).exists():
49+
raise ValidationError(
50+
"""The Provider ID value must be unique and cannot match an existing Provider name.
51+
Please use a different value."""
52+
)
53+
return provider_id
54+
55+
56+
class RequireProviderIdSocialAppAdmin(SocialAppAdmin):
57+
form = RequireProviderIdSocialAppForm
58+
59+
class Meta:
60+
proxy = True
61+
62+
63+
admin.site.unregister(SocialApp)
64+
admin.site.register(SocialApp, RequireProviderIdSocialAppAdmin)
65+
1666
admin.site.register(SocialAppCustomData)

kobo/apps/django_allauth/migrations/0001_initial.py renamed to kobo/apps/accounts/migrations/0007_add_providers_from_environment_to_db.py

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
import os
22
import re
33
from django.db import migrations
4+
from django.db.models import Q
5+
46

5-
"""
6-
Add any OIDC providers from the environment variables to the database. The upgrade to
7-
[email protected] removed code from /settings/base.py that parsed OIDC settings from
8-
environment variables, so this migration is needed to avoid manually setting up OIDC
9-
providers from the admin. Adapted from:
10-
https://gitlab.com/glitchtip/glitchtip-backend/-/blob/master/users/migrations/0010_allauth_oidc_from_env_var.py?ref_type=heads
11-
"""
127
def add_OIDC_settings_from_env(apps, schema_editor):
8+
"""
9+
Add any OIDC providers from the environment variables to the database. The upgrade to
10+
[email protected] removed code from /settings/base.py that parsed OIDC settings from
11+
environment variables, so this migration is needed to avoid manually setting up OIDC
12+
providers from the admin. Adapted from:
13+
https://gitlab.com/glitchtip/glitchtip-backend/-/blob/master/users/migrations/0010_allauth_oidc_from_env_var.py?ref_type=heads
14+
"""
1315
SocialApp = apps.get_model('socialaccount', 'SocialApp')
1416
SocialAppCustomData = apps.get_model('accounts', 'SocialAppCustomData')
1517

@@ -31,24 +33,36 @@ def add_OIDC_settings_from_env(apps, schema_editor):
3133

3234
for index, app in enumerate(oidc_apps):
3335
app_id = app.get('id')
36+
3437
# the app needs a name to be editable in the admin - give it a default name if necessary
3538
app_name = app.get('name', f'OIDC {index}')
3639
app_settings = {'server_url': app.get('server_url', None)}
40+
3741
# parse the tenant variable for microsoft OIDC providers - check for uppercase (old way) and lowercase (new way)
3842
if app_tenant := app.get('TENANT', app.get('tenant', None)):
3943
app_settings['tenant'] = app_tenant
40-
if app_id and app_settings['server_url'] and not (
41-
# if the app already exists, let's assume it's been configured
42-
SocialApp.objects.filter(provider_id=app_id).exists()
43-
):
44-
db_social_app, created = SocialApp.objects.get_or_create(provider_id=app_id)
45-
db_social_app.provider = 'openid_connect'
46-
db_social_app.name = app_name
47-
db_social_app.client_id = app.get('APP_client_id', '')
48-
db_social_app.secret = app.get('APP_secret', '')
49-
db_social_app.key = app.get('APP_key', '')
44+
45+
if app_id and app_settings['server_url']:
46+
db_social_app = SocialApp.objects.filter(
47+
Q(provider=app_id) |
48+
Q(provider__iexact='openid_connect', provider_id=app_id) |
49+
Q(provider='', name__iexact=app_name)
50+
).first()
51+
if not db_social_app:
52+
db_social_app = SocialApp.objects.create()
5053
db_social_app.settings = app_settings
54+
if not db_social_app.settings.get('previous_provider'):
55+
db_social_app.settings['previous_provider'] = app_id
56+
db_social_app.provider = 'openid_connect'
57+
db_social_app.provider_id = app_id
58+
# we don't want to overwrite the following settings if they're already defined
59+
# on the social app we grabbed
60+
db_social_app.name = db_social_app.name or app_name
61+
db_social_app.client_id = db_social_app.client_id or app.get('APP_client_id', '')
62+
db_social_app.secret = db_social_app.secret or app.get('APP_secret', '')
63+
db_social_app.key = db_social_app.key or app.get('APP_key', '')
5164
db_social_app.save()
65+
5266
# hide the OIDC provider from the login page, since it was already hidden
5367
SocialAppCustomData.objects.get_or_create(social_app=db_social_app)
5468

@@ -65,14 +79,24 @@ def add_OIDC_settings_from_env(apps, schema_editor):
6579
ms_app.save()
6680

6781

82+
def revert_OIDC_provider_id(apps, schema_editor):
83+
SocialApp = apps.get_model('socialaccount', 'SocialApp')
84+
85+
changed_apps = SocialApp.objects.filter(settings__has_key='previous_provider')
86+
for app in list(changed_apps):
87+
app.provider = app.settings['previous_provider']
88+
app.save()
89+
90+
6891
class Migration(migrations.Migration):
6992
dependencies = [
7093
('socialaccount', '0005_socialtoken_nullable_app'),
71-
('accounts', '0005_do_nothing'),
94+
('accounts', '0006_alter_emailcontent_unique_together'),
7295
]
7396

7497
operations = [
7598
migrations.RunPython(
76-
code=add_OIDC_settings_from_env, reverse_code=migrations.RunPython.noop
99+
code=add_OIDC_settings_from_env,
100+
reverse_code=revert_OIDC_provider_id,
77101
)
78102
]

kobo/apps/django_allauth/__init__.py

Whitespace-only changes.

kobo/apps/django_allauth/admin.py

Lines changed: 0 additions & 51 deletions
This file was deleted.

kobo/apps/django_allauth/migrations/__init__.py

Whitespace-only changes.

kobo/settings/base.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,6 @@
128128
'kobo.apps.trash_bin.TrashBinAppConfig',
129129
'kobo.apps.markdownx_uploader.MarkdownxUploaderAppConfig',
130130
'kobo.apps.form_disclaimer.FormDisclaimerAppConfig',
131-
'kobo.apps.django_allauth',
132131
)
133132

134133
MIDDLEWARE = [

0 commit comments

Comments
 (0)