Replace the certificate field by an authentication_level field (fixes #7653)

The field cannot be empty. On each sso it's matched agains the
authentication levels found in the session.
This commit is contained in:
Benjamin Dauvergne 2015-06-22 17:05:58 +02:00
parent 40b1b6d5f2
commit 3cac2e4402
5 changed files with 85 additions and 28 deletions

View File

@ -1,4 +1,10 @@
from django.utils.translation import ugettext_lazy as _
AUTHENTICATION_LEVELS = (
('password-on-https', _('login/password')),
('ssl-collectivity', _('collectivity certificate + login/password')),
('ssl', _('X509 RGS certificate')),
)
PRATIC_AUTOLOGIN_COOKIE_NAME = 'pratic-autologin'
PRATIC_AUTOLOGIN_COOKIE_MAX_AGE = 60*10 # 10 minutes
PRATIC_AUTHN_CONTEXT_SSL_COLLECTIVITY = 'urn:cdg59.fr:names:tc:SAML:2.0:ac:classes:X509Collectivity'

View File

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
import authentic2.saml.fields
class Migration(migrations.Migration):
dependencies = [
('authentic2_pratic', '0006_auto_20150622_1600'),
]
operations = [
migrations.RemoveField(
model_name='serviceinstance',
name='certificate',
),
migrations.AddField(
model_name='serviceinstance',
name='authentication_level',
field=authentic2.saml.fields.MultiSelectField(default='password-on-https,ssl-collectivity,ssl', max_length=256, verbose_name='authentification level', choices=[(b'password-on-https', 'login/password'), (b'ssl-collectivity', 'collectivity certificate + login/password'), (b'ssl', 'X509 RGS certificate')]),
preserve_default=True,
),
]

View File

@ -15,6 +15,9 @@ from authentic2.idp.signals import authorize_service
from authentic2.custom_user.models import User as BaseUser
from authentic2.a2_rbac.models import OrganizationalUnit
from authentic2.constants import AUTHENTICATION_EVENTS_SESSION_KEY
from authentic2.saml.fields import MultiSelectField
from . import constants
class User(BaseUser):
@ -341,10 +344,12 @@ class ServiceInstance(Model):
cas_service_url = URLField(
verbose_name=_('CAS service URL'),
blank=True)
certificate = BooleanField(
verbose_name=_('Authentication by certificate only'),
default=False,
blank=True)
authentication_level = MultiSelectField(
verbose_name=_('authentification level'),
max_length=256,
blank=False,
default='password-on-https,ssl-collectivity,ssl',
choices=constants.AUTHENTICATION_LEVELS)
a2_service = ForeignKey(
to='authentic2.Service',
verbose_name=_('related authentic2 service'),
@ -445,10 +450,9 @@ def authorize_service_cb(request, user, audience, attributes, **kwargs):
logger.warn('unable to find service for audience %r and user %r in collectivity %r',
audience, unicode(user), unicode(collectivity))
return authz(False, _('This service is unknown.'))
if si.certificate:
events = request.session.get(AUTHENTICATION_EVENTS_SESSION_KEY, [])
if not any(event['how'] in ('ssl', 'ssl-collectivity') for event in events):
return authz(False, _('This service requires certificate authentication.'))
events = request.session.get(AUTHENTICATION_EVENTS_SESSION_KEY, [])
if not any(event['how'] in si.authentication_level):
return authz(False, _('This service requires certificate authentication.'))
if Access.objects.filter(service_instance=si, user=user).exists():
logger.info('%r of collectivity %r is authorized to connect on %r', unicode(user),

View File

@ -13,11 +13,17 @@
{% block content %}
<h2 id="services-header">Services</h2>
<ul>
{% for name, url, slug in service_links %}
{% for name, url, slug, needed_authent in service_links %}
<li>
<a id="service-link-{{ slug }}" href="{{ url }}">
{{ name }}
</a>
{% if needed_authent %}
<a id="service-link-{{ slug }}">
{{ name }}
</a> (niveau d'authentification insuffisant: {{ needed_authent }})
{% else %}
<a id="service-link-{{ slug }}" href="{{ url }}">
{{ name }}
</a>
{% endif %}
</li>
{% endfor %}
</ul>

View File

@ -11,12 +11,12 @@ from django.views.generic import (TemplateView, UpdateView,
CreateView, DeleteView)
from django_tables2 import SingleTableView
from authentic2.constants import AUTHENTICATION_EVENTS_SESSION_KEY
from authentic2.manager.views import AjaxFormViewMixin, \
ActionMixin, OtherActionsMixin, TitleMixin, Action
from authentic2.manager.user_views import UserEditView, UserAddView
from . import models, tables, forms
from . import models, tables, forms, constants
class SearchMixin(object):
search_filter = []
@ -365,20 +365,36 @@ service_instance_delete = ServiceInstanceDeleteView.as_view()
# accesses
collectivity_accesses = AccessesView.as_view()
def get_service_links(request):
events = request.session.get(AUTHENTICATION_EVENTS_SESSION_KEY, [])
authentication_levels = set(event['how'] for event in events)
if not hasattr(request.user, 'collectivity'):
return []
service_links = []
qs = request.user.collectivity.service_instances.select_related()
qs = qs.filter(access__user=request.user)
for service_instance in qs:
needed_authent = ''
levels = dict(constants.AUTHENTICATION_LEVELS)
if not bool(set(service_instance.authentication_level) & authentication_levels):
needed_authent0 = service_instance.authentication_level
needed_authent1 = [levels[level] for level in needed_authent0]
needed_authent2 = map(unicode, needed_authent1)
needed_authent = u', '.join(needed_authent2)
if service_instance.service.is_global:
name = service_instance.service.name
slug = service_instance.service.slug
url = service_instance.service.service_url
else:
name = service_instance.service.name
slug = service_instance.slug
url = service_instance.service_url
service_links.append((name, url, slug, needed_authent))
return service_links
@login_required
def agent_homepage(request):
ctx = {}
if hasattr(request.user, 'collectivity'):
service_links = ctx['service_links'] = []
qs = request.user.collectivity.service_instances.select_related()
for service_instance in qs:
if service_instance.service.is_global:
name = service_instance.service.name
slug = service_instance.service.slug
url = service_instance.service.service_url
else:
name = service_instance.service.name
slug = service_instance.slug
url = service_instance.service_url
service_links.append((name, url, slug))
ctx = {'service_links': get_service_links(request)}
return render(request, 'authentic2_pratic/agent_homepage.html', ctx)