Split orchestration by durable use case
This commit is contained in:
parent
069f3813ae
commit
fa81cb7f21
2 changed files with 609 additions and 513 deletions
|
|
@ -16,6 +16,8 @@ use crate::types::{
|
|||
};
|
||||
use crate::vault::{EmptyPathFence, VaultClient};
|
||||
|
||||
mod query;
|
||||
|
||||
/// Durable source-credential registry with Vault-backed secret versions.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CredentialRegistry {
|
||||
|
|
@ -417,122 +419,6 @@ impl CredentialRegistry {
|
|||
.ok_or_else(|| DurableError::Effect("revoked credential is missing".into()))
|
||||
}
|
||||
|
||||
pub async fn get(
|
||||
&self,
|
||||
caller: &CredentialCaller,
|
||||
credential_id: &OpaqueId,
|
||||
) -> Result<Option<SourceCredential>, CredentialError> {
|
||||
let record = self.get_internal(credential_id).await?;
|
||||
match require_manage(caller, record.as_ref()) {
|
||||
Ok(visible) => Ok(Some(visible)),
|
||||
Err(CredentialError::NotFound) => Ok(None),
|
||||
Err(error) => Err(error),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn list(
|
||||
&self,
|
||||
caller: &CredentialCaller,
|
||||
limit: u32,
|
||||
cursor: Option<&str>,
|
||||
) -> Result<SourceCredentialPage, CredentialError> {
|
||||
let limit = limit.clamp(1, 100) as i64;
|
||||
let after = cursor.map(decode_cursor).transpose()?;
|
||||
let owner_filter = if caller.administrator {
|
||||
None
|
||||
} else {
|
||||
Some(caller.principal_id.as_str().to_owned())
|
||||
};
|
||||
|
||||
let rows = if let Some(owner) = owner_filter.as_deref() {
|
||||
if let Some((created_at, id)) = after {
|
||||
sqlx::query_as::<_, CredentialRow>(
|
||||
"SELECT c.credential_id, c.owner_id, c.label, c.kind, c.status,
|
||||
c.created_at, c.updated_at,
|
||||
v.version_id AS active_version_id, v.created_at AS active_version_created_at
|
||||
FROM source_credentials c
|
||||
LEFT JOIN source_credential_versions v
|
||||
ON v.version_id = c.active_version_id
|
||||
WHERE c.owner_id = $1
|
||||
AND (c.created_at, c.credential_id) < ($2, $3)
|
||||
ORDER BY c.created_at DESC, c.credential_id DESC
|
||||
LIMIT $4",
|
||||
)
|
||||
.bind(owner)
|
||||
.bind(created_at)
|
||||
.bind(id)
|
||||
.bind(limit + 1)
|
||||
.fetch_all(&self.pool)
|
||||
.await
|
||||
.map_err(DurableError::from)?
|
||||
} else {
|
||||
sqlx::query_as::<_, CredentialRow>(
|
||||
"SELECT c.credential_id, c.owner_id, c.label, c.kind, c.status,
|
||||
c.created_at, c.updated_at,
|
||||
v.version_id AS active_version_id, v.created_at AS active_version_created_at
|
||||
FROM source_credentials c
|
||||
LEFT JOIN source_credential_versions v
|
||||
ON v.version_id = c.active_version_id
|
||||
WHERE c.owner_id = $1
|
||||
ORDER BY c.created_at DESC, c.credential_id DESC
|
||||
LIMIT $2",
|
||||
)
|
||||
.bind(owner)
|
||||
.bind(limit + 1)
|
||||
.fetch_all(&self.pool)
|
||||
.await
|
||||
.map_err(DurableError::from)?
|
||||
}
|
||||
} else if let Some((created_at, id)) = after {
|
||||
sqlx::query_as::<_, CredentialRow>(
|
||||
"SELECT c.credential_id, c.owner_id, c.label, c.kind, c.status,
|
||||
c.created_at, c.updated_at,
|
||||
v.version_id AS active_version_id, v.created_at AS active_version_created_at
|
||||
FROM source_credentials c
|
||||
LEFT JOIN source_credential_versions v
|
||||
ON v.version_id = c.active_version_id
|
||||
WHERE (c.created_at, c.credential_id) < ($1, $2)
|
||||
ORDER BY c.created_at DESC, c.credential_id DESC
|
||||
LIMIT $3",
|
||||
)
|
||||
.bind(created_at)
|
||||
.bind(id)
|
||||
.bind(limit + 1)
|
||||
.fetch_all(&self.pool)
|
||||
.await
|
||||
.map_err(DurableError::from)?
|
||||
} else {
|
||||
sqlx::query_as::<_, CredentialRow>(
|
||||
"SELECT c.credential_id, c.owner_id, c.label, c.kind, c.status,
|
||||
c.created_at, c.updated_at,
|
||||
v.version_id AS active_version_id, v.created_at AS active_version_created_at
|
||||
FROM source_credentials c
|
||||
LEFT JOIN source_credential_versions v
|
||||
ON v.version_id = c.active_version_id
|
||||
ORDER BY c.created_at DESC, c.credential_id DESC
|
||||
LIMIT $1",
|
||||
)
|
||||
.bind(limit + 1)
|
||||
.fetch_all(&self.pool)
|
||||
.await
|
||||
.map_err(DurableError::from)?
|
||||
};
|
||||
|
||||
let mut items: Vec<SourceCredential> = Vec::with_capacity(rows.len().min(limit as usize));
|
||||
let mut next_cursor = None;
|
||||
for (index, row) in rows.into_iter().enumerate() {
|
||||
if index as i64 >= limit {
|
||||
let last = items
|
||||
.last()
|
||||
.ok_or_else(|| CredentialError::Config("page overflow without items".into()))?;
|
||||
next_cursor = Some(encode_cursor(last.created_at, last.id.as_str()));
|
||||
break;
|
||||
}
|
||||
items.push(row.into_record()?);
|
||||
}
|
||||
Ok(SourceCredentialPage { items, next_cursor })
|
||||
}
|
||||
|
||||
/// Pin the current authorized version for a new execution grant.
|
||||
///
|
||||
/// Serializes with revoke/rotate on the credential advisory lock so a
|
||||
|
|
|
|||
119
crates/remora-credentials/src/registry/query.rs
Normal file
119
crates/remora-credentials/src/registry/query.rs
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
use super::*;
|
||||
|
||||
impl CredentialRegistry {
|
||||
pub async fn get(
|
||||
&self,
|
||||
caller: &CredentialCaller,
|
||||
credential_id: &OpaqueId,
|
||||
) -> Result<Option<SourceCredential>, CredentialError> {
|
||||
let record = self.get_internal(credential_id).await?;
|
||||
match require_manage(caller, record.as_ref()) {
|
||||
Ok(visible) => Ok(Some(visible)),
|
||||
Err(CredentialError::NotFound) => Ok(None),
|
||||
Err(error) => Err(error),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn list(
|
||||
&self,
|
||||
caller: &CredentialCaller,
|
||||
limit: u32,
|
||||
cursor: Option<&str>,
|
||||
) -> Result<SourceCredentialPage, CredentialError> {
|
||||
let limit = limit.clamp(1, 100) as i64;
|
||||
let after = cursor.map(decode_cursor).transpose()?;
|
||||
let owner_filter = if caller.administrator {
|
||||
None
|
||||
} else {
|
||||
Some(caller.principal_id.as_str().to_owned())
|
||||
};
|
||||
|
||||
let rows = if let Some(owner) = owner_filter.as_deref() {
|
||||
if let Some((created_at, id)) = after {
|
||||
sqlx::query_as::<_, CredentialRow>(
|
||||
"SELECT c.credential_id, c.owner_id, c.label, c.kind, c.status,
|
||||
c.created_at, c.updated_at,
|
||||
v.version_id AS active_version_id, v.created_at AS active_version_created_at
|
||||
FROM source_credentials c
|
||||
LEFT JOIN source_credential_versions v
|
||||
ON v.version_id = c.active_version_id
|
||||
WHERE c.owner_id = $1
|
||||
AND (c.created_at, c.credential_id) < ($2, $3)
|
||||
ORDER BY c.created_at DESC, c.credential_id DESC
|
||||
LIMIT $4",
|
||||
)
|
||||
.bind(owner)
|
||||
.bind(created_at)
|
||||
.bind(id)
|
||||
.bind(limit + 1)
|
||||
.fetch_all(&self.pool)
|
||||
.await
|
||||
.map_err(DurableError::from)?
|
||||
} else {
|
||||
sqlx::query_as::<_, CredentialRow>(
|
||||
"SELECT c.credential_id, c.owner_id, c.label, c.kind, c.status,
|
||||
c.created_at, c.updated_at,
|
||||
v.version_id AS active_version_id, v.created_at AS active_version_created_at
|
||||
FROM source_credentials c
|
||||
LEFT JOIN source_credential_versions v
|
||||
ON v.version_id = c.active_version_id
|
||||
WHERE c.owner_id = $1
|
||||
ORDER BY c.created_at DESC, c.credential_id DESC
|
||||
LIMIT $2",
|
||||
)
|
||||
.bind(owner)
|
||||
.bind(limit + 1)
|
||||
.fetch_all(&self.pool)
|
||||
.await
|
||||
.map_err(DurableError::from)?
|
||||
}
|
||||
} else if let Some((created_at, id)) = after {
|
||||
sqlx::query_as::<_, CredentialRow>(
|
||||
"SELECT c.credential_id, c.owner_id, c.label, c.kind, c.status,
|
||||
c.created_at, c.updated_at,
|
||||
v.version_id AS active_version_id, v.created_at AS active_version_created_at
|
||||
FROM source_credentials c
|
||||
LEFT JOIN source_credential_versions v
|
||||
ON v.version_id = c.active_version_id
|
||||
WHERE (c.created_at, c.credential_id) < ($1, $2)
|
||||
ORDER BY c.created_at DESC, c.credential_id DESC
|
||||
LIMIT $3",
|
||||
)
|
||||
.bind(created_at)
|
||||
.bind(id)
|
||||
.bind(limit + 1)
|
||||
.fetch_all(&self.pool)
|
||||
.await
|
||||
.map_err(DurableError::from)?
|
||||
} else {
|
||||
sqlx::query_as::<_, CredentialRow>(
|
||||
"SELECT c.credential_id, c.owner_id, c.label, c.kind, c.status,
|
||||
c.created_at, c.updated_at,
|
||||
v.version_id AS active_version_id, v.created_at AS active_version_created_at
|
||||
FROM source_credentials c
|
||||
LEFT JOIN source_credential_versions v
|
||||
ON v.version_id = c.active_version_id
|
||||
ORDER BY c.created_at DESC, c.credential_id DESC
|
||||
LIMIT $1",
|
||||
)
|
||||
.bind(limit + 1)
|
||||
.fetch_all(&self.pool)
|
||||
.await
|
||||
.map_err(DurableError::from)?
|
||||
};
|
||||
|
||||
let mut items: Vec<SourceCredential> = Vec::with_capacity(rows.len().min(limit as usize));
|
||||
let mut next_cursor = None;
|
||||
for (index, row) in rows.into_iter().enumerate() {
|
||||
if index as i64 >= limit {
|
||||
let last = items
|
||||
.last()
|
||||
.ok_or_else(|| CredentialError::Config("page overflow without items".into()))?;
|
||||
next_cursor = Some(encode_cursor(last.created_at, last.id.as_str()));
|
||||
break;
|
||||
}
|
||||
items.push(row.into_record()?);
|
||||
}
|
||||
Ok(SourceCredentialPage { items, next_cursor })
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue