[776830d] | 1 | #!/usr/bin/python |
---|
| 2 | |
---|
[5543d51] | 3 | import afs.pts |
---|
[776830d] | 4 | import ldap |
---|
| 5 | import ldap.dn |
---|
| 6 | import ldap.filter |
---|
| 7 | import os |
---|
| 8 | import sys |
---|
[8d36a62] | 9 | import subprocess |
---|
[776830d] | 10 | |
---|
[5543d51] | 11 | if __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 | |
---|
| 17 | import django.contrib.auth.models |
---|
[df88ee9] | 18 | from django.db import transaction |
---|
[5543d51] | 19 | |
---|
| 20 | import mit |
---|
[8d36a62] | 21 | import settings |
---|
[5543d51] | 22 | |
---|
| 23 | class DjangoConnector(object): |
---|
[776830d] | 24 | def __init__(self, ): |
---|
[5543d51] | 25 | self.dj_groups = django.contrib.auth.models.Group.objects |
---|
| 26 | |
---|
[c8cb9ba] | 27 | def sync_helper(self, sys_name, dj_members, adder, remover, ): |
---|
[5543d51] | 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) |
---|
[c8cb9ba] | 43 | adder(user) |
---|
[5543d51] | 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: |
---|
[c8cb9ba] | 52 | remover(user) |
---|
[5543d51] | 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 | |
---|
[c8cb9ba] | 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 | |
---|
[df88ee9] | 80 | @transaction.commit_on_success |
---|
[5543d51] | 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 |
---|
[c8cb9ba] | 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) |
---|
[5543d51] | 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 | |
---|
| 99 | class LDAPConnector(DjangoConnector): |
---|
| 100 | name = "LDAP" |
---|
| 101 | |
---|
| 102 | def __init__(self, *args, **kwargs): |
---|
| 103 | super(LDAPConnector, self).__init__(*args, **kwargs) |
---|
[776830d] | 104 | self.con = ldap.initialize('ldaps://ldap-too.mit.edu') |
---|
| 105 | self.con.simple_bind_s("", "") |
---|
| 106 | |
---|
[5543d51] | 107 | def get_members(self, groupname): |
---|
[776830d] | 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, ) |
---|
[5543d51] | 130 | return [r[1] for r in ret if r[0] == 'user'] |
---|
| 131 | |
---|
| 132 | class AFSConnector(DjangoConnector): |
---|
| 133 | name = "AFS" |
---|
| 134 | |
---|
| 135 | def __init__(self, *args, **kwargs): |
---|
| 136 | super(AFSConnector, self).__init__(*args, **kwargs) |
---|
[ac1d897] | 137 | #self.login() |
---|
[5543d51] | 138 | self.pts = afs.pts.PTS(sec=afs.pts.PTS_ENCRYPT, cell='athena.mit.edu', ) |
---|
[8d36a62] | 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 | |
---|
[5543d51] | 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 | |
---|
| 150 | sync_pairs = [ |
---|
[c8cb9ba] | 151 | ('asa-admin', 'STAFF', ), |
---|
[8cb8a9c] | 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', ), |
---|
[5543d51] | 156 | ] |
---|
| 157 | |
---|
| 158 | def 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)) |
---|
[776830d] | 163 | |
---|
| 164 | if __name__ == '__main__': |
---|
[5543d51] | 165 | con_afs = AFSConnector() |
---|
| 166 | #con_ldap = LDAPConnector() |
---|
| 167 | #test_memberships([con_afs, con_ldap, ]) |
---|
| 168 | con_afs.sync_many(sync_pairs) |
---|