Repository: DefGuard/defguard (language: rust)

Issue:
Change enrollment token to be time-based
Currently our token is time based & one time. It should be changed to time-based - if we have 10min window, it should be accessible during this time (for example some people reopened it so it crashes with strange error).


Discussion / hints:
@j-chmielewski to verify if activated should be able to return within the "window" of 10min to enrollmenty any time.

Fix patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 8fbcfe86b..7fb7214af 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -38,8 +38,6 @@ jobs:
           --health-retries 5
 
     steps:
-      - name: Debug
-        run: echo ${{ github.ref_name }}
       - name: Checkout
         uses: actions/checkout@v3
         with:
diff --git a/src/db/models/enrollment.rs b/src/db/models/enrollment.rs
index 2625a9ff3..5f5704fff 100644
--- a/src/db/models/enrollment.rs
+++ b/src/db/models/enrollment.rs
@@ -160,17 +160,24 @@ impl Token {
         if self.is_expired() {
             return Err(TokenError::TokenExpired);
         }
-        if self.is_used() {
-            return Err(TokenError::TokenUsed);
-        }
-
-        let now = Utc::now().naive_utc();
-        query!("UPDATE token SET used_at = $1 WHERE id = $2", now, self.id)
-            .execute(transaction)
-            .await?;
-        self.used_at = Some(now);
+        match self.used_at {
+            // session started but still valid
+            Some(used_at) if self.is_session_valid(session_timeout_seconds) => {
+                Ok(used_at + Duration::seconds(session_timeout_seconds as i64))
+            }
+            // session expired
+            Some(_) => Err(TokenError::TokenUsed),
+            // session not yet started
+            None => {
+                let now = Utc::now().naive_utc();
+                query!("UPDATE token SET used_at = $1 WHERE id = $2", now, self.id)
+                    .execute(transaction)
+                    .await?;
+                self.used_at = Some(now);
 
-        Ok(now + Duration::seconds(session_timeout_seconds as i64))
+                Ok(now + Duration::seconds(session_timeout_seconds as i64))
+            }
+        }
     }
 
     pub async fn find_by_id(pool: &DbPool, id: &str) -> Result<Self, TokenError> {
@@ -222,6 +229,7 @@ impl Token {
 
         let admin_id = self.admin_id.unwrap();
         let user = User::find_by_id(executor, admin_id).await?;
+        info!("Fetched admin id {} for enrollment", admin_id);
         Ok(user)
     }
 
@@ -238,7 +246,7 @@ impl Token {
         )
         .execute(transaction)
         .await?;
-        debug!(
+        info!(
             "Deleted {} unused enrollment tokens for user {user_id}",
             result.rows_affected()
         );


Test patch: