#!/usr/bin/env python # coding: utf8 from gluon import * import re import smtplib import tempfile import ssam_utils import ttsd.ldaputils import ttsd.ldaputils.ad from ttsd.ldaputils import StudentConfiguration import ldap debug_on = False def debug(message): if debug_on: print message elementary_schools = ['104', '115', '161', '192', '210', '224', '228', '235', '246', '257', '994'] secondary_schools = ['085', '090', '094', '095', '345', '353', '373', '622', '632'] other_locations = ['096', '097', '777', '806', '900', '980', '990', '991', '999'] substitute_codes = ['100', '888', '000'] def search(query, attrs=['cn', 'givenname', 'lastname', 'mail', 'employeeid']): # {{{1 search_attrs = [] for a in attrs: filter = '(%s=*%%s*)' if a.lower() == 'employeeid': # match exactly filter = '(%s=%%s)' search_attrs.append(filter % a) f = [] attr_filter = '(|' + ''.join(search_attrs) + ')' # (|(a=*%s*)(b=*%s*)) for q in query.split(): f.append(attr_filter % tuple([q for i in range(len(search_attrs))])) filter = '(&(!(objectclass=computer))(&%s))' % ''.join(f) accounts = {} accounts['ttsd.k12.or.us'] = ttsd.ldaputils.ad.search(filter) accounts['student.ttsd.k12.or.us'] = ttsd.ldaputils.ad.search(filter, configuration=StudentConfiguration()) return accounts # }}}1 def format_search_results(accounts): # {{{1 results = [] for domain in accounts.keys(): account_data = [P(A('%s, %s (%s)' % (account.get('sn'), account.get('givenname'), account.get('samaccountname')), _href=URL('ad', 'view', vars=dict(dn=account.dn))), UL(LI('Email: %s' % account.get('mail')) )) for account in accounts[domain]] results.extend(DIV(H2('Active Directory for %s' % domain), P('%s result(s)' % len(accounts[domain])), *account_data )) return results # }}}1 def format_search_results_(accounts): # {{{1 results = {} for domain in accounts.keys(): for account in accounts[domain]: name = '%s, %s (%s)' % (account.get('sn'), account.get('givenname'), account.get('samaccountname')) results[name] = results.get(name, {}) view = A('Active Directory (%s)' % domain, _href=URL('ad', 'view', vars=dict(dn=account.dn))) results[name]['view'] = results[name].get('view', []) results[name]['view'].append(view) passwd = A('Change Password', _href=URL(vars=dict(u=account.get('samaccountname')))) results[name]['passwd'] = passwd rows = [] for key in results.keys(): tr = TR(TD(name)) for view in results[key]['view']: tr.append(TD(view)) tr.append(results[key]['passwd']) rows.append(tr) return tr # }}}1 def change_password(dn, password): # {{{1 accounts = [] filter = '(distinguishedname=%s)' % dn configuration = ttsd.ldaputils.StaffConfiguration() accounts = ttsd.ldaputils.ad.search(filter, configuration=configuration) if not accounts: configuration = ttsd.ldaputils.StudentConfiguration() accounts.extend(ttsd.ldaputils.ad.search(filter, configuration=configuration)) # using a dn should result in one account account = accounts[0] account.unicodePwd = ttsd.ldaputils.ad.prepare_ad_password(password) ttsd.ldaputils.ad.modify(account, configuration=configuration) # }}}1 def format_changes(account): # {{{1 # name, samaccountname, dn, changes data = {'name': account.get('displayName'), 'samaccountname': account.get('sAMAccountName'), 'dn': account.dn, 'changes': '', } for mod, key, values in account.get_modlist(): if mod == ldap.MOD_ADD: for value in values: data['changes'] += '%10s %s: %s\n' % ('adding', key, value) elif mod == ldap.MOD_REPLACE: data['changes'] += '%10s %s\n' % ('changing', key) if key.lower() != 'unicodepwd': try: original = account.original_attributes[key] if len(original) == 1: original = original[0] data['changes'] += '%10s: %s\n' % ('from', original) except KeyError: # Ignore if we have no original. pass try: data['changes'] += '%10s: %s\n' % ('to', account.get(key)) except KeyError: # Ignore if we have no new value. pass elif mod == ldap.MOD_DELETE: for value in values: data['changes'] += '%10s %s: %s\n' % ('deleting', key, value) return '%(name)s: %(samaccountname)s\n %(dn)s\n%(changes)s' % data # }}}1 def send_email(created_accounts, updated_accounts, name_changes=None, is_student=False, dry=False): # {{{1 mail = current.mail # TODO: removed hard coded information rcpt_to = 'chanson@ttsd.k12.or.us' #rcpt_to = ['sysadmin', 'smooney'] if dry: rcpt_to = 'chanson@ttsd.k12.or.us' subject = 'Staff Active Directory Update' if is_student: subject = 'Student Active Directory Update' message = '' if updated_accounts: message = message + "Updated accounts:\n\n" for info in updated_accounts: message = message + info + "\n" else: message = message + "No existing account changes.\n\n" if created_accounts: message = message + "New accounts:\n\n" for info in created_accounts: message = message + info + "\n" else: message = message + "No new accounts.\n\n" if name_changes: message = message + "Potential name changes:\n\n" for info in name_changes: message = message + info + "\n" else: message = message + "No name changes required.\n\n" mail.send(rcpt_to, subject, message) # }}}1 def update_ssam_record(record, account): # {{{1 """Given a staff or student record and its Active Directory account, create or update a staff or student record in SSAM. Return the AD record. """ db = current.db record_type = 'student' record_ad = 'student_ad' if record.has_key('staff_ad'): record_type = 'staff' record_ad = 'staff_ad' ad_data = dict(samaccountname=account.get('samaccountname'), dn=account.dn, enabled=account.is_enabled(), expired=account.is_expired()) ad_record = record.get(record_ad).select().first() ad_record_id = 0 try: if ad_record: ad_record_id = ad_record.id changes = ssam_utils.get_changes(ad_record, ad_data) if changes: ad_record.update_record(**changes) else: ad_data[record_type] = record ad_record_id = db.get(record_ad).insert(**ad_data) except Exception, e: db.error_log.insert(event_name='ad.update_ssam_record: %s record id: %s' % (record_type, record.id), message=e.message) db.commit() return db(db.get(record_ad).id==ad_record_id).select().first() # }}}1 def set_password(account, password, configuration): # {{{1 # Came up with an alternate plan. Commented out temporary file code: # 2012.02.24. ## Create a temporary file to override the configuration file used in ## StudentConfiguration. #tmpconfig = tempfile.TemporaryFile() #tmpconfig.write('[%s]\n' % configuration.section) #tmpconfig.write('host = %s\n' % configuration.get('host')) #tmpconfig.write('binddn = %s\n' % account.get('userPrincipalName')) #tmpconfig.write('passwd = %s\n' % password) #tmpconfig.write('searchbase = %s\n' % configuration.get('searchbase')) ## Set the file back to the beginning. #tmpconfig.seek(0) #configuration.configuration.readfp(tmpconfig) # Our Configuration object has a ConfigParser named "configuration" # (probably not the best choice). We can set the values in the ConfigParser # instead of creating a temporary file. configuration.configuration.set(configuration.section, 'binddn', account.get('userPrincipalName')) configuration.configuration.set(configuration.section, 'passwd', password) try: # Authenticate as the student to see if the SIS password matches the # current active directory password. Change the password if # authentication fails. connection = ttsd.ldaputils.ad.get_connection(configuration=configuration) except ldap.INVALID_CREDENTIALS: # http://support.microsoft.com/kb/906305/en-us # ...domain users can use their old password to access the network for # one hour after the password is changed. account.unicodePwd = ttsd.ldaputils.ad.prepare_ad_password(password) return account # }}}1 # ----------------- # Student Functions # ----------------- def delete_or_suspend_student(record, dry=False): # {{{1 """Given a SSAM student withdrawn record, delete or suspend the student AD account. Return a tuple of the samaccountname and the delete or suspend message. """ db = current.db ad_connection = ttsd.ldaputils.ad.get_connection(configuration=StudentConfiguration()) accounts = ttsd.ldaputils.ad.search('employeeid=%s' % record.student_id, connection=ad_connection) info = None account = None samaccountname = None if not accounts: info = 'No Active Directory account found for %s' % record.student_id ssam_utils.update_student_withdrawn_status(record, 'AD account not found') else: if len(accounts) == 2: # should only happen in school years 2008, 2009, 2010 for a in accounts: if 'ou=tigardhs' not in a.dn.lower(): account = a elif len(accounts) != 1: # shouldn't happen # TODO: log pass else: account = accounts.pop() if account: data = {'name': account.get('displayName'), 'samaccountname': account.get('sAMAccountName'), 'dn': account.dn, 'location': account.get('physicalDeliveryOfficeName'), } samaccountname = account.get('sAMAccountName') if record.action == 'delete': if dry: print 'DRY: delete AD account for %s' % account.get('displayName') else: ttsd.ldaputils.ad.delete(account, connection=ad_connection) ssam_utils.update_student_withdrawn_status(record, 'AD account deleted') # TODO: delete SSAM records info = 'Deleted: Active Directory account %(name)s: %(samaccountname)s' % data elif record.action == 'suspend': if dry: print 'DRY: suspend AD account for %s' % account.get('displayName') else: account.useraccountcontrol = str(int(account.get('useraccountcontrol')) | account.ACCOUNTDISABLE) ttsd.ldaputils.ad.modify(account, connection=ad_connection) ssam_utils.update_student_withdrawn_status(record, 'AD account suspended') info = 'Suspended: Active Directory account %(name)s: %(samaccountname)s' % data db.commit() return samaccountname, info # }}}1 def modify_or_add_student(record, dry=False): # {{{1 """Given a SSAM student record, modify or add a student AD account. Return a tuple of the account, the create info and the update info. The create and update info are information strings about what was done. """ if record.location == 'Unknown': # TODO: log return None ad_connection = ttsd.ldaputils.ad.get_connection(configuration=StudentConfiguration()) accounts = ttsd.ldaputils.ad.search('employeeid=%s' % record.student_id, connection=ad_connection) created_info = None updated_info = None account = None if not accounts: # create a new active directory account for count in range(11): debug('create_student_account: %s - %s' % (record.student_id, count)) try: account = create_student_account(record, count) # name, samaccountname, dn, location data = {'name': account.get('displayName'), 'samaccountname': account.get('sAMAccountName'), 'dn': account.dn, 'location': account.get('physicalDeliveryOfficeName'), } created_info = '%(name)s: %(samaccountname)s\n %(dn)s\n %(location)s\n' % data if dry: print 'DRY: create AD account for %s' % account.get('displayName') else: account = ttsd.ldaputils.ad.add(account, connection=ad_connection) break except ldap.ALREADY_EXISTS, e: # Try again continue except Exception, e: raise e # TODO: log failed create else: # update an existing active directory account if len(accounts) == 2: # should only happen in school years 2008, 2009, 2010 for a in accounts: if 'ou=tigardhs' not in a.dn.lower(): account = a elif len(accounts) != 1: # shouldn't happen # TODO: log pass else: account = accounts.pop() if account: for count in range(11): debug('update_student_account: %s - %s' % (record.student_id, count)) try: updated_account = update_student_account(account, record, ad_connection, count) if updated_account: if updated_account.get_modlist(): updated_info = format_changes(updated_account) if dry: print 'DRY: modify AD account for %s' % account.get('displayName') else: account = ttsd.ldaputils.ad.modify(updated_account, connection=ad_connection) break else: debug('No changes to %s.' % account.get('samaccountname')) break else: debug('Need to increment count for %s.' % account.get('samaccountname')) except ldap.ALREADY_EXISTS, e: # Try again debug('%s already exists.' % account.get('samaccountname')) continue except Exception, e: raise e # TODO: log failed update return account, created_info, updated_info # }}}1 def create_student_username(record, count=0): # {{{1 # clean data gradyear = str(record.grad_year)[-2:] lastname = re.sub("[ .'/]", "", record.last_name.lower()) first_initial = record.first_name[0].lower() # create username sAMAccountName = "%s%s%s" % (gradyear, lastname, first_initial) if count > 0: sAMAccountName = "%s%s" % (sAMAccountName, count) debug("trying ... %s" % sAMAccountName) # pre-Windows 2000 login name has 20 character limit while len(sAMAccountName) > 20: lastname = lastname[:len(lastname) - 1] sAMAccountName = "%s%s%s" % (gradyear, lastname, first_initial) if count > 0: sAMAccountName = "%s%s" % (sAMAccountName, count) return sAMAccountName # }}}1 def set_student_attributes(account, record): # {{{1 account.sn = record.last_name account.givenName = record.first_name displayName = '%s, %s (%s)' % (record.last_name, record.first_name, record.grad_year) account.displayName = displayName sAMAccountName = account.get('sAMAccountName').lower() account.mail = '%s@ttsdstudents.org' % sAMAccountName userPrincipalName = '%s@ttsdstudents.org' % sAMAccountName account.userPrincipalName = userPrincipalName homeDirectory = '/home/%s/%s' % (record.last_name[0].lower(), sAMAccountName) if record.location == 'Tigard High': server = 'bighouse' homeDirectory = r'\\%s\Users\%s' % (server, sAMAccountName) account.homeDrive = 'Y:' if account.attributes.has_key('scriptPath'): del account.scriptpath elif record.location == 'Tualatin High': server = 'gearhart.ttsd.k12.or.us' homeDirectory = r'\\%s\home\%s\%s' % (server, record.last_name[0].lower(), sAMAccountName) account.homeDrive = 'Y:' account.scriptPath = 'tuhs.bat' else: account.scriptPath = 'login.bat' if account.attributes.has_key('homeDrive'): del account.homeDrive account.homeDirectory = homeDirectory account.physicalDeliveryOfficeName = record.location account.description = record.location account.department = record.homeroom_teacher return account # }}}1 def create_student_account(record, count=0): # {{{1 sAMAccountName = create_student_username(record, count) base_dn = 'OU=schools,DC=student,DC=ttsd,DC=k12,DC=or,DC=us' superior_dn = 'OU=Users,OU=%s,%s' % (record.location, base_dn) relative_dn = 'cn=%s' % sAMAccountName new_dn = '%s,%s' % (relative_dn, superior_dn) account = ttsd.ldaputils.ad.ADEntry(new_dn) account.objectClass = 'user' account.sAMAccountName = sAMAccountName account = set_student_attributes(account, record) account = set_password(account, record.password, configuration=StudentConfiguration()) account.employeeID = record.student_id account.userAccountControl = account.NORMAL_ACCOUNT # enable account # 2009.06.18 no group changes until member;Range issue resolved return account # }}}1 def move_student_account(account, relative_dn, superior_dn, connection): # {{{1 # check new dn, make sure it does not exist # check new (or current) sAMAccountName, make sure it does not exist new_dn = '%s,%s' % (relative_dn.lower(), superior_dn.lower()) if account.dn.lower() != new_dn: debug('old dn ... %s' % account.dn.lower()) debug('new dn ... %s' % new_dn) # Search for existing accounts excluding the current account. search_filter = '(&(|(distinguishedName=%s)(%s))(!(distinguishedName=%s)))' % (new_dn, relative_dn, account.dn) existing_accounts = ttsd.ldaputils.ad.search(search_filter, connection=connection) if existing_accounts: debug('existing accounts ... skip') # Cannot move to an existing account. dn_list = [e.dn.lower() for e in existing_accounts] return None debug('modify account ... ') account = ttsd.ldaputils.ad.move(account, relative_dn, superior_dn, connection=connection) return account # }}}1 def update_student_account(account, record, connection, count=0): # {{{1 sAMAccountName = account.get('sAMAccountName') account.givenName = record.first_name account.sn = record.last_name old_gradyear = sAMAccountName[:2] new_gradyear = str(record.grad_year)[-2:] if account.get_modlist() or old_gradyear != new_gradyear or count > 0: debug('update: create new username ... ') sAMAccountName = create_student_username(record, count) old_location = account.get('physicalDeliveryOfficeName') base_dn = 'OU=schools,DC=student,DC=ttsd,DC=k12,DC=or,DC=us' superior_dn = 'OU=Users,OU=%s,%s' % (record.location, base_dn) relative_dn = 'cn=%s' % sAMAccountName # make lower case new_dn = '%s,%s' % (relative_dn.lower(), superior_dn.lower()) account = move_student_account(account, relative_dn, superior_dn, connection) if account is None: return debug('%s: %s' % (account.get('displayname'), account.get('samaccountname'))) # We should be settled on any sAMAccountName changes. account.sAMAccountName = sAMAccountName # Password may have changed in student assistant. Check before settings attributes. account = set_password(account, record.password, configuration=StudentConfiguration()) account = set_student_attributes(account, record) # 2009.06.18 no group changes until member;Range issue resolved return account # }}}1 # --------------- # Staff Functions # --------------- classifications = { 'Hourly': 'Classified', # in hourly less than 4 hours "classified type" job } def staff_name(record): return '%s, %s (%s)' % (record.last_name, record.first_name, record.employee_id) def valid_location(locations): # {{{1 location_set = set(locations) if (location_set.intersection(set(elementary_schools)) or location_set.intersection(set(secondary_schools)) or location_set.intersection(set(other_locations)) or location_set.intersection(set(substitute_codes))): return True else: return False # }}}1 def modify_or_add_staff(record, dry=False): # {{{1 """Given a SSAM staff record, modify or add a staff AD account. Return a tuple of the account, the create info and the update info. The create and update info are information strings about what was done. """ account = None created_info = None updated_info = None name_change = None if not record.employee_id: # TODO: log # return the None values return account, created_info, updated_info, name_change if not valid_location(record.location_codes): debug('modify_or_add_staff: %s: skipping - invalid location' % record.employee_id) # return the None values return account, created_info, updated_info, name_change ad_connection = ttsd.ldaputils.ad.get_connection() accounts = ttsd.ldaputils.ad.search('employeeid=%s' % record.employee_id, connection=ad_connection) if not accounts: # create a new active directory account for count in range(11): debug('create_staff_account: %s: count %s' % (record.employee_id, count)) try: account = create_staff_account(record, count) created_info = basic_info(account) if dry: print 'DRY: create AD account for %s' % account.get('displayName') else: account = ttsd.ldaputils.ad.add(account, connection=ad_connection) account = set_unix_attributes(account, ad_connection) # Seems like adding groups is broken. # Throws ldap.ALREADY_EXISTS #set_staff_groups(account, record, ad_connection) break except ldap.ALREADY_EXISTS, e: # Try again continue except Exception, e: print 'Exception for %s, %s (%s)' % (record.last_name, record.first_name, record.employee_id) print e break # TODO: log failed create else: # update an existing active directory account if len(accounts) != 1: # shouldn't happen # TODO: log pass else: account = accounts.pop() if account: # update an existing active directory account # currently not doing any moves, so count loop is not actually necessary for count in range(11): debug('update_staff_account: %s: count %s' % (record.employee_id, count)) try: updated_account, name_change = update_staff_account(account, record, ad_connection, count) if updated_account: if updated_account.get_modlist(): updated_info = format_changes(updated_account) if dry: print 'DRY: modify AD account for %s' % account.get('displayName') else: account = ttsd.ldaputils.ad.modify(updated_account, connection=ad_connection) break else: debug('No changes to %s.' % account.get('samaccountname')) break else: debug('Need to increment count for %s.' % account.get('samaccountname')) except ldap.ALREADY_EXISTS, e: # Try again debug('%s already exists.' % account.get('samaccountname')) continue except Exception, e: raise e # TODO: log failed update return account, created_info, updated_info, name_change # }}}1 def staff_location_code(record): # {{{1 # could have more than one location code, so pick the first # (lame, I know) return record.location_codes[0] # }}}1 def get_staff_location(record): # {{{1 location = ssam_utils.location_map[staff_location_code(record)] if ssam_utils.alt_location_name.has_key(location): location = ssam_utils.alt_location_name[location] return location # }}}1 def create_staff_account(record, count=0): # {{{1 sAMAccountName = create_staff_username(record, count) location = get_staff_location(record) base_dn = 'DC=ttsd,DC=ttsd,DC=k12,DC=or,DC=us' superior_dn = 'OU=Users,OU=%s,%s' % (location, base_dn) relative_dn = 'cn=%s' % sAMAccountName new_dn = '%s,%s' % (relative_dn, superior_dn) account = ttsd.ldaputils.ad.ADEntry(new_dn) account.objectClass = 'user' account.cn = sAMAccountName account.sAMAccountName = sAMAccountName account = set_staff_name(account, record) account = set_attributes(account, record, location) account = set_profile(account, record, location) password = ttsd.ldaputils.ad.prepare_ad_password(record.employee_id) account.unicodePwd = password account.employeeID = record.employee_id account.userAccountControl = account.NORMAL_ACCOUNT | account.ACCOUNTDISABLE # disable account location_set = set(record.location_codes) if location_set.intersection(substitute_codes): account.userAccountControl = account.NORMAL_ACCOUNT # enable account return account # }}}1 def create_staff_username(record, count=0): # {{{1 # clean data lastname = re.sub("[ .'/]", "", record.last_name.lower()) first_initial = record.first_name[0].lower() # create username sAMAccountName = "%s%s" % (first_initial, lastname) if count > 0: sAMAccountName = "%s%s" % (sAMAccountName, count) debug("trying ... %s" % sAMAccountName) # pre-Windows 2000 login name has 20 character limit while len(sAMAccountName) > 20: lastname = lastname[:len(lastname) - 1] sAMAccountName = "%s%s" % (first_initial, lastname) if count > 0: sAMAccountName = "%s%s" % (sAMAccountName, count) return sAMAccountName # }}}1 def set_staff_name(account, record): # {{{1 givenName = ' '.join([name.capitalize() for name in record.first_name.split()]) sn = ' '.join([name.capitalize() for name in record.last_name.split()]) account.givenName = givenName account.sn = sn displayName = '%s, %s' % (sn, givenName) displayNamePrintable = '%s %s' % (givenName, sn) preferred = record.familiar_name if preferred is not None and preferred.strip() != '': preferred = ' '.join([name.capitalize() for name in preferred.split()]) displayName = '%s, %s' % (sn, preferred) displayNamePrintable = '%s %s' % (preferred, sn) account.displayName = displayName account.displayNamePrintable = displayNamePrintable return account # }}}1 def set_attributes(account, record, location): # {{{1 sAMAccountName = account.get('sAMAccountName').lower() if account.homemdb is not None: account.mail = '%s@ttsd.k12.or.us' % sAMAccountName account.userPrincipalName = '%s@ttsd.k12.or.us' % sAMAccountName if account.attributes.has_key('physicalDeliveryOfficeName') and location == 'Substitutes': # Existing account. Do not update, so it can be manually changed. debug('set_attributes: physicalDeliveryOfficeName not set to allow manual change') else: account.physicalDeliveryOfficeName = location # Set company the same as physicalDeliveryOfficeName in case it is changed. # Company is used by VOIP for Organization. account.company = account.get('physicalDeliveryOfficeName') # not using record.template_name c = record.classification account.extensionattribute1 = classifications.get(c, c).lower() return account # }}}1 def set_profile(account, record, location): # {{{1 profile = { '345': (r'\\fowstaff\staff\%s', 'H:', 'fowcim-k.bat'), '353': (r'\\hazstaff\staff\%s', 'H:', 'hazcim-k.bat'), '373': (r'\\twastaff\staff\%s', 'H:', 'twacim-k.bat'), '622': (r'\\tuhsstaff\staff\%s', 'H:', 'tuhscim-k.bat'), '632': (r'\\thsstaff\staff\%s', 'H:', 'thscim-k.bat'), } sAMAccountName = account.get('sAMAccountName').lower() homeDirectory = '/home/%s/%s' % (sAMAccountName[0], sAMAccountName) homeDrive = None scriptPath = 'logon.bat' if account.homeDirectory is not None: homeDirectory = account.get('homeDirectory') if account.homeDrive is not None: homeDrive = account.get('homeDrive') if account.scriptPath is not None: scriptPath = account.get('scriptPath') regex = re.compile('\\\\hood') if location == 'Hibbard' or regex.search(homeDirectory): #homeDirectory = r'\\hood\%s' % sAMAccountName #homeDrive = 'H:' #scriptPath = 'hibbard.bat' return account elif profile.has_key(staff_location_code(record)): (homeDirectory, homeDrive, scriptPath) = profile[staff_location_code(record)] homeDirectory = homeDirectory % sAMAccountName elif staff_location_code(record) in elementary_schools: homeDirectory = '/home/%s/%s' % (sAMAccountName[0], sAMAccountName) homeDrive = '' scriptPath = 'logon.bat' account.homeDirectory = homeDirectory if homeDrive: account.homeDrive = homeDrive account.scriptPath = scriptPath return account # }}}1 def set_staff_groups(account, record, connection): # {{{1 group_names = [] for code in record.location_codes: location = ssam_utils.location_map[code] if ssam_utils.alt_location_name.has_key(location): location = ssam_utils.alt_location_name[location] group_names.append(location) for name in group_names: group_filter = "(&(objectClass=group)(name=%s))" % name groups = ttsd.ldaputils.ad.search(group_filter, connection=connection) if groups: group = groups.pop() if account.dn not in group.member: group.add('member', account.dn) try: group = ttsd.ldaputils.ad.modify(group, connection=connection) except ldap.ALREADY_EXISTS, e: print 'Already Exists: %s in group %s?' % (sAMAccountName, name) # }}}1 def set_unix_attributes(account, connection): # {{{1 # update Unix group groups = ttsd.ldaputils.ad.search('sAMAccountName=Unix', connection=connection) unixgroup = groups.pop() #if account.dn not in unixgroup.member: #unixgroup.add('member', account.dn) # TODO: deal with member;range=0-1499 #unixgroup = manager.modify_account(unixgroup) # get account again since there is a change after adding to the Unix group # NOTE: This does not make sense for a new account. #account = ttsd.ldaputils.ad.search( # 'distinguishedName=%s' % account.dn, connection=connection # ).pop() # add unix attributes sAMAccountName = account.get('sAMAccountName') account.uid = sAMAccountName account.gidNumber = unixgroup.get('gidNumber') account.loginShell = '/bin/false' unixHomeDirectory = '/home/%s/%s' % (sAMAccountName[0], sAMAccountName) account.unixHomeDirectory = unixHomeDirectory # add services for unix attributes account.msSFU30Name = sAMAccountName account.msSFU30NisDomain = 'ttsd' return ttsd.ldaputils.ad.modify(account, connection=connection) # }}}1 def update_staff_account(account, record, connection, count=0): # {{{1 # connection and count are not used, since we are not doing moves # Keep account name the same during update. Use manual name changes. sAMAccountName = account.get('sAMAccountName') # Notify if a name change is required based on last name. sn = account.get('sn') new_sn = ' '.join([name.capitalize() for name in record.last_name.split()]) name_change = None if new_sn.lower() != sn.lower(): n = staff_name(record) + '\n' n += ' %s\n' % account.dn n += ' rename sAMAccountName:\n%12s: %s\n%12s: %s\n' % ( 'from', sAMAccountName, 'to', create_staff_username(record)) n += ' Current name:\n' n += '%12s: %s\n' % ('sn', account.get('sn')) n += '%12s: %s\n' % ('givenName', account.get('givenName')) n += '%12s: %s\n' % ('displayName', account.get('displayName')) name_change = n old_location = account.get('physicalDeliveryOfficeName') location = get_staff_location(record) #base_dn = 'DC=ttsd,DC=ttsd,DC=k12,DC=or,DC=us' #superior_dn = 'OU=Users,OU=%s,%s' % (location, base_dn) #relative_dn = 'CN=%s' % sAMAccountName #new_dn = '%s,%s' % (relative_dn, superior_dn) # renames and moves? account = set_attributes(account, record, location) account = set_profile(account, record, location) # uid must stay the same as sAMAccountName for email logins to work account.uid = sAMAccountName # TODO: group changes, e.g. riderstaff to woodwardstaff return account, name_change # }}}1 def send_error(exception): # {{{1 message = 'To: sysadmin\n' message = message + 'Subject: ERROR: Staff Active Directory Update\n\n' message = '%s%s' % (message, exception) print message server = smtplib.SMTP('smtp.ttsd.k12.or.us') server.set_debuglevel(1) mail_from = 'postmaster@ttsd.k12.or.us' rcpt_to = ['sysadmin', ] #rcpt_to = ['chanson'] server.sendmail(mail_from, rcpt_to, message) server.quit() # }}}1 def basic_info(account): # {{{1 info = "%s, %s (%s)\n" % ( account.get('sn'), account.get('givenName'), account.get('sAMAccountName') ) info = info + " %s\n" % account.dn info = info + " physicalDeliveryOfficeName: %s\n" % \ account.get('physicalDeliveryOfficeName') return info # }}}1 def get_staff_groups(employeeid): """Return the main staff groups for user with employeeid. For example, riderstaff@ttsd.k12.or.us. """ ad_account = ttsd.ldaputils.ad.search('employeeid=%s' % employeeid, configuration=ttsd.ldaputils.ad.StaffConfiguration())[0] groups = [g for g in ttsd.ldaputils.ad.search('member=%s' % ad_account.dn) if g.get('samaccountname') in ssam_utils.staff_group_names] return groups # all_org_unit_users = ou_service.RetrieveAllOrgUsers(ou_service.RetrieveCustomerId()['customerId'])
Run
Reset
Share
Import
Link
Embed
Language▼
English
中文
Python Fiddle
Python Cloud IDE
Follow @python_fiddle
Browser Version Not Supported
Due to Python Fiddle's reliance on advanced JavaScript techniques, older browsers might have problems running it correctly. Please download the latest version of your favourite browser.
Chrome 10+
Firefox 4+
Safari 5+
IE 10+
Let me try anyway!
url:
Go
Python Snippet
Stackoverflow Question