Securing a session#
Note
This feature is not part of the community edition: it needs to be unlocked.
By default, no authentication is required to query an Atoti session and the users have access to all the data in the session.
This shows how to configure:
An authentication mechanism to secure access to the session.
Restrictions to control the data each user is allowed to see.
Configuring authentication#
Atoti supports multiple authentication mechanisms.
Here we’ll use OpenID Connect:
>>> import os
>>> import atoti as tt
>>> session_config = tt.SessionConfig(
... security=tt.SecurityConfig(
... sso=tt.OidcConfig(
... access_token_format="opaque", # noqa: S106
... client_id=os.environ["OIDC_CLIENT_ID"],
... client_secret=os.environ["OIDC_CLIENT_SECRET"],
... issuer_url=os.environ["OIDC_ISSUER_URL"],
... name_claim="preferred_username",
... provider_id="unused",
... roles_claims={
... ("resource_access", os.environ["OIDC_CLIENT_ID"], "roles")
... },
... scopes={"openid", "profile", "roles"},
... ),
... ),
... )
>>> session = tt.Session.start(session_config)
>>> sales_table = session.read_csv(
... resources_directory / "sales.csv", keys={"Sale ID"}, table_name="Sales"
... )
>>> shops_table = session.read_csv(
... resources_directory / "shops.csv", keys={"Shop ID"}, table_name="Shops"
... )
>>> sales_table.join(shops_table, sales_table["Shop"] == shops_table["Shop ID"])
>>> session.tables.schema
>>> cube = session.create_cube(sales_table)
The users configured in the OIDC provider are:
global-user with the role user.
french-user with the roles france and atoti.
parisian-user with the roles paris and atoti.
Querying the session#
When navigating to the URL of the session, users are redirected to the login page of the configured authentication provider. Let’s connect using our different users and make sure they can only see the expected data subset.
>>> def query(session_url: str, /, *, impersonated_username: str):
... authentication = tt.OAuth2ResourceOwnerPasswordAuthentication(
... client_id=os.environ["OIDC_CLIENT_ID"],
... client_secret=os.environ["OIDC_CLIENT_SECRET"],
... issuer_url=os.environ["OIDC_ISSUER_URL"],
... # To keep things simple in this guide, all the users share the same password.
... password=os.environ["OIDC_USER_PASSWORD"],
... scopes={"openid"},
... username=impersonated_username,
... )
...
... with tt.Session.connect(session_url, authentication=authentication) as session:
... cube = next(iter(session.cubes.values()))
... return cube.query(
... cube.measures["Quantity.SUM"],
... levels=[cube.levels["City"]],
... include_totals=True,
... )
First, we can check that parisian-user can only see data for shops in Paris:
>>> query(session.url, impersonated_username="parisian-user")
Quantity.SUM
City
Total 603.00
Paris 603.00
french-user can only see data for shops in France:
>>> query(session.url, impersonated_username="french-user")
Quantity.SUM
City
Total 3,027.00
Lyon 609.00
Marseille 603.00
Nice 609.00
Paris 603.00
Saint-Étienne 603.00
And finally, global-user can see data for all the shops:
>>> query(session.url, impersonated_username="global-user")
Quantity.SUM
City
Total 8,077.00
Chicago 603.00
Houston 606.00
Los Angeles 606.00
Lyon 609.00
Marseille 603.00
New York 808.00
Nice 609.00
Paris 603.00
Saint-Étienne 603.00
San Antonio 606.00
San Diego 606.00
San Francisco 612.00
San Jose 603.00