chore: publish clean community baseline
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use time::OffsetDateTime;
|
||||
|
||||
use crate::{
|
||||
edition::{MachineAccessMode, OperationSecurityLevel},
|
||||
ids::{AgentId, AuthProfileId, OperationId, SecretId, WorkspaceId},
|
||||
protocol::AuthKind,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct BearerAuthConfig {
|
||||
pub header_name: String,
|
||||
pub secret_id: SecretId,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct BasicAuthConfig {
|
||||
pub username_secret_id: SecretId,
|
||||
pub password_secret_id: SecretId,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct ApiKeyHeaderAuthConfig {
|
||||
pub header_name: String,
|
||||
pub secret_id: SecretId,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct ApiKeyQueryAuthConfig {
|
||||
pub param_name: String,
|
||||
pub secret_id: SecretId,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum AuthConfig {
|
||||
Bearer(BearerAuthConfig),
|
||||
Basic(BasicAuthConfig),
|
||||
ApiKeyHeader(ApiKeyHeaderAuthConfig),
|
||||
ApiKeyQuery(ApiKeyQueryAuthConfig),
|
||||
}
|
||||
|
||||
impl AuthConfig {
|
||||
pub fn secret_ids(&self) -> Vec<&SecretId> {
|
||||
match self {
|
||||
Self::Bearer(config) => vec![&config.secret_id],
|
||||
Self::Basic(config) => vec![&config.username_secret_id, &config.password_secret_id],
|
||||
Self::ApiKeyHeader(config) => vec![&config.secret_id],
|
||||
Self::ApiKeyQuery(config) => vec![&config.secret_id],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct AuthProfile {
|
||||
pub id: AuthProfileId,
|
||||
pub workspace_id: WorkspaceId,
|
||||
pub name: String,
|
||||
pub kind: AuthKind,
|
||||
pub config: AuthConfig,
|
||||
#[serde(with = "time::serde::rfc3339")]
|
||||
pub created_at: OffsetDateTime,
|
||||
#[serde(with = "time::serde::rfc3339")]
|
||||
pub updated_at: OffsetDateTime,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum AgentTokenGrantType {
|
||||
AgentKey,
|
||||
RefreshToken,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct IssueAgentTokenRequest {
|
||||
pub grant_type: AgentTokenGrantType,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub agent_key: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub refresh_token: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub scope: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct IssueOneTimeAgentTokenRequest {
|
||||
pub agent_key: String,
|
||||
pub operation_id: OperationId,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub scope: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct IssuedAgentTokenResponse {
|
||||
pub access_token: String,
|
||||
pub token_type: String,
|
||||
pub expires_in: u64,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub refresh_token: Option<String>,
|
||||
pub machine_access_mode: MachineAccessMode,
|
||||
pub security_level: OperationSecurityLevel,
|
||||
pub agent_id: AgentId,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde_json::json;
|
||||
use time::{OffsetDateTime, format_description::well_known::Rfc3339};
|
||||
|
||||
use super::{
|
||||
AgentTokenGrantType, ApiKeyHeaderAuthConfig, AuthConfig, AuthProfile,
|
||||
IssueAgentTokenRequest, IssueOneTimeAgentTokenRequest, IssuedAgentTokenResponse,
|
||||
};
|
||||
use crate::{
|
||||
edition::{MachineAccessMode, OperationSecurityLevel},
|
||||
ids::{AuthProfileId, SecretId, WorkspaceId},
|
||||
protocol::AuthKind,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn auth_profile_serializes_timestamps_as_rfc3339() {
|
||||
let profile = AuthProfile {
|
||||
id: AuthProfileId::new("auth_01"),
|
||||
workspace_id: WorkspaceId::new("ws_default"),
|
||||
name: "header".to_owned(),
|
||||
kind: AuthKind::ApiKeyHeader,
|
||||
config: AuthConfig::ApiKeyHeader(ApiKeyHeaderAuthConfig {
|
||||
header_name: "X-Api-Key".to_owned(),
|
||||
secret_id: SecretId::new("secret_01"),
|
||||
}),
|
||||
created_at: OffsetDateTime::parse("2026-03-25T12:00:00Z", &Rfc3339).unwrap(),
|
||||
updated_at: OffsetDateTime::parse("2026-03-25T12:05:00Z", &Rfc3339).unwrap(),
|
||||
};
|
||||
|
||||
let value = serde_json::to_value(&profile).unwrap();
|
||||
|
||||
assert_eq!(value["created_at"], json!("2026-03-25T12:00:00Z"));
|
||||
assert_eq!(value["updated_at"], json!("2026-03-25T12:05:00Z"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_agent_token_request_serializes_stable_contract() {
|
||||
let request = IssueAgentTokenRequest {
|
||||
grant_type: AgentTokenGrantType::AgentKey,
|
||||
agent_key: Some("crk_agent_secret".to_owned()),
|
||||
refresh_token: None,
|
||||
scope: vec!["tools:call".to_owned()],
|
||||
};
|
||||
|
||||
let value = serde_json::to_value(&request).unwrap();
|
||||
|
||||
assert_eq!(value["grant_type"], json!("agent_key"));
|
||||
assert_eq!(value["agent_key"], json!("crk_agent_secret"));
|
||||
assert_eq!(value["scope"], json!(["tools:call"]));
|
||||
assert_eq!(value.get("refresh_token"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_one_time_agent_token_request_serializes_stable_contract() {
|
||||
let request = IssueOneTimeAgentTokenRequest {
|
||||
agent_key: "crk_agent_secret".to_owned(),
|
||||
operation_id: "op_01".into(),
|
||||
scope: vec!["tools:call".to_owned()],
|
||||
};
|
||||
|
||||
let value = serde_json::to_value(&request).unwrap();
|
||||
|
||||
assert_eq!(value["agent_key"], json!("crk_agent_secret"));
|
||||
assert_eq!(value["operation_id"], json!("op_01"));
|
||||
assert_eq!(value["scope"], json!(["tools:call"]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issued_agent_token_response_serializes_machine_access_metadata() {
|
||||
let response = IssuedAgentTokenResponse {
|
||||
access_token: "tok_01".to_owned(),
|
||||
token_type: "Bearer".to_owned(),
|
||||
expires_in: 300,
|
||||
refresh_token: Some("rfr_01".to_owned()),
|
||||
machine_access_mode: MachineAccessMode::ShortLivedToken,
|
||||
security_level: OperationSecurityLevel::Elevated,
|
||||
agent_id: "agt_01".into(),
|
||||
};
|
||||
|
||||
let value = serde_json::to_value(&response).unwrap();
|
||||
|
||||
assert_eq!(value["access_token"], json!("tok_01"));
|
||||
assert_eq!(value["token_type"], json!("Bearer"));
|
||||
assert_eq!(value["expires_in"], json!(300));
|
||||
assert_eq!(value["refresh_token"], json!("rfr_01"));
|
||||
assert_eq!(value["machine_access_mode"], json!("short_lived_token"));
|
||||
assert_eq!(value["security_level"], json!("elevated"));
|
||||
assert_eq!(value["agent_id"], json!("agt_01"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user