Skip to content

Instantly share code, notes, and snippets.

@20esaua
Created March 20, 2018 16:02
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save 20esaua/e408d6076376b3a9d0f49ee24334a67b to your computer and use it in GitHub Desktop.
Save 20esaua/e408d6076376b3a9d0f49ee24334a67b to your computer and use it in GitHub Desktop.
Script that Carlos Hanson uses to import users into AD.
#!/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'])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment