2

I have a single file for a Flask application, views.py.

Inside views.py, I have the setup for Flask-Admin and the corresponding sqlalchemy database here:

db = SQLAlchemy(flaskapp)

def build_db():

import random
import datetime

db.drop_all()
db.create_all()

# Create sample Users
testuser = []
testgroup = []
testlevel = []

user_list = []
for i in range(len(testuser)):
    user = User()
    user.testuser = testuser[i]
    user.testlevel = testlevel[i]
    user.testgroup = testgroup[i]

    user_list.append(user)
    db.session.add(user)

for user in user_list:
    entry = random.choice(sample_text)  # select text at random
    post = Post()
    post.user = user
    post.title = entry['title']
    post.text = entry['content']
    tmp = int(1000*random.random())  # random number between 0 and 1000:
    post.date = datetime.datetime.now() - datetime.timedelta(days=tmp)
    post.tags = random.sample(tag_list, 2)  # select a couple of tags at random
    db.session.add(post)

db.session.commit()
return

# Create models
class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    testuser = db.Column(db.String(100))
    testgroup = db.Column(db.String(100))
    testlevel = db.Column(db.String(100))

# Required for administrative interface. For python 3 please use __str__ instead.
def __repr__(self):
    return self.testuser

class UserInfo(db.Model):
    id = db.Column(db.Integer, primary_key=True)

    key = db.Column(db.String(64), nullable=False)
    value = db.Column(db.String(64))

    user_id = db.Column(db.Integer(), db.ForeignKey(User.id))
    user = db.relationship(User, backref='info')

def __repr__(self):
    return '%s - %s' % (self.key, self.value)

### Attempt at doing something...
class MyView(ModelView):
    @expose('/admin/userview', methods=('GET', 'POST'))
    def adminview():
        return "yes"
##ADMIN
admin = Admin(flaskapp, name="Test Aptly")
admin.add_view(MyView(User, db.session))

The Question.

How do I block access to the "/admin/userview" page depending on what level the user is (defined with the database "testlevel")? Users have 3 qualities, An example for two users would be:

testuser          testgroup        testlevel

joe               it               admin

john              dev              basic

How would I restrict the admin page to only let people with testlevel="admin"? Do I use Flask-Security? Flask-Principal? If so, how? I've been tinkering with both for a little bit and I haven't gotten anything working so far.

Here's an example "/admin/userview" looks like: example Flask-Admin

First of all, you can use various class-level properties to configure what should be displayed and how. For example, column_list can be used to show some of the column or include extra columns from related models.

For example:

class UserView(ModelView):
    # Show only name and email columns in list view
    column_list = ('name', 'email')

    # Enable search functionality - it will search for terms in
    # name and email fields
    column_searchable_list = ('name', 'email')

    # Add filters for name and email columns
    column_filters = ('name', 'email')

Alternatively, you can override some of the ModelView methods and implement your custom logic.

For example, if you need to contribute additional field to the generated form, you can do something like this:

class UserView(ModelView):
    def scaffold_form(self):
        form_class = super(UserView, self).scaffold_form()
        form_class.extra = wtf.TextField('Extra')
        return form_class

Check flask.ext.admin.contrib.sqlamodel documentation for list of configuration properties and methods. Thanks!

Palu Macil
  • 1,708
  • 1
  • 24
  • 34

1 Answers1

4

I'll assume that you subclassed BaseView to create your admin view and that you are using Flask-login.

Then override the is_accessible method in your view class, to check the current user's quality:

from flask.ext.admin.base import BaseView
from flask.ext.login import current_user

class MyView(BaseView):
    def is_accessible(self):
        return current_user.testlevel == 'admin'

Hope this helps!

rkz_io
  • 187
  • 10
  • I should have also put that I use kerberos to get the username like so: `request.environ.get('REMOTE_USER')` I haven't subclassed anything, what I posted there is everything I used to get Flask-Admin/sqlalchemy up and running. I have no idea where Flask-Admin keeps its web pages or anything :/ – asdfgthereisnocowlevel Jul 14 '14 at 17:47
  • If `request.environ.get('REMOTE_USER')` contains the username (as a string) you can query your database to get his testlevel. Regarding subclassing, there must be some place where you create an `Admin` object and add views to it (example [here](http://flask-admin.readthedocs.org/en/latest/quickstart/#adding-views)). Which class are you using in `admin.add_view(...)`? – rkz_io Jul 14 '14 at 17:58
  • Yea I've been doing it throughout the Flask app, but with Flask-Admin I haven't been able to get it to work with querying the database. – asdfgthereisnocowlevel Jul 14 '14 at 18:06
  • Can you edit your question with the code you have to initialize Flask-Admin? Will help figuring out why you're not able to run queries specifically with Flask-Admin. – rkz_io Jul 14 '14 at 18:11
  • Won't let me edit but at the bottom of the code above is how I initialize Flask-Admin `admin = Admin(flaskapp, name="Test Aptly")` `admin.add_view(MyView(User, db.session))` – asdfgthereisnocowlevel Jul 14 '14 at 18:23
  • Browse your code to find where the `MyView` class is declared. As you instantiate it with a `session` I guess it is subclassed from `ModelView`. Within such a view you can access the database session with `self.session`, from here you can query for the appropriate `User` (`self.session.query(User).filter(...)`) and check whether it has the right `testlevel`. – rkz_io Jul 14 '14 at 22:02