feat: complete NX9-Auth management and integrity hardening
This commit is contained in:
1 parent
dc5417334b
commit
a969f9c571
59 files changed
+6508
-290
No files matched your search
@@ -1,7 +1,11 @@
|
||||
use sqlx::PgPool;
|
||||
|
||||
use crate::db::models::Tenant;
|
||||
use crate::db::repository::postgres::global_slugs::{
|
||||
release_slug_by_name_postgres, release_slug_postgres, reserve_slug_postgres,
|
||||
};
|
||||
use crate::db::repository::traits::TenantsRepository;
|
||||
use crate::identity::slug::{slugify, validate_slug};
|
||||
|
||||
pub struct PostgresTenantsRepository {
|
||||
pub pool: PgPool,
|
||||
@@ -47,7 +51,17 @@ impl TenantsRepository for PostgresTenantsRepository {
|
||||
name: &str,
|
||||
slug: Option<&str>,
|
||||
) -> Result<Tenant, sqlx::Error> {
|
||||
let slug = slug.unwrap_or(id);
|
||||
let final_slug = match slug {
|
||||
Some(s) if !s.trim().is_empty() => {
|
||||
let trimmed = s.trim();
|
||||
validate_slug(trimmed).map_err(|e| sqlx::Error::Protocol(e.to_string()))?;
|
||||
trimmed.to_string()
|
||||
}
|
||||
_ => slugify(name).map_err(|e| sqlx::Error::Protocol(e.to_string()))?,
|
||||
};
|
||||
|
||||
let mut tx = self.pool.begin().await?;
|
||||
|
||||
let row = sqlx::query_as::<_, Tenant>(
|
||||
r#"
|
||||
INSERT INTO tenants (id, name, slug, enabled)
|
||||
@@ -57,27 +71,68 @@ impl TenantsRepository for PostgresTenantsRepository {
|
||||
)
|
||||
.bind(id)
|
||||
.bind(name)
|
||||
.bind(slug)
|
||||
.fetch_one(&self.pool)
|
||||
.bind(&final_slug)
|
||||
.fetch_one(&mut *tx)
|
||||
.await?;
|
||||
|
||||
reserve_slug_postgres(&mut tx, &final_slug, "tenant", id, id).await?;
|
||||
|
||||
tx.commit().await?;
|
||||
Ok(row)
|
||||
}
|
||||
|
||||
async fn update(&self, id: &str, name: &str, slug: Option<&str>) -> Result<(), sqlx::Error> {
|
||||
let slug = slug.unwrap_or(name);
|
||||
sqlx::query(
|
||||
r#"
|
||||
UPDATE tenants
|
||||
SET name = $1, slug = $2, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $3
|
||||
"#,
|
||||
)
|
||||
.bind(name)
|
||||
.bind(slug)
|
||||
.bind(id)
|
||||
.execute(&self.pool)
|
||||
.await?;
|
||||
let existing = self.find_by_id(id).await?.ok_or(sqlx::Error::RowNotFound)?;
|
||||
|
||||
let target_slug = match slug {
|
||||
Some(s) if !s.trim().is_empty() => {
|
||||
let trimmed = s.trim();
|
||||
validate_slug(trimmed).map_err(|e| sqlx::Error::Protocol(e.to_string()))?;
|
||||
trimmed.to_string()
|
||||
}
|
||||
_ => existing.slug.clone().unwrap_or_else(|| id.to_string()),
|
||||
};
|
||||
|
||||
let existing_slug_str = existing.slug.as_deref().unwrap_or("");
|
||||
|
||||
if target_slug == existing_slug_str {
|
||||
// Unchanged slug: registry no-op
|
||||
sqlx::query(
|
||||
r#"
|
||||
UPDATE tenants
|
||||
SET name = $1, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $2
|
||||
"#,
|
||||
)
|
||||
.bind(name)
|
||||
.bind(id)
|
||||
.execute(&self.pool)
|
||||
.await?;
|
||||
} else {
|
||||
// Changed slug: single transaction reserve -> update -> release
|
||||
let mut tx = self.pool.begin().await?;
|
||||
|
||||
reserve_slug_postgres(&mut tx, &target_slug, "tenant", id, id).await?;
|
||||
|
||||
sqlx::query(
|
||||
r#"
|
||||
UPDATE tenants
|
||||
SET name = $1, slug = $2, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $3
|
||||
"#,
|
||||
)
|
||||
.bind(name)
|
||||
.bind(&target_slug)
|
||||
.bind(id)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
if !existing_slug_str.is_empty() {
|
||||
release_slug_by_name_postgres(&mut tx, existing_slug_str, "tenant", id).await?;
|
||||
}
|
||||
|
||||
tx.commit().await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -99,10 +154,16 @@ impl TenantsRepository for PostgresTenantsRepository {
|
||||
}
|
||||
|
||||
async fn delete(&self, id: &str) -> Result<(), sqlx::Error> {
|
||||
let mut tx = self.pool.begin().await?;
|
||||
|
||||
release_slug_postgres(&mut tx, "tenant", id).await?;
|
||||
|
||||
sqlx::query("DELETE FROM tenants WHERE id = $1")
|
||||
.bind(id)
|
||||
.execute(&self.pool)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
|
||||
tx.commit().await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user