Russell Jackson wrote:
Jeremy Childress wrote:
We are planning on moving our students to AD, did you ever get a
response to this question?
<snip>
Is anyone using a turnkey web application that allows students to
change their WiFi login password in Active Directory? I was going to
contract someone to design a password change webpage for us but I
wanted to know if there is something already out there.
I hacked up a python script in a couple of hours a few months ago. It's
pathetically simple and ugly as sin; on the other hand, it only has a
single dependency on py-ldap2, and it gets the job done (for now). You
should be able to just throw it CGI directory and be off to the races.
If anyone improves upon it (such as totally rewriting it within proper
session management and/or using turbogears et. al.), send me back the
"patch" ;-). If anything, it'll show you the procedure for setting
passwords through LDAP to Active Directory.
Script attached. If the list strips it off, I'll repost with it inline.
Forgot to attach it.
--
Russell A. Jackson <[EMAIL PROTECTED]>
Network Analyst
California State University, Bakersfield
Grelb's Reminder:
Eighty percent of all people consider
themselves to be above average drivers.
**********
Participation and subscription information for this EDUCAUSE Constituent Group
discussion list can be found at http://www.educause.edu/groups/.
#!/usr/local/bin/python
#
# $Id$
import cgi
import cgitb
import os
import sys
import ldap
import ldap.filter
import textwrap
import base64
##
# Configuration
#
BASEDN = "dc=org,dc=com"
BINDURI = "ldaps://ldap.org.com"
DEBUG = True
##
# LDAP library functions
#
#
# Constants for LDAP result sets
#
LDAP_RS_DN = 0
LDAP_RS_DATA = 1
def ldap_user_setpwd(dn, oldpwd, newpwd):
ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
ldap.set_option(ldap.OPT_REFERRALS, 0)
lh = ldap.initialize(BINDURI)
lh.simple_bind(dn, oldpwd)
rid = lh.modify_s(dn,
[
(ldap.MOD_DELETE, "unicodePwd",
encode_unicodepwd(oldpwd)),
(ldap.MOD_ADD, "unicodePwd", encode_unicodepwd(newpwd))
]
)
def ldap_admin_setpwd(admindn, userdn, adminpwd, newpwd):
ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
ldap.set_option(ldap.OPT_REFERRALS, 0)
lh = ldap.initialize(BINDURI)
lh.simple_bind(admindn, adminpwd)
rid = lh.modify_s(userdn, [(ldap.MOD_REPLACE, "unicodePwd",
encode_unicodepwd(newpwd))])
def ldap_getuser(uid):
ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT, ldap.OPT_X_TLS_NEVER)
ldap.set_option(ldap.OPT_REFERRALS, 0)
binddn = "cn=ldap,ou=IT,%s" % BASEDN
lh = ldap.initialize(BINDURI)
lh.simple_bind(binddn, "ldap")
domain = '.'.join([str(i) for i in ldap.explode_dn(BASEDN, 1)])
uid = ldap.filter.escape_filter_chars(uid)
rset = lh.search_s(BASEDN,
ldap.SCOPE_SUBTREE,
'(&(objectClass=User)([EMAIL PROTECTED]))' % (uid,
domain),
None
)
if len(rset) != 4:
return None
userdn = rset[0][LDAP_RS_DN]
user = rset[0][LDAP_RS_DATA]
return user
def encode_unicodepwd(s):
#
# Active directory wants a utf-16 encoded string to set the password
with.
# The catch is that the unicode string isn't quite unicode. You need to
strip
# off the type header which is the first two bytes.
#
return ('"%s"' % s).encode("utf-16").lstrip('\377\376')
##
# HTML library functions
#
def http_response_header(type):
return 'Content-Type: %s\n\n' % type
def html_attrs(attr_dict):
attrs = ['']
for k, v in sorted(attr_dict.iteritems()):
if v != None:
attrs.append('%s="%s"' % (k, v))
attrs = ' '.join(attrs)
return attrs
def html_doctype():
return '\n'.join([
'<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"',
'\t"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">',
])
def html_header(title):
return '\n'.join(
[
'<html xmlns="http://www.w3.org/1999/xhtml" lang="en"
xml:lang="en">',
'<head>',
html_link("stylesheet", "text/css", "/style.css"),
'<title>%s</title>' % title,
'</head>\n',
'<body>',
html_h1(title)
],
)
def html_link(rel, type, href):
attrs = html_attrs({ 'rel' : rel, 'type' : type, 'href' : href })
return '<link%s />' % attrs
def html_footer():
return '\n</body>\n</html>'
def html_h1(s, id = None, css_class = None):
attrs = html_attrs({ 'id' : id, 'class' : css_class })
return '<h1%s>%s</h1>' % (attrs, s)
def html_h2(s, id = None, css_class = None):
attrs = html_attrs({ 'id' : id, 'class' : css_class})
return '<h2%s>%s</h2>' % (attrs, s)
def html_hr(id = None, css_class = None):
attrs = html_attrs({ 'id' : id, 'class' : css_class })
return '<hr%s />' % attrs
def html_p(par, id = None, css_class = None):
attrs = html_attrs({ 'id' : id, 'class' : css_class })
lines = []
lines.append('\n<p%s>' % attrs)
lines.extend([l for l in textwrap.wrap(par, 90)])
lines.append('</p>\n')
return '\n'.join(lines)
def html_span(s, id = None, css_class = None, css_style = None):
attrs = html_attrs({ 'id' : id, 'class' : css_class, 'style' :
css_style })
return '<span%s>%s</span>' % (attrs, s)
def html_table_begin(id = None, css_class = None):
attrs = html_attrs({ 'id' : id, 'class' : css_class })
return '<table%s>' % attrs
def html_table_end():
return '</table>'
def html_tr(s, id = None, css_class = None):
attrs = html_attrs({ 'id' : id, 'class' : css_class })
return '<tr%s>\n%s</tr>\n' % (attrs, s)
def html_td(s, align = None, colspan = None, id = None, css_class = None):
attrs = html_attrs({ 'id' : id, 'class' : css_class, 'align' : align,
'colspan': colspan })
return '<td%s>%s</td>\n' % (attrs, s)
def html_th(s, align = None, id = None, css_class = None):
attrs = html_attrs({ 'id' : id, 'class' : css_class, 'align' : align })
return '<th%s>%s</th>\n' % (attrs, s)
def html_form_begin(action, method = "post", id = None, css_class = None):
attrs = html_attrs({ 'id' : id, 'class' : css_class, 'action' : action,
'method' : method })
return '<form%s>' % attrs
def html_form_end():
return '</form>'
def html_input(name, type, value = None, size = None, maxlength = None, id =
None, css_class = None):
attrs = html_attrs({
'name' : name,
'type' : type,
'value' : value,
'size' : size,
'maxlength' : maxlength,
'id' : id,
'class' : css_class,
})
return '<input%s />' % attrs
def html_a(title, href, id = None, css_class= None):
attrs = html_attrs({ 'href' : href, 'id' : id, 'class' : css_class })
return '<a%s>%s</a>' % (attrs, title)
def html_ldap_record(user):
s = []
s.append(html_table_begin("user_table"))
for k, v in sorted(user.iteritems()):
for i in range(len(v)):
if k == "objectSid" or k == "objectGUID":
v[i] = base64.b64encode(v[i])
s.append(html_tr(''.join([html_th("%s:" % k, "right"),
html_td(v[i])])))
s.append(html_table_end())
return '\n'.join(s)
##
# Main program
#
#
# Allow user to reset password to their student id
#
def runcgi():
if DEBUG == True:
cgitb.enable()
form = cgi.FieldStorage()
print http_response_header("text/html")
print html_header("Really Simple LDAP Account Manager")
print html_p("If you do not know your CSUB NetID, logon to %s to find
out." \
% html_a("BannerWeb",
"https://oraweb2.csub.edu:4700/PROD/prod/twbkwbis.P_WWWLogin"))
print html_hr()
print html_h2("Change Password")
print html_form_begin(os.environ["SCRIPT_NAME"], "post",
"change_pwd_form")
print html_table_begin()
print html_tr(html_th("NetID:", "right") + html_td(html_input("uid",
"text", None, 15, 15)))
print html_tr(html_th("Old Password:", "right") +
html_td(html_input("old_passwd", "password", None, 15, 15)))
print html_tr(html_th("New Password:", "right") +
html_td(html_input("new_passwd", "password", None, 15, 15)))
print html_tr(html_th("Verify Password:", "right") +
html_td(html_input("verify_passwd", "password", None, 15, 15)))
print html_tr(html_td(html_input("change_pwd_submit", "submit",
"Submit", None, None, "submit"), "right", "2"))
print html_table_end()
print html_form_end()
if form.has_key("change_pwd_submit"):
class error: pass
try:
uid = form["uid"].value
old_passwd = form["old_passwd"].value
new_passwd = form["new_passwd"].value
verify_passwd = form["verify_passwd"].value
print html_hr()
if new_passwd != verify_passwd:
print html_p(html_span("New password
verification failed.", None, None, "color: red"))
print html_p(
"Ensure that you entered an identical
verification password against your new password." +
" This is to help prevent you from
making a typo and setting your new password to" +
" something you did not intend."
)
raise error()
user = ldap_getuser(uid)
if user == None:
print html_p(html_span("User does not exist or
password incorrect.", None, None, "color: red"))
raise error()
try: ldap_user_setpwd(user["distinguishedName"][0],
old_passwd, new_passwd)
except ldap.OPERATIONS_ERROR, e:
print html_p(html_span("User does not exist or
password incorrect.", None, None, "color: red"))
raise error()
except ldap.CONSTRAINT_VIOLATION, e:
print html_p(html_span("Your new password
cannot be the same as that set previously.", None, None, "color: red"))
raise error()
print html_p(html_span("Password changed
successfully.", None, None, "color: green"))
if DEBUG == True:
print html_ldap_record(user)
except (error, KeyError): pass
print html_hr()
print html_h2("Administrative Password Reset")
print html_form_begin(os.environ["SCRIPT_NAME"], "post",
"admin_pwd_reset_form")
print html_table_begin()
print html_tr(html_th("NetID:", "right") +
html_td(html_input("user_uid", "text", None, 15, 15)))
print html_tr(html_th("New Password:", "right") +
html_td(html_input("new_passwd", "password", None, 15, 15)))
print html_tr(html_th("Verify Password:", "right") +
html_td(html_input("verify_passwd", "password", None, 15, 15)))
print html_tr(html_th("Admin NetID:", "right") +
html_td(html_input("admin_uid", "text", None, 15, 15)))
print html_tr(html_th("Admin Password:", "right") +
html_td(html_input("admin_passwd", "password", None, 15, 15)))
print html_tr(html_td(html_input("admin_pwd_reset_submit", "submit",
"Submit", None, None, "submit"), "right", "2"))
print html_table_end()
print html_form_end()
if form.has_key("admin_pwd_reset_submit"):
class error: pass
try:
user_uid = form["user_uid"].value
new_passwd = form["new_passwd"].value
verify_passwd = form["verify_passwd"].value
admin_uid = form["admin_uid"].value
admin_passwd = form["admin_passwd"].value
print html_hr()
if new_passwd != verify_passwd:
print html_p(html_span("New password
verification failed.", None, None, "color: red"))
raise error()
admin = ldap_getuser(admin_uid)
user = ldap_getuser(user_uid)
if admin == None or user == None:
print html_p(html_span("User does not exist or
password incorrect.", None, None, "color: red"))
raise error()
try:
ldap_admin_setpwd(admin["distinguishedName"][0],
user["distinguishedName"][0],
admin_passwd, new_passwd)
except ldap.OPERATIONS_ERROR:
print html_p(html_span("Administrator
credentials invalid.", None, None, "color: red"))
raise error()
print html_p(html_span("Password reset.", None, None,
"color: green"))
if DEBUG == True:
print html_ldap_record(user)
except (error, KeyError): pass
print html_footer()
def main():
if os.environ.has_key("SCRIPT_NAME"):
runcgi()
sys.exit(0)
if __name__ == "__main__":
main()
**********
Participation and subscription information for this EDUCAUSE Constituent Group
discussion list can be found at http://www.educause.edu/groups/.