I'm looking at an open source app https://github.com/AlvinCJin/deepfit-app/tree/master/app
This is using flask-admin for admin purposes/ On the page https://github.com/AlvinCJin/deepfit-app/blob/master/app/main/views.py:
# admin management setup
admin.add_view(ModelView(User, db.session))
admin.add_view(ModelView(Post, db.session))
path = op.join(os.path.abspath(__file__ + "/../../"), 'static') # need to get parent path of this code
admin.add_view(FileAdmin(path, '/static/', name='Static Files'))
This works fine as far as producing a helpful dashboard but is unsecured. I have flask-login installed and I read the section http://flask-admin.readthedocs.io/en/latest/introduction/#rolling-your-own , but it is unclear to me how to link the class discussed:
class MicroBlogModelView(sqla.ModelView):
def is_accessible(self):
return login.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))
with the admin route.
The user table is defined in models.py as:
class User(UserMixin, db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
nickname = db.Column(db.String(64), unique=True)
firstname = db.Column(db.String(100))
lastname = db.Column(db.String(100))
email = db.Column(db.String(120), index=True, unique=True)
pwdhash = db.Column(db.String(54))
phone = db.Column(db.Integer)
address = db.Column(db.String(64))
confirmed = db.Column(db.Boolean, default=False)
role = db.Column(db.SmallInteger, default=ROLE_APPLICANT)
comments = db.relationship('Comment', backref='author', lazy='dynamic')
posts = db.relationship('Post', order_by="Post.timestamp", backref='author',
lazy='dynamic', cascade="all, delete, delete-orphan")
about_me = db.Column(db.Text())
last_seen = db.Column(db.DateTime, default=datetime.utcnow)
member_since = db.Column(db.DateTime(), default=datetime.utcnow)
portrait = db.Column(db.String(140))
pref = db.relationship('Preference', uselist=False, backref='author')
fav = db.relationship('Favourite', backref='user', lazy='dynamic')
active = db.Column(db.Boolean, default=False)
@staticmethod
def make_unique_nickname(nickname):
if User.query.filter_by(nickname=nickname).first() is None:
return nickname
version = 2
while True:
new_nickname = nickname + str(version)
if User.query.filter_by(nickname=new_nickname).first() is None:
break
version += 1
return new_nickname
def __init__(self, nickname, firstname, lastname, email, password, role):
self.nickname = nickname.title()
self.firstname = firstname.title()
self.lastname = lastname.title()
self.email = email.lower()
self.set_password(password)
self.role = role
def ping(self):
self.last_seen = datetime.utcnow()
db.session.add(self)
def set_password(self, password):
self.pwdhash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.pwdhash, password)
def is_authenticated(self):
return True
def generate_confirmation_token(self, expiration=3600):
s = Serializer(current_app.config['SECRET_KEY'], expiration)
return s.dumps({'confirm': self.id})
def confirm(self, token):
s = Serializer(current_app.config['SECRET_KEY'])
try:
data = s.loads(token)
except:
return False
if data.get('confirm') != self.id:
return False
self.confirmed = True
db.confirmed = True
db.session.add(self)
return True
def to_json(self):
json_user = {
'url': url_for('api.get_post', id=self.id, _external=True),
'nickname': self.nickname,
'member_since': self.member_since,
'last_seen': self.last_seen,
'posts': url_for('api.get_user_posts', id=self.id, _external=True),
'post_count': self.posts.count(),
}
return json_user
def generate_reset_token(self, expiration=3600):
s = Serializer(current_app.config['SECRET_KEY'], expiration)
return s.dumps({'reset': self.id})
def generate_auth_token(self, expiration):
s = Serializer(current_app.config['SECRET_KEY'], expiration)
return s.dumps({'id': self.id})
@staticmethod
def verify_auth_token(token):
s = Serializer(current_app.config['SECRET_KEY'])
try:
data = s.loads(token)
except:
return None
return User.query.get(data['id'])
def is_active(self):
if self.active is True:
return True
else:
return False
def is_anonymous(self):
return False
def get_id(self):
return unicode(self.id)
def __repr__(self):
return '<User %r>' % self.nickname
And has a is_authenticated method, but how do I use this to require login of as specific user?
I have tried :
class MyView(BaseView):
@expose('/')
def index(self):
return self.render('admin/index.html')
def is_accessible(self):
return login.current_user.is_authenticated()
# admin management setup
admin.add_view(ModelView(User, db.session))
admin.add_view(ModelView(Post, db.session))
path = op.join(os.path.abspath(__file__ + "/../../"), 'static') # need to get parent path of this code
admin.add_view(FileAdmin(path, '/static/', name='Static Files'))
Based on:
https://flask-admin.readthedocs.io/en/v1.0.7/quickstart/
EDIT:
So just for my understanding you are subclassing the ModelViews and adding the ability to have routes?
I've changed it to:
class MyView(ModelView):
@expose('/')
def index(self):
return self.render('admin/index.html')
def is_accessible(self):
return login.current_user.is_authenticated()
# admin management setup
admin.add_view(MyView(User, db.session))
admin.add_view(MyView(Post, db.session))
path = op.join(os.path.abspath(__file__ + "/../../"), 'static') # need to get parent path of this code
admin.add_view(FileAdmin(path, '/static/', name='Static Files'))
That is getting closer but I need to integrate this with flask login - I'm getting:
NameError: global name 'login' is not defined
EDIT2:
I've changed it to:
class MyView(ModelView):
@expose('/')
@login_required
def index(self):
return self.render('admin/index.html')
removing the is_accessible function, so its not overridden, The user class already has a built in is_accessible function.
This is at least partially working, but I would like to only allow access to admins that have a ROLE in user defined as 0
You need it do it like below
from flask_login.utils import current_user
class MyView(ModelView):
@expose('/')
def index(self):
return self.render('admin/index.html')
def is_accessible(self):
return current_user.is_authenticated()
# admin management setup
admin.add_view(MyView(User, db.session))
admin.add_view(MyView(Post, db.session))
path = op.join(os.path.abspath(__file__ + "/../../"), 'static') # need to get parent path of this code
admin.add_view(FileAdmin(path, '/static/', name='Static Files'))
You need to use MyView
class while registering the views. If you need more customization then you need to implement them in your User object used by Flask login. There you can create check group and anything else you need