Split MCP and approval agent keys

This commit is contained in:
github-ops
2026-06-24 10:31:52 +00:00
parent d739f17393
commit 5922aea68f
16 changed files with 471 additions and 56 deletions
+56 -3
View File
@@ -1,7 +1,10 @@
use crank_core::{AgentId, PlatformApiKey, PlatformApiKeyId, PlatformApiKeyStatus, WorkspaceId};
use crank_core::{
AgentId, PlatformApiKey, PlatformApiKeyId, PlatformApiKeyKind, PlatformApiKeyScope,
PlatformApiKeyStatus, WorkspaceId,
};
use crank_registry::{CreatePlatformApiKeyRequest, PlatformApiKeyRecord};
use serde_json::json;
use time::OffsetDateTime;
use time::{OffsetDateTime, format_description::well_known::Rfc3339};
use tracing::instrument;
use crate::{
@@ -53,7 +56,19 @@ impl AdminService {
)
})?;
let secret = generate_access_secret("crk");
validate_platform_api_key_payload(&payload)?;
let expires_at = match payload.expires_at.as_deref() {
Some(value) => Some(
OffsetDateTime::parse(value, &Rfc3339)
.map_err(|_| ApiError::validation("expires_at must be RFC3339 timestamp"))?,
),
None => None,
};
let secret = generate_access_secret(match payload.key_kind {
PlatformApiKeyKind::McpClient => "crk",
PlatformApiKeyKind::Approval => "crk_appr",
});
let api_key = PlatformApiKeyRecord {
api_key: PlatformApiKey {
id: PlatformApiKeyId::new(new_prefixed_id("pk")),
@@ -61,10 +76,13 @@ impl AdminService {
agent_id: Some(agent_id.clone()),
name: payload.name,
prefix: secret.chars().take(16).collect(),
key_kind: payload.key_kind,
scopes: payload.scopes,
status: PlatformApiKeyStatus::Active,
created_at: OffsetDateTime::now_utc(),
last_used_at: None,
expires_at,
allowed_origins: payload.allowed_origins,
},
};
@@ -109,3 +127,38 @@ impl AdminService {
Ok(())
}
}
fn validate_platform_api_key_payload(payload: &PlatformApiKeyPayload) -> Result<(), ApiError> {
if payload.name.trim().is_empty() {
return Err(ApiError::validation("key name is required"));
}
if payload.scopes.is_empty() {
return Err(ApiError::validation("at least one key scope is required"));
}
let valid = payload.scopes.iter().all(|scope| match payload.key_kind {
PlatformApiKeyKind::McpClient => matches!(
scope,
PlatformApiKeyScope::Read | PlatformApiKeyScope::Write | PlatformApiKeyScope::Deploy
),
PlatformApiKeyKind::Approval => matches!(
scope,
PlatformApiKeyScope::Approve
| PlatformApiKeyScope::Deny
| PlatformApiKeyScope::ReadPending
),
});
if !valid {
return Err(ApiError::validation(
"key scopes do not match selected key kind",
));
}
if payload.key_kind == PlatformApiKeyKind::Approval && payload.allowed_origins.len() > 20 {
return Err(ApiError::validation(
"approval key can contain at most 20 allowed origins",
));
}
Ok(())
}