Hello, I just push these change in production, then face some exception... Some of our users have email as username some don't... So the fix I found is not the proper solution... I think we will have to improve ldap_auth logic to get out of the mud with this... I will look throught this a bit more see what I can come up with. What ldap_auth should do is to check user credentials, if the user log with email but he already have a user with the same username and password it should use this user instead of creating a new user with email as username and missing first_name and many other field empty...
Richard On Wed, Aug 21, 2013 at 9:34 AM, Richard Vézina <[email protected] > wrote: > No problem, thank for the follow... > > Richard > > > On Wed, Aug 21, 2013 at 4:51 AM, Massimo Di Pierro < > [email protected]> wrote: > >> I have not forgotten. I opened issue 1645. >> >> >> On Tuesday, 13 August 2013 15:13:30 UTC-5, Richard wrote: >> >>> Massimo, >>> >>> If you are concern about possible backward compatibility issue that this >>> change could raise... Maybe we could find a way to let the ldap_auth return >>> validator error to form (to user)... I could live with that too... I just >>> didn't find a easy way to make it works from ldap_auth (mean a lot of >>> refactoring could be required and I don't want to screw up ldap_auth for >>> others, I am not equiped to test it properly over different ldap >>> implementation). I just have to change a single line in tools.py + add a >>> IS_NOT_EMAIL() validator, that was the easiest... >>> >>> :) >>> >>> Richard >>> >>> >>> On Tue, Aug 13, 2013 at 10:19 AM, Richard Vézina >>> <[email protected]>wrote: >>> >>>> No problem! >>>> >>>> As long as there is a solution in the next version I will be happy... >>>> Curious to know what is bugging you though, adding a new validator? Maybe >>>> the IS_MAIL() could be hack in order that it usage could be reversed, >>>> something like this : IS_MAIL(..., complement=True), then it will return >>>> true if the string is not a email... So don't need a new validator. >>>> >>>> Richard >>>> >>>> >>>> >>>> >>>> On Tue, Aug 13, 2013 at 10:04 AM, Massimo Di Pierro < >>>> [email protected]> wrote: >>>> >>>>> 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]> 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=**T**rue)" 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_**m** >>>>>>> essage=T(auth.messages.is_**empt**y)), >>>>>>> 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]> wrote: >>>>>>> >>>>>>>> I would also add this : >>>>>>>> >>>>>>>> tmpvalidator = [IS_NOT_EMPTY(error_message=se****lf.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]> wrote: >>>>>>>> >>>>>>>>> Better then that I think : >>>>>>>>> >>>>>>>>> In gluon/tools.py in Auth login() near line 2006 : >>>>>>>>> >>>>>>>>> Replace : >>>>>>>>> tmpvalidator = IS_NOT_EMPTY(error_message=**sel** >>>>>>>>> f.messages.is_empty) >>>>>>>>> >>>>>>>>> With : >>>>>>>>> if 'username' in table_user.fields or \ >>>>>>>>> not self.settings.login_email_**vali**date: >>>>>>>>> tmpvalidator = >>>>>>>>> [IS_NOT_EMPTY(error_message=**se**lf.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]> 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]> wrote: >>>>>>>>>> >>>>>>>>>>> Ok! >>>>>>>>>>> >>>>>>>>>>> :) >>>>>>>>>>> >>>>>>>>>>> Richard >>>>>>>>>>> >>>>>>>>>>> >>>>>>>>>>> On Wed, Aug 7, 2013 at 10:10 AM, Massimo Di Pierro < >>>>>>>>>>> [email protected]> 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_**PROTOC****OL_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_**f**** >>>>>>>>>>>>>> irstname_attrib, >>>>>>>>>>>>>> >>>>>>>>>>>>>> user_lastname_attrib, >>>>>>>>>>>>>> user_mail_attrib]) >>>>>>>>>>>>>> result = con.search_ext_s( >>>>>>>>>>>>>> ldap_basedn, ldap.SCOPE_SUBTREE, >>>>>>>>>>>>>> "(&(sAMAccountName=%s)(%s))" % ( >>>>>>>>>>>>>> ldap.filter.escape_filter_** >>>>>>>>>>>>>> char****s(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/**grou**** >>>>>>>>>>>>>>> ps/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 web2py+un...@**googlegroups.com. >>>>>>>>>>>> For more options, visit https://groups.google.com/**grou** >>>>>>>>>>>> ps/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 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]. >> 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.

