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=**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]>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=**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] >>>>>> > 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/**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.

