Control Who Could Access Your Streamlit Dashboards

May 16, 2022

Control Who Could Access Your Streamlit Dashboards

Authentication may prevent outsiders, but we need an authorization flow to control who could see what.

Steramlit simplifies one of the grandest difficulties of Data Scientists — Building an app to interact with their machine learning models. It provides a set of declarative methods to create web components.

But security is a problem when you share Streamlit apps with others. Streamlit has a built-in solution to restrict access to users. But it’s far from perfect.

In a previous post, I shared how we can use Django’s authentication framework to improve the security of Streamlit apps.

Related: Secure Your Streamlit App With Django

This post continues the previous one and covers more granular access control strategies for your Streamlit app. I suggest you read the first one before diving into this.

Related: How to Create Stunning Web Apps for your Data Science Projects

Why do we need authorization control?

A login screen prevents unidentified people from accessing the system. We call it user authentication.

Yet, authentication is only one, but an essential item on your security checklist. There are several ways you could improve this.

For instance, you could put an IP restriction on your firewall. But doing so put’s a high-level rule on blocking a mass of people.

What if you want a granular control?

Say you need to show employees only a specific part of the dashboard. But you may decide to offer a team-level dashboard and individual ones to the team leads.

The ability to do this is access control.

We’ve already discussed why Stramlit’s suggested user authentication flow isn’t good enough for production-grade apps. We’ve used Django to bridge the gap.

Streamlit also has no option for authorization. Let’s go ahead and extend the Django authentication to handle permissions.

Related: How to Run Python Tests on Every Commit Using GitHub Actions

Controlling dashboard permissions with Django.

The plan is simple.

Assign users to specific groups. A user can be in multiple groups too. We can do this in the admin interface.

Then in the Streamlit app, we check for group membership of the logged-in user.

If this check is passed, we’ll render the dashboard. Otherwise, we’ll display a message saying, “You cannot view this dashboard.”

But don’t we need the groups in the first place? Let’s start there.

Related: 7 Ways to Make Your Python Project Structure More Elegant

Create user groups in the Django admin console.

If you’re following the previous post, you’d now have a Django app running, and you can access the admin portal.

We’ve also created users. In the same way, let’s click the add button next to “Group” and add the following groups.

  • Manager
  • Data Science Team
  • Finance Team

Django add user permissions

Create users in the Django admin console.

We’ve already seen how to add new users from the admin console in our last post. Let’s add some users with the following group memberships.

  • A — Manager
  • B — Member of the Data Science team
  • C — Member of the Finance team
  • D — No team membership.

You can assign them by moving groups from available to chosen sections. You can find this section on the user creation page as soon as you click ‘save.’

Django pick user permissions

Check permissions in the Streamlit app

Lastly, we adjust our streamlit app code to check for group membership.

If a user (A) is a member of the Managers group, they can access all the dashboards. The app requires no further permissions.

If a user (B & C) has only a team membership, they can access their Team’s dashboard. But not other dashboards.

Finally, users (D) who don’t belong to any group can log in. Yet they can’t access any dashboard.

The adjusted streamlit app code will look like this:

# Imports
# -----------------------------------------------------------
import os

import streamlit as st
from django.core.wsgi import get_wsgi_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")

application = get_wsgi_application()

from django.contrib.auth import authenticate


def check_password():
    """Returns `True` if the user had a correct password."""

    def password_entered():
        """Checks whether a password entered by the user is correct."""
        user = authenticate(
            username=st.session_state["username"], password=st.session_state["password"]
        )

        if user is not None:
            st.session_state["password_correct"] = True
            del st.session_state["password"]  # don't store username + password
            del st.session_state["username"]

            # Add the user object to Streamlit session
            st.session_state.user = user
        else:
            st.session_state["password_correct"] = False

    if "password_correct" not in st.session_state:
        # First run, show inputs for username + password.
        st.text_input("Username", on_change=password_entered, key="username")
        st.text_input(
            "Password", type="password", on_change=password_entered, key="password"
        )
        return False
    elif not st.session_state["password_correct"]:
        # Password not correct, show input + error.
        st.text_input("Username", on_change=password_entered, key="username")
        st.text_input(
            "Password", type="password", on_change=password_entered, key="password"
        )
        st.error("😕 User not known or password incorrect")
        return False
    else:
        # Password correct.
        return True


if check_password():

    with st.sidebar:
        report = st.selectbox(
            "Choose a dashboard",
            ("Data Science Team KPI", "Finance Team KPI"),
        )

    st.write(f"Hello, {st.session_state.user.username}!")

    # Data Science Dashboard Section
    if report == "Data Science Team KPI":
        if st.session_state.user.groups.filter(
            name__in=["Manager", "Data Science Team"]
        ).exists():
            st.title("Data Science KPI Dashboard")
        else:
            st.write("Sorry, you don't have permissions to view this dashboard!")

    # Finance Dashboard Section
    elif "Finance Team KPI":
        if st.session_state.user.groups.filter(
            name__in=["Manager", "Finance Team"]
        ).exists():
            st.title("Finance KPI Dashboard")
        else:
            st.write("Sorry, you don't have permissions to view this dashboard!")

    # Section when no dashboard is selected
    else:
        st.write("Please select a Dashboard")

In the above code, the critical ones are,

  • line #30: storing Django user object on session
  • line #65: the condition that checks manager or data science team membership
  • line #74: the condition that matches manager or finance team membership

Let’s run our app and see if permissions are working correctly. To start the server, let’s run:

streamlit run quickstart.py

If we check the app on the browser, this is how it works for all our four users.

Testing Streamlit user permissions powered by Django

Final thoughts

We’ve further extended our Streamlit app to handle permissions in this post.

We’re benefiting from its admin interface by using Django’s inbuilt permission system. We can use this interface to create new users and user groups and manage group memberships.

As we saw in the previous post, Streamlit is still young. It takes time to develop its authentication and authorization system. Until then, we can use Django to build production-grade apps more securely.

Related: How I Create Dazzling Dashboards Purely in Python.

Thanks for reading, friend! Say Hi to me on LinkedIn, Twitter, and Medium.
Not a Medium member yet? Please use this link to become a member because, at no extra cost for you, I earn a small commission for referring you.

How we work

Readers support The Analytics Club. We earn through display ads. Also, when you buy something we recommend, we may get an affiliate commission. But it never affects your price or what we pick.

Connect with us