Index: trunk/tools/subversion/user-management/homedirectorymanager.py |
— | — | @@ -1,5 +1,5 @@ |
2 | 2 | #!/usr/bin/python |
3 | | -import sys, traceback, os, datetime, ldapsupportlib, shutil, pwd, re, pycurl |
| 3 | +import sys, traceback, os, datetime, ldapsupportlib, shutil, pwd, re, pycurl, time, datetime |
4 | 4 | from optparse import OptionParser |
5 | 5 | from cStringIO import StringIO |
6 | 6 | |
— | — | @@ -8,7 +8,6 @@ |
9 | 9 | import ldap.modlist |
10 | 10 | except ImportError: |
11 | 11 | sys.stderr.write("Unable to import LDAP library.\n") |
12 | | - sys.exit(1) |
13 | 12 | |
14 | 13 | class HomeDirectoryManager: |
15 | 14 | |
— | — | @@ -31,6 +30,9 @@ |
32 | 31 | # LDAP accounts associated with them |
33 | 32 | self.excludedFromModification = ['lost+found', 'SAVE', 'svn-private'] |
34 | 33 | |
| 34 | + # Limit home directory management to the specified group |
| 35 | + self.group = None |
| 36 | + |
35 | 37 | # Skeleton files to add to the user's home directory |
36 | 38 | self.skelFiles = {} |
37 | 39 | self.skelFiles['/etc/skel/'] = ['.bashrc', '.profile', '.bash_logout'] |
— | — | @@ -51,10 +53,16 @@ |
52 | 54 | |
53 | 55 | parser.add_option("--dry-run", action="store_true", dest="dryRun", help="Show what would be done, but don't actually do anything") |
54 | 56 | parser.add_option("--debug", action="store_true", dest="debugStatus", help="Run in debug mode (you likely want to use --dry-run with this)") |
| 57 | + parser.add_option("--basedir", dest="basedir", help="Base directory to manage home directories (default: /home)") |
| 58 | + parser.add_option("--group", dest="group", help="Only manage home directories for users in the provided group (default: manage all users)") |
55 | 59 | (self.options, args) = parser.parse_args() |
56 | 60 | |
57 | 61 | self.dryRun = self.options.dryRun |
58 | 62 | self.debugStatus = self.options.debugStatus |
| 63 | + if self.options.basedir: |
| 64 | + self.basedir = self.options.basedir |
| 65 | + if self.options.group: |
| 66 | + self.group = self.options.group |
59 | 67 | |
60 | 68 | # use proxy agent by default |
61 | 69 | ldapSupportLib.setBindInfoByOptions(self.options, parser) |
— | — | @@ -69,6 +77,9 @@ |
70 | 78 | # get all user's uids |
71 | 79 | UsersData = ldapSupportLib.getUsers(ds, '*') |
72 | 80 | self.logDebug("Pulled the user information") |
| 81 | + if self.group: |
| 82 | + GroupData = ds.search_s("ou=groups," + base,ldap.SCOPE_SUBTREE,"(&(objectclass=posixGroup)(cn=" + self.group + "))") |
| 83 | + groupdns = GroupData[0][1]['member'] |
73 | 84 | |
74 | 85 | # We are going to use a dictionary (associative array) as a hash bucket (keys pointing to dictionaries) |
75 | 86 | # for the AllUsers data structure. |
— | — | @@ -77,6 +88,10 @@ |
78 | 89 | # "<uid>": {"uidNumber": <uidNumber>, "gidNumber": <gidNumber>, "sshPublicKey": ['key1', 'key2']}} |
79 | 90 | AllUsers = {} |
80 | 91 | for user in UsersData: |
| 92 | + if self.group: |
| 93 | + dn = user[0] |
| 94 | + if dn not in groupdns: |
| 95 | + continue |
81 | 96 | uid = user[1]['uid'][0] |
82 | 97 | # uidNumber and gidNumber come back from LDAP as strings, we need ints here. |
83 | 98 | uidNumber = int(user[1]['uidNumber'][0]) |
— | — | @@ -85,21 +100,24 @@ |
86 | 101 | if 'sshPublicKey' not in user[1]: |
87 | 102 | continue |
88 | 103 | sshPublicKey = user[1]['sshPublicKey'] |
| 104 | + modifyTimestamp = user[1]['modifyTimestamp'] |
89 | 105 | |
90 | 106 | AllUsers[uid] = {} |
91 | 107 | AllUsers[uid]["uidNumber"] = uidNumber |
92 | 108 | AllUsers[uid]["gidNumber"] = gidNumber |
93 | 109 | AllUsers[uid]["sshPublicKey"] = sshPublicKey |
| 110 | + AllUsers[uid]["modifyTimestamp"] = modifyTimestamp[0] |
94 | 111 | |
95 | 112 | self.changeGid(AllUsers) |
96 | 113 | self.changeUid(AllUsers) |
97 | 114 | self.moveUsers(AllUsers) |
| 115 | + self.updateKeys(AllUsers) |
98 | 116 | self.createHomeDir(AllUsers) |
99 | 117 | |
100 | 118 | except ldap.UNWILLING_TO_PERFORM, msg: |
101 | 119 | sys.stderr.write("The search returned an error. Error was: %s\n" % msg[0]["info"]) |
102 | 120 | ds.unbind() |
103 | | - sys.exit(1) |
| 121 | + return 1 |
104 | 122 | except Exception: |
105 | 123 | try: |
106 | 124 | sys.stderr.write("There was a general error, please contact an administrator via the helpdesk. Please include the following stack trace with your report:\n") |
— | — | @@ -107,10 +125,10 @@ |
108 | 126 | ds.unbind() |
109 | 127 | except Exception: |
110 | 128 | pass |
111 | | - sys.exit(1) |
| 129 | + return 1 |
112 | 130 | |
113 | 131 | ds.unbind() |
114 | | - sys.exit(0) |
| 132 | + return 0 |
115 | 133 | |
116 | 134 | # Creates home directories for new users. Will not create home directories |
117 | 135 | # for users that already have a directory in SAVE |
— | — | @@ -176,7 +194,6 @@ |
177 | 195 | return uniqueKeys |
178 | 196 | |
179 | 197 | # Write a list of keys to the user's authorized_keys file |
180 | | - # TODO: run this when keys change as well as when directories need to be created |
181 | 198 | def writeKeys(self, user, keys): |
182 | 199 | self.writeFile(self.basedir + user + '/.ssh/authorized_keys', "\n".join(keys) + "\n") |
183 | 200 | |
— | — | @@ -258,6 +275,26 @@ |
259 | 276 | for name in dirs: |
260 | 277 | self.chown(os.path.join(root, name), newUid, -1) |
261 | 278 | |
| 279 | + def updateKeys(self, users): |
| 280 | + for userdir in os.listdir(self.basedir): |
| 281 | + if not os.path.isdir(self.basedir + userdir) or userdir in self.excludedFromModification: |
| 282 | + continue |
| 283 | + if userdir not in users.keys(): |
| 284 | + continue |
| 285 | + stat = os.stat(self.basedir + userdir + "/.ssh/authorized_keys") |
| 286 | + atime = stat.st_atime |
| 287 | + mtime = stat.st_mtime |
| 288 | + d_mtime = datetime.datetime.utcfromtimestamp(mtime) |
| 289 | + d_ldap_mtime = users[userdir]["modifyTimestamp"] |
| 290 | + d_ldap_mtime = datetime.datetime.strptime(d_ldap_mtime[0:-1], "%Y%m%d%H%M%S") |
| 291 | + if d_ldap_mtime != d_mtime: |
| 292 | + # Either the user's entry has been updated, or someone |
| 293 | + # has been manually mucking with the keys, either way |
| 294 | + # let's overwrite them |
| 295 | + self.writeKeys(userdir, users[userdir]['sshPublicKey']) |
| 296 | + self.log( "Updating keys for %s" % (userdir) ) |
| 297 | + os.utime(self.basedir + userdir + "/.ssh/authorized_keys", (atime, time.mktime(d_ldap_mtime.timetuple()))) |
| 298 | + |
262 | 299 | def log(self, logstring): |
263 | 300 | print datetime.datetime.now().strftime("%m/%d/%Y - %H:%M:%S - ") + logstring |
264 | 301 | |
Index: trunk/tools/subversion/user-management/manage-exports |
— | — | @@ -0,0 +1,77 @@ |
| 2 | +#!/usr/bin/python |
| 3 | +import sys, traceback, ldapsupportlib, os, homedirectorymanager |
| 4 | +from optparse import OptionParser |
| 5 | +from subprocess import call |
| 6 | + |
| 7 | +try: |
| 8 | + import ldap |
| 9 | + import ldap.modlist |
| 10 | +except ImportError: |
| 11 | + sys.stderr.write("Unable to import LDAP library.\n") |
| 12 | + sys.exit(1) |
| 13 | + |
| 14 | +def main(): |
| 15 | + parser = OptionParser(conflict_handler="resolve") |
| 16 | + parser.set_usage('modify-ldap-group [options]') |
| 17 | + |
| 18 | + ldapSupportLib = ldapsupportlib.LDAPSupportLib() |
| 19 | + ldapSupportLib.addParserOptions(parser) |
| 20 | + |
| 21 | + (options, args) = parser.parse_args() |
| 22 | + ldapSupportLib.setBindInfoByOptions(options, parser) |
| 23 | + |
| 24 | + base = ldapSupportLib.getBase() |
| 25 | + ds = ldapSupportLib.connect() |
| 26 | + |
| 27 | + # w00t We're in! |
| 28 | + try: |
| 29 | + projectdata = ds.search_s("ou=groups," + base,ldap.SCOPE_SUBTREE,"(&(cn=*)(owner=*))") |
| 30 | + projects = [] |
| 31 | + basedir = "/export/home/" |
| 32 | + if not projectdata: |
| 33 | + raise ldap.NO_SUCH_OBJECT() |
| 34 | + hdm = homedirectorymanager.HomeDirectoryManager() |
| 35 | + for project in projectdata: |
| 36 | + project_name = project[1]["cn"][0] |
| 37 | + if not os.path.exists(basedir + project_name): |
| 38 | + os.mkdir(basedir + project_name, 0755) |
| 39 | + hdm.basedir = basedir + project_name + "/" |
| 40 | + hdm.group = project_name |
| 41 | + hdm.run() |
| 42 | + hostdata = ds.search_s("ou=hosts," + base,ldap.SCOPE_SUBTREE,"(puppetvar=instanceproject=" + project_name + ")") |
| 43 | + hosts = [] |
| 44 | + for host in hostdata: |
| 45 | + |
| 46 | + host_ip = host[1]["aRecord"][0] |
| 47 | + hosts.append(host_ip + "(rw,no_subtree_check)") |
| 48 | + projects.append(basedir + project_name + " " + " ".join(hosts) + "\n") |
| 49 | + |
| 50 | + exports = open('/etc/exports', 'w') |
| 51 | + exports.writelines(projects) |
| 52 | + exports.close() |
| 53 | + retcode = call("/usr/sbin/exportfs" + " -r", shell=True) |
| 54 | + |
| 55 | + except ldap.NO_SUCH_OBJECT: |
| 56 | + sys.stderr.write("The project search returned no entries.\n") |
| 57 | + ds.unbind() |
| 58 | + sys.exit(1) |
| 59 | + except ldap.PROTOCOL_ERROR: |
| 60 | + sys.stderr.write("There was an LDAP protocol error; see traceback.\n") |
| 61 | + traceback.print_exc(file=sys.stderr) |
| 62 | + ds.unbind() |
| 63 | + sys.exit(1) |
| 64 | + except Exception: |
| 65 | + try: |
| 66 | + sys.stderr.write("There was a general error, this is unexpected; see traceback.\n") |
| 67 | + traceback.print_exc(file=sys.stderr) |
| 68 | + ds.unbind() |
| 69 | + except Exception: |
| 70 | + sys.stderr.write("Also failed to unbind.\n") |
| 71 | + traceback.print_exc(file=sys.stderr) |
| 72 | + sys.exit(1) |
| 73 | + |
| 74 | + ds.unbind() |
| 75 | + sys.exit(0) |
| 76 | + |
| 77 | +if __name__ == "__main__": |
| 78 | + main() |
Property changes on: trunk/tools/subversion/user-management/manage-exports |
___________________________________________________________________ |
Added: svn:executable |
1 | 79 | + * |
Index: trunk/tools/subversion/user-management/ldapsupportlib.py |
— | — | @@ -34,7 +34,7 @@ |
35 | 35 | self.defaults['authuser'] = "scriptuser" |
36 | 36 | |
37 | 37 | def getUsers(self, ds, username): |
38 | | - PosixData = ds.search_s("ou=people," + self.base,ldap.SCOPE_SUBTREE,"(&(objectclass=inetOrgPerson)(uid=" + username + "))") |
| 38 | + PosixData = ds.search_s("ou=people," + self.base,ldap.SCOPE_SUBTREE,"(&(objectclass=inetOrgPerson)(uid=" + username + "))", attrlist=['*', '+']) |
39 | 39 | return PosixData |
40 | 40 | |
41 | 41 | def getKeys(self, ds, username): |