简体   繁体   中英

Flask-basicAuth auth required decorator for Flask-Admin views

I'm currently making a back-end application with Flask to manage resources for organization-based user consumption.

Since I am using Flask-SQLAlchemy, I decided to use Flask-Admin for an admin view of the DB but I'm having problems protecting the views.

I'm trying to use Flask-BasicAuth just to protect the admin views but since the route is autogenerated I cannot add the @basic-auth.required decorator to it. Forcing the site to use Flask-BasicAuth would block the resource endpoints and thus would not be a good solution.

Tried something like this but it doesn't work:

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_admin import Admin
from flask_admin.contrib.sqla import ModelView
from flask_basicauth import BasicAuth

app = Flask(__name__)
db = SQLAlchemy(app)
basic_auth = BasicAuth(app)
admin = Admin(app)

class Module(db.Model):
  __tablename__='Modules'
  name = db.Column(db.String(30), unique=True, nullable=False)

@basic_auth.required
class AdminView(ModelView):
  pass

admin.add_view(AdminView(Module, db.session))

TL;DR: Flask Admin assumes I use a login & session manager. Flask BasicAuth assumes I can manually declare routes. Need to integrate them somehow without blocking the resource endpoints.

I faced the same issue while implementing it. The @basic_auth.required decorator won't work. Instead we have to tweak couple of classes of flask_admin to make it compatible with BasicAuth. After referring dozens of resources, Here is how I implemented it successfully!

Just to mention I'm using: Python 3.6.9, Flask==1.1.1, Flask-Admin==1.5.4, Flask-BasicAuth==0.2.0

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_admin import Admin, AdminIndexView
from flask_admin.contrib.sqla import ModelView
from flask_basicauth import BasicAuth
from werkzeug.exceptions import HTTPException

app = Flask(__name__)
db = SQLAlchemy(app)
basic_auth = BasicAuth(app)

class Module(db.Model):
  __tablename__='Modules'
  name = db.Column(db.String(30), unique=True, nullable=False)

"""
The following three classes are inherited from their respective base class,
and are customized, to make flask_admin compatible with BasicAuth.
"""
class AuthException(HTTPException):
    def __init__(self, message):
        super().__init__(message, Response(
            "You could not be authenticated. Please refresh the page.", 401,
            {'WWW-Authenticate': 'Basic realm="Login Required"'} ))

class MyModelView(ModelView):
    def is_accessible(self):
        if not basic_auth.authenticate():
            raise AuthException('Not authenticated.')
        else:
            return True
    def inaccessible_callback(self, name, **kwargs):
        return redirect(basic_auth.challenge())

class MyAdminIndexView(AdminIndexView):
    def is_accessible(self):
        if not basic_auth.authenticate():
            raise AuthException('Not authenticated.')
        else:
            return True
    def inaccessible_callback(self, name, **kwargs):
        return redirect(basic_auth.challenge())

admin = Admin(app, index_view=MyAdminIndexView())

admin.add_view(MyModelView(Module, db.session))

The official documentation says that there is no simple way to implement it just to your admin page. However, I have found a workaround. You may need to modify some of the existing classes in flask for it to be compatible with basic auth. Add these to your code. Fyi, you will need to import Response from flask.

class ModelView(sqla.ModelView):
    def is_accessible(self):
        if not basic_auth.authenticate():
            raise AuthException('Not authenticated.')
        else:
            return True

    def inaccessible_callback(self, name, **kwargs):
    return redirect(basic_auth.challenge())
from werkzeug.exceptions import HTTPException


class AuthException(HTTPException):
    def __init__(self, message):
        super().__init__(message, Response(
            "You could not be authenticated. Please refresh the page.", 401,
            {'WWW-Authenticate': 'Basic realm="Login Required"'}
        ))

Then add the admin views normally like this

admin = Admin(app)
admin.add_view(ModelView(Module, db.session))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM