Compare commits

...

29 Commits
v1.27 ... main

Author SHA1 Message Date
Frédéric Péters a3753c9bd2 misc: remove metadata cache as django-mellon has that now (#88579)
gitea/authentic2-auth-fedict/pipeline/head This commit looks good Details
2024-03-23 18:20:26 +01:00
Daniel Muyshond e257c5551d update beid/itsme images with improved better versions (#81401)
gitea/authentic2-auth-fedict/pipeline/head This commit looks good Details
2023-10-05 09:18:12 +02:00
Frédéric Péters 563762da8a ci: keep on using pylint 2 while pylint-django is not ready (#81905)
gitea/authentic2-auth-fedict/pipeline/head This commit looks good Details
2023-10-03 06:25:43 +02:00
Valentin Deniaud fe520f541f misc: update git-blame-ignore-revs to ignore quote changes (#79788)
gitea/authentic2-auth-fedict/pipeline/head This commit looks good Details
2023-08-16 10:12:20 +02:00
Valentin Deniaud cc93edc608 misc: apply double-quote-string-fixer (#79788) 2023-08-16 10:12:20 +02:00
Valentin Deniaud 8a0ccd8303 misc: add pre commit hook to force single quotes (#79788) 2023-08-16 10:12:20 +02:00
Frédéric Péters d78e9129ec ci: build deb package for bookworm (#78968)
gitea/authentic2-auth-fedict/pipeline/head This commit looks good Details
2023-06-23 17:02:50 +02:00
Frédéric Péters b750ba777f tox: mark build in error if pylint fails (#61362)
gitea/authentic2-auth-fedict/pipeline/head This commit looks good Details
2023-05-25 09:24:28 +02:00
Frédéric Péters fa85790460 tox: do not run pylint on .egg-info directory (#61362) 2023-05-25 09:23:24 +02:00
Frédéric Péters 6cd2fc25a6 misc: use with-statement to open files (#61362)
gitea/authentic2-auth-fedict/pipeline/head This commit looks good Details
2023-05-25 09:17:20 +02:00
Frédéric Péters 6d92e2a30b misc: use lazy logging (#61362) 2023-05-25 09:16:12 +02:00
Frédéric Péters 854b7b1d62 misc: remove old import location fallback (#61362) 2023-05-25 09:15:39 +02:00
Frédéric Péters 45f99dade4 misc: ignore modelform-uses-exclude (#61362) 2023-05-25 09:14:31 +02:00
Frédéric Péters d18f51af20 misc: give imported class its own name (#61362) 2023-05-25 09:12:14 +02:00
Frédéric Péters 6a95789905 misc: add timeouts to requests (#61362) 2023-05-25 09:11:07 +02:00
Frédéric Péters 3a43f1c597 misc: remove unused variable (#61362) 2023-05-25 09:10:13 +02:00
Frédéric Péters 97bbe15e70 misc: remove unused imports (#61362) 2023-05-25 09:09:39 +02:00
Frédéric Péters eef804ded9 tox: make pylint run (remove limits, add lasso and psycopg2) (#61362)
gitea/authentic2-auth-fedict/pipeline/head This commit looks good Details
2023-05-25 09:02:44 +02:00
Frédéric Péters 187c9b38b9 templates: wrap login form with a .cell--body div (#76861)
gitea/authentic2-auth-fedict/pipeline/head This commit looks good Details
2023-04-21 09:51:28 +02:00
Emmanuel Cazenave a32dc95770 do not rely on pkg_resources (#46476)
gitea/authentic2-auth-fedict/pipeline/head This commit looks good Details
2023-03-22 11:10:24 +01:00
Valentin Deniaud 1408cc051e ci: remove Django 2.2 target (#75506)
gitea/authentic2-auth-fedict/pipeline/head This commit looks good Details
2023-03-16 12:12:48 +01:00
Paul Marillonnet 653ea603a7 tox: add django3.2 (and matching dependencies) target (#74995)
gitea/authentic2-auth-fedict/pipeline/head This commit looks good Details
2023-03-01 17:37:07 +01:00
Frédéric Péters dd1944e309 ci: update .tar.gz URLs for gitea (part 2, like #74706)
gitea/authentic2-auth-fedict/pipeline/head Something is wrong with the build of this commit Details
2023-02-22 10:02:13 +01:00
Frédéric Péters 90277e6eff ci: update .tar.gz URLs for gitea (like #74706) 2023-02-22 09:59:34 +01:00
Agate 317a950898 Prepare Jenkinsfile for Gitea migration (#74572)
gitea/authentic2-auth-fedict/pipeline/head There was a failure building this commit Details
2023-02-20 14:54:44 +01:00
Frédéric Péters 4951c3b5fa misc: handle missing title or birthdate profile attributes (#74596) 2023-02-16 14:14:14 +01:00
Frédéric Péters cc60ef58ad ci: upgrade isort (#74044) 2023-02-01 09:28:54 +01:00
Frédéric Péters 5e875dbd7c ci: only build package for bullseye (#72729) 2022-12-22 17:21:25 +01:00
Benjamin Dauvergne 5e5ebf6961 tests: use https:// scheme in webtest client (#71945)
Adaptation to change in authentic settings (SESSION_COOKIE_SECURE=True).
2022-12-01 19:11:05 +01:00
21 changed files with 127 additions and 99 deletions

View File

@ -2,3 +2,5 @@
7a234d5fe7ae6bee3ba1d0f688967e8e6cf209e3
# trivial: apply isort & pyupgrade
1abbbadd9469a3f2ff7eafb0ec6956c2b1c6763c
# misc: apply double-quote-string-fixer (#79788)
cc93edc60807663edcaa50f439364fbb6a449252

View File

@ -1,13 +1,17 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: double-quote-string-fixer
- repo: https://github.com/psf/black
rev: 22.3.0
hooks:
- id: black
args: ['--target-version', 'py37', '--skip-string-normalization', '--line-length', '110']
- repo: https://github.com/PyCQA/isort
rev: 5.7.0
rev: 5.12.0
hooks:
- id: isort
args: ['--profile', 'black', '--line-length', '110']

14
Jenkinsfile vendored
View File

@ -26,10 +26,18 @@ pipeline {
stage('Packaging') {
steps {
script {
if (env.JOB_NAME == 'authentic2-auth-fedict' && env.GIT_BRANCH == 'origin/main') {
sh 'sudo -H -u eobuilder /usr/local/bin/eobuilder -d buster,bullseye authentic2-auth-fedict'
env.SHORT_JOB_NAME=sh(
returnStdout: true,
// given JOB_NAME=gitea/project/PR-46, returns project
// given JOB_NAME=project/main, returns project
script: '''
echo "${JOB_NAME}" | sed "s/gitea\\///" | awk -F/ '{print $1}'
'''
).trim()
if (env.GIT_BRANCH == 'main' || env.GIT_BRANCH == 'origin/main') {
sh "sudo -H -u eobuilder /usr/local/bin/eobuilder -d bullseye,bookworm ${SHORT_JOB_NAME}"
} else if (env.GIT_BRANCH.startsWith('hotfix/')) {
sh "sudo -H -u eobuilder /usr/local/bin/eobuilder -d buster,bullseye --branch ${env.GIT_BRANCH} --hotfix authentic2-auth-fedict"
sh "sudo -H -u eobuilder /usr/local/bin/eobuilder -d bullseye,bookworm --branch ${env.GIT_BRANCH} --hotfix ${SHORT_JOB_NAME}"
}
}
}

5
README
View File

@ -20,6 +20,11 @@ the django-mellon adapter:
And appropriate django-mellon parameters (MELLON_PUBLIC_KEYS,
MELLON_PRIVATE_KEY, MELLON_IDENTITY_PROVIDERS).
You also have to register the plugin:
INSTALLED_APPS += ('authentic2_auth_fedict')
AUTHENTICATION_BACKENDS += ('authentic2_auth_fedict.backends.FedictBackend',)
Code Style
----------

View File

@ -12,6 +12,7 @@ DATABASES = {
'ENGINE': 'django.db.backends.dummy',
}
}
INSTALLED_APPS += ('authentic2_auth_fedict',)
EOF
TEMPFILE=`mktemp`
trap "rm -f ${TEMPFILE} ${CHECK_MIGRATIONS_SETTINGS}" EXIT

View File

@ -10,4 +10,4 @@ else
echo No pylint RC found
exit 0
fi
pylint -f parseable --rcfile ${PYLINT_RC} "$@" > pylint.out || /bin/true
pylint -f parseable --rcfile ${PYLINT_RC} "$@" | tee pylint.out; test $PIPESTATUS -eq 0

View File

@ -89,7 +89,7 @@ setup(
description='Authentic2 Fedict plugin',
author="Entr'ouvert",
url='https://repos.entrouvert.org/authentic2-auth-fedict.git',
author_email="info@entrouvert.com",
author_email='info@entrouvert.com',
packages=find_packages('src'),
package_dir={
'': 'src',
@ -98,11 +98,6 @@ setup(
install_requires=[
'authentic2',
],
entry_points={
'authentic2.plugin': [
'authentic2-auth-fedict = authentic2_auth_fedict.apps:Plugin',
],
},
cmdclass={
'build': build,
'install_lib': install_lib,

View File

@ -15,21 +15,14 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import datetime
import hashlib
import logging
import os
import time
import lasso
import mellon.utils as mellon_utils
import requests
from authentic2.a2_rbac.utils import get_default_ou
from authentic2.models import Attribute
from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.files.storage import default_storage
from django.utils.encoding import force_bytes, force_text
from mellon.adapters import DefaultAdapter, app_settings
from mellon.adapters import DefaultAdapter
try:
import authentic2.utils.misc as a2_utils_misc
@ -55,34 +48,6 @@ class AuthenticAdapter(DefaultAdapter):
def auth_login(self, request, user):
a2_utils_misc.login(request, user, 'fedict')
def get_identity_providers_setting(self):
providers = app_settings.IDENTITY_PROVIDERS
cache_path = default_storage.path('fedict-cache')
if not os.path.exists(cache_path):
os.makedirs(cache_path)
for idp in providers:
if 'METADATA_URL' in idp and 'METADATA' not in idp:
url_hash = hashlib.sha1(force_bytes(idp['METADATA_URL'])).hexdigest()
metadata_cache_filename = os.path.join(cache_path, url_hash)
if os.path.exists(metadata_cache_filename):
stat_info = os.stat(metadata_cache_filename)
if stat_info.st_size and stat_info.st_mtime > (time.time() - 86400):
idp['METADATA'] = force_text(open(metadata_cache_filename).read())
continue
verify_ssl_certificate = mellon_utils.get_setting(idp, 'VERIFY_SSL_CERTIFICATE')
try:
response = requests.get(idp['METADATA_URL'], verify=verify_ssl_certificate)
response.raise_for_status()
except requests.exceptions.RequestException as e:
if os.path.exists(metadata_cache_filename):
# accept older cache in case of error
idp['METADATA'] = force_text(open(metadata_cache_filename).read())
continue
idp['METADATA'] = response.text
with open(metadata_cache_filename, 'wb') as fd:
fd.write(response.content)
return providers
def lookup_user(self, idp, saml_attributes):
if 'email' in saml_attributes:
# XXX: remove email from received attributes for now, this
@ -185,15 +150,21 @@ class AuthenticAdapter(DefaultAdapter):
else:
birthdate = ''
try:
Attribute.objects.get(name='birthdate').set_value(user, birthdate, verified=True)
except AttributeError: # native authentic date field
birthdate = datetime.datetime.strptime(birthdate, '%d/%m/%Y').date()
Attribute.objects.get(name='birthdate').set_value(user, birthdate, verified=True)
try:
Attribute.objects.get(name='birthdate').set_value(user, birthdate, verified=True)
except AttributeError: # native authentic date field
birthdate = datetime.datetime.strptime(birthdate, '%d/%m/%Y').date()
Attribute.objects.get(name='birthdate').set_value(user, birthdate, verified=True)
except Attribute.DoesNotExist:
pass
if int(nrn[6:9]) % 2:
title = 'Monsieur'
else:
title = 'Madame'
Attribute.objects.get(name='title').set_value(user, title, verified=True)
try:
Attribute.objects.get(name='title').set_value(user, title, verified=True)
except Attribute.DoesNotExist:
pass
if saml_attributes.get('givenName'):
Attribute.objects.get(name='first_name').set_value(

View File

@ -16,12 +16,12 @@
import json
from authentic2_auth_saml.apps import AppConfig
from authentic2_auth_saml.apps import AppConfig as SamlAppConfig
from django.contrib.auth.signals import user_logged_in
from django.utils.translation import ugettext_lazy as _
class AppConfig(AppConfig):
class AppConfig(SamlAppConfig):
name = 'authentic2_auth_fedict'
def ready(self):
@ -37,6 +37,9 @@ class AppConfig(AppConfig):
user = kwargs.get('user')
user.backend = 'authentic2_auth_fedict.backends.FedictBackend'
def get_a2_plugin(self):
return Plugin()
class Plugin:
def get_before_urls(self):
@ -44,12 +47,6 @@ class Plugin:
return urls.urlpatterns
def get_apps(self):
return ['mellon', 'authentic2_auth_fedict']
def get_authentication_backends(self):
return ['authentic2_auth_fedict.backends.FedictBackend']
def registration_form_prefill(self, request):
if request.token.get('first_name'):
return [

View File

@ -88,6 +88,6 @@ class FedictBackend(SAMLBackend):
value = getattr(old_user.verified_attributes, attribute.name, None)
if value:
setattr(user.verified_attributes, attribute.name, value)
logger.debug('deleting user %s, new fedict link manually created' % old_user)
logger.debug('deleting user %s, new fedict link manually created', old_user)
old_user.delete()
return user

View File

@ -118,7 +118,9 @@ class CountryWidget(forms.Select):
passerelle_url = list(settings.KNOWN_SERVICES['passerelle'].values())[0]['url']
country_url = urljoin(passerelle_url, '/csvdatasource/pays/data')
try:
self.choices = [(x['id'], x['text']) for x in requests.get(country_url).json()['data']]
self.choices = [
(x['id'], x['text']) for x in requests.get(country_url, timeout=30).json()['data']
]
except ValueError:
self.choices = []
super(forms.Select, self).__init__(attrs=attrs, choices=self.choices)
@ -135,7 +137,7 @@ class NumHouseField(forms.CharField):
if not value:
return
try:
if not re.match("^[1-9][0-9]*$", value):
if not re.match('^[1-9][0-9]*$', value):
raise ValueError()
except ValueError:
raise forms.ValidationError(getattr(settings, 'A2_NUMHOUSE_ERROR_MESSAGE', _('Invalid format')))
@ -147,7 +149,7 @@ class NumPhoneField(forms.CharField):
if not value:
return
try:
if not re.match("^(0|\\+|00)(\\d{8,})", value):
if not re.match('^(0|\\+|00)(\\d{8,})', value):
raise ValueError()
except ValueError:
raise forms.ValidationError(getattr(settings, 'A2_NUMPHONE_ERROR_MESSAGE', _('Invalid format')))

View File

@ -22,4 +22,4 @@ from .models import FedictAuthenticator
class FedictAuthenticatorForm(forms.ModelForm):
class Meta:
model = FedictAuthenticator
exclude = ('name', 'slug', 'ou')
exclude = ('name', 'slug', 'ou') # pylint: disable=modelform-uses-exclude

View File

@ -15,16 +15,12 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from authentic2.apps.authenticators.models import BaseAuthenticator
from authentic2.utils.misc import redirect_to_login
from django.shortcuts import render
from django.template.loader import render_to_string
from django.utils.translation import ugettext_lazy as _
from mellon.utils import get_idp, get_idps
try:
from authentic2.utils import redirect_to_login
except ImportError:
from authentic2.utils.misc import redirect_to_login
class FedictAuthenticator(BaseAuthenticator):
type = 'fedict'

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 27 KiB

View File

@ -1,4 +1,4 @@
{% load i18n static %}<form method="post" id="csam-login">
{% load i18n static %}<div class="cell--body"><form method="post" id="csam-login">
<div>
{% if has_itsme %}
<img src="{% static "authentic2_auth_fedict/img/beid-itsme.png" %}" alt="">
@ -26,3 +26,4 @@ $(function() {
});
});
</script>
</div>

View File

@ -19,14 +19,11 @@ import urllib.parse
from django.conf import settings
from django.contrib import messages
from django.core import signing
from django.db import transaction
from django.http import HttpResponseRedirect
from django.shortcuts import redirect, resolve_url
from django.urls import reverse
from django.utils.translation import ugettext_lazy as _
from django.views.decorators.csrf import csrf_exempt
from django.views.generic import View
try:
import authentic2.utils.misc as a2_utils_misc

View File

@ -18,7 +18,7 @@ def app(request, db, settings, tmpdir):
wtm._patch_settings()
request.addfinalizer(wtm._unpatch_settings)
settings.MEDIA_DIR = str(tmpdir.mkdir('media'))
return django_webtest.DjangoTestApp(extra_environ={'HTTP_HOST': 'localhost'})
return django_webtest.DjangoTestApp(extra_environ={'HTTP_HOST': 'localhost', 'wsgi.url_scheme': 'https'})
class AllHook:

View File

@ -19,22 +19,25 @@ if 'postgres' in DATABASES['default']['ENGINE']:
LANGUAGE_CODE = 'en'
A2_AUTH_SAML_ENABLE = False
MELLON_ADAPTER = ["authentic2_auth_fedict.adapters.AuthenticAdapter"]
MELLON_LOGIN_URL = "fedict-login"
MELLON_PUBLIC_KEYS = ["./tests/saml.crt"]
MELLON_PRIVATE_KEY = "./tests/saml.key"
MELLON_ADAPTER = ['authentic2_auth_fedict.adapters.AuthenticAdapter']
MELLON_LOGIN_URL = 'fedict-login'
MELLON_PUBLIC_KEYS = ['./tests/saml.crt']
MELLON_PRIVATE_KEY = './tests/saml.key'
MELLON_IDENTITY_PROVIDERS = [
{
"METADATA": open("./tests/metadata.xml").read(),
"ENTITY_ID": "https://idp.com/",
"SLUG": "idp",
'METADATA': open('./tests/metadata.xml').read(),
'ENTITY_ID': 'https://idp.com/',
'SLUG': 'idp',
},
]
MELLON_ATTRIBUTE_MAPPING = {
"last_name": "{attributes[surname][0]}",
"first_name": "{attri,butes[givenName][0]}",
'last_name': '{attributes[surname][0]}',
'first_name': '{attri,butes[givenName][0]}',
}
INSTALLED_APPS += ('authentic2_auth_fedict',)
AUTHENTICATION_BACKENDS += ('authentic2_auth_fedict.backends.FedictBackend',)
# test hook handlers
A2_HOOKS_PROPAGATE_EXCEPTIONS = True

View File

@ -194,11 +194,11 @@ def test_eid_unlink(app, settings, issuer, user, authenticator):
)
response = login(app, user, path='/accounts/', password=user.username)
assert "Unlink my account" in response.text
assert 'Unlink my account' in response.text
app.get('/accounts/fedict/unlink/').follow()
response = app.get('/accounts/')
assert "Link my account to my eID card" in response.text
assert 'Link my account to my eID card' in response.text
def test_provision_new_attributes_verified(app, settings, issuer, user):
@ -250,6 +250,50 @@ def test_provision_new_attributes_verified(app, settings, issuer, user):
assert backend_user.last_name == 'Bar'
def test_missing_title_attribute(app, settings, issuer, user):
Attribute.objects.filter(kind='title').delete()
# email & title verified
user.email = 'john.doe@verified.publik.love'
user.email_verified = True
user.first_name = 'Johnny'
user.last_name = 'Smith'
user.save()
UserSAMLIdentifier.objects.create(
user=user,
name_id='c54db0a8ddc24a02a2d057f857d3b102',
issuer=Issuer.objects.first(),
)
backend = FedictBackend()
request = factory.get(path='/accounts/')
request_user = User.objects.create(
first_name='Foo',
last_name='Bar',
email='foo.bar@nowhere.null',
)
request.user = request_user
saml_attributes = {
'givenName': ['Doe'],
'surname': ['John'],
'last_name': ['Doe'],
'first_name': ['John'],
'username': ['john.doe'],
'urn:be:fedict:iam:attr:fedid': ['c54db0a8ddc24a02a2d057f857d3b102'],
'egovNRN': ['85073003328'],
'is_superuser': ['false'],
'issuer': 'https://idp.com/',
'name_id_content': 'c54db0a8ddc24a02a2d057f857d3b102',
'name_id_format': 'urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified',
'name_id_name_qualifier': 'https://idp.com/idp/saml2/metadata',
'name_id_content_orig': 'c54db0a8ddc24a02a2d057f857d3b102',
}
credentials = {'saml_attributes': saml_attributes}
SessionMiddleware().process_request(request)
MessageMiddleware().process_request(request)
backend_user = backend.authenticate(request, **credentials)
assert backend_user == request_user
def test_provision_old_account_deleted(app, settings, issuer, user):
backend = FedictBackend()
request = factory.get(path='/accounts/')
@ -303,7 +347,7 @@ def test_fedict_authenticator_data_migration(settings):
FedictAuthenticator = old_apps.get_model(app, 'FedictAuthenticator')
settings.AUTH_FRONTENDS_KWARGS = {
"fedict": {"priority": 3, "show_condition": "'backoffice' not in login_hint"}
'fedict': {'priority': 3, 'show_condition': "'backoffice' not in login_hint"}
}
settings.A2_AUTH_FEDICT_ENABLE = True

26
tox.ini
View File

@ -6,13 +6,13 @@
[tox]
toxworkdir = {env:TMPDIR:/tmp}/tox-{env:USER}/authentic2-auth-fedict/{env:BRANCH_NAME:}
envlist =
py3-dj22
py3-dj32
[tox:jenkins]
envlist =
pylint
code-style
py3-dj22
py3-dj32
[testenv]
@ -31,14 +31,14 @@ passenv=
PGPASSWORD
usedevelop = true
deps =
!local: https://git.entrouvert.org/authentic.git/snapshot/authentic-main.tar.gz
!local: https://git.entrouvert.org/entrouvert/authentic/archive/main.tar.gz
local: ../authentic2
# dependency constraints for authentic
py3: file-magic
djangorestframework>=3.12,<3.13
dj22: django<2.3
django-tables<2.0
psycopg2-binary<2.9
dj32: django>=3.2.12,<3.3
dj32: django-tables2==2.4.1
psycopg2-binary
oldldap: python-ldap<3
ldaptools
@ -50,7 +50,6 @@ deps =
pytest-random
django-webtest
pyquery
astroid!=2.5.7
commands =
py3: ./getlasso3.sh
@ -61,12 +60,15 @@ commands =
usedevelop = true
basepython = python3
deps =
https://git.entrouvert.org/authentic.git/snapshot/authentic-main.tar.gz
Django<2.3
pylint<1.8
pylint-django<0.8.1
https://git.entrouvert.org/entrouvert/authentic/archive/main.tar.gz
Django<3.3
psycopg2-binary
pylint<3
astroid<3
pylint-django
commands =
/bin/bash -c "./pylint.sh src/*/"
./getlasso3.sh
./pylint.sh src/authentic2_auth_fedict/
[testenv:code-style]
skip_install = true