Repository: lldap/lldap (language: rust)

Issue:
[BUG] Repetitive Validation Loop for 'admin' User in lldap/lldap:2024-02-06-debian-rootless Container Logs
**Describe the bug**
A loop appears to be occurring in the container logs when validating accounts using the lldap/lldap:2024-02-06-debian-rootless image. The logs indicate a repetitive pattern of actions related to user validation, specifically for the "admin" user. This includes token validation, user detail retrieval, and user group fetching, which seems to execute in a loop without termination. This behaviour suggests there might be an issue with either the application logic or state management causing unnecessary repeated validations and queries.

**To Reproduce**
Steps to reproduce the behaviour are not directly observable through user interactions since the issue manifests at the application's backend/log output level. However, based on the logs, the sequence of actions leading to the loop can be inferred as follows:

1.     Pull and start the lldap/lldap:2024-02-06-debian-rootless container.
2.     Observe the container logs (enable DEBUG logging if not already enabled).
3.     Notice the repetitive logging of user validation, details retrieval, and group fetching for the "admin" user occurring in a short timeframe without any external triggers.
4.     The loop continues indefinitely, as seen in the provided log timestamps.

**Expected behavior**

The expected behaviour is for the application to validate the user's token, retrieve user details, and fetch user groups once per request or action without entering into a repetitive loop. After a successful validation and retrieval process, the application should proceed with normal operations without repeatedly performing the same actions for the same user unless triggered by a new request or action.

**Logs**

```
lldap_1  | 2024-02-08T15:54:09.511756116+00:00  DEBUG    │     ┕━ 🐛 [debug]:  | return: Ok(User { user_id: UserId(CaseInsensitiveString("admin")), email: Email(""), display_name: Some("Administrator"), creation_date: 2024-02-08T15:28:59.595063417, uuid: Uuid("9ab43c18-a2e8-3f9b-8d91-53b670e8816e"), attributes: [] })
...
(repeats with user group fetching and token validation)
...
lldap_1  | 2024-02-08T15:54:09.775744064+00:00  DEBUG    │     ┕━ 🐛 [debug]:  | return: Ok(User { user_id: UserId(CaseInsensitiveString("admin")), email: Email(""), display_name: Some("Administrator"), creation_date: 2024-02-08T15:28:59.595063417, uuid: Uuid("9ab43c18-a2e8-3f9b-8d91-53b670e8816e"), attributes: [] })
```

The provided logs show the repetitive loop involving user validation, details retrieval, and group fetching actions for the "admin" user, all occurring within a very short timeframe and without any user interaction suggesting a backend process issue with the lldap/lldap:2024-02-06-debian-rootless image.



Discussion / hints:
I've observed that the loop initiates specifically when the admin user logs into the webUI. From that point onwards, the loop runs continuously, executing repetitive actions related to user validation, details retrieval, and group fetching for the "admin" user. This loop persists until the admin user logs out of the webUI. This behavior suggests that the issue is directly tied to the admin user session lifecycle within the webUI, potentially indicating a session handling or state management flaw that triggers the loop upon login and sustains it until logout.

Fix patch:
diff --git a/app/src/infra/functional.rs b/app/src/infra/functional.rs
index 2e2cbb5b..8d3fac5d 100644
--- a/app/src/infra/functional.rs
+++ b/app/src/infra/functional.rs
@@ -2,7 +2,7 @@ use crate::infra::api::HostService;
 use anyhow::Result;
 use graphql_client::GraphQLQuery;
 use wasm_bindgen_futures::spawn_local;
-use yew::{use_effect, use_state, UseStateHandle};
+use yew::{use_effect, use_state_eq, UseStateHandle};
 
 // Enum to represent a result that is fetched asynchronously.
 #[derive(Debug)]
@@ -13,14 +13,28 @@ pub enum LoadableResult<T> {
     Loaded(Result<T>),
 }
 
+impl<T: PartialEq> PartialEq for LoadableResult<T> {
+    fn eq(&self, other: &Self) -> bool {
+        match (self, other) {
+            (LoadableResult::Loading, LoadableResult::Loading) => true,
+            (LoadableResult::Loaded(Ok(d1)), LoadableResult::Loaded(Ok(d2))) => d1.eq(d2),
+            (LoadableResult::Loaded(Err(e1)), LoadableResult::Loaded(Err(e2))) => {
+                e1.to_string().eq(&e2.to_string())
+            }
+            _ => false,
+        }
+    }
+}
+
 pub fn use_graphql_call<QueryType>(
     variables: QueryType::Variables,
 ) -> UseStateHandle<LoadableResult<QueryType::ResponseData>>
 where
     QueryType: GraphQLQuery + 'static,
+    <QueryType as graphql_client::GraphQLQuery>::ResponseData: std::cmp::PartialEq,
 {
     let loadable_result: UseStateHandle<LoadableResult<QueryType::ResponseData>> =
-        use_state(|| LoadableResult::Loading);
+        use_state_eq(|| LoadableResult::Loading);
     {
         let loadable_result = loadable_result.clone();
         use_effect(move || {


Test patch: