You need to have the roles listed as well in the dictionary.
Hmm, I’ll take a look at the code on my computer later.
You need to have the roles listed as well in the dictionary.
Hmm, I’ll take a look at the code on my computer later.
the roles is listed in the dataframe.
username | password | role |
---|---|---|
test | test | admin |
hello | world | user |
user | 123 | user |
What I normally do is this.
users = pd.read_sql(‘select * from tblUsers’, con).index(‘username’).to_dict(‘records’)
If use your method, will not require the VALID_USERNAME_PASSWORD_PAIRS
?
normally, i will do like this :
df_userpwd = pd.read_sql_query('''
SELECT * FROM dbo.DRR_UsernamePwd
''', conn)
VALID_USERNAME_PASSWORD_PAIRS = df_userpwd.set_index('username')['password'].to_dict()
But now have additional one more column to call the role listing, so I not sure how can I add in the VALID_USERNAME_PASSWORD_PAIRS.
Don’t put passwords, otherwise you won’t have the roles.
mean I just remove the password right?
VALID_USERNAME_PASSWORD_PAIRS = df_userpwd.set_index('username').to_dict()
What that should do is this:
{'test':{'password':'test', 'role': 'admin'},
'hello':{'password':'world', 'role':'user'},
'user':{'password':'123','role':'user'}
}
Where the username is the key that determines the information.
how about if all the information is store in dataframe or database?
As long as you convert it in this dictionary, it is easy to reference with the username.
How’s your page-2 look?
my page 2 is hide in the menu bar
Yes, but how is it registered?
dash.register_page(__name__,
path='/page2', # '/' is home page and it represents the url
name=' page2', # name of page, commonly used as name of link
title='page2', # title that appears on browser's tab
icon="bi bi-clipboard-check",
# image='pg1.png', # image in the assets folder
# description='Histograms are the new bar charts.'
)
require_login(__name__,
access_level=['admin', 'user']
)
Did you take out the .id from current_user?
for all the current_user?
yes, but still hide in menu bar
What does it say if you put it back in again?
for page in dash.page_registry.values():
if (
page["name"] not in home
and page["path"] in restricted_page
and "access_level" in restricted_page[page["path"]]
):
if VALID_USERNAME_PASSWORD.get(current_user):
# if VALID_USERNAME_PASSWORD_PAIRS.get(current_user):
if (
VALID_USERNAME_PASSWORD.get(current_user.id)["role"]
# VALID_USERNAME_PASSWORD_PAIRS.get(current_user.id)["role"]
in restricted_page[page["path"]]["access_level"]
):
if VALID_USERNAME_PASSWORD.get(current_user):
id in here must remove, if not, will not work.
other current user has put it back, but is same, still hidden
here is the full app.py:
import os
from flask import Flask, request, redirect, session
from flask_login import login_user, LoginManager, UserMixin, logout_user, current_user
import dash
from dash import dcc, html, Input, Output, State, ALL
from dash.exceptions import PreventUpdate
from utils.login_handler import restricted_page
import plotly.express as px
from plotly.offline import plot
import dash_bootstrap_components as dbc
import dash_mantine_components as dmc
from dash_iconify import DashIconify
import pandas as pd
from pprint import pp
# Exposing the Flask Server to enable configuring it for logging in
server = Flask(__name__)
@server.route('/login', methods=['POST'])
def login_button_click():
if request.form:
username = request.form['username']
password = request.form['password']
if VALID_USERNAME_PASSWORD.get(username) is None:
# if VALID_USERNAME_PASSWORD_PAIRS.get(username) is None:
return """invalid username and/or password <a href='/login'>login here</a>"""
if VALID_USERNAME_PASSWORD.get(username)['password'] == password:
# if VALID_USERNAME_PASSWORD_PAIRS.get(username)['password'] == password:
login_user(User(username))
if 'url' in session:
if session['url']:
url = session['url']
session['url'] = None
return redirect(url) ## redirect to target url
return redirect('/') ## redirect to home
return """invalid username and/or password <a href='/login'>login here</a>"""
app = dash.Dash(
__name__, server=server, use_pages=True, suppress_callback_exceptions=True
,external_stylesheets=[dbc.themes.BOOTSTRAP, dbc.icons.BOOTSTRAP],
meta_tags=[{'name': 'viewport',
'content': 'width=device-width, initial-scale=1.0'}]
)
# auth = dash_auth.BasicAuth(app, VALID_USERNAME_PASSWORD_PAIRS)
# Keep this out of source code repository - save in a file or a database
# passwords should be encrypted
# VALID_USERNAME_PASSWORD = {"abc": "abc", "welcome": "world"}
VALID_USERNAME_PASSWORD = {"abc": {"password":"abc", "role":"admin"}, "hello": {"password":"world", "role":"user"}}
# df_userpwd= pd.read_csv("pwd.csv")
# df_userpwd= pd.read_csv("pwd.csv").set_index('username').transpose()
# userpwd = pp(df_userpwd.to_dict())
# VALID_USERNAME_PASSWORD = pp(df_userpwd.to_dict())
# Updating the Flask Server configuration with Secret Key to encrypt the user session cookie
server.config.update(SECRET_KEY='291a47103f3cd8fc26d05ffc7b31e33f73ca3d459d6259bd')
# Login manager object will be used to login / logout users
login_manager = LoginManager()
login_manager.init_app(server)
login_manager.login_view = "/login"
class User(UserMixin):
# User data model. It has to have at least self.id as a minimum
def __init__(self, username):
self.id = username
@login_manager.user_loader
def load_user(username):
"""This function loads the user by user id. Typically this looks up the user from a user database.
We won't be registering or looking up users in this example, since we'll just login using LDAP server.
So we'll simply return a User object with the passed in username.
"""
return User(username)
SIDEBAR_STYLE = {
"position": "fixed",
"width": "20rem",
"padding": "2rem 1rem",
"background-color": "#cbd3dd",
"z-index": 1050,
"transition": "width 0.1s ease-in-out",
}
# padding for the page content
CONTENT_STYLE = {
"margin-left": "10rem",
"margin-right": "2rem",
"padding": "2rem 1rem",
}
content = html.Div(id="page-content", children=[],
style=CONTENT_STYLE
)
home = [' Home', ' About', ' login', ' Logout', ' Permission']
######################
# sidebar = dbc.Nav([
# dbc.NavLink([
# html.Div([
# html.I(className='bi bi-house', style={'position': 'absolute', 'left': '42px'}),
# html.Span(' Home', style={'padding-left': '20px'}),
# ], className="ms-2"),
# ],
# href='/',
# active="exact",
# ),
# dmc.Accordion(
# dmc.AccordionItem([
# dbc.NavLink([
# html.Div([
# html.I(className=page["icon"]),
# page["name"]
# ], className="ms-2"),
# ],
# href=page["path"],
# active="exact",
# )
# for page in dash.page_registry.values() if page['name'] not in home
# ], label='Profile',
# icon=[
# DashIconify(
# icon="carbon:search-locate",
# color=dmc.theme.DEFAULT_COLORS["blue"][6],
# width=20,
# )
# ],
# ),
# multiple=True,
# disableIconRotation=True,
# ),
# dbc.NavLink([
# html.Div([
# html.I(className='bi bi-box-arrow-right', style={'position': 'absolute', 'left': '42px'}),
# html.Span(' Logout', style={'padding-left': '20px'}),
# ], className="ms-2"),
# ],
# href='/logout',
# active="exact",
# ),
# ],
# vertical=True,
# pills=True,
# # className="bg-light",
# # className="sidebar"
# )
########################
def navigation():
navs = []
for page in dash.page_registry.values():
if page["name"] not in home and page["path"] not in restricted_page:
navs.append(
dbc.NavLink(
[
html.Div(
[html.I(className=page["icon"]), page["name"]],
className="ms-2",
),
],
href=page["path"],
active="exact",
)
)
for page in dash.page_registry.values():
if (
page["name"] not in home
and page["path"] in restricted_page
and "access_level" not in restricted_page[page["path"]]
):
navs.append(
dbc.NavLink(
[
html.Div(
[html.I(className=page["icon"]), page["name"]],
className="ms-2",
),
],
href=page["path"],
active="exact",
)
)
for page in dash.page_registry.values():
if (
page["name"] not in home
and page["path"] in restricted_page
and "access_level" in restricted_page[page["path"]]
):
if VALID_USERNAME_PASSWORD.get(current_user):
# if VALID_USERNAME_PASSWORD_PAIRS.get(current_user):
if (
VALID_USERNAME_PASSWORD.get(current_user.id)["role"]
# VALID_USERNAME_PASSWORD_PAIRS.get(current_user.id)["role"]
in restricted_page[page["path"]]["access_level"]
):
navs.append(
dbc.NavLink(
[
html.Div(
[html.I(className=page["icon"]), page["name"]],
className="ms-2",
),
],
href=page["path"],
active="exact",
)
)
return navs
# page 1 data
def sidebar():
sidebar = dbc.Nav(
[
dbc.NavLink(
[
html.Div(
[
html.I(
className="bi bi-house",
style={"position": "absolute", "left": "42px"},
),
html.Span(" Home", style={"padding-left": "20px"}),
],
className="ms-2",
),
],
href="/",
active="exact",
),
dmc.Accordion(
dmc.AccordionItem(
navigation(),
label="Profile",
icon=[
DashIconify(
icon="carbon:search-locate",
color=dmc.theme.DEFAULT_COLORS["blue"][6],
width=20,
)
],
),
multiple=True,
disableIconRotation=True,
),
dbc.NavLink(
[
html.Div(
[
html.I(
className="bi bi-box-arrow-right",
style={"position": "absolute", "left": "42px"},
),
html.Span(" Logout", style={"padding-left": "20px"}),
],
className="ms-2",
),
],
href="/logout",
active="exact",
),
],
vertical=True,
pills=True,
# className="bg-light",
# className="sidebar"
)
return sidebar
########################
app.layout = dbc.Container([
dcc.Location(id="url"),
html.Div([
dbc.Button(html.I(className ="bi bi-list"), #"Menu",
id="open-offcanvas", n_clicks=0),
dbc.Offcanvas([
sidebar(),
content
],
id="offcanvas",
title="Welcome to .../",
is_open=False,
scrollable = True,
backdrop="static",
style=SIDEBAR_STYLE,
# className="sidebar-header"
),
],
style={'position': 'fixed'},
),
# html.Div([
# dbc.Nav([
# dbc.NavLink(id = 'home', href="/", className='bi bi-house'),
# dbc.NavLink(id = 'logout', href="/logout", className='bi bi-box-arrow-right'),
# ], id = 'nav'
# , className="d-grid gap-2 d-md-flex justify-content-md-end"
# ),
# ]),
html.Div(id="user-status-header"),
dbc.Row([
# dbc.Col(
# [html.Div([
# sidebar
# ]),
# ], xs=4, sm=4, md=2, lg=2, xl=2, xxl=2),
# dbc.Col([
dash.page_container
# ], xs=8, sm=8, md=10, lg=10, xl=10, xxl=10)
], className="content"
)
], fluid=True)
# return layout
@app.callback(
Output("offcanvas", "is_open"),
Input("open-offcanvas", "n_clicks"),
[State("offcanvas", "is_open")],
)
def toggle_offcanvas(n1, is_open):
if n1:
return not is_open
return is_open
@app.callback(
Output("user-status-header", "children"),
Output('url','pathname'),
Output('open-offcanvas','style'),
# Output('nav','style'),
Input("url", "pathname"),
Input({'index': ALL, 'type':'redirect'}, 'n_intervals')
)
def update_authentication_status(path, n):
### logout redirect
if n:
if not n[0]:
return '', dash.no_update, {'display':'none'}
else:
return '', '/login', {'display':'none'}
### test if user is logged in
if current_user.is_authenticated:
if path in restricted_page:
if 'access_level' in restricted_page[path]:
if (VALID_USERNAME_PASSWORD[current_user.id]['role'] not in restricted_page[path]['access_level']):
# if (VALID_USERNAME_PASSWORD_PAIRS[current_user.id]['role'] not in restricted_page[path]['access_level']):
return (dcc.Link("",id = 'logout', href="/logout", style={'position': 'fixed'})), '/permission', {'display':'initial'}
if path == '/login':
return (dcc.Link("" , id = 'logout', href="/logout", style={'position': 'fixed'}), '/', {'display':'initial'})
return (dcc.Link("",id = 'logout', href="/logout", style={'position': 'fixed'}), dash.no_update, {'display':'initial'})
else:
### if page is restricted, redirect to login and save path
if path in restricted_page:
session['url'] = path
return dcc.Link("login", href="/login"), '/login', {'display':'none'}
### if path not login and logout display login link
if current_user and path not in ['/login', '/logout']:
return dcc.Link("login", href="/login"), dash.no_update, {'display':'initial'}
### if path login and logout hide links
if path in ['/login', '/logout']:
return '', dash.no_update, {'display':'none'}
if __name__ == "__main__":
app.run_server(debug=True, port=1009)
# app.run(debug=True, port='1009')
##################################
# @app.route('/login', methods=['GET', 'POST'])
# def login():
# error = None
# if request.method == 'POST':
# if request.form['username'] != 'admin' or request.form['password'] != 'admin':
# error = 'Invalid Credentials. Please try again.'
# else:
# return redirect(url_for('home'))
# return render_template('login.html', error=error)
Alright, here you go:
import os
from flask import Flask, request, redirect, session
from flask_login import login_user, LoginManager, UserMixin, logout_user, current_user
import dash
from dash import dcc, html, Input, Output, State, ALL
from dash.exceptions import PreventUpdate
from utils.login_handler import restricted_page
import plotly.express as px
from plotly.offline import plot
import dash_bootstrap_components as dbc
import dash_mantine_components as dmc
from dash_iconify import DashIconify
import pandas as pd
from pprint import pp
# Exposing the Flask Server to enable configuring it for logging in
server = Flask(__name__)
@server.route('/login', methods=['POST'])
def login_button_click():
if request.form:
username = request.form['username']
password = request.form['password']
if VALID_USERNAME_PASSWORD.get(username) is None:
# if VALID_USERNAME_PASSWORD_PAIRS.get(username) is None:
return """invalid username and/or password <a href='/login'>login here</a>"""
if VALID_USERNAME_PASSWORD.get(username)['password'] == password:
# if VALID_USERNAME_PASSWORD_PAIRS.get(username)['password'] == password:
login_user(User(username))
if 'url' in session:
if session['url']:
url = session['url']
session['url'] = None
return redirect(url) ## redirect to target url
return redirect('/') ## redirect to home
return """invalid username and/or password <a href='/login'>login here</a>"""
app = dash.Dash(
__name__, server=server, use_pages=True, suppress_callback_exceptions=True
, external_stylesheets=[dbc.themes.BOOTSTRAP, dbc.icons.BOOTSTRAP],
meta_tags=[{'name': 'viewport',
'content': 'width=device-width, initial-scale=1.0'}]
)
# auth = dash_auth.BasicAuth(app, VALID_USERNAME_PASSWORD_PAIRS)
# Keep this out of source code repository - save in a file or a database
# passwords should be encrypted
# VALID_USERNAME_PASSWORD = {"abc": "abc", "welcome": "world"}
VALID_USERNAME_PASSWORD = {"abc": {"password": "abc", "role": "admin"}, "hello": {"password": "world", "role": "user"}}
# df_userpwd= pd.read_csv("pwd.csv")
# df_userpwd= pd.read_csv("pwd.csv").set_index('username').transpose()
# userpwd = pp(df_userpwd.to_dict())
# VALID_USERNAME_PASSWORD = pp(df_userpwd.to_dict())
# Updating the Flask Server configuration with Secret Key to encrypt the user session cookie
server.config.update(SECRET_KEY='291a47103f3cd8fc26d05ffc7b31e33f73ca3d459d6259bd')
# Login manager object will be used to login / logout users
login_manager = LoginManager()
login_manager.init_app(server)
login_manager.login_view = "/login"
class User(UserMixin):
# User data model. It has to have at least self.id as a minimum
def __init__(self, username):
self.id = username
@login_manager.user_loader
def load_user(username):
"""This function loads the user by user id. Typically this looks up the user from a user database.
We won't be registering or looking up users in this example, since we'll just login using LDAP server.
So we'll simply return a User object with the passed in username.
"""
return User(username)
SIDEBAR_STYLE = {
"position": "fixed",
"width": "20rem",
"padding": "2rem 1rem",
"background-color": "#cbd3dd",
"z-index": 1050,
"transition": "width 0.1s ease-in-out",
}
# padding for the page content
CONTENT_STYLE = {
"margin-left": "10rem",
"margin-right": "2rem",
"padding": "2rem 1rem",
}
content = html.Div(id="page-content", children=[],
style=CONTENT_STYLE
)
home = [' Home', ' About', ' login', ' Logout', ' Permission']
######################
# sidebar = dbc.Nav([
# dbc.NavLink([
# html.Div([
# html.I(className='bi bi-house', style={'position': 'absolute', 'left': '42px'}),
# html.Span(' Home', style={'padding-left': '20px'}),
# ], className="ms-2"),
# ],
# href='/',
# active="exact",
# ),
# dmc.Accordion(
# dmc.AccordionItem([
# dbc.NavLink([
# html.Div([
# html.I(className=page["icon"]),
# page["name"]
# ], className="ms-2"),
# ],
# href=page["path"],
# active="exact",
# )
# for page in dash.page_registry.values() if page['name'] not in home
# ], label='Profile',
# icon=[
# DashIconify(
# icon="carbon:search-locate",
# color=dmc.theme.DEFAULT_COLORS["blue"][6],
# width=20,
# )
# ],
# ),
# multiple=True,
# disableIconRotation=True,
# ),
# dbc.NavLink([
# html.Div([
# html.I(className='bi bi-box-arrow-right', style={'position': 'absolute', 'left': '42px'}),
# html.Span(' Logout', style={'padding-left': '20px'}),
# ], className="ms-2"),
# ],
# href='/logout',
# active="exact",
# ),
# ],
# vertical=True,
# pills=True,
# # className="bg-light",
# # className="sidebar"
# )
########################
def navigation():
navs = []
for page in dash.page_registry.values():
if page["name"] not in home and page["path"] not in restricted_page:
navs.append(
dbc.NavLink(
[
html.Div(
[#html.I(className=page["icon"]),
page["name"]],
className="ms-2",
),
],
href=page["path"],
active="exact",
)
)
for page in dash.page_registry.values():
if (
page["name"] not in home
and page["path"] in restricted_page
and "access_level" not in restricted_page[page["path"]]
):
navs.append(
dbc.NavLink(
[
html.Div(
[#html.I(className=page["icon"]),
page["name"]],
className="ms-2",
),
],
href=page["path"],
active="exact",
)
)
for page in dash.page_registry.values():
if (
page["name"] not in home
and page["path"] in restricted_page
and "access_level" in restricted_page[page["path"]]
):
if current_user:
# if VALID_USERNAME_PASSWORD_PAIRS.get(current_user):
if (
VALID_USERNAME_PASSWORD[current_user.id]["role"]
# VALID_USERNAME_PASSWORD_PAIRS.get(current_user.id)["role"]
in restricted_page[page["path"]]["access_level"]
):
navs.append(
dbc.NavLink(
[
html.Div(
[#html.I(className=page["icon"]),
page["name"]],
className="ms-2",
),
],
href=page["path"],
active="exact",
)
)
return navs
# page 1 data
def sidebar():
sidebar = dbc.Nav(
[
dbc.NavLink(
[
html.Div(
[
html.I(
className="bi bi-house",
style={"position": "absolute", "left": "42px"},
),
html.Span(" Home", style={"padding-left": "20px"}),
],
className="ms-2",
),
],
href="/",
active="exact",
),
dmc.Accordion(
dmc.AccordionItem(
navigation(),
label="Profile",
icon=[
DashIconify(
icon="carbon:search-locate",
color=dmc.theme.DEFAULT_COLORS["blue"][6],
width=20,
)
],
),
multiple=True,
disableIconRotation=True,
),
dbc.NavLink(
[
html.Div(
[
html.I(
className="bi bi-box-arrow-right",
style={"position": "absolute", "left": "42px"},
),
html.Span(" Logout", style={"padding-left": "20px"}),
],
className="ms-2",
),
],
href="/logout",
active="exact",
),
],
vertical=True,
pills=True,
# className="bg-light",
# className="sidebar"
)
return sidebar
########################
app.layout = dbc.Container([
dcc.Location(id="url"),
html.Div([
dbc.Button(html.I(className="bi bi-list"), # "Menu",
id="open-offcanvas", n_clicks=0),
dbc.Offcanvas([
sidebar(),
content
],
id="offcanvas",
title="Welcome to .../",
is_open=False,
scrollable=True,
backdrop="static",
style=SIDEBAR_STYLE,
# className="sidebar-header"
),
],
style={'position': 'fixed'},
),
# html.Div([
# dbc.Nav([
# dbc.NavLink(id = 'home', href="/", className='bi bi-house'),
# dbc.NavLink(id = 'logout', href="/logout", className='bi bi-box-arrow-right'),
# ], id = 'nav'
# , className="d-grid gap-2 d-md-flex justify-content-md-end"
# ),
# ]),
html.Div(id="user-status-header"),
dbc.Row([
# dbc.Col(
# [html.Div([
# sidebar
# ]),
# ], xs=4, sm=4, md=2, lg=2, xl=2, xxl=2),
# dbc.Col([
dash.page_container
# ], xs=8, sm=8, md=10, lg=10, xl=10, xxl=10)
], className="content"
)
], fluid=True)
# return layout
@app.callback(
Output("offcanvas", "is_open"),
Output("offcanvas","children"),
Input("open-offcanvas", "n_clicks"),
[State("offcanvas", "is_open")],
)
def toggle_offcanvas(n1, is_open):
if n1:
return not is_open, [sidebar(), content]
return is_open, dash.no_update
@app.callback(
Output("user-status-header", "children"),
Output('url', 'pathname'),
Output('open-offcanvas', 'style'),
# Output('nav','style'),
Input("url", "pathname"),
Input({'index': ALL, 'type': 'redirect'}, 'n_intervals')
)
def update_authentication_status(path, n):
### logout redirect
if n:
if not n[0]:
return '', dash.no_update, {'display': 'none'}
else:
return '', '/login', {'display': 'none'}
### test if user is logged in
if current_user.is_authenticated:
if path in restricted_page:
if 'access_level' in restricted_page[path]:
if (VALID_USERNAME_PASSWORD[current_user.id]['role'] not in restricted_page[path]['access_level']):
# if (VALID_USERNAME_PASSWORD_PAIRS[current_user.id]['role'] not in restricted_page[path]['access_level']):
return (dcc.Link("", id='logout', href="/logout", style={'position': 'fixed'})), '/permission', {
'display': 'initial'}
if path == '/login':
return (dcc.Link("", id='logout', href="/logout", style={'position': 'fixed'}), '/', {'display': 'initial'})
return (
dcc.Link("", id='logout', href="/logout", style={'position': 'fixed'}), dash.no_update, {'display': 'initial'})
else:
### if page is restricted, redirect to login and save path
if path in restricted_page:
session['url'] = path
return dcc.Link("login", href="/login"), '/login', {'display': 'none'}
### if path not login and logout display login link
if current_user and path not in ['/login', '/logout']:
return dcc.Link("login", href="/login"), dash.no_update, {'display': 'initial'}
### if path login and logout hide links
if path in ['/login', '/logout']:
return '', dash.no_update, {'display': 'none'}
if __name__ == "__main__":
app.run_server(debug=True, port=12345)
# app.run(debug=True, port='1009')
##################################
# @app.route('/login', methods=['GET', 'POST'])
# def login():
# error = None
# if request.method == 'POST':
# if request.form['username'] != 'admin' or request.form['password'] != 'admin':
# error = 'Invalid Credentials. Please try again.'
# else:
# return redirect(url_for('home'))
# return render_template('login.html', error=error)