Kaow
Kaow

Reputation: 563

How to do authorization in Flask?

now i write the flask login application using flask_login library and in this part is work fine, but now what i want is how can i specify the user who can access or not access to the route, and this is my code.

from flask import Flask, render_template, url_for, request, redirect
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, UserMixin, login_required, login_user, current_user, logout_user

app = Flask(__name__)

app.config['SECRET_KEY'] = 'thisissecretekey'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///sites.db'
db = SQLAlchemy(app)

login_manager = LoginManager(app)
 
@login_manager.user_loader
def load_user(user_id):
    return User.query.get(user_id)

# ----------- Database Tables -------------
class User(db.Model, UserMixin):
    __tablename__ = 'user'
    id = db.Column(db.Integer, primary_key = True)
    username = db.Column(db.String(50), unique=True)
    password = db.Column(db.String(40))
    role_code = db.Column(db.String(30), db.ForeignKey('roles.role_code'))

    def get_id(self):
           return (self.id)

class Roles(db.Model, UserMixin):
    __tablename__ = 'roles'
    role_code = db.Column(db.String(40), primary_key = True)
    role_name = db.Column(db.String(50))

# --------------- Routes ----------------
@app.route('/login', methods=['GET', 'POST'])
def login():
    if current_user.is_authenticated:
        return redirect(url_for('index'))
    if request.method == "POST":
        username = request.form['username']
        password = request.form['password']
        user = User.query.filter(User.username == username).first()
        if user and password == user.password:
            login_user(user)
            return redirect(url_for('index'))
        else:
            return redirect(url_for('login'))
    return render_template('login.html')

@app.route('/index')
@login_required
def index():
    return render_template('index.html')

@app.route('/logout', methods=['GET', 'POST'])
def logout():
    logout_user()
    return redirect(url_for('login'))

if __name__ == '__main__':
    app.run(debug=True)

For example i have this data in database.

Roles table

Roles(role_code='a', role_name='Admin')

Roles(role_code='o', role_name='Operator')

User table

User(username='tom', password='pass', role_code='a')

User(username='frank', password='pass', role_code='e')

If i want specify the /index route can only access by user who have role_code equal to a or Admin, what is the best way can i do that?

Upvotes: 1

Views: 3402

Answers (1)

cizario
cizario

Reputation: 4269

flask-login provides @login_required decorator so you need to create your own @admin_required decorator, the code below is taken from flask-base a starter project (github repo )

[..]
from functools import wraps

[..]

class Permission:
    GENERAL = 0x01
    ADMINISTER = 0xff

[..]

def permission_required(permission):
    """Restrict a view to users with the given permission."""

    def decorator(f):
        @wraps(f)
        def decorated_function(*args, **kwargs):
            if not current_user.can(permission):
                abort(403)
            return f(*args, **kwargs)

        return decorated_function

    return decorator


def admin_required(f):
    return permission_required(Permission.ADMINISTER)(f)

then you can decorate /index route with @admin_required, please note the order how to call decorators:

@app.route('/index')
@login_required
@admin_required
def index():
    return render_template('index.html')

and in your template:

<table class="ui compact definition table">
  <tr><td>Full name</td><td>{{ '%s %s' % (user.first_name, user.last_name) }}</td></tr>
  <tr><td>Email address</td><td>{{ user.email }}</td></tr>
  <tr><td>Account type</td><td>{{ user.role.name }}</td></tr>
</table>

Upvotes: 3

Related Questions