Gitlab CSE Unil

authentication.py 4.39 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from django.contrib.auth import login as auth_login
from django.contrib.auth.models import User
from django.db import IntegrityError

from rest_framework.authentication import BaseAuthentication

from .utils import get_request_attp

try:
    from shibauth.models import ShibbUserAttributes
except ImportError:
    ShibbUserAttributes = None


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
def validate_username(email):
    """
    Return a unique username based on the email truncated to 30 char.
    If a username already exists, try to build a new one by incrementing last char
    :param email:
    :return:
    """
    candidate = email[:30]
    if not User.objects.filter(username=candidate).exists():
        return candidate

    # A user exists with that username, change it until it's unique
    found = False
    inc = 0
    while not found:
        inc += 1
        if inc > 2000:
            raise Exception("Unexpected error, max iteration reached")

        candidate = candidate[:-len(str(inc))] + str(inc)
        if not User.objects.filter(username=candidate).exists():
            found = True
    return candidate


40
41
42
43
44
45
46
47
48
49
def get_or_create_user(attp_user):
    """
    Try to find an existing user corresponding to the parameters passed by attp_user.
    The first attribute with data in attp_user will be used for lookup
    :param attp_user:
    :return:
    """
    # attp_attr is the name of the attribute in attp_user,
    # model_attr is the corresponding attribute name on the User model,
    # given in attr_choice as (attp_attr, model_attr)
50
51
    attr_choice = (('id', 'pk'), ('uniqueId', 'shibbuserattributes__uid'), ('email', 'email'))

52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73

    # Lookup the user, using the various attributes
    user = None
    add_uniqueid = False
    for (attp_attr, model_attr) in attr_choice:
        # The attribute was not provided in the message, continue with next attribute
        if not attp_user.get(attp_attr):
            continue

        # If the user cannot be found with the current attribute, try with the next ones
        try:
            user = User.objects.get(**{"{}__iexact".format(model_attr): attp_user.get(attp_attr)})
        except User.DoesNotExist:
            continue

        add_uniqueid = (model_attr != 'shibbuserattributes__uid')
        # user is found, get out of the loop
        break
    user_changed = False

    # If the user could not be found, create it
    if user is None:
74
        user = User(username=validate_username(attp_user.get('email')), email=attp_user.get('email'))
75
76
77
78
79
80
81
        user_changed = True

    # Update User data
    if attp_user.get('first_name') and user.first_name != attp_user['first_name']:  # Update first_name, if provided
        user.first_name = attp_user['first_name']
        user_changed = True

82
    if attp_user.get('last_name') and user.last_name != attp_user['last_name']:  # Update last_name if provided
83
84
85
86
        user.last_name = attp_user['last_name']
        user_changed = True

    if attp_user.get('email') and user.email != attp_user['email']:  # Update email and username if provided
87
88
        user.email = attp_user['email']
        user.username = validate_username(attp_user.get('email'))
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
        user_changed = True

    if user_changed:
        user.save()

    # Save uniqueId if user has not already one and a value is given
    if add_uniqueid and attp_user.get('uniqueId') and ShibbUserAttributes:
        try:
            ShibbUserAttributes.objects.create(user=user, uid=attp_user.get('uniqueId'))
        except IntegrityError:
            pass

    return user


def login_attp_user(request, attp_message, persist=True):
    """
    Authenticate a user based on the data given in attp_message['user']
    The attp_message is considered valid

    :param request:
    :param attp_message:
    :return:
    """
    attp_user = attp_message.get('user')
    if not attp_user:
        return

    user = get_or_create_user(attp_user)
M. Chardon's avatar
M. Chardon committed
118
    if request.user.is_anonymous or request.user != user:
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
        if persist:
            user.backend = 'django.contrib.auth.backends.ModelBackend'
            auth_login(request, user)
        else:
            request.user = user


class TTPAuthentication(BaseAuthentication):
    """

    """

    def authenticate(self, request):

        attp_message = get_request_attp(request)
        if not attp_message:
            return None

        attp_user = attp_message.get('user')
        if not attp_user:
            return None

        user = get_or_create_user(attp_user)
        return user, None