caps and delete
This commit is contained in:
+135
-72
@@ -16,7 +16,10 @@ use diesel::PgConnection;
|
||||
use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
|
||||
use http_body_util::BodyExt;
|
||||
use once_cell::sync::Lazy;
|
||||
use papercrate::auth::jwt::JwtService;
|
||||
use papercrate::auth::capability_sets::{
|
||||
ensure_capability_set, owner_capabilities, user_capabilities, webdav_capabilities,
|
||||
};
|
||||
use papercrate::auth::jwt::{AccessTokenContext, JwtService, PrincipalKind};
|
||||
use papercrate::config::AppConfig;
|
||||
use papercrate::db::{self, PgPool};
|
||||
use papercrate::models::{
|
||||
@@ -81,7 +84,12 @@ impl ObjectStorage for FakeStorage {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn presign_get_object(&self, key: &str, expires_in: Duration) -> Result<String> {
|
||||
async fn presign_get_object(
|
||||
&self,
|
||||
key: &str,
|
||||
expires_in: Duration,
|
||||
_response_content_disposition: Option<&str>,
|
||||
) -> Result<String> {
|
||||
let guard = self.objects.lock().await;
|
||||
ensure!(guard.contains_key(key), "object {key} missing");
|
||||
Ok(format!(
|
||||
@@ -217,34 +225,49 @@ impl TestApp {
|
||||
Ok(format!("{}{}", root, key))
|
||||
}
|
||||
|
||||
pub async fn insert_user(&self, username: &str, _password: &str, _role: &str) -> Result<Uuid> {
|
||||
pub async fn insert_user(&self, username: &str, _password: &str, role: &str) -> Result<Uuid> {
|
||||
let username = username.to_string();
|
||||
let role = role.to_string();
|
||||
let tenant_id = self.ensure_default_tenant().await?;
|
||||
self.with_conn(move |conn| {
|
||||
let user = NewUser {
|
||||
id: Uuid::new_v4(),
|
||||
username,
|
||||
};
|
||||
diesel::insert_into(papercrate::schema::users::table)
|
||||
.values(&user)
|
||||
.execute(conn)
|
||||
.context("failed to insert user")?;
|
||||
let user_id = self
|
||||
.with_conn(move |conn| {
|
||||
let user = NewUser {
|
||||
id: Uuid::new_v4(),
|
||||
username,
|
||||
};
|
||||
diesel::insert_into(papercrate::schema::users::table)
|
||||
.values(&user)
|
||||
.execute(conn)
|
||||
.context("failed to insert user")?;
|
||||
|
||||
let membership = NewUserMembership {
|
||||
id: Uuid::new_v4(),
|
||||
user_id: user.id,
|
||||
tenant_id,
|
||||
};
|
||||
let capabilities = match role.as_str() {
|
||||
"admin" => owner_capabilities(),
|
||||
"webdav" => webdav_capabilities(),
|
||||
_ => user_capabilities(),
|
||||
};
|
||||
|
||||
diesel::insert_into(papercrate::schema::user_memberships::table)
|
||||
.values(&membership)
|
||||
.execute(conn)
|
||||
.context("failed to insert user membership")?;
|
||||
Ok(user.id)
|
||||
})
|
||||
.await
|
||||
let capability_set = ensure_capability_set(conn, tenant_id, capabilities)
|
||||
.map_err(|err| anyhow!("failed to ensure capability set: {:?}", err))?;
|
||||
|
||||
let membership = NewUserMembership {
|
||||
id: Uuid::new_v4(),
|
||||
user_id: user.id,
|
||||
tenant_id,
|
||||
capability_set_id: Some(capability_set.id),
|
||||
};
|
||||
|
||||
diesel::insert_into(papercrate::schema::user_memberships::table)
|
||||
.values(&membership)
|
||||
.execute(conn)
|
||||
.context("failed to insert user membership")?;
|
||||
Ok(user.id)
|
||||
})
|
||||
.await?;
|
||||
|
||||
Ok(user_id)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub async fn insert_passkey(&self, user_id: Uuid, nickname: Option<&str>) -> Result<Uuid> {
|
||||
let passkey_id = Uuid::new_v4();
|
||||
let nickname = nickname.map(|value| value.to_string());
|
||||
@@ -276,56 +299,71 @@ impl TestApp {
|
||||
async fn ensure_default_tenant(&self) -> Result<Uuid> {
|
||||
let name_value = TEST_TENANT_NAME.to_string();
|
||||
let quickwit_enabled = self.state.config.quickwit_endpoint.is_some();
|
||||
self.with_conn(move |conn| {
|
||||
use papercrate::schema::tenants::dsl as tenants_dsl;
|
||||
let tenant_id = self
|
||||
.with_conn(move |conn| {
|
||||
use papercrate::schema::tenants::dsl as tenants_dsl;
|
||||
|
||||
let existing = tenants_dsl::tenants
|
||||
.filter(tenants_dsl::name.eq(&name_value))
|
||||
.first::<Tenant>(conn)
|
||||
.optional()
|
||||
.context("failed to load default tenant")?;
|
||||
let existing = tenants_dsl::tenants
|
||||
.filter(tenants_dsl::name.eq(&name_value))
|
||||
.first::<Tenant>(conn)
|
||||
.optional()
|
||||
.context("failed to load default tenant")?;
|
||||
|
||||
let tenant_id = if let Some(current) = existing {
|
||||
let desired_root = current
|
||||
.storage_root
|
||||
.clone()
|
||||
.filter(|root| root.ends_with('/'))
|
||||
.unwrap_or_else(|| format!("test-tenants/{}/", current.id));
|
||||
let tenant_id = if let Some(current) = existing {
|
||||
let desired_root = current
|
||||
.storage_root
|
||||
.clone()
|
||||
.filter(|root| root.ends_with('/'))
|
||||
.unwrap_or_else(|| format!("test-tenants/{}/", current.id));
|
||||
|
||||
if current.storage_root.as_deref() != Some(desired_root.as_str()) {
|
||||
diesel::update(tenants_dsl::tenants.filter(tenants_dsl::id.eq(current.id)))
|
||||
.set(tenants_dsl::storage_root.eq(Some(desired_root)))
|
||||
.execute(conn)
|
||||
.context("failed to update default tenant storage root")?;
|
||||
}
|
||||
if current.storage_root.as_deref() != Some(desired_root.as_str()) {
|
||||
diesel::update(tenants_dsl::tenants.filter(tenants_dsl::id.eq(current.id)))
|
||||
.set(tenants_dsl::storage_root.eq(Some(desired_root)))
|
||||
.execute(conn)
|
||||
.context("failed to update default tenant storage root")?;
|
||||
}
|
||||
|
||||
current.id
|
||||
} else {
|
||||
let new_id = Uuid::new_v4();
|
||||
let root = format!("test-tenants/{}/", new_id);
|
||||
let quickwit_value = if quickwit_enabled {
|
||||
Some(format!("documents-{}", new_id))
|
||||
current.id
|
||||
} else {
|
||||
None
|
||||
let new_id = Uuid::new_v4();
|
||||
let root = format!("test-tenants/{}/", new_id);
|
||||
let quickwit_value = if quickwit_enabled {
|
||||
Some(format!("documents-{}", new_id))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
diesel::insert_into(tenants_dsl::tenants)
|
||||
.values((
|
||||
tenants_dsl::id.eq(new_id),
|
||||
tenants_dsl::name.eq(&name_value),
|
||||
tenants_dsl::storage_root.eq(Some(root)),
|
||||
tenants_dsl::quickwit_index.eq(quickwit_value),
|
||||
tenants_dsl::status.eq(TenantStatus::Active),
|
||||
))
|
||||
.execute(conn)
|
||||
.context("failed to insert default tenant")?;
|
||||
|
||||
new_id
|
||||
};
|
||||
|
||||
diesel::insert_into(tenants_dsl::tenants)
|
||||
.values((
|
||||
tenants_dsl::id.eq(new_id),
|
||||
tenants_dsl::name.eq(&name_value),
|
||||
tenants_dsl::storage_root.eq(Some(root)),
|
||||
tenants_dsl::quickwit_index.eq(quickwit_value),
|
||||
tenants_dsl::status.eq(TenantStatus::Active),
|
||||
))
|
||||
.execute(conn)
|
||||
.context("failed to insert default tenant")?;
|
||||
Ok(tenant_id)
|
||||
})
|
||||
.await?;
|
||||
|
||||
new_id
|
||||
};
|
||||
let mut conn = self
|
||||
.state
|
||||
.db_for_tenant(tenant_id)
|
||||
.map_err(|err| anyhow!("failed to scope tenant connection: {err:?}"))?;
|
||||
|
||||
Ok(tenant_id)
|
||||
})
|
||||
.await
|
||||
ensure_capability_set(&mut conn, tenant_id, owner_capabilities())
|
||||
.map_err(|err| anyhow!("ensure owner capability set: {err:?}"))?;
|
||||
ensure_capability_set(&mut conn, tenant_id, user_capabilities())
|
||||
.map_err(|err| anyhow!("ensure user capability set: {err:?}"))?;
|
||||
ensure_capability_set(&mut conn, tenant_id, webdav_capabilities())
|
||||
.map_err(|err| anyhow!("ensure webdav capability set: {err:?}"))?;
|
||||
|
||||
Ok(tenant_id)
|
||||
}
|
||||
|
||||
pub async fn login_token(&self, username: &str, _password: &str) -> Result<String> {
|
||||
@@ -337,6 +375,7 @@ impl TestApp {
|
||||
let username = username.to_string();
|
||||
let state = self.state.clone();
|
||||
self.with_conn(move |conn| {
|
||||
use papercrate::schema::capability_sets::dsl as capability_sets_dsl;
|
||||
use papercrate::schema::tenants::dsl as tenants_dsl;
|
||||
use papercrate::schema::user_memberships::dsl as memberships_dsl;
|
||||
use papercrate::schema::users::dsl as users_dsl;
|
||||
@@ -353,10 +392,28 @@ impl TestApp {
|
||||
.find(membership.tenant_id)
|
||||
.first(conn)?;
|
||||
|
||||
let capability_set_id = membership
|
||||
.capability_set_id
|
||||
.ok_or_else(|| anyhow!("membership missing capability set"))?;
|
||||
|
||||
let cap_version = capability_sets_dsl::capability_sets
|
||||
.find(capability_set_id)
|
||||
.select(capability_sets_dsl::cap_version)
|
||||
.first::<i32>(conn)?;
|
||||
|
||||
let now = Utc::now();
|
||||
let session_id = Uuid::new_v4();
|
||||
let access_token = state
|
||||
.jwt
|
||||
.generate_token(user.id, tenant.id, &user.username)
|
||||
.generate_token(AccessTokenContext {
|
||||
user_id: user.id,
|
||||
tenant_id: tenant.id,
|
||||
username: user.username.clone(),
|
||||
principal_kind: PrincipalKind::UserSession,
|
||||
principal_id: session_id,
|
||||
capability_set_id,
|
||||
cap_version,
|
||||
})
|
||||
.map_err(|err| anyhow!(err))?;
|
||||
|
||||
let session_value = generate_session_token();
|
||||
@@ -365,7 +422,7 @@ impl TestApp {
|
||||
now + ChronoDuration::days(state.config.refresh_token_expiry_days);
|
||||
|
||||
let new_session = NewUserSession {
|
||||
id: Uuid::new_v4(),
|
||||
id: session_id,
|
||||
user_id: user.id,
|
||||
token_hash: session_hash,
|
||||
issued_at: now.naive_utc(),
|
||||
@@ -540,7 +597,7 @@ impl TestApp {
|
||||
tag_ids_json: None,
|
||||
correspondents_json: None,
|
||||
issued_at: None,
|
||||
skip_existing: false,
|
||||
skip_existing: None,
|
||||
};
|
||||
self.upload_document_with_extras(
|
||||
path,
|
||||
@@ -620,9 +677,15 @@ impl TestApp {
|
||||
body.extend(b"\r\n");
|
||||
}
|
||||
|
||||
if extras.skip_existing {
|
||||
if let Some(skip_flag) = extras.skip_existing {
|
||||
body.extend(format!("--{boundary}\r\n").as_bytes());
|
||||
body.extend(b"Content-Disposition: form-data; name=\"skip_existing\"\r\n\r\ntrue\r\n");
|
||||
body.extend(b"Content-Disposition: form-data; name=\"skip_existing\"\r\n\r\n");
|
||||
body.extend(if skip_flag {
|
||||
b"true".as_ref()
|
||||
} else {
|
||||
b"false".as_ref()
|
||||
});
|
||||
body.extend(b"\r\n");
|
||||
}
|
||||
|
||||
body.extend(format!("--{boundary}--\r\n").as_bytes());
|
||||
@@ -668,7 +731,7 @@ pub struct UploadExtras<'a> {
|
||||
pub tag_ids_json: Option<&'a str>,
|
||||
pub correspondents_json: Option<&'a str>,
|
||||
pub issued_at: Option<&'a str>,
|
||||
pub skip_existing: bool,
|
||||
pub skip_existing: Option<bool>,
|
||||
}
|
||||
|
||||
impl<'a> UploadExtras<'a> {
|
||||
@@ -679,7 +742,7 @@ impl<'a> UploadExtras<'a> {
|
||||
tag_ids_json: None,
|
||||
correspondents_json: None,
|
||||
issued_at: None,
|
||||
skip_existing: false,
|
||||
skip_existing: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user