identity export (and every identity lookup) falls back to the key directory in a repository that has not stored the identity yet, checking it hashes to the key; its help names the file it writes e3d071cecfe2
closes #9
- Author
- Andy McCall @andymccall <[email protected]>
- When
- · 2026-09-28 19:24 UTC
- Signature
- ✔ signed by Andy McCall
- Hash
e3d071cecfe240b44cc4225a6649d78830ba5d6ee9ff8b97684d8e91d8b7e225- Depends on
- 982864068a59 afaba5f1306d d42369abf180 fce44ae9ad29
- Depended on by
- a39754d02c58 cbe8499a5a8c cc7a00565161
- In bookmarks
- agents-in-readme bare-hashes carried-not-removed carry-identical doctor-trust identical-sides keyless-tags main merge-remote-bookmark only-help-103 paths-for-resolutions-and-amend push-notes show-stat subrepo-pin-state switch-says
- In tags
- v0.1.0 v0.10.0 v0.11.0 v0.2.0 v0.3.0 v0.3.1 v0.4.0 v0.4.1 v0.4.2 v0.4.3 v0.4.4 v0.4.5 v0.4.6 v0.5.0 v0.5.1 v0.6.0 v0.7.0 v0.8.0 v0.9.0
- Edits
- 6 atoms, 0 tree operations, 3 files
Browse the tree at this change
modified crates/oxy-core/src/repo/identity.rs +23 −1
| … | 292 unchanged lines |
||
| 293 | 293 | } |
|
| 294 | 294 | } |
|
| 295 | 295 | ||
| 296 | + | /// An identity: from this repository's store, or else from the key |
|
| 297 | + | /// directory, where `oxy identity create` keeps the user's own (a new |
|
| 298 | + | /// repository has not stored it yet). The file must hash to `key`. |
|
| 296 | 299 | pub fn identity(&self, key: &KeyId) -> Result<Identity> { |
|
| 297 | - | Ok(self.store.get_identity(key)?) |
|
| 300 | + | match self.store.get_identity(key) { |
|
| 301 | + | Ok(id) => Ok(id), |
|
| 302 | + | Err(e) => { |
|
| 303 | + | let path = self.keys_dir.join(format!("{}.identity", key.to_hex())); |
|
| 304 | + | match fs::read(&path) { |
|
| 305 | + | Ok(bytes) => { |
|
| 306 | + | let id = Identity::decode(&bytes)?; |
|
| 307 | + | if id.hash() != *key { |
|
| 308 | + | return fail(format!( |
|
| 309 | + | "{} does not hold identity {}", |
|
| 310 | + | path.display(), |
|
| 311 | + | key.short(12) |
|
| 312 | + | )); |
|
| 313 | + | } |
|
| 314 | + | Ok(id) |
|
| 315 | + | } |
|
| 316 | + | Err(_) => Err(e.into()), |
|
| 317 | + | } |
|
| 318 | + | } |
|
| 319 | + | } |
|
| 298 | 320 | } |
|
| 299 | 321 | ||
| 300 | 322 | /// Sign a stored change with the configured secret key. |
|
| … | 186 unchanged lines |
modified crates/oxy-core/src/repo/tests.rs +37 −0
| … | 4768 unchanged lines |
||
| 4769 | 4769 | assert!(repo2.load_state(&repo2.bookmark_heads().unwrap()).is_err()); |
|
| 4770 | 4770 | assert!(repo2.fsck().unwrap().errors() > 0, "fsck reports it"); |
|
| 4771 | 4771 | } |
|
| 4772 | + | ||
| 4773 | + | /// A new repository has not stored the user's identity yet (that happens |
|
| 4774 | + | /// at the first signed change); looking it up falls back to the key |
|
| 4775 | + | /// directory, and a file there that does not hash to the key is refused. |
|
| 4776 | + | #[test] |
|
| 4777 | + | fn identity_is_found_in_the_key_directory_for_a_new_repository() { |
|
| 4778 | + | hermetic(); |
|
| 4779 | + | let dir = tempfile::tempdir().unwrap(); |
|
| 4780 | + | let home = dir.path().join("home"); |
|
| 4781 | + | let mut first = Repo::init(&dir.path().join("a")) |
|
| 4782 | + | .unwrap() |
|
| 4783 | + | .with_home(home.clone()); |
|
| 4784 | + | let (key, _) = first |
|
| 4785 | + | .create_identity("Ada", "[email protected]", IdentityScope::User) |
|
| 4786 | + | .unwrap(); |
|
| 4787 | + | let fresh = Repo::init(&dir.path().join("b")) |
|
| 4788 | + | .unwrap() |
|
| 4789 | + | .with_home(home.clone()); |
|
| 4790 | + | assert!(!fresh.store().has(&key), "nothing stored yet"); |
|
| 4791 | + | let id = fresh.identity(&key).unwrap(); |
|
| 4792 | + | assert_eq!((id.name.as_str(), id.hash()), ("Ada", key)); |
|
| 4793 | + | // A key file that is not this identity is an error, not a wrong answer. |
|
| 4794 | + | let keys = fresh.keys_dir.clone(); |
|
| 4795 | + | let (other, _) = first |
|
| 4796 | + | .create_identity("Bob", "[email protected]", IdentityScope::User) |
|
| 4797 | + | .unwrap(); |
|
| 4798 | + | fs::copy( |
|
| 4799 | + | keys.join(format!("{}.identity", other.to_hex())), |
|
| 4800 | + | keys.join(format!("{}.identity", key.to_hex())), |
|
| 4801 | + | ) |
|
| 4802 | + | .unwrap(); |
|
| 4803 | + | assert!(fresh |
|
| 4804 | + | .identity(&key) |
|
| 4805 | + | .unwrap_err() |
|
| 4806 | + | .to_string() |
|
| 4807 | + | .contains("does not hold identity")); |
|
| 4808 | + | } |
modified src/cli/mod.rs +1 −1
| … | 636 unchanged lines |
||
| 637 | 637 | Show, |
|
| 638 | 638 | /// Write your identity object to a file, to hand to a policy holder |
|
| 639 | 639 | Export { |
|
| 640 | - | /// File to write (default: stdout) |
|
| 640 | + | /// File to write (default: <key>.identity in the current directory) |
|
| 641 | 641 | path: Option<PathBuf>, |
|
| 642 | 642 | }, |
|
| 643 | 643 | /// Sign a host's challenge (hex, at least 16 bytes) to prove you hold your key; prints the signature as hex |
|
| … | 430 unchanged lines |