radicle-cli: more helpful error for non-delegate updates
When a non-delegate attempts to update the identity document, their action will be rejected. Provide a better error, along with a hint, to the non-delegate.
This commit is contained in:
parent
60959f7e83
commit
b937a93892
|
|
@ -15,6 +15,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
`tags`, `notes`, `rad`, and `cobs`. The restriction is lifted, and the only
|
||||
references filtered out are `refs/tmp/heads` – used by `radicle-remote-helper`
|
||||
to create temporary patches.
|
||||
- The `rad id` command will provide a better error message when a non-delegate
|
||||
attempts to modify the identity document.
|
||||
|
||||
## Fixed Bugs
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
Alice has created a new repository `rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji` with only herself as the sole delegate. After Bob has cloned it, let's ensure he can't add himself as a delegate too:
|
||||
|
||||
``` ~bob (fail)
|
||||
$ rad id update --repo rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --title "Add myself!" --delegate did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --no-confirm
|
||||
✗ Error: did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk is not a delegate, and only delegates are allowed to create a revision
|
||||
✗ Hint: bob (you) is attempting to modify the identity document but is not a delegate!
|
||||
```
|
||||
|
||||
|
|
@ -19,6 +19,7 @@ use crate::git::unified_diff::Encode as _;
|
|||
use crate::git::Rev;
|
||||
use crate::terminal as term;
|
||||
use crate::terminal::args::Error;
|
||||
use crate::terminal::format::Author;
|
||||
use crate::terminal::patch::Message;
|
||||
|
||||
pub use args::Args;
|
||||
|
|
@ -193,7 +194,14 @@ pub fn run(args: Args, ctx: impl term::Context) -> anyhow::Result<()> {
|
|||
return Ok(());
|
||||
}
|
||||
let signer = term::signer(&profile)?;
|
||||
let revision = update(title, description, proposal, &mut identity, &signer)?;
|
||||
let revision = update(
|
||||
title,
|
||||
description,
|
||||
proposal,
|
||||
&mut identity,
|
||||
&signer,
|
||||
&profile,
|
||||
)?;
|
||||
|
||||
if revision.is_accepted() && revision.parent == Some(current.id) {
|
||||
// Update the canonical head to point to the latest accepted revision.
|
||||
|
|
@ -420,13 +428,16 @@ fn update<R, G>(
|
|||
doc: Doc,
|
||||
current: &mut IdentityMut<R>,
|
||||
signer: &Device<G>,
|
||||
profile: &Profile,
|
||||
) -> anyhow::Result<Revision>
|
||||
where
|
||||
R: WriteRepository + cob::Store<Namespace = NodeId>,
|
||||
G: crypto::signature::Signer<crypto::Signature>,
|
||||
{
|
||||
if let Some((title, description)) = edit_title_description(title, description)? {
|
||||
let id = current.update(title, description, &doc, signer)?;
|
||||
let id = current
|
||||
.update(title, description, &doc, signer)
|
||||
.map_err(|e| on_identity_err(e, profile))?;
|
||||
let revision = current
|
||||
.revision(&id)
|
||||
.ok_or(anyhow!("update failed: revision {id} is missing"))?;
|
||||
|
|
@ -437,6 +448,46 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn on_identity_err(e: identity::Error, profile: &Profile) -> anyhow::Error {
|
||||
let e = anyhow::Error::from(e);
|
||||
|
||||
e.chain()
|
||||
.find_map(|c| c.downcast_ref::<identity::ApplyError>())
|
||||
.map(|e| on_apply_err(e, profile))
|
||||
.unwrap_or(e)
|
||||
}
|
||||
|
||||
fn on_apply_err(e: &identity::ApplyError, profile: &Profile) -> anyhow::Error {
|
||||
match e {
|
||||
e @ identity::ApplyError::NonDelegateUnauthorized { author, .. } => {
|
||||
let nid = NodeId::from(*author);
|
||||
let labels = Author::new(&nid, profile, false).labels();
|
||||
|
||||
Error::with_hint(
|
||||
anyhow!(e.to_string()),
|
||||
format!(
|
||||
"{} {} is attempting to modify the identity document but is not a delegate!",
|
||||
labels.0, labels.1
|
||||
),
|
||||
)
|
||||
.into()
|
||||
}
|
||||
e @ radicle::cob::identity::ApplyError::Missing(_)
|
||||
| e @ radicle::cob::identity::ApplyError::Init(_)
|
||||
| e @ radicle::cob::identity::ApplyError::InvalidSignature(..)
|
||||
| e @ radicle::cob::identity::ApplyError::NotAuthorized
|
||||
| e @ radicle::cob::identity::ApplyError::MissingParent
|
||||
| e @ radicle::cob::identity::ApplyError::DuplicateVerdict
|
||||
| e @ radicle::cob::identity::ApplyError::UnexpectedState
|
||||
| e @ radicle::cob::identity::ApplyError::Redacted
|
||||
| e @ radicle::cob::identity::ApplyError::DocUnchanged
|
||||
| e @ radicle::cob::identity::ApplyError::Git(_)
|
||||
| e @ radicle::cob::identity::ApplyError::Doc(_) => {
|
||||
anyhow!(e.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn print_diff(
|
||||
previous: Option<&RevisionId>,
|
||||
current: &RevisionId,
|
||||
|
|
|
|||
|
|
@ -602,6 +602,56 @@ fn rad_id_multi_delegate() {
|
|||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rad_id_unauthorized_delegate() {
|
||||
let mut environment = Environment::new();
|
||||
let alice = environment.node("alice");
|
||||
let bob = environment.node("bob");
|
||||
let acme = RepoId::from_str("z42hL2jL4XNk6K8oHQaSWfMgCL7ji").unwrap();
|
||||
|
||||
environment.repository(&alice);
|
||||
|
||||
test(
|
||||
"examples/rad-init.md",
|
||||
environment.work(&alice),
|
||||
Some(&alice.home),
|
||||
[],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut alice = alice.spawn();
|
||||
let mut bob = bob.spawn();
|
||||
|
||||
// Alice sets up the seed
|
||||
alice.handle.seed(acme, Scope::Followed).unwrap();
|
||||
|
||||
bob.connect(&alice).converge([&alice]);
|
||||
bob.rad(
|
||||
"clone",
|
||||
&[acme.to_string().as_str()],
|
||||
environment.work(&bob),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
formula(
|
||||
&environment.tempdir(),
|
||||
"examples/rad-id-unauthorized-delegate.md",
|
||||
)
|
||||
.unwrap()
|
||||
.home(
|
||||
"alice",
|
||||
environment.work(&alice),
|
||||
[("RAD_HOME", alice.home.path().display())],
|
||||
)
|
||||
.home(
|
||||
"bob",
|
||||
environment.work(&bob),
|
||||
[("RAD_HOME", bob.home.path().display())],
|
||||
)
|
||||
.run()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore = "slow"]
|
||||
fn rad_id_collaboration() {
|
||||
|
|
|
|||
Loading…
Reference in New Issue