feat: complete Phase 0 Enterprise IAM

This commit is contained in:
thakares committed 2026-07-21 15:26:16 +05:30
1 parent 3d2d291006
commit c2f5ba3f54
202 files changed
+21371 -1360

No files matched your search

+123
View File
@@ -0,0 +1,123 @@
use crate::db::repository::traits::SessionsRepository;
use async_trait::async_trait;
use sqlx::PgPool;
use crate::db::models::Session;
pub struct PostgresSessionsRepository {
pub pool: PgPool,
}
#[async_trait]
impl SessionsRepository for PostgresSessionsRepository {
async fn create(
&self,
id: &str,
user_id: &str,
token_hash: &str,
ip_address: Option<&str>,
user_agent: Option<&str>,
expires_at: &str,
) -> Result<Session, sqlx::Error> {
sqlx::query_as::<_, Session>(
r#"
INSERT INTO sessions (id, user_id, token_hash, ip_address, user_agent, expires_at)
VALUES ($1, $2, $3, $4, $5, $6)
RETURNING *
"#,
)
.bind(id)
.bind(user_id)
.bind(token_hash)
.bind(ip_address)
.bind(user_agent)
.bind(expires_at)
.fetch_one(&self.pool)
.await
}
async fn find_by_token_hash(&self, token_hash: &str) -> Result<Option<Session>, sqlx::Error> {
sqlx::query_as::<_, Session>("SELECT * FROM sessions WHERE token_hash = $1 AND revoked = 0")
.bind(token_hash)
.fetch_optional(&self.pool)
.await
}
async fn revoke(&self, id: &str) -> Result<(), sqlx::Error> {
sqlx::query("UPDATE sessions SET revoked = 1 WHERE id = $1")
.bind(id)
.execute(&self.pool)
.await?;
Ok(())
}
async fn revoke_all_for_user(&self, user_id: &str) -> Result<(), sqlx::Error> {
sqlx::query("UPDATE sessions SET revoked = 1 WHERE user_id = $1")
.bind(user_id)
.execute(&self.pool)
.await?;
Ok(())
}
async fn update_last_seen(&self, id: &str) -> Result<(), sqlx::Error> {
sqlx::query(
"UPDATE sessions SET last_seen_at = strftime('%Y-%m-%dT%H:%M:%SZ', 'now') WHERE id = $1",
)
.bind(id)
.execute(&self.pool)
.await?;
Ok(())
}
/// List active (non-revoked, non-expired) sessions for a user.
async fn list_active_for_user(&self, user_id: &str) -> Result<Vec<Session>, sqlx::Error> {
sqlx::query_as::<_, Session>(
r#"
SELECT * FROM sessions
WHERE user_id = $1
AND revoked = 0
AND expires_at >= strftime('%Y-%m-%dT%H:%M:%SZ', 'now')
ORDER BY last_seen_at DESC
"#,
)
.bind(user_id)
.fetch_all(&self.pool)
.await
}
async fn list_all_active(&self) -> Result<Vec<Session>, sqlx::Error> {
unimplemented!()
}
/// Count active sessions system-wide.
async fn count_active(&self) -> Result<i64, sqlx::Error> {
let row: (i64,) = sqlx::query_as(
r#"
SELECT COUNT(*) FROM sessions
WHERE revoked = 0
AND expires_at >= strftime('%Y-%m-%dT%H:%M:%SZ', 'now')
"#,
)
.fetch_one(&self.pool)
.await?;
Ok(row.0)
}
/// Delete sessions that are expired or revoked. Called once at startup.
async fn cleanup_expired(&self) -> Result<u64, sqlx::Error> {
let result = sqlx::query(
r#"
DELETE FROM sessions
WHERE revoked = 1
OR expires_at < strftime('%Y-%m-%dT%H:%M:%SZ', 'now')
"#,
)
.execute(&self.pool)
.await?;
Ok(result.rows_affected())
}
async fn revoke_others(&self, _user_id: &str, _except_id: &str) -> Result<u64, sqlx::Error> {
unimplemented!()
}
}