0001-remove-authentic2_idp_openid-fixes-23515.patch
MANIFEST.in | ||
---|---|---|
13 | 13 |
recursive-include src/authentic2/saml/templates *.html *.txt *.xml |
14 | 14 |
recursive-include src/authentic2/templates *.html *.txt *.xml |
15 | 15 |
recursive-include src/authentic2/idp/templates *.html *.txt *.xml |
16 |
recursive-include src/authentic2_idp_openid/templates *.html *.txt *.xml |
|
17 | 16 |
recursive-include src/authentic2_idp_cas/templates *.html *.txt *.xml |
18 | 17 |
recursive-include src/authentic2/idp/saml/templates *.html *.txt *.xml |
19 | 18 |
recursive-include src/authentic2/auth2_auth/auth2_ssl/templates *.html *.txt *.xml |
... | ... | |
30 | 29 |
recursive-include src/authentic2/saml/locale *.po *.mo |
31 | 30 |
recursive-include src/authentic2/idp/locale *.po *.mo |
32 | 31 |
recursive-include src/authentic2/idp/saml/locale *.po *.mo |
33 |
recursive-include src/authentic2_idp_openid/locale *.po *.mo |
|
34 | 32 |
recursive-include src/authentic2_idp_cas/locale *.po *.mo |
35 | 33 |
recursive-include src/authentic2/auth2_auth/locale *.po *.mo |
36 | 34 |
recursive-include src/authentic2/auth2_auth/auth2_ssl/locale *.po *.mo |
debian-jessie/conf/authentic.conf | ||
---|---|---|
29 | 29 | |
30 | 30 |
# Enables some features |
31 | 31 |
#export IDP_SAML2='yes' |
32 |
#export IDP_OPENID='yes' # require package python-openid |
|
33 | 32 |
#export IDP_CAS='yes' |
34 | 33 |
#export AUTH_SAML2='yes' |
35 | 34 |
#export AUTH_OPENID='yes' # require package python-openid |
debian-jessie/debian_config.py | ||
---|---|---|
137 | 137 |
'A2_AUTH_PASSWORD_ENABLE', |
138 | 138 |
'SSLAUTH_ENABLE', |
139 | 139 |
'A2_IDP_SAML2_ENABLE', |
140 |
'IDP_OPENID', |
|
141 | ||
142 | 140 |
) |
143 | 141 | |
144 | 142 |
def to_boolean(name, default=True): |
debian-jessie/multitenant/config.py | ||
---|---|---|
64 | 64 |
#A2_IDP_SAML2_ENABLE = False |
65 | 65 |
# CAS 1.0 / 2.0 IDP |
66 | 66 |
#A2_IDP_CAS_ENABLE = False |
67 |
# OpenID 1.0 / 2.0 IDP |
|
68 |
#A2_IDP_OPENID_ENABLE = False |
|
69 | 67 | |
70 | 68 |
# Authentifications |
71 | 69 |
#A2_AUTH_PASSWORD_ENABLE = True |
debian-wheezy/conf/authentic.conf | ||
---|---|---|
29 | 29 | |
30 | 30 |
# Enables some features |
31 | 31 |
#export IDP_SAML2='yes' |
32 |
#export IDP_OPENID='yes' # require package python-openid |
|
33 | 32 |
#export IDP_CAS='yes' |
34 | 33 |
#export AUTH_SAML2='yes' |
35 | 34 |
#export AUTH_OPENID='yes' # require package python-openid |
debian-wheezy/debian_config.py | ||
---|---|---|
140 | 140 |
'A2_AUTH_PASSWORD_ENABLE', |
141 | 141 |
'SSLAUTH_ENABLE', |
142 | 142 |
'A2_IDP_SAML2_ENABLE', |
143 |
'IDP_OPENID', |
|
144 | ||
145 | 143 |
) |
146 | 144 | |
147 | 145 |
def to_boolean(name, default=True): |
debian-wheezy/multitenant/config.py | ||
---|---|---|
64 | 64 |
#A2_IDP_SAML2_ENABLE = False |
65 | 65 |
# CAS 1.0 / 2.0 IDP |
66 | 66 |
#A2_IDP_CAS_ENABLE = False |
67 |
# OpenID 1.0 / 2.0 IDP |
|
68 |
#A2_IDP_OPENID_ENABLE = False |
|
69 | 67 | |
70 | 68 |
# Authentifications |
71 | 69 |
#A2_AUTH_PASSWORD_ENABLE = True |
debian-wheezy/patches/01-hide-oidc-plugins.diff | ||
---|---|---|
11 | 11 |
'XStatic-jQuery', |
12 | 12 |
'XStatic-jquery-ui', |
13 | 13 |
'xstatic-select2', |
14 |
@@ -163,11 +161,9 @@ setup(name="authentic2",
|
|
14 |
@@ -163,10 +161,8 @@ setup(name="authentic2",
|
|
15 | 15 |
'authentic2.plugin': [ |
16 | 16 |
'authentic2-auth-ssl = authentic2.auth2_auth.auth2_ssl:Plugin', |
17 | 17 |
'authentic2-auth-saml = authentic2_auth_saml:Plugin', |
18 | 18 |
- 'authentic2-auth-oidc = authentic2_auth_oidc:Plugin', |
19 | 19 |
'authentic2-idp-saml2 = authentic2.idp.saml:Plugin', |
20 |
'authentic2-idp-openid = authentic2_idp_openid:Plugin', |
|
21 | 20 |
'authentic2-idp-cas = authentic2_idp_cas:Plugin', |
22 | 21 |
- 'authentic2-idp-oidc = authentic2_idp_oidc:Plugin', |
23 | 22 |
'authentic2-provisionning-ldap = authentic2_provisionning_ldap:Plugin', |
doc/settings_listing.rst | ||
---|---|---|
86 | 86 |
the signature keys are used for encryption. |
87 | 87 | |
88 | 88 | |
89 |
Activate or deactivate Authentic 2 as an OpenID provider |
|
90 |
======================================================== |
|
91 | ||
92 |
Variable: IDP_OPENID |
|
93 | ||
94 |
Values: |
|
95 | ||
96 |
* False: deactivate OpenID provider |
|
97 |
* True: activate OpenID provider |
|
98 | ||
99 | 89 |
Activate or deactivate Authentic 2 as a CAS server |
100 | 90 |
================================================== |
101 | 91 |
local_settings.py.example | ||
---|---|---|
5 | 5 |
# Activate SAML 2 idp |
6 | 6 |
A2_IDP_SAML2_ENABLE = True |
7 | 7 | |
8 |
# Activate OpenID idp |
|
9 |
A2_IDP_OPENID_ENABLE = True |
|
10 | ||
11 | 8 |
# Debugging helper |
12 | 9 |
try: |
13 | 10 |
import debug_toolbar |
setup.py | ||
---|---|---|
133 | 133 |
'XStatic-jquery-ui<1.12', |
134 | 134 |
'xstatic-select2', |
135 | 135 |
], |
136 |
extras_require = { |
|
137 |
'idp-openid': ['python-openid'], |
|
138 |
}, |
|
139 | 136 |
zip_safe=False, |
140 | 137 |
classifiers=[ |
141 | 138 |
"Development Status :: 5 - Production/Stable", |
... | ... | |
165 | 162 |
'authentic2-auth-saml = authentic2_auth_saml:Plugin', |
166 | 163 |
'authentic2-auth-oidc = authentic2_auth_oidc:Plugin', |
167 | 164 |
'authentic2-idp-saml2 = authentic2.idp.saml:Plugin', |
168 |
'authentic2-idp-openid = authentic2_idp_openid:Plugin', |
|
169 | 165 |
'authentic2-idp-cas = authentic2_idp_cas:Plugin', |
170 | 166 |
'authentic2-idp-oidc = authentic2_idp_oidc:Plugin', |
171 | 167 |
'authentic2-provisionning-ldap = authentic2_provisionning_ldap:Plugin', |
src/authentic2/dashboard.py | ||
---|---|---|
67 | 67 |
'authentic2.models.AuthenticationEvent', |
68 | 68 |
'authentic2.models.UserExternalId', |
69 | 69 |
'authentic2.models.DeletedUser', |
70 |
'authentic2.idp.idp_openid.models.*', |
|
71 | 70 |
'django.contrib.sessions.*', |
72 | 71 |
), |
73 | 72 |
)) |
src/authentic2_idp_openid/__init__.py | ||
---|---|---|
1 |
from django.utils.translation import ugettext_lazy as _ |
|
2 |
from django.core.exceptions import ImproperlyConfigured |
|
3 | ||
4 |
try: |
|
5 |
import openid |
|
6 |
except ImportError: |
|
7 |
from . import app_settings |
|
8 |
if app_settings.ENABLE: |
|
9 |
raise ImproperlyConfigured('OpenID idp is enabled by python-openid is not installed') |
|
10 |
class Plugin(object): |
|
11 |
pass |
|
12 |
else: |
|
13 |
class Plugin(object): |
|
14 |
def get_before_urls(self): |
|
15 |
from . import app_settings |
|
16 |
from django.conf.urls import patterns, include |
|
17 |
from authentic2.decorators import (setting_enabled, required, |
|
18 |
lasso_required) |
|
19 | ||
20 |
return required( |
|
21 |
( |
|
22 |
setting_enabled('ENABLE', settings=app_settings), |
|
23 |
lasso_required() |
|
24 |
), |
|
25 |
patterns('', |
|
26 |
(r'^idp/openid/', include(__name__ + '.urls')))) |
|
27 | ||
28 |
def get_apps(self): |
|
29 |
return [__name__] |
|
30 | ||
31 |
def get_admin_modules(self): |
|
32 |
from admin_tools.dashboard import modules |
|
33 |
return [modules.ModelList( |
|
34 |
_('OpenID'), |
|
35 |
models=( |
|
36 |
'%s.models.*' % __name__, |
|
37 |
), |
|
38 |
)] |
|
39 | ||
40 |
def get_idp_backends(self): |
|
41 |
return ['%s.backend.OpenIDBackend' % __name__] |
|
42 | ||
43 |
def get_before_middleware(self): |
|
44 |
return ['%s.middleware.OpenIDMiddleware' % __name__] |
src/authentic2_idp_openid/admin.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 | ||
3 |
from django.contrib import admin |
|
4 |
from models import TrustedRoot, Association, Nonce |
|
5 | ||
6 |
admin.site.register(TrustedRoot) |
|
7 |
admin.site.register(Association) |
|
8 |
admin.site.register(Nonce) |
src/authentic2_idp_openid/app_settings.py | ||
---|---|---|
1 |
class AppSettings(object): |
|
2 |
__DEFAULTS = dict( |
|
3 |
ENABLE=False, |
|
4 |
OPENID_ACTIONS={}, |
|
5 |
) |
|
6 | ||
7 |
def __init__(self, prefix): |
|
8 |
self.prefix = prefix |
|
9 | ||
10 |
def _setting(self, name, dflt): |
|
11 |
from django.conf import settings |
|
12 |
return getattr(settings, name, dflt) |
|
13 | ||
14 |
def _setting_with_prefix(self, name, dflt): |
|
15 |
return self._setting(self.prefix + name, dflt) |
|
16 | ||
17 |
@property |
|
18 |
def ENABLE(self): |
|
19 |
return self._setting_with_prefix('ENABLE', |
|
20 |
self._settings('IDP_OPENID', |
|
21 |
self.__DEFAULTS['ENABLE'])) |
|
22 | ||
23 |
def __getattr__(self, name): |
|
24 |
if name not in self.__DEFAULTS: |
|
25 |
raise AttributeError(name) |
|
26 |
return self._setting_with_prefix(name, self.__DEFAULTS[name]) |
|
27 | ||
28 | ||
29 |
# Ugly? Guido recommends this himself ... |
|
30 |
# http://mail.python.org/pipermail/python-ideas/2012-May/014969.html |
|
31 |
import sys |
|
32 |
app_settings = AppSettings('A2_IDP_OPENID_') |
|
33 |
app_settings.__name__ = __name__ |
|
34 |
sys.modules[__name__] = app_settings |
src/authentic2_idp_openid/backend.py | ||
---|---|---|
1 |
import logging |
|
2 | ||
3 |
from django.core.urlresolvers import reverse |
|
4 | ||
5 |
from authentic2.utils import Service |
|
6 | ||
7 |
from . import models, app_settings |
|
8 | ||
9 | ||
10 |
logger = logging.getLogger(__name__) |
|
11 | ||
12 | ||
13 |
class OpenIDBackend(object): |
|
14 |
def service_list(self, request): |
|
15 |
if not request.user.is_authenticated(): |
|
16 |
return () |
|
17 |
q = models.TrustedRoot.objects.filter(user=request.user.id) |
|
18 |
ls = [] |
|
19 |
for service_provider in q: |
|
20 |
actions = [] |
|
21 |
actions.append(('go', 'GET', service_provider.trust_root, None)) |
|
22 |
if app_settings.OPENID_ACTIONS: |
|
23 |
tpl = app_settings.OPENID_ACTIONS.get(service_provider.trust_root, None) |
|
24 |
if tpl: |
|
25 |
actions.append(('template', tpl)) |
|
26 |
actions.append(('unlink', 'GET', reverse('trustedroot_delete', |
|
27 |
kwargs={'pk': service_provider.id}), None)) |
|
28 |
ls.append(Service(url=None, name=service_provider.trust_root, |
|
29 |
actions=actions)) |
|
30 |
return ls |
src/authentic2_idp_openid/conf.py | ||
---|---|---|
1 |
import os |
|
2 |
import tempfile |
|
3 |
from django.conf import settings |
|
4 | ||
5 |
tempdir = tempfile.gettempdir() |
|
6 | ||
7 |
STORE = getattr(settings, 'OPENID_PROVIDER_STORE', |
|
8 |
'authentic2.idp.idp_openid.openid_store.DjangoOpenIDStore') |
|
9 | ||
10 |
FILESTORE_PATH = getattr(settings, 'OPENID_PROVIDER_FILESTORE_PATH', |
|
11 |
os.path.join(tempdir, 'openid-filestore')) |
src/authentic2_idp_openid/decorators.py | ||
---|---|---|
1 |
from authentic2.decorators import setting_enabled |
|
2 | ||
3 |
from . import app_settings |
|
4 | ||
5 |
def openid_enabled(func): |
|
6 |
return setting_enabled('ENABLE', app_settings)(func) |
|
7 |
src/authentic2_idp_openid/locale/fr/LC_MESSAGES/django.po | ||
---|---|---|
1 |
# authentic2 idp openid 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: 2018-01-24 12:02+0100\n" |
|
11 |
"PO-Revision-Date: 2018-01-24 12:12+0100\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_idp_openid/__init__.py:34 |
|
21 |
msgid "OpenID" |
|
22 |
msgstr "Gérer la connexion OpenID" |
|
23 | ||
24 |
#: src/authentic2_idp_openid/models.py:35 |
|
25 |
msgid "trusted root" |
|
26 |
msgstr "Site de confiance" |
|
27 | ||
28 |
#: src/authentic2_idp_openid/models.py:36 |
|
29 |
msgid "trusted roots" |
|
30 |
msgstr "Sites de confiance" |
|
31 | ||
32 |
#: src/authentic2_idp_openid/models.py:57 |
|
33 |
msgid "association" |
|
34 |
msgstr "association" |
|
35 | ||
36 |
#: src/authentic2_idp_openid/models.py:58 |
|
37 |
msgid "associations" |
|
38 |
msgstr "associations" |
|
39 | ||
40 |
#: src/authentic2_idp_openid/models.py:125 |
|
41 |
msgid "nonce" |
|
42 |
msgstr "nonce" |
|
43 | ||
44 |
#: src/authentic2_idp_openid/models.py:126 |
|
45 |
msgid "nonces" |
|
46 |
msgstr "nonces" |
|
47 | ||
48 |
#: src/authentic2_idp_openid/templates/django_openid_provider/manage_id.html:5 |
|
49 |
#: src/authentic2_idp_openid/templates/django_openid_provider/manage_id_confirm.html:5 |
|
50 |
msgid "Manage OpenID" |
|
51 |
msgstr "Gérer la connexion OpenID" |
|
52 | ||
53 |
#: src/authentic2_idp_openid/templates/django_openid_provider/manage_id.html:23 |
|
54 |
msgid "Your current OpenID" |
|
55 |
msgstr "Vos identifiants OpenID actuels" |
|
56 | ||
57 |
#: src/authentic2_idp_openid/templates/django_openid_provider/manage_id.html:52 |
|
58 |
msgid "Add a new OpenID identity" |
|
59 |
msgstr "Ajouter une nouvelle identité OpenID" |
|
60 | ||
61 |
#: src/authentic2_idp_openid/templates/django_openid_provider/manage_trustroot.html:5 |
|
62 |
msgid "Manage trusted site" |
|
63 |
msgstr "Gérer vos sites de confiance" |
|
64 | ||
65 |
#: src/authentic2_idp_openid/templates/django_openid_provider/manage_trustroot.html:17 |
|
66 |
msgid "Your trusted site" |
|
67 |
msgstr "Vos sites de confiance" |
|
68 | ||
69 |
#: src/authentic2_idp_openid/templates/idp/openid/trustedroot_confirm_delete.html:5 |
|
70 |
msgid "Remove Link ?" |
|
71 |
msgstr "Supprimer le lien ?" |
|
72 | ||
73 |
#: src/authentic2_idp_openid/templates/idp/openid/trustedroot_confirm_delete.html:7 |
|
74 |
msgid "Are you sure you want to delete link with" |
|
75 |
msgstr "Êtes-vous sûr de vouloir supprimer le lien avec" |
|
76 | ||
77 |
#: src/authentic2_idp_openid/templates/idp/openid/trustedroot_confirm_delete.html:10 |
|
78 |
msgid "Back" |
|
79 |
msgstr "Retour" |
|
80 | ||
81 |
#: src/authentic2_idp_openid/views.py:278 |
|
82 |
msgid "Trust this site?" |
|
83 |
msgstr "Faire confiance à ce site ?" |
src/authentic2_idp_openid/middleware.py | ||
---|---|---|
1 |
from django.core.urlresolvers import reverse |
|
2 | ||
3 |
from . import views |
|
4 | ||
5 |
class OpenIDMiddleware(object): |
|
6 |
'''Add OpenID discovery header to all responses, |
|
7 |
if Accept header is 'application/xrds+xml' also return an XRDS document. |
|
8 |
''' |
|
9 |
def process_response(self, request, response): |
|
10 |
response['X-XRDS-Location'] = request.build_absolute_uri(reverse('a2-idp-openid-xrds')) |
|
11 |
if request.META.get('HTTP_ACCEPT') == 'application/xrds+xml': |
|
12 |
return views.openid_xrds(request) |
|
13 |
return response |
src/authentic2_idp_openid/migration_utils.py | ||
---|---|---|
1 |
"""utils to help rename apps and South migrations""" |
|
2 |
import os |
|
3 |
from south.models import MigrationHistory |
|
4 | ||
5 |
def was_applied(migration_file_path, app_name): |
|
6 |
"""true if migration with a given file name ``migration_file`` |
|
7 |
was applied to app with name ``app_name``""" |
|
8 |
try: |
|
9 |
migration_file = os.path.basename(migration_file_path) |
|
10 |
migration_name = migration_file.split('.')[0] |
|
11 |
MigrationHistory.objects.get( |
|
12 |
app_name = app_name, |
|
13 |
migration = migration_name |
|
14 |
) |
|
15 |
return True |
|
16 |
except MigrationHistory.DoesNotExist: |
|
17 |
return False |
src/authentic2_idp_openid/migrations/0001_initial.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
from __future__ import unicode_literals |
|
3 | ||
4 |
from django.db import models, migrations |
|
5 |
import authentic2.saml.fields |
|
6 | ||
7 | ||
8 |
class Migration(migrations.Migration): |
|
9 | ||
10 |
dependencies = [ |
|
11 |
] |
|
12 | ||
13 |
operations = [ |
|
14 |
migrations.CreateModel( |
|
15 |
name='Association', |
|
16 |
fields=[ |
|
17 |
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), |
|
18 |
('server_url', models.CharField(max_length=768)), |
|
19 |
('handle', models.CharField(max_length=255)), |
|
20 |
('secret', authentic2.saml.fields.PickledObjectField(editable=False)), |
|
21 |
('issued', models.DateTimeField(verbose_name=b'Issue time for this association, as seconds since EPOCH', editable=False)), |
|
22 |
('lifetime', models.IntegerField(verbose_name=b'Lifetime of this association as seconds since the issued time')), |
|
23 |
('expire', models.DateTimeField(verbose_name=b'After this time, the association will be expired')), |
|
24 |
('assoc_type', models.CharField(max_length=64)), |
|
25 |
], |
|
26 |
options={ |
|
27 |
'db_table': 'idp_openid_association', |
|
28 |
'verbose_name': 'association', |
|
29 |
'verbose_name_plural': 'associations', |
|
30 |
}, |
|
31 |
bases=(models.Model,), |
|
32 |
), |
|
33 |
migrations.CreateModel( |
|
34 |
name='Nonce', |
|
35 |
fields=[ |
|
36 |
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), |
|
37 |
('salt', models.CharField(max_length=40)), |
|
38 |
('server_url', models.CharField(max_length=768)), |
|
39 |
('timestamp', models.IntegerField()), |
|
40 |
], |
|
41 |
options={ |
|
42 |
'db_table': 'idp_openid_nonce', |
|
43 |
'verbose_name': 'nonce', |
|
44 |
'verbose_name_plural': 'nonces', |
|
45 |
}, |
|
46 |
bases=(models.Model,), |
|
47 |
), |
|
48 |
migrations.CreateModel( |
|
49 |
name='TrustedRoot', |
|
50 |
fields=[ |
|
51 |
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), |
|
52 |
('user', models.CharField(max_length=255)), |
|
53 |
('trust_root', models.CharField(max_length=200)), |
|
54 |
('choices', authentic2.saml.fields.PickledObjectField()), |
|
55 |
], |
|
56 |
options={ |
|
57 |
'db_table': 'idp_openid_trustedroot', |
|
58 |
'verbose_name': 'trusted root', |
|
59 |
'verbose_name_plural': 'trusted roots', |
|
60 |
}, |
|
61 |
bases=(models.Model,), |
|
62 |
), |
|
63 |
migrations.AlterUniqueTogether( |
|
64 |
name='nonce', |
|
65 |
unique_together=set([('server_url', 'salt')]), |
|
66 |
), |
|
67 |
migrations.AlterUniqueTogether( |
|
68 |
name='association', |
|
69 |
unique_together=set([('server_url', 'handle')]), |
|
70 |
), |
|
71 |
] |
src/authentic2_idp_openid/models.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
# vim: set ts=4 sw=4 : */ |
|
3 | ||
4 |
import datetime |
|
5 |
import time |
|
6 |
import calendar |
|
7 | ||
8 |
import openid.association |
|
9 |
import openid.store.nonce |
|
10 |
from django.db import models |
|
11 |
from django.utils.timezone import now, utc |
|
12 |
from django.conf import settings |
|
13 |
from django.utils.translation import ugettext_lazy as _ |
|
14 | ||
15 |
from authentic2.saml.fields import PickledObjectField |
|
16 | ||
17 | ||
18 |
def utctimestamp_to_aware_datetime(tst): |
|
19 |
if settings.USE_TZ: |
|
20 |
return datetime.datetime.utcfromtimestamp(tst) \ |
|
21 |
.replace(tz_info=utc) |
|
22 |
else: |
|
23 |
return datetime.datetime.utcfromtimestamp(tst) |
|
24 | ||
25 | ||
26 |
class TrustedRoot(models.Model): |
|
27 |
user = models.CharField(max_length=255) |
|
28 |
trust_root = models.CharField(max_length=200) |
|
29 |
choices = PickledObjectField() |
|
30 | ||
31 |
def __unicode__(self): |
|
32 |
return unicode(self.trust_root) |
|
33 | ||
34 |
class Meta: |
|
35 |
verbose_name = _('trusted root') |
|
36 |
verbose_name_plural = _('trusted roots') |
|
37 |
db_table = 'idp_openid_trustedroot' # app was named idp_openid before |
|
38 | ||
39 | ||
40 | ||
41 |
class Association(models.Model): |
|
42 |
server_url = models.CharField(max_length=768, blank=False) |
|
43 |
handle = models.CharField(max_length=255, blank=False) |
|
44 |
secret = PickledObjectField(editable=False) |
|
45 |
issued = models.DateTimeField(editable=False, |
|
46 |
verbose_name="Issue time for this association, as seconds \ |
|
47 |
since EPOCH") |
|
48 |
lifetime = models.IntegerField( |
|
49 |
verbose_name="Lifetime of this association as seconds since \ |
|
50 |
the issued time") |
|
51 |
expire = models.DateTimeField("After this time, the association will \ |
|
52 |
be expired") |
|
53 |
assoc_type = models.CharField(max_length=64, blank=False) |
|
54 | ||
55 |
class Meta: |
|
56 |
unique_together = ('server_url', 'handle') |
|
57 |
verbose_name = _('association') |
|
58 |
verbose_name_plural = _('associations') |
|
59 |
db_table = 'idp_openid_association' # app was named idp_openid before |
|
60 | ||
61 |
def save(self, *args, **kwargs): |
|
62 |
'''Overload default save() method to compute the expire field''' |
|
63 |
self.issued = now() |
|
64 |
self.expire = self.issued + datetime.timedelta(seconds=self.lifetime) |
|
65 |
super(Association, self).save(*args, **kwargs) |
|
66 | ||
67 |
def to_association(self): |
|
68 |
'''Convert a model instance to an Association object of the openid |
|
69 |
library. |
|
70 |
''' |
|
71 |
return openid.association.Association(handle=self.handle, |
|
72 |
secret=self.secret, |
|
73 |
issued=calendar.timegm(self.issued.utctimetuple()), |
|
74 |
lifetime=self.lifetime, |
|
75 |
assoc_type=self.assoc_type) |
|
76 | ||
77 |
@classmethod |
|
78 |
def get_association(cls, server_url, handle=None): |
|
79 |
try: |
|
80 |
filter = cls.objects.filter(server_url=server_url, |
|
81 |
expire__gt=now()) |
|
82 |
if handle is not None: |
|
83 |
filter = filter.filter(handle=handle) |
|
84 |
return filter.latest('issued').to_association() |
|
85 |
except cls.DoesNotExist: |
|
86 |
return None |
|
87 | ||
88 |
@classmethod |
|
89 |
def cleanup_associations(cls): |
|
90 |
filter = cls.objects.filter(expire__lt=now()) |
|
91 |
count = filter.count() |
|
92 |
filter.delete() |
|
93 |
return count |
|
94 | ||
95 |
@classmethod |
|
96 |
def remove_association(cls, server_url, handle=None): |
|
97 |
filter = cls.objects.filter(server_url=server_url) |
|
98 |
if handle is not None: |
|
99 |
filter = filter.filter(handle=handle) |
|
100 |
filter.delete() |
|
101 | ||
102 |
@classmethod |
|
103 |
def store_association(cls, server_url, association): |
|
104 |
Association(server_url=server_url, |
|
105 |
handle=association.handle, |
|
106 |
secret=association.secret, |
|
107 |
issued=utctimestamp_to_aware_datetime(association.issued), |
|
108 |
lifetime=association.lifetime, |
|
109 |
assoc_type=association.assoc_type).save() |
|
110 | ||
111 |
class NonceManager(models.Manager): |
|
112 |
def cleanup(self): |
|
113 |
expire = openid.store.nonce.SKEW |
|
114 |
timestamp = calendar.timegm(now().utctimetuple()) |
|
115 |
self.filter(timestamp__lt=timestamp-expire).delete() |
|
116 | ||
117 |
class Nonce(models.Model): |
|
118 |
salt = models.CharField(max_length=40) |
|
119 |
server_url = models.CharField(max_length=768) |
|
120 |
timestamp = models.IntegerField() |
|
121 | ||
122 |
objects = NonceManager() |
|
123 | ||
124 |
class Meta: |
|
125 |
verbose_name = _('nonce') |
|
126 |
verbose_name_plural = _('nonces') |
|
127 |
unique_together = ('server_url', 'salt') |
|
128 |
db_table = 'idp_openid_nonce' # app was named idp_openid before |
|
129 | ||
130 |
@classmethod |
|
131 |
def use_nonce(cls, server_url, timestamp, salt): |
|
132 |
now = time.time() |
|
133 |
if timestamp > now or timestamp + openid.store.nonce.SKEW < now: |
|
134 |
return False |
|
135 | ||
136 |
n, created = cls.objects.get_or_create(server_url=server_url, |
|
137 |
salt=salt) |
|
138 |
if created: |
|
139 |
n.timestamp = timestamp |
|
140 |
n.save() |
|
141 |
return created |
|
142 | ||
143 |
@classmethod |
|
144 |
def cleanup_nonces(cls): |
|
145 |
filter = cls.objects.filter( |
|
146 |
timestamp_lt=time.time()-openid.store.nonce.SKEW) |
|
147 |
count = filter.count() |
|
148 |
filter.delete() |
|
149 |
return count |
src/authentic2_idp_openid/openid_store.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
# vim: set ts=4 sw=4 : */ |
|
3 | ||
4 |
import time |
|
5 | ||
6 |
import openid.store.interface |
|
7 |
from django.conf import settings |
|
8 | ||
9 |
import models |
|
10 |
from authentic2 import nonce |
|
11 | ||
12 | ||
13 |
NONCE_TIMEOUT = getattr(settings, 'OPENID_NONCE_TIMEOUT', |
|
14 |
getattr(settings, 'NONCE_TIMEOUT', openid.store.nonce.SKEW)) |
|
15 | ||
16 |
class DjangoOpenIDStore(openid.store.interface.OpenIDStore): |
|
17 |
def cleanupAssociations(self): |
|
18 |
return models.Association.cleanup_associations() |
|
19 | ||
20 |
def cleanupNonces(self): |
|
21 |
nonce.cleanup_nonces() |
|
22 | ||
23 |
def storeAssociation(self, server_url, association): |
|
24 |
return models.Association.store_association(server_url, association) |
|
25 | ||
26 |
def getAssociation(self, server_url, handle=None): |
|
27 |
return models.Association.get_association(server_url, handle) |
|
28 | ||
29 |
def removeAssociation(self, server_url, handle): |
|
30 |
return models.Association.remove_association(server_url, handle) |
|
31 | ||
32 |
def useNonce(self, server_url, timestamp, salt): |
|
33 |
now = time.time() |
|
34 |
if not (timestamp < now < (timestamp + NONCE_TIMEOUT)): |
|
35 |
return False |
|
36 |
value = '%s_%s_%s' % (server_url, timestamp, salt) |
|
37 | ||
38 |
return nonce.accept_nonce(value, 'OpenID', NONCE_TIMEOUT) |
src/authentic2_idp_openid/templates/django_openid_provider/manage_id.html | ||
---|---|---|
1 |
{% extends "authentic2/base-page.html" %} |
|
2 |
{% load i18n %} |
|
3 | ||
4 |
{% block title %} |
|
5 |
{% trans "Manage OpenID" %} |
|
6 |
{% endblock %} |
|
7 | ||
8 |
{% load breadcrumbs %} |
|
9 |
{% block breadcrumbs %} |
|
10 |
{{ block.super }} |
|
11 |
{% breadcrumb_url 'Manage OpenID' 'manage_id' %} |
|
12 |
{% endblock %} |
|
13 | ||
14 |
{% block content %} |
|
15 |
{% if message %} |
|
16 |
<ul class="errorlist">{{ message }}</ul> |
|
17 |
{% endif %} |
|
18 |
<p> |
|
19 |
{% if nb_openids > 0 %} |
|
20 |
<a href="/openid/manage/">Manage your trusted site</a> |
|
21 |
</p> |
|
22 |
<fieldset> |
|
23 |
<legend>{% trans "Your current OpenID" %}</legend> |
|
24 |
<p> Choose your identities to remove, be careful this will remove all the trusted site for these identities. |
|
25 |
You can change your default OpenID by clicking on "Make Default" |
|
26 |
</p> |
|
27 |
<form action="." id="form1" method="post"> |
|
28 |
{% csrf_token %} |
|
29 |
{% for key,value in openids.items %} |
|
30 |
<p> <h4> {{ uri }}{{ oipath }}/{{ value.caption }}/ |
|
31 |
{% if value.Default %} |
|
32 |
(Default) |
|
33 |
{% endif %} |
|
34 |
</h4> |
|
35 |
{% for key2, value2 in value.trustroot.items %} |
|
36 |
<li class="indented"> {{ value2 }} </li> |
|
37 |
{% empty %} |
|
38 |
There is no trusted site for this identity. |
|
39 |
{% endfor %} |
|
40 |
{% if not value.Default %} |
|
41 |
<input type = "submit" name= {{ value.caption }} value = "Make default" /> |
|
42 |
{% endif %} |
|
43 |
<input type = "submit" name = {{ value.caption }} value = "Remove"/> |
|
44 |
</p> |
|
45 |
{% endfor %} |
|
46 |
</form> |
|
47 |
</fieldset> |
|
48 |
{% else %} |
|
49 |
<p> You have no OpenID account for the moment</p> |
|
50 |
{% endif %} |
|
51 |
<fieldset> |
|
52 |
<legend>{% trans "Add a new OpenID identity" %} </legend> |
|
53 |
<p> |
|
54 |
<form action = "/openid/addopenid/" id = "form2" method = "post"> |
|
55 |
{% csrf_token %} |
|
56 |
<p> Leave blank to create an anonymous OpenID</p> |
|
57 |
{{ uri }}{{ oipath }}/{{ form.openid }}/ |
|
58 |
{{ form.Default }} |
|
59 |
Check if you want this OpenID account become your default OpenID account. |
|
60 |
<input type = "submit" value = "Add" /> |
|
61 |
</p> |
|
62 |
</form> |
|
63 |
</fieldset> |
|
64 |
{% endblock %} |
src/authentic2_idp_openid/templates/django_openid_provider/manage_id_confirm.html | ||
---|---|---|
1 |
{% extends "authentic2/base-page.html" %} |
|
2 |
{% load i18n %} |
|
3 | ||
4 |
{% block title %} |
|
5 |
{% trans "Manage OpenID" %} |
|
6 |
{% endblock %} |
|
7 | ||
8 |
{% block content %} |
|
9 |
<form action = "/openid/manageid_confirm/" id = "form" method = "post"> |
|
10 |
{% csrf_token %} |
|
11 |
<p>Are you sure, you want to delete <strong> {{ id }} </strong> and these trusted site:</p> |
|
12 |
{% for i in trust %} |
|
13 |
{{ i }} |
|
14 |
{% endfor %} |
|
15 |
<input type = "hidden" name = "idremove" value = {{ id }}> |
|
16 |
<input type = "submit" value = "Yes" name = "Answer" /> |
|
17 |
<input type = "submit" value = "No" name = "Answer" /> |
|
18 |
</form> |
|
19 |
{% endblock %} |
src/authentic2_idp_openid/templates/django_openid_provider/manage_trustroot.html | ||
---|---|---|
1 |
{% extends "authentic2/base-page.html" %} |
|
2 |
{% load i18n %} |
|
3 | ||
4 |
{% block title %} |
|
5 |
{% trans "Manage trusted site" %} |
|
6 |
{% endblock %} |
|
7 | ||
8 |
{% load breadcrumbs %} |
|
9 |
{% block breadcrumbs %} |
|
10 |
{{ block.super }} |
|
11 |
{% breadcrumb_url 'Manage OpenID' 'manage_id' %} |
|
12 |
{% breadcrumb_url 'Manage trusted site' 'manage_trustroot' %} |
|
13 |
{% endblock %} |
|
14 | ||
15 |
{% block content %} |
|
16 |
<fieldset> |
|
17 |
<legend>{% trans "Your trusted site" %} </legend> |
|
18 |
<p>Check the trusted site that you want to remove and click on remove to remove these trusted site from these </p> |
|
19 |
<form action="." id="form" method="post"> |
|
20 |
{% csrf_token %} |
|
21 |
{% for key, value in openids.items %} |
|
22 |
<p> <h4> {{ uri }}{{ oipath }}/{{ value.caption }}/ </h4> |
|
23 |
<ul class="NoBullet"> |
|
24 |
{% for key2, value2 in value.trustroot.items %} |
|
25 |
<li class="indented"> <input type="checkbox" name = "trustremove" value = {{ key2 }} /> {{ value2 }} </li> |
|
26 |
{% empty %} |
|
27 |
You have no trusted site for this openid |
|
28 |
{% endfor %} |
|
29 |
</ul> |
|
30 |
</p> |
|
31 |
{% endfor %} |
|
32 |
{% if trust_sum != 0 %} |
|
33 |
<input type="submit" value="Remove" /> |
|
34 |
{% endif %} |
|
35 |
</form> |
|
36 |
</fieldset> |
|
37 |
{% endblock %} |
src/authentic2_idp_openid/templates/django_openid_provider/server.html | ||
---|---|---|
1 |
{% extends "authentic2/base-page.html" %} |
|
2 | ||
3 |
{% block extrahead %}{{ block.super }} |
|
4 |
<meta http-equiv="x-xrds-location" content="{{ host }}{% url 'openid-provider-xrds' %}"> |
|
5 |
{% endblock %} |
|
6 | ||
7 |
{% block title %} |
|
8 |
OpenID endpoint |
|
9 |
{% endblock %} |
|
10 | ||
11 |
{% load breadcrumbs %} |
|
12 |
{% block breadcrumbs %} |
|
13 |
{{ block.super }} |
|
14 |
{% breadcrumb_url 'OpenID' 'openid-provider-root' %} |
|
15 |
{% endblock %} |
|
16 | ||
17 |
{% block content %} |
|
18 | ||
19 |
{% if nb_openid > 0 %} |
|
20 |
<p>To manage your OpenID accounts go on <a href='/openid/manageid/'> manage your OpenID account</a></p> |
|
21 |
<h3>Your OpenID account are: </h3> |
|
22 |
{% for key, value in openids.items %} |
|
23 |
<p> <h4> {{ uri }}{{ oipath }}/{{ value.caption }}/ </h4> |
|
24 |
{% for key2, value2 in value.trustroot.items %} |
|
25 |
<li> {{ value2 }} </li> |
|
26 |
{% empty %} |
|
27 |
You have no trusted site for this openid. |
|
28 |
{% endfor %} |
|
29 |
</p> |
|
30 |
{% endfor %} |
|
31 |
{% else %} |
|
32 |
<p>You currently have no OpenID account. |
|
33 |
To create an OpenID account go on <a href='/openid/manageid/'> create an OpenID account</a></p> |
|
34 |
{% endif %} |
|
35 | ||
36 |
{% endblock %} |
src/authentic2_idp_openid/templates/idp/openid/base.html | ||
---|---|---|
1 |
{% extends "base.html" %} |
src/authentic2_idp_openid/templates/idp/openid/decide.html | ||
---|---|---|
1 |
{% extends "authentic2/base-page.html" %} |
|
2 | ||
3 |
{% block content %} |
|
4 |
{% ifequal trust_root_valid "Valid" %} |
|
5 |
<!-- Trust root has been validated by OpenID 2 mechanism. --> |
|
6 |
<p>The site <tt>{{ trust_root|escape }}</tt> has requested verification |
|
7 |
of your OpenID.</p> |
|
8 |
{% endifequal %} |
|
9 |
{% ifequal trust_root_valid "Invalid" %} |
|
10 |
<div class="error"> |
|
11 |
<p>This request claims to be from {{ trust_root|escape }} but I have |
|
12 |
determined that <em>it is a pack of lies</em>. Beware, if you release |
|
13 |
information to them, they are likely to do unconscionable things with it, |
|
14 |
being the lying liars that they are.</p> |
|
15 |
<p>Please tell the <em>real</em> {{ trust_root|escape }} that someone is |
|
16 |
trying to abuse your trust in their good name.</p> |
|
17 |
</div> |
|
18 |
{% endifequal %} |
|
19 |
{% ifequal trust_root_valid "Unreachable" %} |
|
20 |
<p>The site <tt>{{ trust_root|escape }}</tt> has requested verification |
|
21 |
of your OpenID. I have failed to reach it and thus cannot vouch for its |
|
22 |
authenticity. Perhaps it is on your local network.</p> |
|
23 |
{% endifequal %} |
|
24 |
{% ifequal trust_root_valid "DISCOVERY_FAILED" %} |
|
25 |
<p>The site <tt>{{ trust_root|escape }}</tt> has requested verification |
|
26 |
of your OpenID. However, <tt>{{ trust_root|escape }}</tt> does not |
|
27 |
implement OpenID 2.0's relying party verification mechanism. Please use |
|
28 |
extra caution in deciding whether to release information to this party, |
|
29 |
and ask <tt>{{ trust_root|escape }}</tt> to implement relying party |
|
30 |
verification for your future transactions.</p> |
|
31 |
{% endifequal %} |
|
32 | ||
33 |
<!-- trust_root_valid is {{ trust_root_valid }} --> |
|
34 | ||
35 |
<form method="post" action="{% url 'openid-provider-decide' %}"> |
|
36 |
{% csrf_token %} |
|
37 |
{% if required %} |
|
38 |
<p> |
|
39 |
It requires the following attributes: |
|
40 |
<ul> |
|
41 |
{% for att in required %} |
|
42 |
<li>{{ att }}</li> |
|
43 |
{% endfor %} |
|
44 |
</ul> |
|
45 |
{% endif %} |
|
46 |
{% if optional %} |
|
47 |
The following attributes are optional: |
|
48 |
<ul> |
|
49 |
{{ form.as_ul }} |
|
50 |
</ul> |
|
51 |
</p> |
|
52 |
{% endif %} |
|
53 |
<p>Allow Identity-Hub to verify your identity ?</p> |
|
54 |
<input type="submit" value="Yes (Allow)" name="allow" /> |
|
55 |
<input type="submit" value="No (Cancel)" name="cancel" /> |
|
56 |
</form> |
|
57 |
{% endblock %} |
src/authentic2_idp_openid/templates/idp/openid/discovery.html | ||
---|---|---|
1 |
<html> |
|
2 |
<head> |
|
3 |
<link rel="openid.server" href="{{ openid_server }}" /> |
|
4 |
<meta http-equiv="refresh" content="1;url=/"> |
|
5 |
</head> |
|
6 |
<body> |
|
7 |
</body> |
|
8 |
</html> |
src/authentic2_idp_openid/templates/idp/openid/error.html | ||
---|---|---|
1 |
{% extends "authentic2/base-page.html" %} |
|
2 | ||
3 |
{% block content %} |
|
4 |
<h2>{{ title }}</h2> |
|
5 |
{{ msg }} |
|
6 |
{% endblock %} |
src/authentic2_idp_openid/templates/idp/openid/trustedroot_confirm_delete.html | ||
---|---|---|
1 |
{% extends "authentic2/base-page.html" %} |
|
2 |
{% load i18n %} |
|
3 | ||
4 |
{% block content %} |
|
5 |
<h2>{% trans "Remove Link ?" %}</h2> |
|
6 |
<form action="" method="post">{% csrf_token %} |
|
7 |
<p>{% trans "Are you sure you want to delete link with" %} {{ trustedroot }} ?</p> |
|
8 |
<input type="submit" value="Yes" /> |
|
9 |
</form> |
|
10 |
<p><a href="/">{% trans "Back" %}</a></p> |
|
11 |
{% endblock %} |
src/authentic2_idp_openid/templates/idp/openid/xrds.xml | ||
---|---|---|
1 |
<?xml version="1.0" encoding="UTF-8"?> |
|
2 |
<xrds:XRDS xmlns:xrds="xri://$xrds" xmlns="xri://$xrd*($v*2.0)"> |
|
3 |
<XRD> |
|
4 |
<Service priority="0">{% for uri in types %} |
|
5 |
<Type>{{ uri|escape }}</Type> |
|
6 |
{% endfor %}{% for endpoint in endpoints %} |
|
7 |
<URI>{{ endpoint }}</URI> |
|
8 |
{% endfor %}{% for local_id in local_ids %} |
|
9 |
<LocalID>{{ local_id }}</LocalID> |
|
10 |
{% endfor %}</Service> |
|
11 |
</XRD> |
|
12 |
</xrds:XRDS> |
src/authentic2_idp_openid/urls.py | ||
---|---|---|
1 |
# vim: set ts=4 sw=4 : */ |
|
2 | ||
3 |
from django.conf.urls import patterns, url |
|
4 |
from . import views |
|
5 | ||
6 |
urlpatterns = patterns('authentic2.idp.idp_openid.views', |
|
7 |
url(r'^$', |
|
8 |
views.openid_server, |
|
9 |
name='a2-idp-openid-root'), |
|
10 |
url(r'^trustedroot/(?P<pk>\d+)/delete/$', |
|
11 |
views.openid_trustedroot_delete, |
|
12 |
name='trustedroot_delete'), |
|
13 |
url(r'^decide/$', |
|
14 |
views.openid_decide, |
|
15 |
name='a2-idp-openid-decide'), |
|
16 |
url(r'^xrds/$', |
|
17 |
views.openid_xrds, |
|
18 |
name='a2-idp-openid-xrds'), |
|
19 |
url(r'^(?P<id>.+)/xrds/$', |
|
20 |
views.openid_xrds, |
|
21 |
{'identity': True}, |
|
22 |
name='a2-idp-openid-identity-xrds'), |
|
23 |
url(r'^(?P<id>.+)/$', |
|
24 |
views.openid_discovery, |
|
25 |
name='a2-idp-openid-identity'), |
|
26 |
) |
src/authentic2_idp_openid/utils.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
# vim: set ts=4 sw=4 fdm=indent : */ |
|
3 |
# some code from http://www.djangosnippets.org/snippets/310/ by simon |
|
4 |
# and from examples/djopenid from python-openid-2.2.4 |
|
5 | ||
6 |
import openid.server |
|
7 |
from django.core.exceptions import ImproperlyConfigured |
|
8 |
from django.utils.importlib import import_module |
|
9 |
from django.http import HttpResponse, HttpResponseRedirect |
|
10 | ||
11 |
import conf |
|
12 | ||
13 |
def oresponse_to_response(server, oresponse): |
|
14 |
try: |
|
15 |
webresponse = server.encodeResponse(oresponse) |
|
16 |
except openid.server.EncodingError: |
|
17 |
return HttpResponseRedirect('/') |
|
18 |
response = HttpResponse(webresponse.body) |
|
19 |
response.status_code = webresponse.code |
|
20 |
for key, value in webresponse.headers.items(): |
|
21 |
response[key] = value |
|
22 |
return response |
|
23 | ||
24 |
def import_module_attr(path): |
|
25 |
package, module = path.rsplit('.', 1) |
|
26 |
return getattr(import_module(package), module) |
|
27 | ||
28 |
def get_store(request): |
|
29 |
try: |
|
30 |
store_class = import_module_attr(conf.STORE) |
|
31 |
except ImportError: |
|
32 |
raise ImproperlyConfigured("OpenID store %r could not be imported" % conf.STORE) |
|
33 |
# The FileOpenIDStore requires a path to save the user files. |
|
34 |
if conf.STORE == 'openid.store.filestore.FileOpenIDStore': |
|
35 |
return store_class(conf.FILESTORE_PATH) |
|
36 |
return store_class() |
src/authentic2_idp_openid/views.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
# some code from http://www.djangosnippets.org/snippets/310/ by simon |
|
3 |
# and from examples/djopenid from python-openid-2.2.4 |
|
4 | ||
5 |
import logging |
|
6 |
import hashlib |
|
7 |
import urlparse |
|
8 | ||
9 |
from django.core.urlresolvers import reverse |
|
10 |
from django.shortcuts import render_to_response |
|
11 |
from django.template import RequestContext |
|
12 |
from django.utils.translation import ugettext as _ |
|
13 |
try: |
|
14 |
from django.views.decorators.csrf import csrf_exempt |
|
15 |
except ImportError: |
|
16 |
from django.contrib.csrf.middleware import csrf_exempt |
|
17 |
import django.forms as forms |
|
18 |
from django.conf import settings |
|
19 |
from django.http import Http404 |
|
20 |
from django.views.generic import DeleteView |
|
21 | ||
22 |
from openid.consumer.discover import OPENID_IDP_2_0_TYPE, \ |
|
23 |
OPENID_2_0_TYPE, OPENID_1_0_TYPE, OPENID_1_1_TYPE |
|
24 |
from openid.fetchers import HTTPFetchingError |
|
25 |
from openid.server.server import Server, ProtocolError |
|
26 |
from openid.server.trustroot import verifyReturnTo |
|
27 |
from openid.yadis.discover import DiscoveryFailure |
|
28 |
from openid.yadis.constants import YADIS_CONTENT_TYPE |
|
29 |
from openid.message import IDENTIFIER_SELECT |
|
30 |
from openid.extensions.sreg import ns_uri as SREG_TYPE, SRegRequest, \ |
|
31 |
SRegResponse, data_fields |
|
32 | ||
33 |
from utils import get_store, oresponse_to_response |
|
34 |
from authentic2.utils import login_require, redirect |
|
35 |
from authentic2.constants import NONCE_FIELD_NAME |
|
36 |
from . import models |
|
37 |
from .decorators import openid_enabled |
|
38 | ||
39 | ||
40 |
logger = logging.getLogger('authentic.idp.idp_openid') |
|
41 | ||
42 | ||
43 |
def check_exploded(exploded, request): |
|
44 |
username = request.user.username |
|
45 |
return not exploded.path.startswith(request.path) \ |
|
46 |
or exploded.path[len(request.path):].strip('/') != username \ |
|
47 |
or exploded.params \ |
|
48 |
or exploded.query \ |
|
49 |
or exploded.fragment |
|
50 | ||
51 |
@csrf_exempt |
|
52 |
@openid_enabled |
|
53 |
def openid_server(request): |
|
54 |
""" |
|
55 |
This view is the actual OpenID server - running at the URL pointed to by |
|
56 |
the <link rel="openid.server"> tag. |
|
57 |
""" |
|
58 |
server = Server(get_store(request), |
|
59 |
op_endpoint=request.build_absolute_uri( |
|
60 |
reverse('openid-provider-root'))) |
|
61 | ||
62 |
# Cancellation |
|
63 |
if 'cancel' in request.GET: |
|
64 |
if 'OPENID_REQUEST' in request.session: |
|
65 |
return oresponse_to_response(server, |
|
66 |
request.session['OPENID_REQUEST'].answer(False)) |
|
67 |
else: |
|
68 |
return redirect('auth_homepage') |
|
69 | ||
70 |
# Clear AuthorizationInfo session var, if it is set |
|
71 |
if request.session.get('AuthorizationInfo', None): |
|
72 |
del request.session['AuthorizationInfo'] |
|
73 | ||
74 |
querydict = dict(request.GET.items()) |
|
75 |
try: |
|
76 |
orequest = server.decodeRequest(querydict) |
|
77 |
except ProtocolError, why: |
|
78 |
logger.error('Invalid OpenID message %s' % querydict) |
|
79 |
return oresponse_to_response(server, why) |
|
80 |
if not orequest: |
|
81 |
orequest = request.session.get('OPENID_REQUEST', None) |
|
82 |
if orequest: |
|
83 |
logger.info('Restarting saved request by %s' % orequest.trust_root) |
|
84 |
# remove session stored data: |
|
85 |
pass |
|
86 |
# del request.session['OPENID_REQUEST'] |
|
87 |
else: |
|
88 |
logger.info('No OpenID request redirecting to homepage') |
|
89 |
return redirect('auth_homepage') |
|
90 |
else: |
|
91 |
logger.info('Received OpenID request: %s' % querydict) |
|
92 |
sreg_request = SRegRequest.fromOpenIDRequest(orequest) |
|
93 |
logger.debug('SREG request: %s' % sreg_request.__dict__) |
|
94 | ||
95 |
if orequest.mode in ("checkid_immediate", "checkid_setup"): |
|
96 |
# User is not logged |
|
97 |
if not request.user.is_authenticated(): |
|
98 |
# Site does not want interaction |
|
99 |
if orequest.immediate: |
|
100 |
logger.debug('User not logged and checkid immediate request, \ |
|
101 |
returning OpenID failure') |
|
102 |
return oresponse_to_response(server, orequest.answer(False)) |
|
103 |
else: |
|
104 |
# Try to login |
|
105 |
request.session['OPENID_REQUEST'] = orequest |
|
106 |
logger.debug('User not logged and checkid request, \ |
|
107 |
redirecting to login page') |
|
108 |
return login_require(request, params={NONCE_FIELD_NAME: '1'}) |
|
109 |
else: |
|
110 |
identity = orequest.identity |
|
111 |
if identity != IDENTIFIER_SELECT: |
|
112 |
exploded = urlparse.urlparse(identity) |
|
113 |
# Allows only /openid/<user_id> |
|
114 |
if check_exploded(exploded, request): |
|
115 |
# We only support directed identity |
|
116 |
logger.debug('Invalid OpenID identity %s' % identity) |
|
117 |
return oresponse_to_response(server, orequest.answer(False)) |
|
118 |
if getattr(settings, 'RESTRICT_OPENID_RP', None): |
|
119 |
logger.debug('RP restriction is activated') |
|
120 |
if orequest.trust_root in getattr(settings, 'RESTRICT_OPENID_RP'): |
|
121 |
logger.debug('The RP %s is authorized' % orequest.trust_root) |
|
122 |
else: |
|
123 |
logger.debug('The RP %s is not authorized, return 404.' \ |
|
124 |
% orequest.trust_root) |
|
125 |
raise Http404 |
|
126 |
try: |
|
127 |
trusted_root = models.TrustedRoot.objects.get( |
|
128 |
user=request.user.id, trust_root=orequest.trust_root) |
|
129 |
# Check the choices are sufficient |
|
130 |
if not set(sreg_request.required)\ |
|
131 |
.issubset(set(trusted_root.choices)): |
|
132 |
# Current assertion is not sufficent, ask again ! |
|
133 |
if orequest.immediate: |
|
134 |
logger.debug('Attributes authorization unsufficient \ |
|
135 |
and checkid immediate, returning OpenID failure') |
|
136 |
return oresponse_to_response(server, |
|
137 |
orequest.answer(False)) |
|
138 |
request.session['OPENID_REQUEST'] = orequest |
|
139 |
logger.debug('Attributes authorization unsufficient \ |
|
140 |
for %s, redirecting to consent page' % orequest.trust_root) |
|
141 |
return redirect('openid-provider-decide') |
|
142 |
user_data = {} |
|
143 |
for field in trusted_root.choices: |
|
144 |
if field == 'email': |
|
145 |
user_data[field] = request.user.email |
|
146 |
elif field == 'fullname': |
|
147 |
user_data[field] = '%s %s' % (request.user.first_name, |
|
148 |
request.user.last_name) |
|
149 |
elif field == 'nickname': |
|
150 |
user_data[field] = getattr(request.user, 'username', |
|
151 |
'') |
|
152 |
else: |
|
153 |
logger.debug('Could not provide SReg field %s' % field) |
|
154 |
except models.TrustedRoot.MultipleObjectsReturned: |
|
155 |
# Too much trustedroots remove |
|
156 |
models.TrustedRoot.objects.filter(user=request.user.id, |
|
157 |
trust_root=orequest.trust_root).delete() |
|
158 |
# RP does not want any interaction |
|
159 |
if orequest.immediate: |
|
160 |
logger.warning('Too much trusted root records and \ |
|
161 |
checkid immediate, returning OpenID failure') |
|
162 |
return oresponse_to_response(server, |
|
163 |
orequest.answer(False)) |
|
164 |
request.session['OPENID_REQUEST'] = orequest |
|
165 |
logger.info('Too much trusted root for %s, redirecting to \ |
|
166 |
consent page' % orequest.trust_root) |
|
167 |
return redirect('openid-provider-decide') |
|
168 |
except models.TrustedRoot.DoesNotExist: |
|
169 |
# RP does not want any interaction |
|
170 |
if orequest.immediate: |
|
171 |
logger.info('Trusted root unknown and checkid \ |
|
172 |
immediate, returning OpenID failure') |
|
173 |
return oresponse_to_response(server, |
|
174 |
orequest.answer(False)) |
|
175 |
request.session['OPENID_REQUEST'] = orequest |
|
176 |
logger.info('Trusted root %s unknown, redirecting to \ |
|
177 |
consent page' % orequest.trust_root) |
|
178 |
return redirect('openid-provider-decide') |
|
179 | ||
180 |
# Create a directed identity if needed |
|
181 |
if identity == IDENTIFIER_SELECT: |
|
182 |
hash = hashlib.sha1(str(request.user.id)+'|'+orequest.trust_root) \ |
|
183 |
.hexdigest() |
|
184 |
claimed_id = request.build_absolute_uri( |
|
185 |
reverse('openid-provider-identity', args=[hash])) |
|
186 |
logger.info('Giving directed identity %r to trusted root %r \ |
|
187 |
with sreg data %s' % (claimed_id, orequest.trust_root, user_data)) |
|
188 |
else: |
|
189 |
claimed_id = identity |
|
190 |
logger.info('Giving claimed identity %r to trusted root %r \ |
|
191 |
with sreg data %s' % (claimed_id, orequest.trust_root, user_data)) |
|
192 | ||
193 |
oresponse = orequest.answer(True, identity=claimed_id) |
|
194 |
sreg_response = SRegResponse.extractResponse(sreg_request, user_data) |
|
195 |
oresponse.addExtension(sreg_response) |
|
196 |
else: |
|
197 |
oresponse = server.handleRequest(orequest) |
|
198 |
logger.info('Returning OpenID response %s' % oresponse) |
|
199 |
return oresponse_to_response(server, oresponse) |
|
200 | ||
201 |
@openid_enabled |
|
202 |
def openid_xrds(request, identity=False, id=None): |
|
203 |
'''XRDS discovery page''' |
|
204 |
logger.debug('OpenID XRDS identity:%(identity)s id:%(id)s' % locals()) |
|
205 |
if identity: |
|
206 |
types = [OPENID_2_0_TYPE, OPENID_1_0_TYPE, OPENID_1_1_TYPE, SREG_TYPE] |
|
207 |
local_ids = [] |
|
208 |
else: |
|
209 |
types = [OPENID_IDP_2_0_TYPE,SREG_TYPE] |
|
210 |
local_ids = [] |
|
211 |
endpoints = [request.build_absolute_uri(reverse('openid-provider-root'))] |
|
212 |
return render_to_response('idp/openid/xrds.xml', { |
|
213 |
'host': request.build_absolute_uri('/'), |
|
214 |
'types': types, |
|
215 |
'endpoints': endpoints, |
|
216 |
'local_ids': local_ids, |
|
217 |
}, context_instance=RequestContext(request), mimetype=YADIS_CONTENT_TYPE) |
|
218 | ||
219 |
class DecideForm(forms.Form): |
|
220 |
def __init__(self, sreg_request=None, *args, **kwargs): |
|
221 |
super(DecideForm, self).__init__(*args, **kwargs) |
|
222 |
for field in sreg_request.optional: |
|
223 |
self.fields[str(field)] = forms.BooleanField( |
|
224 |
label=data_fields[str(field)], required=False) |
|
225 |
logger.info('3SREG request: %s' % self.fields) |
|
226 | ||
227 |
@openid_enabled |
|
228 |
def openid_decide(request): |
|
229 |
""" |
|
230 |
The page that asks the user if they really want to sign in to the site, and |
|
231 |
lets them add the consumer to their trusted whitelist. |
|
232 |
# If user is logged in, ask if they want to trust this trust_root |
|
233 |
# If they are NOT logged in, show the landing page |
|
234 |
""" |
|
235 |
orequest = request.session.get('OPENID_REQUEST') |
|
236 |
# No request ? Failure.. |
|
237 |
if not orequest: |
|
238 |
logger.warning('OpenID decide view failed, \ |
|
239 |
because no OpenID request is saved') |
|
240 |
return redirect('auth_homepage') |
|
241 |
sreg_request = SRegRequest.fromOpenIDRequest(orequest) |
|
242 |
logger.debug('SREG request: %s' % sreg_request.__dict__) |
|
243 |
if not request.user.is_authenticated(): |
|
244 |
# Not authenticated ? Authenticate and go back to the server endpoint |
|
245 |
return login_require(request, params={NONCE_FIELD_NAME: '1'}) |
|
246 | ||
247 |
if request.method == 'POST': |
|
248 |
if 'cancel' in request.POST: |
|
249 |
# User refused |
|
250 |
logger.info('OpenID decide canceled') |
|
251 |
return redirect(openid_server, params={'cancel': ''}) |
|
252 |
else: |
|
253 |
form = DecideForm(sreg_request=sreg_request, data=request.POST) |
|
254 |
if form.is_valid(): |
|
255 |
data = form.cleaned_data |
|
256 |
# Remember the choice |
|
257 |
t, created = models.TrustedRoot.objects.get_or_create( |
|
258 |
user=request.user.id, trust_root=orequest.trust_root) |
|
259 |
t.choices = sreg_request.required \ |
|
260 |
+ [ field for field in data if data[field] ] |
|
261 |
t.save() |
|
262 |
logger.debug('OpenID decide, user choice:%s' % data) |
|
263 |
return redirect('openid-provider-root') |
|
264 |
else: |
|
265 |
form = DecideForm(sreg_request=sreg_request) |
|
266 |
logger.info('OpenID device view, orequest:%s' % orequest) |
|
267 | ||
268 |
# verify return_to of trust_root |
|
269 |
try: |
|
270 |
trust_root_valid = verifyReturnTo(orequest.trust_root, |
|
271 |
orequest.return_to) and "Valid" or "Invalid" |
|
272 |
except HTTPFetchingError: |
|
273 |
trust_root_valid = "Unreachable" |
|
274 |
except DiscoveryFailure: |
|
275 |
trust_root_valid = "DISCOVERY_FAILED" |
|
276 | ||
277 |
return render_to_response('idp/openid/decide.html', { |
|
278 |
'title': _('Trust this site?'), |
|
279 |
'required': sreg_request.required, |
|
280 |
'optional': sreg_request.optional, |
|
281 |
'trust_root_valid': trust_root_valid, |
|
282 |
'form': form, |
|
283 |
}, context_instance=RequestContext(request)) |
|
284 | ||
285 |
@openid_enabled |
|
286 |
def openid_discovery(request, id): |
|
287 |
'''HTML discovery page''' |
|
288 |
xrds_url = request.build_absolute_uri( |
|
289 |
reverse('openid-provider-identity-xrds', args=[id])) |
|
290 |
response = render_to_response('idp/openid/discovery.html', { |
|
291 |
'xrds': xrds_url, |
|
292 |
'openid_server': request.build_absolute_uri( |
|
293 |
reverse('openid-provider-root')) |
|
294 |
}, context_instance=RequestContext(request)) |
|
295 |
response['X-XRDS-Location'] = xrds_url |
|
296 |
return response |
|
297 | ||
298 |
class TrustedRootDelete(DeleteView): |
|
299 |
model = models.TrustedRoot |
|
300 |
success_url = '/' |
|
301 |
template_name = 'idp/openid/trustedroot_confirm_delete.html' |
|
302 | ||
303 |
openid_trustedroot_delete = openid_enabled(TrustedRootDelete.as_view()) |
|
304 |
- |