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/.

Reply via email to