Home > Back-end >  Securing Flask-Admin When Using Blueprints and Application Factory
Securing Flask-Admin When Using Blueprints and Application Factory

Time:07-06

I've set up Flask Admin and it is working, but am struggling with adding authentication. There are several tutorials I've followed but can't seem to get them to work with how I've set up my app. Per the documentation on Flask-Admin regarding authentication (and slightly tweaked based on how I'm importing various elements), you just need to add:

class AdminView(ModelView):

    def is_accessible(self):
        return current_user.is_authenticated

    def inaccessible_callback(self, name, **kwargs):
        # redirect to login page if user doesn't have access
        return redirect(url_for('login', next=request.url))

But I can still access the /admin route without logging in. (I also would like to add an additional conditional that checks that the user is listed as an admin, which is a boolean column in the user table, but I haven't gotten this first part to work).

I've tried putting the above inside and outside of the create_app() function. Does this have to do with my blueprints? If so, where would I put this code?

# __init__.py

from flask import Flask
from dotenv import load_dotenv
from flask_sqlalchemy import SQLAlchemy
from flask_bcrypt import Bcrypt
from flask_login import LoginManager, current_user
from flask_migrate import Migrate
from SIMS_Portal.config import Config
from flask_admin import Admin
from flask_admin.contrib.sqla import ModelView
from flaskext.markdown import Markdown

load_dotenv()
db = SQLAlchemy()
bcrypt = Bcrypt()
login_manager = LoginManager()
login_manager.login_view = 'users.login'
login_manager.login_message_category = 'danger'
mail = Mail()

from SIMS_Portal import models

def create_app(config_class=Config):
    app = Flask(__name__)
    app.config.from_object(Config)
    
    db.init_app(app)
    bcrypt.init_app(app)
    login_manager.init_app(app)
    mail.init_app(app)
    admin = Admin(app, name='SIMS Admin Portal', template_mode='bootstrap4', endpoint='admin')
    Markdown(app)
    
    from SIMS_Portal.main.routes import main
    from SIMS_Portal.assignments.routes import assignments
    from SIMS_Portal.emergencies.routes import emergencies
    from SIMS_Portal.portfolios.routes import portfolios
    from SIMS_Portal.users.routes import users
    from SIMS_Portal.errors.handlers import errors
    
    app.register_blueprint(main)
    app.register_blueprint(assignments)
    app.register_blueprint(emergencies)
    app.register_blueprint(portfolios)
    app.register_blueprint(users)
    app.register_blueprint(errors)
    
    from SIMS_Portal.models import User, Assignment, Emergency, Portfolio, NationalSociety
    admin.add_view(ModelView(User, db.session))
    admin.add_view(ModelView(Assignment, db.session))
    admin.add_view(ModelView(Emergency, db.session))
    admin.add_view(ModelView(Portfolio, db.session))
    admin.add_view(ModelView(NationalSociety, db.session))
    
    return app

CodePudding user response:

Got some help from the r/flask community which I'll share here for anyone else that has set their app up the same way and found existing tutorials unhelpful. The key when using an app factory like in my case is the swap out the ModelView for the AdminView you define, which can go before the create_app() function. In my __init__.py, I first defined that custom class that inherits from ModelView (and add a check that the user is not only logged in but also listed as an admin in my database):

class AdminView(ModelView):
    def is_accessible(self):
        if current_user.is_admin == 1:
            return current_user.is_authenticated
    
    def inaccessible_callback(self, name, **kwargs):
        return render_template('errors/403.html'), 403

Then within the create_app() I swap out what I had previously included as admin.add_view(ModelView(User, db.session)) for admin.add_view(AdminView(User, db.session))

That little difference makes obvious sense, but again, I couldn't find tutorials that covered this.

  • Related