# require username, not email on signup
auth.define_tables(username=True)
auth.define_tables(username=True)
db.auth_user.username.requires.insert(0,IS_MATCH("[a-z].*"))
auth.define_tables(username=True)
db.auth_user.username.requires=IS_MATCH('[a-z].*', error_message='letters only')
auth.settings.table_user.username.requires = [IS_NOT_IN_DB(db, auth.settings.table_user.username),
IS_LENGTH(30,6,'must be between 6-30 letters'),
IS_MATCH('[.a-z].*', error_message='lower case only and periods only'),
IS_EXPR("value[:1]<>'.'", error_message='User name can\'t start with period'),
IS_EXPR("value.find(' ') < 0", error_message='User name can\'t contain spaces'),
IS_EXPR("value.find('-') < 0", error_message='User name can\'t contain dashes'),
IS_EXPR("value.find('_') < 0", error_message='User name can\'t contain underscores'),
]
To make is same as google user name policy:They allow periods, but NOT as a first letter - I added to your RegExI'm almost got it, I want to use exactly what Google uses, (see image attached) - I bet this could be on RegEx, but won't have separate messages then.auth.settings.table_user.username.requires = [IS_NOT_IN_DB(db, auth.settings.table_user.username),
IS_LENGTH(30,6,'must be between 6-30 letters'),
IS_MATCH('[.a-z].*', error_message='lower case only and periods only'),
IS_EXPR("value[:1]<>'.'", error_message='User name can\'t start with period'),
IS_EXPR("value.find(' ') < 0", error_message='User name can\'t contain spaces'),
IS_EXPR("value.find('-') < 0", error_message='User name can\'t contain dashes'),
IS_EXPR("value.find('_') < 0", error_message='User name can\'t contain underscores'),
]I'll keep testing, thanksRob
I'm almost got it, I want to use exactly what Google uses, (see image attached) - I bet this could be on RegEx, but won't have separate messages then.
. I wouldn't say forbid, but if your name google user name is jonlun, or jon.lun you can sign in with JonLun, or JON.LUN -But. I notice in web2py, I can have users like JSMITH, jSmith, smith - and I can't think of a reason I would want that.
. No, I want just like google, you can have periods, but NOT as first letter. (I have plans for first letter NOT being a period)When you say: '[.a-z].*' is probably too permissive,is that because someone could enter names like john..........smith ?
auth.define_tables(username=True)
db.auth_user.username.requires.insert(0,IS_MATCH("[a-z].*"))the .insert seemed to give me error.
Important Note:I had to add, IS_NOT_IN_DB, or new registrations would OVERWRITE existing ones,which surprised me a lot.
auth.define_tables(username=True)
## create all tables needed by auth if not custom tables
auth.define_tables()
auth.settings.table_user.username.requires=IS_MATCH('[a-z].*', error_message='letters only')
3- Try to register him again, and it will not work because of the duplicate email address,but if I changed the email address to , jsmithxxx[at]gmail.comand left user name jsmith - AND enter a new password, not jsmith's password, it lets me into jsmith's original account.
Anthony:You're correct about log gin out and back in, but I did gain access to jsmith's account upon registration,and I could (and did)change his password in profile, and now I control his account - locking smith out.I did assume that the "old validator" would still fire, and not be replacedwith just my validator.- but used WITH my validator.
db.auth_user.username.requires = [list, of, validators]
db.auth_user.username.requires = IS_MATCH(...)
If you add a field called "username", it will be used in place of "email" for login. If you do, you will need to add a validator as well:
1. | auth_table.username.requires = IS_NOT_IN_DB(db, auth_table.username) |
auth.define_tables(username=True)
db.auth_user.username.requires.insert(0,IS_MATCH("[a-z].*"))
I never got this to work, as I mentioned above
auth.define_tables(username=True)
db.auth_user.username.requires.insert(0,IS_MATCH("[a-z].*"))
# -*- coding: utf-8 -*-
#########################################################################
## This scaffolding model makes your app work on Google App Engine too
## File is released under public domain and you can use without limitations
#########################################################################
## if SSL/HTTPS is properly configured and you want all HTTP requests to
## be redirected to HTTPS, uncomment the line below:
# request.requires_https()
if not request.env.web2py_runtime_gae:
## if NOT running on Google App Engine use SQLite or other DB
db = DAL('sqlite://storage.sqlite')
else:
## connect to Google BigTable (optional 'google:datastore://namespace')
db = DAL('google:datastore')
## store sessions and tickets there
session.connect(request, response, db = db)
## or store session in Memcache, Redis, etc.
## from gluon.contrib.memdb import MEMDB
## from google.appengine.api.memcache import Client
## session.connect(request, response, db = MEMDB(Client()))
## by default give a view/generic.extension to all actions from localhost
## none otherwise. a pattern can be 'controller/function.extension'
response.generic_patterns = ['*'] if request.is_local else []
## (optional) optimize handling of static files
# response.optimize_css = 'concat,minify,inline'
# response.optimize_js = 'concat,minify,inline'
#########################################################################
## Here is sample code if you need for
## - email capabilities
## - authentication (registration, login, logout, ... )
## - authorization (role based authorization)
## - services (xml, csv, json, xmlrpc, jsonrpc, amf, rss)
## - old style crud actions
## (more options discussed in gluon/tools.py)
#########################################################################
from gluon.tools import Auth, Crud, Service, PluginManager, prettydate
auth = Auth(db, hmac_key=Auth.get_or_create_key())
crud, service, plugins = Crud(db), Service(), PluginManager()
## create all tables needed by auth if not custom tables
#auth.define_tables()
auth.define_tables(username=True)
db.auth_user.username.requires.insert(0,IS_MATCH("[a-z].*"))
## configure email
mail=auth.settings.mailer
mail.settings.server = 'logging' or 'smtp.gmail.com:587'
mail.settings.sender = 'y...@gmail.com'
mail.settings.login = 'username:password'
## configure auth policy
auth.settings.registration_requires_verification = False
auth.settings.registration_requires_approval = False
auth.settings.reset_password_requires_verification = True
## if you need to use OpenID, Facebook, MySpace, Twitter, Linkedin, etc.
## register with janrain.com, write your domain:api_key in private/janrain.key
from gluon.contrib.login_methods.rpx_account import use_janrain
use_janrain(auth,filename='private/janrain.key')
#########################################################################
## Define your tables below (or better in another model file) for example
##
## >>> db.define_table('mytable',Field('myfield','string'))
##
## Fields can be 'string','text','password','integer','double','boolean'
## 'date','time','datetime','blob','upload', 'reference TABLENAME'
## There is an implicit 'id integer autoincrement' field
## Consult manual for more options, validators, etc.
##
## More API examples for controllers:
##
## >>> db.mytable.insert(myfield='value')
## >>> rows=db(db.mytable.myfield=='value').select(db.mytable.ALL)
## >>> for row in rows: print row.id, row.myfield
#########################################################################
--
auth.define_tables(username=True)
db.auth_user.username.requires.insert(0,IS_MATCH("[a-z].*"))
from gluon.tools import Auth, Crud, Service, PluginManager, prettydate
auth = Auth(db, hmac_key=Auth.get_or_create_key())
crud, service, plugins = Crud(db), Service(), PluginManager()
## - START CUSTOMIZATION - - - - - - - - - - - - - - - - - - - - - - ##
# | Summary:
# | Modify web2py to allow user registrations similar to
# | Google registrations.
# | i.e.
# | - lower case only [a-z]
# | - numbers [0-9] and period are OK
# | - can't end in a period
# | - can't start with a period
# | - can't have consecutive periods
# | - min 8 letter password
# | - username can't be changed once registered
# |
# | Note: Messages are nearly same as Google displays
## create all tables needed by auth if not custom tables
# use usernames rather than email addresses to register
auth.define_tables(username=True)
# allow username only on registration, but can only
# be viewed (readable) in Profile
# user can't change username once registered.
if auth.is_logged_in():
db.auth_user.username.writable = False
db.auth_user.username.readable = True
#custom message for password length - like Google
# ref:
"""
https://groups.google.com/forum/?fromgroups#!searchin/web2py/$20default$20length$20for$20password/web2py/k5os3bMz228/vG-UOLbhcBUJ[1-25]
"""
db.auth_user.password.requires.insert(0,IS_LENGTH(minsize=8))
db.auth_user.password.requires = CRYPT(key=auth.settings.hmac_key, min_length=8)
#add a comments to exlain policy
db.auth_user.password.comment='minimum 8 letters'
db.auth_user.username.comment='min. 6 letters (a-z), you may use numbers, and periods.'
# apply nearly identical username policy and message that Google Accounts use.
# this OVERWRITES web2py's default username validation
# reference and thanks to web2py community for help:
# https://groups.google.com/forum/?fromgroups#!starred/web2py/HBODB00HMfU[1-25]
auth.settings.table_user.username.requires = [IS_LENGTH(30,6,'Please use between 6 and 30 characters.'),
IS_MATCH('^[a-z0-9.]*$', error_message='Please use only letters (a-z) and numbers (0-9), and periods.'),
IS_NOT_EMPTY(error_message='You can\'t leave this empty. '),
IS_EXPR("value[0]<>'.'", error_message='The FIRST character of your username should be a letter (a-z) or number.'),
IS_EXPR("value[-1]<>'.'", error_message='The LAST character of your username should be a letter (a-z) or number.'),
IS_EXPR("str(value).find('..')==-1",error_message='A fan of punctuation! Alas, usernames can\'t have consecutive periods.'),
IS_NOT_IN_DB(db, auth.settings.table_user.username, 'Someone already has that username. ')
]
## - END CUSTOMIZATION - - - - - - - - - - - - - - - - - - - - - - ##
db.auth_user.password.requires.insert(0,IS_LENGTH(minsize=8))
Hey Jon:
Q:
> BTW, are you deliberately forbidding upper-case letters?
A:
. Yes, just like Google does, usernames are lowercase,
although if your gmail username is
johnsmith
you can log in with
JohnSmith, or
JOHNSMITH, or
Johnsmith
etc. but there is ONLY ONE user.
At least for my app, Just case lowercase names are best...
user's have enough trouble remembering names without burden of case-sensitivity.
Note, you shouldn't need:db.auth_user.password.requires.insert(0,IS_LENGTH(minsize=8))
given that you specify min_length in the CRYPT validator. You might consider the IS_STRONG validator as well.
db.auth_user.password.requires.insert(0,IS_LENGTH(minsize=8))
It's maybe worth pointing out that these validators should be imposed only when registering or changing a password, not during login. The problem with having password validators on login is that they leak password constraints to an attacker. (Of course, the registration form can be used to extract this information as well, but still...)
>validators on login is that they leak password constraints to an attacker.
(Of course, the registration form can be used to extract this information as well, but still...)
- I think I understand, when you say "leak"--
is it just a matter than anyone would see the message on the screen, ie. min 8 letters?
or is there more of a technical security leak you are referring to.
It's maybe worth pointing out that these validators should be imposed only when registering or changing a password, not during login. The problem with having password validators on login is that they leak password constraints to an attacker. (Of course, the registration form can be used to extract this information as well, but still...)Looks like the code does remove the min_length constraint of CRYPT for login: http://code.google.com/p/web2py/source/browse/gluon/tools.py#1829, but doesn't do anything about IS_STRONG. Do you think we should change that?
I did assume that the "old validator" would still fire, and not be replacedwith just my validator.- but used WITH my validator.
db.auth_user.username.requires = [list, of, validators]
db.auth_user.username.requires = IS_MATCH(...)
With all of the below validations....
1- Do they ALL execute
or
2- Execution stops at the first one that fails,
What I mean is say a user uses a username="bill"
the first validator will display the message, but does web2py continue to evaluate the others?
If so..
Wouldn't it be wise to put the most likely validators at the beginning of the list?
badlist = ['frig', 'asdf', 'poop']
BADWORDS = re.compile(r'|'.join(badlist))
auth.settings.table_user.username.requires = [IS_LENGTH(30,6,'Please use between 6 and 30 characters.'),
IS_MATCH('^[a-z0-9.]*$', error_message='Please use only letters (a-z) and numbers (0-9), and periods.'),
IS_NOT_EMPTY(error_message='You can\'t leave this empty. '),
IS_EXPR("value[0]<>'.'", error_message='The FIRST character of your username should be a letter (a-z) or number.'),
IS_EXPR("value[-1]<>'.'", error_message='The LAST character of your username should be a letter (a-z) or number.'),
IS_EXPR("str(value).find('..')==-1",error_message='A fan of punctuation! Alas, usernames can\'t have consecutive periods.'),
IS_EXPR("BADWORDS.search(value)", error_message='Bad word'),
IS_NOT_IN_DB(db, auth.settings.table_user.username, 'Someone already has that username. ')
]
## - START CUSTOMIZATION - - - - - - - - - - - - - - - - - - - - - - ##
# | Summary:
# | Modify web2py to allow user registrations similar to
# | Google registrations.
# | i.e.
# | - lower case only [a-z]
# | - numbers [0-9] and period are OK
# | - can't end in a period
# | - can't start with a period
# | - can't have consecutive periods
# | - min 8 letter password
# | - username can't be changed once registered
# |
# | Note: Messages are nearly same as Google displays
## create all tables needed by auth if not custom tables
# use usernames rather than email addresses to register
auth.define_tables(username=True)
# allow username only on registration, but can only
# be viewed (readable) in Profile
# user can't change username once registered.
#if auth.is_logged_in():
# db.auth_user.username.writable = False
# db.auth_user.username.readable = True
#custom message for password length - like Google
# ref:
"""
https://groups.google.com/forum/?fromgroups#!searchin/web2py/$20default$20length$20for$20password/web2py/k5os3bMz228/vG-UOLbhcBUJ[1-25]
"""
#add a comments to exlain policy
db.auth_user.username.comment='NO BAD WORDS..min. 6 letters (a-z), you may use numbers, and periods.'
# apply nearly identical username policy and message that Google Accounts use.
# this OVERWRITES web2py's default username validation
# reference and thanks to web2py community for help:
# https://groups.google.com/forum/?fromgroups#!starred/web2py/HBODB00HMfU[1-25]
# this import is required in web2py
import base64, re
#let's assume:
# username can't contain spaces, just a-z and periods
# 'frig' is a very bad word, and "poop" too :)
# 'sadf' is a racial slur
# so even if a person's name as frig, or asdf in it
# we will not let them use that.
# asdf - is a bad username
# asdfyou - is a bad username
# youasdf - is a bad username
badlist = ['frig', 'asdf', 'poop']
BADWORDS = re.compile(r'|'.join(badlist))
auth.settings.table_user.username.requires = [IS_LENGTH(30,6,'Please use between 6 and 30 characters.'),
IS_MATCH('^[a-z0-9.]*$', error_message='Please use only letters (a-z) and numbers (0-9), and periods.'),
IS_NOT_EMPTY(error_message='You can\'t leave this empty. '),
IS_EXPR("value[0]<>'.'", error_message='The FIRST character of your username should be a letter (a-z) or number.'),
IS_EXPR("value[-1]<>'.'", error_message='The LAST character of your username should be a letter (a-z) or number.'),
IS_EXPR("str(value).find('..')==-1",error_message='A fan of punctuation! Alas, usernames can\'t have consecutive periods.'),
IS_EXPR("BADWORDS.search(value)", error_message='Bad word'),
IS_NOT_IN_DB(db, auth.settings.table_user.username, 'Someone already has that username. ')
]
Still trying to make my username example, google-like.
Google prohibits a very small set of bad words.
I have a BADWORDS working outside of the validation, but when I insert in validation, I get error.
Any help would be appreciated, I bet it is one little thing :)
--
Question:
Would it be worth considering an actual validator for username and user-like fields.
IS_LIKE_GOOGLEIS_LIKE_GOOGLE validator enforces a username policy similar to Google Accounts (as of 2012),
min 6 - 30 characters, only letters (a-z) and numbers (0-9), and periods, can't start or end with a period,
can't have consecutive periods, and of course can't exist in database.
Note: if an optional
IS_LIKE_GOOGLE(db, 'table.fieldname') - then any user created user type field would be validated,
badlist = ['frig', 'asdf', 'poop']
BADWORDS = re.compile(r'|'.join(badlist))
...
IS_EXPR("BADWORDS.search(value)", error_message='Bad word',
environment=dict(BADWORDS=BADWORDS))