Flask 中基于角色的访问控制以及多个嵌入式 Dash 应用程序

问题描述 投票:0回答:1

过去一周这一直压垮我的头骨。我希望能够根据用户的角色限制 Flask 应用程序中对 Dash 应用程序的访问。我使用 flask mega 教程 作为创建此应用程序的指南,并使用 this 指南 嵌入破折号应用程序。我在 index.html 模板中创建了条件语句,以控制用户登录后看到的与其角色相关的链接:

{% extends "base.html" %}

{% block app_content %}
<h1>Hi, {{ current_user.username }}!</h1>
<h2>
    My Dashboards
</h2>
{% if current_user.role == 'Department Head' %}
<a href="/laborcosts" class="dash-link" target="_blank">
    <span>Labor Costs</span>
</a>
<br>
<a href="/dtidpriority" class="dash-link" target="_blank">
    <span>DTID Priority</span>
</a>
<br>
<a href="/auditreturnreport" class="dash-link" target="_blank">
    <span>Audit Return Report</span>
</a>
<br>
<a href="/automationerrorreport" class="dash-link" target="_blank">
    <span>Automation Error Report</span>
</a>
<br>
<a href="/clientvolume" class="dash-link" target="_blank">
    <span>Client Volume</span>
</a>
<br>
<a href="/outofscope" class="dash-link" target="_blank">
    <span>Out Of Scope</span>
</a>
<br>
<a href="/teamlevelpoints" class="dash-link" target="_blank">
    <span>Team Level Points</span>
</a>
<br>
<a href="/userlevelpoints" class="dash-link" target="_blank">
    <span>User Level Points</span>
</a>
<br>
<a href="/usererrorreport" class="dash-link" target="_blank">
    <span>User Error Report</span>
</a>
{% endif %}
{% if current_user.role == 'Team Lead' %}
<a href="/auditreturnreport" class="dash-link" target="_blank">
    <span>Audit Return Report</span>
</a>
<br>
<a href="/automationerrorreport" class="dash-link" target="_blank">
    <span>Automation Error Report</span>
</a>
<br>
<a href="/clientvolume" class="dash-link" target="_blank">
    <span>Client Volume</span>
</a>
<br>
<a href="/outofscope" class="dash-link" target="_blank">
    <span>Out Of Scope</span>
</a>
<br>
<a href="/teamlevelpoints" class="dash-link" target="_blank">
    <span>Team Level Points</span>
</a>
<br>
<a href="/userlevelpoints" class="dash-link" target="_blank">
    <span>User Level Points</span>
</a>
<br>
<a href="/usererrorreport" class="dash-link" target="_blank">
    <span>User Error Report</span>
</a>
{% endif %}
</div>
{% endblock %}

这是解决该问题的一种方法。但是,如果用户只需在地址栏中输入任何嵌入式仪表板应用程序的路径,则无论其角色如何,他们都可以访问仪表板。这就是我试图限制的。我研究过 Flask-Authorize、Flask-Principal 和 Flask-RBAC,但发现几乎所有它们的文档充其量都是神秘的。至少,我无法理解在我的特定实例中实施这些解决方案的正确方法。任何帮助将不胜感激!

模型.py

import jwt
from time import time
from flask import current_app
from landing_page_pkg import db, login
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import UserMixin
from landing_page_pkg import login
from flask_security import RoleMixin

class User(UserMixin,db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), index=True, unique=True)
    email = db.Column(db.String(120), index=True, unique=True)
    password_hash = db.Column(db.String(128))
    role = db.Column(db.String(64))
    pin = db.Column(db.Integer)

    def __repr__(self):
        return f'<User {self.username}>'
    
    def set_password(self, password):
        self.password_hash = generate_password_hash(password)

    def check_password(self, password):
        return check_password_hash(self.password_hash, password)

    def get_reset_password_token(self, expires_in=600):
        return jwt.encode(
            {'reset_password': self.id, 'exp': time() + expires_in},
            current_app.config['SECRET_KEY'], algorithm='HS256')

    @staticmethod
    def verify_reset_password_token(token):
        try:
            id = jwt.decode(token, current_app.config['SECRET_KEY'],
                            algorithms=['HS256'])['reset_password']
        except:
            return
        return User.query.get(id)    

    
@login.user_loader
def load_user(id):
    return User.query.get(int(id))

forms.py

from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField, RadioField, IntegerField
from wtforms.validators import DataRequired, ValidationError,Email,EqualTo
from landing_page_pkg.models import User

class LoginForm(FlaskForm):
    username = StringField('Username', validators=[DataRequired()])
    password = PasswordField('Password', validators=[DataRequired()])
    remember_me = BooleanField('Remember Me')
    submit = SubmitField('Sign In')
    
class RegistrationForm(FlaskForm):
    username = StringField('Username', validators=[DataRequired()])
    email = StringField('Email', validators=[DataRequired(), Email()])
    password = PasswordField('Password', validators=[DataRequired()])
    password2 = PasswordField(
        'Repeat Password', validators=[DataRequired(), EqualTo('password')])
    ### NOTICE: To get the label to show for the "Role" field using wtf.quickform
    ### you must edit flask-bootstrap/templates/wtf.html and add {{field.label(class="control-label")|safe}}
    ### BEFORE the for loop {% for item in field -%}
    ###                       <div class="radio">
    role = RadioField(label='Role',choices=['Department Head','Team Lead','Project Manager'],validators=[DataRequired()])
    pin = IntegerField(label='Access Code')
    submit = SubmitField('Register')

    def validate_username(self, username):
        user = User.query.filter_by(username=username.data).first()
        if user is not None:
            raise ValidationError('Please use a different username.')

    def validate_email(self, email):
        user = User.query.filter_by(email=email.data).first()
        if user is not None:
            raise ValidationError('Please use a different email address.')
        
    def validate_role(self,role,pin):
        roles = role.data
        pins = pin.data
        
        if pins != 1000 and roles == 'Department Head':
            raise ValidationError('This role requires the proper Access Code. Please contact Seymour Stats administration for access.')        
        elif pins != 2000 and roles == 'Team Lead':
            raise ValidationError('This role requires the proper Access Code. Please contact Seymour Stats administration for access.')    
        
class ResetPasswordRequestForm(FlaskForm):
    email = StringField('Email', validators=[DataRequired(), Email()])
    submit = SubmitField('Request Password Reset')
    
class ResetPasswordForm(FlaskForm):
    password = PasswordField('Password', validators=[DataRequired()])
    password2 = PasswordField(
        'Repeat Password', validators=[DataRequired(), EqualTo('password')])
    submit = SubmitField('Request Password Reset')

如果我可以提供更多代码来帮助解决,请告诉我。

python flask rbac
1个回答
0
投票
from flask import Flask, redirect, url_for
from flask_login import LoginManager, UserMixin, login_user, current_user
from dash import Dash, html

app = Flask(__name__)
app.secret_key = 'your_secret_key'

# Flask-Login setup
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'

# User model
class User(UserMixin):
    def __init__(self, id, role):
        self.id = id
        self.role = role

# Dummy user database
users = {'admin': User('admin', 'admin'), 'user': User('user', 'user')}

@login_manager.user_loader
def load_user(user_id):
    return users.get(user_id)

@app.route('/login')
def login():
    user = users['admin']  # Simulate login
    login_user(user)
    return 'Logged in as admin'

# Initialize Dash app
dash_app1 = Dash(__name__, server=app, url_base_pathname='/dash1/')
dash_app2 = Dash(__name__, server=app, url_base_pathname='/dash2/')

# Function to protect Dash layout based on user role
def protected_dash_layout(dash_app, role):
    def serve_layout():
        if not current_user.is_authenticated or current_user.role != role:
            return html.Div("Access denied")
        # Original Dash layout here
        return html.Div("This is a protected Dash app for role: " + role)

    dash_app.layout = serve_layout

# Protecting Dash app layouts
protected_dash_layout(dash_app1, 'admin')
protected_dash_layout(dash_app2, 'user')

if __name__ == '__main__':
    app.run(debug=True)
© www.soinside.com 2019 - 2024. All rights reserved.