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.

Reply via email to