atoti_plus.security module#

class atoti_plus.security.BasicSecurity#

Manage basic authentication on the session.

Note

This requires a BasicAuthenticationConfig to be passed to Session()’s authentication parameter.

create_user(username, *, password=None, roles=())#

Add a user able to authenticate against the session using Basic Authentication.

Users without the role ROLE_USER will not have access to the application.

Parameters
  • username (str) – The name of the user.

  • password (Optional[str]) – The password of the user, can be changed at any time.

  • roles (Iterable[str]) – Roles of the user, can be changed at any time.

Example

>>> session = tt.Session(authentication=tt.BasicAuthenticationConfig())
>>> list(session.security.basic.users)
[]
>>> elon = session.security.basic.create_user("elon", password="X Æ A-12")
>>> list(session.security.basic.users)
['elon']
>>> # The special ROLE_USER role is automatically added
>>> list(elon.roles)
['ROLE_USER']
>>> # Change the password
>>> elon.password = "AE A-XII"
>>> # Revoke access
>>> del session.security.basic.users["elon"]
>>> list(session.security.basic.users)
[]
Return type

User

users: atoti_plus.security.BasicUsers#
class atoti_plus.security.BasicUsers#

Manage basic authentication users.

authentication_type()#

Get the type of user this contains.

Return type

str

class atoti_plus.security.DefaultRoles#

Roles granted to users who had no roles granted by IndividualRoles and RoleMapping.

class atoti_plus.security.IndividualRoles#

The roles given to users on top of the ones that can be added by authentication providers.

Users without the ROLE_USER will not be able to access the session.

Example

>>> session = tt.Session(authentication=tt.BasicAuthenticationConfig())
>>> elon = session.security.basic.create_user("John", password="X Æ A-12")
>>> elon.roles
{'ROLE_USER'}
>>> session.security.individual_roles[elon.username].add("ROLE_USA")
>>> sorted(elon.roles)
['ROLE_USA', 'ROLE_USER']
>>> session.security.individual_roles[elon.username].remove("ROLE_USA")
>>> elon.roles
{'ROLE_USER'}
>>> # Removing all the roles will prevent the user from accessing the session:
>>> del session.security.individual_roles[elon.username]
>>> list(elon.roles)
[]
update(__m: SupportsKeysAndGetItem[str, _UserRolesDescription], **kwargs: Union[Collection[str], atoti_plus.security._UserRoles]) None#
update(__m: Iterable[Tuple[str, Union[Collection[str], atoti_plus.security._UserRoles]]], **kwargs: Union[Collection[str], atoti_plus.security._UserRoles]) None
update(**kwargs: Union[Collection[str], atoti_plus.security._UserRoles]) None

Update the mapping.

Return type

None

class atoti_plus.security.KerberosSecurity#

Manage Kerberos authentication on the session.

Note

This requires KerberosConfig to be configured.

create_user(username, *, roles=())#

Add a user able to authenticate against the session using Kerberos.

Users without the role ROLE_USER will not have access to the application.

Parameters
  • username (str) – The name of the user.

  • roles (Iterable[str]) – Roles of the user, can be changed at any time.

See also

create_user() for a similar usage example.

Return type

User

default_roles: atoti_plus.security.DefaultRoles#
users: atoti_plus.security.KerberosUsers#
class atoti_plus.security.KerberosUsers#

Manage Kerberos users.

authentication_type()#

Get the type of user this contains.

Return type

str

class atoti_plus.security.LdapSecurity#

Allows mapping roles granted by the authentication provider to the roles to use in the session.

Users who do not have the ROLE_USER session role will not be able to access the session.

Note

This requires LdapConfig to be configured.

Example
>>> session = tt.Session(
...     authentication=tt.LdapConfig(
...         url="ldap://example.com:389",
...         base_dn="dc=example,dc=com",
...         user_search_base="ou=people",
...         group_search_base="ou=roles",
...     )
... )
>>> mathematicians_role = session.security.create_role(
...     "ROLE_MATHS", restrictions={("Restrictions example", "City"): ["Paris"]}
... )

Roles from the authentication provider can be mapped to roles in the session.

>>> session.security.ldap.role_mapping["MATHEMATICIANS"] = [
...     "ROLE_MATHS",
...     "ROLE_USER",
... ]
>>> sorted(session.security.ldap.role_mapping["MATHEMATICIANS"])
['ROLE_MATHS', 'ROLE_USER']

Default roles can be given to users who had no individual or mapped roles granted.

>>> session.security.ldap.default_roles.add("ROLE_USER")
>>> session.security.ldap.default_roles
{'ROLE_USER'}
default_roles: atoti_plus.security.DefaultRoles#
role_mapping: atoti_plus.security.RoleMapping#
class atoti_plus.security.OidcSecurity#

Allows mapping roles granted by the authentication provider’s ID Token to the roles to use in the session.

Users who do not have the ROLE_USER session role will not be able to access the session.

Note

This requires OidcConfig to be configured.

Example

>>> import os
>>> session = tt.Session(
...     authentication=tt.OidcConfig(
...         provider_id="auth0",
...         issuer_url=os.environ["AUTH0_ISSUER"],
...         client_id=os.environ["AUTH0_CLIENT_ID"],
...         client_secret=os.environ["AUTH0_CLIENT_SECRET"],
...         name_claim="email",
...         scopes=["email", "profile", "username"],
...         roles_claims=["https://activeviam:eu:auth0:com/roles"],
...     ),
...     port=1234,
... )
>>> france_role = session.security.create_role(
...     "ROLE_FRANCE",
...     restrictions={("Restrictions example", "Country"): "France"},
... )
>>> uk_role = session.security.create_role(
...     "ROLE_UK", restrictions={("Restrictions example", "Country"): "UK"}
... )

Roles from the authentication provider’s ID Token can be mapped to roles in the session:

>>> session.security.oidc.role_mapping.update(
...     {"atoti user": ["ROLE_USER"], "France": [france_role.name]}
... )
>>> session.security.oidc.role_mapping
{'atoti user': {'ROLE_USER'}, 'France': {'ROLE_FRANCE'}}

Default roles can be given to users who had no individual or mapped roles granted.

>>> session.security.oidc.default_roles.add(uk_role.name)
>>> session.security.oidc.default_roles
{'ROLE_UK'}
default_roles: atoti_plus.security.DefaultRoles#
role_mapping: atoti_plus.security.RoleMapping#
class atoti_plus.security.Restrictions#

The restrictions associated with a role can be modified at any time.

  • Restrictions apply on table columns and are inherited by all hierarchies based on these columns.

  • Restrictions on different hierarchies are intersected.

  • However, if a user has several roles with restrictions on the same hierarchies, access to the union of restricted members will be granted.

    Example:

    >>> from typing import List
    >>> df = pd.DataFrame(
    ...     [
    ...         ("Asia", "Korea", "KRW"),
    ...         ("Asia", "Japan", "JPY"),
    ...         ("Europe", "France", "EUR"),
    ...         ("Europe", "Germany", "EUR"),
    ...         ("Europe", "Norway", "NOK"),
    ...         ("Europe", "Sweden", "SEK"),
    ...     ],
    ...     columns=["Continent", "Country", "Currency"],
    ... )
    >>> session = tt.Session(authentication=tt.BasicAuthenticationConfig())
    >>> table = session.read_pandas(
    ...     df,
    ...     keys=["Continent", "Country", "Currency"],
    ...     table_name="Restrictions example",
    ... )
    >>> cube = session.create_cube(table)
    >>> h, l, m = cube.hierarchies, cube.levels, cube.measures
    >>> cube.hierarchies["Geography"] = [
    ...     table["Continent"],
    ...     table["Country"],
    ... ]
    >>> for name in cube.hierarchies["Geography"].levels:
    ...     del cube.hierarchies[name]
    ...
    >>> password = "abcdef123456"
    >>> user = session.security.basic.create_user("john", password=password)
    

    Opening a query session to authenticate as the user just created:

    >>> query_session = tt.QuerySession(
    ...     f"http://localhost:{session.port}",
    ...     auth=tt.BasicAuthentication(
    ...         username=user.username, password=password
    ...     ),
    ... )
    >>> query_cube = query_session.cubes[cube.name]
    >>> l, m = query_cube.levels, query_cube.measures
    

    The user initially only has the ROLE_USER:

    >>> user.roles
    {'ROLE_USER'}
    

    This role has no restrictions so all the countries and currencies are accessible:

    >>> query_cube.query(
    ...     m["contributors.COUNT"], levels=[l["Country"], l["Currency"]]
    ... )
                               contributors.COUNT
    Continent Country Currency
    Asia      Japan   JPY                       1
              Korea   KRW                       1
    Europe    France  EUR                       1
              Germany EUR                       1
              Norway  NOK                       1
              Sweden  SEK                       1
    

    Adding a restricting role to the user so that only France is accessible:

    >>> role_france = session.security.create_role(
    ...     "ROLE_FRANCE",
    ...     restrictions={("Restrictions example", "Country"): ["France"]},
    ... )
    >>> session.security.individual_roles[user.username].add(
    ...     role_france.name
    ... )
    >>> query_cube.query(m["contributors.COUNT"], levels=[l["Country"]])
                      contributors.COUNT
    Continent Country
    Europe    France                   1
    

    Restrictions on the same hierarchy grant access to the union of the restricted members:

    >>> role_germany = session.security.create_role(
    ...     "ROLE_GERMANY",
    ...     restrictions={("Restrictions example", "Country"): ["Germany"]},
    ... )
    >>> session.security.individual_roles[user.username].add(
    ...     role_germany.name
    ... )
    >>> query_cube.query(m["contributors.COUNT"], levels=[l["Country"]])
                      contributors.COUNT
    Continent Country
    Europe    France                   1
              Germany                  1
    

    Restrictions can grant access to multiple members:

    >>> role_nordic = session.security.create_role(
    ...     "ROLE_NORDIC",
    ...     restrictions={
    ...         ("Restrictions example", "Country"): ["Norway", "Sweden"]
    ...     },
    ... )
    >>> session.security.individual_roles[user.username].add(
    ...     role_nordic.name
    ... )
    >>> query_cube.query(m["contributors.COUNT"], levels=[l["Country"]])
                      contributors.COUNT
    Continent Country
    Europe    France                   1
              Germany                  1
              Norway                   1
              Sweden                   1
    

    Also give access to the Asian countries with a restriction on Continent:

    >>> role_asia = session.security.create_role(
    ...     "ROLE_ASIA",
    ...     restrictions={("Restrictions example", "Continent"): ["Asia"]},
    ... )
    >>> session.security.individual_roles[user.username].add(role_asia.name)
    >>> query_cube.query(m["contributors.COUNT"], levels=[l["Country"]])
                      contributors.COUNT
    Continent Country
    Asia      Japan                    1
              Korea                    1
    Europe    France                   1
              Germany                  1
              Norway                   1
              Sweden                   1
    

    Restrictions on different hierarchies are intersected:

    >>> role_eur = session.security.create_role(
    ...     "ROLE_EUR",
    ...     restrictions={("Restrictions example", "Currency"): ["EUR"]},
    ... )
    >>> session.security.individual_roles[user.username].add(role_eur.name)
    >>> query_cube.query(
    ...     m["contributors.COUNT"], levels=[l["Country"], l["Currency"]]
    ... )
                               contributors.COUNT
    Continent Country Currency
    Europe    France  EUR                       1
              Germany EUR                       1
    

    Removing the roles granting access to France and Germany leaves no remaining accessible countries:

    >>> for role in [role_france, role_germany]:
    ...     session.security.individual_roles[user.username].remove(
    ...         role.name
    ...     )
    ...
    >>> query_cube.query(m["contributors.COUNT"], levels=[l["Country"]])
    Empty DataFrame
    Columns: [contributors.COUNT]
    Index: []
    
class atoti_plus.security.Role#

A role and its restrictions.

name: str#
restrictions: atoti_plus.security.Restrictions#
class atoti_plus.security.RoleMapping#

Mapping between roles or usernames coming from the authentication provider to those to use in the session.

update(__m: SupportsKeysAndGetItem[str, _RoleMappingDescription], **kwargs: Union[Iterable[str], atoti_plus.security._MappedRoles]) None#
update(__m: Iterable[Tuple[str, Union[Iterable[str], atoti_plus.security._MappedRoles]]], **kwargs: Union[Iterable[str], atoti_plus.security._MappedRoles]) None
update(**kwargs: Union[Iterable[str], atoti_plus.security._MappedRoles]) None

Update the mapping.

Return type

None

class atoti_plus.security.Roles#

All of the roles defined in the session.

class atoti_plus.security.Security#

Security management for the current session.

basic: atoti_plus.security.BasicSecurity#
create_role(name, *, restrictions={})#

Create a role with the given restrictions.

There are special roles which cannot be redefined:

  • ROLE_USER: required to access the application

  • ROLE_ADMIN: gives full access (read, write, delete, etc) to the application

Return type

Role

individual_roles: atoti_plus.security.IndividualRoles#
kerberos: atoti_plus.security.KerberosSecurity#
ldap: atoti_plus.security.LdapSecurity#
oidc: atoti_plus.security.OidcSecurity#
roles: atoti_plus.security.Roles#
class atoti_plus.security.User#

A user with some roles.

password: Optional[str]#
property roles: Collection[str]#
Return type

Collection[str]

username: str#
class atoti_plus.security.Users#

Manage the users of some authentication provider.

abstract authentication_type()#

Get the type of user this contains.

Return type

str