Project

General

Profile

0001-Add-API-method-to-generate-auto-login-link.patch

Benjamin Renard, 24 Feb 2017 02:41 PM

Download (6.91 KB)

View differences:

Subject: [PATCH] Add API method to generate auto-login link

 src/authentic2/api_urls.py  |   4 ++
 src/authentic2/api_views.py | 114 +++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 117 insertions(+), 1 deletion(-)
src/authentic2/api_urls.py
11 11
                           name='a2-api-user'),
12 12
                       url(r'^roles/(?P<role_uuid>[\w+]*)/members/(?P<member_uuid>[\w+]*)/$', api_views.roles, 
13 13
                           name='a2-api-role-member'),
14
                       url(r'^user/autologin$', api_views.userautologin,
15
                           name='a2-api-user-autologin'),
16
                       url(r'^user/autologin/(?P<token>[\w: -]+)$', api_views.userautologinview,
17
                           name='autologin'),
14 18
)
15 19
urlpatterns += api_views.router.urls
src/authentic2/api_views.py
1 1
'''Views for Authentic2 API'''
2 2
import logging
3 3
import smtplib
4
import datetime
4 5

  
6
from django import http
5 7
from django.db import models
6
from django.contrib.auth import get_user_model
8
from django.contrib.auth import REDIRECT_FIELD_NAME, get_user_model
9
from django.core import signing
7 10
from django.core.exceptions import MultipleObjectsReturned
8 11
from django.utils.translation import ugettext as _
12
from django.utils.timezone import now
13
from django.utils.dateparse import parse_datetime
9 14
from django.views.decorators.vary import vary_on_headers
10 15
from django.views.decorators.cache import cache_control
11 16
from django.shortcuts import get_object_or_404
17
from django.views.generic.base import RedirectView
18
from django.core.urlresolvers import reverse
12 19

  
13 20
from django_rbac.utils import get_ou_model, get_role_model
14 21

  
......
27 34
from .models import Attribute, PasswordReset
28 35
from .a2_rbac.utils import get_default_ou
29 36

  
37
from utils import login
38

  
30 39

  
31 40
class HasUserAddPermission(permissions.BasePermission):
32 41
    def has_permission(self, request, view):
......
34 43
            return False
35 44
        return True
36 45

  
46
class IsSuperUser(permissions.BasePermission):
47
    """
48
    Allows access only to super users.
49
    """
50

  
51
    def has_permission(self, request, view):
52
        return request.user and request.user.is_superuser
37 53

  
38 54
class RegistrationSerializer(serializers.Serializer):
39 55
    '''Register RPC payload'''
......
242 258

  
243 259
password_change = PasswordChange.as_view()
244 260

  
261
class UserAutoLoginSerializer(serializers.Serializer):
262
    '''UserAutoLogin RPC payload'''
263
    email = serializers.EmailField()
264
    ou = serializers.SlugRelatedField(
265
        queryset=get_ou_model().objects.all(),
266
        slug_field='slug',
267
        required=False, allow_null=True)
268
    expire_duration = serializers.IntegerField(
269
        min_value=1, required=False, allow_null=False)
270
    redirect_url = serializers.CharField(
271
        required=False, allow_null=True)
272

  
273
    def validate(self, data):
274
        User = get_user_model()
275
        qs = User.objects.filter(email=data['email'])
276
        if data.get('ou'):
277
            qs = qs.filter(ou=data.get('ou'))
278
        try:
279
            self.user = qs.get()
280
        except User.DoesNotExist:
281
            raise serializers.ValidationError('no user found')
282
        except MultipleObjectsReturned:
283
            raise serializers.ValidationError('more than one user have this email')
284
        return data
285

  
286

  
287
class UserAutoLogin(BaseRpcView):
288
    permission_classes = (permissions.IsAuthenticated,
289
                          IsSuperUser)
290

  
291
    serializer_class = UserAutoLoginSerializer
292

  
293
    def rpc(self, request, serializer):
294
        data = {}
295
        data['email'] = serializer.validated_data.get('email')
296
        data['expire'] = (now()+datetime.timedelta(seconds=serializer.validated_data.get('expire_duration',300))).isoformat()
297
        if serializer.validated_data.get('ou'):
298
            data['ou'] = serializer.validated_data.get('ou')
299
        data[REDIRECT_FIELD_NAME] = serializer.validated_data.get('redirect_url')
300
        token = signing.dumps(data)
301
        autologinurl = request.build_absolute_uri(
302
            reverse('autologin', kwargs={'token': token}))
303
        return {'autologinurl': autologinurl}, status.HTTP_200_OK
304

  
305
userautologin = UserAutoLogin.as_view()
306

  
307
class UserAutoLoginView(RedirectView):
308

  
309
    def valid_token(self, token):
310
        try:
311
            result = signing.loads(token.replace(' ', ''))
312
            expire = result.get('expire')
313
            if expire:
314
                if now()>=parse_datetime(expire):
315
                    logging.getLogger(__name__).error(u'Specified token is expired')
316
                else:
317
                    return result
318
            else:
319
                logging.getLogger(__name__).error(u'No expire in token')
320
        except signing.BadSignature:
321
            logging.getLogger(__name__).error(u'Specified token is invalid')
322
        return None
323

  
324
    def get_redirect_url(self, *args, **kwargs):
325
            if kwargs.get('token') and kwargs.get('token').get(REDIRECT_FIELD_NAME):
326
                return kwargs.get('token').get(REDIRECT_FIELD_NAME)
327
            return reverse('auth_homepage')
328

  
329
    def get(self, request, *args, **kwargs):
330
        token = self.valid_token(kwargs.get('token'))
331

  
332
        url = None
333
        if token:
334
            logging.getLogger(__name__).info(u"Valid token : %s" % token)
335
            User = get_user_model()
336
            qs = User.objects.filter(email=token['email'])
337
            if token.get('ou'):
338
                qs = qs.filter(ou=token['ou'])
339
            try:
340
                user = qs.get()
341
                logging.getLogger(__name__).info(u"User : %s" % user)
342
                user.backend = 'authentic2.backends.models_backend.ModelBackend'
343
                login(request, user, 'autologin')
344
                url = self.get_redirect_url(token = token)
345
                logging.getLogger(__name__).info(u"Redirect to : %s" % url)
346
            except User.DoesNotExist:
347
                logging.getLogger(__name__).error(u'User %s from token does not exists' % token['email'])
348
            except MultipleObjectsReturned:
349
                logging.getLogger(__name__).error(u"More than one user return from token's email (%s)" % token['email'])
350

  
351
        if not url:
352
            url = reverse('auth_login')
353

  
354
        return http.HttpResponseRedirect(url)
355

  
356
userautologinview = UserAutoLoginView.as_view()
245 357

  
246 358
@vary_on_headers('Cookie', 'Origin', 'Referer')
247 359
@cache_control(private=True, max_age=60)
248
-