Using PassLib to Verify Hash from Flask User Passwords - django

I'm currently trying to migrate my Flask Users over to a Django Backend.
However, when I'm using passlib to verify the hash, I can't figure out why it won't verify.
Our flask app settings
SECURITY_PASSWORD_HASH = "pbkdf2_sha512"
SECURITY_PASSWORD_SALT = "stackoverflow" # this is an example
An example of a hash I pulled from a database
flask_hash =
"$pbkdf2sha512$12000$ZQyhNEbIOSfk/J/T2vs/Bw$j.yxtixV.DqAcpsY9XTnJZZb3lCkR2fMWmV329Uc7Y/vz5Z0yMshEkYlUsE2Y9xm8TICwYkG55RgAplzZzLl7g"
So I created a custom pbkdf2_sha512 with the the rounds and salt
from passlib.hash import pbkdf2_sha512
rounds = 12000
salt = "stackoverflow".encode() # assume I swapped this out with the right salt
custom_pbkdf2 = pbkdf2_sha512.using(rounds=rounds, salt=salt)
verify_result = custom_pbkdf2.verify(hash=flask_hash, secret=password)
print (verify_result) # false
But if I create a new hash ... it does work
test_hash = custom_pbkdf2.hash('testing-if-this-works')
test_hash_confirm = custom_pbkdf2.verify('testing-if-this-works', hash=test_hash)
Is there something I'm missing? Thank you so much for any help here ... I know the password to this -- it's a dummy account I used for testing.

I was struck in exactly the same situation, luckily found this reddit thread, which had the explanation.
Basically, what you have to do verify the user is:
from flask_security.utils import verify_password
verify_password(<plain text password>, <password hash>)
More details here

Related

username & password with presto-python-client

I am trying to replace
jaydebeapi with the presto-python-client by facebook
the question is how to replace the authentication bit
db = jaydebeapi.connect(connection['jclass'], connection['host'],[ connection['user'], connection['pass']], connection['jar'])
while with presto-python-client
import prestodb
conn= prestodb.dbapi.connect(
host='localhost',
port=8080,
user='the-user',
catalog='the-catalog',
schema='the-schema',
isolation_level=transaction.IsolationLevel.REPEATABLE_READ,
)
and I couldn't find anywhere to specify how to pass a password.
The reason for this change is that I am getting ambigous errors when trying to pass long queries (18k characters) with jaydebeapi
we ended up using SQLAlchemy so the solution is
from sqlalchemy.engine import create_engine
engine = create_engine('presto://{0}:{1}#{2}:{3}/hive'.format(connection['user'],connection['pass'],connection['host'],int(connection['port'])), connect_args={'protocol': 'https', 'requests_kwargs': {'verify': False}})
db = engine.raw_connection()
Now the old method are deprecated, so here is the new one to connect to presto
cursor = presto.connect(presto_host,
presto_port,
requests_kwargs={'auth': HTTPBasicAuth(presto_username, presto_password)},
poll_interval=1,
protocol='https',
source='pyhive').cursor()

Migrating Discourse user to Django user

I have exported database from Discourse. it does contain password_hash and salt.
I have done my research and found out that Django uses PBKDF2 by default and even Discours use that with hashing algorithm sha256 and number of iterations 64000.
I want to migrate those password so that Django will be able to authenticate a user with the same password.
There's a number of ways you could do this.
Write your own authentication method in the backend - which accepts the same hashing method as Discourse when a user attempts to login. This way the hashed password should match from the user's salt and the password they have entered.
This can be done as follows:
from django.contrib.auth.hashers import PBKDF2PasswordHasher
class MyPBKDF2PasswordHasher(PBKDF2PasswordHasher):
"""
A subclass of PBKDF2PasswordHasher that uses 64000 times more iterations.
"""
iterations = PBKDF2PasswordHasher.iterations * n
iterations = 64000 #Use this for simplicity!!
in hashers.py. Please note - PBKDF2PasswordHasher.iterations * n will have to equal 64000 - I think the number of iterations is currently set to 150000, so probably easier to have iterations = 64000 directly. The iterations is all you're looking to change, and all other behaviour will be inherited from the PBKDF2PasswordHasher Class.
Then, all you will need is:
PASSWORD_HASHERS = [
'application_name.hashers.MyPBKDF2PasswordHasher',
]
in settings.py, where application_name is, yep you guessed it, the name of the application where hashers.py can be found.
However...the following documentation on storage and hashing of passwords may be extremely useful in your search:
https://docs.djangoproject.com/en/2.1/topics/auth/passwords/#auth-password-storage

Check if document exists using cloudant-2.0.0b2 in IBM Bluemix and Python

I am using:
A Python application in Bluemix
Bluemix cloudant v2.0.0b2 database linked to the Python app
According to https://pypi.python.org/pypi/cloudant/2.0.0b2, everything broke from 0.5 to 2.0, and they are still working on the documentation as everything is Beta. Next to this, I am also new to Python and databases. Documentation can be found here:
http://python-cloudant.readthedocs.io/en/latest/getting_started.html
What I am trying to do is check if a document already exists.
Things that I have tried:
from cloudant.account import Cloudant
import time
import json
# Connect to the database
client = Cloudant(*hidden*)
client.connect()
# The database we work in
db = client['myDatabase']
# The document we work on
doc = db['myDocument']
print doc.exists()
But the code fails before retrieving the document. I checked the source code, and it looks like it is supposed to:
def __getitem__(self, key):
if key in list(self.keys()):
return super(CouchDatabase, self).__getitem__(key)
if key.startswith('_design/'):
doc = DesignDocument(self, key)
else:
doc = Document(self, key)
if doc.exists():
doc.fetch()
super(CouchDatabase, self).__setitem__(key, doc)
return doc
else:
raise KeyError(key)
Source: https://pypi.python.org/pypi/cloudant/2.0.0b2
Is there a way I can check if the document exists before I retrieve it? Or should I retrieve it and catch the error? Or is there a different approach?
The behavior you are describing is the desired behavior for the python-cloudant library database object, so if you intend to use the database object to retrieve your documents and populate your local database cache you should look to except a KeyError in the event of a non-existent document and handle accordingly. However, if are interested in capturing whether a document exists before bringing it into your local database cache then changing your code to something like:
from cloudant.account import Cloudant
from cloudant.document import Document
# Connect to the database
client = Cloudant(*hidden*)
client.connect()
# The database we work in
db = client['myDatabase']
# The document we work on
if Document(db, 'myDocument').exists():
doc = db['myDocument']
would do the trick.
Similarly you could just do:
from cloudant.account import Cloudant
from cloudant.document import Document
# Connect to the database
client = Cloudant(*hidden*)
client.connect()
# The database we work in
db = client['myDatabase']
# The document we work on
doc = Document(db, 'myDocument')
if doc.exists():
doc.fetch()
But this would not populate your local database cache, the db dictionary.

Biopython Entrez wanting to access /root/.config in Django app

I have some code in a Django app which does the following, to get a Pubmed article by DOI:
def getPubmedByDOI(request,doi):
Entrez.email = 'me#mydomain.com'
handle = Entrez.esearch(db="pubmed", term=doi)
record = Entrez.read(handle)
return getPubmedArticle(request,record["IdList"][0]) // renders the article
This works nicely but for one thing - the Entrez.esearch call insists upon access to /root/.config on the server, specifically to write to the following empty directory:
/root/.config/biopython/Bio/Entrez/DTDs/
It's Apache on Gentoo, running as follows:
User django
Group apache
All the code for the application is in ~django/, so I'd expect any writing to be in ~django/.config rather than /root/.config. I can work around this by changing permissions on /root but a better solution would be to configure Biopython or Apache so as not to write to /root. Does anyone have any suggestions as to how this might be done?
Logged upstream as https://github.com/biopython/biopython/issues/918 which suggests setting:
>>> from Bio.Entrez.Parser import DataHandler
>>> DataHandler.global_dtd_dir
'/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/Bio/Entrez/DTDs'
>>> DataHandler.local_dtd_dir = '...'

What is the cleanest way to add code to contrib.auth

I've migrated an old joomla installation over to django. The password hashes is an issue though. I had to modify the get_hexdigest in contrib.auth.models to have an extra if statement to reverse the way the hash is generated.
# Custom for Joomla
if algorithm == 'joomla':
return md5_constructor(raw_password + salt).hexdigest()
# Djangos original md5
if algorithm == 'md5':
return md5_constructor(salt + raw_password).hexdigest()
I also added the following to the User model to update the passwords after login if they have the old joomla style:
# Joomla Backwards compat
algo, salt, hsh = self.password.split('$')
if algo == 'joomla':
is_correct = (hsh == get_hexdigest(algo, salt, raw_password))
if is_correct:
# Convert the password to the new more secure format.
self.set_password(raw_password)
self.save()
return is_correct
Everything is working perfectly but I'd rather not edit this code directly in the django tree. Is there a cleaner way to do this in my own project?
Thanks
Your best bet would be to roll a custom auth backend and rewrite get_hexdigest in there. Never done it myself, but documentation on how to do so is available at http://docs.djangoproject.com/en/dev/topics/auth/#authentication-backends.
Thanks for the guidance. For anyone who needs to go the other way (DJango to Joomla) with DJ passwords, the DJ format is Sha1$salt$crypt.
Joomla standard auth plugin and joomla core JUserHelper do not implement the same SHA1 algorithum but it is fairly easy to patch into joomla.php in that plugin, where the plugin normally does an explode on ':'. Do a three-part explode with '$' and use salt = [1], compare that against $encrypted = sha1($salt.$plaintext), match that against the crypt [2].