I need to review this. I understand the problem but I am not convinced by the solution. I will need one week.
On Monday, 12 August 2013 08:46:31 UTC-5, Richard wrote: > > UP! > > > > On Fri, Aug 9, 2013 at 10:59 AM, Richard Vézina > <[email protected]<javascript:> > > wrote: > >> Here the patch!! >> >> I have not been able to use the IS_NOT_EMAIL() validator from >> validators.py didn't understand how validators are import in tools.py... >> >> NOTE : >> About my precedent mail... the "auth_table.username.requires = >> IS_NOT_IN_DB(db, auth_table.username" from the book is not require if the >> user use the "auth.define_tables(username=True)" and the recommended >> auth_tables customization mechanism. So, I think the book should be revised >> this way : >> >> "In case you use old style customizing auth_tables. Make sure your >> username field definition looks like that : >> Field('username', 'string', >> notnull=True, >> required=True, >> >> requires=[IS_NOT_EMPTY(error_message=T(auth.messages.is_empty)), >> IS_NOT_IN_DB(db, 'auth_user.username'), >> IS_NOT_EMAIL()] >> ), >> >> Where you make sure you use these validators in order to make sure email >> is not used as username and there is no duplicated username in your >> auth_user table." >> >> :) >> >> Richard >> >> >> >> >> On Wed, Aug 7, 2013 at 1:48 PM, Richard Vézina >> <[email protected]<javascript:> >> > wrote: >> >>> I would also add this : >>> >>> tmpvalidator = [IS_NOT_EMPTY(error_message=self.messages.is_empty), >>> IS_NOT_IN_DB(db, >>> 'auth_user.username'), IS_NOT_EMAIL()] >>> >>> Since this line in the book : >>> >>> auth_table.username.requires = IS_NOT_IN_DB(db, auth_table.username) >>> >>> >>> Doesn't seem to works and could be erase since who want a duplicated >>> username in his auth_user?? >>> >>> >>> Richard >>> >>> >>> >>> >>> On Wed, Aug 7, 2013 at 1:35 PM, Richard Vézina >>> <[email protected]<javascript:> >>> > wrote: >>> >>>> Better then that I think : >>>> >>>> In gluon/tools.py in Auth login() near line 2006 : >>>> >>>> Replace : >>>> tmpvalidator = IS_NOT_EMPTY(error_message=self.messages.is_empty) >>>> >>>> With : >>>> if 'username' in table_user.fields or \ >>>> not self.settings.login_email_validate: >>>> tmpvalidator = >>>> [IS_NOT_EMPTY(error_message=self.messages.is_empty), IS_NOT_EMAIL()] >>>> >>>> Will require to add this new validator though : >>>> >>>> class IS_NOT_EMAIL: >>>> def __init__(self, error_message='You can\'t use email as >>>> username'): >>>> self.e = error_message >>>> def __call__(self, value): >>>> if not IS_EMAIL()(value)[1]: >>>> return (value, self.e) >>>> return (value, None) >>>> >>>> What you think about that?? >>>> >>>> Richard >>>> >>>> >>>> On Wed, Aug 7, 2013 at 1:23 PM, Richard Vézina >>>> <[email protected]<javascript:> >>>> > wrote: >>>> >>>>> from gluon.validators import IS_EMAIL >>>>> >>>>> if ldap_mode == 'ad': >>>>> # Microsoft Active Directory >>>>> if IS_EMAIL()(username)[1] is not None: >>>>> #if '@' not in username: >>>>> domain = [] >>>>> for x in ldap_basedn.split(','): >>>>> if "DC=" in x.upper(): >>>>> domain.append(x.split('=')[-1]) >>>>> username = "%s@%s" % (username, '.'.join(domain)) >>>>> else: >>>>> return False >>>>> username_bare = username.split("@")[0] >>>>> >>>>> >>>>> This prevent login to occure and new user to be inserted when email is >>>>> used as username... however it not returning any advise to the user... I >>>>> will try to figure out how to implement validation from ldap_auth and get >>>>> back with a patch. >>>>> >>>>> Richard >>>>> >>>>> >>>>> >>>>> >>>>> >>>>> On Wed, Aug 7, 2013 at 10:56 AM, Richard Vézina >>>>> <[email protected]<javascript:> >>>>> > wrote: >>>>> >>>>>> Ok! >>>>>> >>>>>> :) >>>>>> >>>>>> Richard >>>>>> >>>>>> >>>>>> On Wed, Aug 7, 2013 at 10:10 AM, Massimo Di Pierro < >>>>>> [email protected] <javascript:>> wrote: >>>>>> >>>>>>> Please send me a patch when you test it. ;-) >>>>>>> >>>>>>> On Wednesday, 7 August 2013 07:51:58 UTC-5, Richard wrote: >>>>>>> >>>>>>>> No change... Auth seems to delegate entirely the validation on >>>>>>>> username input field in case ldap_auth is used as authentication >>>>>>>> method. >>>>>>>> >>>>>>>> I guess this simple refactor (not tested) could do the tricks at >>>>>>>> least for Active directory : >>>>>>>> >>>>>>>> if not IS_EMAIL()(username)[1]: >>>>>>>> domain = [] >>>>>>>> for x in ldap_basedn.split(','): >>>>>>>> if "DC=" in x.upper(): >>>>>>>> domain.append(x.split('=')[-1]**) >>>>>>>> identifier = "%s@%s" % (username, >>>>>>>> '.'.join(domain)) >>>>>>>> else: return ERROR... >>>>>>>> username_bare = username.split("@")[0] >>>>>>>> >>>>>>>> Richard >>>>>>>> >>>>>>>> >>>>>>>> >>>>>>>> On Wed, Aug 7, 2013 at 8:42 AM, Richard Vézina < >>>>>>>> [email protected]> wrote: >>>>>>>> >>>>>>>>> Hello, >>>>>>>>> >>>>>>>>> I was about to post this (I think I answer your question) : >>>>>>>>> >>>>>>>>> Hello, >>>>>>>>> >>>>>>>>> I think I found a flaw in the interaction between Auth and LDAP >>>>>>>>> contrib (web2py 2.4.7). >>>>>>>>> >>>>>>>>> If I set LDAP as unique authentification method >>>>>>>>> (auth.settings.login_methods = LDAP) as written in the book, web2py >>>>>>>>> should >>>>>>>>> leaves LDAP to create user... The things is web2py Auth seems to >>>>>>>>> create >>>>>>>>> user even if it LDAP that is responsible of doing it. I mean, I >>>>>>>>> carefully >>>>>>>>> read the code of LDAP and the only way it could create a new user is >>>>>>>>> if >>>>>>>>> manage_groups=True by calling do_manage_groups() since the other >>>>>>>>> place >>>>>>>>> where LDAP is instert new user it set email, first_name and >>>>>>>>> last_name. In >>>>>>>>> my case, if user use email instead of username (that should not be >>>>>>>>> email, >>>>>>>>> but I can't enforce this with the custom IS_NOT_EMAIL() validator I >>>>>>>>> wrote) >>>>>>>>> for login a new user get inserted like this : first_name = email (or >>>>>>>>> the >>>>>>>>> content of username input that is an email), username = email and >>>>>>>>> registration_id = email. As far as I can see the only way LDAP could >>>>>>>>> produce this result is if the do_manage_groups method is called, but >>>>>>>>> it >>>>>>>>> can't be call if manage_groups is set to False. So, the only >>>>>>>>> remaining >>>>>>>>> possibility is that Auth is creating the new user because it recieve >>>>>>>>> a bad >>>>>>>>> signal from LDAP. >>>>>>>>> >>>>>>>>> I make a couples tests and found that the insert new user base on >>>>>>>>> the credentials of already existing user that log with it email >>>>>>>>> instead of >>>>>>>>> it username occure at line 2147-2148. So I guess Auth recieve a True >>>>>>>>> flag >>>>>>>>> from LDAP mean the user exist in directory, since web2py can't match >>>>>>>>> a >>>>>>>>> existing user base on the wrong username (email) it insert a new user >>>>>>>>> with >>>>>>>>> wrong setting. >>>>>>>>> >>>>>>>>> The origin of this is multifold. First, I think it could be >>>>>>>>> prevent if there was a IS_NOT_EMAIL() validator on the username >>>>>>>>> field, for >>>>>>>>> some reason I can't get it to work properly with LDAP because of the >>>>>>>>> way >>>>>>>>> LDAP is working the validator seems to be skipped, and the username >>>>>>>>> is >>>>>>>>> first check against directory. Maybe using IS_NOT_EMAIL() inside >>>>>>>>> ldap_auth >>>>>>>>> contrib could solve this issue. Other possible origin is the way >>>>>>>>> ldap_auth >>>>>>>>> is written. I mean it seems that for saving a variable "username" is >>>>>>>>> re-used... I think that the issue is coming from line 8 of code >>>>>>>>> extract >>>>>>>>> below : >>>>>>>>> >>>>>>>>> if ldap_mode == 'ad': >>>>>>>>> # Microsoft Active Directory >>>>>>>>> if '@' not in username: >>>>>>>>> domain = [] >>>>>>>>> for x in ldap_basedn.split(','): >>>>>>>>> if "DC=" in x.upper(): >>>>>>>>> domain.append(x.split('=')[-1]**) >>>>>>>>> username = "%s@%s" % (username, >>>>>>>>> '.'.join(domain)) >>>>>>>>> username_bare = username.split("@")[0] >>>>>>>>> con.set_option(ldap.OPT_**PROTOCOL_VERSION, 3) >>>>>>>>> # In cases where ForestDnsZones and DomainDnsZones >>>>>>>>> are found, >>>>>>>>> # result will look like the following: >>>>>>>>> # ['ldap://ForestDnsZones.** >>>>>>>>> domain.com/DC=ForestDnsZones<http://ForestDnsZones.domain.com/DC=ForestDnsZones> >>>>>>>>> , >>>>>>>>> # DC=domain,DC=com'] >>>>>>>>> if ldap_binddn: >>>>>>>>> # need to search directory with an admin >>>>>>>>> account 1st >>>>>>>>> con.simple_bind_s(ldap_binddn, ldap_bindpw) >>>>>>>>> else: >>>>>>>>> # credentials should be in the form of >>>>>>>>> [email protected] >>>>>>>>> con.simple_bind_s(username, password) >>>>>>>>> # this will throw an index error if the account is >>>>>>>>> not found >>>>>>>>> # in the ldap_basedn >>>>>>>>> requested_attrs = ['sAMAccountName'] >>>>>>>>> if manage_user: >>>>>>>>> requested_attrs.extend([user_** >>>>>>>>> firstname_attrib, >>>>>>>>> user_lastname_attrib, >>>>>>>>> user_mail_attrib]) >>>>>>>>> result = con.search_ext_s( >>>>>>>>> ldap_basedn, ldap.SCOPE_SUBTREE, >>>>>>>>> "(&(sAMAccountName=%s)(%s))" % ( >>>>>>>>> ldap.filter.escape_filter_** >>>>>>>>> chars(username_bare), >>>>>>>>> filterstr), >>>>>>>>> requested_attrs)[0][1] >>>>>>>>> if not isinstance(result, dict): >>>>>>>>> # result should be a dict in the form >>>>>>>>> # {'sAMAccountName': [username_bare]} >>>>>>>>> logger.warning('User [%s] not found!' % >>>>>>>>> username) >>>>>>>>> return False >>>>>>>>> if ldap_binddn: >>>>>>>>> # We know the user exists & is in the correct >>>>>>>>> OU >>>>>>>>> # so now we just check the password >>>>>>>>> con.simple_bind_s(username, password) >>>>>>>>> username = username_bare >>>>>>>>> >>>>>>>>> This peace of code is pretty unreliable : It start by re-creating >>>>>>>>> a email and store it in username vars if username it recieves from >>>>>>>>> web2py >>>>>>>>> is not a email before derive a username_bare from the altered >>>>>>>>> username var >>>>>>>>> and at the end it finally set username = username_bare... Why all >>>>>>>>> this just >>>>>>>>> to avoid create a var?! >>>>>>>>> >>>>>>>>> I propose to refator this using creating a new ID or identifier >>>>>>>>> var to store connection "identifier" var instead reusing the username >>>>>>>>> for >>>>>>>>> that. Then it will require to determine if the IS_NOT_EMAIL() should >>>>>>>>> go at >>>>>>>>> Auth level or ldap_auth. I don't know so much LDAP in general and >>>>>>>>> even less >>>>>>>>> the different implementation, so I don't know if some implementation >>>>>>>>> use >>>>>>>>> email as an identifier or not. Since, the Auth class as mechanism to >>>>>>>>> create >>>>>>>>> missing user I don't no if it intentional to allow the creation of >>>>>>>>> user >>>>>>>>> with email as username or not... So, maybe it a option in to use >>>>>>>>> IS_NOT_EMAIL() on username field in this case it will require that >>>>>>>>> IS_NOT_EMAIL be at level of Auth. Maybe, I didn't be able to make >>>>>>>>> work my >>>>>>>>> custom validator because of the order of validator (I had set >>>>>>>>> multiple >>>>>>>>> validator on username), I will try to set only IS_NOT_EMAIL and >>>>>>>>> report here >>>>>>>>> if it solve the problem I have with LDAP authentication. >>>>>>>>> >>>>>>>>> Thanks >>>>>>>>> >>>>>>>>> Richard >>>>>>>>> >>>>>>>>> >>>>>>>>> >>>>>>>>> On Wed, Aug 7, 2013 at 7:22 AM, Massimo Di Pierro < >>>>>>>>> [email protected]> wrote: >>>>>>>>> >>>>>>>>>> How would you change this? >>>>>>>>>> >>>>>>>>>> >>>>>>>>>> On Monday, 5 August 2013 15:42:39 UTC-5, Richard wrote: >>>>>>>>>> >>>>>>>>>>> Hello, >>>>>>>>>>> >>>>>>>>>>> Is there a way to prevent user to log with there email? I set >>>>>>>>>>> LDAP authentication, I create a username field on custom auth_user >>>>>>>>>>> model >>>>>>>>>>> and set auth.define_tables(username=**Tr**ue) >>>>>>>>>>> >>>>>>>>>>> But I notice that I can still login with [email protected]. In >>>>>>>>>>> this case, ldap_auth create a new user with first_name and username >>>>>>>>>>> = >>>>>>>>>>> [email protected] >>>>>>>>>>> >>>>>>>>>>> So, I think there is a flaw here in ldap_auth : >>>>>>>>>>> >>>>>>>>>>> if ldap_mode == 'ad': >>>>>>>>>>> # Microsoft Active Directory >>>>>>>>>>> if '@' not in username: >>>>>>>>>>> domain = [] >>>>>>>>>>> for x in ldap_basedn.split(','): >>>>>>>>>>> if "DC=" in x.upper(): >>>>>>>>>>> domain.append(x.split('=')[-1]****) >>>>>>>>>>> username = "%s@%s" % (username, >>>>>>>>>>> '.'.join(domain)) >>>>>>>>>>> username_bare = username.split("@")[0] >>>>>>>>>>> >>>>>>>>>>> Since it seems to recreate email as username... >>>>>>>>>>> >>>>>>>>>>> Thanks >>>>>>>>>>> >>>>>>>>>>> Richard >>>>>>>>>>> >>>>>>>>>> -- >>>>>>>>>> >>>>>>>>>> --- >>>>>>>>>> You received this message because you are subscribed to the >>>>>>>>>> Google Groups "web2py-users" group. >>>>>>>>>> To unsubscribe from this group and stop receiving emails from it, >>>>>>>>>> send an email to web2py+un...@**googlegroups.com. >>>>>>>>>> >>>>>>>>>> For more options, visit https://groups.google.com/** >>>>>>>>>> groups/opt_out <https://groups.google.com/groups/opt_out>. >>>>>>>>>> >>>>>>>>>> >>>>>>>>>> >>>>>>>>> >>>>>>>>> >>>>>>>> -- >>>>>>> >>>>>>> --- >>>>>>> You received this message because you are subscribed to the Google >>>>>>> Groups "web2py-users" group. >>>>>>> To unsubscribe from this group and stop receiving emails from it, >>>>>>> send an email to [email protected] <javascript:>. >>>>>>> For more options, visit https://groups.google.com/groups/opt_out. >>>>>>> >>>>>>> >>>>>>> >>>>>> >>>>>> >>>>> >>>> >>> >> > -- --- You received this message because you are subscribed to the Google Groups "web2py-users" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. For more options, visit https://groups.google.com/groups/opt_out.

