source: asadb/mit/__init__.py @ 0290330

space-accessstablestage
Last change on this file since 0290330 was 0290330, checked in by MIT Association of Student Activities <asa@…>, 13 years ago

Set a password of UNUSABLE_PASSWORD

  • Changes ScriptsRemoteUserBackend?'s configure_user method to set the default password to UNUSABLE_PASSWORD instead of ScriptsSSLAuth. UNUSABLE_PASSWORD displays in the admin as "Password: None", instead of an ugly error message. This should fix "Unknown password hashing algorithm" errors for users correctly created in the future. (ASA-#132)
  • Adds a migration to change current users with passwords of "" or "ScriptsSSLAuth" to a password of UNUSABLE_PASSWORD ("!"). This will fix ASA-#132 and the symptoms of ASA-#133 for already-existent users.
  • Property mode set to 100644
File size: 5.5 KB
Line 
1import os
2import subprocess
3import tempfile
4import ldap
5import ldap.filter
6
7from django.contrib.auth.backends import RemoteUserBackend
8from django.contrib.auth.hashers import UNUSABLE_PASSWORD
9from django.contrib.auth.middleware import RemoteUserMiddleware
10from django.contrib.auth.views import login
11from django.contrib.auth import REDIRECT_FIELD_NAME
12from django.http import HttpResponseRedirect
13from django.contrib import auth
14from django.core.exceptions import ObjectDoesNotExist
15from django.core.validators import URLValidator, ValidationError
16
17from django.conf import settings
18
19def zephyr(msg, clas='message', instance='log', rcpt='nobody',):
20    proc = subprocess.Popen(
21        ['zwrite', '-d', '-n', '-c', clas, '-i', instance, rcpt, ],
22        stdin=subprocess.PIPE, stdout=subprocess.PIPE
23    )
24    proc.communicate(msg)
25
26def UrlOrAfsValidator(value):
27    if value.startswith('/mit/') or value.startswith('/afs/'):
28        return
29    else:
30        try:
31            URLValidator()(value)
32        except ValidationError:
33            raise ValidationError('Provide a valid URL or AFS path')
34
35def pag_check_helper(fn, args, aklog=False, ccname=None, **kwargs):
36    if 'executable' in kwargs:
37        raise ValueError('"executable" not supported with pag_check_*')
38
39    env = None
40    if 'env' in kwargs:
41        env = kwargs['env']
42        del kwargs['env']
43    if ccname:
44        if env is not None:
45            env = dict(env)
46        else:
47            env = dict(os.environ)
48        env['KRB5CCNAME'] = ccname
49
50    pagsh_cmd = 'exec "$@"'
51    if aklog: pagsh_cmd = "aklog && " + pagsh_cmd
52    args = ['pagsh', '-c', pagsh_cmd, 'exec', ] + args
53
54    return fn(args, env=env, **kwargs)
55
56def pag_check_call(args, **kwargs):
57    return pag_check_helper(subprocess.check_call, args, **kwargs)
58def pag_check_output(args, **kwargs):
59    return pag_check_helper(subprocess.check_output, args, **kwargs)
60
61def kinit(keytab=None, principal=None, autodelete=True, ):
62    if not keytab:
63        keytab = settings.KRB_KEYTAB
64    if not principal:
65        principal = settings.KRB_PRINCIPAL
66    assert keytab and principal
67    fd = tempfile.NamedTemporaryFile(mode='rb', prefix="krb5cc_djmit_", delete=autodelete, )
68    env = dict(KRB5CCNAME=fd.name)
69    kinit_cmd = ['kinit', '-k', '-t', keytab, principal, ]
70    subprocess.check_call(kinit_cmd, env=env)
71    return fd
72
73class ScriptsRemoteUserMiddleware(RemoteUserMiddleware):
74    header = 'SSL_CLIENT_S_DN_Email'
75
76class ScriptsRemoteUserBackend(RemoteUserBackend):
77    def clean_username(self, username, ):
78        if '@' in username:
79            name, domain = username.split('@')
80            assert domain.upper() == 'MIT.EDU'
81            return name
82        else:
83            return username
84    def configure_user(self, user, ):
85        username = user.username
86        user.password = UNUSABLE_PASSWORD
87        con = ldap.open('ldap-too.mit.edu')
88        con.simple_bind_s("", "")
89        dn = "dc=mit,dc=edu"
90        fields = ['cn', 'sn', 'givenName', 'mail', ]
91        userfilter = ldap.filter.filter_format('uid=%s', [username])
92        result = con.search_s('dc=mit,dc=edu', ldap.SCOPE_SUBTREE, userfilter, fields)
93        if len(result) == 1:
94            user.first_name = result[0][1]['givenName'][0]
95            user.last_name = result[0][1]['sn'][0]
96            try:
97                user.email = result[0][1]['mail'][0]
98            except KeyError:
99                user.email = username + '@mit.edu'
100            try:
101                user.groups.add(auth.models.Group.objects.get(name='mit'))
102            except ObjectDoesNotExist:
103                print "Failed to retrieve mit group"
104        else:
105            raise ValueError, ("Could not find user with username '%s' (filter '%s')"%(username, userfilter))
106        try:
107            user.groups.add(auth.models.Group.objects.get(name='autocreated'))
108        except ObjectDoesNotExist:
109            print "Failed to retrieve autocreated group"
110        user.save()
111        return user
112
113def get_or_create_mit_user(username, ):
114    """
115    Given an MIT username, return a Django user object for them.
116    If necessary, create (and save) the Django user for them.
117    If the MIT user doesn't exist, raises ValueError.
118    """
119    user, created = auth.models.User.objects.get_or_create(username=username, )
120    if created:
121        backend = ScriptsRemoteUserBackend()
122        # Raises ValueError if the user doesn't exist
123        try:
124            return backend.configure_user(user), created
125        except ValueError:
126            user.delete()
127            raise
128    else:
129        return user, created
130
131def scripts_login(request, **kwargs):
132    host = request.META['HTTP_HOST'].split(':')[0]
133    if host == 'localhost':
134        return login(request, **kwargs)
135    elif request.META['SERVER_PORT'] == '444':
136        if request.user.is_authenticated():
137            # They're already authenticated --- go ahead and redirect
138            if 'redirect_field_name' in kwargs:
139                redirect_field_name = kwargs['redirect_field_names']
140            else:
141                from django.contrib.auth import REDIRECT_FIELD_NAME
142                redirect_field_name = REDIRECT_FIELD_NAME
143            redirect_to = request.REQUEST.get(redirect_field_name, '')
144            if not redirect_to or '//' in redirect_to or ' ' in redirect_to:
145                redirect_to = settings.LOGIN_REDIRECT_URL
146            return HttpResponseRedirect(redirect_to)
147        else:
148            return login(request, **kwargs)
149    else:
150        # Move to port 444
151        redirect_to = "https://%s:444%s" % (host, request.META['REQUEST_URI'], )
152        return HttpResponseRedirect(redirect_to)
Note: See TracBrowser for help on using the repository browser.