Index: trunk/tools/subversion/user-management/homedirectorymanager.py |
— | — | @@ -0,0 +1,263 @@ |
| 2 | +#!/usr/bin/python |
| 3 | +import sys, traceback, os, datetime, ldapsupportlib, shutil, pwd, re, pycurl |
| 4 | +from optparse import OptionParser |
| 5 | +from cStringIO import StringIO |
| 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 | +class HomeDirectoryManager: |
| 15 | + |
| 16 | + def __init__(self): |
| 17 | + ################################################### |
| 18 | + # Configuration options # |
| 19 | + ################################################### |
| 20 | + |
| 21 | + # Change this if we change the home directory location! |
| 22 | + self.basedir = '/home/' |
| 23 | + |
| 24 | + # Directory to move deleted user's home directories |
| 25 | + self.savedir = self.basedir + 'SAVE/' |
| 26 | + |
| 27 | + # Add to this array if we add LDAP accounts that shouldn't |
| 28 | + # have NFS mounted home directories. |
| 29 | + self.excludedFromCreation = [] |
| 30 | + |
| 31 | + # Add to this array if we add directories that don't have |
| 32 | + # LDAP accounts associated with them |
| 33 | + self.excludedFromModification = ['lost+found', 'SAVE', 'svn-private'] |
| 34 | + |
| 35 | + # Skeleton files to add to the user's home directory |
| 36 | + self.skelFiles = {} |
| 37 | + self.skelFiles['/etc/skel/'] = ['.bashrc', '.profile', '.bash_logout'] |
| 38 | + |
| 39 | + self.dryRun = False |
| 40 | + self.debugStatus = False |
| 41 | + |
| 42 | + os.system('nscd -i passwd') |
| 43 | + os.system('nscd -i group') |
| 44 | + |
| 45 | + def run(self): |
| 46 | + parser = OptionParser(conflict_handler="resolve") |
| 47 | + parser.set_usage("homedirectorymanager.py [options]\n\nexample: homedirectorymanager.py --dry-run") |
| 48 | + |
| 49 | + ldapSupportLib = ldapsupportlib.LDAPSupportLib() |
| 50 | + ldapSupportLib.addParserOptions(parser) |
| 51 | + |
| 52 | + parser.add_option("--dry-run", action="store_true", dest="dryRun", help="Show what would be done, but don't actually do anything") |
| 53 | + parser.add_option("--debug", action="store_true", dest="debugStatus", help="Run in debug mode (you likely want to use --dry-run with this)") |
| 54 | + (self.options, args) = parser.parse_args() |
| 55 | + |
| 56 | + self.dryRun = self.options.dryRun |
| 57 | + self.debugStatus = self.options.debugStatus |
| 58 | + |
| 59 | + # use proxy agent by default |
| 60 | + ldapSupportLib.setBindInfoByOptions(self.options, parser) |
| 61 | + |
| 62 | + base = ldapSupportLib.getBase() |
| 63 | + |
| 64 | + ds = ldapSupportLib.connect() |
| 65 | + self.logDebug("Connected") |
| 66 | + |
| 67 | + # w00t We're in! |
| 68 | + try: |
| 69 | + # get all user's uids |
| 70 | + UsersData = ldapSupportLib.getUsers(ds, '*') |
| 71 | + self.logDebug("Pulled the user information") |
| 72 | + |
| 73 | + # We are going to use a dictionary (associative array) as a hash bucket (keys pointing to dictionaries) |
| 74 | + # for the AllUsers data structure. |
| 75 | + # The data structure will look like this: |
| 76 | + # {"<uid>": {"uidNumber": <uidNumber>, "gidNumber": <gidNumber>, "sshPublicKey": ['key1', 'key2']}, |
| 77 | + # "<uid>": {"uidNumber": <uidNumber>, "gidNumber": <gidNumber>, "sshPublicKey": ['key1', 'key2']}} |
| 78 | + AllUsers = {} |
| 79 | + for user in UsersData: |
| 80 | + uid = user[1]['uid'][0] |
| 81 | + # uidNumber and gidNumber come back from LDAP as strings, we need ints here. |
| 82 | + uidNumber = int(user[1]['uidNumber'][0]) |
| 83 | + gidNumber = int(user[1]['gidNumber'][0]) |
| 84 | + sshPublicKey = user[1]['sshPublicKey'] |
| 85 | + |
| 86 | + AllUsers[uid] = {} |
| 87 | + AllUsers[uid]["uidNumber"] = uidNumber |
| 88 | + AllUsers[uid]["gidNumber"] = gidNumber |
| 89 | + AllUsers[uid]["sshPublicKey"] = sshPublicKey |
| 90 | + |
| 91 | + self.changeGid(AllUsers) |
| 92 | + self.changeUid(AllUsers) |
| 93 | + self.moveUsers(AllUsers) |
| 94 | + self.createHomeDir(AllUsers) |
| 95 | + |
| 96 | + except ldap.UNWILLING_TO_PERFORM, msg: |
| 97 | + sys.stderr.write("The search returned an error. Error was: %s\n" % msg[0]["info"]) |
| 98 | + ds.unbind() |
| 99 | + sys.exit(1) |
| 100 | + except Exception: |
| 101 | + try: |
| 102 | + 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") |
| 103 | + traceback.print_exc(file=sys.stderr) |
| 104 | + ds.unbind() |
| 105 | + except Exception: |
| 106 | + pass |
| 107 | + sys.exit(1) |
| 108 | + |
| 109 | + ds.unbind() |
| 110 | + sys.exit(0) |
| 111 | + |
| 112 | + # Creates home directories for new users. Will not create home directories |
| 113 | + # for users that already have a directory in SAVE |
| 114 | + def createHomeDir(self, users): |
| 115 | + alreadyCreated = [] |
| 116 | + |
| 117 | + for user in users.keys(): |
| 118 | + if user not in self.excludedFromCreation: |
| 119 | + if os.path.exists(self.savedir + user): |
| 120 | + # User's home directory already exists |
| 121 | + alreadyCreated.append(user) |
| 122 | + continue |
| 123 | + if not os.path.exists(self.basedir + user): |
| 124 | + self.log( "Creating a home directory for %s at %s%s" % (user, self.basedir, user) ) |
| 125 | + if not self.dryRun: |
| 126 | + os.mkdir(self.basedir + user, 0700) |
| 127 | + os.mkdir(self.basedir + user + '/.ssh', 0700) |
| 128 | + self.writeKeys(user, users[user]['sshPublicKey']) |
| 129 | + os.chmod(self.basedir + user + '/.ssh/authorized_keys', 0600) |
| 130 | + for skeldir,skels in self.skelFiles.iteritems(): |
| 131 | + for skel in skels: |
| 132 | + shutil.copy(skeldir + skel, self.basedir + user + "/") |
| 133 | + os.chmod(self.basedir + user + "/" + skel, 0600) |
| 134 | + newGid = users[user]['gidNumber'] |
| 135 | + newUid = users[user]['uidNumber'] |
| 136 | + os.chown(self.basedir + user, newUid, newGid) |
| 137 | + for root, dirs, files in os.walk(self.basedir + user): |
| 138 | + for name in files: |
| 139 | + os.chown(os.path.join(root, name), newUid, newGid) |
| 140 | + for name in dirs: |
| 141 | + os.chown(os.path.join(root, name), newUid, newGid) |
| 142 | + |
| 143 | + if alreadyCreated != []: |
| 144 | + self.log( "The following users already have a home directory in the SAVE directory: " + ", ".join(alreadyCreated) ) |
| 145 | + |
| 146 | + def fetchKeys(self, location): |
| 147 | + keys = [] |
| 148 | + if re.match('^http', location): |
| 149 | + buffer = StringIO() |
| 150 | + c = pycurl.Curl() |
| 151 | + c.setopt(c.URL, location) |
| 152 | + c.setopt(c.WRITEFUNCTION, buffer.write) |
| 153 | + c.perform() |
| 154 | + c.close() |
| 155 | + raw_keys = buffer.getvalue().splitlines(True) |
| 156 | + else: |
| 157 | + file = open(location, 'r') |
| 158 | + raw_keys = file.readlines() |
| 159 | + for raw_key in raw_keys: |
| 160 | + if (re.match('^$', raw_key) or re.match('^#', raw_key)): |
| 161 | + continue |
| 162 | + keys.append(raw_key) |
| 163 | + return self.uniqueKeys(keys) |
| 164 | + |
| 165 | + def uniqueKeys(self, keys): |
| 166 | + uniqueKeys = [] |
| 167 | + [uniqueKeys.append(i) for i in keys if not uniqueKeys.count(i)] |
| 168 | + |
| 169 | + return uniqueKeys |
| 170 | + |
| 171 | + # Write a list of keys to the user's authorized_keys file |
| 172 | + def writeKeys(self, user, keys): |
| 173 | + f = open(self.basedir + user + '/.ssh/authorized_keys', 'w') |
| 174 | + f.writelines(keys) |
| 175 | + f.close() |
| 176 | + |
| 177 | + # Moved deleted users to SAVE |
| 178 | + def moveUsers(self, users): |
| 179 | + for userdir in os.listdir(self.basedir): |
| 180 | + if os.path.isdir(self.basedir + userdir) and userdir not in self.excludedFromModification: |
| 181 | + try: |
| 182 | + stat = os.stat(self.basedir + userdir) |
| 183 | + uidNumber = stat.st_uid |
| 184 | + # index 0 of getpwuid's return tuple is pw_name |
| 185 | + uid = pwd.getpwuid(uidNumber)[0] |
| 186 | + if userdir != uid: |
| 187 | + # User name has changed, rename the home directory |
| 188 | + self.renameUser(userdir, uid) |
| 189 | + continue |
| 190 | + except KeyError: |
| 191 | + pass |
| 192 | + if userdir not in users.keys(): |
| 193 | + try: |
| 194 | + # Ensure the user isn't local |
| 195 | + checkexist = pwd.getpwnam(userdir)[0] |
| 196 | + except KeyError: |
| 197 | + self.deleteUser(userdir) |
| 198 | + |
| 199 | + def renameUser(self, olduserdir, newuserdir): |
| 200 | + self.log( "Moving " + self.basedir + olduserdir + " to " + self.basedir + newuserdir ) |
| 201 | + if not self.dryRun: |
| 202 | + os.rename(self.basedir + olduserdir, self.basedir + newuserdir) |
| 203 | + |
| 204 | + def deleteUser(self, userdir): |
| 205 | + # User has been deleted, move user's home directory to SAVE |
| 206 | + if os.path.isdir(self.savedir + userdir): |
| 207 | + self.log( userdir + " exists at both " + self.basedir + userdir + " and " + self.savedir + userdir ) |
| 208 | + else: |
| 209 | + self.log( "Moving " + self.basedir + userdir + " to " + self.savedir + userdir ) |
| 210 | + if not self.dryRun: |
| 211 | + os.rename(self.basedir + userdir, self.savedir + userdir) |
| 212 | + |
| 213 | + # Changes the group ownership of a directory when a user's gid changes |
| 214 | + def changeGid(self, users): |
| 215 | + for userdir in os.listdir(self.basedir): |
| 216 | + if os.path.isdir(self.basedir + userdir) and userdir not in self.excludedFromModification: |
| 217 | + stat = os.stat(self.basedir + userdir) |
| 218 | + gid = stat.st_gid |
| 219 | + if userdir in users.keys() and users[userdir]["gidNumber"] != gid: |
| 220 | + newGid = users[userdir]["gidNumber"] |
| 221 | + self.log( "Changing group ownership of %s%s to %s; was set to %s" % (self.basedir, userdir, newGid, gid) ) |
| 222 | + if not self.dryRun: |
| 223 | + # Python doesn't have a recursive chown, so we have to walk the directory |
| 224 | + # and change everything manually |
| 225 | + self.logDebug("Doing chgrp for: " + self.basedir + userdir + " with gid: " + str(gid)) |
| 226 | + os.chown(self.basedir + userdir, -1, newGid) |
| 227 | + for root, dirs, files in os.walk(self.basedir + userdir): |
| 228 | + for name in files: |
| 229 | + os.chown(os.path.join(root, name), -1, newGid) |
| 230 | + for name in dirs: |
| 231 | + os.chown(os.path.join(root, name), -1, newGid) |
| 232 | + |
| 233 | + # Changes the ownership of a directory when a user's uid changes |
| 234 | + def changeUid(self, users): |
| 235 | + for userdir in os.listdir(self.basedir): |
| 236 | + if os.path.isdir(self.basedir + userdir) and userdir not in self.excludedFromModification: |
| 237 | + stat = os.stat(self.basedir + userdir) |
| 238 | + uid = stat.st_uid |
| 239 | + if userdir in users.keys() and users[userdir]["uidNumber"] != uid: |
| 240 | + newUid = users[userdir]["uidNumber"] |
| 241 | + self.log( "Changing ownership of %s%s to %s; was set to %s" % (self.basedir, userdir, newUid, uid) ) |
| 242 | + if not self.dryRun: |
| 243 | + # Python doesn't have a recursive chown, so we have to walk the directory |
| 244 | + # and change everything manually |
| 245 | + os.chown(self.basedir + userdir, newUid, -1) |
| 246 | + for root, dirs, files in os.walk(self.basedir + userdir): |
| 247 | + for name in files: |
| 248 | + os.chown(os.path.join(root, name), newUid, -1) |
| 249 | + for name in dirs: |
| 250 | + os.chown(os.path.join(root, name), newUid, -1) |
| 251 | + |
| 252 | + def log(self, logstring): |
| 253 | + print datetime.datetime.now().strftime("%m/%d/%Y - %H:%M:%S - ") + logstring |
| 254 | + |
| 255 | + def logDebug(self, logstring): |
| 256 | + if self.debugStatus == True: |
| 257 | + sys.stderr.write("Debug: " + logstring + "\n") |
| 258 | + |
| 259 | +def main(): |
| 260 | + homeDirectoryManager = HomeDirectoryManager() |
| 261 | + homeDirectoryManager.run() |
| 262 | + |
| 263 | +if __name__ == "__main__": |
| 264 | + main() |
Property changes on: trunk/tools/subversion/user-management/homedirectorymanager.py |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 265 | + native |
Added: svn:executable |
2 | 266 | + * |
Index: trunk/tools/subversion/user-management/modify-ldap-group |
— | — | @@ -0,0 +1,128 @@ |
| 2 | +#!/usr/bin/python |
| 3 | +import sys, pwd, grp, traceback, getpass, ldapsupportlib, copy |
| 4 | +from optparse import OptionParser |
| 5 | + |
| 6 | +try: |
| 7 | + import ldap |
| 8 | + import ldap.modlist |
| 9 | +except ImportError: |
| 10 | + sys.stderr.write("Unable to import LDAP library.\n") |
| 11 | + sys.exit(1) |
| 12 | + |
| 13 | +def main(): |
| 14 | + parser = OptionParser(conflict_handler="resolve") |
| 15 | + parser.set_usage('modify-ldap-group [options] <groupname> [--rename <newusergroup>]\nexample: modify-ldap-group --gid=501 wikidev') |
| 16 | + |
| 17 | + ldapSupportLib = ldapsupportlib.LDAPSupportLib() |
| 18 | + ldapSupportLib.addParserOptions(parser, "scriptuser") |
| 19 | + |
| 20 | + parser.add_option("-m", "--directorymanager", action="store_true", dest="directorymanager", help="Use the Directory Manager's credentials, rather than your own") |
| 21 | + parser.add_option("--gid", action="store", dest="gidNumber", help="Set the group's gid") |
| 22 | + parser.add_option("--rename", action="store_true", dest="rename", help="Rename the user") |
| 23 | + parser.add_option("--addmembers", action="store", dest="addMembers", help="Add a comma separated list of users to this group") |
| 24 | + parser.add_option("--deletemembers", action="store", dest="deleteMembers", help="Delete a comma separated list of users from this") |
| 25 | + (options, args) = parser.parse_args() |
| 26 | + |
| 27 | + if len(args) != 1: |
| 28 | + if options.rename and len(args) != 2: |
| 29 | + parser.error("modify-ldap-group expects exactly two arguments when using rename.") |
| 30 | + elif not options.rename: |
| 31 | + parser.error("modify-ldap-group expects exactly one argument, unless using --rename.") |
| 32 | + |
| 33 | + ldapSupportLib.setBindInfoByOptions(options, parser) |
| 34 | + |
| 35 | + base = ldapSupportLib.getBase() |
| 36 | + |
| 37 | + ds = ldapSupportLib.connect() |
| 38 | + |
| 39 | + # w00t We're in! |
| 40 | + try: |
| 41 | + groupname = args[0] |
| 42 | + PosixData = ds.search_s("ou=group," + base,ldap.SCOPE_SUBTREE,"(&(objectclass=posixGroup)(cn=" + groupname + "))") |
| 43 | + if not PosixData: |
| 44 | + raise ldap.NO_SUCH_OBJECT() |
| 45 | + dn = PosixData[0][0] |
| 46 | + |
| 47 | + if options.rename: |
| 48 | + newgroupname = args[1] |
| 49 | + |
| 50 | + # Rename the entry |
| 51 | + newrdn = 'cn=' + newgroupname |
| 52 | + ds.rename_s(dn, newrdn) |
| 53 | + else: |
| 54 | + PosixData = PosixData[0][1] |
| 55 | + NewPosixData = copy.deepcopy(PosixData) |
| 56 | + if options.gidNumber: |
| 57 | + try: |
| 58 | + groupcheck = grp.getgrgid(options.gidNumber) |
| 59 | + raise ldap.TYPE_OR_VALUE_EXISTS() |
| 60 | + except KeyError: |
| 61 | + NewPosixData['gidNumber'] = options.gidNumber |
| 62 | + if options.addMembers: |
| 63 | + raw_members = options.addMembers.split(',') |
| 64 | + for raw_member in raw_members: |
| 65 | + try: |
| 66 | + # Ensure the user exists |
| 67 | + # TODO: make this use LDAP calls instead of getent |
| 68 | + checkuid = pwd.getpwnam(raw_member) |
| 69 | + |
| 70 | + membertoadd = 'uid=' + raw_member + ',ou=people,' + base |
| 71 | + # uniqueMember expects DNs |
| 72 | + if 'uniqueMember' in NewPosixData.keys(): |
| 73 | + if membertoadd in NewPosixData['uniqueMember']: |
| 74 | + sys.stderr.write(raw_member + " is already a member of the group, skipping.\n") |
| 75 | + else: |
| 76 | + NewPosixData['uniqueMember'].append(membertoadd) |
| 77 | + else: |
| 78 | + NewPosixData['uniqueMember'] = [ membertoadd ] |
| 79 | + except KeyError: |
| 80 | + sys.stderr.write(raw_member + " doesn't exist, and won't be added to the group.\n") |
| 81 | + elif options.deleteMembers: |
| 82 | + raw_members = options.deleteMembers.split(',') |
| 83 | + for raw_member in raw_members: |
| 84 | + membertoremove = 'uid=' + raw_member + ',ou=people,' + base |
| 85 | + if 'uniqueMember' in NewPosixData.keys(): |
| 86 | + if membertoremove in NewPosixData['uniqueMember']: |
| 87 | + NewPosixData['uniqueMember'].remove(membertoremove) |
| 88 | + else: |
| 89 | + sys.stderr.write(raw_member + " isn't a member of the group, skipping.\n") |
| 90 | + else: |
| 91 | + sys.stderr.write("This group contains no members.\n") |
| 92 | + |
| 93 | + if PosixData == NewPosixData: |
| 94 | + sys.stderr.write("No changes to make; exiting.\n") |
| 95 | + else: |
| 96 | + modlist = ldap.modlist.modifyModlist(PosixData,NewPosixData) |
| 97 | + ds.modify_s(dn, modlist) |
| 98 | + except ldap.UNWILLING_TO_PERFORM, msg: |
| 99 | + sys.stderr.write("LDAP was unwilling to modify the group. Error was: %s\n" % msg[0]["info"]) |
| 100 | + ds.unbind() |
| 101 | + sys.exit(1) |
| 102 | + except ldap.NO_SUCH_OBJECT: |
| 103 | + sys.stderr.write("The group you are trying to modify doesn't exist.\n") |
| 104 | + ds.unbind() |
| 105 | + sys.exit(1) |
| 106 | + except ldap.TYPE_OR_VALUE_EXISTS: |
| 107 | + sys.stderr.write("The gid given already exists.\n") |
| 108 | + ds.unbind() |
| 109 | + sys.exit(1) |
| 110 | + except ldap.PROTOCOL_ERROR: |
| 111 | + sys.stderr.write("There was an LDAP protocol error; see traceback.\n") |
| 112 | + traceback.print_exc(file=sys.stderr) |
| 113 | + ds.unbind() |
| 114 | + sys.exit(1) |
| 115 | + except Exception: |
| 116 | + try: |
| 117 | + sys.stderr.write("There was a general error, this is unexpected; see traceback.\n") |
| 118 | + traceback.print_exc(file=sys.stderr) |
| 119 | + ds.unbind() |
| 120 | + except Exception: |
| 121 | + sys.stderr.write("Also failed to unbind.\n") |
| 122 | + traceback.print_exc(file=sys.stderr) |
| 123 | + sys.exit(1) |
| 124 | + |
| 125 | + ds.unbind() |
| 126 | + sys.exit(0) |
| 127 | + |
| 128 | +if __name__ == "__main__": |
| 129 | + main() |
Property changes on: trunk/tools/subversion/user-management/modify-ldap-group |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 130 | + native |
Added: svn:executable |
2 | 131 | + * |
Index: trunk/tools/subversion/user-management/add-ldap-group |
— | — | @@ -0,0 +1,109 @@ |
| 2 | +#!/usr/bin/python |
| 3 | +import sys, grp, pwd, traceback, getpass, re, ldapsupportlib |
| 4 | +from optparse import OptionParser |
| 5 | + |
| 6 | +try: |
| 7 | + import ldap |
| 8 | + import ldap.modlist |
| 9 | +except ImportError: |
| 10 | + sys.stderr.write("Unable to import LDAP library.\n") |
| 11 | + sys.exit(1) |
| 12 | + |
| 13 | +def main(): |
| 14 | + parser = OptionParser(conflict_handler="resolve") |
| 15 | + parser.set_usage('add-ldap-group [options] <groupname>\nexample: add-ldap-group wikidev') |
| 16 | + |
| 17 | + ldapSupportLib = ldapsupportlib.LDAPSupportLib() |
| 18 | + ldapSupportLib.addParserOptions(parser, "scriptuser") |
| 19 | + |
| 20 | + parser.add_option("-m", "--directorymanager", action="store_true", dest="directorymanager", help="Use the Directory Manager's credentials, rather than your own") |
| 21 | + parser.add_option("--gid", action="store", dest="gidNumber", help="The group's gid (default: next available gid)") |
| 22 | + parser.add_option("--members", action="store", dest="members", help="A comma separated list of group members to add to this group") |
| 23 | + (options, args) = parser.parse_args() |
| 24 | + |
| 25 | + if len(args) != 1: |
| 26 | + parser.error("add-ldap-group expects exactly one argument.") |
| 27 | + |
| 28 | + ldapSupportLib.setBindInfoByOptions(options, parser) |
| 29 | + |
| 30 | + base = ldapSupportLib.getBase() |
| 31 | + |
| 32 | + ds = ldapSupportLib.connect() |
| 33 | + |
| 34 | + # w00t We're in! |
| 35 | + try: |
| 36 | + groupname = args[0] |
| 37 | + |
| 38 | + dn = 'cn=' + groupname + ',ou=group,' + base |
| 39 | + cn = groupname |
| 40 | + objectClasses = ['posixGroup', 'groupOfUniqueNames', 'top'] |
| 41 | + if options.gidNumber: |
| 42 | + try: |
| 43 | + groupcheck = grp.getgrgid(options.gidNumber) |
| 44 | + raise ldap.TYPE_OR_VALUE_EXISTS() |
| 45 | + except KeyError: |
| 46 | + gidNumber = options.gidNumber |
| 47 | + else: |
| 48 | + # Find the next gid |
| 49 | + # TODO: make this use LDAP calls instead of getent |
| 50 | + gids = [] |
| 51 | + for group in grp.getgrall(): |
| 52 | + tmpgid = group[2] |
| 53 | + if tmpgid < 50000: |
| 54 | + gids.append(group[2]) |
| 55 | + gids.sort() |
| 56 | + gidNumber = gids.pop() |
| 57 | + gidNumber = str(gidNumber + 1) |
| 58 | + |
| 59 | + members = [] |
| 60 | + if options.members: |
| 61 | + raw_members = options.members.split(',') |
| 62 | + for raw_member in raw_members: |
| 63 | + try: |
| 64 | + # Ensure the user exists |
| 65 | + # TODO: make this use LDAP calls instead of getent |
| 66 | + checkuid = pwd.getpwnam(raw_member) |
| 67 | + |
| 68 | + # uniqueMember expects DNs |
| 69 | + members.append('uid=' + raw_member + ',ou=people,' + base) |
| 70 | + except KeyError: |
| 71 | + sys.stderr.write(raw_member + " doesn't exist, and won't be added to the group.\n") |
| 72 | + |
| 73 | + groupEntry = {} |
| 74 | + groupEntry['objectclass'] = objectClasses |
| 75 | + groupEntry['gidNumber'] = gidNumber |
| 76 | + groupEntry['cn'] = cn |
| 77 | + if members: |
| 78 | + groupEntry['uniqueMember'] = members |
| 79 | + |
| 80 | + modlist = ldap.modlist.addModlist(groupEntry) |
| 81 | + ds.add_s(dn, modlist) |
| 82 | + except ldap.UNWILLING_TO_PERFORM, msg: |
| 83 | + sys.stderr.write("LDAP was unwilling to create the group. Error was: %s\n" % msg[0]["info"]) |
| 84 | + ds.unbind() |
| 85 | + sys.exit(1) |
| 86 | + except ldap.TYPE_OR_VALUE_EXISTS: |
| 87 | + sys.stderr.write("The group or gid you are trying to add already exists.\n") |
| 88 | + traceback.print_exc(file=sys.stderr) |
| 89 | + ds.unbind() |
| 90 | + sys.exit(1) |
| 91 | + except ldap.PROTOCOL_ERROR: |
| 92 | + sys.stderr.write("There was an LDAP protocol error; see traceback.\n") |
| 93 | + traceback.print_exc(file=sys.stderr) |
| 94 | + ds.unbind() |
| 95 | + sys.exit(1) |
| 96 | + except Exception: |
| 97 | + try: |
| 98 | + sys.stderr.write("There was a general error, this is unexpected; see traceback.\n") |
| 99 | + traceback.print_exc(file=sys.stderr) |
| 100 | + ds.unbind() |
| 101 | + except Exception: |
| 102 | + sys.stderr.write("Also failed to unbind.\n") |
| 103 | + traceback.print_exc(file=sys.stderr) |
| 104 | + sys.exit(1) |
| 105 | + |
| 106 | + ds.unbind() |
| 107 | + sys.exit(0) |
| 108 | + |
| 109 | +if __name__ == "__main__": |
| 110 | + main() |
Property changes on: trunk/tools/subversion/user-management/add-ldap-group |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 111 | + native |
Added: svn:executable |
2 | 112 | + * |
Index: trunk/tools/subversion/user-management/netgroup-mod |
— | — | @@ -0,0 +1,286 @@ |
| 2 | +#!/usr/bin/python |
| 3 | +import sys, os, getpass, socket, copy, string, ldapsupportlib, traceback |
| 4 | +from optparse import OptionParser |
| 5 | + |
| 6 | +try: |
| 7 | + import ldap |
| 8 | + import ldap.modlist |
| 9 | +except ImportError: |
| 10 | + sys.stderr.write("Unable to import LDAP library.\n") |
| 11 | + sys.exit(1) |
| 12 | + |
| 13 | +def main(): |
| 14 | + parser = OptionParser(conflict_handler="resolve") |
| 15 | + parser.set_usage('netgroup-mod [options] netgroup-name [host|-u user] [-f|--file filename]\n\nexample: netgroup-mod "test-ng" "fenari.wikimedia.org"\nexample: netgroup-mod "test-ng" -f test.file') |
| 16 | + |
| 17 | + ldapSupportLib = ldapsupportlib.LDAPSupportLib() |
| 18 | + ldapSupportLib.addParserOptions(parser, "scriptuser") |
| 19 | + |
| 20 | + parser.add_option("-m", "--directorymanager", action="store_true", dest="directorymanager", help="Use the Directory Manager's credentials, rather than your own") |
| 21 | + parser.add_option("-u", "--user", action="store_true", dest="user", help="Modify a user netgroup instead of a host netgroup.") |
| 22 | + parser.add_option("--add", action="store_true", dest="addentry", help="Add new netgroup") |
| 23 | + parser.add_option("--delete", action="store_true", dest="deleteentry", help="Delete a netgroup") |
| 24 | + parser.add_option("-h", action="store_true", dest="hosts", help="Show available hosts") |
| 25 | + parser.add_option("-n", action="store_true", dest="netgroups", help="Show available netgroups (notice you may not be able to add hosts to some netgroups shown). This option will show all available netgroups unless the additional non-exclusive flags are used.") |
| 26 | + parser.add_option("--showhost", action="store_true", dest="showhost", help="Show ou=host netgroups (used with -n)") |
| 27 | + parser.add_option("--showshare", action="store_true", dest="showshares", help="Show ou=shares netgroups (used with -n)") |
| 28 | + parser.add_option("--showuser", action="store_true", dest="showuser", help="Show ou=user netgroups (used with -n)") |
| 29 | + parser.add_option("-d", action="store_true", dest="delete", help="Remove provided host/user from provided netgroup") |
| 30 | + parser.add_option("-f", "--file", dest="file", help="Add hosts provided from the following file. The file should have one IP address or hostname per line. IP addresses that are not in DNS will be ignored (you will be informed though).", metavar="FILE") |
| 31 | + (options, args) = parser.parse_args() |
| 32 | + |
| 33 | + ldapSupportLib.setBindInfoByOptions(options, parser) |
| 34 | + |
| 35 | + base = ldapSupportLib.getBase() |
| 36 | + |
| 37 | + ds = ldapSupportLib.connect() |
| 38 | + |
| 39 | + # w00t We're in! |
| 40 | + try: |
| 41 | + if options.hosts: |
| 42 | + PosixData = ds.search_s("ou=hosts," + base,ldap.SCOPE_SUBTREE,"(&(objectclass=iphost)(cn=*))") |
| 43 | + elif options.netgroups: |
| 44 | + PosixData = ds.search_s("ou=netgroup," + base,ldap.SCOPE_SUBTREE,"(&(objectclass=nisnetgroup)(cn=*))") |
| 45 | + elif options.addentry: |
| 46 | + PosixData = ds.search_s("ou=netgroup," + base,ldap.SCOPE_SUBTREE,"(&(objectclass=nisNetGroup)(cn=" + args[0] + "))") |
| 47 | + if PosixData: |
| 48 | + sys.stderr.write("The entry you wish to add already exists.\n") |
| 49 | + sys.exit(1) |
| 50 | + if not PosixData and options.addentry: |
| 51 | + nametoadd = args[1] |
| 52 | + if options.user: |
| 53 | + formattednametoadd = "(," + nametoadd + ",)" |
| 54 | + else: |
| 55 | + nametocheck = nametoadd |
| 56 | + nametoadd = socket.gethostbyaddr(nametocheck)[0] |
| 57 | + formattednametoadd = "(" + nametoadd + ",,)" |
| 58 | + netgrouplist = [] |
| 59 | + if options.file: |
| 60 | + infofromfile(netgrouplist, "add", options.file, netgrouptype, PosixCheckData) |
| 61 | + else: |
| 62 | + netgrouplist.append(formattednametoadd) |
| 63 | + NewPosixData = {} |
| 64 | + NewPosixData['objectclass'] = ['top', 'nisnetgroup'] |
| 65 | + NewPosixData['nisnetgrouptriple'] = netgrouplist |
| 66 | + mods = ldap.modlist.addModlist(NewPosixData) |
| 67 | + ds.add_s('cn=' + args[0] + ",ou=netgroup," + base, mods) |
| 68 | + print "The netgroup was successfully added." |
| 69 | + sys.exit(0) |
| 70 | + elif options.deleteentry: |
| 71 | + PosixData = ds.search_s("ou=netgroup," + base,ldap.SCOPE_SUBTREE,"(&(objectclass=nisNetGroup)(cn=" + args[0] + "))") |
| 72 | + if not PosixData: |
| 73 | + sys.stderr.write("The entry you wish to delete doesn't exist.\n") |
| 74 | + sys.exit(1) |
| 75 | + else: |
| 76 | + dn = PosixData[0][0] |
| 77 | + verification = raw_input("Are you sure you wish to delete the following entry: " + dn + "? [y/N] ") |
| 78 | + if verification == "y" or verification == "Y": |
| 79 | + try: |
| 80 | + ds.delete_s(dn) |
| 81 | + print "The netgroup was successfully deleted.\n" |
| 82 | + except Exception: |
| 83 | + sys.stderr.write("There was an error while trying to delete the netgroup; see traceback\n") |
| 84 | + traceback.print_exc(file=sys.stderr) |
| 85 | + sys.exit(1) |
| 86 | + sys.exit(0) |
| 87 | + else: |
| 88 | + print "Cancelling deletion\n" |
| 89 | + sys.exit() |
| 90 | + else: |
| 91 | + try: |
| 92 | + if options.user: |
| 93 | + netgrouptype = "user" |
| 94 | + else: |
| 95 | + netgrouptype = "host" |
| 96 | + if not options.file: |
| 97 | + nametoadd = args[1] |
| 98 | + if options.user: |
| 99 | + formattednametoadd = "(," + nametoadd + ",)" |
| 100 | + else: |
| 101 | + nametocheck = nametoadd |
| 102 | + nametoadd = socket.gethostbyaddr(nametocheck)[0] |
| 103 | + formattednametoadd = "(" + nametoadd + ",,)" |
| 104 | + |
| 105 | + netgrouptomod = args[0] |
| 106 | + try: |
| 107 | + PosixData = ds.search_s("ou=netgroup," + base,ldap.SCOPE_SUBTREE,"(&(objectclass=nisNetGroup)(cn=" + netgrouptomod + "))") |
| 108 | + except Exception: |
| 109 | + sys.stderr.write("There was an error while searching for the netgroup; see traceback\n") |
| 110 | + traceback.print_exc(file=sys.stderr) |
| 111 | + ds.unbind() |
| 112 | + sys.exit(1) |
| 113 | + if options.user: |
| 114 | + PosixCheckData = ds.search_s("ou=people," + base,ldap.SCOPE_SUBTREE,"(&(objectclass=posixaccount)(uid=*))") |
| 115 | + else: |
| 116 | + PosixCheckData = ds.search_s("ou=hosts," + base,ldap.SCOPE_SUBTREE,"(&(objectclass=iphost)(cn=*))") |
| 117 | + |
| 118 | + NewPosixData = copy.deepcopy(PosixData) |
| 119 | + if NewPosixData[0][1].has_key('nisNetgroupTriple'): |
| 120 | + if options.delete: |
| 121 | + if options.file: |
| 122 | + infofromfile(NewPosixData[0][1]['nisNetgroupTriple'], "delete", options.file, netgrouptype, PosixCheckData) |
| 123 | + elif "(" + nametoadd + ",,)" in NewPosixData[0][1]['nisNetgroupTriple']: |
| 124 | + NewPosixData[0][1]['nisNetgroupTriple'].remove(formattednametoadd) |
| 125 | + else: |
| 126 | + if options.file: |
| 127 | + infofromfile(NewPosixData[0][1]['nisNetgroupTriple'], "add", options.file, netgrouptype, PosixCheckData) |
| 128 | + else: |
| 129 | + NewPosixData[0][1]['nisNetgroupTriple'].append(formattednametoadd) |
| 130 | + else: |
| 131 | + if not options.delete: |
| 132 | + if options.file: |
| 133 | + NewPosixData[0][1]['nisNetgroupTriple'] = [] |
| 134 | + infofromfile(NewPosixData[0][1]['nisNetgroupTriple'], "add", options.file, netgrouptype, PosixCheckData) |
| 135 | + else: |
| 136 | + NewPosixData[0][1]['nisNetgroupTriple'] = [formattednametoadd] |
| 137 | + |
| 138 | + if PosixData == NewPosixData: |
| 139 | + if options.delete: |
| 140 | + sys.stderr.write("The host/user(s) you are trying to remove are not in the netgroup provided.\n") |
| 141 | + ds.unbind() |
| 142 | + sys.exit(1) |
| 143 | + else: |
| 144 | + sys.stderr.write("The host/user(s) you are trying to add are already in the netgroup provided.\n") |
| 145 | + ds.unbind() |
| 146 | + sys.exit(1) |
| 147 | + |
| 148 | + netgroupdn = PosixData[0][0] |
| 149 | + modlist = ldap.modlist.modifyModlist(PosixData[0][1], NewPosixData[0][1]) |
| 150 | + ds.modify_s(netgroupdn, modlist) |
| 151 | + except socket.herror: |
| 152 | + sys.stderr.write("This IP address isn't in DNS, please have it added, then try again.\n") |
| 153 | + ds.unbind() |
| 154 | + sys.exit(1) |
| 155 | + except ldap.UNWILLING_TO_PERFORM, msg: |
| 156 | + sys.stderr.write("The search returned an error. Error was: %s\n" % msg[0]["info"]) |
| 157 | + ds.unbind() |
| 158 | + sys.exit(1) |
| 159 | + except ldap.NO_SUCH_OBJECT: |
| 160 | + sys.stderr.write("The netgroup provided cannot be found, please try again.\n") |
| 161 | + ds.unbind() |
| 162 | + sys.exit(1) |
| 163 | + except ldap.TYPE_OR_VALUE_EXISTS: |
| 164 | + sys.stderr.write("The host/user you are trying to add is already in the netgroup you provided.\n") |
| 165 | + ds.unbind() |
| 166 | + sys.exit(1) |
| 167 | + except ldap.PROTOCOL_ERROR: |
| 168 | + if options.delete: |
| 169 | + sys.stderr.write("The host/user you are trying to remove is not in the netgroup you provided.\n") |
| 170 | + else: |
| 171 | + sys.stderr.write("There was an LDAP protocol error, please contact an administrator via the helpdesk.\n") |
| 172 | + ds.unbind() |
| 173 | + sys.exit(1) |
| 174 | + #except Exception: |
| 175 | + # try: |
| 176 | + # ds.unbind() |
| 177 | + # sys.stderr.write("There was a general error, please contact an administrator via the helpdesk.\n") |
| 178 | + # except Exception: |
| 179 | + # pass |
| 180 | + # sys.exit(1) |
| 181 | + |
| 182 | + # /End of stolen stuff |
| 183 | + |
| 184 | + # PosixData is a list of lists where: |
| 185 | + # index 0 of PosixData[N]: contains the distinquished name |
| 186 | + # index 1 of PosixData[N]: contains a dictionary of lists hashed by the following keys: |
| 187 | + # telephoneNumber, departmentNumber, uid, objectClass, loginShell, |
| 188 | + # uidNumber, gidNumber, sn, homeDirectory, givenName, cn |
| 189 | + |
| 190 | + if options.hosts or options.netgroups: |
| 191 | + for i in range(len(PosixData)): |
| 192 | + if options.hosts: |
| 193 | + print "hostname: " + PosixData[i][1]["cn"][0] |
| 194 | + print " IP: " + PosixData[i][1]["ipHostNumber"][0] |
| 195 | + elif options.netgroups: |
| 196 | + if not (options.showhost or options.showshares or options.showuser): |
| 197 | + print "Netgroup Name: " + PosixData[i][1]["cn"][0] |
| 198 | + else: |
| 199 | + if options.showhost: |
| 200 | + if "ou=host" in PosixData[i][0]: |
| 201 | + print "Netgroup Name: " + PosixData[i][1]["cn"][0] |
| 202 | + if options.showshares: |
| 203 | + if "ou=shares" in PosixData[i][0]: |
| 204 | + print "Netgroup Name: " + PosixData[i][1]["cn"][0] |
| 205 | + if options.showuser: |
| 206 | + if "ou=user" in PosixData[i][0]: |
| 207 | + print "Netgroup Name: " + PosixData[i][1]["cn"][0] |
| 208 | + else: |
| 209 | + if options.user: |
| 210 | + netgrouptype = "user" |
| 211 | + else: |
| 212 | + netgrouptype = "host" |
| 213 | + if options.delete: |
| 214 | + print "The " + netgrouptype + "(s) were successfully removed." |
| 215 | + else: |
| 216 | + print "The " + netgrouptype + "(s) were successfully added." |
| 217 | + |
| 218 | + ds.unbind() |
| 219 | + sys.exit(0) |
| 220 | + |
| 221 | +def checkargs(options, args): |
| 222 | + if (len(args) < 2 or len(args) > 2): |
| 223 | + if options.file and len(args) == 1: |
| 224 | + return |
| 225 | + sys.stderr.write("Invalid syntax, please see \"netgroup-mod --help\"\n") |
| 226 | + sys.exit(1) |
| 227 | + |
| 228 | +def infofromfile(list, changetype, file, netgrouptype, PosixCheckData): |
| 229 | + f = open(file) |
| 230 | + for line in f: |
| 231 | + try: |
| 232 | + line = string.strip(line) |
| 233 | + if netgrouptype == "user": |
| 234 | + nametoadd = "(," + line + ",)" |
| 235 | + else: |
| 236 | + # netgrouptype is "host" |
| 237 | + nametoadd1 = socket.gethostbyaddr(line)[0] |
| 238 | + nametoadd = "(" + nametoadd1 + ",,)" |
| 239 | + if changetype == "delete" and nametoadd in list: |
| 240 | + list.remove(nametoadd) |
| 241 | + elif changetype == "add" and nametoadd not in list: |
| 242 | + if netgrouptype == "user": |
| 243 | + if checkuserinldap(line, PosixCheckData): |
| 244 | + list.append(nametoadd) |
| 245 | + else: |
| 246 | + continue |
| 247 | + else: |
| 248 | + # netgrouptype is "host" |
| 249 | + if checkhostinldap(line, nametoadd1, PosixCheckData): |
| 250 | + list.append(nametoadd) |
| 251 | + else: |
| 252 | + continue |
| 253 | + else: |
| 254 | + if changetype == "delete": |
| 255 | + sys.stderr.write(line + " is not in the netgroup provided.\n") |
| 256 | + else: |
| 257 | + sys.stderr.write(line + " is already in the netgroup provided.\n") |
| 258 | + except socket.herror: |
| 259 | + sys.stderr.write(line + " isn't in DNS, please have it added, then try again.\n") |
| 260 | + except socket.gaierror: |
| 261 | + sys.stderr.write(line + " isn't in DNS, please have it added, then try again.\n") |
| 262 | + |
| 263 | +def checkuserinldap(user, PosixCheckData): |
| 264 | + for i in range(len(PosixCheckData)): |
| 265 | + if string.lower(PosixCheckData[i][1]['uid'][0]) == string.lower(user): |
| 266 | + return True |
| 267 | + # We just looped through all the users in LDAP. The user doesn't exist. |
| 268 | + sys.stderr.write(user + " cannot be found in LDAP.\n") |
| 269 | + return False |
| 270 | + |
| 271 | +def checkhostinldap(host, hostfromdns, PosixCheckData): |
| 272 | + for i in range(len(PosixCheckData)): |
| 273 | + if string.lower(PosixCheckData[i][1]['cn'][0]) == string.lower(hostfromdns): |
| 274 | + return True |
| 275 | + elif string.lower(PosixCheckData[i][1]['cn'][0]) == string.lower(host): |
| 276 | + sys.stderr.write(host + " matches an entry in LDAP; however, the DNS entry is different than the LDAP entry, please put in a ripken ticket (skipping)\n") |
| 277 | + return False |
| 278 | + elif string.lower(PosixCheckData[i][1]['ipHostNumber'][0]) == string.lower(host): |
| 279 | + sys.stderr.write(host + " matches an IP address in LDAP; however, the LDAP entry and the DNS entry are in conflict, please put in a ripken ticket (skipping)\n") |
| 280 | + return False |
| 281 | + # We just looped through all the hosts in LDAP. The host doesn't exist. |
| 282 | + # Since we are only checking to make sure there aren't LDAP/DNS |
| 283 | + # conflicts, this is ok. |
| 284 | + return True |
| 285 | + |
| 286 | +if __name__ == "__main__": |
| 287 | + main() |
Property changes on: trunk/tools/subversion/user-management/netgroup-mod |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 288 | + native |
Added: svn:executable |
2 | 289 | + * |
Index: trunk/tools/subversion/user-management/modify-ldap-user |
— | — | @@ -0,0 +1,135 @@ |
| 2 | +#!/usr/bin/python |
| 3 | +import sys, pwd, traceback, getpass, re, ldapsupportlib, pycurl, copy, homedirectorymanager |
| 4 | +from optparse import OptionParser |
| 5 | + |
| 6 | +try: |
| 7 | + import ldap |
| 8 | + import ldap.modlist |
| 9 | +except ImportError: |
| 10 | + sys.stderr.write("Unable to import LDAP library.\n") |
| 11 | + sys.exit(1) |
| 12 | + |
| 13 | +def main(): |
| 14 | + parser = OptionParser(conflict_handler="resolve") |
| 15 | + parser.set_usage('modify-ldap-user [options] <username> [--rename <newusername>]\nexample: modify-ldap-user --replacekeys=http://ryandlane.com/static/pubkey.key laner') |
| 16 | + |
| 17 | + ldapSupportLib = ldapsupportlib.LDAPSupportLib() |
| 18 | + ldapSupportLib.addParserOptions(parser, "scriptuser") |
| 19 | + |
| 20 | + parser.add_option("-m", "--directorymanager", action="store_true", dest="directorymanager", help="Use the Directory Manager's credentials, rather than your own") |
| 21 | + parser.add_option("--shell", action="store", dest="loginShell", help="Set the user's shell") |
| 22 | + parser.add_option("--gid", action="store", dest="gidNumber", help="Set the user's gid") |
| 23 | + parser.add_option("--uid", action="store", dest="uidNumber", help="Set the user's uid") |
| 24 | + parser.add_option("--cn", action="store", dest="cn", help="Set the user's CN") |
| 25 | + parser.add_option("--firstname", action="store", dest="givenName", help="Set the user's first name") |
| 26 | + parser.add_option("--lastname", action="store", dest="sn", help="Set the user's last name") |
| 27 | + parser.add_option("--replacekeys", action="store", dest="replaceKeyLocation", help="Replaces all of the user's keys") |
| 28 | + parser.add_option("--addkeys", action="store", dest="addKeyLocation", help="Adds keys to the user's entry") |
| 29 | + parser.add_option("--rename", action="store_true", dest="rename", help="Rename the user") |
| 30 | + (options, args) = parser.parse_args() |
| 31 | + |
| 32 | + if len(args) != 1: |
| 33 | + if options.rename and len(args) != 2: |
| 34 | + parser.error("modify-ldap-user expects exactly two arguments when using rename.") |
| 35 | + elif not options.rename: |
| 36 | + parser.error("modify-ldap-user expects exactly one argument, unless using --rename.") |
| 37 | + |
| 38 | + ldapSupportLib.setBindInfoByOptions(options, parser) |
| 39 | + |
| 40 | + base = ldapSupportLib.getBase() |
| 41 | + |
| 42 | + ds = ldapSupportLib.connect() |
| 43 | + |
| 44 | + # w00t We're in! |
| 45 | + try: |
| 46 | + username = args[0] |
| 47 | + PosixData = ds.search_s("ou=people," + base,ldap.SCOPE_SUBTREE,"(&(objectclass=inetOrgPerson)(uid=" + username + "))") |
| 48 | + if not PosixData: |
| 49 | + raise ldap.NO_SUCH_OBJECT() |
| 50 | + dn = PosixData[0][0] |
| 51 | + hdm = homedirectorymanager.HomeDirectoryManager() |
| 52 | + |
| 53 | + if options.rename: |
| 54 | + newusername = args[1] |
| 55 | + if newusername == "root": |
| 56 | + sys.stderr.write("You are not allowed to rename a user to root.\n") |
| 57 | + sys.exit(1) |
| 58 | + |
| 59 | + # Rename the entry |
| 60 | + newrdn = 'uid=' + newusername |
| 61 | + ds.rename_s(dn, newrdn) |
| 62 | + |
| 63 | + # Fix the user's home directory |
| 64 | + PosixData = ds.search_s("ou=people," + base,ldap.SCOPE_SUBTREE,"(&(objectclass=inetOrgPerson)(uid=" + newusername + "))") |
| 65 | + dn = PosixData[0][0] |
| 66 | + PosixData = PosixData[0][1] |
| 67 | + NewPosixData = copy.deepcopy(PosixData) |
| 68 | + NewPosixData['homeDirectory'] = '/home/' + newusername |
| 69 | + modlist = ldap.modlist.modifyModlist(PosixData,NewPosixData) |
| 70 | + ds.modify_s(dn, modlist) |
| 71 | + |
| 72 | + # Update the home directory |
| 73 | + hdm.moveUser(username, newusername) |
| 74 | + else: |
| 75 | + PosixData = PosixData[0][1] |
| 76 | + NewPosixData = copy.deepcopy(PosixData) |
| 77 | + if options.replaceKeyLocation: |
| 78 | + keys = hdm.fetchKeys(options.replaceKeyLocation) |
| 79 | + NewPosixData['sshPublicKey'] = keys |
| 80 | + if options.addKeyLocation: |
| 81 | + keys = hdm.fetchKeys(options.addKeyLocation) |
| 82 | + NewPosixData['sshPublicKey'].extend(keys) |
| 83 | + NewPosixData['sshPublicKey'] = hdm.uniqueKeys(NewPosixData['sshPublicKey']) |
| 84 | + if options.loginShell: |
| 85 | + NewPosixData['loginShell'] = options.loginShell |
| 86 | + if options.uidNumber: |
| 87 | + if int(options.uidNumber) < 500: |
| 88 | + sys.stderr.write("Setting uid to less than 500 is not allowed.\n") |
| 89 | + sys.exit(1) |
| 90 | + NewPosixData['uidNumber'] = options.uidNumber |
| 91 | + if options.gidNumber: |
| 92 | + NewPosixData['gidNumber'] = options.gidNumber |
| 93 | + if options.sn: |
| 94 | + NewPosixData['sn'] = options.sn |
| 95 | + if options.givenName: |
| 96 | + NewPosixData['givenName'] = options.givenName |
| 97 | + if options.cn: |
| 98 | + NewPosixData['cn'] = options.cn |
| 99 | + |
| 100 | + if PosixData == NewPosixData: |
| 101 | + sys.stderr.write("No changes to make; exiting.\n") |
| 102 | + else: |
| 103 | + modlist = ldap.modlist.modifyModlist(PosixData,NewPosixData) |
| 104 | + ds.modify_s(dn, modlist) |
| 105 | + |
| 106 | + if options.replaceKeyLocation or options.addKeyLocation: |
| 107 | + # Update the keys |
| 108 | + hdm.writeKeys(username, NewPosixData['sshPublicKey']) |
| 109 | + except ldap.UNWILLING_TO_PERFORM, msg: |
| 110 | + sys.stderr.write("LDAP was unwilling to create the user. Error was: %s\n" % msg[0]["info"]) |
| 111 | + ds.unbind() |
| 112 | + sys.exit(1) |
| 113 | + except ldap.NO_SUCH_OBJECT: |
| 114 | + sys.stderr.write("The user you are trying to modify doesn't exist.\n") |
| 115 | + ds.unbind() |
| 116 | + sys.exit(1) |
| 117 | + except ldap.PROTOCOL_ERROR: |
| 118 | + sys.stderr.write("There was an LDAP protocol error; see traceback.\n") |
| 119 | + traceback.print_exc(file=sys.stderr) |
| 120 | + ds.unbind() |
| 121 | + sys.exit(1) |
| 122 | + except Exception: |
| 123 | + try: |
| 124 | + sys.stderr.write("There was a general error, this is unexpected; see traceback.\n") |
| 125 | + traceback.print_exc(file=sys.stderr) |
| 126 | + ds.unbind() |
| 127 | + except Exception: |
| 128 | + sys.stderr.write("Also failed to unbind.\n") |
| 129 | + traceback.print_exc(file=sys.stderr) |
| 130 | + sys.exit(1) |
| 131 | + |
| 132 | + ds.unbind() |
| 133 | + sys.exit(0) |
| 134 | + |
| 135 | +if __name__ == "__main__": |
| 136 | + main() |
Property changes on: trunk/tools/subversion/user-management/modify-ldap-user |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 137 | + native |
Added: svn:executable |
2 | 138 | + * |
Index: trunk/tools/subversion/user-management/add-ldap-user |
— | — | @@ -0,0 +1,155 @@ |
| 2 | +#!/usr/bin/python |
| 3 | +import sys, pwd, traceback, getpass, re, ldapsupportlib, pycurl, homedirectorymanager |
| 4 | +from optparse import OptionParser |
| 5 | +from cStringIO import StringIO |
| 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('add-ldap-user [options] <username> <key(s)>\nexample: add-ldap-user laner http://ryandlane.com/static/pubkey.key') |
| 17 | + |
| 18 | + ldapSupportLib = ldapsupportlib.LDAPSupportLib() |
| 19 | + ldapSupportLib.addParserOptions(parser, "scriptuser") |
| 20 | + |
| 21 | + parser.add_option("-m", "--directorymanager", action="store_true", dest="directorymanager", help="Use the Directory Manager's credentials, rather than your own") |
| 22 | + parser.add_option("--shell", action="store", dest="loginShell", default="/usr/local/bin/sillyshell", help="The user's shell (default: /usr/bin/sillyshell)") |
| 23 | + parser.add_option("--gid", action="store", dest="gidNumber", default="550", help="The user's gid (default: 550)") |
| 24 | + parser.add_option("--uid", action="store", dest="uidNumber", help="The user's uid (default: next available uid)") |
| 25 | + parser.add_option("--home", action="store", dest="homeDirectory", help="The user's home directory (default /home/username)") |
| 26 | + parser.add_option("--cn", action="store", dest="cn", help="The user's CN (default: firstname + lastname, or username, if first/last not set)") |
| 27 | + parser.add_option("--firstname", action="store", dest="givenName", help="The user's first name (default: username)") |
| 28 | + parser.add_option("--lastname", action="store", dest="sn", help="The user's last name (default: username)") |
| 29 | + (options, args) = parser.parse_args() |
| 30 | + |
| 31 | + if len(args) != 2: |
| 32 | + parser.error("add-ldap-user expects exactly two arguments.") |
| 33 | + |
| 34 | + ldapSupportLib.setBindInfoByOptions(options, parser) |
| 35 | + |
| 36 | + base = ldapSupportLib.getBase() |
| 37 | + |
| 38 | + ds = ldapSupportLib.connect() |
| 39 | + |
| 40 | + # w00t We're in! |
| 41 | + try: |
| 42 | + username = args[0] |
| 43 | + if username == "root": |
| 44 | + sys.stderr.write("You are not allowed to add a root user\n") |
| 45 | + sys.exit(1) |
| 46 | + |
| 47 | + keyLocation = args[1] |
| 48 | + keys = [] |
| 49 | + if re.match('^http', keyLocation): |
| 50 | + buffer = StringIO() |
| 51 | + c = pycurl.Curl() |
| 52 | + c.setopt(c.URL, keyLocation) |
| 53 | + c.setopt(c.WRITEFUNCTION, buffer.write) |
| 54 | + c.perform() |
| 55 | + c.close() |
| 56 | + raw_keys = buffer.getvalue() |
| 57 | + else: |
| 58 | + file = open(keyLocation, 'r') |
| 59 | + raw_keys = file.readlines() |
| 60 | + for raw_key in raw_keys: |
| 61 | + if (re.match('^$', raw_key) or re.match('^#', raw_key)): |
| 62 | + continue |
| 63 | + raw_key = raw_key.strip() |
| 64 | + keys.append(raw_key) |
| 65 | + |
| 66 | + # We need to ensure the keys are unique to avoid an exception |
| 67 | + uniqueKeys = [] |
| 68 | + [uniqueKeys.append(i) for i in keys if not uniqueKeys.count(i)] |
| 69 | + keys = uniqueKeys |
| 70 | + |
| 71 | + dn = 'uid=' + username + ',ou=people,' + base |
| 72 | + uid = username |
| 73 | + objectClasses = ['person', 'organizationalPerson', 'inetorgperson', 'ldapPublicKey', 'shadowaccount', 'posixaccount', 'top'] |
| 74 | + loginShell = options.loginShell |
| 75 | + if options.homeDirectory: |
| 76 | + homeDirectory = options.homeDirectory |
| 77 | + else: |
| 78 | + homeDirectory = '/home/' + username |
| 79 | + if options.uidNumber: |
| 80 | + uidNumber = options.uidNumber |
| 81 | + if int(uidNumber) < 500: |
| 82 | + sys.stderr.write("Setting uidnumber to less than 500 is not allowed\n") |
| 83 | + sys.exit(1) |
| 84 | + else: |
| 85 | + # Find the next uid |
| 86 | + uids = [] |
| 87 | + for user in pwd.getpwall(): |
| 88 | + tmpuid = user[2] |
| 89 | + if tmpuid < 50000: |
| 90 | + uids.append(user[2]) |
| 91 | + uids.sort() |
| 92 | + uidNumber = uids.pop() |
| 93 | + uidNumber = str(uidNumber + 1) |
| 94 | + gidNumber = options.gidNumber |
| 95 | + if options.sn: |
| 96 | + sn = options.sn |
| 97 | + else: |
| 98 | + sn = username |
| 99 | + if options.givenName: |
| 100 | + givenName = options.givenName |
| 101 | + else: |
| 102 | + givenName = username |
| 103 | + if options.cn: |
| 104 | + cn = options.cn |
| 105 | + elif options.givenName and options.sn: |
| 106 | + cn = options.givenName + " " + options.sn |
| 107 | + else: |
| 108 | + cn = username |
| 109 | + |
| 110 | + userEntry = {} |
| 111 | + userEntry['uid'] = uid |
| 112 | + userEntry['objectclass'] = objectClasses |
| 113 | + userEntry['loginShell'] = loginShell |
| 114 | + userEntry['homeDirectory'] = homeDirectory |
| 115 | + userEntry['sshPublicKey'] = keys |
| 116 | + userEntry['uidNumber'] = uidNumber |
| 117 | + userEntry['gidNumber'] = gidNumber |
| 118 | + userEntry['givenName'] = givenName |
| 119 | + userEntry['sn'] = sn |
| 120 | + userEntry['cn'] = cn |
| 121 | + |
| 122 | + modlist = ldap.modlist.addModlist(userEntry) |
| 123 | + ds.add_s(dn, modlist) |
| 124 | + |
| 125 | + userdict = {uid: {"uidNumber": int(uidNumber), "gidNumber": int(gidNumber), "sshPublicKey": keys}} |
| 126 | + hdm = homedirectorymanager.HomeDirectoryManager() |
| 127 | + hdm.createHomeDir(userdict) |
| 128 | + except ldap.UNWILLING_TO_PERFORM, msg: |
| 129 | + sys.stderr.write("LDAP was unwilling to create the user. Error was: %s\n" % msg[0]["info"]) |
| 130 | + ds.unbind() |
| 131 | + sys.exit(1) |
| 132 | + except ldap.TYPE_OR_VALUE_EXISTS: |
| 133 | + sys.stderr.write("The user you are trying to add already exists.\n") |
| 134 | + traceback.print_exc(file=sys.stderr) |
| 135 | + ds.unbind() |
| 136 | + sys.exit(1) |
| 137 | + except ldap.PROTOCOL_ERROR: |
| 138 | + sys.stderr.write("There was an LDAP protocol error; see traceback.\n") |
| 139 | + traceback.print_exc(file=sys.stderr) |
| 140 | + ds.unbind() |
| 141 | + sys.exit(1) |
| 142 | + except Exception: |
| 143 | + try: |
| 144 | + sys.stderr.write("There was a general error, this is unexpected; see traceback.\n") |
| 145 | + traceback.print_exc(file=sys.stderr) |
| 146 | + ds.unbind() |
| 147 | + except Exception: |
| 148 | + sys.stderr.write("Also failed to unbind.\n") |
| 149 | + traceback.print_exc(file=sys.stderr) |
| 150 | + sys.exit(1) |
| 151 | + |
| 152 | + ds.unbind() |
| 153 | + sys.exit(0) |
| 154 | + |
| 155 | +if __name__ == "__main__": |
| 156 | + main() |
Property changes on: trunk/tools/subversion/user-management/add-ldap-user |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 157 | + native |
Added: svn:executable |
2 | 158 | + * |
Index: trunk/tools/subversion/user-management/change-ldap-passwd |
— | — | @@ -0,0 +1,71 @@ |
| 2 | +#!/usr/bin/python |
| 3 | +import sys, traceback, getpass, ldapsupportlib |
| 4 | +from optparse import OptionParser |
| 5 | + |
| 6 | +try: |
| 7 | + import ldap |
| 8 | + import ldap.modlist |
| 9 | +except ImportError: |
| 10 | + sys.stderr.write("Unable to import LDAP library.\n") |
| 11 | + sys.exit(1) |
| 12 | + |
| 13 | +def main(): |
| 14 | + parser = OptionParser(conflict_handler="resolve") |
| 15 | + parser.set_usage('change-ldap-passwd [options] <username>') |
| 16 | + |
| 17 | + ldapSupportLib = ldapsupportlib.LDAPSupportLib() |
| 18 | + ldapSupportLib.addParserOptions(parser, "scriptuser") |
| 19 | + |
| 20 | + parser.add_option("-m", "--directorymanager", action="store_true", dest="directorymanager", help="Use the Directory Manager's credentials, rather than your own") |
| 21 | + (options, args) = parser.parse_args() |
| 22 | + |
| 23 | + if len(args) != 1: |
| 24 | + parser.error("add-ldap-user expects exactly one argument.") |
| 25 | + |
| 26 | + ldapSupportLib.setBindInfoByOptions(options, parser) |
| 27 | + |
| 28 | + base = ldapSupportLib.getBase() |
| 29 | + |
| 30 | + ds = ldapSupportLib.connect() |
| 31 | + |
| 32 | + # w00t We're in! |
| 33 | + try: |
| 34 | + username = args[0] |
| 35 | + dn = 'uid=' + username + ',ou=people,' + base |
| 36 | + while True: |
| 37 | + newpass = getpass.getpass('New password: ') |
| 38 | + repeat = getpass.getpass('Repeat new password: ') |
| 39 | + if newpass != repeat: |
| 40 | + print "Passwords do no match, please try again" |
| 41 | + else: |
| 42 | + break |
| 43 | + mod_attrs = [( ldap.MOD_REPLACE, 'userPassword', newpass )] |
| 44 | + ds.modify_s(dn, mod_attrs) |
| 45 | + except ldap.UNWILLING_TO_PERFORM, msg: |
| 46 | + sys.stderr.write("LDAP was unwilling to change the user's password. Error was: %s\n" % msg[0]["info"]) |
| 47 | + ds.unbind() |
| 48 | + sys.exit(1) |
| 49 | + except ldap.NO_SUCH_OBJECT: |
| 50 | + sys.stderr.write("The user you are trying to modify does not exists.\n") |
| 51 | + ds.unbind() |
| 52 | + sys.exit(1) |
| 53 | + except ldap.PROTOCOL_ERROR: |
| 54 | + sys.stderr.write("There was an LDAP protocol error; see traceback.\n") |
| 55 | + traceback.print_exc(file=sys.stderr) |
| 56 | + ds.unbind() |
| 57 | + sys.exit(1) |
| 58 | + except Exception: |
| 59 | + try: |
| 60 | + sys.stderr.write("There was a general error, this is unexpected; see traceback.\n") |
| 61 | + traceback.print_exc(file=sys.stderr) |
| 62 | + ds.unbind() |
| 63 | + except Exception: |
| 64 | + sys.stderr.write("Also failed to unbind.\n") |
| 65 | + traceback.print_exc(file=sys.stderr) |
| 66 | + sys.exit(1) |
| 67 | + |
| 68 | + ds.unbind() |
| 69 | + sys.exit(0) |
| 70 | + |
| 71 | +if __name__ == "__main__": |
| 72 | + main() |
Property changes on: trunk/tools/subversion/user-management/change-ldap-passwd |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 73 | + native |
Added: svn:executable |
2 | 74 | + * |
Index: trunk/tools/subversion/user-management/delete-ldap-group |
— | — | @@ -0,0 +1,63 @@ |
| 2 | +#!/usr/bin/python |
| 3 | +import sys, pwd, traceback, getpass, ldapsupportlib, homedirectorymanager |
| 4 | +from optparse import OptionParser |
| 5 | + |
| 6 | +try: |
| 7 | + import ldap |
| 8 | + import ldap.modlist |
| 9 | +except ImportError: |
| 10 | + sys.stderr.write("Unable to import LDAP library.\n") |
| 11 | + sys.exit(1) |
| 12 | + |
| 13 | +def main(): |
| 14 | + parser = OptionParser(conflict_handler="resolve") |
| 15 | + parser.set_usage('delete-ldap-group [options] <username>') |
| 16 | + |
| 17 | + ldapSupportLib = ldapsupportlib.LDAPSupportLib() |
| 18 | + ldapSupportLib.addParserOptions(parser, "scriptuser") |
| 19 | + |
| 20 | + parser.add_option("-m", "--directorymanager", action="store_true", dest="directorymanager", help="Use the Directory Manager's credentials, rather than your own") |
| 21 | + (options, args) = parser.parse_args() |
| 22 | + |
| 23 | + if len(args) != 1: |
| 24 | + parser.error("delete-ldap-group expects exactly one argument.") |
| 25 | + |
| 26 | + ldapSupportLib.setBindInfoByOptions(options, parser) |
| 27 | + |
| 28 | + base = ldapSupportLib.getBase() |
| 29 | + |
| 30 | + ds = ldapSupportLib.connect() |
| 31 | + |
| 32 | + # w00t We're in! |
| 33 | + try: |
| 34 | + groupname = args[0] |
| 35 | + dn = 'cn=' + groupname + ',ou=group,' + base |
| 36 | + ds.delete_s(dn) |
| 37 | + except ldap.UNWILLING_TO_PERFORM, msg: |
| 38 | + sys.stderr.write("LDAP was unwilling to delete the group. Error was: %s\n" % msg[0]["info"]) |
| 39 | + ds.unbind() |
| 40 | + sys.exit(1) |
| 41 | + except ldap.NO_SUCH_OBJECT: |
| 42 | + sys.stderr.write("The group you are trying to delete does not exists.\n") |
| 43 | + ds.unbind() |
| 44 | + sys.exit(1) |
| 45 | + except ldap.PROTOCOL_ERROR: |
| 46 | + sys.stderr.write("There was an LDAP protocol error; see traceback.\n") |
| 47 | + traceback.print_exc(file=sys.stderr) |
| 48 | + ds.unbind() |
| 49 | + sys.exit(1) |
| 50 | + except Exception: |
| 51 | + try: |
| 52 | + sys.stderr.write("There was a general error, this is unexpected; see traceback.\n") |
| 53 | + traceback.print_exc(file=sys.stderr) |
| 54 | + ds.unbind() |
| 55 | + except Exception: |
| 56 | + sys.stderr.write("Also failed to unbind.\n") |
| 57 | + traceback.print_exc(file=sys.stderr) |
| 58 | + sys.exit(1) |
| 59 | + |
| 60 | + ds.unbind() |
| 61 | + sys.exit(0) |
| 62 | + |
| 63 | +if __name__ == "__main__": |
| 64 | + main() |
Property changes on: trunk/tools/subversion/user-management/delete-ldap-group |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 65 | + native |
Added: svn:executable |
2 | 66 | + * |
Index: trunk/tools/subversion/user-management/ldapsupportlib.py |
— | — | @@ -0,0 +1,120 @@ |
| 2 | +#!/usr/bin/python |
| 3 | +import os, traceback, getpass, sys |
| 4 | + |
| 5 | +try: |
| 6 | + import ldap |
| 7 | +except ImportError: |
| 8 | + sys.stderr.write("Unable to import LDAP library.\n") |
| 9 | + sys.exit(1) |
| 10 | + |
| 11 | +class LDAPSupportLib: |
| 12 | + |
| 13 | + def __init__(self): |
| 14 | + self.base = self.getLdapInfo("base") |
| 15 | + self.ldapHost = self.getLdapInfo("uri") |
| 16 | + self.sslType = self.getLdapInfo("ssl") |
| 17 | + self.binddn = self.getLdapInfo("binddn") |
| 18 | + self.bindpw = self.getLdapInfo("bindpw") |
| 19 | + self.defaults = {} |
| 20 | + |
| 21 | + def addParserOptions(self, parser, default="proxy"): |
| 22 | + parser.add_option("-s", "--self", action="store_true", dest="useself", help="Use your credentials") |
| 23 | + parser.add_option("-D", "--bindas", action="store", dest="bindas", help="Specify user to bind as") |
| 24 | + parser.add_option("-m", "--directorymanager", action="store_true", dest="directorymanager", help="Use the Directory Manager's credentials") |
| 25 | + parser.add_option("--scriptuser", action="store_true", dest="scriptuser", help="Use the scriptusers' credentials") |
| 26 | + self.defaults['authuser'] = "proxy" |
| 27 | + if ( default == "user" ): |
| 28 | + self.defaults['authuser'] = "user" |
| 29 | + if ( default == "Directory Manager" ): |
| 30 | + self.defaults['authuser'] = "Directory Manager" |
| 31 | + if ( default == "scriptuser" ): |
| 32 | + self.defaults['authuser'] = "scriptuser" |
| 33 | + |
| 34 | + def getUsers(self, ds, username): |
| 35 | + PosixData = ds.search_s("ou=people," + self.base,ldap.SCOPE_SUBTREE,"(&(objectclass=inetOrgPerson)(uid=" + username + "))") |
| 36 | + return PosixData |
| 37 | + |
| 38 | + def getKeys(self, ds, username): |
| 39 | + user = self.getUsers(ds, username) |
| 40 | + if 'sshPublicKey' in user[0][1].keys(): |
| 41 | + return user[1]['sshPublicKey'] |
| 42 | + else: |
| 43 | + return [] |
| 44 | + |
| 45 | + def setHost(self, host): |
| 46 | + self.ldapHost = host |
| 47 | + |
| 48 | + def setBase(self, base): |
| 49 | + self.base = base |
| 50 | + |
| 51 | + def setBindInfoByOptions(self, options, parser): |
| 52 | + if not (options.useself or options.directorymanager): |
| 53 | + if self.defaults['authuser'] == "user": |
| 54 | + options.useself = True |
| 55 | + if self.defaults['authuser'] == "Directory Manager": |
| 56 | + options.directorymanager = True |
| 57 | + if self.defaults['authuser'] == "scriptuser": |
| 58 | + options.scriptuser = True |
| 59 | + if options.useself: |
| 60 | + self.binddn = "uid=" + os.environ['USER'] + ",ou=people," + self.base |
| 61 | + self.bindpw = getpass.getpass() |
| 62 | + elif options.directorymanager: |
| 63 | + self.binddn = "cn=Directory Manager" |
| 64 | + self.bindpw = getpass.getpass() |
| 65 | + elif options.bindas: |
| 66 | + self.binddn = "uid=" + options.bindas + ",ou=people," + self.base |
| 67 | + self.bindpw = getpass.getpass() |
| 68 | + elif options.scriptuser: |
| 69 | + self.binddn = self.getLdapInfo('USER', '/etc/ldap/.ldapscriptrc') |
| 70 | + self.bindpw = self.getLdapInfo('PASS', '/etc/ldap/.ldapscriptrc') |
| 71 | + |
| 72 | + def setBindDN(self, binddn): |
| 73 | + self.binddn = binddn |
| 74 | + |
| 75 | + def setBindPW(self, bindpw): |
| 76 | + self.bindpw = bindpw |
| 77 | + |
| 78 | + def getBase(self): |
| 79 | + return self.base |
| 80 | + |
| 81 | + def getHost(self): |
| 82 | + return self.ldapHost |
| 83 | + |
| 84 | + def getLdapInfo(self, attr, conffile="/etc/ldap.conf"): |
| 85 | + f = open(conffile) |
| 86 | + for line in f: |
| 87 | + if line.split()[0] == attr: |
| 88 | + return line.split()[1] |
| 89 | + break |
| 90 | + |
| 91 | + def connect(self): |
| 92 | + try: |
| 93 | + ds = ldap.initialize(self.ldapHost) |
| 94 | + ds.protocol_version=ldap.VERSION3 |
| 95 | + if self.sslType == "start_tls": |
| 96 | + ds.start_tls_s() |
| 97 | + except Exception: |
| 98 | + sys.stderr.write("Unable to connect to LDAP host: %s\n" % self.ldapHost) |
| 99 | + traceback.print_exc(file=sys.stderr) |
| 100 | + sys.exit(1) |
| 101 | + |
| 102 | + try: |
| 103 | + ds.simple_bind_s(self.binddn,self.bindpw) |
| 104 | + return ds |
| 105 | + except ldap.CONSTRAINT_VIOLATION: |
| 106 | + sys.stderr.write("You typed your password incorrectly too many times, and are now locked out. Please try again later.\n") |
| 107 | + sys.exit(1) |
| 108 | + except ldap.INVALID_DN_SYNTAX: |
| 109 | + sys.stderr.write("The bind DN is incorrect... \n") |
| 110 | + sys.exit(1) |
| 111 | + except ldap.NO_SUCH_OBJECT: |
| 112 | + sys.stderr.write("Unable to locate the bind DN account.\n") |
| 113 | + sys.exit(1) |
| 114 | + except ldap.UNWILLING_TO_PERFORM, msg: |
| 115 | + sys.stderr.write("The LDAP server was unwilling to perform the action requested.\nError was: %s\n" % msg[0]["info"]) |
| 116 | + sys.exit(1) |
| 117 | + except ldap.INVALID_CREDENTIALS: |
| 118 | + sys.stderr.write("Password incorrect.\n") |
| 119 | + #traceback.print_exc(file=sys.stderr) |
| 120 | + sys.exit(1) |
| 121 | + |
Property changes on: trunk/tools/subversion/user-management/ldapsupportlib.py |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 122 | + native |
Index: trunk/tools/subversion/user-management/delete-ldap-user |
— | — | @@ -0,0 +1,68 @@ |
| 2 | +#!/usr/bin/python |
| 3 | +import sys, pwd, traceback, getpass, ldapsupportlib, homedirectorymanager |
| 4 | +from optparse import OptionParser |
| 5 | + |
| 6 | +try: |
| 7 | + import ldap |
| 8 | + import ldap.modlist |
| 9 | +except ImportError: |
| 10 | + sys.stderr.write("Unable to import LDAP library.\n") |
| 11 | + sys.exit(1) |
| 12 | + |
| 13 | +def main(): |
| 14 | + parser = OptionParser(conflict_handler="resolve") |
| 15 | + parser.set_usage('delete-ldap-user [options] <username>') |
| 16 | + |
| 17 | + ldapSupportLib = ldapsupportlib.LDAPSupportLib() |
| 18 | + ldapSupportLib.addParserOptions(parser, "scriptuser") |
| 19 | + |
| 20 | + parser.add_option("-m", "--directorymanager", action="store_true", dest="directorymanager", help="Use the Directory Manager's credentials, rather than your own") |
| 21 | + parser.add_option("--no-delete-home", action="store_true", dest="nodeletehome", help="Don't delete the user's home directory") |
| 22 | + (options, args) = parser.parse_args() |
| 23 | + |
| 24 | + if len(args) != 1: |
| 25 | + parser.error("add-ldap-user expects exactly one argument.") |
| 26 | + |
| 27 | + ldapSupportLib.setBindInfoByOptions(options, parser) |
| 28 | + |
| 29 | + base = ldapSupportLib.getBase() |
| 30 | + |
| 31 | + ds = ldapSupportLib.connect() |
| 32 | + |
| 33 | + # w00t We're in! |
| 34 | + try: |
| 35 | + username = args[0] |
| 36 | + dn = 'uid=' + username + ',ou=people,' + base |
| 37 | + ds.delete_s(dn) |
| 38 | + |
| 39 | + if not options.nodeletehome: |
| 40 | + hdm = homedirectorymanager.HomeDirectoryManager() |
| 41 | + hdm.deleteUser(username) |
| 42 | + except ldap.UNWILLING_TO_PERFORM, msg: |
| 43 | + sys.stderr.write("LDAP was unwilling to delete the user. Error was: %s\n" % msg[0]["info"]) |
| 44 | + ds.unbind() |
| 45 | + sys.exit(1) |
| 46 | + except ldap.NO_SUCH_OBJECT: |
| 47 | + sys.stderr.write("The user you are trying to delete does not exists.\n") |
| 48 | + ds.unbind() |
| 49 | + sys.exit(1) |
| 50 | + except ldap.PROTOCOL_ERROR: |
| 51 | + sys.stderr.write("There was an LDAP protocol error; see traceback.\n") |
| 52 | + traceback.print_exc(file=sys.stderr) |
| 53 | + ds.unbind() |
| 54 | + sys.exit(1) |
| 55 | + except Exception: |
| 56 | + try: |
| 57 | + sys.stderr.write("There was a general error, this is unexpected; see traceback.\n") |
| 58 | + traceback.print_exc(file=sys.stderr) |
| 59 | + ds.unbind() |
| 60 | + except Exception: |
| 61 | + sys.stderr.write("Also failed to unbind.\n") |
| 62 | + traceback.print_exc(file=sys.stderr) |
| 63 | + sys.exit(1) |
| 64 | + |
| 65 | + ds.unbind() |
| 66 | + sys.exit(0) |
| 67 | + |
| 68 | +if __name__ == "__main__": |
| 69 | + main() |
Property changes on: trunk/tools/subversion/user-management/delete-ldap-user |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 70 | + native |
Added: svn:executable |
2 | 71 | + * |
Index: trunk/tools/subversion/user-management/ldaplist |
— | — | @@ -0,0 +1,214 @@ |
| 2 | +#!/usr/bin/python |
| 3 | +import sys, traceback, re, ldapsupportlib |
| 4 | +from optparse import OptionParser |
| 5 | + |
| 6 | +try: |
| 7 | + import ldap |
| 8 | +except ImportError: |
| 9 | + sys.stderr.write("Unable to import LDAP library.\n") |
| 10 | + sys.exit(1) |
| 11 | + |
| 12 | +def main(): |
| 13 | + "An application that implements the functionality of Solaris's ldaplist." |
| 14 | + |
| 15 | + parser = OptionParser(conflict_handler="resolve") |
| 16 | + parser.set_usage("ldaplist [options] [database] [object-name]\n\nexample: ldaplist -l passwd ldap_user") |
| 17 | + |
| 18 | + ldapSupportLib = ldapsupportlib.LDAPSupportLib() |
| 19 | + ldapSupportLib.addParserOptions(parser) |
| 20 | + |
| 21 | + parser.add_option("-v", "--verbose", action="store_true", dest="verbose", help="Show the database and search filter used for this search") |
| 22 | + parser.add_option("-l", "--longlisting", action="store_true", dest="longlisting", help="List all the attributes for each entry matching the search criteria. By default, ldaplist lists only the Distiguished Name of the entries found.") |
| 23 | + parser.add_option("-h", action="store_true", dest="helpme", help="Show available databases to search") |
| 24 | + parser.add_option("-d", "--showdatabase", action="store_true", dest="showdatabase", help="Show the base dn being used for this database") |
| 25 | + parser.add_option("-a", "--showattributes", dest="showattributes", help="Show the given attributes") |
| 26 | + parser.add_option("-r", "--recursive", action="store_true", dest="recursive", help="Recurse netgroups") |
| 27 | + parser.add_option("--like", action="store_true", dest="like", help="Search for objects that equal or sound like [object-name]") |
| 28 | + (options, args) = parser.parse_args() |
| 29 | + |
| 30 | + ldapSupportLib.setBindInfoByOptions(options, parser) |
| 31 | + |
| 32 | + base = ldapSupportLib.getBase() |
| 33 | + |
| 34 | + objectbasedns = {"base":base, "passwd":"ou=people,"+base, "group":"ou=group,"+base, "netgroup":"ou=netgroup,"+base, "hosts":"ou=hosts,"+base, "automount":base, "auto_*":"nisMapName=auto_AUTO,"+base, "uids":"ou=uids,"+base} |
| 35 | + objectdefaulttypes = {"base":"none", "passwd":"uid", "group":"cn", "netgroup":"cn", "hosts":"cn", "automount":"nisMapName", "auto_*":"cn", "uids":"cn"} |
| 36 | + objectobjectclasses = {"base":"none", "passwd":"posixaccount", "group":"posixgroup", "netgroup":"nisNetGroup", "hosts":"iphost", "automount":"nisMap", "auto_*":"nisObject", "uids":"inetOrgPerson"} |
| 37 | + |
| 38 | + if options.showdatabase: |
| 39 | + showdatabase(objectbasedns, args) |
| 40 | + sys.exit() |
| 41 | + |
| 42 | + if options.helpme: |
| 43 | + print "" |
| 44 | + print 'database'.ljust(17) + 'default type'.ljust(20) + 'objectclass' |
| 45 | + print '============='.ljust(17) + '================='.ljust(20)+ '=============' |
| 46 | + |
| 47 | + for a, b, c in zip(objectbasedns.keys(), objectdefaulttypes.values(), objectobjectclasses.values()): |
| 48 | + print '%s%s%s' % (a.ljust(17), b.ljust(20), c) |
| 49 | + sys.exit() |
| 50 | + |
| 51 | + if len(args) >= 1: |
| 52 | + if args[0].find('auto_') != -1: |
| 53 | + objectbasedns["auto_*"] = objectbasedns["auto_*"].replace("auto_AUTO", args[0]) |
| 54 | + searchkeysave = args[0] |
| 55 | + args[0] = "auto_*" |
| 56 | + if objectbasedns.has_key(args[0]): |
| 57 | + database = args[0] |
| 58 | + base = objectbasedns[args[0]] |
| 59 | + objectclass = objectobjectclasses[args[0]] |
| 60 | + attribute = objectdefaulttypes[args[0]] |
| 61 | + if len(args) > 1: |
| 62 | + searchlist = args |
| 63 | + del searchlist[0] |
| 64 | + first = True |
| 65 | + for key in searchlist: |
| 66 | + if first == True: |
| 67 | + searchkey = key |
| 68 | + first = False |
| 69 | + else: |
| 70 | + searchkey = searchkey + " " + key |
| 71 | + #elif args[0] == "auto_*": |
| 72 | + #searchkey = searchkeysave |
| 73 | + else: |
| 74 | + searchkey = "*" |
| 75 | + else: |
| 76 | + print 'The database you selected does not exist. Please use "ldaplist -h" to see available databases.' |
| 77 | + sys.exit(1) |
| 78 | + else: |
| 79 | + database = "base" |
| 80 | + objectclass = "*" |
| 81 | + attribute = "" |
| 82 | + |
| 83 | + ds = ldapSupportLib.connect() |
| 84 | + |
| 85 | + # w00t We're in! |
| 86 | + try: |
| 87 | + if database == "uids": |
| 88 | + options.like = True |
| 89 | + if options.showattributes is not None: |
| 90 | + options.showattributes = options.showattributes + " cn uid departmentNumber employeeType seeAlso" |
| 91 | + else: |
| 92 | + options.showattributes = "cn uid departmentNumber employeeType seeAlso" |
| 93 | + options.longlisting = True |
| 94 | + if options.like and searchkey != "*": |
| 95 | + searchoperator = "~=" |
| 96 | + else: |
| 97 | + searchoperator = "=" |
| 98 | + if attribute != "": |
| 99 | + attrlist = [] |
| 100 | + if options.showattributes is not None: |
| 101 | + attrlist = re.split(" ", options.showattributes) |
| 102 | + if options.verbose: |
| 103 | + if options.showattributes is None: |
| 104 | + attributes = "" |
| 105 | + else: |
| 106 | + attributes = options.showattributes |
| 107 | + print "+++ database=" + database |
| 108 | + print "+++ filter=(&(objectclass=" + objectclass + ")(" + attribute + searchoperator + searchkey + ")) " + attributes |
| 109 | + PosixData = ds.search_s(base,ldap.SCOPE_SUBTREE,"(&(objectclass=" + objectclass + ")(" + attribute + searchoperator + searchkey + "))",attrlist) |
| 110 | + else: |
| 111 | + if options.verbose: |
| 112 | + print "(objectclass=" + objectclass + ")" |
| 113 | + PosixData = ds.search_s(base,ldap.SCOPE_SUBTREE,"(objectclass=" + objectclass + ")") |
| 114 | + except ldap.NO_SUCH_OBJECT: |
| 115 | + sys.stderr.write("Object not found. If you are trying to use * in your search, make sure that you wrap your string in single quotes to avoid shell expansion.\n") |
| 116 | + ds.unbind() |
| 117 | + sys.exit(1) |
| 118 | + except ldap.PROTOCOL_ERROR: |
| 119 | + sys.stderr.write("The search returned a protocol error, this shouldn't ever happen, please submit a trouble ticket.\n") |
| 120 | + ds.unbind() |
| 121 | + sys.exit(1) |
| 122 | + except Exception: |
| 123 | + sys.stderr.write("The search returned an error.\n") |
| 124 | + ds.unbind() |
| 125 | + sys.exit(1) |
| 126 | + |
| 127 | + PosixData.sort() |
| 128 | + # /End of stolen stuff |
| 129 | + |
| 130 | + # PosixData is a list of lists where: |
| 131 | + # index 0 of PosixData[N]: contains the distinquished name |
| 132 | + # index 1 of PosixData[N]: contains a dictionary of lists hashed by the following keys: |
| 133 | + # telephoneNumber, departmentNumber, uid, objectClass, loginShell, |
| 134 | + # uidNumber, gidNumber, sn, homeDirectory, givenName, cn |
| 135 | + |
| 136 | + if options.recursive: |
| 137 | + members_array = [] |
| 138 | + triples = [] |
| 139 | + |
| 140 | + # get the members and triples from the entry we are looking for |
| 141 | + for i in range(len(PosixData)): |
| 142 | + if 'memberNisNetgroup' in PosixData[i][1]: |
| 143 | + members_array.extend(PosixData[i][1]['memberNisNetgroup']) |
| 144 | + if 'nisNetgroupTriple' in PosixData[i][1]: |
| 145 | + triples.extend(PosixData[i][1]['nisNetgroupTriple']) |
| 146 | + |
| 147 | + # get triples from any sub-members |
| 148 | + triples = recursenetgroups(base, ds, members_array, triples) |
| 149 | + |
| 150 | + for str in triples: |
| 151 | + print str |
| 152 | + |
| 153 | + # clean up |
| 154 | + ds.unbind() |
| 155 | + sys.exit(0) |
| 156 | + |
| 157 | + for i in range(len(PosixData)): |
| 158 | + print "" |
| 159 | + if not options.longlisting: |
| 160 | + print "dn: " + PosixData[i][0] |
| 161 | + else: |
| 162 | + print "dn: " + PosixData[i][0] |
| 163 | + for (k,v) in PosixData[i][1].items(): |
| 164 | + if len(v) > 1: |
| 165 | + for v2 in v: |
| 166 | + print " %s: %s" % (k,v2) |
| 167 | + else: |
| 168 | + print " %s: %s" % (k,v[0]) |
| 169 | + |
| 170 | + ds.unbind() |
| 171 | + |
| 172 | +def showdatabase(objectbasedns, args): |
| 173 | + print "" |
| 174 | + if len(args) < 1: |
| 175 | + print objectbasedns["base"] |
| 176 | + else: |
| 177 | + if args[0].find('auto_') != -1: |
| 178 | + objectbasedns["auto_*"] = objectbasedns["auto_*"].replace("auto_AUTO", args[0]) |
| 179 | + args[0] = "auto_*" |
| 180 | + if objectbasedns.has_key(args[0]): |
| 181 | + print objectbasedns[args[0]] |
| 182 | + else: |
| 183 | + print "Database " + args[0] + " not found, use ldaplist -h to list database types." |
| 184 | + |
| 185 | +def recursenetgroups(base, ds, members_array, triples, oldmembers=[]): |
| 186 | + # Base case. This netgroup has no netgroup members. |
| 187 | + if members_array == []: |
| 188 | + return triples |
| 189 | + |
| 190 | + # members_array is the total list of netgroup members from the previous search. |
| 191 | + for member in members_array: |
| 192 | + if member in oldmembers: |
| 193 | + # ensure we don't follow infinite recursion loops |
| 194 | + members_array.remove(member) |
| 195 | + continue |
| 196 | + else: |
| 197 | + # add this member to the oldmembers list to avoid infinite recursion loops |
| 198 | + oldmembers.extend(member) |
| 199 | + |
| 200 | + # we need to remove the member to avoid infinite recursion |
| 201 | + members_array.remove(member) |
| 202 | + |
| 203 | + # get the triples and members for this member, and add them to the current members list |
| 204 | + PosixData = ds.search_s(base,ldap.SCOPE_SUBTREE,"(&(objectclass=nisNetgroup)(cn=" + member + "))") |
| 205 | + for data in PosixData: |
| 206 | + if 'nisNetgroupTriple' in data[1]: |
| 207 | + triples.extend(data[1]['nisNetgroupTriple']) |
| 208 | + if 'memberNisNetgroup' in data[1]: |
| 209 | + members_array.extend(data[1]['memberNisNetgroup']) |
| 210 | + |
| 211 | + # Recurse iteratively (tail recursion) |
| 212 | + return recursenetgroups(base, ds, members_array, triples, oldmembers) |
| 213 | + |
| 214 | +if __name__ == "__main__": |
| 215 | + main() |
Property changes on: trunk/tools/subversion/user-management/ldaplist |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 216 | + native |
Added: svn:executable |
2 | 217 | + * |