source: asadb/mit/__init__.py @ 08d4fa6

space-accessstablestage
Last change on this file since 08d4fa6 was 08d4fa6, checked in by Alex Dehnert <adehnert@…>, 13 years ago

Wrappers for safely calling commands in a new PAG

The usual mechanism for starting a new PAG is pagsh(1). Unfortunately, because
it basically just execvp(3) /bin/sh passing the appropriate arguments, it isn't
immediately obvious how to safely pass arguments that may contain shell
metacharacters. By using the shell's exec and taking advantage of the fact that
later arguments to /bin/sh end up in $@ we can safely avoid shell
metacharacters. We wrap subprocess.check_{call,output} in
pag_check_{call,output}, which perform appropriate contortions to establish the
PAG before safely executing the passed commands without evaluating any
metacharacters.

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