Skip to content

Commit

Permalink
Fix special character
Browse files Browse the repository at this point in the history
  • Loading branch information
arcanaxion committed Jan 23, 2025
1 parent 482b847 commit a687efe
Showing 1 changed file with 23 additions and 24 deletions.
47 changes: 23 additions & 24 deletions docs/tutorials/articles/user_management/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@ hide:
- toc
---


Adding User Management to your Taipy application using Taipy Enterprise is a smooth experience — integration is intuitive, and just makes sense. This is because Taipy was designed from the beginning to be paired with Taipy Enterprise for secure and easy-to-develop User Management.

!!! note "Taipy Enterprise edition"

Taipy provides robust, business-focused applications tailored for enterprise
Expand All @@ -24,9 +21,11 @@ Adding User Management to your Taipy application using Taipy Enterprise is a smo
[Try it live](https://investment-screening.taipy.cloud){: .tp-btn target='blank' }
[Contact us](https://taipy.io/book-a-call){: .tp-btn .tp-btn--accent target='blank' }

Adding User Management to your Taipy application using Taipy Enterprise is a smooth experience — integration is intuitive, and just makes sense. This is because Taipy was from the beginning to be paired with Taipy Enterprise for secure and easy-to-develop User Management.

![Investment Screening application.](images/company_page_alice.png){width=80% : .tp-image-border}

In this tutorial, we will create an application for an investment firm use case. This investment firm is testing out a **new screening model to evaluate companies** that they should invest in. This comprehensive screening model is **expensive to run** because it requires access to paid data sources — accordingly, our applications purpose is to:
In this tutorial, we will create an application for an investment firm use case. This investment firm is testing out a **new screening model to evaluate companies** that they should invest in. This comprehensive screening model is **expensive to run** because it requires access to paid data sources — accordingly, our application's purpose is to:

1. Allow Bob, the **analyst**, to add companies that look promising for investment; and
2. Allow Alice, the **manager**, to submit companies to the screening model.
Expand All @@ -38,14 +37,14 @@ In developing this multi-page application, we will demonstrate these features of
1. **Permissions** for **Scenario and Data Management** using [**predefined roles**](../../../userman/advanced_features/auth/authorization.md#permissions-for-scenario-and-data-management) ("TAIPY_EDITOR", "TAIPY_ADMIN", etc.);
2. **Authorizing** that a user possesses **specified roles** using a [*RoleTraits*](../../../userman/advanced_features/auth/authorization.md#role-traits-value) filter (*AnyOf*, *AllOf* and *NoneOf*);
3. Managing **role-based page access** using [*AuthorizedPage*](https://docs.taipy.io/en/latest/refmans/reference/pkg_taipy/pkg_enterprise/pkg_gui/AuthorizedPage/);
4. **Hiding elements** based on the users roles;
4. **Hiding elements** based on the user's roles;
5. And more.

This tutorial focuses on Taipy Enterprise features, and assumes some understanding of Taipy GUI and Scenario Management concepts. Please consult the useful User Manual and Tutorials if you have trouble understanding the latter!

# 1. Folder structure

First off, lets take a look at the final folder structure that well end up with:
First off, let's take a look at the final folder structure that we'll end up with:

```
src/
Expand All @@ -68,7 +67,7 @@ src/

# 2. Business logic

Well start with the business logic and scenario management.
We'll start with the business logic and scenario management.

First, we have the screening model algorithm:

Expand Down Expand Up @@ -127,9 +126,9 @@ In our first Taipy Enterprise feature, we will define the Taipy authentication a

Note that Taipy Enterprise supports several authentication protocols which you can check out [here](../../../userman/advanced_features/auth/authentication.md). To properly secure your application, you **should use an established supported protoco**l like LDAP or Microsoft Entra ID. However, for our purpose, we will use an **internal password-based protocol** simply called "taipy". This lets us get started quickly, before **migrating to one of the aforementioned protocols** at a later time.

First, Taipys "taipy" protocol expects a "*TAIPY_AUTH_HASH*" environment variable used to [salt the password](https://en.wikipedia.org/wiki/Salt_(cryptography)) before it is hashed.
First, Taipy's "taipy" protocol expects a "*TAIPY_AUTH_HASH*" environment variable used to [salt the password](https://en.wikipedia.org/wiki/Salt_(cryptography)) before it is hashed.

Well do this in an "*.env*" file:
We'll do this in an "*.env*" file:

```toml
# .env
Expand Down Expand Up @@ -177,21 +176,21 @@ We perform the configuration with the `Config.configure_authentication` method.

A dictionary mapping usernames to their hashed password string. Password hashes should be generated using `taipy.auth.hash_taipy_password`.

Notice that we did not provide an entry for Alices password. By default, if omitted, the password hash is automatically generated with the username as the password. In other words, we can sign in as the user "Alice" with the password "Alice".
Notice that we did not provide an entry for Alice's password. By default, if omitted, the password hash is automatically generated with the username as the password. In other words, we can sign in as the user "Alice" with the password "Alice".


# 4. Login page

Were down to our final 4 files, and well start with the login page. This is where things get the most interesting, and its worth spending some time to understand whats going on.
We're down to our final 4 files, and we'll start with the login page. This is where things get the most interesting, and it's worth spending some time to understand what's going on.

A user can be identified through a [`taipy.auth.Credentials`](https://docs.taipy.io/en/latest/refmans/reference/pkg_taipy/pkg_auth/Credentials/) object which holds information about its username and roles. We typically obtain an **authenticated Credentials object** using:

1. [`taipy.auth.login`](https://docs.taipy.io/en/latest/refmans/reference/pkg_taipy/pkg_auth/login/); or
2. [`taipy.enterprise.gui.login`](https://docs.taipy.io/en/latest/refmans/reference/pkg_taipy/pkg_enterprise/pkg_gui/login/).

Although they both return valid Credentials objects (or raise errors), the **latter function** differs in that it also **performs some operations behind-the-scenes** to enable **interoperability with Taipy GUI** (e.g. *AuthorizedPage*, described in a later section). Consequently, well be using the latter function for our application.
Although they both return valid Credentials objects (or raise errors), the **latter function** differs in that it also **performs some operations behind-the-scenes** to enable **interoperability with Taipy GUI** (e.g. *AuthorizedPage*, described in a later section). Consequently, we'll be using the latter function for our application.

Lets look at the code for the login page:
Let's look at the code for the login page:

```python
# pages/login.py
Expand Down Expand Up @@ -226,7 +225,7 @@ with tgb.Page() as login_page:

```

We dont need much code for this page. The actual Page object (`login_page`) contains a single element: a [login control](../../../refmans/gui/viselements/generic/login.md). Interacting with the login control implicitly calls the "on_login" callback function (unless a callback function is explicitly assigned via `tgb.login`s "on_action" parameter).
We don't need much code for this page. The actual Page object (`login_page`) contains a single element: a [login control](../../../refmans/gui/viselements/generic/login.md). Interacting with the login control implicitly calls the "on_login" callback function (unless a callback function is explicitly assigned via `tgb.login`'s "on_action" parameter).

In turn, our "on_login" callback function calls "handle_login", where we implement the main logic for attempting a login. We choose to do this in 3 steps:

Expand All @@ -253,7 +252,7 @@ Similarly, we define a "handle_logout" function:

2. `state.credentials = get_guest_credentials()`.

Here, were assigning a "blank" Credentials object to `state.credentials`, which has no user roles. This is for our convenience so that we can always use this `state.credentials` object to get a users roles — returning an empty list if the user is not authenticated.
Here, we're assigning a "blank" Credentials object to `state.credentials`, which has no user roles. This is for our convenience so that we can always use this `state.credentials` object to get a user's roles — returning an empty list if the user is not authenticated.

It is an important distinction that `state.credentials` is a state variable of our own making — **it is not some reserved keyword** internally used by Taipy Enterprise. Notably and contrastingly, at this point, calling [`taipy.enterprise.gui.get_credentials`](https://docs.taipy.io/en/latest/refmans/reference/pkg_taipy/pkg_enterprise/pkg_gui/get_credentials/) would return None, since the previous logout statement has removed session credentials.

Expand All @@ -262,7 +261,7 @@ Similarly, we define a "handle_logout" function:

# 5. Admin page

Taking a quick break from the fancier stuff, lets make a simple page:
Taking a quick break from the fancier stuff, let's make a simple page:

```python
# pages/admin.py
Expand Down Expand Up @@ -327,7 +326,7 @@ with tgb.Page() as company_page:

```

If youre already familiar with Taipy GUI and Scenario Management, you will notice that we used some [scenario management controls](../../../refmans/gui/viselements/index.md#scenario-and-data-management-controls), namely: *scenario_selector*, *scenario*, *data_node_selector* and *data_node*.
If you're already familiar with Taipy GUI and Scenario Management, you will notice that we used some [scenario management controls](../../../refmans/gui/viselements/index.md#scenario-and-data-management-controls), namely: *scenario_selector*, *scenario*, *data_node_selector* and *data_node*.

In fact, the only new thing present is the *AnyOf* [role trait](../../../userman/advanced_features/auth/authorization.md#role-traits) filter. A role trait filter takes 3 parameters:

Expand All @@ -337,14 +336,14 @@ In fact, the only new thing present is the *AnyOf* [role trait](../../../userman

Additionally, *success* and *failure* could also be role trait filters, which would be **recursively evaluated** until some value is returned.

Rather self-explanatorily, the *AnyOf* role traits filter checks if a user has any of the listed roles. In our example, we created one like so: `is_admin = AnyOf([TAIPY_ADMIN_ROLE], True, False)`. Then, in our page code, we used the expression `not is_admin.get_traits(credentials)` for a parts render property. Hence, the full expression (with the negation from `not`) evaluates as `True` if the user does not have the "TAIPY_ADMIN" role.
Rather self-explanatorily, the *AnyOf* role traits filter checks if a user has any of the listed roles. In our example, we created one like so: `is_admin = AnyOf([TAIPY_ADMIN_ROLE], True, False)`. Then, in our page code, we used the expression `not is_admin.get_traits(credentials)` for a part's render property. Hence, the full expression (with the negation from `not`) evaluates as `True` if the user does not have the "TAIPY_ADMIN" role.

![Bob is unable to click the button as he has insufficient permissions to submit the scenario.](images/company_page_bob.png){width=80% : .tp-image-border}
/// caption
Company page: Bob is unable to click the button as he has insufficient permissions to submit the scenario.
///

Note that using scenario management controls abstracts away some auth functionality for our convenience. For example, the *scenario* controls submit button is **automatically disabled** when the **user does not have permission** to execute a scenario. Often, you may wish to use generic controls like selectors and buttons, which trigger user-defined callbacks that call scenario management functionality. In this case, an added step is to use the [Authorize](https://docs.taipy.io/en/latest/refmans/reference/pkg_taipy/pkg_auth/Authorize/) context manager when performing a protected operation, for example:
Note that using scenario management controls abstracts away some auth functionality for our convenience. For example, the *scenario* control's submit button is **automatically disabled** when the **user does not have permission** to execute a scenario. Often, you may wish to use generic controls like selectors and buttons, which trigger user-defined callbacks that call scenario management functionality. In this case, an added step is to use the [Authorize](https://docs.taipy.io/en/latest/refmans/reference/pkg_taipy/pkg_auth/Authorize/) context manager when performing a protected operation, for example:

```python
import taipy as tp
Expand Down Expand Up @@ -446,11 +445,11 @@ if __name__ == "__main__":

```

Since this file is a little bit longer than the others, well break it down in sections:
Since this file is a little bit longer than the others, we'll break it down in sections:

## 7.1 Initialize credentials

Starting from the top, after the imports, we initialize `credentials = get_guest_credentials()`. By defining this in our main module, we now have a global `state.credentials` state variable in our application. As mentioned earlier, were giving non-logged in users this "blank" guest credentials, so that `state.credentials` is always of type *Credentials* — and guest users can be identified by their empty role list.
Starting from the top, after the imports, we initialize `credentials = get_guest_credentials()`. By defining this in our main module, we now have a global `state.credentials` state variable in our application. As mentioned earlier, we're giving non-logged in users this "blank" guest credentials, so that `state.credentials` is always of type *Credentials* — and guest users can be identified by their empty role list.

## 7.2 "on_exception" global callback

Expand All @@ -462,9 +461,9 @@ Remember how in "login.handle_login" we opted not to handle the exception that `

We usually create a multi-page application in Taipy by defining a dictionary mapping page names to a Taipy *Page* (like `company_page`). This dictionary is then passed to the `taipy.gui.Gui` "pages" parameter.

This time however, we may want to show different pages depending on the users roles. For example, we only want Alice, the manager (who has the "TAIPY_ADMIN" role), to be able to view `admin_page`. Any user without this role should not be able to view the page.
This time however, we may want to show different pages depending on the user's roles. For example, we only want Alice, the manager (who has the "TAIPY_ADMIN" role), to be able to view `admin_page`. Any user without this role should not be able to view the page.

To do this, we started by creating `guest_page`, a short page simply informing the user that they arent signed in. Now, we want to make it so that navigating to "/admin" may conditionally show `admin_page` or `guest_page`, depending on the users roles. We can do this with *AuthorizedPage*.
To do this, we started by creating `guest_page`, a short page simply informing the user that they aren't signed in. Now, we want to make it so that navigating to "/admin" may conditionally show `admin_page` or `guest_page`, depending on the user's roles. We can do this with *AuthorizedPage*.

See the following snippet:

Expand Down Expand Up @@ -505,7 +504,7 @@ Bob, the **analyst**, can now create a scenario, and add a company name that he

![Bob edits the company name in the scenario.](images/bob_edits_company_name.png){width=80% : .tp-image-border}

Alice, the **manager**, will see the scenario that Bob created, and can decide if its worth submitting to the screening model. She **has permission** to submit the scenario for execution:
Alice, the **manager**, will see the scenario that Bob created, and can decide if it's worth submitting to the screening model. She **has permission** to submit the scenario for execution:

![Alice submits the scenario.](images/alice_submits_scenario.png){width=80% : .tp-image-border}

Expand Down

0 comments on commit a687efe

Please sign in to comment.