Package: dash-security
dash-securitypipinstalldash-securityDash Security provides building blocks for developing secure, modern Dash apps with Dash Pages.In addition, it allows you to granularly control access to different layouts for users and orgs, and dynamically serve different content to different users with the same layouts and assets.(future: arbitrary permission levels and groups)Dash Security emulates Dash Pages: it securely registers layouts and assets todash.page_registry.dash-security example:examples/basicdash-securityallows you to granularly control access to different layouts for users and orgs, and dynamically serve different content to different users with the same layouts and assets.This example shows off the features ofdash-security.integrates with Dash Pagesbuilds on top of Flask-Loginintegrated with SQLAlchemy 2.0+all page layouts protected by defaultall callbacks protected by defaultthelayouts/folderregistering pages into Dash Securityregistering assets into Dash Securitydynamically serving assets based on emails/orgsdynamically mocking userspip3installdash-securitysetup environment for the examplecdexamples/basic

python3-mvenvvenvsourcevenv/bin/activate
pip3install-rrequirements.txt

python3create_tables.py
python3app.pyadd userMany Dash users need a way to add a user, but don't need a complex admin dashboard. Theadd_user.pyscript allows you to do this simply. Make sure to change the database credentials to your own database.# copy examples/add_user.py to your directory# edit the values in add_user.py# make sure `venv` is activatedpython3add_user.pyintegratingdash-securityinto a projectAssumptions:the project uses Flask-Login for authentication, with theflask_login.LoginManagersetup on a separateflask.Flaskobject that is then used by thedash.Dashobject.there is a module calledmodelsin the root level that has aUserclass that inherits from sqlalchemy.orm.DeclarativeBase and flask_login.UserMixin;emailis the primary key and there's a non-nullorgstr attribute; there's a methodget_idthat returns theemailattribute (for Flask-Login to work)theflask.Flaskobject has aengineattribute that is a SQLAlchemy engine i.e. just do:server=flask.Flask(__name__)engine=sqlalchemy.create_engine(...)server.engine=enginedeveloping withdash-securityfeaturesThis code has a full example inlayouts/andpages/where different assets and layouts are granted to different users or orgs.init_securityAt the end of theapp.pyfile (or wherever you run the app), run theinit_securityfunction:fromdashimportDash,html,page_container,callbackfromserverimportserver# flask serverapp=Dash(__name__,server=server,use_pages=True)app.layout=html.Div(["My App",page_container,])@callbackdefsome_callback(...):...# at the end of the file that you run with `python3 app.py` or import with e.g. gunicorninit_security(app,default=True)if__name__=="__main__":app.run_server(port=8000,debug=True)registering layoutsEach Dash page layout must be registered into thedash-securitysystem withregister_layout, and explicitly given access to the given emails or orgs. If the emails and orgs arguments are left blank, any logged in user can access. Ifopen=Truethen any user can access regardless of login.If a user ends up at a URL path that doesn't exist,dash-securitydisplays a "404 Page does not exist" content by default; you can update this by passing a valid Dash component object to theinit_securityfunction.pages/page.pyfromdashimportregister_page,htmlfromsecurityimportregister_layoutregister_page(__name__,"/page")@register_layout(__name__,emails="me@this.com")deflayout():returnhtml.Div(...)getting a user's pagesA user will have access to some pages and not have access to others. Get the list of accessible pages withget_user_page_registrywhich returns the subset of the Dash Pages page registry that the current user has access to - including if there is no logged-in user.pages/home.pyfromdashimportregister_pagefromsecurityimportget_user_page_registry,register_layoutregister_page(__name__,"/")@register_layout(__name__,open=True)deflayout():returnhtml.Div([dcc.Link(page["title"],href=page['path'])forpageinget_user_page_registry()])protecting callbacksAny callback you want to be accessible to non-logged-in users must be explicitly unprotected with theunprotecteddecorator. For example you might unprotect callbacks on your home page or for login.fromsecurityimportunprotected@callback(...)@unprotecteddeflogin_callback(...):...registering assets and using in layouts"Assets" are the content in layouts that can be given different permissions or fetching logic for different users/orgs/permissions levels, without importing the specific function for it.This must happen in files in thelayouts/folder.Fetch the relevant asset content for a given user/org withget_user_asset.layouts/page.pyfromsecurityimportregister_asset,register_layout,get_user_assetfromdashimportregister_pagepage="page"asset="content"@register_asset(page,asset,emails=["test1@test.com"])deff():# this name doesn't matterreturn"unique content"@register_asset(page,asset,emails=["test2@test.com"])deff():return"special content"pages/page.pyfromdashimportregister_pagefromsecurityimportregister_layout,get_user_assetpage="page"asset="content"@register_layout(__name__,orgs=["test"])deflayout():returnhtml.Div(["Page",get_user_asset(page,asset)])redirecting authenticated users away from non-authed pagesSometimes you want to redirect a user away from a page if they're already logged in. A good example of this is login - users don't need to see the login page if they're already logged in.fromsecurityimportunprotected,register_layoutregister_page(__name__,"/login")HOME_PATH="/"@register_layout(__name__,open=True)deflayout():ifcurrent_user.is_authenticated:returndcc.Location(id='redirect-authed',href=HOME_PATH)return...mocking usersOne unique feature enabled by Dash Security is user mocking. This allows you
to see the app through the eyes of a different user than the one that is
logged in. This becomes very useful when you're building an application for
many users and rendering layouts differently depending on who the user is....fromserverimportserver# flask.Flaskfromsecurityimportmock_users,init_securityfromdashimportDash,page_container,html,dccfrommodelsimportUser...app=Dash(__name__,use_pages=True,server=server)mocks=mock_users(app,user=User)# add logic to remove this in production environmentsapp.layout=html.Div([dcc.Location(id="url"),*mocks,# add logic to remove this in production environmentshtml.Div(id="links"),html.Hr(),page_container,])...init_security(app)if__name__=="__main__":app.run_server(debug=True,port=8050)