Allow user to switch accounts during a session - python

I have a web app that I'm developing. User registers and logs in. Login requires username and password. The session starts with the username.
session['username'] = user.username
Currently the user only has one account per session. I want to allow the user to have multiple accounts per session. Example: Facebook allows you to have a personal page and public person page (or other type of page) under the same session. I am wanting something similar to that.
To do this, I am going to change the session to:
session['email'] = user.email
Assummming that username does not have a unique constraint in the database, this should allow me to switch between usernames in the same session.
Not having much experience with sessions, my questions are: Is this the correct way to do this? Is there a standard or better way to do this?

Based off of what I provided above, the app was further developed. The login is similar to FB in the sense that you provide your email and password. There is a default user given on registration.
def login():
if request.method == 'POST':
email = request.form.get('email', '')
password = request.form.get('password', '')
dbsession = sessionmaker(bind=get_db())()
error = None
login = dbsession.query(Login).filter(Login.email == email).one_or_none()
login_user, _ = dbsession.query(User, Login).\
filter(
User.email == Login.email,
User.username == email
).one_or_none()
print(login_user)
if login is None or login_user is None:
error = 'Username/Password Combination is invalid.'
else:
if login.check_password(password) is False:
error = 'Username/Password Combination is invalid.'
print(error)
if error is None:
session.clear()
session['email'] = login.email
session['username'] = login_user.email
session['userID'] = login_user.userID
return redirect(url_for('base'))
flash(error)
return render_template('auth/login.html')
After creating a new user, switching between them is done with the select user function, which is below.
#bp.route('/user/select', methods=('GET',))
def select_user():
session_email = session.get('email', None)
if session_email is not None:
db = get_db()
db_session = sessionmaker(bind=db)()
results = db_session.query(User, Login).\
filter(
User.email == Login.email,
Login.email == session_email
).all()
users = dict(map(lambda x: (x[0].username, x[0]), results))
requested_swap = request.args.get('user', None)
if requested_swap is not None:
if requested_swap in users:
session['username'] = users[requested_swap].username
session['userID'] = users[requested_swap].userID
db_session.close()
return render_template('auth/select_user.html', users=users)
return render_template('auth/select_user.html')

Related

Checking for an existing user in a database return user if exists return false if user does not exist

I'm refactoring my app.py for my flask application. I'm trying to get a user.py class handle all user related things. How should I structure my user.py class to return false if that user does not exist in my database?
app.py:
db = get_db_connection()
user = User(db, request.form.get("email"))
if not user.get_email(): # If user does not exist send user to registration page
return redirect("/register", email=request.form.get("email")) # go to the registration page and fill in the used e-mail address
# Check password and send to user_index if correct
password = request.form.get('password')
if user.check_password(password):
session['userID'] = user.get_id()
session['name'] = user.get_first_name()
session['email'] = user.get_email()
return redirect("/user_index")
return message("Wrong Password. Go to log in page to try again.")
user.py:
class User:
def __init__(self, database, email):
db = database
user = db.execute("SELECT * FROM users WHERE users.email = ?", [email]).fetchall()
if user:
self.__id = user['id']
self.__last_name = user['lastName']
self.__first_name = user['firstName']
self.__email = user['email']
self.__date_of_birth = user['dateOfBirth']
self.__hash = user['hash']
I understand that when I instantiate an object in python it should return none. How can I structure my code so that if a user with the given email does not exist I could get some type of false value? Once I get a false value I should be able to redirect the users to the registration page.
If I'm doing this completely wrong please point me in the right direction.
There are many things to mention here:
I recommend using an ORM like SQLAlchemy together with Flask. It will make your life a lot easier and you won't have to worry about some security issues (such as SQL Injection).
If a user is going to log in, it is a good practice to return the message "wrong credentials" if their email OR password is incorrect, so an attacker will not know which one is correct or incorrect, therefore they will have many, many more combinations to try. That is, by just changing the phrase "wrong password" to "wrong credentials" you will be covering a security hole in your application. The same message should be returned if a user is not registered.
I recommend reading up on the uses of underscore in Python, since you are apparently using it in a place where it isn't needed.
In Python you don't need to use getters and setters unless you need to do some preprocessing before getting or setting an attribute.
Finally, in Flask this is a common way to structure a small project:
__init__.py
from flask import Flask
from utils import get_db_connection
app = Flask(__name__)
db = get_db_connection()
utils.py
# Do the necessary imports.
def get_db_connection():
...
run.py
from . import app
if __name__ == "__main__":
app.run() # You can set multiple settings in run().
routes.py
from . import app
from user import User
#app.route("/login", methods=["POST"])
def login():
user = User.get(request.form.get("email"))
# This is not recommended.
# if not user:
# return redirect("/register", email=request.form.get("email"))
password = request.form.get('password')
if user and user.check_password(password):
session['userID'] = user.id
session['name'] = user.first_name
session['email'] = user.email
return redirect("/user_index")
return message("Wrong credentials. Go to log in page to try again.")
models.py
from . import db
class User:
def __init__(self, id, last_name, first_name, email, date_of_birth, hash):
self.id = id
self.last_name = last_name
self.first_name = first_name
self.email = email
self.date_of_birth = date_of_birth
self.hash = hash
def check_password(self, password):
return check_password_hash(self.hash, password)
#staticmethod
def get(email):
user_result = db.execute("SELECT * FROM users WHERE users.email = ?", [email]).fetchall()
if user_result:
return User(user_result['id'], user_result['lastName'], user_result['firstName'],
user_result['email'], user_result['dateOfBirth'], user_result['hash'])
return None
# Declare more classes for your database models!
# class Admin:
#
# ...

Why do I always get a blind parameter error?

The Problem:
I have been receiving an error whenever I tried to register a user from the UI. The idea is to submit the form and take the data into python and run an INSERT query in order to add it to the database to allow logging in.
Sample input:
Name: Kieron
Password(Hashed):
pbkdf2:sha256:150000$aQfm996V$af032bc39170c1e2b59cab53691ebb48e50d3824b851de6921c56f07f4a10d3c
Email: Kieron#example.com
Error Message:
> sqlalchemy.exc.StatementError: (sqlalchemy.exc.InvalidRequestError) A
> value is required for bind parameter 'password'
>[SQL: INSERT INTO
> users (name, password, email) VALUES(%(user)s, %(password)s,
> %(email)s)] [parameters: [{'user': 'Kieron', ':password':
> 'pbkdf2:sha256:150000$aQfm996V$af032bc39170c1e2b59cab53691ebb48e50d3824b851de6921c56f07f4a10d3c',
> 'email': 'kieron#example.com'}]]
Database Connection:
The database is connected with a sqlalchemy engine bind without using ORM with the intention of learning more about doing it without the ORM and the application config is below:
app = Flask(__name__)
app.config['DATABASE_URL']="url to db"
# Check for environment variable
if not os.getenv("DATABASE_URL"):
raise RuntimeError("DATABASE_URL is not set")
# Configure session to use filesystem
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
Session(app)
# Set up database
engine = create_engine(os.getenv("DATABASE_URL"))
db = scoped_session(sessionmaker(bind=engine))
Code Associated with the Problem:
I believe the following route is the source of the problem and I am not quite sure where I have gone wrong within it. The entire idea of the route is:
Clear previous session and take details from the form.
Run form entries against database to ensure unique entries.
Create password hash and input into the database.
#app.route("/register", methods=['POST','GET'])
def register():
session.clear()
if request.method == 'POST':
#check for empty fields
if not request.form.get("name"):
return apology("must provide username", 403)
elif not request.form.get("password"):
return apology("must provide password", 403)
elif not request.form.get("email"):
return apology("must provide email", 403)
#get variables from form
user = request.form.get("name")
password = request.form.get("password")
email = request.form.get("email")
#hash password
passwordHash = generate_password_hash(password, method='pbkdf2:sha256', salt_length=8)
#check email and username don't exist
if db.execute("SELECT name FROM users WHERE name = :user", {"user": user}).rowcount != 0:
return apology("Username already taken", 403)
elif db.execute("SELECT email FROM users WHERE email = :email", {"email": email}).rowcount != 0:
return apology("Email already has an account in system",403)
#add to db
db.execute("INSERT INTO users (name, password, email) VALUES(:user, :password, :email)", {"user":user, "password":passwordHash, "email":email})
db.commit()
return redirect("/login")
return render_template("register.html")
Question Summary:
I believe the error is coming from within the INSERT query, however I am not certain where.
Where am I going wrong and how could I make this work correctly?
You have a typo in the values to be inserted.
The current code has {"user":user, ":password":passwordHash, "email":email} while it should be {"user":user, "password":passwordHash, "email":email} (removed : before password)

Flask-Login logs me in, but whenever I do a GET request, immediately logs me out again

I'm trying to build a web app using Flask. My front page (/) checks to see if anyone is logged in and if they are not, displays a form asking for username and password. Whne the user submits the from, these credentials are validated, and if valid a user object is created and sent to flask_login using its login_user function:
#app.route('/', methods=['POST', 'GET'])
def front():
tem = jinjaEnv.get_template("front_page.html")
msg = "(no message)"
if request.method=='POST':
un = request.form['username']
pw = request.form['password']
user = modsal.getUser(un, pw)
if user.is_authenticated:
login_user(user)
msg = ("<span style='color:#080; background:#efe;'>"
"<i class='fa fa-check-circle'></i> "
"You are logged in</span>")
else:
msg = ("<span style='color:#800; background:#fee;'>"
"<i class='fa fa-times-circle'></i> "
"login failed</span>")
#//if
h = tem.render(
msg = msg,
)
return h
This works fine, and the user is logged in.
However, when I do a GET request to another page (or even the same one), flask-login logs me out. I have a user_loader callback, but it isn't being called by flask_login:
#loginManager.user_loader
def load_user(userId):
""" returns a user object (for Flask_login).
Given a userId (which is the username), returns an Engineer object
(if it was a valid user), or None if not.
#return Engineer|None
"""
pr("%s-=-=- userId=%r -=-=-%s",
termcolours.TermColours.MAGENTA,
userId,
termcolours.TermColours.NORMAL)
from modsal import session, Engineer
u = session.query(Engineer).filter(
Engineer.username == username).one_or_none()
# Note that if a user wasn't found, (u) will be None
# here, which is what loginManager wants.
return u
(Note that pr() prints debugging code to stderr.)
I wrote some debugging code to see what's going on. Before every request, this prints the session cookie:
#app.before_request
def checkSessionCookie():
sessionStr = request.cookies.get('session_8210')
decoded = decookie.decodeCookie(sessionStr)
pr("%s***** decoded=%s%s",
termcolours.TermColours.MAGENTA,
decoded,
termcolours.TermColours.NORMAL)
In my Jinja2 templates, I have a banner at the top of every page showing the username of the logged in user. This is implemented by calling currentUserName(), which returns a string containing the user name, or "" if none:
def currentUserName():
pr("%s current_user=%s %s",
termcolours.TermColours.RED,
current_user,
termcolours.TermColours.NORMAL)
if (not current_user) or current_user.is_anonymous:
prvars("current_user")
return ""
try:
un = str(current_user.username)
prvars("un")
return un
except:
return ""
jinjaEnv.globals['currentUserName'] = currentUserName
Note that this function also prints out debugging code when executing.
When I log in with a username of Mike and then do a GET / request, my stderr looks like this:
checkSessionCookie():113: ***** decoded={"_fresh":true,"_id":{" b":"MDY5N2MyNDhmZGU4YWVmZmE5NzMzNjEwODQwYmE2NWNhMmU2YzI4MDRiMWZlMmRiNmFhYjg2MmE5NDMyMGY2Y2RiYjBjMTNjOWYzMjE3ODk0YWVmMDk1YjA2ZWJlZjkyNmQ3MDE1MDdkZjI2MDRhNzg2MTI1NzFkOTU0MmJkM2M="},"csrf_token":{" b":"MTVlNGY4MDBjNWQwNThmODhjNzc2ZGZlMzRjODllNmQ5YzU5MWZlMA=="},"user_id":"Mike"}
currentUserName():72: current_user=<flask_login.AnonymousUserMixin object at 0x7f2f10bff690>
currentUserName():74
127.0.0.1 - - [10/Feb/2016 11:54:51] "GET / HTTP/1.1" 200 -
Note that:
before the request, the session cookie has the correct username of Mike
my load_user() function doesn't get called by flask_login
When Jinja2 renders the template for /, Flask's current_user is now an anonymous user
So, why is flask-login changing my current_user from a correct logged-in user, to an anonymous one? And how do I fix this?
FWIW, I'm using Flask (0.10.1), Flask-Login (0.3.2), itsdangerous (0.24).
Edit:
My user class is called Engineer. It's an SQLAlchemy class that also inherits from Flask-login's UserMixin.
class Engineer(Base, UserMixin):
__tablename__ = 'tblEngineers'
engineerId = Column('EngineerID', Integer(), primary_key=True)
username = Column('Username', String())
password = Column('Password', String())
firstName = Column('FirstName', String())
lastName = Column('LastName', String())
engineer = Column('Engineer', Integer())
def __repr__(self):
s = "<Engineer %d %r %r>" % (
self.engineerId, self.username, self.engineer)
return s
#========== stuff Flask-login needs: ==========
def get_id(self):
result = unicode(self.username)
pr("get_id() => %r", result)
return result
Unfortunately the get_id() function isn't getting called when flask_login automatically logs me out.
You need to check your User model. You need to make sure the model is returning the proper id you want to use. By default it will load via the id property of the model however I see you are using username. You probably need to add:
def get_id(self):
return self.username
You can see an example based on the code you submitted here.

Get username information from a cookie in Pyramid (Python)

I'm writing a Pyramid app that allows registration of arbitrary number of users in a database table. This is my login code:
#view_config(route_name='login', renderer='templates/login.jinja2')
def login(request):
username = request.params.get('username', '')
error = ''
if request.method == 'POST':
error = 'Login Failed'
authenticated = False
try:
authenticated = do_login(request)
except ValueError as e:
error = str(e)
if authenticated:
headers = remember(request, username)
return HTTPFound(request.route_url('home'), headers=headers)
return {'error': error, 'username': username}
where
def do_login(request):
username = request.params.get('username', None)
password = request.params.get('password', None)
if not (username and password):
raise ValueError('both username and password required')
manager = BCRYPTPasswordManager()
cur = request.db.cursor()
try:
cur.execute("SELECT password FROM users WHERE username=%s", [username])
except psycopg2.Error:
raise ValueError("That username already exists!")
actual_password = cur.fetchall()[0][0] # Extrrrract the data
return manager.check(actual_password, password)
I want to display the username on all views once a given user is authenticated. My understanding is that the authentication information is stored in a cookie, and that cookie looks like (auth_tkt=""). How do I get the "current" username from this cookie?
Or am I more confused than I realize?
You can get the authenticated username by calling request.authenticated_userid. You can find more about this in official docs.
I also tend to store the whole user object (and dbsession) in the request like this:
def includeme(config):
from .models.user import User
settings = config.get_settings()
dbsession_factory = get_dbsession_factory(settings)
config.add_request_method(
lambda request: dbsession_factory(),
'dbsession',
reify=True)
config.add_request_method(
lambda request: User.get_by_username(
request.dbsession,
request.authenticated_userid),
'user',
reify=True)
def get_dbsession_factory(settings):
engine = engine_from_config(settings, 'sqlalchemy.')
dbsession_factory = sessionmaker()
register(dbsession_factory)
dbsession_factory.configure(bind=engine)
return dbsession_factory
Then you just call config.include('your_app.models') in your app __init__.

flask-login user is set to anonymous after login

im new to flask and flask-login and ive been struggling with this for days.
Im trying to log a user in like this:
from creds import auth_username, auth_password, pgsql_dbuser, pgsql_dbpassword, pgsql_db1name
from flask import Flask, render_template, request, Response, redirect, url_for
from flask.ext.bcrypt import Bcrypt
from flask.ext.login import LoginManager, login_required, login_user, current_user, logout_user
import logging
import psycopg2
import uuid
import datetime
app = Flask(__name__)
app.secret_key = str(uuid.uuid4()) # <- required by login_manager.init_app(app)
bcrypt = Bcrypt(app)
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'index'
#app.route('/', methods=['GET','POST'])
def index():
page_name = '/'
if request.method == 'POST':
email = request.form['email']
candidate_password = request.form['password']
user = finduserindbbyemail(email)
if user != None:
password_hash = checkuserpasswordindb(email)
if bcrypt.check_password_hash(password_hash, candidate_password):
user_object = User(user)
result = login_user(user_object) # <- here for successful login
return redirect(url_for('loggedin', user_object=type(user_object), user=user, result=result, current_user=current_user))
else:
user_object = User(user)
error_message = "The password you entered is incorrect"
return render_template('index.html', error_message=error_message)
else:
error_message = "The email address you entered does not match any we have in our records"
return render_template('index.html', error_message=error_message)
if request.method == 'GET':
return render_template('index.html')
I have a User class and a user callback:
class User():
def __init__(self, user):
self.user = user
def is_authenticated(self):
return True
def is_active(self):
return True
def is_anonymous(self):
return False
def get_id(self):
return unicode(self.user)
#login_manager.user_loader
def load_user(user):
con = psycopg2.connect(database=pgsql_db1name, user=pgsql_dbuser, password=pgsql_dbpassword, host='localhost')
uuid = "'"+user+"'"
cur = con.cursor()
cur.execute("SELECT uuid FROM users WHERE uuid = "+ uuid)
uuid = cur.fetchone()
con.close()
if uuid != None:
user = unicode(uuid[0])
return User.get_id(user)
else:
return None
After authentication is successful (apparently?), the user is redirected to a loggedin page which has a #login_required decorator. But instead of loading the loggedin page, the app redirects the user to the login page, telling me the user isnt being logged in?
If try to send values to the page and i remove the #login_required decorator so i can see the page, this is what i see in the browser after 'logging in':
current_user.is_authenticated() = False
current_user.is_active() = False
current_user.is_anonymous() = True
current_user.get_id() = None
user_object = <type 'instance'>
user = 2ca1296c-374d-43b4-bb7b-94b8c8fe7e44
login_user = True
current_user = <flask_login.AnonymousUserMixin object at 0x7f2aec80f190> Logout
It looks like my user hasn't been logged and is being treated as anonymous?
Can anyone see what I've done wrong? I'm having a lot of trouble understanding how this is supposed to work.
Another reason you might not be able to log a user in or current_user is Anonymous after going through your login form: The active=false flag is set on the user in the db. This behavior is confirmed in the docs:
flask_login.login_user(user, remember=False, duration=None, force=False, fresh=True)[source]
Logs a user in. You should pass the actual user object to this. If the user’s is_active property is False, they will not be logged in unless force is True.
This will return True if the log in attempt succeeds, and False if it fails (i.e. because the user is inactive).
So, when you call login_user, you can do this:
login_user(user, remember=form.remember_me.data, force=True), if you want to allow inactive users to log in.
So.. I managed to get it to work, but not using the user_loader callback. For whatever reason, my user loader exhibits the same behaviour as this:
Flask-login with static user always yielding 401- Unauthorized
Anyway, I used a request_loader callback instead based on this example:
http://gouthamanbalaraman.com/blog/minimal-flask-login-example.html
so for a user logging in, which starts here:
if bcrypt.check_password_hash(password_hash, candidate_password):
user_object = User(user, password_hash)
result = login_user(user_object) # <- here for successful login
token = user_object.get_auth_token(user, password_hash)
return redirect(url_for('loggedin', token=token))
I create a user object which has the user's id and their password hash.
then i log the user in. then i create a time-serialized token of the user id and password hash using itsdangerous. the get_auth_token function is part of the User class. it looks like this:
class User():
def __init__(self, user, password_hash):
self.user = user
self.password = password_hash
.
.
.
def get_auth_token(self, user, password):
data = [str(self.user), self.password]
return serializer.dumps(data, salt=serializer_secret)
you need to create a serializer at the beginning of your code somewhere:
serializer = URLSafeTimedSerializer(serializer_secret)
So after the token is created, pass it to the loggedin view as a URL query parameter.
When you try to load a login_required page, like my loggedin page, which is where login_user redirects me to after a successful login, the request_loader callback is executed. it looks like this:
#login_manager.request_loader
def load_user_from_request(request):
if request.args.get('token'):
token = request.args.get('token')
max_age = 1
try:
data = serializer.loads(token, salt=serializer_secret, max_age=max_age)
username = data[0]
password_hash = data[1]
found_user = finduserindbbyuuid(username)
found_password = checkuserpasswordindbbyuuid(username)
if found_user and found_password == password_hash:
user_object = User(found_user, password_hash)
if (user_object.password == password_hash):
return user_object
else:
return None
else:
return None
except BadSignature, e:
pass
else:
return None
This is the point where my user_loader was failing. I was logging in successfully, but the user_loader was always returning None and so my user would be deemed as anonymous.
So with the request loader, it checks that the request URL contains a 'token' argument in the query string. if so, it takes its value and using itsdangerous, deserializes the data.
you can make the token expire with timed serializers, but there are also non timed ones. after the token is deserialized, take the user and password hash and check in the database if they exist, in exactly the same way that the user_loader was supposed to work.. i imagine? my user_loader didnt work so i was most probably doing it wrong.
anyway if a user and password match in the db, then return the user object and bam, login works.
Im not sure if im doing it the right way, cos pretty much flying by the seat of my pants. i saw examples where people used the token_loader, rather than the request_loader callback function, to load the token, but i couldnt figure out how to set & get the auth token to & from the client. maybe ill figure it out... one day...
if you have the same problem, maybe this might help? or just let me know what you think
cheers
I found this page when searching for help with Flask-Login + Flask-Dance. I was seeing current_user as AnonymousUserMixin in a handler with the #login_required decorator. In my case making sure #app.route is on the line above #login_required fixed the problem. The correct order is in the docs: https://flask-login.readthedocs.io/en/latest/#flask_login.login_required.

Categories

Resources