login.py 6.39 KB
Newer Older
1 2
# -*- coding: utf-8 -*-
#
3
# LOGIN.PY -- Gère l'interface d'authentification à l'aide des modèles Django.
4
#
5
# Copyright (C) 2009-2010 Nicolas Dandrimont
6
# Authors: Nicolas Dandrimont <olasd@crans.org>
7
# Censor: Antoine Durand-Gasselin <adg@crans.org>
8 9 10 11 12 13 14 15 16 17 18 19 20 21
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

22

23
import ldap
24
from django.contrib.auth.models import Group, User, Permission
25
from django.contrib.auth.backends import ModelBackend
26
from django.views.decorators.debug import sensitive_variables
27
from django.contrib.contenttypes.models import ContentType
28 29

# Pour se connecter à la base ldap
Vincent Le gallic's avatar
Vincent Le gallic committed
30
import lc_ldap.shortcuts
31

32
from intranet import conn_pool, settings
33

34 35 36 37 38 39 40
GROUPES_SPECIAUX = { # Dictionnaire de groupe spéciaux à attribuer selon le test correspondant
"crans_paiement_ok": lambda u: u.paiement_ok(),
"crans_imprimeur_club": lambda u: u.imprimeur_clubs(),
"crans_respo_club": lambda u: u.clubs(),
"crans_adherent": lambda u: 'aid' in u,
"crans_club": lambda u: 'cid' in u,
}
41 42 43 44 45 46 47 48 49 50
USER_TYPE = ContentType.objects.get(app_label="auth", model="user")
CRANS_MULTIMACHINES,created = Permission.objects.get_or_create(codename=u"crans_multimachines",
                                       name=u"crans_multimachines",
                                       content_type=USER_TYPE)

DROITS_SPECIAUX = { # Dictionnaire de groupe devant contenir les droits correspondant
"crans_cableur": [CRANS_MULTIMACHINES],
"crans_imprimeur": [CRANS_MULTIMACHINES],
"crans_apprenti": [CRANS_MULTIMACHINES],
"crans_bureau": [CRANS_MULTIMACHINES],
51
"crans_ma": [CRANS_MULTIMACHINES],
52
}
53 54 55


def get_or_create_cransgroup(name):
56 57
    """
        Crée le groupe crans_name avec le droit crans_name.
58 59 60 61 62
    """
    group, created = Group.objects.get_or_create(name=name)
    if created:
        permission,created = Permission.objects.get_or_create(codename=name,
                                       name=name,
63
                                       content_type=USER_TYPE)
64
        group.permissions.add(permission)
65 66
	if name in DROITS_SPECIAUX:
		group.permissions.add(*DROITS_SPECIAUX[name])
67 68 69
        group.save()
    return group

70
def refresh_droits(user, cl_user, allow_staff=False):
71 72 73 74
    """
        Rafraîchit les droits de l'utilisateur django `user' depuis
        l'utilisateur LDAP `cl_user'.
        N'ajoute pas les droits crans si allow_staff=False (default)
75
    """
76

77 78 79
    cl_droits_reels = set(x.value for x in cl_user.get('droits', []))
    if allow_staff:
        cl_droits = cl_droits_reels
80
    else:
81 82 83 84 85 86 87 88 89 90 91
        cl_droits = set()

    # Droit membre actif générique
    if cl_droits_reels:
        cl_droits.add(u"MA")
    if cl_droits_reels and allow_staff:
        cl_droits.add(u"enabled")

    # Fais le calcul des droits d'accès
    user.is_staff = not cl_droits.isdisjoint([u"Nounou", u"Apprenti", u"Imprimeur"])
    user.is_superuser = not cl_droits.isdisjoint([u"Nounou"])
92

93
    # et celui des groupes
94 95
    groups = []
    for cl_droit in cl_droits:
96 97 98 99
        groups.append(get_or_create_cransgroup(name=u"crans_%s" % cl_droit.lower()))
    for group in GROUPES_SPECIAUX:
        if GROUPES_SPECIAUX[group](cl_user):
            groups.append(get_or_create_cransgroup(name=group))
100 101 102 103 104 105

    user.groups = [ group for group in user.groups.all() if not group.name.startswith('crans_') ]
    user.groups.add(*groups)
    user.save()

def refresh_fields(user, cl_user):
106 107 108 109
    """
        Rafraîchit les champs correspondants à l'utilisateur
        (nom, prénom, email)
    """
110

111
    user.first_name = unicode(cl_user.get('prenom', [u"club"])[0])
112
    user.last_name = unicode(cl_user['nom'][0])
113
    mail = unicode(cl_user.get('mail', cl_user['uid'])[0])
114 115 116 117 118 119
    if '@' not in mail:  # Ne devrait pas arriver (pour migration)
        mail += u'@crans.org'
    user.email = mail

    user.save()

120 121 122 123 124
def post_cas_login(sender, user, created, attributes, ticket, service, **kwargs):
    ldap_user = conn_pool.get_user(user)
    refresh_droits(user, ldap_user)
    refresh_fields(user, ldap_user)

125
class LDAPUserBackend(ModelBackend):
126 127 128
    """
        Authentifie un utilisateur à l'aide de la base LDAP
    """
129
    supports_anonymous_user = False
130

131
    @sensitive_variables('password')
132
    def authenticate(self, username=None, password=None, **kwargs):
133 134 135 136
        """
            Authentifie l'utilisateur sur la base LDAP. Crée un
            utilisateur django s'il n'existe pas encore.
        """
137

138
        # Si ``username`` ou ``password`` 'est pas fourni, on quitte
139 140
        if not username or not password:
            return None
141
        # Sinon, on ouvre une connexion
Vincent Le gallic's avatar
Vincent Le gallic committed
142
        shortcut = lc_ldap.shortcuts.lc_ldap_test if settings.BASE_LDAP_TEST else lc_ldap.shortcuts.lc_ldap
143
        try:
144 145 146
            # On ouvre une connection à la base LDAP avec les credentials de l'user
            conn = shortcut(user=username, cred=password)
            myself = conn.search(dn=conn.dn, scope=ldap.SCOPE_BASE)
147
            ldap_user = myself[0]
148
        except IndexError:
149
            # Si pas de resultats, on renvoie ``None``
150
            return None
151
        except ldap.INVALID_CREDENTIALS:
152
            # Si les identifiants sont invalides, on renvoie ``None``
153
            return None
154

155
        django_username = username
156
        try:
157
            # On essaie de récupérer l'utilisateur dans la base
158 159
            user = User.objects.get(username=django_username)
        except User.DoesNotExist:
160 161 162 163 164
            # Si l'utilisateur n'est pas dans la base, on le cré
            user = User(
                    username=django_username,
                    password="LDAP Backend User!",
            )
165 166
        user.save()
        conn_pool.CONNS[django_username] = conn
167 168
        refresh_droits(user, ldap_user)
        refresh_fields(user, ldap_user)
169
        return user
170 171

    def get_user(self, uid):
172 173 174
        """
            Récupère l'objet django correspondant à l'uid
        """
175 176 177 178
        try:
            return User.objects.get(pk=uid)
        except User.DoesNotExist:
            return None
179