This might work, give it a test:
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
# from dash import dcc, html, callback
# from dash.dependencies import Output,Input, State
import dash_bootstrap_components as dbc
# from dash import dash_table
# from dash_table import DataTable, FormatTemplate
import dash_mantine_components as dmc
from dash_iconify import DashIconify
# import pandas as pd
# import pandas_datareader.data as web
#
# from datetime import date, datetime
#
#
# import pyodbc
# import os
# import psycopg2
#
# from plotly.io import write_image
# import flask
# import base64
# 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:
return (
"""invalid username and/or password <a href='/login'>login here</a>"""
)
if VALID_USERNAME_PASSWORD.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"}
],
)
# 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 = {
"test": {"password": "test", "role": "admin"},
"hello": {"password": "world", "role": "user"},
}
# 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"]
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.id):
if (
VALID_USERNAME_PASSWORD.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"]
):
return (
dcc.Link(
"", id="logout", href="/logout", style={"position": "fixed"}
),
"/permission",
)
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"}
app.run(debug=True, port="1009")