Get username information from a cookie in Pyramid (Python) - 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__.

Related

Not raising error when I log in with a wrong user, SQLITE + Tornado

I am developing a simple login application using tornado and SQLITE. When I login the application does what I expect, the user get redirected to his route, https://some_url/user_n. My problem came when I give wrong credentials, here instead to get redirected to the login area, https://some_url/login, and raise a custom error message I got a 500 generic error page. So far, I have tried everything, but I am a kind of “brand new” to python and sure to Tornado server.
Below, you can see my code:
import tornado
from tornado.web import RequestHandler
import sqlite3
# could define get_user_async instead
def get_user(request_handler):
return request_handler.get_cookie("user")
# could also define get_login_url function (but must give up LoginHandler)
login_url = "/login"
# Initialize SQLITE3 parameters
db_file = "user_login.db"
connection = None
cursor = None
# optional login page for login_url
class LoginHandler(RequestHandler):
def get(self):
try:
errormessage = self.get_argument("error")
except Exception:
errormessage = ""
self.render("login.html", errormessage=errormessage)
def check_permission(self, username, password):
connection = sqlite3.connect(db_file)
cursor = connection.cursor()
cursor.execute(
"SELECT * FROM users WHERE username=? AND password=?", (username, password)
)
data = cursor.fetchone()
if username == data[1] and password == data[2]:
return True
return False
def post(self):
username = self.get_argument("username", "")
password = self.get_argument("password", "")
auth = self.check_permission(username, password)
if auth:
self.set_current_user(username)
self.redirect(self.get_argument("next", f"/{username}"))
else:
error_msg = "?error=" + tornado.escape.url_escape("Login incorrect.")
self.redirect(login_url + error_msg)
def set_current_user(self, user):
if user:
self.set_cookie("user", tornado.escape.json_encode(user), expires_days=None)
else:
self.clear_cookie("user")
# class DashboardHandler(RequestHandler):
# def post(self, *arg, **kwargs):
# user_from_URL = kwargs["user_id"]
# user_from_cookie = self.get_cookie("user", "")
# # do_some_permission_check()
# if user_from_URL != user_from_cookie:
# self.redirect(self.get_argument("next", f"/{user_from_cookie}"))
# optional logout_url, available as curdoc().session_context.logout_url
logout_url = "/logout"
# optional logout handler for logout_url
class LogoutHandler(RequestHandler):
def get(self, username):
username = self.current_user
self.clear_cookie(username)
self.redirect(self.get_argument("next", "/login"))
The error happens in the check_permission method - you are fetching a record from the database that does not exist. This means that data = cursor.fetchone() sets the value of data to None in the case where username/password pair does not exist in the database, but you try to then get data[0] in the next line which dies with TypeError: 'NoneType' object is not subscriptable, which in turn triggers the 500 error page.
To fix the error in the question, you should fix that line to:
if data and username == data[1] and password == data[2]:
But really you should do a lot more as you shouldn't:
store plaintext passwords in the database (use a salted hash)
use set_cookie to store user data (use set_secure_cookie and only store the username)

Allow user to switch accounts during a session

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')

How to know whether user is logged in or not in Pyrebase and Flask?

app = Flask(__name__)
firebase = pyrebase.initialize_app(config)
auth = firebase.auth()
db = firebase.database()
#app.route('/login', methods=["POST", "GET"])
def login():
message = ""
if request.method == "POST":
email = request.form["login_email"]
password = request.form["login_password"]
try:
user = auth.sign_in_with_email_and_password(email, password)
user = auth.refresh(user['refreshToken'])
user_id = user['idToken']
return redirect(url_for('admin'))
except:
message = "Incorrect Password!"
return render_template("login.html", message=message)
#app.route('/admin')
def admin():
return render_template("admin.html")
if __name__ == '__main__':
app.run()
How can I only load /admin page when the user is logged in? I know it has something to do with the user token, but I'm still not sure about how I could use the token to identify whether the user is logged in or not. Also, the user and user_id are not defined in admin()and only in login() since they're in a function.
So what do I need to change in my code in order to only load the /admin page when the user is logged in?
use flask session to store your key,if key exist then user is logged,
also you can access all session variables globally for individual session
from flask import Flask, session, request
import requests
import os
app = Flask(__name__)
app.secret_key = os.urandom(24)
firebase = pyrebase.initialize_app(config)
auth = firebase.auth()
db = firebase.database()
#app.route('/login', methods=["POST", "GET"])
def login():
message = ""
try:
print(session['usr'])
return redirect(url_for('admin'))
except KeyError:
if request.method == "POST":
email = request.form["login_email"]
password = request.form["login_password"]
try:
user = auth.sign_in_with_email_and_password(email, password)
user = auth.refresh(user['refreshToken'])
user_id = user['idToken']
session['usr'] = user_id
return redirect(url_for('admin'))
except:
message = "Incorrect Password!"
return render_template("login.html", message=message)
#app.route('/admin')
def admin():
try:
print(session['usr'])
return render_template("admin.html")
except KeyError:
return redirect(url_for('login'))
if __name__ == '__main__':
app.run()
if session['usr'] is not assigned then it will give key error which means that usr in not logged in. but note that in the process of logout you need to delete the session for that usr.

Django REST Framework client.login() not working. (User created)

I'm building simple API with Django REST Framework, everything works as expected with curl and API web admin, but if I run the following test:
class OrderTest(APITestCase):
username = 'admin'
password = '12345'
def setUp(self):
User.objects.create(
username=self.username,
password=self.password,
email='demo#demo.com',
is_superuser=True,
is_staff=True
)
def test_create_order_by_admin(self):
url = '/api/orders/'
data = {
'name': 'John Doe',
'phone': '380000000000',
'status': 1,
'email': 'jonhn.doe#gmail.com',
'date': datetime.now(),
}
# Cheking if user exist
self.assertEqual(User.objects.get(pk=1).username, self.username)
self.client.login(
username=self.username,
password=self.password,
)
response = self.client.post(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(Order.objects.count(), 1)
for key, value in data.items():
self.assertEqual(Order.objects.get().key, value)
it fails with the following error:
Failure
Traceback (most recent call last):
File "/home/linevich/projects/swebion.com/project/order_form/tests.py", line 71, in test_create_order_by_admin
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
AssertionError: 403 != 201
That means that client.login() not working.
Any ideas?
You shouldn't set the password directly. This will store password in clear text while Django will try a hashing algorithm when trying to login.
See set_password to store it encrypted.
Use authenticate function in place of client.login
Here i use generic class based view to login user..
class Login(generics.CreateAPIView):
'''
API for SignIn to return User models along with access token.
'''
def post(self, request, *args, **kwargs):
username = self.request.data.get('email', None)
password = self.request.data.get('password', None)
user = authenticate(username=username, password=password)
response = {}
if user:
access = AppCustomMethods()
access_token = access.create_access_token(user, request,
settings.XAMARIN_APPLICATION_NAME)
response_data = {}
response_data['access_token'] = access_token
signup_serializer = serializers.GetUserWithAllBusinesses(user)
response_data[settings.USER] = signup_serializer.data
response = GetAccesUtility.data_wrapper(response_data)
return Response(response, status=status.HTTP_200_OK)
else:
response['error'] = Messages.NOT_AUTHENTICATED # error_data
response["status_code"] = settings.HTTP_USER_ERROR
return Response(response)
user = authenticate(username=username, password=password)
Django authenticate function authenticate the user username or password and return user info. If it return user then a new access token is generated and return in response with status 200 else if user not return by authenticated function then error message return in response.
Problem was in using User.objects.create() insetad of User.objects.create_superuser(), thanks to #C14L.

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