Server-Side PreValidation of User Form and Conditionals (Design/Best Practice) - python

Issue: Create a method that verifies if username exists when Creating an Account, deny account if username exists, and advises the user to create a new username and repeat the process should need be.
I have some idea of how to do this based on some STACKS questions (1, 2) that I have read on here. Such as something like:
Send the username to the server.
Check for the existence of the
username in the Database.
Respond to the client with true or false
depending on the presence of the username.
Based on the response, send
the user a client side alert!
I am uncertain how to properly execute the process in the Sign Up Page (also known as the Create an Account page) using Pyramid and SQLAlchemy. Since I am newbie, I want to make sure I am creating code that is fast, efficient and with smart design. I want to ensure I am staying within best practices.
Right now in the User database, Username is UNIQUE; this causes the system to crash when a user tries to create a username that exists in the db. My code is missing something as there is a traceback that indicates DETAIL: Key (username)=(baseball) already exists. Any help or suggestions is truly appreciated! If I have a poor method, suggestions of a better method is highly welcomed!
Software: Python 2.7, Pyramid 1.5.7, SQLAlchemy 1.0.9
views.py
(code: to the create a user page and save new user)
#view_config(route_name='create_user', request_method='GET', renderer='templates/create_account.jinja2')
def user_form_view(request):
return {}
#view_config(route_name='save_new_user')
def save_new_user(request):
with transaction.manager:
username = request.params['username']
check_username = api.retrieve_user(username) #retrieves one_user
#check_users = api.retrieve_users() #this retrieves ALL the users
taken = False
for user in check_username: #prints out all user info
if username == user.username:
taken = True
break
if taken:
username = request.params['username']
password = request.params['password']
firstname = request.params['firstname']
lastname = request.params['lastname']
email = request.params['email']
new_user = api.create_user(username, password, firstname, lastname, email)
new_account = api.update_group_add_user('Registered User', new_user)
transaction.commit()
return HTTPSeeOther(location=request.route_url('login'))
Traceback:
IntegrityError: (raised as a result of Query-invoked autoflush; consider using a session.no_autoflush block if this flush is occurring prematurely) (psycopg2.IntegrityError) duplicate key value violates unique constraint "users_username_key"
DETAIL: Key (username)=(baseball) already exists.
[SQL: 'INSERT INTO users (username, firstname, lastname, email, password, institution, created_on) VALUES (%(username)s, %(firstname)s, %(lastname)s, %(email)s, %(password)s, %(institution)s, %(created_on)s) RETURNING users.id'] [parameters: {'username': u'baseball', 'firstname': u'jlo', 'lastname': u'lo', 'institution': None, 'created_on': datetime.datetime(2015, 11, 24, 22, 27, 20, 286260), 'password': '78d8045d684abd2eece923758f3cd781489df3a48e1278982466017f', 'email': u'j'}]
Updates based on suggestion below
Question:
Where should I create the function validate_registration -- outside of the registration_view function? Should this be a Boolean statement? Is this the best method? Where would transaction.commit() exist?
View Code with GET and POST:
def validate_registration_form(request):
with transaction.manager:
username = request.params['username']
check_username = api.retrieve_user(username)
password = request.params['password']
firstname = request.params['firstname']
lastname = request.params['lastname']
email = request.params['email']
if check_username is not None:
return False
else:
return True
#view_config(route_name='registration', renderer='templates/create_account.jinja2')
#view_config(route_name='save_registration', renderer='templates/create_account.jinja2')
def registration_view(request):
if request.method == 'GET':
return {} # render the empty form
elif request.method == 'POST':
if validate_registration_form(request): #save new_user and redirect
new_user = api.create_user(username, password, firstname, lastname, email)
new_account = api.update_group_add_user('Registered User', new_user)
transaction.commit()
raise HTTPSeeOther(location=request.route_url('login'))
else:
# form is not valid, re-render the form
# with the data user entered and an error message
return {
'error_message': 'username already taken',
'username': request.POST.get('username', ''),
'password': request.POST.get('password', ''),
'firstname': request.POST.get('firstname', ''),
'lastname': request.POST.get('lastname', ''),
'email': request.POST.get('email', '')
}
form:
<form action="/save_registration" method="POST">
<div class="form-group">
<dl>
<dt><label for = 'username'> Username: <em>single word--no spaces</em> </label></dt>
#more ....

Well, the classic approach to server-side form validation on submit is something like this (in pseudocode):
#view_config(route_name='registration', renderer='my_rego_form.jinja2') # handles both GET and POST
def rego_form(request):
if request.metod == 'GET':
return {} # render the empty form
elif request.method == 'POST':
if validate_rego_form(request):
# create a new user and redirect elsewhere
create_user(request)
raise HTTPFound('/post_registration_page')
else:
# form is not valid, re-render the form
# with the data user entered and an error message
return {
'error_message': 'Error creating user',
'username': request.POST.get('username', '')
'email': request.POST.get('email', '')
'phone_num': request.POST.get('phone_num', '')
}
else:
# some other HTTP method, not sure how to deal
So, basically, the form needs to be able to re-render itself with the data submitted by the client.
The validation method itself can be trivial and just check for the user with the given email in the db. Or, as you tried to do, instead of pre-validation you can try to just create a record and handle the exception if one occurs.

Related

Using passlib can register the password but can't verify it again getting error

I am using passlib to store the password in my sqlite database. I am not getting error while storing the new password(registration). But when I try to login with the same user I am getting this error 'TypeError: hash must be unicode or bytes, not sqlalchemy.orm.attributes.InstrumentedAttribute'.
My script
**models.py**
class Login(db.Model,UserMixin):
"Creates username and password"
id = db.Column(db.Integer,primary_key=True,nullable=False)
username = db.Column(db.String,nullable=False)
password = db.Column(db.String,nullable=False)
email = db.Column(db.String,nullable=False)
def __repr__(self):
return f"Login('{self.id}','{self.username}','{self.password}','{self.email}')"
**routes.py**
from myfolder.security import encrypt_password,check_encrypted_password
def login():
if request.method == 'POST':
username = request.form.get('username')
password1 = request.form.get('password')
data = {'username':username}
#fetch the email id of the user whose logged in
user_email_id = Login.query.filter(Login.username==username).values(Login.email,Login.password)
for logged_user in user_email_id:
logged_email_id = logged_user.email
hashed = logged_user.password
session['logged_user'] = logged_email_id
completion = validate(username)
if completion ==False:
error = 'error.'
else:
password_check = check_encrypted_password(password1,Login.password)
if password_check ==False:
error = 'error.'
else:
user = Login()
user.name=username
user.password=password
login_user(user)
error = 'Success'
api_response = {'data':data,'error':error}
return jsonify(api_response)
I have created new file called security.py
from passlib.context import CryptContext
pwd_context = CryptContext(
schemes=["pbkdf2_sha256"],
default="pbkdf2_sha256",
pbkdf2_sha256__default_rounds=30000
)
def encrypt_password(password):
return pwd_context.encrypt(password)
def check_encrypted_password(password, hashed):
return pwd_context.verify(password, hashed)
In the login method I tried different ways but nothing is working.I tried passing the password here.
password_check = check_encrypted_password(password1,password)
But I am getting this error
raise ValueError("hash could not be identified")
ValueError: hash could not be identified
How should I verify my password and login?
There is a bug in your code. The line
password_check = check_encrypted_password(password1,Login.password)
should be
password_check = check_encrypted_password(password1,hashed)
Instead of passing in the hashed password from the database, you are currently passing in the sqlalchemy column definition for the password.
There are number of other errors in you code that you should be aware of.
Most of the time you use "password1", but there is also one instance of "password".
If a username is provided that is not in the database, both "logged_email_id" and "hashed" variables would not be defined.
So I would suggest to refactor you code to this:
from myfolder.security import encrypt_password,check_encrypted_password
def login():
if request.method == 'POST':
username = request.form.get('username')
password = request.form.get('password')
data = {'username':username}
#fetch the email id of the user whose logged in
logged_user = Login.query.filter(Login.username==username).values(Login.email,Login.password).one_or_none()
if not logged_user:
error = 'error.'
else:
session['logged_user'] = logged_user.email
completion = validate(username)
if completion ==False:
error = 'error.'
else:
password_check = check_encrypted_password(password,logged_user.password)
if password_check ==False:
error = 'error.'
else:
user = Login()
user.name=username
user.password=password
login_user(user)
error = 'Success'
api_response = {'data':data,'error':error}
return jsonify(api_response)
In this code I have:
Replaced "password1" with "password", to ensure that "user.password=password" does not generated an error because password is not defined.
Replaced the for loop with "one_or_none()". This returns the first value or none, if the username cannot be found.
Check that the value returned from the database query exists, before trying to user the result of the query.

The view returned None (not returned an HttpResponse object)

This question might appear to be a duplicate and/or too boring. I have already read this, this, this, this and this questions & answers. But couldn't find solution that fits to my problem.
I'm new to Django framework. To learn it I want to create simple blog. When user clicks Register button (after filling required fields), following error thrown:
ValueError at /user/register/ The view user.views.register didn't
return an HttpResponse object. It returned None instead.
views.py
def register(request):
"""
Registers new user.
"""
if request.POST:
if request.method == 'POST':
personal_info = UserFormModel(request.POST)
if personal_info.is_valid():
email = personal_info.cleaned_data['email']
username = personal_info.cleaned_data['username']
if User.objects.filter(email=email).exists():
return HttpResponse('email error')
elif User.objects.filter(username=username).exists():
return HttpResponse('username error')
else:
return HttpResponse('saved')
else:
personal_info = UserFormModel()
return render_to_response('user/registration.html',
{
'title': 'Registration',
'username_error': 'Sorry, someone already has that username.',
'personal_info': personal_info,
},
context_instance=RequestContext(request))
If necessary I can share any files content.
Any helpful comment or answer would be appreciated.
In line
if personal_info.is_valid():
if personal info is not valid, it will return None.(return nothing)
add an else condition, because you are not handling a case.
if personal_info.is_valid():
# code here
else:
return HttpResponse('personal info provided not valid')
One of the better ways to handle situations like this from not occurring is to keep a dictionary for status message and result initialised at the start of the function, and return only at a single place instead of returning at multiple places, and storing result in result_dict.
result_dict = {'status': False}
and always return at the end of the function
return HttpResponse(json.dumps(result_dict))
This way you will never miss a case in returning values.
So Final code should look like
def register(request):
"""
Registers new user.
"""
result_dict = {'status': False}
if request.POST:
if request.method == 'POST':
personal_info = UserFormModel(request.POST)
if personal_info.is_valid():
email = personal_info.cleaned_data['email']
username = personal_info.cleaned_data['username']
if User.objects.filter(email=email).exists():
result_dict['message'] = 'email error'
elif User.objects.filter(username=username).exists():
result_dict['message'] = 'username error'
else:
result_dict['message'] = 'saved'
result_dict['status'] = True
else:
result_dict['message'] = 'personal info provided not valid'
else:
personal_info = UserFormModel()
return render_to_response('user/registration.html',
{
'title': 'Registration',
'username_error': 'Sorry, someone already has that username.',
'personal_info': personal_info,
},
context_instance=RequestContext(request))
return HttpResponse(json.dumps(result_dict))

How to fetch column values using SQLAlchemy?

I am using Flask+Python and to check if a username (and email) is already taken or not i am using this logic:
#app.route('/register', methods=['GET', 'POST'])
def register():
form = SignupForm()
if form.validate_on_submit():
user = Users.query.filter_by(username=form.username.data).first()
email = Users.query.filter_by(email=form.email.data).first()
if form.username.data in user:
error = 'Username already taken. Choose another'
elif form.email.data in email:
error = 'Email already registered. Login or register with another Email'
else:
user = Users(
form.username.data,
form.password.data,
#form.confirm.data ,
form.email.data,
1,
# form.cityaddress.data,
# form.countryaddress.data,
#form.accept_tos.data,
)
db.session.add(user)
db.session.commit()
return redirect(url_for('index'))
But its giving error like object has no attribute 'username'
I know my logic for fetching data from db is not correct. I have little knowledge of SQLalchemy.
Could you suggest me How can i fetch Username (and Email) column value from table Users and then check them if there are same as form.username.data ?
Your queries look fine, the return value from first() will be an instance of your User object, or None if there were no results:
u = Users.query.filter_by(username=form.username.data).first()
if u is not None:
print u.username
print u.email
So given that, here's what your logic could look like:
user_by_name = Users.query.filter_by(username=form.username.data).first()
user_by_email = Users.query.filter_by(email=form.email.data).first()
if user_by_name:
error = 'Username already taken. Choose another'
elif user_by_email:
error = 'Email already registered. Login or register with another Email'
else:
#Unique user and email
You could also do it in one query:
existing = Users.query.filter((Users.username == form.username.data) | (Users.email == form.email.data)).all()
if existing:
error = 'User or email taken'
Note the use of filter rather than filter_by - you cant use the bitwise operators in filter_by. Here's a quick working example
Your error confuses me. That said, your code looks okayish, except for the test. I use this then:
user = Users.query.filter_by(username=form.username.data).first()
...
if user is not None:
error("user already found")

Sqlite manipulates the string out of the database

In my login method I'm trying to check wether the password in the database corresponds to the password submitted by the user. I use this code:
#app.route('/login', methods=['GET', 'POST'])
def login():
error = None
db = get_db()
if request.method == 'POST':
cur = db.execute('select password from users where username = ?', (request.form['username'], ))
password = cur.fetchone()
print password
print request.form['password']
if request.form['password'] != password:
error = 'Invalid username/password combination'
else:
session['logged_in'] = True
flash('You were logged in')
return redirect(url_for('show_entries'))
return render_template('login.html', error=error)
For example when the password is default, this what the print commands show me:
print password : (u'default',)
print request.form['password'] : default
So they are indeed not equal. Does anybody knows how to fix this?
Each row returned by the database is a tuple of columns. You retrieved one row (.fetchone()) and that row is a tuple with one element (you selected only the password column). Simply take it out from the tuple using it's index:
password = cur.fetchone()[0]

Django: Transferring/Accessing forms full error message

On a website that I'm working on for my school, the user enters their school email and password, and if they have registered they log in. If not, a second part of the log in is revealed asking for a pen name and to confirm the password. Because of this, and my convoluted amateur Django programming, I have a list of errors named er. For instants, when the program tests whether the email is a school one, it might add to the er list "school email only". I am also using two form classes as well. The page uses ajax to call this function, which uses plain html instead of JSON because of the sites small size.
In the forms.py file I have:
class log_in(forms.Form):
username = forms.EmailField(error_messages= {'required': "Email Field is required,", 'invalid' : "Invalid Email Address."})
password = forms.CharField(help_text = 'Password Invalid')
class new_user(forms.Form):
username = forms.EmailField(error_messages = {'required': "Email Field is required,", 'invalid' : "Invalid Email Address."})
password = forms.CharField(required=True)
password2 = forms.CharField(required=True)
pen_name = forms.CharField(max_length=30, min_length=3, error_messages = {'required': "Pen Name is required", 'max_length': "Pen Name must be less than 30 characters", 'min_length': "Pen Name must be more than 3 characters"})
The problem is that I want to transfer the full error message that I specified in the error_message argument to the er list.
This is my views.py file
def user_log_in(request):
er = []
user_pass = log_in(request.POST)
if user_pass.is_valid(): # If it is valid at all
cleaned_info = user_pass.cleaned_data
email_bbn = cleaned_info['username'].split("#")
if 'bbns.org' in email_bbn: # Check if BBN email address
user_object = User.objects.filter(email = cleaned_info['username'])
if user_object.exists():
logged_in_user = auth.authenticate(username=cleaned_info['username'], password=cleaned_info['password'])
#add in is_active
if logged_in_user is not None: #If password is right
if user_object[0].get_profile().activated:
auth.login(request, logged_in_user)
return HttpResponseRedirect("")
else:
return HttpResponse("not_act")
else:
er.append("Incorrect Password")
else: # If new user
new_user_pass = new_user(request.POST)
if new_user_pass.is_valid():
cleaned_info_new = new_user_pass.cleaned_data
if cleaned_info_new['password'] == cleaned_info_new['password2']:
msg = "In order to activate your account at Knights of the Round Table, please click on this link:"
try:
send_mail('Activate', msg, 'michaelrgoldfine#gmail.com', [cleaned_info_new['username']], fail_silently=False)
new_user_object = User.objects.create_user(
username=cleaned_info_new['username'],
password=cleaned_info_new['password'],
email=cleaned_info_new['username']
)
new_user_profile = new_user_object.get_profile()
new_user_profile.pen_name = cleaned_info_new['pen_name']
new_user_profile.activated = False;
new_user_profile.save()
return HttpResponse("not_act")
except:
er.append("Error Sending Email")
else:
er.append('Passwords are not the same')
elif "TN" in request.POST: #If open but not filled in
print "TN"
er.append(new_user_pass.pen_name.error_messages)
else: # if new user field
print "n_usr"
return HttpResponse('n_usr')
else:
er.append("BBN email addresses only")
else:
for e in user_pass.errors:
er.append(e)
errors_template = Template("{% for e in errors %}<li>{{ e }}</li> {% endfor %}")
errors_html = errors_template.render(Context({'errors':er}))
return HttpResponse(errors_html)
I try to accsess the errors twice. Once, on the else you see right at the end with a for loop, and two elses up from that on elif 'TN'... The last one just returns the field thats invalid (so i get user_name or pen_name). The other one says that the form has no object pen_name or whatever I use it for.
It would be better to add errors to the actual form. Forms have an _errors dict attached to them that contain all the errors generated by the form. "Non-field errors" (errors that don't directly relate to a particular field or that relate to multiple fields) go in form._errors['__all__']. All field-specific errors go into the key of the field's name. So, errors for a foo field would go in form._errors['foo'].
Now, the list of errors for each item in the _errors dict is actually an ErrorList type, not a standard list. So, to add errors to the form you do:
from django.forms.util import ErrorList
form._errors.setdefault('foo', ErrorList()).append('Some error here')
Or, to add the error to non-field errors:
form._errors.setdefault('__all__', ErrorList()).append('Some error here')
Then, when your form renders, the errors will all fall naturally where they should, just like any normal validation error.
The array probably looks like error[i].field[i].error, so you're just calling the fieldname and not the error message. Call e.error in your Template() function.

Categories

Resources