Make peewee admin work with flask security

883 Views Asked by At

I like both peewee and Flask security, and they both work well together, but I can't get the admin functionality of peewee to work. I am really not sure about how to do this, but I really want to use both together. I have tried a lot of things, but nothing seems to work.

My current code looks like this:

import datetime
from flask import Flask, abort, redirect, render_template, url_for, flash, request
from flask_peewee.auth import Auth, BaseUser
from flask_peewee.db import Database
from flask_peewee.admin import Admin, ModelAdmin
from flask_peewee.rest import RestAPI, UserAuthentication
from peewee import *
from flask.ext.security import *
# Create app
app = Flask(__name__)
app.config['DEBUG'] = True
app.config['SECRET_KEY'] = 'super-secret'
app.config['DATABASE'] = {
    'name': 'ee.db',
    'engine': 'peewee.SqliteDatabase',
}

# Create database connection object
db = Database(app)

class Role(db.Model, RoleMixin):
    name = TextField(unique=True)
    description = TextField(null=True)

class RoleAdmin(ModelAdmin):
    columns = ('name', 'description')

class User(db.Model, UserMixin):
    username = TextField()
    password = TextField()
    email = TextField()
    role = ForeignKeyField(Role)
    active = BooleanField(default=True)
    confirmed_at = DateTimeField(null=True)

class UserAdmin(ModelAdmin):
    columns = ('username', 'email', 'role', 'active', 'confirmed_at')

class UserRoles(db.Model):
    user = ForeignKeyField(User, related_name='roles')
    role = ForeignKeyField(Role, related_name='users')
    name = property(lambda self: self.role.name)
    description = property(lambda self: self.role.description)

class UserRolesAdmin(ModelAdmin):
    columns = ('user', 'role', 'name', 'description')

class Note(db.Model):
    user = ForeignKeyField(User, related_name='notes')
    title = CharField()
    message = TextField()
    created = DateTimeField(default=datetime.datetime.now)
    def __unicode__(self):
        return '%s: %s %s' % (self.user.username, self.message, self.created)

class NoteAdmin(ModelAdmin):
    columns = ("user", "message", "created")
    foreign_key_lookups = {'user': 'username'}


class CustomAuth(Auth):
    def get_user_model(self):
        return User

    def get_model_admin(self):
        return UserAdmin

class CustomAdmin(Admin):
    def check_user_permission(self, user):
        if has_role(current_user).name == "admin":
            return True

# Setup Flask-Security
user_datastore = PeeweeUserDatastore(db, User, Role, UserRoles)
security = Security(app, user_datastore)

#Setup Flask-Peewee admin
auth = CustomAuth(app, db)
admin = CustomAdmin(app, auth)
admin.register(Note, NoteAdmin)
admin.register(User, UserAdmin)
admin.setup()

#create a RestAPI container
api = RestAPI(app)
#register the Note model
api.register(Note)
api.setup()

#instantiate the user auth
user_auth = UserAuthentication(auth)

# Views
@app.route("/")
@login_required
def index():
    messages = Note.select()
    return render_template("base.html",
    notes = messages,
    username = current_user.username,
    role = current_user.role.name
    )

@app.route("/add", methods = ['post'])
@login_required
def add():
    user = current_user
    now = datetime.datetime.now()
    Note.create(user = current_user.get_id(), title = request.form["title"], message = request.form["message"], created = now)
    flash('New entry was successfully posted')
    return redirect(url_for('index'))

@app.route("/delete/<id>")
@login_required
def delete(id):
    note = Note.get(Note.id == id)
    note.delete_instance()
    flash('Entry was successfully removed')
    return redirect(url_for('index'))

if __name__ == '__main__':
    for Model in (Role, User, UserRoles):
        Model.drop_table(fail_silently=True)
        Model.create_table(fail_silently=True)
    user_datastore.create_role(name="admin", description="admin")
    user_datastore.create_user(username="Ben", email='hello', password='password', active=True, confirmed_at=datetime.datetime(1935,9,9,9,9), role=1)
    user_datastore.add_role_to_user('hello', "admin")
    Note.create_table(fail_silently=True)
    app.run(port = 8080)

When I try to log in into the Peewee Admin, I get the following error: `AttributeError: 'User' object has no attribute 'check_password'

EDIT:

Full debug:

File "/anaconda/lib/python2.7/site-packages/flask_peewee/auth.py", line 170, in login

form.password.data,

File "/anaconda/lib/python2.7/site-packages/flask_peewee/auth.py", line 128, in authenticate

 if not user.check_password(password):

AttributeError: 'User' object has no attribute 'check_password

I really do not understand why this raises an AttributeError. The password is registered, and the BaseUser class is imported (required for check_password). What am I doing wrong?

1

There are 1 best solutions below

0
On

Your User class needs to implement the check_password and set_password methods, because Flask-Peewee calls those to authenticate requests and update models.

Flask-Peewee provides a class, BaseUser, with simple implementations you could mix in to User, but this probably isn't useful when you're also using Flask-Security. You'll need to implement the methods yourself in a way that they call into Flask-Security to compare and hash passwords, because the two projects' approaches to password hashing are different, and you're only saving one hash in the database.