Public dry run dryrun-6341cce4cf9f

Source commit: 6341cce4cf9f801a3bfe2248d6e7e6a119cea715

Public tree identity: sha256:d909cba912925e66131fc55fe33774a2b26b1ff2c12b53ec4ef217c9788d8b7a
This commit is contained in:
Michel Paulissen 2026-07-03 20:50:16 +02:00
parent 84ef57d84b
commit 9f2d6ec010
7 changed files with 654 additions and 27 deletions

View file

@ -771,18 +771,64 @@ fn auth_logout_report(args: AuthLogoutArgs, cwd: PathBuf) -> Result<Value> {
fn key_add_report(args: KeyAddArgs) -> Result<Value> {
if let Some(coordinator) = &args.scope.coordinator {
let tenant = args.scope.tenant.clone();
let project = args.scope.project.clone();
let user = args.scope.user.clone();
let agent = args.agent.clone();
let public_key_fingerprint = Digest::sha256(&args.public_key);
let mut session = JsonLineSession::connect(coordinator)?;
let response = session.request(json!({
"type": "register_agent_public_key",
"tenant": args.scope.tenant,
"project": args.scope.project,
"user": args.scope.user,
"agent": args.agent,
"tenant": tenant,
"project": project,
"user": user,
"agent": agent,
"public_key": args.public_key,
}))?;
let record = response.get("record").cloned().unwrap_or_else(|| {
json!({
"tenant": tenant,
"project": project,
"user": user,
"agent": agent,
"public_key_fingerprint": public_key_fingerprint,
"scopes": ["project:read", "project:run"],
"human_account_creation_privilege": false,
"browser_interaction_required_each_run": false,
})
});
return Ok(json!({
"command": "key add",
"coordinator": coordinator,
"tenant": tenant,
"project": project,
"user": user,
"agent": agent,
"public_key_fingerprint": record
.get("public_key_fingerprint")
.cloned()
.unwrap_or_else(|| json!(public_key_fingerprint)),
"credential_scope": {
"tenant": tenant,
"project": project,
"actions": record
.get("scopes")
.cloned()
.unwrap_or_else(|| json!(["project:read", "project:run"])),
"human_account_creation_privilege": record
.get("human_account_creation_privilege")
.cloned()
.unwrap_or_else(|| json!(false)),
},
"browser_interaction_required_each_run": record
.get("browser_interaction_required_each_run")
.cloned()
.unwrap_or_else(|| json!(false)),
"attribution": {
"registered_by_user": user,
"agent": agent,
"credential_kind": "public_key",
},
"response": response,
"coordinator_session_requests": session.requests(),
}));
@ -798,16 +844,32 @@ fn key_add_report(args: KeyAddArgs) -> Result<Value> {
fn key_list_report(args: KeyListArgs) -> Result<Value> {
if let Some(coordinator) = &args.scope.coordinator {
let tenant = args.scope.tenant.clone();
let project = args.scope.project.clone();
let user = args.scope.user.clone();
let mut session = JsonLineSession::connect(coordinator)?;
let response = session.request(json!({
"type": "list_agent_public_keys",
"tenant": args.scope.tenant,
"project": args.scope.project,
"user": args.scope.user,
"tenant": tenant,
"project": project,
"user": user,
}))?;
return Ok(json!({
"command": "key list",
"coordinator": coordinator,
"tenant": tenant,
"project": project,
"user": user,
"records": response
.get("records")
.cloned()
.unwrap_or_else(|| json!([])),
"credential_scope": {
"tenant": tenant,
"project": project,
"listed_for_user": user,
"human_account_creation_privilege": false,
},
"response": response,
"coordinator_session_requests": session.requests(),
}));
@ -821,18 +883,41 @@ fn key_list_report(args: KeyListArgs) -> Result<Value> {
fn key_revoke_report(args: KeyRevokeArgs) -> Result<Value> {
if let Some(coordinator) = &args.scope.coordinator {
let tenant = args.scope.tenant.clone();
let project = args.scope.project.clone();
let user = args.scope.user.clone();
let agent = args.agent.clone();
let mut session = JsonLineSession::connect(coordinator)?;
let response = session.request(json!({
"type": "revoke_agent_public_key",
"tenant": args.scope.tenant,
"project": args.scope.project,
"user": args.scope.user,
"agent": args.agent,
"tenant": tenant,
"project": project,
"user": user,
"agent": agent,
}))?;
return Ok(json!({
"command": "key revoke",
"coordinator": coordinator,
"requires_confirmation": !args.yes,
"tenant": tenant,
"project": project,
"user": user,
"agent": agent,
"revoked": response
.pointer("/record/revoked")
.cloned()
.unwrap_or_else(|| json!(true)),
"credential_scope": {
"tenant": tenant,
"project": project,
"revoked_for_user": user,
"human_account_creation_privilege": false,
},
"attribution": {
"revoked_by_user": user,
"agent": agent,
"credential_kind": "public_key",
},
"response": response,
"coordinator_session_requests": session.requests(),
}));
@ -4273,6 +4358,84 @@ mod tests {
}
}
#[test]
fn key_lifecycle_reports_project_scoped_agent_credentials() {
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
let addr = listener.local_addr().unwrap().to_string();
let server = std::thread::spawn(move || {
let add_response = concat!(
r#"{"type":"agent_public_key","actor":"user","record":{"tenant":"tenant","project":"project","user":"user","agent":"agent-ci","public_key":"agent-key-v1","public_key_fingerprint":"sha256:agent-v1","version":1,"revoked":false,"scopes":["project:read","project:run"],"human_account_creation_privilege":false,"browser_interaction_required_each_run":false}}"#
);
let list_response = concat!(
r#"{"type":"agent_public_keys","actor":"user","records":[{"tenant":"tenant","project":"project","user":"user","agent":"agent-ci","public_key":"agent-key-v1","public_key_fingerprint":"sha256:agent-v1","version":1,"revoked":false,"scopes":["project:read","project:run"],"human_account_creation_privilege":false,"browser_interaction_required_each_run":false}]}"#
);
let revoke_response = concat!(
r#"{"type":"agent_public_key","actor":"user","record":{"tenant":"tenant","project":"project","user":"user","agent":"agent-ci","public_key":"agent-key-v1","public_key_fingerprint":"sha256:agent-v1","version":1,"revoked":true,"scopes":["project:read","project:run"],"human_account_creation_privilege":false,"browser_interaction_required_each_run":false}}"#
);
for (expected, response) in [
("register_agent_public_key", add_response),
("list_agent_public_keys", list_response),
("revoke_agent_public_key", revoke_response),
] {
let (mut stream, _) = listener.accept().unwrap();
let mut reader = BufReader::new(stream.try_clone().unwrap());
let mut line = String::new();
reader.read_line(&mut line).unwrap();
assert!(line.contains(&format!(r#""type":"{expected}""#)));
assert!(line.contains(r#""tenant":"tenant""#));
assert!(line.contains(r#""project":"project""#));
assert!(line.contains(r#""user":"user""#));
if expected != "list_agent_public_keys" {
assert!(line.contains(r#""agent":"agent-ci""#));
}
if expected == "register_agent_public_key" {
assert!(line.contains(r#""public_key":"agent-key-v1""#));
}
stream.write_all(response.as_bytes()).unwrap();
stream.write_all(b"\n").unwrap();
}
});
let scope = CliScopeArgs {
coordinator: Some(addr),
tenant: "tenant".to_owned(),
project: "project".to_owned(),
user: "user".to_owned(),
json: false,
};
let added = key_add_report(KeyAddArgs {
scope: scope.clone(),
agent: "agent-ci".to_owned(),
public_key: "agent-key-v1".to_owned(),
})
.unwrap();
let listed = key_list_report(KeyListArgs {
scope: scope.clone(),
})
.unwrap();
let revoked = key_revoke_report(KeyRevokeArgs {
scope,
agent: "agent-ci".to_owned(),
yes: true,
})
.unwrap();
server.join().unwrap();
assert_eq!(added["command"], "key add");
assert_eq!(added["agent"], "agent-ci");
assert_eq!(added["credential_scope"]["actions"][0], "project:read");
assert_eq!(
added["credential_scope"]["human_account_creation_privilege"],
false
);
assert_eq!(added["browser_interaction_required_each_run"], false);
assert_eq!(added["attribution"]["registered_by_user"], "user");
assert_eq!(listed["records"].as_array().unwrap().len(), 1);
assert_eq!(listed["credential_scope"]["listed_for_user"], "user");
assert_eq!(revoked["revoked"], true);
assert_eq!(revoked["attribution"]["revoked_by_user"], "user");
}
#[test]
fn human_report_is_text_not_json() {
let report = json!({