|
|
… |
1126 unchanged lines |
| 1127 |
1127 |
|
); |
| 1128 |
1128 |
|
} |
| 1129 |
1129 |
|
|
|
1130 |
+ |
/// A tag made under one short-lived key and pushed under another: its |
|
1131 |
+ |
/// signer travels with the push (identity and certificate), so a receiver |
|
1132 |
+ |
/// that has never seen that key accepts it. A tag-only push by a keyless |
|
1133 |
+ |
/// agent was refused "unknown key" (found releasing OxygenFlow 0.2.0). |
|
1134 |
+ |
#[test] |
|
1135 |
+ |
fn a_tag_signed_by_an_earlier_short_lived_key_is_pushed() { |
|
1136 |
+ |
use crate::cert::{Certificate, SignedCertificate}; |
|
1137 |
+ |
use crate::identity::Scope; |
|
1138 |
+ |
hermetic(); |
|
1139 |
+ |
let dir = tempfile::tempdir().unwrap(); |
|
1140 |
+ |
let root = dir.path(); |
|
1141 |
+ |
let _origin = Repo::init(&root.join("origin")).unwrap(); |
|
1142 |
+ |
let url = root.join("origin").to_string_lossy().to_string(); |
|
1143 |
+ |
let mut a = Repo::clone(&url, &root.join("a")) |
|
1144 |
+ |
.unwrap() |
|
1145 |
+ |
.with_home(root.join("home-a")); |
|
1146 |
+ |
a.create_identity("Ada", "[email protected]", IdentityScope::User) |
|
1147 |
+ |
.unwrap(); |
|
1148 |
+ |
write(&root.join("a"), "a.txt", b"a\n"); |
|
1149 |
+ |
a.commit("a", &[]).unwrap(); |
|
1150 |
+ |
a.policy_init().unwrap(); |
|
1151 |
+ |
assert!(a.push("origin", false).unwrap().iter().all(|r| r.ok)); |
|
1152 |
+ |
let hub_sk = SecretKey::generate(); |
|
1153 |
+ |
let hub = hub_sk.identity("hub", "[email protected]", 1); |
|
1154 |
+ |
a.policy_grant_subject( |
|
1155 |
+ |
"agent:claude@ada", |
|
1156 |
+ |
hub.clone(), |
|
1157 |
+ |
Scope::CHANGES | Scope::REFS, |
|
1158 |
+ |
Some("*"), |
|
1159 |
+ |
) |
|
1160 |
+ |
.unwrap(); |
|
1161 |
+ |
assert!(a.push("origin", false).unwrap().iter().all(|r| r.ok)); |
|
1162 |
+ |
|
|
1163 |
+ |
let now = Hlc::now().wall_ms; |
|
1164 |
+ |
let mut agent = Repo::clone(&url, &root.join("agent")) |
|
1165 |
+ |
.unwrap() |
|
1166 |
+ |
.with_home(root.join("home-agent")); |
|
1167 |
+ |
agent.store().put_identity(&hub).unwrap(); |
|
1168 |
+ |
let certify = |agent: &Repo, id| { |
|
1169 |
+ |
let cert = SignedCertificate::sign( |
|
1170 |
+ |
Certificate { |
|
1171 |
+ |
key: id, |
|
1172 |
+ |
issuer: hub.hash(), |
|
1173 |
+ |
subject: "agent:claude@ada".into(), |
|
1174 |
+ |
claims: vec![], |
|
1175 |
+ |
scope: Scope::CHANGES | Scope::REFS, |
|
1176 |
+ |
not_before: now - 60_000, |
|
1177 |
+ |
expires: now + 600_000, |
|
1178 |
+ |
grant: None, |
|
1179 |
+ |
}, |
|
1180 |
+ |
&hub, |
|
1181 |
+ |
&hub_sk, |
|
1182 |
+ |
); |
|
1183 |
+ |
agent.store().put_certificate(&cert).unwrap(); |
|
1184 |
+ |
}; |
|
1185 |
+ |
// The tag, under the first short-lived key. |
|
1186 |
+ |
let (first, first_id) = agent |
|
1187 |
+ |
.create_identity("claude", "[email protected]", IdentityScope::User) |
|
1188 |
+ |
.unwrap(); |
|
1189 |
+ |
certify(&agent, first_id); |
|
1190 |
+ |
let heads = agent.bookmark_heads().unwrap(); |
|
1191 |
+ |
agent.tag_create("v1", &heads, "first").unwrap(); |
|
1192 |
+ |
// The push, under a second one (a later process's ten-minute key). |
|
1193 |
+ |
let (second, second_id) = agent |
|
1194 |
+ |
.create_identity("claude", "[email protected]", IdentityScope::User) |
|
1195 |
+ |
.unwrap(); |
|
1196 |
+ |
assert_ne!(first, second); |
|
1197 |
+ |
certify(&agent, second_id); |
|
1198 |
+ |
agent |
|
1199 |
+ |
.set_user_config("author.signer", &second.to_hex()) |
|
1200 |
+ |
.unwrap(); |
|
1201 |
+ |
let host = Repo::open(&root.join("origin")).unwrap(); |
|
1202 |
+ |
assert!(host.store().get_identity(&first).is_err(), "the host has never seen the tag's key"); |
|
1203 |
+ |
let r = agent.push("origin", false).unwrap(); |
|
1204 |
+ |
assert!(r.iter().all(|x| x.ok), "{r:?}"); |
|
1205 |
+ |
let host = Repo::open(&root.join("origin")).unwrap(); |
|
1206 |
+ |
assert!(host.tag_refs().unwrap().iter().any(|t| t.name == "v1"), "the tag arrived"); |
|
1207 |
+ |
} |
|
1208 |
+ |
|
| 1130 |
1209 |
|
/// oxygenhub#266: a short-lived key its issuer certified moves a bookmark |
| 1131 |
1210 |
|
/// and signs no change (a keyless agent naming a bookmark). A third |
| 1132 |
1211 |
|
/// party's clone gets the key's identity, its certificate and the |
|
|
… |
8026 unchanged lines |