SQLAlchemy & PassLib - python

tl;dr -- How do I use a Python-side library such as PassLib to hash passwords before inserting them into a MySQL DB with SQLAlchemy?
Alright, so I've been banging my head on my desk for a day or two trying to figure this out, so here it goes:
I am writing a web application using Pyramid/SQLAlchemy and I'm trying to interface with my MySQL database's Users table.
Ultimately, I want to do something like the following:
Compare a password to the hash:
if user1.password == 'supersecret'
Insert a new password:
user2.password = 'supersecret'
I'd like to be able to use PassLib to hash my passwords before they go to the database, and I'm not really a fan of using the built-in MySQL SHA2 function since it's not salted.
However, just to try it, I do have this working using the SQL-side function:
from sqlalchemy import func, TypeDecorator, type_coerce
from sqlalchemy.dialects.mysql import CHAR, VARCHAR, INTEGER
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column
class SHA2Password(TypeDecorator):
"""Applies the SHA2 function to incoming passwords."""
impl = CHAR(64)
def bind_expression(self, bindvalue):
return func.sha2(bindvalue, 256)
class comparator_factory(CHAR.comparator_factory):
def __eq__(self, other):
local_pw = type_coerce(self.expr, CHAR)
return local_pw == func.sha2(other, 256)
class User(Base):
__tablename__ = 'Users'
_id = Column('userID', INTEGER(unsigned=True), primary_key=True)
username = Column(VARCHAR(length=64))
password = Column(SHA2Password(length=64))
def __init__(self, username, password):
self.username = username
self.password = password
This was copied from the example 2 at http://www.sqlalchemy.org/trac/wiki/UsageRecipes/DatabaseCrypt
So that works and allows me to use the built-in MySQL SHA2 function (by calling func.sha2()) and do exactly what I want. However, now I'm trying to replace this with PassLib on the Python side.
PassLib presents two functions: one to create a new password hash, and one to verify a password:
from passlib.hash import sha256_crypt
new_password = sha256_crypt.encrypt("supersecret")
sha256_crypt.verify("supersecret", new_password)
I can't quite figure out how to actually implement this. Having read all the documentation, I think it is either a different form of TypeDecorator, a custom type declaration, a hybrid value, or a hybrid property. I tried following this, but it doesn't really make sense to me nor does the code suggested there actually run.
So, to sum up my question -- how do I overload the = and == operators so that they run things through the appropriate hash functions?

PasswordType from sqlalchemy-utils should be the best fit for this issue. It uses passlib. Snipped from the docs:
The following usage will create a password column that will automatically hash new passwords as pbkdf2_sha512 but still compare passwords against pre-existing md5_crypt hashes. As passwords are compared; the password hash in the database will be updated to be pbkdf2_sha512.
class Model(Base):
password = sa.Column(PasswordType(
schemes=[
'pbkdf2_sha512',
'md5_crypt'
],
deprecated=['md5_crypt']
))
Verifying password is as easy as:
target = Model()
target.password = 'b'
# '$5$rounds=80000$H.............'
target.password == 'b'
# True

As I understand it, what you want is this:
Encrypt the user's password when creating the account. Use your salt and algorithm
When the user logs in, hash the incoming password the same way you did when you stored it
Compare the two hashes using regular string comparison in your db request
So, something like this for the login code:
from passlib.hash import sha256_crypt
passHash = sha256_crypt.encrypt(typed_password)
// call your sqlalchemy code to query the db with this value (below)
// In your SQLAlchemy code assuming "users" is your users table
// and "password" is your password field
s = users.select(and_(users.username == typed_username, users.password == passHash))
rs = s.execute()
rs would be the resultset of matching users (should be zero or one of course).
Disclaimer - I did not test any of this
Edit:
Thank you for pointing out that PassLib uses a different salt each time it's run. Your best bet in that case, since there doesn't seem to be a straightforward way to do it with sqlalchemy, is the below:
s=users.select(users.username == typed_username)
rs = s.execute()
userRow = rs.fetchone()
if (sha256_crypt.verify(userRow.password)):
# you have a match
Also, to address your request for abstracting: a common methodology for handling this operation is to create a "security" utility class for getting the user (object) that matches the passed login credentials.
The problem with your current setup is that the User constructor has two different operational goals that, though related, are not necessarily the same thing: authenticating a user and getting a User object (for, say, a list of users in a group). The constructor becomes needlessly complex in that case. It's better to put that logic where it can be encapsulated with other security or login-related functionality such as logging in a user via session ID or SSO token instead of username/password:
security.loginUser(username, password)
# or security.loginUser(single_sign_on_token), etc. for polymorphic Security
loggedInUser = security.getLoggedInUser()
... later ...
otherUser = User(username) #single job, simple, clean

Related

Password not working when inserting users in Sonarqube in Python

user_password = 'admin'
salt = bcrypt.gensalt(rounds=16)
password = bcrypt.hashpw(user_password.encode('utf-8'), salt)
print(password)
sql_statementInsert = "INSERT into users
(crypted_password,login,uuid,external_login,external_identity_provider,external_id,is_root,onboarded,hash_method,active,name)
values ('{}','cosmin','1ea2ad82-b07c-11ea-b3de-0242ac130004','cosmin','sonarqube','user',false,true,'BCRYPT',true,'cosmin') on conflict (login) do nothing;".format(password.decode("utf-8"))
The generated hash for this example is: $2b$08$1KDDzD5DoVOEopOWUb0Rbu8A0FtYtI02BopFoY4Qxp5URuf3KA0s2.
I have this code which is generating some hash based on the user_password, but when trying to log in with "admin" value is not working. But when I am inserting directly the following hash in the crypted_password is working: $2a$12$uCkkXmhW5ThVK8mpBvnXOOJRLd64LJeHTeCkSuB3lfaR2N0AYBaSi
Solved this by using the good library, I needed py-bcrypt, the wrong one was bcrypt only. Also if you use the new lib you don't need to decode the password when you insert it.

how to list an object arguments in python?

Consider this code:
import mysql.connector
db = mysql.connector.connect(url = "url", username = "myusername", password = "mysecretpassword")
Is it possible to get the argument of the password parameter from the db object?
Not sure why you would want to, but no you can't. You can read the mysql.connector documentation and see that it does not expose the password as an attribute.
You could also run dir(db) to see what it supports.

hashnig passwords with argon2_cffi

I am trying to understand how should I use argon2_cffi to store hashed passwords in my database.
Specifically, I am using this code to write the hashed password into my PostgreSQL table.
from argon2 import PasswordHasher
ph = PasswordHasher()
new_user = User(
name=POST.get('name', 'default_value'),
fullname=POST.get('fullname', 'default_value'),
nickname=POST.get('nickname', 'default_value'),
hashed_password=ph.hash(POST.get('password', 'default_value')))
session.add(new_user)
However, this produces a different password everytime the user inserts a password in my form, although the inserted text is the same.
Of course, I know this is he correct behaviour, but what should I do in order to verify that a given registered user has inserted the right password if I cannot produce the same hash?
Sorry, found out myself in the docs...
import argon2
ph = argon2.PasswordHasher()
def login(db, user, password):
hash = db.get_password_hash_for_user(user)
# Verify password, raises exception if wrong.
ph.verify(hash, password)
# Now that we have the cleartext password,
# check the hash's parameters and if outdated,
# rehash the user's password in the database.
if ph.check_needs_rehash(hash):
db.set_password_hash_for_user(user, ph.hash(password))

Python, Flask, MySQL - Check if email exists in database

I'm pretty new to SQL but I need it for a school project. I'm trying to make a (python) web-app which requires accounts. I'm able to put data into my SQL database but now I need some way to verify if an e-mail (inputted via html form) already exists inside the database. Probably the easiest query ever but I haven't got a single clue on how to get started. :(
I'm sorry if this is a duplicate question but I can't find anything out there that does what I need.
if you are using SQLAlchemy in your project:
#app.route("/check_email")
def check_email():
# get email from you form data
email = request.form.get("email")
# check if someone already register with the email
user = Users.query.filter_by(email=email).first()
if not user:
# the email doesnt exist
pass
else:
# the email exists
pass
Users.query.filter_by(email=email).first() equal to SQL:
SELECT * from users where email="EMAIL_FROM_FORM_DATA"
if you are using pymsql(or something like that):
import pymsql
#app.route("/check_email")
def check_email():
# get email from you form data
email = request.form.get("email")
conn = connect(host='localhost',port=3306,user='',password='',database='essentials')
cs1 = conn.cursor()
params = [email]
# cursor return affected rows
count = cs1.execute('select * from users where email=%s', params) # prevent SqlInject
if count == 0:
# count 0 email
else:
# the email exists
# and if you want to fetch the user's info
user_info = cs1.fetchall() # the user_info should be a tuple
# close the connection
cs1.close()
conn.close()
I was able to solve my issue by simply using
INSERT IGNORE and after that checking if it was ignored with the primary key.
Thank you for everyone that helped out though!

How to query a MySQL database via python using peewee/mysqldb?

I'm creating an iOS client for App.net and I'm attempting to setup a push notification server. Currently my app can add a user's App.net account id (a string of numbers) and a APNS device token to a MySQL database on my server. It can also remove this data. I've adapted code from these two tutorials:
How To Write A Simple PHP/MySQL Web Service for an iOS App - raywenderlich.com
Apple Push Notification Services in iOS 6 Tutorial: Part 1/2 - raywenderlich.com
In addition, I've adapted this awesome python script to listen in to App.net's App Stream API.
My python is horrendous, as is my MySQL knowledge. What I'm trying to do is access the APNS device token for the accounts I need to notify. My database table has two fields/columns for each entry, one for user_id and a one for device_token. I'm not sure of the terminology, please let me know if I can clarify this.
I've been trying to use peewee to read from the database but I'm in way over my head. This is a test script with placeholder user_id:
import logging
from pprint import pprint
import peewee
from peewee import *
db = peewee.MySQLDatabase("...", host="localhost", user="...", passwd="...")
class MySQLModel(peewee.Model):
class Meta:
database = db
class Active_Users(MySQLModel):
user_id = peewee.CharField(primary_key=True)
device_token = peewee.CharField()
db.connect()
# This is the placeholder user_id
userID = '1234'
token = Active_Users.select().where(Active_Users.user_id == userID)
pprint(token)
This then prints out:
<class '__main__.User'> SELECT t1.`id`, t1.`user_id`, t1.`device_token` FROM `user` AS t1 WHERE (t1.`user_id` = %s) [u'1234']
If the code didn't make it clear, I'm trying to query the database for the row with the user_id of '1234' and I want to store the device_token of the same row (again, probably the wrong terminology) into a variable that I can use when I send the push notification later on in the script.
How do I correctly return the device_token? Also, would it be easier to forgo peewee and simply query the database using python-mysqldb? If that is the case, how would I go about doing that?
The call User.select().where(User.user_id == userID) returns a User object but you are assigning it to a variable called token as you're expecting just the device_token.
Your assignment should be this:
matching_users = Active_Users.select().where(Active_Users.user_id == userID) # returns an array of matching users even if there's just one
if matching_users is not None:
token = matching_users[0].device_token

Categories