0001-remove-auth2_ssl-33992.patch
src/authentic2/auth2_auth/auth2_ssl/README | ||
---|---|---|
1 |
================================ |
|
2 |
Enable SSL Client authentication |
|
3 |
================================ |
|
4 | ||
5 |
Intro |
|
6 |
===== |
|
7 |
Tested with Apache 2 and mod_ssl. |
|
8 |
Django over mod_wsgi. From http://docs.djangoproject.com/en/dev/howto/deployment/modwsgi/ |
|
9 |
"Deploying Django with Apache and mod_wsgi is the recommended way to get Django into production." |
|
10 | ||
11 |
Generate Keys |
|
12 |
============= |
|
13 |
* Create a CA (passphrase) |
|
14 |
openssl genrsa -des3 -out ca.key 2048 |
|
15 |
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt |
|
16 |
openssl x509 -in ca.crt -text -noout |
|
17 |
* Server key material (challenge) |
|
18 |
openssl genrsa -des3 -out server.key 1024 |
|
19 |
openssl req -new -key server.key -out server.csr |
|
20 |
openssl x509 -req -in server.csr -out server.crt -sha1 -CA ca.crt -CAkey ca.key -CAcreateserial -days 3650 |
|
21 |
openssl x509 -in server.crt -text -noout |
|
22 |
* User Key material (challenge/password) |
|
23 |
openssl genrsa -des3 -out c.key 1024 |
|
24 |
openssl req -new -key c.key -out c.csr |
|
25 |
openssl x509 -req -in c.csr -out c.crt -sha1 -CA ca.crt -CAkey ca.key -CAcreateserial -days 3650 |
|
26 |
openssl pkcs12 -export -in c.crt -inkey c.key -name "Mikael Ates" -out c.p12 |
|
27 |
openssl pkcs12 -in c.p12 -clcerts -nokeys -info |
|
28 | ||
29 |
Configure Apache and WSGI |
|
30 |
========================= |
|
31 |
Add a file django.wsgi, e.g.: |
|
32 |
""" |
|
33 |
import os |
|
34 |
import sys |
|
35 | ||
36 |
sys.path.append("/usr/local/lib/python2.6/site-packages/") |
|
37 |
try: |
|
38 |
import lasso |
|
39 |
except: |
|
40 |
print("Unable to import Lasso.", file=sys.stderr) |
|
41 | ||
42 |
apache_configuration= os.path.dirname(__file__) |
|
43 |
project = os.path.dirname(apache_configuration) |
|
44 |
sys.path.append(project) |
|
45 |
try: |
|
46 |
import authentic2.settings |
|
47 |
os.environ['DJANGO_SETTINGS_MODULE'] = 'authentic2.settings' |
|
48 |
except: |
|
49 |
print("Unable to import settings.", file=sys.stderr) |
|
50 | ||
51 |
import django.core.handlers.wsgi |
|
52 |
application = django.core.handlers.wsgi.WSGIHandler() |
|
53 |
""" |
|
54 | ||
55 |
Activate apache2 modules: |
|
56 |
* a2enmod wsgi |
|
57 |
* a2enmod ssl |
|
58 | ||
59 |
Add a Apache vhost for SSL. |
|
60 |
""" |
|
61 |
<IfModule mod_ssl.c> |
|
62 |
<VirtualHost *:443> |
|
63 | ||
64 |
LimitInternalRecursion 1000 |
|
65 |
ServerAdmin webmaster@entrouvert.org |
|
66 |
ServerName localhost |
|
67 | ||
68 |
Alias /media/admin/ /usr/local/lib/python2.6/dist-packages/django/contrib/admin/media/ |
|
69 | ||
70 |
WSGIScriptAlias / /Donnees/devs/authentic/apache/django.wsgi |
|
71 | ||
72 |
<Directory /Donnees/devs/authentic/> |
|
73 |
SSLVerifyClient optional_no_ca |
|
74 |
Options Indexes MultiViews FollowSymLinks |
|
75 |
AllowOverride None |
|
76 |
Order deny,allow |
|
77 |
Allow from all |
|
78 |
</Directory> |
|
79 | ||
80 |
SSLEngine on |
|
81 |
SSLCipherSuite HIGH:MEDIUM |
|
82 |
SSLProtocol all -SSLv2 |
|
83 | ||
84 |
SSLCertificateFile /Donnees/devs/authentic/apache/key_mat/server.crt |
|
85 |
SSLCertificateKeyFile /Donnees/devs/authentic/apache/key_mat/server.key |
|
86 | ||
87 |
SSLCertificateChainFile /Donnees/devs/authentic/apache/key_mat/ca.crt |
|
88 |
SSLCACertificateFile /Donnees/devs/authentic/apache/key_mat/ca.crt |
|
89 | ||
90 |
SSLOptions +StdEnvVars +ExportCertData |
|
91 | ||
92 |
BrowserMatch "MSIE [2-6]" \ |
|
93 |
nokeepalive ssl-unclean-shutdown \ |
|
94 |
downgrade-1.0 force-response-1.0 |
|
95 |
BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown |
|
96 | ||
97 |
</VirtualHost> |
|
98 |
</IfModule> |
|
99 |
""" |
|
100 | ||
101 |
Give rights to Apache on your Authentic directory. |
|
102 |
Reload Apache. |
|
103 | ||
104 |
Configure Authentic |
|
105 |
=================== |
|
106 | ||
107 |
Key Description |
|
108 |
-------------------------- ---------------------------------------- |
|
109 |
ACCEPT_SELF_SIGNED accept certificate for which the validation failed, |
|
110 |
default: False |
|
111 |
STRICT_MATCH do a binary compare to match certificate and users, |
|
112 |
default: False |
|
113 |
SUBJECT_MATCH_KEYS SSL information to use to match recorded |
|
114 |
certificates. default: ('subject_dn', 'issuer_dn'), |
|
115 |
possible values: serial, subject_dn, issuer_dn, cert. |
|
116 |
CREATE_USERNAME_CALLBACK function receiving a SSLInfo object as first |
|
117 |
parameter and returning a username, default: None |
|
118 |
CREATE_USER function receiving a SSLInfo object as first |
|
119 |
parameter and returning a user, default: None |
|
120 |
USE_COOKIE to be described |
|
121 | ||
122 |
in settings.py: |
|
123 |
Set AUTH_SSL = True |
|
124 |
To create a user with the mail adress as identifier: |
|
125 |
SSLAUTH_CREATE_USER = True |
|
126 |
To use another identifier: |
|
127 |
def myusernamegen(ssl_info): |
|
128 |
import re |
|
129 |
if(ssl_info.subject_cn): |
|
130 |
return return re.sub('[^a-zA-Z0-9]', '_', ssl_info.subject_cn) |
|
131 |
else: |
|
132 |
return return re.sub('[^a-zA-Z0-9]', '_', ssl_info.serial) |
|
133 |
SSLAUTH_CREATE_USERNAME_CALLBACK = myusernamegen |
|
134 | ||
135 | ||
136 |
Nginx configuration |
|
137 |
=================== |
|
138 | ||
139 |
You must be able to retrieve SSL environment variable, for example with the |
|
140 |
SCGI backend you must add those lines to /etc/nginx/scgi_params:: |
|
141 | ||
142 |
scgi_param SSL_CLIENT_CERT $ssl_client_cert; |
|
143 |
scgi_param SSL_CLIENT_RAW_CERT $ssl_client_raw_cert; |
|
144 |
scgi_param SSL_CLIENT_S_DN $ssl_client_s_dn; |
|
145 |
scgi_param SSL_CLIENT_I_DN $ssl_client_i_dn; |
|
146 |
scgi_param SSL_CLIENT_SERIAL $ssl_client_serial; |
|
147 |
scgi_param SSL_CLIENT_M_SERIAL $ssl_client_serial; |
|
148 |
scgi_param SSL_CLIENT_VERIFY $ssl_client_verify; |
|
149 | ||
150 |
It would be the same with FCGI but using the fcgi_param directive in the |
|
151 |
fcgi_params file. It does not currently work when using proxy_pass. |
|
152 | ||
153 |
A virtualhost configuration example:: |
|
154 | ||
155 |
server { |
|
156 |
listen 80; |
|
157 |
server_name authentic.localhost; |
|
158 | ||
159 |
rewrite ^ https://$server_name$request_uri? permanent; |
|
160 |
} |
|
161 | ||
162 |
server { |
|
163 |
listen 443; |
|
164 |
server_name authentic.localhost; |
|
165 | ||
166 |
ssl on; |
|
167 |
ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem; |
|
168 |
ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key; |
|
169 |
ssl_verify_client optional_no_ca; |
|
170 | ||
171 |
location / { |
|
172 |
include scgi_params; |
|
173 |
scgi_pass localhost:8000; |
|
174 |
} |
|
175 |
} |
|
176 | ||
177 |
The serveur must be run using the SCGI protocol, with this command line for |
|
178 |
example:: |
|
179 | ||
180 |
./manage.py runfcgi protocol=scgi method=threaded daemonize=false host=localhost port=8000 |
src/authentic2/auth2_auth/auth2_ssl/__init__.py | ||
---|---|---|
16 | 16 | |
17 | 17 | |
18 | 18 |
class Plugin(object): |
19 |
def get_before_urls(self): |
|
20 |
from . import app_settings |
|
21 |
from django.conf.urls import include, url |
|
22 |
from authentic2.decorators import setting_enabled, required |
|
23 | ||
24 |
return required( |
|
25 |
setting_enabled('ENABLE', settings=app_settings), |
|
26 |
[url(r'^accounts/sslauth/', include(__name__ + '.urls'))]) |
|
27 | ||
28 | 19 |
def get_apps(self): |
29 | 20 |
return [__name__] |
30 | ||
31 |
def get_authentication_backends(self): |
|
32 |
return ['authentic2.auth2_auth.auth2_ssl.backends.SSLBackend'] |
|
33 | ||
34 |
def get_authenticators(self): |
|
35 |
return ['authentic2.auth2_auth.auth2_ssl.authenticators.SSLAuthenticator'] |
src/authentic2/auth2_auth/auth2_ssl/admin.py | ||
---|---|---|
1 |
# authentic2 - versatile identity manager |
|
2 |
# Copyright (C) 2010-2019 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
from django.contrib import admin |
|
18 | ||
19 |
from . import models |
|
20 | ||
21 | ||
22 |
class ClientCertificateAdmin(admin.ModelAdmin): |
|
23 |
list_display = ('user', 'subject_dn', 'issuer_dn', 'serial') |
|
24 | ||
25 |
admin.site.register(models.ClientCertificate, ClientCertificateAdmin) |
src/authentic2/auth2_auth/auth2_ssl/app_settings.py | ||
---|---|---|
1 |
# authentic2 - versatile identity manager |
|
2 |
# Copyright (C) 2010-2019 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
import sys |
|
18 | ||
19 | ||
20 |
class AppSettings(object): |
|
21 |
'''Thanks django-allauth''' |
|
22 |
__DEFAULTS = dict( |
|
23 |
# settings for TEST only, make it easy to simulate the SSL |
|
24 |
# environment |
|
25 |
ENABLE=False, |
|
26 |
FORCE_ENV={}, |
|
27 |
ACCEPT_SELF_SIGNED=False, |
|
28 |
STRICT_MATCH=False, |
|
29 |
SUBJECT_MATCH_KEYS=('subject_dn', 'issuer_dn'), |
|
30 |
CREATE_USERNAME_CALLBACK=None, |
|
31 |
USE_COOKIE=False, |
|
32 |
CREATE_USER=False, |
|
33 |
) |
|
34 | ||
35 |
def __init__(self, prefix): |
|
36 |
self.prefix = prefix |
|
37 | ||
38 |
def _setting(self, name, dflt): |
|
39 |
from django.conf import settings |
|
40 |
return getattr(settings, self.prefix + name, dflt) |
|
41 | ||
42 |
def __getattr__(self, name): |
|
43 |
if name not in self.__DEFAULTS: |
|
44 |
raise AttributeError(name) |
|
45 |
return self._setting(name, self.__DEFAULTS[name]) |
|
46 | ||
47 | ||
48 |
app_settings = AppSettings('SSLAUTH_') |
|
49 |
app_settings.__name__ = __name__ |
|
50 |
app_settings.__file__ = __file__ |
|
51 |
sys.modules[__name__] = app_settings |
src/authentic2/auth2_auth/auth2_ssl/authentic_ssl.vhost | ||
---|---|---|
1 |
<IfModule mod_ssl.c> |
|
2 |
<VirtualHost *:443> |
|
3 | ||
4 |
LimitInternalRecursion 1000 |
|
5 |
ServerAdmin webmaster@entrouvert.org |
|
6 |
ServerName localhost |
|
7 | ||
8 |
#Alias /media/ /Donnees/devs/Authentic/authentic/media/ |
|
9 |
Alias /media/admin/ /usr/local/lib/python2.6/dist-packages/django/contrib/admin/media/ |
|
10 | ||
11 |
WSGIScriptAlias / /Donnees/devs/Authentic/authentic/apache/django.wsgi |
|
12 | ||
13 |
<Location /> |
|
14 | ||
15 |
Options Indexes MultiViews FollowSymLinks |
|
16 |
AllowOverride None |
|
17 |
Order deny,allow |
|
18 |
Allow from all |
|
19 | ||
20 |
</Location> |
|
21 | ||
22 |
<Location /sslauth/> |
|
23 | ||
24 |
SSLVerifyClient require |
|
25 | ||
26 |
Options Indexes MultiViews FollowSymLinks |
|
27 |
AllowOverride None |
|
28 |
Order deny,allow |
|
29 |
Allow from all |
|
30 | ||
31 |
</Location> |
|
32 | ||
33 |
SSLEngine on |
|
34 |
SSLCipherSuite HIGH:MEDIUM |
|
35 |
SSLProtocol all -SSLv2 |
|
36 | ||
37 |
SSLCertificateFile /Donnees/devs/Authentic/authentic/apache/key_mat/server.crt |
|
38 |
SSLCertificateKeyFile /Donnees/devs/Authentic/authentic/apache/key_mat/server.key |
|
39 | ||
40 |
SSLCertificateChainFile /Donnees/devs/Authentic/authentic/apache/key_mat/ca.crt |
|
41 |
SSLCACertificateFile /Donnees/devs/Authentic/authentic/apache/key_mat/ca.crt |
|
42 | ||
43 |
SSLOptions +StdEnvVars +ExportCertData |
|
44 |
#SSLProtocol all |
|
45 | ||
46 |
BrowserMatch "MSIE [2-6]" \ |
|
47 |
nokeepalive ssl-unclean-shutdown \ |
|
48 |
downgrade-1.0 force-response-1.0 |
|
49 |
BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown |
|
50 | ||
51 |
</VirtualHost> |
|
52 |
</IfModule> |
src/authentic2/auth2_auth/auth2_ssl/authenticators.py | ||
---|---|---|
1 |
# authentic2 - versatile identity manager |
|
2 |
# Copyright (C) 2010-2019 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
from django.utils.translation import ugettext_lazy as _ |
|
18 |
import django.forms |
|
19 | ||
20 |
from . import views, app_settings |
|
21 |
from authentic2.utils import redirect_to_login |
|
22 | ||
23 | ||
24 |
class SSLAuthenticator(object): |
|
25 |
def enabled(self): |
|
26 |
return app_settings.ENABLE |
|
27 | ||
28 |
def id(self): |
|
29 |
return 'ssl' |
|
30 | ||
31 |
def name(self): |
|
32 |
return _('SSL with certificates') |
|
33 | ||
34 |
def form(self): |
|
35 |
return django.forms.Form |
|
36 | ||
37 |
def post(self, request, form, nonce, next_url): |
|
38 |
return redirect_to_login(request, login_url='user_signin_ssl',) |
|
39 | ||
40 |
def template(self): |
|
41 |
return 'auth/login_form_ssl.html' |
|
42 | ||
43 |
def profile(self, request, *args, **kwargs): |
|
44 |
return views.profile(request, *args, **kwargs) |
src/authentic2/auth2_auth/auth2_ssl/backends.py | ||
---|---|---|
1 |
# authentic2 - versatile identity manager |
|
2 |
# Copyright (C) 2010-2019 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
from django.contrib.auth import get_user_model |
|
18 |
from django.db.models import Q |
|
19 |
import logging |
|
20 | ||
21 |
from authentic2.backends import is_user_authenticable |
|
22 | ||
23 |
from . import models, app_settings |
|
24 | ||
25 |
logger = logging.getLogger(__name__) |
|
26 | ||
27 |
User = get_user_model() |
|
28 | ||
29 | ||
30 |
class AuthenticationError(Exception): |
|
31 |
pass |
|
32 | ||
33 | ||
34 |
class SSLBackend: |
|
35 |
""" |
|
36 |
authenticates a client certificate against the records stored |
|
37 |
in ClientCertificate model and looks up the corresponding django user |
|
38 | ||
39 |
In all methods, the ssl_info parameter is supposed to be an SSLInfo |
|
40 |
instance |
|
41 |
""" |
|
42 |
supports_object_permissions = False |
|
43 |
supports_anonymous_user = False |
|
44 | ||
45 |
def authenticate(self, ssl_info): |
|
46 |
cert = self.get_certificate(ssl_info) |
|
47 |
if cert is None: |
|
48 |
return None |
|
49 |
else: |
|
50 |
if not is_user_authenticable(cert.user): |
|
51 |
logger.info('SSLAuth: authentication refused by user filters') |
|
52 |
return None |
|
53 |
return cert.user |
|
54 | ||
55 |
def get_user(self, user_id): |
|
56 |
""" |
|
57 |
simply return the user object. That way, we only need top look-up the |
|
58 |
certificate once, when loggin in |
|
59 |
""" |
|
60 |
try: |
|
61 |
return User.objects.get(id=user_id) |
|
62 |
except User.DoesNotExist: |
|
63 |
return None |
|
64 | ||
65 |
def get_certificate(self, ssl_info): |
|
66 |
""" |
|
67 |
returns a ClientCertificate object for the passed |
|
68 |
cert data or None if not found |
|
69 |
""" |
|
70 | ||
71 |
if app_settings.STRICT_MATCH: |
|
72 |
# compare complete certificate in strict match |
|
73 |
if not ssl_info.cert: |
|
74 |
logger.error('SSLAuth: strict match required but PEM encoded certificate ' |
|
75 |
'not found in environment. Check your server settings') |
|
76 |
return None |
|
77 |
query = Q(cert=ssl_info.cert) |
|
78 |
else: |
|
79 |
query_args = {} |
|
80 |
for key in app_settings.SUBJECT_MATCH_KEYS: |
|
81 |
if not ssl_info.get(key): |
|
82 |
logger.error(u'SSLAuth: key %s is missing from ssl_info', key) |
|
83 |
return None |
|
84 |
query_args[key] = ssl_info.get(key) |
|
85 | ||
86 |
query = Q(**query_args) |
|
87 |
try: |
|
88 |
cert = models.ClientCertificate.objects.select_related().get(query) |
|
89 |
return cert |
|
90 |
except models.ClientCertificate.DoesNotExist: |
|
91 |
return None |
|
92 | ||
93 |
def create_user(self, ssl_info): |
|
94 |
""" |
|
95 |
This method creates a new django User and ClientCertificate record |
|
96 |
for the passed certificate info. It does not create an issuer record, |
|
97 |
just a subject for the ClientCertificate. |
|
98 |
""" |
|
99 |
# auto creation only created a DN for the subject, not the issuer |
|
100 | ||
101 |
# get username and check if the user exists already |
|
102 |
if app_settings.CREATE_USERNAME_CALLBACK: |
|
103 |
build_username = app_settings.CEATE_USERNAME_CALLBACK |
|
104 |
else: |
|
105 |
build_username = self.build_username |
|
106 | ||
107 |
username = build_username(ssl_info) |
|
108 | ||
109 |
try: |
|
110 |
user = User.objects.get(username=username) |
|
111 |
except User.DoesNotExist: |
|
112 |
if app_settings.CREATE_USER_CALLBACK: |
|
113 |
build_user = app_settings.CREATE_USER_CALLBACK |
|
114 |
else: |
|
115 |
build_user = self.build_user |
|
116 |
user = build_user(username, ssl_info) |
|
117 | ||
118 |
# create the certificate record and save |
|
119 |
self.link_user(ssl_info, user) |
|
120 |
return user |
|
121 | ||
122 |
def link_user(self, ssl_info, user): |
|
123 |
""" |
|
124 |
This method creates a new django User and ClientCertificate record |
|
125 |
for the passed certificate info. It does not create an issuer record, |
|
126 |
just a subject for the ClientCertificate. |
|
127 |
""" |
|
128 |
# create the certificate record and save |
|
129 |
cert = models.ClientCertificate() |
|
130 |
cert.user = user |
|
131 |
cert.subject_dn = ssl_info.subject_dn |
|
132 |
cert.issuer_dn = ssl_info.issuer_dn |
|
133 |
cert.serial = ssl_info.serial |
|
134 |
cert.cert = ssl_info.cert |
|
135 |
cert.save() |
|
136 | ||
137 |
return user |
|
138 | ||
139 |
def build_user(self, username, ssl_info): |
|
140 |
""" |
|
141 |
create a valid (and stored) django user to be associated with the |
|
142 |
newly created certificate record. This method can be "overwritten" by |
|
143 |
using the SSLAUTH_CREATE_USER_CALLBACK setting. |
|
144 |
""" |
|
145 |
User = get_user_model() |
|
146 |
user = User() |
|
147 |
setattr(user, User.USERNAME_FIELD, username) |
|
148 |
if hasattr(User, 'set_unusable_password'): |
|
149 |
user.set_unusable_password() |
|
150 |
user.is_active = True |
|
151 |
user.save() |
|
152 |
return user |
|
153 | ||
154 |
@classmethod |
|
155 |
def get_saml2_authn_context(cls): |
|
156 |
from authentic2.compat_lasso import lasso |
|
157 |
return lasso.SAML2_AUTHN_CONTEXT_X509 |
src/authentic2/auth2_auth/auth2_ssl/locale/fr/LC_MESSAGES/django.po | ||
---|---|---|
1 |
# authentic2 auth ssl french l10n |
|
2 |
# Copyright (C) 2015 Entr'ouvert |
|
3 |
# This file is distributed under the same license as the Authentic package. |
|
4 |
# Frederic Peters <fpeters@entrouvert.com>, 2010. |
|
5 |
# |
|
6 |
msgid "" |
|
7 |
msgstr "" |
|
8 |
"Project-Id-Version: Authentic\n" |
|
9 |
"Report-Msgid-Bugs-To: \n" |
|
10 |
"POT-Creation-Date: 2019-03-08 10:43+0100\n" |
|
11 |
"PO-Revision-Date: 2013-07-23 17:41+0200\n" |
|
12 |
"Last-Translator: Mikaël Ates <mates@entrouvert.com>\n" |
|
13 |
"Language-Team: None\n" |
|
14 |
"Language: fr\n" |
|
15 |
"MIME-Version: 1.0\n" |
|
16 |
"Content-Type: text/plain; charset=UTF-8\n" |
|
17 |
"Content-Transfer-Encoding: 8bit\n" |
|
18 |
"Plural-Forms: nplurals=2; plural=n>1;\n" |
|
19 | ||
20 |
#: src/authentic2/auth2_auth/auth2_ssl/frontends.py:16 |
|
21 |
msgid "SSL with certificates" |
|
22 |
msgstr "Certificats SSL" |
|
23 | ||
24 |
#: src/authentic2/auth2_auth/auth2_ssl/templates/auth/account_linking_ssl.html:5 |
|
25 |
#: src/authentic2/auth2_auth/auth2_ssl/templates/auth/account_linking_ssl.html:9 |
|
26 |
msgid "Log in to link your certificate with an existing account" |
|
27 |
msgstr "connectez-vous pour lier votre certificat avec un compte existant" |
|
28 | ||
29 |
#: src/authentic2/auth2_auth/auth2_ssl/templates/auth/account_linking_ssl.html:18 |
|
30 |
#: src/authentic2/auth2_auth/auth2_ssl/templates/auth/account_linking_ssl.html:25 |
|
31 |
msgid "Username:" |
|
32 |
msgstr "Nom d'utilisateur :" |
|
33 | ||
34 |
#: src/authentic2/auth2_auth/auth2_ssl/templates/auth/account_linking_ssl.html:21 |
|
35 |
#: src/authentic2/auth2_auth/auth2_ssl/templates/auth/account_linking_ssl.html:29 |
|
36 |
msgid "Password:" |
|
37 |
msgstr "Mot de passe :" |
|
38 | ||
39 |
#: src/authentic2/auth2_auth/auth2_ssl/templates/auth/account_linking_ssl.html:34 |
|
40 |
msgid "Create me a new account" |
|
41 |
msgstr "Me créer un nouveau compte" |
|
42 | ||
43 |
#: src/authentic2/auth2_auth/auth2_ssl/templates/auth/account_linking_ssl.html:38 |
|
44 |
#: src/authentic2/auth2_auth/auth2_ssl/templates/auth/login_form_ssl.html:9 |
|
45 |
msgid "Log in" |
|
46 |
msgstr "S'identifier" |
|
47 | ||
48 |
#: src/authentic2/auth2_auth/auth2_ssl/templates/auth/login_form_ssl.html:4 |
|
49 |
msgid "Login using a certificate." |
|
50 |
msgstr "Connexion par certificat." |
|
51 | ||
52 |
#: src/authentic2/auth2_auth/auth2_ssl/templates/auth/login_form_ssl.html:11 |
|
53 |
msgid "Cancel" |
|
54 |
msgstr "Annuler" |
|
55 | ||
56 |
#: src/authentic2/auth2_auth/auth2_ssl/templates/ssl/profile.html:3 |
|
57 |
msgid "SSL Certificates" |
|
58 |
msgstr "Certificats SSL" |
|
59 | ||
60 |
#: src/authentic2/auth2_auth/auth2_ssl/templates/ssl/profile.html:21 |
|
61 |
msgid "Delete" |
|
62 |
msgstr "Supprimer" |
|
63 | ||
64 |
#: src/authentic2/auth2_auth/auth2_ssl/templates/ssl/profile.html:28 |
|
65 |
msgid "Add a certificate?" |
|
66 |
msgstr "Ajouter un certificat ?" |
|
67 | ||
68 |
#: src/authentic2/auth2_auth/auth2_ssl/templates/ssl/profile.html:30 |
|
69 |
msgid "Add" |
|
70 |
msgstr "Ajouter" |
|
71 | ||
72 |
#: src/authentic2/auth2_auth/auth2_ssl/views.py:28 |
|
73 |
msgid "SSL Client Authentication failed. No client certificate found." |
|
74 |
msgstr "Echec de l'authentification cliente SSL. Aucun certificat trouvé." |
|
75 | ||
76 |
#: src/authentic2/auth2_auth/auth2_ssl/views.py:35 |
|
77 |
msgid "SSL Client Authentication failed. Your client certificate is not valid." |
|
78 |
msgstr "" |
|
79 |
"Echec de l'authentification cliente SSL. Votre certificat n'est pas valide." |
|
80 | ||
81 |
#: src/authentic2/auth2_auth/auth2_ssl/views.py:54 |
|
82 |
#: src/authentic2/auth2_auth/auth2_ssl/views.py:70 |
|
83 |
#: src/authentic2/auth2_auth/auth2_ssl/views.py:118 |
|
84 |
msgid "SSL Client Authentication failed. Internal server error." |
|
85 |
msgstr "Echec de l'authentification SSL. Erreur interne du serveur." |
|
86 | ||
87 |
#: src/authentic2/auth2_auth/auth2_ssl/views.py:140 |
|
88 |
msgid "Certificate deleted." |
|
89 |
msgstr "Certificat supprimé." |
src/authentic2/auth2_auth/auth2_ssl/middleware.py | ||
---|---|---|
1 |
# authentic2 - versatile identity manager |
|
2 |
# Copyright (C) 2010-2019 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
from django.contrib.auth import authenticate, login |
|
18 | ||
19 |
from . import util, app_settings |
|
20 | ||
21 | ||
22 |
class SSLAuthMiddleware(object): |
|
23 |
""" |
|
24 |
attempts to find a valid user based on the client certificate info |
|
25 |
""" |
|
26 |
def process_request(self, request): |
|
27 |
if app_settings.USE_COOKIE and request.user.is_authenticated(): |
|
28 |
return |
|
29 |
ssl_info = util.SSLInfo(request) |
|
30 |
user = authenticate(ssl_info=ssl_info) |
|
31 |
if user and request.user != user: |
|
32 |
login(request, user) |
src/authentic2/auth2_auth/auth2_ssl/migrations/0003_auto_20190614_1438.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
# Generated by Django 1.11.20 on 2019-06-14 12:38 |
|
3 |
from __future__ import unicode_literals |
|
4 | ||
5 |
from django.db import migrations |
|
6 | ||
7 | ||
8 |
class Migration(migrations.Migration): |
|
9 | ||
10 |
dependencies = [ |
|
11 |
('auth2_ssl', '0002_auto_20150409_1840'), |
|
12 |
] |
|
13 | ||
14 |
operations = [ |
|
15 |
migrations.RemoveField( |
|
16 |
model_name='clientcertificate', |
|
17 |
name='user', |
|
18 |
), |
|
19 |
migrations.DeleteModel( |
|
20 |
name='ClientCertificate', |
|
21 |
), |
|
22 |
] |
src/authentic2/auth2_auth/auth2_ssl/models.py | ||
---|---|---|
1 |
# authentic2 - versatile identity manager |
|
2 |
# Copyright (C) 2010-2019 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
from django.db import models |
|
18 |
from django.conf import settings |
|
19 |
from django.utils import six |
|
20 | ||
21 |
from . import util |
|
22 | ||
23 | ||
24 |
@six.python_2_unicode_compatible |
|
25 |
class ClientCertificate(models.Model): |
|
26 |
serial = models.CharField(max_length=255, blank=True) |
|
27 |
subject_dn = models.CharField(max_length=255) |
|
28 |
issuer_dn = models.CharField(max_length=255) |
|
29 |
cert = models.TextField() |
|
30 |
user = models.ForeignKey(settings.AUTH_USER_MODEL) |
|
31 | ||
32 |
def __str__(self): |
|
33 |
return self.subject_dn |
|
34 | ||
35 |
def explode_subject_dn(self): |
|
36 |
return util.explode_dn(self.subject_dn) |
|
37 | ||
38 |
def explode_issuer_dn(self): |
|
39 |
return util.explode_dn(self.issuer_dn) |
|
40 |
src/authentic2/auth2_auth/auth2_ssl/templates/auth/account_linking_ssl.html | ||
---|---|---|
1 |
{% extends "authentic2/base-page.html" %} |
|
2 |
{% load i18n %} |
|
3 | ||
4 |
{% block title %} |
|
5 |
{% trans "Log in to link your certificate with an existing account" %} |
|
6 |
{% endblock %} |
|
7 | ||
8 |
{% block content %} |
|
9 |
<p>* {% trans "Log in to link your certificate with an existing account" %}</p> |
|
10 |
<div id="login-actions"> |
|
11 |
<form id="login-form" method="post" action="{% url "post_account_linking" %}"> |
|
12 |
{% csrf_token %} |
|
13 |
<ul class="errorlist"> |
|
14 |
{% for error in form.non_field_errors %} |
|
15 |
<li>{{ error|escape }}</li> |
|
16 |
{% endfor %} |
|
17 |
{% for error in form.username.errors %} |
|
18 |
<li>{% trans "Username:" %} {{ error|escape }}</li> |
|
19 |
{% endfor %} |
|
20 |
{% for error in form.password.errors %} |
|
21 |
<li>{% trans "Password:" %} {{ error|escape }}</li> |
|
22 |
{% endfor %} |
|
23 |
</ul> |
|
24 |
<p> |
|
25 |
<label for="id_username">{% trans "Username:" %}</label> |
|
26 |
<input id="id_username" type="text" name="username" maxlength="30" /> |
|
27 |
</p> |
|
28 |
<p> |
|
29 |
<label for="id_password">{% trans "Password:" %}</label> |
|
30 |
<input type="password" name="password" id="id_password" /> |
|
31 |
</p> |
|
32 | ||
33 |
<p> |
|
34 |
<label for="id_do_creation">{% trans "Create me a new account" %}</label> |
|
35 |
<input type="checkbox" name="do_creation" id="id_do_creation" /> |
|
36 |
</p> |
|
37 | ||
38 |
<button class="submit-button">{% trans 'Log in' %}</button> |
|
39 |
<input type="hidden" name="next" value="{{ next_url }}" /> |
|
40 |
</form> |
|
41 |
</div> |
|
42 |
<script type="text/javascript"> |
|
43 |
document.getElementById('id_username').focus(); |
|
44 |
</script> |
|
45 |
{% endblock %} |
src/authentic2/auth2_auth/auth2_ssl/templates/auth/login_form_ssl.html | ||
---|---|---|
1 |
{% load i18n %} |
|
2 |
<div id="login-ssl"> |
|
3 |
<p> |
|
4 |
{% trans "Login using a certificate." %} |
|
5 |
</p> |
|
6 |
<form method="post" action=""> |
|
7 |
{% csrf_token %} |
|
8 |
{{ form.as_p }} |
|
9 |
<button class="submit-button" name="{{ submit_name }}">{% trans "Log in" %}</button> |
|
10 |
{% if cancel %} |
|
11 |
<button class="cancel-button" name="cancel" formnovalidate>{% trans 'Cancel' %}"</button> |
|
12 |
{% endif %} |
|
13 |
</form> |
|
14 |
</div> |
src/authentic2/auth2_auth/auth2_ssl/templates/ssl/profile.html | ||
---|---|---|
1 |
{% load i18n %} |
|
2 |
<h4 id="a2-ssl-certificate-profile" class="a2-ssl-certificate-profile-title"> |
|
3 |
{% trans "SSL Certificates" %} |
|
4 |
</h4> |
|
5 | ||
6 |
<div class="a2-ssl-certificate-profile-body"> |
|
7 |
<ul class="a2-ssl-certificate-list"> |
|
8 |
{% for certificate in certificates %} |
|
9 |
<li class="a2-ssl-certificate-item"> |
|
10 |
<form action="{% url "delete_certificate" certificate_pk=certificate.pk %}" |
|
11 |
method="post"> |
|
12 |
{% csrf_token %} |
|
13 |
<p class="a2-ssl-certificate-dn"> |
|
14 |
<dl class="a2-ssl-certificate-dn-parts"> |
|
15 |
{% for k, v in certificate.explode_subject_dn %} |
|
16 |
<dt class="a2-ssl-certificate-dn-part-name">{{ k }}</dt> |
|
17 |
<dd class="a2-ssl-certificate-dn-part-value">{{ v }}</dd> |
|
18 |
{% endfor %} |
|
19 |
</dl> |
|
20 |
</p> |
|
21 |
<button class="submit-button a2-ssl-certificate-submit-button">{% trans "Delete" %}</button> |
|
22 |
</form> |
|
23 |
</p> |
|
24 |
{% endfor %} |
|
25 |
</ul> |
|
26 |
<p> |
|
27 |
<form action="{% url "user_signin_ssl" %}" method="get"> |
|
28 |
<label for="id_del_cert">{% trans "Add a certificate?" %}</label> |
|
29 |
<input type="hidden" name="next" value="{% url "account_management" %}#a2-ssl-certificate-profile" /> |
|
30 |
<button class="submit-button a2-ssl-certificate-submit-button">{% trans "Add" %}</button> |
|
31 |
</form> |
|
32 |
</p> |
|
33 |
</div> |
src/authentic2/auth2_auth/auth2_ssl/urls.py | ||
---|---|---|
1 |
# authentic2 - versatile identity manager |
|
2 |
# Copyright (C) 2010-2019 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
from django.conf.urls import url |
|
18 |
from .views import (handle_request, post_account_linking, delete_certificate, error_ssl) |
|
19 | ||
20 |
urlpatterns = [ |
|
21 |
url(r'^$', |
|
22 |
handle_request, |
|
23 |
name='user_signin_ssl'), |
|
24 |
url(r'^post_account_linking/$', |
|
25 |
post_account_linking, |
|
26 |
name='post_account_linking'), |
|
27 |
url(r'^delete_certificate/(?P<certificate_pk>\d+)/$', |
|
28 |
delete_certificate, |
|
29 |
name='delete_certificate'), |
|
30 |
url(r'^error_ssl/$', |
|
31 |
error_ssl, |
|
32 |
name='error_ssl'), |
|
33 |
] |
src/authentic2/auth2_auth/auth2_ssl/util.py | ||
---|---|---|
1 |
# authentic2 - versatile identity manager |
|
2 |
# Copyright (C) 2010-2019 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
import base64 |
|
18 |
import six |
|
19 | ||
20 |
from . import app_settings |
|
21 | ||
22 |
X509_KEYS = { |
|
23 |
'subject_dn': 'SSL_CLIENT_S_DN', |
|
24 |
'issuer_dn': 'SSL_CLIENT_I_DN', |
|
25 |
'serial': ('SSL_CLIENT_M_SERIAL', 'SSL_CLIENT_SERIAL'), |
|
26 |
'cert': 'SSL_CLIENT_CERT', |
|
27 |
'verify': 'SSL_CLIENT_VERIFY', |
|
28 |
} |
|
29 | ||
30 | ||
31 |
def normalize_cert(certificate_pem): |
|
32 |
'''Normalize content of the certificate''' |
|
33 |
base64_content = ''.join(certificate_pem.splitlines()[1:-1]) |
|
34 |
content = base64.b64decode(base64_content) |
|
35 |
return base64.b64encode(content) |
|
36 | ||
37 | ||
38 |
def explode_dn(dn): |
|
39 |
'''Extract sub element of a DN as displayed by mod_ssl or nginx_ssl''' |
|
40 |
dn = dn.strip('/') |
|
41 |
parts = dn.split('/') |
|
42 |
parts = [part.split('=') for part in parts] |
|
43 |
parts = [(part[0], part[1].decode('string_escape').decode('utf-8')) for part in parts] |
|
44 |
return parts |
|
45 | ||
46 | ||
47 |
TRANSFORM = { |
|
48 |
'cert': normalize_cert, |
|
49 |
} |
|
50 | ||
51 | ||
52 |
class SSLInfo(object): |
|
53 |
""" |
|
54 |
Encapsulates the SSL environment variables in a read-only object. It |
|
55 |
attempts to find the ssl vars based on the type of request passed to the |
|
56 |
constructor. Currently only WSGIRequest and ModPythonRequest are |
|
57 |
supported. |
|
58 |
""" |
|
59 |
def __init__(self, request): |
|
60 |
name = request.__class__.__name__ |
|
61 |
if app_settings.FORCE_ENV: |
|
62 |
env = app_settings.FORCE_ENV |
|
63 |
elif name == 'WSGIRequest': |
|
64 |
env = request.environ |
|
65 |
elif name == 'ModPythonRequest': |
|
66 |
env = request._req.subprocess_env |
|
67 |
else: |
|
68 |
raise EnvironmentError('The SSL authentication currently only \ |
|
69 |
works with mod_python or wsgi requests') |
|
70 |
self.read_env(env) |
|
71 |
pass |
|
72 | ||
73 |
def read_env(self, env): |
|
74 |
for attr, keys in X509_KEYS.items(): |
|
75 |
if isinstance(keys, six.string_types): |
|
76 |
keys = [keys] |
|
77 |
for key in keys: |
|
78 |
if key in env and env[key]: |
|
79 |
v = env[key] |
|
80 |
if attr in TRANSFORM: |
|
81 |
v = TRANSFORM[attr](v) |
|
82 |
self.__dict__[attr] = v |
|
83 |
else: |
|
84 |
self.__dict__[attr] = None |
|
85 | ||
86 |
if self.__dict__['verify'] == 'SUCCESS': |
|
87 |
self.__dict__['verify'] = True |
|
88 |
else: |
|
89 |
self.__dict__['verify'] = False |
|
90 | ||
91 |
def get(self, attr): |
|
92 |
return self.__getattr__(attr) |
|
93 | ||
94 |
def __getattr__(self, attr): |
|
95 |
if attr in self.__dict__: |
|
96 |
return self.__dict__[attr] |
|
97 |
else: |
|
98 |
raise AttributeError('SSLInfo does not contain key %s' % attr) |
|
99 | ||
100 |
def __setattr__(self, attr, value): |
|
101 |
raise AttributeError('SSL vars are read only!') |
|
102 | ||
103 |
def __repr__(self): |
|
104 |
return '<SSLInfo %s>' % self.__dict__ |
src/authentic2/auth2_auth/auth2_ssl/views.py | ||
---|---|---|
1 |
# authentic2 - versatile identity manager |
|
2 |
# Copyright (C) 2010-2019 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
import logging |
|
18 | ||
19 |
from django.utils.translation import ugettext as _ |
|
20 |
from django.shortcuts import render |
|
21 |
from django.views.decorators.csrf import csrf_exempt |
|
22 |
from django.views.generic.base import TemplateView |
|
23 |
from django.template.loader import render_to_string |
|
24 |
from django.contrib import messages |
|
25 |
from django.contrib.auth.forms import AuthenticationForm |
|
26 |
from django.contrib.auth import authenticate, login |
|
27 | ||
28 | ||
29 |
from authentic2.utils import continue_to_next_url, record_authentication_event, redirect, redirect_to_login |
|
30 | ||
31 |
from . import models, util, app_settings |
|
32 | ||
33 |
logger = logging.getLogger(__name__) |
|
34 | ||
35 | ||
36 |
def handle_request(request): |
|
37 |
# Check certificate validity |
|
38 |
ssl_info = util.SSLInfo(request) |
|
39 |
accept_self_signed = app_settings.ACCEPT_SELF_SIGNED |
|
40 | ||
41 |
if not ssl_info.cert: |
|
42 |
logger.error('SSL Client Authentication failed: SSL CGI variable CERT is missing') |
|
43 |
messages.add_message(request, messages.ERROR, |
|
44 |
_('SSL Client Authentication failed. No client certificate found.')) |
|
45 |
return redirect_to_login(request) |
|
46 |
elif not accept_self_signed and not ssl_info.verify: |
|
47 |
logger.error('SSL Client Authentication failed: SSL CGI variable VERIFY is not SUCCESS') |
|
48 |
messages.add_message(request, messages.ERROR, |
|
49 |
_('SSL Client Authentication failed. Your client certificate is not valid.')) |
|
50 |
return redirect_to_login(request) |
|
51 | ||
52 |
# SSL entries for this certificate? |
|
53 |
user = authenticate(ssl_info=ssl_info) |
|
54 | ||
55 |
# If the user is logged in, no need to create an account |
|
56 |
# If there is an SSL entries, no need for account creation, |
|
57 |
# just need to login, treated after |
|
58 |
if 'do_creation' in request.session and not user \ |
|
59 |
and not request.user.is_authenticated(): |
|
60 |
from backends import SSLBackend |
|
61 |
if SSLBackend().create_user(ssl_info): |
|
62 |
user = authenticate(ssl_info=ssl_info) |
|
63 |
logger.info(u'account created for %s', user) |
|
64 |
else: |
|
65 |
logger.error('account creation failure') |
|
66 |
messages.add_message(request, messages.ERROR, |
|
67 |
_('SSL Client Authentication failed. Internal server error.')) |
|
68 |
return redirect_to_login(request) |
|
69 | ||
70 |
# No SSL entries and no user session, redirect account linking page |
|
71 |
if not user and not request.user.is_authenticated(): |
|
72 |
return render(request, 'auth/account_linking_ssl.html') |
|
73 | ||
74 |
# No SSL entries but active user session, perform account linking |
|
75 |
if not user and request.user.is_authenticated(): |
|
76 |
from backend import SSLBackend |
|
77 |
if not SSLBackend().link_user(ssl_info, request.user): |
|
78 |
logger.error('login() failed') |
|
79 |
messages.add_message(request, messages.ERROR, |
|
80 |
_('SSL Client Authentication failed. Internal server error.')) |
|
81 |
return redirect_to_login(request) |
|
82 |
logger.info('Successful linking of the SSL Certificate to an account') |
|
83 | ||
84 |
# SSL Entries found for this certificate, |
|
85 |
# if the user is logged out, we login |
|
86 |
if not request.user.is_authenticated(): |
|
87 |
login(request, user) |
|
88 |
record_authentication_event(request, how='ssl') |
|
89 |
return continue_to_next_url(request) |
|
90 | ||
91 |
# SSL Entries found for this certificate, if the user is logged in, we |
|
92 |
# check that the SSL entry for the certificate is this user. |
|
93 |
# else, we make this certificate point on that user. |
|
94 |
if user.username != request.user.username: |
|
95 |
logger.warning(u'The certificate belongs to %s, but %s is logged with, we change the association!', |
|
96 |
user, request.user) |
|
97 |
from backends import SSLBackend |
|
98 |
cert = SSLBackend().get_certificate(ssl_info) |
|
99 |
cert.user = request.user |
|
100 |
cert.save() |
|
101 |
return continue_to_next_url(request) |
|
102 | ||
103 | ||
104 |
@csrf_exempt |
|
105 |
def post_account_linking(request): |
|
106 |
if request.method == "POST": |
|
107 |
if 'do_creation' in request.POST and request.POST['do_creation'] == 'on': |
|
108 |
request.session['do_creation'] = 'do_creation' |
|
109 |
return redirect_to_login(request, login_url='user_signin_ssl') |
|
110 |
form = AuthenticationForm(data=request.POST) |
|
111 |
if form.is_valid(): |
|
112 |
user = form.get_user() |
|
113 |
login(request, user) |
|
114 |
record_authentication_event(request, how='password') |
|
115 |
return redirect_to_login(request, login_url='user_signin_ssl') |
|
116 |
else: |
|
117 |
return render(request, 'auth/account_linking_ssl.html') |
|
118 |
else: |
|
119 |
return render(request, 'auth/account_linking_ssl.html') |
|
120 | ||
121 | ||
122 |
def profile(request, template_name='ssl/profile.html', *args, **kwargs): |
|
123 |
context = kwargs.pop('context', {}) |
|
124 |
certificates = models.ClientCertificate.objects.filter(user=request.user) |
|
125 |
context.update({'certificates': certificates}) |
|
126 |
return render_to_string(template_name, context, request=request) |
|
127 | ||
128 | ||
129 |
def delete_certificate(request, certificate_pk): |
|
130 |
qs = models.ClientCertificate.objects.filter(pk=certificate_pk) |
|
131 |
count = qs.count() |
|
132 |
qs.delete() |
|
133 |
if count: |
|
134 |
logger.info('client certificate %s deleted', certificate_pk) |
|
135 |
messages.info(request, _('Certificate deleted.')) |
|
136 |
return redirect(request, 'account_management', fragment='a2-ssl-certificate-profile') |
|
137 | ||
138 | ||
139 |
class SslErrorView(TemplateView): |
|
140 |
template_name = 'error_ssl.html' |
|
141 |
error_ssl = SslErrorView.as_view() |
src/authentic2/auth2_auth/locale/fr/LC_MESSAGES/django.po | ||
---|---|---|
1 |
# French translation of Authentic |
|
2 |
# Copyright (C) 2010, 2011 Entr'ouvert |
|
3 |
# This file is distributed under the same license as the Authentic package. |
|
4 |
# Frederic Peters <fpeters@entrouvert.com>, 2010. |
|
5 |
# |
|
6 |
msgid "" |
|
7 |
msgstr "" |
|
8 |
"Project-Id-Version: Authentic\n" |
|
9 |
"Report-Msgid-Bugs-To: \n" |
|
10 |
"POT-Creation-Date: 2013-07-23 18:01+0200\n" |
|
11 |
"PO-Revision-Date: 2013-07-23 18:01+0200\n" |
|
12 |
"Last-Translator: Mikaël Ates <mates@entrouvert.com>\n" |
|
13 |
"Language-Team: None\n" |
|
14 |
"Language: fr\n" |
|
15 |
"MIME-Version: 1.0\n" |
|
16 |
"Content-Type: text/plain; charset=UTF-8\n" |
|
17 |
"Content-Transfer-Encoding: 8bit\n" |
|
18 |
"Plural-Forms: nplurals=2; plural=n>1;\n" |
|
19 | ||
20 |
#: backend.py:17 templates/auth/login_password_profile.html:2 |
|
21 |
msgid "Password" |
|
22 |
msgstr "Mot de passe" |
|
23 | ||
24 |
#: models.py:23 |
|
25 |
#, python-format |
|
26 |
msgid "Authentication of %(who)s by %(how)s at %(when)s" |
|
27 |
msgstr "Authentification de %(who)s par la méthode %(how)s à %(when)s" |
|
28 | ||
29 |
#: templates/error_ssl.html:4 |
|
30 |
msgid "Error: authentication failure" |
|
31 |
msgstr "Erreur: Echec de l'authentification" |
|
32 | ||
33 |
#: templates/error_ssl.html:8 |
|
34 |
msgid "Authentication failure" |
|
35 |
msgstr "Échec d'authentification" |
|
36 | ||
37 |
#: templates/error_ssl.html:10 |
|
38 |
msgid "The SSL authentication has failed" |
|
39 |
msgstr "L'authentification par certificat électronique a échouée." |
|
40 | ||
41 |
#: templates/auth/login.html:5 templates/auth/login_form.html:6 |
|
42 |
msgid "Log in" |
|
43 |
msgstr "S'identifier" |
|
44 | ||
45 |
#: templates/auth/login_form.html:8 |
|
46 |
msgid "Cancel" |
|
47 |
msgstr "Annuler" |
|
48 | ||
49 |
#: templates/auth/login_form.html:14 |
|
50 |
msgid "Forgot password?" |
|
51 |
msgstr "Mot de passe oublié ?" |
|
52 | ||
53 |
#: templates/auth/login_form.html:14 |
|
54 |
msgid "Reset it!" |
|
55 |
msgstr "Le réinitialiser !" |
|
56 | ||
57 |
#: templates/auth/login_form.html:15 |
|
58 |
msgid "Not a member?" |
|
59 |
msgstr "Pas un membre ?" |
|
60 | ||
61 |
#: templates/auth/login_form.html:15 |
|
62 |
msgid "Register!" |
|
63 |
msgstr "S'inscrire !" |
|
64 | ||
65 |
#: templates/auth/login_password_profile.html:6 |
|
66 |
msgid "Change password" |
|
67 |
msgstr "Modifier votre mot de passe" |
src/authentic2/settings.py | ||
---|---|---|
132 | 132 |
'authentic2.saml', |
133 | 133 |
'authentic2.idp', |
134 | 134 |
'authentic2.idp.saml', |
135 |
'authentic2.auth2_auth', |
|
136 | 135 |
'authentic2.attribute_aggregator', |
137 | 136 |
'authentic2.disco_service', |
138 | 137 |
'authentic2.manager', |
139 |
- |