source: asadb/util/sync_moira_authz.py

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

Use transaction.commit_on_success for imports

Fixed the following scripts:

  • forms/update_validations.py (15s->1s for 70ish validations)
  • space/import_into_db.py (67s->1s for 75 spaces and 100 assignments)
  • util/sync_moira_authz.py (65s->4s for importing ~50 new people)

util/update_old_previews.py was skipped:

  • it's dead code, I believe --- I think previews are unused
  • preview updating is slow, IIRC; it's dubious we'd get noticeably better runtime on the script, and there may even be utility to the progress updates not doing this transactionally supplies

This closes ASA Trac #59.

  • Property mode set to 100755
File size: 5.9 KB
Line 
1#!/usr/bin/python
2
3import afs.pts
4import ldap
5import ldap.dn
6import ldap.filter
7import os
8import sys
9import subprocess
10
11if __name__ == '__main__':
12    cur_file = os.path.abspath(__file__)
13    django_dir = os.path.abspath(os.path.join(os.path.dirname(cur_file), '..'))
14    sys.path.append(django_dir)
15    os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
16
17import django.contrib.auth.models
18from django.db import transaction
19
20import mit
21import settings
22
23class DjangoConnector(object):
24    def __init__(self, ):
25        self.dj_groups = django.contrib.auth.models.Group.objects
26
27    def sync_helper(self, sys_name, dj_members, adder, remover, ):
28        kept = []
29        added = []
30        nonexist = []
31        created = []
32        removed = []
33        sys_members = self.get_members(sys_name)
34        dj_usernames = set([m.username for m in dj_members])
35        for username in sys_members:
36            if username in dj_usernames:
37                kept.append(username)
38            else:
39                # Need to add to the Django group
40                try:
41                    user, is_new = mit.get_or_create_mit_user(username, )
42                    if is_new: created.append(username)
43                    adder(user)
44                    added.append(username)
45                except ValueError:
46                    nonexist.append(username)
47        for user in dj_members:
48            username = user.username
49            if username in sys_members:
50                assert username in kept
51            else:
52                remover(user)
53                removed.append(username)
54        return {
55            'change' : len(added) + len(removed),
56            'keep' : kept,
57            'add'  : added,
58            'create' : created,
59            'nonexist' : nonexist,
60            'remove': removed,
61        }
62
63    def sync_staff(self, sys_name, ):
64        dj_members = django.contrib.auth.models.User.objects.filter(is_staff=True, )
65        def adder(user, ):
66            user.is_staff = True
67            user.save()
68        def remover(user, ):
69            user.is_staff = False
70            user.save()
71        return self.sync_helper(sys_name, dj_members, adder, remover, )
72
73    def sync_members(self, sys_name, dj_name, ):
74        dj_group = self.dj_groups.get(name=dj_name)
75        dj_members = dj_group.user_set.all()
76        adder = lambda user: user.groups.add(dj_group)
77        remover = lambda user: user.groups.remove(dj_group)
78        return self.sync_helper(sys_name, dj_members, adder, remover, )
79
80    @transaction.commit_on_success
81    def sync_many(con, what, force_print=False, ):
82        changed = False
83        results = {}
84        for sys_name, dj_group in what:
85            assert dj_group not in results
86            if dj_group == "STAFF":
87                results[dj_group] = con_afs.sync_staff(sys_name, )
88            else:
89                results[dj_group] = con_afs.sync_members(sys_name, dj_group)
90            if results[dj_group]['change']: changed = True
91        if changed or force_print:
92            for group in results:
93                print ""
94                print "Results for %s:" % (group, )
95                for key, value in results[group].items():
96                    print "%7s:\t%s" % (key, value, )
97
98
99class LDAPConnector(DjangoConnector):
100    name = "LDAP"
101
102    def __init__(self, *args, **kwargs):
103        super(LDAPConnector, self).__init__(*args, **kwargs)
104        self.con = ldap.initialize('ldaps://ldap-too.mit.edu')
105        self.con.simple_bind_s("", "")
106
107    def get_members(self, groupname):
108        base_dn = 'ou=lists,ou=moira,dc=mit,dc=edu'
109        groupfilter = ldap.filter.filter_format('(&(objectClass=group)(displayName=%s))', [groupname])
110        result = self.con.search_s(base_dn, ldap.SCOPE_SUBTREE, groupfilter, )
111        if len(result) > 1: print "WARNING: More than one result returned for %s" % groupname
112        if len(result) < 1: print "WARNING: Only no results returned for %s" % groupname
113        attrs = result[0][1]
114        members = attrs['member']
115        ret = set()
116        for member in members:
117            member_dn = ldap.dn.explode_dn(member)
118            assert len(member_dn) == 5
119            c_val, c_type, c_moira, c_mit, c_edu = member_dn
120            assert (c_moira, c_mit, c_edu, ) == ('OU=moira', 'dc=MIT', 'dc=EDU', )
121            c_val_key, c_val_sep, c_val_val = c_val.partition('=')
122            if c_type == 'OU=strings':
123                ret.add(('string', c_val_val, ))
124            elif c_type == 'OU=users':
125                ret.add(('user', c_val_val, ))
126            elif c_type == 'OU=kerberos':
127                ret.add(('kerberos', c_val_val, ))
128            else:
129                assert False, "Don't know what %s is" % (c_type, )
130        return [r[1] for r in ret if r[0] == 'user']
131
132class AFSConnector(DjangoConnector):
133    name = "AFS"
134
135    def __init__(self, *args, **kwargs):
136        super(AFSConnector, self).__init__(*args, **kwargs)
137        #self.login()
138        self.pts = afs.pts.PTS(sec=afs.pts.PTS_ENCRYPT, cell='athena.mit.edu', )
139    def login(self, ):
140        if settings.KRB_KEYTAB:
141            kinit_cmd = ['kinit', '-k', '-t', settings.KRB_KEYTAB, settings.KRB_PRINCIPAL, ]
142            subprocess.check_call(kinit_cmd)
143            subprocess.check_call(['aklog', 'athena', ])
144
145    def get_members(self, groupname, ):
146        afs_members = self.pts.getEntry("system:%s" % (groupname, )).members
147        members = [ m.name for m in afs_members ]
148        return members
149
150sync_pairs = [
151    ('asa-admin', 'STAFF', ),
152    ('asa-admin', 'asa-ebm', ),
153    ('asa-db-mit-deskworker', 'mit-deskworker', ),
154    ('asa-db-mit-offices', 'mit-offices', ),
155    ('asa-db-mit-sao', 'mit-sao', ),
156]
157
158def test_memberships(cons):
159    for sys_name, dj_group in sync_pairs:
160        for con in cons:
161            members = con.get_members(sys_name)
162            print "%s\t%s\t%s" % (con.name, sys_name, sorted(members))
163
164if __name__ == '__main__':
165    con_afs = AFSConnector()
166    #con_ldap = LDAPConnector()
167    #test_memberships([con_afs, con_ldap, ])
168    con_afs.sync_many(sync_pairs)
Note: See TracBrowser for help on using the repository browser.