Skip to content

Commit

Permalink
Merge pull request #4715 from kobotoolbox/django-allauth-0-57-0
Browse files Browse the repository at this point in the history
Upgrade django-allauth to 0.57.0
  • Loading branch information
bufke committed Nov 17, 2023
2 parents 89174c2 + b964fa5 commit 5ec0ce5
Show file tree
Hide file tree
Showing 13 changed files with 68 additions and 102 deletions.
2 changes: 1 addition & 1 deletion dependencies/pip/dev_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ django==3.2.15
# jsonfield
# kobo-service-account
# model-bakery
django-allauth==0.54.0
django-allauth==0.57.0
# via -r dependencies/pip/requirements.in
django-amazon-ses==4.0.1
# via -r dependencies/pip/requirements.in
Expand Down
2 changes: 1 addition & 1 deletion dependencies/pip/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ django==3.2.15
# djangorestframework
# jsonfield
# kobo-service-account
django-allauth==0.54.0
django-allauth==0.57.0
# via -r dependencies/pip/requirements.in
django-amazon-ses==4.0.1
# via -r dependencies/pip/requirements.in
Expand Down
3 changes: 1 addition & 2 deletions jsapp/js/account/security/sso/ssoSection.component.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React from 'react';
import {observer} from 'mobx-react-lite';
import sessionStore from 'js/stores/session';
import {PATHS} from 'js/router/routerConstants';
import styles from './ssoSection.module.scss';
import {deleteSocialAccount} from './sso.api';
import Button from 'jsapp/js/components/common/button';
Expand Down Expand Up @@ -53,7 +52,7 @@ const SsoSection = observer(() => {
<a
href={
'accounts/' +
socialApp.provider +
(socialApp.provider_id || socialApp.provider) +
'/login/?process=connect&next=%2F%23%2Faccount%2Fsecurity'
}
className={styles.passwordLink}
Expand Down
1 change: 1 addition & 0 deletions jsapp/js/dataInterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,7 @@ export type PermissionsConfigResponse = PaginatedResponse<PermissionDefinition>;

interface SocialAccount {
provider: string;
provider_id: string;
uid: string;
last_login: string;
date_joined: string;
Expand Down
1 change: 1 addition & 0 deletions jsapp/js/envStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ export interface EnvStoreFieldItem {
export interface SocialApp {
name: string;
provider: string;
provider_id: string;
client_id: string;
}

Expand Down
26 changes: 0 additions & 26 deletions kobo/apps/accounts/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,3 @@
class AccountExtrasConfig(AppConfig):
name = "kobo.apps.accounts"
verbose_name = "Account Extras"


@register()
def check_socialaccount_providers(app_configs, **kwargs):
"""
Don't allow `kobo` to be set as the `id` value in `SOCIALACCOUNT_PROVIDERS`
settings because it breaks the login page redirect when language is changed.
"""
errors = []
social_app_ids = [
apps['id']
for apps in settings.SOCIALACCOUNT_PROVIDERS['openid_connect'][
'SERVERS'
]
]
if 'kobo' in social_app_ids:
errors.append(
Error(
f'Please do not use `kobo` as the `id` value in '
'`SOCIALACCOUNT_PROVIDERS` settings.',
hint='`kobo` is not a valid value for this setting.',
obj=settings,
id='kobo.apps.accounts.E001',
)
)
return errors
4 changes: 2 additions & 2 deletions kobo/apps/accounts/templates/account/login.html
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ <h2>{% trans "Log in using SSO" %}</h2>
{% for social_app in social_apps %}
<li>
<a title="{{ social_app.name }}"
class="socialaccount_provider {{ social_app.provider }} kobo-button kobo-button--sso kobo-button--fullwidth"
href="{% provider_login_url social_app.provider process='login' scope=scope auth_params=auth_params %}"
class="socialaccount_provider {{ social_app.provider_id }} kobo-button kobo-button--sso kobo-button--fullwidth"
href="{% provider_login_url social_app.provider_id process='login' scope=scope auth_params=auth_params %}"
>
{{ social_app.name }}
</a>
Expand Down
4 changes: 2 additions & 2 deletions kobo/apps/accounts/templates/account/signup.html
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ <h2>{% trans "Register using SSO" %}</h2>
{% for social_app in social_apps %}
<li>
<a title="{{ social_app.name }}"
class="socialaccount_provider {{ social_app.provider }} kobo-button kobo-button--sso kobo-button--fullwidth"
href="{% provider_login_url social_app.provider process='login' scope=scope auth_params=auth_params %}"
class="socialaccount_provider {{ social_app.provider_id }} kobo-button kobo-button--sso kobo-button--fullwidth"
href="{% provider_login_url social_app.provider_id process='login' scope=scope auth_params=auth_params %}"
>{% trans "Register with SSO" %}</a>
{# "Register with {{social_app.name}}" when supporting social_apps|length>1 #}
</li>
Expand Down
6 changes: 2 additions & 4 deletions kobo/apps/accounts/templatetags/get_provider_appname.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def get_provider_appname(context, provider=None):
provider = provider or context['provider']
request = context['request']
try:
appname = provider.get_app(request).name
appname = provider.app.name
if appname:
return appname
return SocialApp.objects.get_current(provider, request).name
Expand All @@ -36,6 +36,4 @@ def get_provider_appname(context, provider=None):

@register.simple_tag()
def get_social_apps():
if settings.SOCIALACCOUNT_PROVIDERS:
return SocialApp.objects.filter(custom_data__isnull=True)
return []
return SocialApp.objects.filter(custom_data__isnull=True)
Empty file.
51 changes: 51 additions & 0 deletions kobo/apps/django_allauth/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
from allauth.socialaccount.admin import SocialAppForm, SocialAppAdmin
from allauth.socialaccount.models import SocialApp
from django.contrib import admin
from django.core.exceptions import ValidationError
from django.db.models import Q


class RequireProviderIdSocialAppForm(SocialAppForm):
def __init__(self, *args, **kwargs):
super(SocialAppForm, self).__init__(*args, **kwargs)
# require the provider_id in the admin, since we can't make it required on allauth's model
self.fields['provider_id'].required = True

def clean_provider_id(self):
reserved_keywords = ['kobo']
provider_id = self.cleaned_data.get('provider_id')
"""
Don't allow `kobo` to be set as the `provider_id` value in `SOCIALACCOUNT_PROVIDERS`
settings because it breaks the login page redirect when language is changed.
"""
if provider_id in reserved_keywords:
raise ValidationError(
f'`{provider_id}` is not a valid value for the `provider_id` setting.'
)

"""
By default, django-allauth only supports showing one provider on the login screen.
But OIDC providers allow multiple subproviders, so kpi has some additional code to display multiple providers.
Because of that, we need to make sure that the `provider` and `provider_id` fields are unique.
django-allauth (as of 0.57.0) technically enforces this on the model level, but in practice it's flawed.
"""
if SocialApp.objects.filter(
Q(provider_id=provider_id) |
Q(provider=provider_id)
).exists():
raise ValidationError(
"""The Provider ID value must be unique and cannot match an existing Provider name.
Please use a different value."""
)
return provider_id


class RequireProviderIdSocialAppAdmin(SocialAppAdmin):
form = RequireProviderIdSocialAppForm

class Meta:
proxy = True


admin.site.unregister(SocialApp)
admin.site.register(SocialApp, RequireProviderIdSocialAppAdmin)
57 changes: 2 additions & 55 deletions kobo/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,13 +128,15 @@
'kobo.apps.trash_bin.TrashBinAppConfig',
'kobo.apps.markdownx_uploader.MarkdownxUploaderAppConfig',
'kobo.apps.form_disclaimer.FormDisclaimerAppConfig',
'kobo.apps.django_allauth',
)

MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'hub.middleware.LocaleMiddleware',
'allauth.account.middleware.AccountMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
Expand Down Expand Up @@ -1031,61 +1033,6 @@ def dj_stripe_request_callback_method():
"UNSAFE_SSO_REGISTRATION_EMAIL_DISABLE", False
)

# See https://django-allauth.readthedocs.io/en/latest/configuration.html
# Map env vars to upstream dict values, include exact case. Underscores for delimiter.
# Example: SOCIALACCOUNT_PROVIDERS_provider_SETTING
# Use numbers for arrays such as _1_FOO, _1_BAR, _2_FOO, _2_BAR
SOCIALACCOUNT_PROVIDERS = {}
if MICROSOFT_TENANT := env.str('SOCIALACCOUNT_PROVIDERS_microsoft_TENANT', None):
SOCIALACCOUNT_PROVIDERS['microsoft'] = {'TENANT': MICROSOFT_TENANT}
# Parse oidc settings as nested dict in array. Example:
# SOCIALACCOUNT_PROVIDERS_openid_connect_SERVERS_0_id: "google" # Must be unique
# SOCIALACCOUNT_PROVIDERS_openid_connect_SERVERS_0_server_url: "https://accounts.google.com"
# SOCIALACCOUNT_PROVIDERS_openid_connect_SERVERS_0_name: "Kobo Google Apps"
# Only OIDC supports multiple providers. For example, to add two Google Apps sign ins - use
# OIDC and assign them a different server number. Do not use the allauth google provider.
oidc_prefix = "SOCIALACCOUNT_PROVIDERS_openid_connect_SERVERS_"
oidc_pattern = re.compile(r"{prefix}\w+".format(prefix=oidc_prefix))
oidc_servers = {}
oidc_nested_keys = ['APP', 'SCOPE', 'AUTH_PARAMS']

for key, value in {
key.replace(oidc_prefix, ""): val
for key, val in os.environ.items()
if oidc_pattern.match(key)
}.items():
number, setting = key.split("_", 1)
parsed_key = None
nested_key = filter(lambda setting_key : setting.startswith(setting_key), oidc_nested_keys)
nested_key = list(nested_key)
if len(nested_key):
_, parsed_key = setting.split(nested_key[0] + "_", 1)
setting = nested_key[0]
if number in oidc_servers:
if parsed_key:
if setting in oidc_servers[number]:
if parsed_key.isdigit():
oidc_servers[number][setting].append(value)
else:
oidc_servers[number][setting][parsed_key] = value
else:
if parsed_key.isdigit():
oidc_servers[number][setting] = [value]
else:
oidc_servers[number][setting] = {parsed_key: value}
else:
oidc_servers[number][setting] = value
else:
if parsed_key:
if parsed_key.isdigit():
oidc_servers[number] = {setting: [value]}
else:
oidc_servers[number] = {setting: {parsed_key: value}}
else:
oidc_servers[number] = {setting: value}
oidc_servers = [x for x in oidc_servers.values()]
SOCIALACCOUNT_PROVIDERS["openid_connect"] = {"SERVERS": oidc_servers}

WEBPACK_LOADER = {
'DEFAULT': {
'BUNDLE_DIR_NAME': 'jsapp/compiled/',
Expand Down
13 changes: 4 additions & 9 deletions kpi/views/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,16 +156,11 @@ def process_user_metadata_configs(request):
def process_other_configs(request):
data = {}

# django-allauth social apps are configured in both settings and the
# database. Optimize by avoiding extra DB call when unnecessary
social_apps = []
if settings.SOCIALACCOUNT_PROVIDERS:
social_apps = list(
SocialApp.objects.filter(custom_data__isnull=True).values(
'provider', 'name', 'client_id'
)
data['social_apps'] = list(
SocialApp.objects.filter(custom_data__isnull=True).values(
'provider', 'name', 'client_id', 'provider_id'
)
data['social_apps'] = social_apps
)

data['asr_mt_features_enabled'] = _check_asr_mt_access_for_user(
request.user
Expand Down

0 comments on commit 5ec0ce5

Please sign in to comment.