Dan Rubio
Dan Rubio

Reputation: 4907

Can someone help me pinpoint why Bcrypt fails (ValueError: Password must be non-empty)

I have a small flask application that I am currently rewriting and changing the directory tree per flask convention. In the previous application, I was able to get flask Bcrypt to work but in this new structure it doesn't seem to work.

Here is a snippet of the code that I have from my models.py

from blackduckflock import app, db, bcrypt


class User(db.Model, flask_login.UserMixin):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(50))
    password = db.Column(db.String(250))

    def __init__(self, username='', password=''):
        self.username = username


        # self.password = password

        #------The code below fails while the code above works just fine ------------

        #self.password = bcrypt.generate_password_hash(password)

    def __repr__(self):
        return '<User %r>' % self.username

The bcrypt.generate_password fails with a (ValueError: Password must be non-empty). For some reason, the Create form is not persisting with the request and is not transferring to bcrypt. A code.interact(local=locals()) shows that the default '' arguments are being passed instead of the actual form value. Without the bcrypt generation, the code works just fine and a User is created.

I'm not exactly sure why that is but does anyone have any ideas?

This is my __init__.py file within blackduckflock/blackduckflock/__init__.py

import os
from flask import Flask
from flask.ext.bcrypt import Bcrypt
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.script import Manager
from flask.ext.migrate import Migrate, MigrateCommand

app = Flask(__name__)
bcrypt = Bcrypt(app)
db = SQLAlchemy(app)
manager = Manager(app)
migrate = Migrate(app,db)
manager.add_command('db', MigrateCommand)

file_path = os.path.join(os.path.dirname(__file__), 'static/images/')

import blackduckflock.views
import blackduckflock.models
import blackduckflock.forms
import blackduckflock.admin

What could be the problem?

---------------------EDIT (Minimal, Complete, Example)------------------

Tree directory:

blackduckflock/
   blackduckflock/
     - __init__.py
     - models.py
     - forms.py
     - views.py
     - admin.py
   - blackduckflock.py
   - config.py

blackduckflock.py:

from blackduckflock import app

app.config.from_object('config')

app.run()

init.py

from flask import Flask
from flask.ext.bcrypt import Bcrypt
from flask.ext.sqlalchemy import SQLAlchemy

app = Flask(__name__)
bcrypt = Bcrypt(app)
db = SQLAlchemy(app)

import blackduckflock.admin
import blackduckflock.views
#import blackduckflock.models
#import blackduckflock.forms

admin.py:

class MyAdminIndexView(AdminIndexView):
    pass

admin = Admin(app, name='BlackDuck Flock',        
              index_view=MyAdminIndexView(),  
              template_mode='bootstrap3')


class UserView(ModelView):
    def is_accessible(self):
        return flask_login.current_user.is_authenticated

admin.add_view(UserView(User, db.session))

models.py:

from blackduckflock import app, db, bcrypt
import flask.ext.login as flask_login

class User(db.Model, flask_login.UserMixin):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(50))
    password = db.Column(db.String(250))

    def __init__(self, username='', password=''):
       self.username = username
       # self.password = password
       # code.interact(local=locals())
       self.password = bcrypt.generate_password_hash(password)

    def __repr__(self):
        return '<User %r>' % self.username

views.py:

import flask.ext.login as flask_login
from blackduckflock import db, app
from blackduckflock.models import User

login_manager = flask_login.LoginManager()
login_manager.init_app(app)

@login_manager.user_loader
def load_user(user_id):
     return db.session.query(User).get(user_id)

Upvotes: 1

Views: 1303

Answers (2)

Dan Rubio
Dan Rubio

Reputation: 4907

I solved my dilemma by implementing an event callback shown below.

@listens_for(User, 'before_insert')
def bcrypt_password(mapper, connection, target):
   target.password = bcrypt.generate_password_hash(target.password)

Upvotes: 1

Velin Georgiev
Velin Georgiev

Reputation: 2489

You cannot have optional parameters for the User class unless you ensure that empty password is not passed to the generate_password_hash function like:

class User:
    def __init__(self, username='', password=''):
        self.username = username

        if password != '':
             self.password = bcrypt.generate_password_hash(password)

or simply remove the optional param and make it obligatory like:

class User:
    def __init__(self, password, username=''):
        self.username = username

        self.password = bcrypt.generate_password_hash(password)

This is important because whenever you try to initialize the class with empty params because they are optional like so:

user = User()

It will fail because it will try to pass password='' to the bcrypt.generate_password_hash(password) and it will raise error.

Upvotes: 1

Related Questions