Different user access login to different page in dash_auth

Hi,

How to create the login access to different user which they only able to login the assigned page?

User who doesn’t have the access will not able to review or click to the page.

Currently, I am using the dash_auth to create a simple login page. However, it may need to lock some page for certain user.

Hello @beginof,

Sure thing, you can check out my auth flow here:

Leveraging this, you can add customizations based upon specific user and access levels. You’ll need to make a different dictionary of access levels, and then test to make sure the user is supposed to be able to see the page they are trying to.

If they dont, then you can return something that says they dont have permission to view the content.

This would look something like:

if path in perm[current_user]:
     return 'uh-oh, you dont have access to this content'

Where perm[current_user] is a list of paths they dont have access to.

1 Like

But, essentially, you dont need to alter the flow, just make a dictionary of users and paths they arent allowed to view. Then bump up against it via the dcc.Location upon request to make sure they can access it.

Can you share how the perm[current_user] add into the existing code and able to restrict the user?

How to call it for the validation if I have a listing which contain username, password and role stored in a dataframe (will based on the role to restrict the user)?

example, “admin” role able to view all the page and “view only” able to view the page-1.

Sure, here you go.

To determine roles, it might be easier with a dictionary, something like this:

VALID_USERNAME_PASSWORD = {"test": {"password":"test", "role":"admin"}, "hello": {"password":"world", "role":"user"}}

utils > loginhandler.py

import dash

restricted_page = {}

def require_login(page, **kwargs):
    for pg in dash.page_registry:
        if page == pg:
            restricted_page[dash.page_registry[pg]['path']] = True
            if access_level in kwargs:
                restricted_page[dash.page_registry[pg]['path']]['access_level'] = kwargs['access_level']

callback changes in app.py:

@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.callback(
    Output("user-status-header", "children"),
    Output('url','pathname'),
    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
        else:
            return '', '/login'

    ### 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]['role'] not in restricted_page[path]['access_level']:
                      return dcc.Link('logout', href='/logout'), '/permission'
        if path == '/login':
            return dcc.Link("logout", href="/logout"), '/'
        return dcc.Link("logout", href="/logout"), dash.no_update
    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'

    ### 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

    ### if path login and logout hide links
    if path in ['/login', '/logout']:
        return '', dash.no_update

pages > permission.py

import dash 

dash.register_page(__name__, path='/permission')

layout = "Sorry, you do not have permission to view this content, please get with your administrator if you think this is wrong"

pages > page-1.py

import dash
from dash import html, dcc, Output, Input, callback
from utils.login_handler import require_login

dash.register_page(__name__)
require_login(__name__, access_level=['admin'])


layout = html.Div(
    [
        html.H1("Page 1"),
        dcc.Dropdown(
            id="page-1-dropdown",
            options=[{"label": i, "value": i} for i in ["LA", "NYC", "MTL"]],
            value="LA",
        ),
        html.Div(id="page-1-content"),
        html.Br(),
        dcc.Link("Go to Page 2", href="/page-2"),
        html.Br(),
        dcc.Link("Go back to home", href="/"),
    ]
)


@callback(Output("page-1-content", "children"), Input("page-1-dropdown", "value"))
def page_1_dropdown(value):
    return f'You have selected "{value}"'

the access_level in the login_handler is invalid, is it I miss one page py?

Sorry, ‘access_level’

Amended to 'access_level'.

Show another error :joy:

restricted_page[dash.page_registry[pg][‘path’]][‘access_level’] = kwargs[‘access_level’]
TypeError: ‘bool’ object does not support item assignment

1 Like

Ok, had to make some more changes:

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

# 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 = {"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', ' login', ' logout']

# page 1 data
# def layout():
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,
    )
],
    vertical=True,
    pills=True,
    # className="bg-light",

)

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,
        ),
    ]),
    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)
        ]
    )


    ], 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'),
    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
        else:
            return '', '/login'

    ### 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('logout', href='/logout'), '/permission'
        if path == '/login':
            return dcc.Link("logout", href="/logout"), '/'
        return dcc.Link("logout", href="/logout"), dash.no_update
    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'

    ### 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

    ### if path login and logout hide links
    if path in ['/login', '/logout']:
        return '', dash.no_update

app.run(debug=True, port='12345')

login_handler.py

import dash

restricted_page = {}

def require_login(page, **kwargs):
    for pg in dash.page_registry:
        if page == pg:
            restricted_page[dash.page_registry[pg]['path']] = {}
            restricted_page[dash.page_registry[pg]['path']]['restricted'] = True
            if 'access_level' in kwargs:
                restricted_page[dash.page_registry[pg]['path']]['access_level'] = kwargs['access_level']
1 Like

image



current version: user which is not include in the access_level will show an error message but the user still able to view the page. How to block the user to visit the page and show a message some kind like this ‘you didn’t have the authority’?

and let say the first page visit when login is the home.py, but the user did not has the authority to view the page, can the webpage directly jump to the page which the user has the authority to visit?

It looks like you added another output to the callback, you just need to add another thing to the return in order to be able to use it.

You are wanting to hide the offcanvas on button click?

I wouldn’t recommend having users not have permission to the home page as this is your default landing page. But, if you wanted to do that, then you need to have fallback location they can go to that would offer navigation.

yes want to hide the button

noted, will keep the hoe page authority for all user. But it is same go to the other page. User not include in the access_level but still can visit the page, only appear error message.

Can you post your app.py?

You have something added that I didn’t.

yes, sure. 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

# 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']

# page 1 data
# def layout():
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"
)

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')


Just add,

{‘display’:’initial’}

After the permission bit, and it should be fine.

thanks @jinnyzor :star_struck:

@jinnyzor ,

can i hide the page title in the menu bar if the user don’t have the authority to visit the page?
is it hard to do so? if yes, then is ok, current version is good enough for me now.
I just ask only.

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")

need to remove the id only able to work…

is it require to amend something in the content page 2 ? because the page is hide for all user even the user has the authority.

besides, can I use the pair function to connect the database to get the username, password and role?

I’m referring to VALID_USERNAME_PASSWORD_PAIRS = df_userpwd.set_index('username')['password'].to_dict(), this is use last time which only have username and password.