so this is my take on how to get ldap (well AD in my case) to work with group (CN) validation. I had to modify ldap_auth and the tools files inorder to get the out come that I needed. I'm sure there are other ways of doing this, but this way worked for and maybe someone else.
* disclaimer, this works for me and need some tweeking if your needs are not the same as mine
so in tools.py I needed to change the section for login() that does the login_methods if the user is not in the db
so here is the whole else statement (around line 1738)
else: # user not in db if not self.settings.alternate_requires_registration: # we're allowed to auto-register users from external systems for login_method in self.settings.login_methods: #check to see if the function is the ldap_auth and skip it so that the results #we get back from the ldpa_auth funct can be parsed out #otherwise do whatever is the default for the given auth method if hasattr(login_method,'__call__'): if hasattr(login_method,'__name__') and 'ldap_auth_aux' in login_method.__name__: results = login_method(request.vars[username], request.vars[passfield]) if isinstance(results, bool): if not results: break else: if results.has_key('first_name'): form.vars.first_name = results['first_name'] if results.has_key('last_name'): form.vars.last_name = results['last_name'] if results.has_key('email'): form.vars.email = results['email'] user = self.get_or_create_user(form.vars) break else: if login_method != self and \ login_method(request.vars[username], request.vars[passfield]): if not self in self.settings.login_methods: # do not store password in db form.vars[passfield] = None user = self.get_or_create_user(form.vars) break
so what I did was:
*check to see if the login_method is the ldap_auth function
** if it is then send the function the username and password arguments and save response to variable
**if the response is a bool and not a dict then we know that authentication failed
**otherwise take the dict and save the k,v to form vars so that the get_or_create funct cna populate the db with: first and last name, username, email
*if the funct is not the lda_auth one then proceed to do the original logic
in the db.py file we need to add the file, I give credit to http://www.web2pyslices.com/slice/show/1468/how-to-set-up-web2py-ldap-with-windows-active-directory for the original guidence and settings
auth.define_tables(username=True) auth.settings.create_user_groups=False # all we need is login auth.settings.actions_disabled=['register','change_password','request_reset_password','retrieve_username'] #,'profile'] # you don't have to remember me auth.settings.remember_me_form = False # ldap authentication and not save password on web2py from gluon.contrib.login_methods.ldap_auth import ldap_auth auth.settings.login_methods = [auth,ldap_auth(mode='ad', server=<IP of your AD server>, base_dn=<base dn to search>, group=<CN group to search>, ou=<OU to filter on>)]
I had to modify the ldap_auth.py file so that I can search/filter on group
I updated the ldap_auth() to include two additional args
def ldap_auth(server='ldap', port=None, base_dn='ou=users,dc=domain,dc=com', mode='uid', secure=False, cert_path=None, bind_dn=None, bind_pw=None, filterstr='objectClass=*', group='',ou=''):
then I updated the ldap_auth_aux funct to take the additional args as well and included a results dict
results = {} def ldap_auth_aux(username, password, ldap_server=server, ldap_port=port, ldap_basedn=base_dn, ldap_mode=mode, ldap_binddn=bind_dn, ldap_bindpw=bind_pw, secure=secure, cert_path=cert_path, filterstr=filterstr, group=group, ou=ou):
I needed to comment out this section since it didn't out of the box
## if not isinstance(result, dict): ## # result should be a dict in the form {'sAMAccountName': [username_bare]} ## return False ## result = con.search_ext_s( ## ldap_basedn, ldap.SCOPE_SUBTREE, ## "(&(sAMAccountName=%s)(%s))" % (username_bare, filterstr), ["sAMAccountName"])[0][1] ## if not isinstance(result, dict): ## # result should be a dict in the form {'sAMAccountName': [username_bare]} ## return False
here is where I do the filter action if the username and password works binds successfully to AD
# this will throw an index error if the account is not found # in the ldap_basedn result = con.search_ext_s( ldap_basedn, ldap.SCOPE_SUBTREE, #"(&(sAMAccountName=%s)(%s))" % (username_bare, filterstr), ["sAMAccountName"])[0][1] "(&(sAMAccountName=%s)(%s))" % (username_bare, filterstr),["sAMAccountName","mail", "sn", "givenName", 'memberOf']) search_path = group+','+ou+','+ldap_basedn if search_path.upper() in (path.upper() for path in result[0][1]['memberOf']): results['email'] = result[0][1]['mail'][0] results['first_name'] = result[0][1]['givenName'][0] results['last_name'] = result[0][1]['sn'][0] results['username'] = result[0][1]['sAMAccountName'][0] else: return False
then down at the bottom of the file I change the object that gets passed back if everything is successful
con.unbind() #return True return results
Comments (2)
0
derek-wilson-10759 12 years ago
This might work, I get the feeling though that it's wrongly done. Not trying to discount your work, but it doesn't quite fit the current design.
Is it possible you could create your own 'onaccept' method that will populate the auth.user.* fields with the correct data?
replies (1)
0
derek-wilson-10759 12 years ago
I think it's great, and I'd gladly use it if it was easily integrated. I wish that the ldap could automatically read in the first and last names as well as any other information that is needed, since I'd rather present a list of first and last names to someone than their login ID (which could be just a bunch of numbers, or in my case, a jumble of letters and numbers). Nobody is going to remember that my Id is "asdloquahdf12" (especially if there are a lot of others with similar starting and ending characters), but if they see Wilson, Derek - they'll know it's me. So, this is great, but it's changing a lot of core files, and not in a way that it would get accepted into the core product, since it changes the return type of the auth event.
replies (1)