r101094 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r101093‎ | r101094 | r101095 >
Date:23:52, 27 October 2011
Author:laner
Status:deferred
Tags:
Comment:
* Add a script to manage /etc/exports based on project, and instances within a project
* Update homedirectorymanager to be able to handle multiple sets of home directories, based on group, with multiple basedirs
* Update homedirectorymanager to be able to update ssh keys based on modification timestamp of the user's account, and the timestamp of the authorized_keys file
Modified paths:
  • /trunk/tools/subversion/user-management/homedirectorymanager.py (modified) (history)
  • /trunk/tools/subversion/user-management/ldapsupportlib.py (modified) (history)
  • /trunk/tools/subversion/user-management/manage-exports (added) (history)

Diff [purge]

Index: trunk/tools/subversion/user-management/homedirectorymanager.py
@@ -1,5 +1,5 @@
22 #!/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
44 from optparse import OptionParser
55 from cStringIO import StringIO
66
@@ -8,7 +8,6 @@
99 import ldap.modlist
1010 except ImportError:
1111 sys.stderr.write("Unable to import LDAP library.\n")
12 - sys.exit(1)
1312
1413 class HomeDirectoryManager:
1514
@@ -31,6 +30,9 @@
3231 # LDAP accounts associated with them
3332 self.excludedFromModification = ['lost+found', 'SAVE', 'svn-private']
3433
 34+ # Limit home directory management to the specified group
 35+ self.group = None
 36+
3537 # Skeleton files to add to the user's home directory
3638 self.skelFiles = {}
3739 self.skelFiles['/etc/skel/'] = ['.bashrc', '.profile', '.bash_logout']
@@ -51,10 +53,16 @@
5254
5355 parser.add_option("--dry-run", action="store_true", dest="dryRun", help="Show what would be done, but don't actually do anything")
5456 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)")
5559 (self.options, args) = parser.parse_args()
5660
5761 self.dryRun = self.options.dryRun
5862 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
5967
6068 # use proxy agent by default
6169 ldapSupportLib.setBindInfoByOptions(self.options, parser)
@@ -69,6 +77,9 @@
7078 # get all user's uids
7179 UsersData = ldapSupportLib.getUsers(ds, '*')
7280 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']
7384
7485 # We are going to use a dictionary (associative array) as a hash bucket (keys pointing to dictionaries)
7586 # for the AllUsers data structure.
@@ -77,6 +88,10 @@
7889 # "<uid>": {"uidNumber": <uidNumber>, "gidNumber": <gidNumber>, "sshPublicKey": ['key1', 'key2']}}
7990 AllUsers = {}
8091 for user in UsersData:
 92+ if self.group:
 93+ dn = user[0]
 94+ if dn not in groupdns:
 95+ continue
8196 uid = user[1]['uid'][0]
8297 # uidNumber and gidNumber come back from LDAP as strings, we need ints here.
8398 uidNumber = int(user[1]['uidNumber'][0])
@@ -85,21 +100,24 @@
86101 if 'sshPublicKey' not in user[1]:
87102 continue
88103 sshPublicKey = user[1]['sshPublicKey']
 104+ modifyTimestamp = user[1]['modifyTimestamp']
89105
90106 AllUsers[uid] = {}
91107 AllUsers[uid]["uidNumber"] = uidNumber
92108 AllUsers[uid]["gidNumber"] = gidNumber
93109 AllUsers[uid]["sshPublicKey"] = sshPublicKey
 110+ AllUsers[uid]["modifyTimestamp"] = modifyTimestamp[0]
94111
95112 self.changeGid(AllUsers)
96113 self.changeUid(AllUsers)
97114 self.moveUsers(AllUsers)
 115+ self.updateKeys(AllUsers)
98116 self.createHomeDir(AllUsers)
99117
100118 except ldap.UNWILLING_TO_PERFORM, msg:
101119 sys.stderr.write("The search returned an error. Error was: %s\n" % msg[0]["info"])
102120 ds.unbind()
103 - sys.exit(1)
 121+ return 1
104122 except Exception:
105123 try:
106124 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 @@
108126 ds.unbind()
109127 except Exception:
110128 pass
111 - sys.exit(1)
 129+ return 1
112130
113131 ds.unbind()
114 - sys.exit(0)
 132+ return 0
115133
116134 # Creates home directories for new users. Will not create home directories
117135 # for users that already have a directory in SAVE
@@ -176,7 +194,6 @@
177195 return uniqueKeys
178196
179197 # 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
181198 def writeKeys(self, user, keys):
182199 self.writeFile(self.basedir + user + '/.ssh/authorized_keys', "\n".join(keys) + "\n")
183200
@@ -258,6 +275,26 @@
259276 for name in dirs:
260277 self.chown(os.path.join(root, name), newUid, -1)
261278
 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+
262299 def log(self, logstring):
263300 print datetime.datetime.now().strftime("%m/%d/%Y - %H:%M:%S - ") + logstring
264301
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
179 + *
Index: trunk/tools/subversion/user-management/ldapsupportlib.py
@@ -34,7 +34,7 @@
3535 self.defaults['authuser'] = "scriptuser"
3636
3737 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=['*', '+'])
3939 return PosixData
4040
4141 def getKeys(self, ds, username):

Follow-up revisions

RevisionCommit summaryAuthorDate
r101095Fix script usage message. Follow up to r101094.laner23:54, 27 October 2011

Status & tagging log