Search code examples
pythonflaskflask-admin

Flask-Admin's inaccessible_callback() not working properly


I want to control who can access admin pages on my flask application.

I've been trying to overwrite the flask_admin.ModelView's methods, 'is_accessible' and 'inaccessible_callback' to handle the situation.

This is the AdminView class I'm creating:

class AdminView(ModelView):

    def is_accessible(self):
        return current_user.admin

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

and the model:

class User(db.Model, UserMixin):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(50), unique=True)
    email = db.Column(db.String(120), unique=True)
    password_hash = db.Column(db.String(128))
    admin = db.Column(db.Boolean, default=False)

    def __init__(self, username=None, email=None, password=None):
        self.username = username
        self.email = email
        self.password_hash = password_hash
        self.admin = admin

and the AdminView initialization:

def init_admin(admin):
    from app.models import User
    admin.add_view(AdminView(User, db.session))

which is called in the application factory:

def create_app(test_config=None):
    # create and configure the app
    app = Flask(__name__, instance_relative_config=True)

    if test_config is None:
        # load the isntance config, if it exists, when not testing
        app.config.from_object(Config)
    else:
        # load the test config passed in
        app.config.from_object(test_config)

    db.init_app(app)
    migrate = Migrate(app, db)
    login_manager.init_app(app)
    mail.init_app(app)
    bootstrap.init_app(app)

    admin = Admin(app, name='app', template_mode='bootstrap3')


    from app.auth import auth_bp
    app.register_blueprint(auth_bp)

    from app.tables import tables_bp
    app.register_blueprint(tables_bp)

    init_admin(admin)

    try:
        os.makedirs(app.instance_path)
    except OSError:
        pass

    return app

When I login with a user that has it's admin attribute set to True it returns the correct admin page with the User model ready to be used. When I login with a user that has a false admin attribute, it still shows the admin page, without the User model attached. I would rather that it redirect them to a login page, with a warning that they are forbidden from that page.


Solution

  • I figured out how to get this to work thanks to a youtube video!

    Flask_Admin Accessibility

    Check it out for a more in-depth explanation!

    The issue was that the '/admin' page is loaded by flask_admin.AdminIndexView

    Therefore I had to create my own child class of AdminIndexView and set this as the index_view parameter when initializing Admin()

    Here is the updated code: I added a MyIndexView class to my admin file:

    # ...
    from flask_admin import AdminIndexView
    # ...
    class MyIndexView(AdminIndexView):
        def is_accessible(self):
            return current_user.admin
    
        def inaccessible_callback(self, name, **kwargs):
            # redirect to login page if user doesn't have access
            return redirect(url_for('auth.login', next=request.url))
    

    And then I set the index_view parameter in my application factory

    # ...
    from app.admin import AdminView, MyIndexView
    # ...
    admin = Admin(app, name='app', template_mode='bootstrap3',
                      index_view=MyIndexView())
    # ...
    

    It now works perfectly!