This repository has been archived by the owner on May 29, 2020. It is now read-only.
forked from fangli/django-saml2-auth
-
Notifications
You must be signed in to change notification settings - Fork 0
/
views.py
222 lines (178 loc) · 7.71 KB
/
views.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from saml2 import (
BINDING_HTTP_POST,
BINDING_HTTP_REDIRECT,
entity,
)
from saml2.client import Saml2Client
from saml2.config import Config as Saml2Config
from django import get_version
from pkg_resources import parse_version
from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group
from django.contrib.auth.decorators import login_required
from django.contrib.auth import login, logout
from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt
from django.template import TemplateDoesNotExist
from django.http import HttpResponseRedirect
from django.utils.http import is_safe_url
try:
import urllib2 as _urllib
except:
import urllib.request as _urllib
import urllib.error
import urllib.parse
if parse_version(get_version()) >= parse_version('1.7'):
from django.utils.module_loading import import_string
else:
from django.utils.module_loading import import_by_path as import_string
User = get_user_model()
def get_current_domain(r):
if 'ASSERTION_URL' in settings.SAML2_AUTH:
return settings.SAML2_AUTH['ASSERTION_URL']
return '{scheme}://{host}'.format(
scheme='https' if r.is_secure() else 'http',
host=r.get_host(),
)
def get_reverse(objs):
'''In order to support different django version, I have to do this '''
if parse_version(get_version()) >= parse_version('2.0'):
from django.urls import reverse
else:
from django.core.urlresolvers import reverse
if objs.__class__.__name__ not in ['list', 'tuple']:
objs = [objs]
for obj in objs:
try:
return reverse(obj)
except:
pass
raise Exception('We got a URL reverse issue: %s. This is a known issue but please still submit a ticket at https://github.com/fangli/django-saml2-auth/issues/new' % str(objs))
def _get_saml_client(domain):
acs_url = domain + get_reverse([acs, 'acs', 'django_saml2_auth:acs'])
saml_settings = {
'metadata': {
'remote': [
{
"url": settings.SAML2_AUTH['METADATA_AUTO_CONF_URL'],
},
],
},
'service': {
'sp': {
'endpoints': {
'assertion_consumer_service': [
(acs_url, BINDING_HTTP_REDIRECT),
(acs_url, BINDING_HTTP_POST)
],
},
'allow_unsolicited': True,
'authn_requests_signed': False,
'logout_requests_signed': True,
'want_assertions_signed': settings.SAML2_AUTH.get('WANT_ASSERTIONS_SIGNED', True),
'want_response_signed': False,
},
},
}
spConfig = Saml2Config()
spConfig.load(saml_settings)
spConfig.allow_unknown_attributes = True
saml_client = Saml2Client(config=spConfig)
return saml_client
@login_required
def welcome(r):
try:
return render(r, 'django_saml2_auth/welcome.html', {'user': r.user})
except TemplateDoesNotExist:
return HttpResponseRedirect(settings.SAML2_AUTH.get('DEFAULT_NEXT_URL', get_reverse('admin:index')))
def denied(r):
return render(r, 'django_saml2_auth/denied.html')
def _create_new_user(username, email, firstname, lastname):
user = User.objects.create_user(username, email)
user.first_name = firstname
user.last_name = lastname
user.groups = [Group.objects.get(name=x) for x in settings.SAML2_AUTH.get('NEW_USER_PROFILE', {}).get('USER_GROUPS', [])]
user.is_active = settings.SAML2_AUTH.get('NEW_USER_PROFILE', {}).get('ACTIVE_STATUS', True)
user.is_staff = settings.SAML2_AUTH.get('NEW_USER_PROFILE', {}).get('STAFF_STATUS', True)
user.is_superuser = settings.SAML2_AUTH.get('NEW_USER_PROFILE', {}).get('SUPERUSER_STATUS', False)
user.save()
return user
@csrf_exempt
def acs(r):
saml_client = _get_saml_client(get_current_domain(r))
resp = r.POST.get('SAMLResponse', None)
next_url = r.session.get('login_next_url', settings.SAML2_AUTH.get('DEFAULT_NEXT_URL', get_reverse('admin:index')))
if not resp:
return HttpResponseRedirect(get_reverse([denied, 'denied', 'django_saml2_auth:denied']))
authn_response = saml_client.parse_authn_request_response(
resp, entity.BINDING_HTTP_POST)
if authn_response is None:
return HttpResponseRedirect(get_reverse([denied, 'denied', 'django_saml2_auth:denied']))
user_identity = authn_response.get_identity()
if user_identity is None:
return HttpResponseRedirect(get_reverse([denied, 'denied', 'django_saml2_auth:denied']))
user_email = user_identity[settings.SAML2_AUTH.get('ATTRIBUTES_MAP', {}).get('email', 'Email')][0]
user_name = user_identity[settings.SAML2_AUTH.get('ATTRIBUTES_MAP', {}).get('username', 'UserName')][0]
user_first_name = user_identity[settings.SAML2_AUTH.get('ATTRIBUTES_MAP', {}).get('first_name', 'FirstName')][0]
user_last_name = user_identity[settings.SAML2_AUTH.get('ATTRIBUTES_MAP', {}).get('last_name', 'LastName')][0]
target_user = None
is_new_user = False
try:
target_user = User.objects.get(**{
getattr(User, 'username', getattr(User, 'USERNAME_FIELD', None)): user_name
})
target_user = User.objects.get(username=user_name)
if settings.SAML2_AUTH.get('TRIGGER', {}).get('BEFORE_LOGIN', None):
import_string(settings.SAML2_AUTH['TRIGGER']['BEFORE_LOGIN'])(user_identity)
except User.DoesNotExist:
if settings.SAML2_AUTH.get('CALLABLE', {}).get('CREATE_USER', None):
target_user = import_string(settings.SAML2_AUTH['CALLABLE']['CREATE_USER'])(user_identity)
else:
target_user = _create_new_user(user_name, user_email, user_first_name, user_last_name)
if settings.SAML2_AUTH.get('TRIGGER', {}).get('CREATE_USER', None):
import_string(settings.SAML2_AUTH['TRIGGER']['CREATE_USER'])(user_identity)
is_new_user = True
r.session.flush()
if target_user.is_active:
target_user.backend = 'django.contrib.auth.backends.ModelBackend'
login(r, target_user)
else:
return HttpResponseRedirect(get_reverse([denied, 'denied', 'django_saml2_auth:denied']))
if is_new_user:
try:
return render(r, 'django_saml2_auth/welcome.html', {'user': r.user})
except TemplateDoesNotExist:
return HttpResponseRedirect(next_url)
else:
return HttpResponseRedirect(next_url)
def signin(r):
try:
import urlparse as _urlparse
from urllib import unquote
except:
import urllib.parse as _urlparse
from urllib.parse import unquote
next_url = r.GET.get('next', settings.SAML2_AUTH.get('DEFAULT_NEXT_URL', get_reverse('admin:index')))
try:
if 'next=' in unquote(next_url):
next_url = _urlparse.parse_qs(_urlparse.urlparse(unquote(next_url)).query)['next'][0]
except:
next_url = r.GET.get('next', settings.SAML2_AUTH.get('DEFAULT_NEXT_URL', get_reverse('admin:index')))
# Only permit signin requests where the next_url is a safe URL
if not is_safe_url(next_url):
return HttpResponseRedirect(get_reverse([denied, 'denied', 'django_saml2_auth:denied']))
r.session['login_next_url'] = next_url
saml_client = _get_saml_client(get_current_domain(r))
_, info = saml_client.prepare_for_authenticate()
redirect_url = None
for key, value in info['headers']:
if key == 'Location':
redirect_url = value
break
return HttpResponseRedirect(redirect_url)
def signout(r):
logout(r)
return render(r, 'django_saml2_auth/signout.html')