Adding a subcommand to person/project to list the delegates of the identity. Published-as: https://github.com/FintanH/radicle-link/tree/patches/cli%2Flnk-identities-delegates%2Fv1 Published-as: urn: rad:git:hnrkxafojjsz4m55qxbwigh1z8sdt7mai81gy peer: hyytgji3qmjmq86ti4b3idgi1wjexowp9jfxmdosreu7qn4t77589e ref: patches/cli/lnk-identities-delegates/v1 Fintan Halpenny (2): lnk-identities: person/project delegates lnk-identities: remove placeholder review function cli/lnk-identities/src/cli/args.rs | 26 ++++++++++++++++++++++ cli/lnk-identities/src/cli/eval/person.rs | 8 +++++++ cli/lnk-identities/src/cli/eval/project.rs | 24 ++++++++++++++++++++ cli/lnk-identities/src/person.rs | 16 +++++++++---- cli/lnk-identities/src/project.rs | 20 +++++++++++++++++ 5 files changed, 90 insertions(+), 4 deletions(-) -- 2.31.1
radicle-link/patches/nixos-latest.yml: FAILED in 21m2s [lnk identities list delegates][0] from [Fintan Halpenny][1] [0]: https://lists.sr.ht/~radicle-link/dev/patches/33671 [1]: mailto:fintan.halpenny@gmail.com ✗ #797373 FAILED radicle-link/patches/nixos-latest.yml https://builds.sr.ht/~radicle-link/job/797373
Copy & paste the following snippet into your terminal to import this patchset into git:
curl -s https://lists.sr.ht/~radicle-link/dev/patches/33671/mbox | git am -3Learn more about email & git
A feature lacking from the `lnk identities` CLI is the ability to list delegates of an identity. This *could* be derived from the output of `tracked`. A dedicated subcommand is more convenient for this specific output and is easier to remember. Add a `delegates` subcommand to `person` and `project` that outputs the delegates of a specific, verified identity. In the case of a project it outputs an object with `"direct"` and `"indirect"` keys -- corresponding to `PeerId`s and `Urn`s respectively. In the case of a person it outputs a list of `PeerId`s. Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com> --- cli/lnk-identities/src/cli/args.rs | 26 ++++++++++++++++++++++ cli/lnk-identities/src/cli/eval/person.rs | 8 +++++++ cli/lnk-identities/src/cli/eval/project.rs | 24 ++++++++++++++++++++ cli/lnk-identities/src/person.rs | 12 ++++++++++ cli/lnk-identities/src/project.rs | 20 +++++++++++++++++ 5 files changed, 90 insertions(+) diff --git a/cli/lnk-identities/src/cli/args.rs b/cli/lnk-identities/src/cli/args.rs index d790f53e..67a86dd6 100644 --- a/cli/lnk-identities/src/cli/args.rs +++ b/cli/lnk-identities/src/cli/args.rs @@ -110,6 +110,7 @@ pub mod project { List(List), Update(Update), Checkout(Checkout), + Delegates(Delegates), Diff(Diff), Accept(Accept), Tracked(Tracked), @@ -267,6 +268,18 @@ pub mod project { pub peer: Option<PeerId>, } + /// list the delegates of the Radicle project + #[derive(Debug, Parser)] + pub struct Delegates { + /// the Radicle URN of the project + #[clap(long)] + pub urn: Urn, + + /// the peer's version of the project + #[clap(long)] + pub peer: Option<PeerId>, + } + /// review the difference between the local Radicle project and a peer's #[derive(Debug, Parser)] pub struct Diff { @@ -322,6 +335,7 @@ pub mod person { List(List), Update(Update), Checkout(Checkout), + Delegates(Delegates), Diff(Diff), Accept(Accept), Tracked(Tracked), @@ -455,6 +469,18 @@ pub mod person { pub peer: Option<PeerId>, } + /// list the delegates of the Radicle person + #[derive(Debug, Parser)] + pub struct Delegates { + /// the Radicle URN of the person + #[clap(long)] + pub urn: Urn, + + /// the peer's version of the person + #[clap(long)] + pub peer: Option<PeerId>, + } + /// review the difference between the local Radicle person and a peer's #[derive(Debug, Parser)] pub struct Diff { diff --git a/cli/lnk-identities/src/cli/eval/person.rs b/cli/lnk-identities/src/cli/eval/person.rs index 1bc39b00..43046f59 100644 --- a/cli/lnk-identities/src/cli/eval/person.rs +++ b/cli/lnk-identities/src/cli/eval/person.rs @@ -46,6 +46,7 @@ pub fn eval(profile: &Profile, sock: SshAuthSock, opts: Options) -> anyhow::Resu eval_accept(profile, sock, urn, peer, force)? }, Options::Tracked(Tracked { urn }) => eval_tracked(profile, urn)?, + Options::Delegates(Delegates { urn, peer }) => eval_delegates(profile, urn, peer)?, } Ok(()) @@ -159,6 +160,13 @@ fn eval_tracked(profile: &Profile, urn: Urn) -> anyhow::Result<()> { Ok(()) } +fn eval_delegates(profile: &Profile, urn: Urn, peer: Option<PeerId>) -> anyhow::Result<()> { + let storage = storage::read_only(profile)?; + let delegates = person::delegates(&storage, &urn, peer)?; + println!("{}", serde_json::to_string(&delegates)?); + Ok(()) +} + fn eval_diff(profile: &Profile, urn: Urn, peer: PeerId) -> anyhow::Result<()> { let storage = storage::read_only(profile)?; diff(&storage, urn, peer)?; diff --git a/cli/lnk-identities/src/cli/eval/project.rs b/cli/lnk-identities/src/cli/eval/project.rs index b36d7b17..2a4e5a65 100644 --- a/cli/lnk-identities/src/cli/eval/project.rs +++ b/cli/lnk-identities/src/cli/eval/project.rs @@ -20,6 +20,7 @@ use librad::{ }, profile::Profile, PeerId, + PublicKey, }; use lnk_clib::{ keys::ssh::SshAuthSock, @@ -48,6 +49,7 @@ pub fn eval(profile: &Profile, sock: SshAuthSock, opts: Options) -> anyhow::Resu eval_accept(profile, sock, urn, peer, force)? }, Options::Tracked(Tracked { urn }) => eval_tracked(profile, urn)?, + Options::Delegates(Delegates { urn, peer }) => eval_delegates(profile, urn, peer)?, } Ok(()) @@ -164,6 +166,28 @@ fn eval_tracked(profile: &Profile, urn: Urn) -> anyhow::Result<()> { Ok(()) } +fn eval_delegates(profile: &Profile, urn: Urn, peer: Option<PeerId>) -> anyhow::Result<()> { + #[derive(serde::Serialize)] + struct Delegates { + indirect: Vec<Urn>, + direct: Vec<PublicKey>, + } + + let storage = storage::read_only(profile)?; + let mut delegates = Delegates { + indirect: Vec::new(), + direct: Vec::new(), + }; + for del in project::delegates(&storage, &urn, peer)? { + match del { + either::Either::Left(key) => delegates.direct.push(key), + either::Either::Right(urn) => delegates.indirect.push(urn), + } + } + println!("{}", serde_json::to_string(&delegates)?); + Ok(()) +} + fn eval_diff(profile: &Profile, urn: Urn, peer: PeerId) -> anyhow::Result<()> { let storage = storage::read_only(profile)?; diff(&storage, urn, peer)?; diff --git a/cli/lnk-identities/src/person.rs b/cli/lnk-identities/src/person.rs index 7e409018..f00d58a1 100644 --- a/cli/lnk-identities/src/person.rs +++ b/cli/lnk-identities/src/person.rs @@ -217,3 +217,15 @@ where let _guard = get(storage, urn)?.ok_or_else(|| identities::Error::NotFound(urn.clone()))?; Ok(identities::relations::tracked(storage, urn)?) } + +pub fn delegates<S, P>(storage: &S, urn: &Urn, peer: P) -> Result<Vec<PublicKey>, Error> +where + S: AsRef<ReadOnly>, + P: Into<Option<PeerId>>, +{ + let urn = Urn::try_from(Reference::rad_id(Namespace::from(urn)).with_remote(peer)).unwrap(); + let project = identities::person::verify(storage, &urn)? + .ok_or_else(|| identities::Error::NotFound(urn.clone()))?; + let delegations = project.delegations(); + Ok(delegations.iter().copied().collect()) +} diff --git a/cli/lnk-identities/src/project.rs b/cli/lnk-identities/src/project.rs index 2469d65a..dcff2cd5 100644 --- a/cli/lnk-identities/src/project.rs +++ b/cli/lnk-identities/src/project.rs @@ -25,6 +25,7 @@ use librad::{ }, paths::Paths, PeerId, + PublicKey, }; use crate::{ @@ -282,3 +283,22 @@ where let _guard = get(storage, urn)?.ok_or_else(|| identities::Error::NotFound(urn.clone()))?; Ok(identities::relations::tracked(storage, urn)?) } + +pub fn delegates<S, P>( + storage: &S, + urn: &Urn, + peer: P, +) -> Result<Vec<Either<PublicKey, Urn>>, Error> +where + S: AsRef<ReadOnly>, + P: Into<Option<PeerId>>, +{ + let urn = Urn::try_from(Reference::rad_id(Namespace::from(urn)).with_remote(peer)).unwrap(); + let project = identities::project::verify(storage, &urn)? + .ok_or_else(|| identities::Error::NotFound(urn.clone()))?; + let delegations = project.delegations(); + Ok(delegations + .iter() + .map(|del| del.map_left(|key| *key).map_right(|id| id.urn())) + .collect()) +} -- 2.31.1
A feature lacking from the `lnk identities` CLI is the ability to list delegates of an identity. This *could* be derived from the output of `tracked`. A dedicated subcommand is more convenient for this specific output and is easier to remember. Add a `delegates` subcommand to `person` and `project` that outputs the delegates of the specified, verified identity. The output format is a JSON list, each entry containing a JSON object: [ { "type": <type>, "payload": <payload> } ] Currently, the type can be either "direct" or "indirect" -- the payloads being a public key or an URN, respectively. This makes the output of both commands uniform while also leaving room for new types if they are ever added. Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com> --- cli/lnk-identities/src/cli.rs | 1 + cli/lnk-identities/src/cli/args.rs | 26 +++++++++++++++ cli/lnk-identities/src/cli/eval/person.rs | 13 ++++++++ cli/lnk-identities/src/cli/eval/project.rs | 20 ++++++++++- cli/lnk-identities/src/cli/fmt.rs | 7 ++++ cli/lnk-identities/src/cli/fmt/delegate.rs | 39 ++++++++++++++++++++++ cli/lnk-identities/src/person.rs | 12 +++++++ cli/lnk-identities/src/project.rs | 20 +++++++++++ 8 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 cli/lnk-identities/src/cli/fmt.rs create mode 100644 cli/lnk-identities/src/cli/fmt/delegate.rs diff --git a/cli/lnk-identities/src/cli.rs b/cli/lnk-identities/src/cli.rs index c4a9afc4..acbea1d1 100644 --- a/cli/lnk-identities/src/cli.rs +++ b/cli/lnk-identities/src/cli.rs @@ -8,3 +8,4 @@ mod main; pub use main::main; pub mod eval; +mod fmt; diff --git a/cli/lnk-identities/src/cli/args.rs b/cli/lnk-identities/src/cli/args.rs index d790f53e..67a86dd6 100644 --- a/cli/lnk-identities/src/cli/args.rs +++ b/cli/lnk-identities/src/cli/args.rs @@ -110,6 +110,7 @@ pub mod project { List(List), Update(Update), Checkout(Checkout), + Delegates(Delegates), Diff(Diff), Accept(Accept), Tracked(Tracked), @@ -267,6 +268,18 @@ pub mod project { pub peer: Option<PeerId>, } + /// list the delegates of the Radicle project + #[derive(Debug, Parser)] + pub struct Delegates { + /// the Radicle URN of the project + #[clap(long)] + pub urn: Urn, + + /// the peer's version of the project + #[clap(long)] + pub peer: Option<PeerId>, + } + /// review the difference between the local Radicle project and a peer's #[derive(Debug, Parser)] pub struct Diff { @@ -322,6 +335,7 @@ pub mod person { List(List), Update(Update), Checkout(Checkout), + Delegates(Delegates), Diff(Diff), Accept(Accept), Tracked(Tracked), @@ -455,6 +469,18 @@ pub mod person { pub peer: Option<PeerId>, } + /// list the delegates of the Radicle person + #[derive(Debug, Parser)] + pub struct Delegates { + /// the Radicle URN of the person + #[clap(long)] + pub urn: Urn, + + /// the peer's version of the person + #[clap(long)] + pub peer: Option<PeerId>, + } + /// review the difference between the local Radicle person and a peer's #[derive(Debug, Parser)] pub struct Diff { diff --git a/cli/lnk-identities/src/cli/eval/person.rs b/cli/lnk-identities/src/cli/eval/person.rs index 1bc39b00..fde873ac 100644 --- a/cli/lnk-identities/src/cli/eval/person.rs +++ b/cli/lnk-identities/src/cli/eval/person.rs @@ -46,6 +46,7 @@ pub fn eval(profile: &Profile, sock: SshAuthSock, opts: Options) -> anyhow::Resu eval_accept(profile, sock, urn, peer, force)? }, Options::Tracked(Tracked { urn }) => eval_tracked(profile, urn)?, + Options::Delegates(Delegates { urn, peer }) => eval_delegates(profile, urn, peer)?, } Ok(()) @@ -159,6 +160,18 @@ fn eval_tracked(profile: &Profile, urn: Urn) -> anyhow::Result<()> { Ok(()) } +fn eval_delegates(profile: &Profile, urn: Urn, peer: Option<PeerId>) -> anyhow::Result<()> { + use crate::cli::fmt::Delegate; + + let storage = storage::read_only(profile)?; + let delegates = person::delegates(&storage, &urn, peer)? + .into_iter() + .map(Delegate::direct) + .collect::<Vec<_>>(); + println!("{}", serde_json::to_string(&delegates)?); + Ok(()) +} + fn eval_diff(profile: &Profile, urn: Urn, peer: PeerId) -> anyhow::Result<()> { let storage = storage::read_only(profile)?; diff(&storage, urn, peer)?; diff --git a/cli/lnk-identities/src/cli/eval/project.rs b/cli/lnk-identities/src/cli/eval/project.rs index b36d7b17..9eb76a61 100644 --- a/cli/lnk-identities/src/cli/eval/project.rs +++ b/cli/lnk-identities/src/cli/eval/project.rs @@ -26,7 +26,12 @@ use lnk_clib::{ storage::{self, ssh}, }; -use crate::{cli::args::project::*, display, project, working_copy_dir::WorkingCopyDir}; +use crate::{ + cli::{self, args::project::*}, + display, + project, + working_copy_dir::WorkingCopyDir, +}; pub fn eval(profile: &Profile, sock: SshAuthSock, opts: Options) -> anyhow::Result<()> { match opts { @@ -48,6 +53,7 @@ pub fn eval(profile: &Profile, sock: SshAuthSock, opts: Options) -> anyhow::Resu eval_accept(profile, sock, urn, peer, force)? }, Options::Tracked(Tracked { urn }) => eval_tracked(profile, urn)?, + Options::Delegates(Delegates { urn, peer }) => eval_delegates(profile, urn, peer)?, } Ok(()) @@ -164,6 +170,18 @@ fn eval_tracked(profile: &Profile, urn: Urn) -> anyhow::Result<()> { Ok(()) } +fn eval_delegates(profile: &Profile, urn: Urn, peer: Option<PeerId>) -> anyhow::Result<()> { + use cli::fmt::Delegate; + + let storage = storage::read_only(profile)?; + let delegates = project::delegates(&storage, &urn, peer)? + .into_iter() + .map(|del| del.either(Delegate::direct, Delegate::indirect)) + .collect::<Vec<_>>(); + println!("{}", serde_json::to_string(&delegates)?); + Ok(()) +} + fn eval_diff(profile: &Profile, urn: Urn, peer: PeerId) -> anyhow::Result<()> { let storage = storage::read_only(profile)?; diff(&storage, urn, peer)?; diff --git a/cli/lnk-identities/src/cli/fmt.rs b/cli/lnk-identities/src/cli/fmt.rs new file mode 100644 index 00000000..fad6a961 --- /dev/null +++ b/cli/lnk-identities/src/cli/fmt.rs @@ -0,0 +1,7 @@ +// Copyright © 2022 The Radicle Link Contributors +// +// This file is part of radicle-link, distributed under the GPLv3 with Radicle +// Linking Exception. For full terms see the included LICENSE file. + +pub mod delegate; +pub use delegate::Delegate; diff --git a/cli/lnk-identities/src/cli/fmt/delegate.rs b/cli/lnk-identities/src/cli/fmt/delegate.rs new file mode 100644 index 00000000..93cd48a6 --- /dev/null +++ b/cli/lnk-identities/src/cli/fmt/delegate.rs @@ -0,0 +1,39 @@ +// Copyright © 2022 The Radicle Link Contributors +// +// This file is part of radicle-link, distributed under the GPLv3 with Radicle +// Linking Exception. For full terms see the included LICENSE file. + +use librad::{git::Urn, PublicKey}; +use serde::Serialize; + +#[non_exhaustive] +#[derive(Clone, Debug, Serialize)] +#[serde(tag = "type", content = "payload", rename_all = "camelCase")] +pub enum Delegate { + #[serde(rename = "direct")] + Key(PublicKey), + #[serde(rename = "indirect")] + Urn(Urn), +} + +impl From<PublicKey> for Delegate { + fn from(key: PublicKey) -> Self { + Self::Key(key) + } +} + +impl From<Urn> for Delegate { + fn from(urn: Urn) -> Self { + Self::Urn(urn) + } +} + +impl Delegate { + pub fn direct(key: PublicKey) -> Self { + key.into() + } + + pub fn indirect(urn: Urn) -> Self { + urn.into() + } +} diff --git a/cli/lnk-identities/src/person.rs b/cli/lnk-identities/src/person.rs index 7e409018..f00d58a1 100644 --- a/cli/lnk-identities/src/person.rs +++ b/cli/lnk-identities/src/person.rs @@ -217,3 +217,15 @@ where let _guard = get(storage, urn)?.ok_or_else(|| identities::Error::NotFound(urn.clone()))?; Ok(identities::relations::tracked(storage, urn)?) } + +pub fn delegates<S, P>(storage: &S, urn: &Urn, peer: P) -> Result<Vec<PublicKey>, Error> +where + S: AsRef<ReadOnly>, + P: Into<Option<PeerId>>, +{ + let urn = Urn::try_from(Reference::rad_id(Namespace::from(urn)).with_remote(peer)).unwrap(); + let project = identities::person::verify(storage, &urn)? + .ok_or_else(|| identities::Error::NotFound(urn.clone()))?;
Alex Good <alex@memoryandthought.me>Should this be `let person = ..`?
+ let delegations = project.delegations(); + Ok(delegations.iter().copied().collect()) +} diff --git a/cli/lnk-identities/src/project.rs b/cli/lnk-identities/src/project.rs index 2469d65a..dcff2cd5 100644 --- a/cli/lnk-identities/src/project.rs +++ b/cli/lnk-identities/src/project.rs @@ -25,6 +25,7 @@ use librad::{ }, paths::Paths, PeerId, + PublicKey, }; use crate::{ @@ -282,3 +283,22 @@ where let _guard = get(storage, urn)?.ok_or_else(|| identities::Error::NotFound(urn.clone()))?; Ok(identities::relations::tracked(storage, urn)?) } + +pub fn delegates<S, P>( + storage: &S, + urn: &Urn, + peer: P, +) -> Result<Vec<Either<PublicKey, Urn>>, Error> +where + S: AsRef<ReadOnly>, + P: Into<Option<PeerId>>, +{ + let urn = Urn::try_from(Reference::rad_id(Namespace::from(urn)).with_remote(peer)).unwrap(); + let project = identities::project::verify(storage, &urn)? + .ok_or_else(|| identities::Error::NotFound(urn.clone()))?; + let delegations = project.delegations(); + Ok(delegations + .iter() + .map(|del| del.map_left(|key| *key).map_right(|id| id.urn())) + .collect()) +} -- 2.31.1
Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com> --- cli/lnk-identities/src/person.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cli/lnk-identities/src/person.rs b/cli/lnk-identities/src/person.rs index f00d58a1..c4ae5a5b 100644 --- a/cli/lnk-identities/src/person.rs +++ b/cli/lnk-identities/src/person.rs @@ -205,10 +205,6 @@ where Ok(repo) } -pub fn review() { - todo!() -} - pub fn tracked<S>(storage: &S, urn: &Urn) -> Result<relations::Tracked, Error> where S: AsRef<ReadOnly>, -- 2.31.1
builds.sr.ht <builds@sr.ht>radicle-link/patches/nixos-latest.yml: FAILED in 21m2s [lnk identities list delegates][0] from [Fintan Halpenny][1] [0]: https://lists.sr.ht/~radicle-link/dev/patches/33671 [1]: mailto:fintan.halpenny@gmail.com ✗ #797373 FAILED radicle-link/patches/nixos-latest.yml https://builds.sr.ht/~radicle-link/job/797373
Signed-off-by: Fintan Halpenny <fintan.halpenny@gmail.com> --- cli/lnk-identities/src/person.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cli/lnk-identities/src/person.rs b/cli/lnk-identities/src/person.rs index f00d58a1..c4ae5a5b 100644 --- a/cli/lnk-identities/src/person.rs +++ b/cli/lnk-identities/src/person.rs @@ -205,10 +205,6 @@ where Ok(repo) } -pub fn review() { - todo!() -} - pub fn tracked<S>(storage: &S, urn: &Urn) -> Result<relations::Tracked, Error> where S: AsRef<ReadOnly>, -- 2.31.1