1 |
1 |
'''Views for Authentic2 API'''
|
2 |
2 |
import logging
|
3 |
3 |
import smtplib
|
|
4 |
import datetime
|
|
5 |
from hashlib import sha1
|
4 |
6 |
|
|
7 |
from django import http
|
5 |
8 |
from django.db import models
|
6 |
|
from django.contrib.auth import get_user_model
|
|
9 |
from django.contrib.auth import REDIRECT_FIELD_NAME, get_user_model
|
|
10 |
from django.core import signing
|
|
11 |
from django.core.cache import cache
|
7 |
12 |
from django.core.exceptions import MultipleObjectsReturned
|
8 |
13 |
from django.utils.translation import ugettext as _
|
|
14 |
from django.utils.timezone import now
|
|
15 |
from django.utils.dateparse import parse_datetime
|
9 |
16 |
from django.views.decorators.vary import vary_on_headers
|
10 |
17 |
from django.views.decorators.cache import cache_control
|
11 |
18 |
from django.shortcuts import get_object_or_404
|
|
19 |
from django.views.generic.base import RedirectView
|
|
20 |
from django.core.urlresolvers import reverse
|
12 |
21 |
|
13 |
22 |
from django_rbac.utils import get_ou_model, get_role_model
|
14 |
23 |
|
... | ... | |
29 |
38 |
from .models import Attribute, PasswordReset
|
30 |
39 |
from .a2_rbac.utils import get_default_ou
|
31 |
40 |
|
|
41 |
from utils import login
|
|
42 |
from . import app_settings
|
|
43 |
|
32 |
44 |
|
33 |
45 |
class HasUserAddPermission(permissions.BasePermission):
|
34 |
46 |
def has_permission(self, request, view):
|
... | ... | |
36 |
48 |
return False
|
37 |
49 |
return True
|
38 |
50 |
|
|
51 |
class IsSuperUser(permissions.BasePermission):
|
|
52 |
"""
|
|
53 |
Allows access only to super users.
|
|
54 |
"""
|
|
55 |
|
|
56 |
def has_permission(self, request, view):
|
|
57 |
return request.user and request.user.is_superuser
|
39 |
58 |
|
40 |
59 |
class RegistrationSerializer(serializers.Serializer):
|
41 |
60 |
'''Register RPC payload'''
|
... | ... | |
244 |
263 |
|
245 |
264 |
password_change = PasswordChange.as_view()
|
246 |
265 |
|
|
266 |
class UserAutoLoginSerializer(serializers.Serializer):
|
|
267 |
'''UserAutoLogin RPC payload'''
|
|
268 |
uuid = serializers.CharField(
|
|
269 |
required=False, allow_null=True)
|
|
270 |
email = serializers.EmailField(
|
|
271 |
required=False, allow_null=True)
|
|
272 |
ou = serializers.SlugRelatedField(
|
|
273 |
queryset=get_ou_model().objects.all(),
|
|
274 |
slug_field='slug',
|
|
275 |
required=False, allow_null=True)
|
|
276 |
expire_duration = serializers.IntegerField(
|
|
277 |
min_value=1, max_value=app_settings.A2_AUTH_TOKEN_MAX_LIFEMENT,
|
|
278 |
required=False, allow_null=False)
|
|
279 |
next_url = serializers.CharField(
|
|
280 |
required=False, allow_null=True)
|
|
281 |
|
|
282 |
def validate(self, data):
|
|
283 |
User = get_user_model()
|
|
284 |
if data.get('uuid'):
|
|
285 |
qs = User.objects.filter(uuid=data['uuid'])
|
|
286 |
elif data.get('email'):
|
|
287 |
qs = User.objects.filter(email=data['email'])
|
|
288 |
else:
|
|
289 |
raise serializers.ValidationError("you must provide user's uuid or email")
|
|
290 |
if data.get('ou'):
|
|
291 |
qs = qs.filter(ou=data.get('ou'))
|
|
292 |
try:
|
|
293 |
self.user = qs.get()
|
|
294 |
data['uuid'] = self.user.uuid
|
|
295 |
except User.DoesNotExist:
|
|
296 |
raise serializers.ValidationError('no user found')
|
|
297 |
except MultipleObjectsReturned:
|
|
298 |
raise serializers.ValidationError('more than one user found')
|
|
299 |
return data
|
|
300 |
|
|
301 |
|
|
302 |
class UserAutoLogin(BaseRpcView):
|
|
303 |
permission_classes = (permissions.IsAuthenticated,
|
|
304 |
IsSuperUser)
|
|
305 |
|
|
306 |
serializer_class = UserAutoLoginSerializer
|
|
307 |
|
|
308 |
def rpc(self, request, serializer):
|
|
309 |
data = {}
|
|
310 |
data['uuid'] = serializer.validated_data.get('uuid')
|
|
311 |
expire_duration = serializer.validated_data.get('expire_duration', app_settings.A2_AUTH_TOKEN_DEFAULT_LIFETIME)
|
|
312 |
data['expire'] = (now()+datetime.timedelta(seconds=expire_duration)).isoformat()
|
|
313 |
if serializer.validated_data.get('ou'):
|
|
314 |
data['ou'] = serializer.validated_data.get('ou')
|
|
315 |
data[REDIRECT_FIELD_NAME] = serializer.validated_data.get('next_url')
|
|
316 |
token = signing.dumps(data)
|
|
317 |
autologinurl = request.build_absolute_uri(
|
|
318 |
reverse('autologin', kwargs={'token': token}))
|
|
319 |
return {'autologinurl': autologinurl}, status.HTTP_200_OK
|
|
320 |
|
|
321 |
userautologin = UserAutoLogin.as_view()
|
|
322 |
|
|
323 |
class UserAutoLoginView(RedirectView):
|
|
324 |
|
|
325 |
def __init__(self, *args, **kwargs):
|
|
326 |
super(UserAutoLoginView, self).__init__(*args, **kwargs)
|
|
327 |
self.logger = logging.getLogger(__name__)
|
|
328 |
|
|
329 |
def valid_token(self, token):
|
|
330 |
try:
|
|
331 |
result = signing.loads(token.replace(' ', ''))
|
|
332 |
expire = result.get('expire')
|
|
333 |
if expire:
|
|
334 |
if now()>=parse_datetime(expire):
|
|
335 |
self.logger.error(u'Specified token is expired')
|
|
336 |
else:
|
|
337 |
cache_first_use_key = 'token-%s' % sha1(token)
|
|
338 |
if cache.get(cache_first_use_key):
|
|
339 |
if now()>=(parse_datetime(cache.get(cache_first_use_key)+datetime.timedelta(0,app_settings.A2_AUTH_TOKEN_LIFETIME_AFTER_FIRST_USE))):
|
|
340 |
self.logger.error(u'Specified token was already used and expired')
|
|
341 |
return None
|
|
342 |
else:
|
|
343 |
cache.set(cache_first_use_key, now().isoformat(), app_settings.A2_AUTH_TOKEN_MAX_LIFEMENT)
|
|
344 |
return result
|
|
345 |
else:
|
|
346 |
self.logger.error(u'No expire in token')
|
|
347 |
except signing.BadSignature:
|
|
348 |
self.logger.error(u'Specified token is invalid')
|
|
349 |
return None
|
|
350 |
|
|
351 |
def get_next_url(self, *args, **kwargs):
|
|
352 |
if kwargs.get('token') and kwargs.get('token').get(REDIRECT_FIELD_NAME):
|
|
353 |
return kwargs.get('token').get(REDIRECT_FIELD_NAME)
|
|
354 |
return reverse('auth_homepage')
|
|
355 |
|
|
356 |
def get(self, request, *args, **kwargs):
|
|
357 |
token = self.valid_token(kwargs.get('token'))
|
|
358 |
|
|
359 |
url = None
|
|
360 |
if token:
|
|
361 |
self.logger.info(u"Valid token : %s" % token)
|
|
362 |
User = get_user_model()
|
|
363 |
qs = User.objects.filter(uuid=token['uuid'])
|
|
364 |
if token.get('ou'):
|
|
365 |
qs = qs.filter(ou=token['ou'])
|
|
366 |
try:
|
|
367 |
user = qs.get()
|
|
368 |
self.logger.info(u"User : %s" % user)
|
|
369 |
user.backend = 'authentic2.backends.models_backend.ModelBackend'
|
|
370 |
login(request, user, 'token')
|
|
371 |
url = self.get_next_url(token = token)
|
|
372 |
self.logger.info(u"Redirect to : %s" % url)
|
|
373 |
except User.DoesNotExist:
|
|
374 |
self.logger.error(u'User %s from token does not exists' % token['uuid'])
|
|
375 |
except MultipleObjectsReturned:
|
|
376 |
self.logger.error(u"More than one user return from token's uuid (%s)" % token['uuid'])
|
|
377 |
|
|
378 |
if not url:
|
|
379 |
url = reverse('auth_login')
|
|
380 |
|
|
381 |
return http.HttpResponseRedirect(url)
|
|
382 |
|
|
383 |
userautologinview = UserAutoLoginView.as_view()
|
247 |
384 |
|
248 |
385 |
@vary_on_headers('Cookie', 'Origin', 'Referer')
|
249 |
386 |
@cache_control(private=True, max_age=60)
|