migrate authenticator to database (#66876)
This commit is contained in:
parent
803d0cb3e5
commit
86033d32eb
|
@ -51,12 +51,6 @@ class Plugin:
|
|||
def get_authentication_backends(self):
|
||||
return ['authentic2_auth_fedict.backends.FedictBackend']
|
||||
|
||||
def get_auth_frontends(self):
|
||||
return ['authentic2_auth_fedict.authenticators.FedictAuthenticator']
|
||||
|
||||
def get_authenticators(self):
|
||||
return ['authentic2_auth_fedict.authenticators.FedictAuthenticator']
|
||||
|
||||
def redirect_logout_list(self, request, next_url=None):
|
||||
from mellon.views import logout
|
||||
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
# authentic2_auth_fedict - Fedict authentication for Authentic
|
||||
# Copyright (C) 2016 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
class AppSettings:
|
||||
'''Thanks django-allauth'''
|
||||
|
||||
__SENTINEL = object()
|
||||
|
||||
def __init__(self, prefix):
|
||||
self.prefix = prefix
|
||||
|
||||
def _setting(self, name, dflt=__SENTINEL):
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
|
||||
v = getattr(settings, self.prefix + name, dflt)
|
||||
if v is self.__SENTINEL:
|
||||
raise ImproperlyConfigured('Missing setting %r' % (self.prefix + name))
|
||||
return v
|
||||
|
||||
@property
|
||||
def enable(self):
|
||||
return self._setting('ENABLE', False)
|
||||
|
||||
|
||||
import sys
|
||||
|
||||
app_settings = AppSettings('A2_AUTH_FEDICT_')
|
||||
app_settings.__name__ = __name__
|
||||
sys.modules[__name__] = app_settings
|
|
@ -0,0 +1,26 @@
|
|||
# authentic2_auth_fedict - Fedict authentication for Authentic
|
||||
# Copyright (C) 2022 Entr'ouvert
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify it
|
||||
# under the terms of the GNU Affero General Public License as published
|
||||
# by the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from authentic2.apps.authenticators.forms import AuthenticatorFormMixin
|
||||
from django import forms
|
||||
|
||||
from .models import FedictAuthenticator
|
||||
|
||||
|
||||
class FedictAuthenticatorForm(AuthenticatorFormMixin, forms.ModelForm):
|
||||
class Meta:
|
||||
model = FedictAuthenticator
|
||||
exclude = ('name', 'slug', 'ou')
|
|
@ -0,0 +1,36 @@
|
|||
# Generated by Django 2.2.26 on 2022-07-06 15:11
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('authenticators', '0003_auto_20220413_1504'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='FedictAuthenticator',
|
||||
fields=[
|
||||
(
|
||||
'baseauthenticator_ptr',
|
||||
models.OneToOneField(
|
||||
auto_created=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
parent_link=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
to='authenticators.BaseAuthenticator',
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Belgian eID',
|
||||
},
|
||||
bases=('authenticators.baseauthenticator',),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,35 @@
|
|||
# Generated by Django 2.2.26 on 2022-07-06 15:12
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def create_fedict_authenticator(apps, schema_editor):
|
||||
if not getattr(settings, 'A2_AUTH_FEDICT_ENABLE', False):
|
||||
return
|
||||
|
||||
FedictAuthenticator = apps.get_model('authentic2_auth_fedict', 'FedictAuthenticator')
|
||||
|
||||
kwargs_settings = getattr(settings, 'AUTH_FRONTENDS_KWARGS', {})
|
||||
authenticator_settings = kwargs_settings.get('fedict', {})
|
||||
|
||||
priority = authenticator_settings.get('priority')
|
||||
priority = priority if priority is not None else -1
|
||||
|
||||
FedictAuthenticator.objects.create(
|
||||
slug='fedict-authenticator',
|
||||
order=priority,
|
||||
show_condition=authenticator_settings.get('show_condition') or '',
|
||||
enabled=True,
|
||||
)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('authentic2_auth_fedict', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(create_fedict_authenticator, reverse_code=migrations.RunPython.noop),
|
||||
]
|
|
@ -14,7 +14,7 @@
|
|||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from authentic2.authenticators import BaseAuthenticator
|
||||
from authentic2.apps.authenticators.models import BaseAuthenticator
|
||||
from django.shortcuts import render
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
@ -25,18 +25,19 @@ try:
|
|||
except ImportError:
|
||||
from authentic2.utils.misc import redirect_to_login
|
||||
|
||||
from . import app_settings
|
||||
|
||||
|
||||
class FedictAuthenticator(BaseAuthenticator):
|
||||
id = 'fedict'
|
||||
priority = -1
|
||||
type = 'fedict'
|
||||
unique = True
|
||||
|
||||
def enabled(self):
|
||||
return app_settings.enable and list(get_idps())
|
||||
class Meta:
|
||||
verbose_name = _('Belgian eID')
|
||||
|
||||
def name(self):
|
||||
return _('Belgian eID')
|
||||
@property
|
||||
def manager_form_class(self):
|
||||
from .forms import FedictAuthenticatorForm
|
||||
|
||||
return FedictAuthenticatorForm
|
||||
|
||||
def login(self, request, *args, **kwargs):
|
||||
context = kwargs.get('context', {}).copy()
|
||||
|
|
|
@ -16,12 +16,12 @@
|
|||
|
||||
from authentic2.models import Attribute, AttributeValue
|
||||
|
||||
from . import app_settings
|
||||
from .adapters import AuthenticAdapter
|
||||
from .models import FedictAuthenticator
|
||||
|
||||
|
||||
def on_user_logged_in(sender, request, user, **kwargs):
|
||||
if not app_settings.enable:
|
||||
if not FedictAuthenticator.objects.filter(enabled=True).exists():
|
||||
return
|
||||
if user.backend == 'authentic2_auth_fedict.backends.FedictBackend':
|
||||
return
|
||||
|
|
|
@ -18,7 +18,6 @@ if 'postgres' in DATABASES['default']['ENGINE']:
|
|||
|
||||
LANGUAGE_CODE = 'en'
|
||||
A2_AUTH_SAML_ENABLE = False
|
||||
A2_AUTH_FEDICT_ENABLE = True
|
||||
|
||||
MELLON_ADAPTER = ["authentic2_auth_fedict.adapters.AuthenticAdapter"]
|
||||
MELLON_LOGIN_URL = "fedict-login"
|
||||
|
|
|
@ -8,12 +8,15 @@ from django.contrib.auth import get_user_model
|
|||
from django.contrib.auth.models import AnonymousUser
|
||||
from django.contrib.messages.middleware import MessageMiddleware
|
||||
from django.contrib.sessions.middleware import SessionMiddleware
|
||||
from django.db import connection
|
||||
from django.db.migrations.executor import MigrationExecutor
|
||||
from django.test.client import RequestFactory
|
||||
from mellon.models import Issuer, UserSAMLIdentifier
|
||||
from utils import login
|
||||
|
||||
from authentic2_auth_fedict.adapters import AuthenticAdapter
|
||||
from authentic2_auth_fedict.backends import FedictBackend
|
||||
from authentic2_auth_fedict.models import FedictAuthenticator
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
@ -22,6 +25,11 @@ pytestmark = pytest.mark.django_db
|
|||
factory = RequestFactory()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def authenticator():
|
||||
return FedictAuthenticator.objects.create(slug='fedict', enabled=True)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def adapter():
|
||||
return AuthenticAdapter()
|
||||
|
@ -81,7 +89,7 @@ def test_custom_lookup_user(adapter, idp_conf, issuer, fedict_attributes):
|
|||
assert user.ou == get_default_ou()
|
||||
|
||||
|
||||
def test_login_fedict_authenticator_displayed(app, settings, issuer):
|
||||
def test_login_fedict_authenticator_displayed(app, settings, issuer, authenticator):
|
||||
response = app.get('/login/')
|
||||
assert 'Belgian eID' in response
|
||||
assert 'csam-login' in response
|
||||
|
@ -175,7 +183,7 @@ def test_authenticate_eid_link_to_existing_user(app, settings, issuer, user):
|
|||
assert backend_user == user
|
||||
|
||||
|
||||
def test_eid_unlink(app, settings, issuer, user):
|
||||
def test_eid_unlink(app, settings, issuer, user, authenticator):
|
||||
assert len(UserSAMLIdentifier.objects.all()) == 0
|
||||
|
||||
# create link
|
||||
|
@ -282,3 +290,49 @@ def test_provision_old_account_deleted(app, settings, issuer, user):
|
|||
# previous user as been deactivated
|
||||
assert User.objects.count() == count - 1
|
||||
assert not User.objects.filter(uuid=user_uuid)
|
||||
|
||||
|
||||
def test_fedict_authenticator_data_migration(settings):
|
||||
app = 'authentic2_auth_fedict'
|
||||
migrate_from = [(app, '0001_initial')]
|
||||
migrate_to = [(app, '0002_auto_20220706_1712')]
|
||||
|
||||
executor = MigrationExecutor(connection)
|
||||
old_apps = executor.loader.project_state(migrate_from).apps
|
||||
executor.migrate(migrate_from)
|
||||
FedictAuthenticator = old_apps.get_model(app, 'FedictAuthenticator')
|
||||
|
||||
settings.AUTH_FRONTENDS_KWARGS = {
|
||||
"fedict": {"priority": 3, "show_condition": "'backoffice' not in login_hint"}
|
||||
}
|
||||
settings.A2_AUTH_FEDICT_ENABLE = True
|
||||
|
||||
executor = MigrationExecutor(connection)
|
||||
executor.migrate(migrate_to)
|
||||
executor.loader.build_graph()
|
||||
new_apps = executor.loader.project_state(migrate_to).apps
|
||||
FedictAuthenticator = new_apps.get_model(app, 'FedictAuthenticator')
|
||||
|
||||
authenticator = FedictAuthenticator.objects.get()
|
||||
assert authenticator.slug == 'fedict-authenticator'
|
||||
assert authenticator.order == 3
|
||||
assert authenticator.show_condition == "'backoffice' not in login_hint"
|
||||
assert authenticator.enabled is True
|
||||
|
||||
|
||||
def test_manager(app, admin):
|
||||
resp = login(app, admin, path='/manage/authenticators/', index=0)
|
||||
|
||||
resp = resp.click('Add new authenticator')
|
||||
resp.form['authenticator'] = 'fedict'
|
||||
resp = resp.form.submit().follow()
|
||||
|
||||
authenticator = FedictAuthenticator.objects.get()
|
||||
assert not authenticator.enabled
|
||||
|
||||
# on edit page, submit
|
||||
resp = resp.form.submit().follow()
|
||||
resp = resp.click('Enable')
|
||||
|
||||
authenticator.refresh_from_db()
|
||||
assert authenticator.enabled
|
||||
|
|
|
@ -2,14 +2,14 @@ from authentic2.utils.misc import make_url
|
|||
from django.urls import reverse
|
||||
|
||||
|
||||
def login(app, user, path=None, password=None, remember_me=None):
|
||||
def login(app, user, path=None, password=None, remember_me=None, index=1):
|
||||
if path:
|
||||
real_path = make_url(path)
|
||||
login_page = app.get(real_path, status=302).maybe_follow()
|
||||
else:
|
||||
login_page = app.get(reverse('auth_login'))
|
||||
assert login_page.request.path == reverse('auth_login')
|
||||
form = login_page.forms[1]
|
||||
form = login_page.forms[index]
|
||||
form.set('username', user.username if hasattr(user, 'username') else user)
|
||||
# password is supposed to be the same as username
|
||||
form.set('password', password or user.username)
|
||||
|
|
Loading…
Reference in New Issue