cob: Remove `author` from `Entry`
It was no longer used. Signed-off-by: Alexis Sellier <alexis@radicle.xyz>
This commit is contained in:
parent
5a47023ac0
commit
dd7052926a
|
|
@ -28,7 +28,6 @@ pub mod error {
|
|||
use thiserror::Error;
|
||||
|
||||
use crate::signatures::error::Signatures;
|
||||
use crate::trailers;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Create {
|
||||
|
|
@ -69,8 +68,6 @@ pub mod error {
|
|||
#[error("the 'change' found at '{0}' has more than one signature")]
|
||||
TooManySignatures(Oid),
|
||||
#[error(transparent)]
|
||||
AuthorTrailer(#[from] trailers::error::InvalidAuthorTrailer),
|
||||
#[error(transparent)]
|
||||
ResourceTrailer(#[from] super::trailers::error::InvalidResourceTrailer),
|
||||
#[error("non utf-8 characters in commit message")]
|
||||
Utf8(#[from] FromUtf8Error),
|
||||
|
|
@ -84,13 +81,11 @@ impl change::Storage for git2::Repository {
|
|||
type LoadError = error::Load;
|
||||
|
||||
type ObjectId = Oid;
|
||||
type Author = Oid;
|
||||
type Resource = Oid;
|
||||
type Signatures = Signature;
|
||||
|
||||
fn create<Signer>(
|
||||
&self,
|
||||
author: Option<Self::Author>,
|
||||
resource: Self::Resource,
|
||||
signer: &Signer,
|
||||
spec: store::Create<Self::ObjectId>,
|
||||
|
|
@ -119,20 +114,11 @@ impl change::Storage for git2::Repository {
|
|||
Signature::from((*key, sig))
|
||||
};
|
||||
|
||||
let id = write_commit(
|
||||
self,
|
||||
author,
|
||||
resource,
|
||||
tips,
|
||||
message,
|
||||
signature.clone(),
|
||||
tree,
|
||||
)?;
|
||||
let id = write_commit(self, resource, tips, message, signature.clone(), tree)?;
|
||||
Ok(Change {
|
||||
id,
|
||||
revision: revision.into(),
|
||||
signature,
|
||||
author,
|
||||
resource,
|
||||
manifest,
|
||||
contents,
|
||||
|
|
@ -141,7 +127,7 @@ impl change::Storage for git2::Repository {
|
|||
|
||||
fn load(&self, id: Self::ObjectId) -> Result<Change, Self::LoadError> {
|
||||
let commit = Commit::read(self, id.into())?;
|
||||
let (author, resource) = parse_trailers(commit.trailers())?;
|
||||
let resource = parse_resource_trailer(commit.trailers())?;
|
||||
let mut signatures = Signatures::try_from(&commit)?
|
||||
.into_iter()
|
||||
.collect::<Vec<_>>();
|
||||
|
|
@ -160,7 +146,6 @@ impl change::Storage for git2::Repository {
|
|||
id,
|
||||
revision: tree.id().into(),
|
||||
signature: signature.into(),
|
||||
author,
|
||||
resource,
|
||||
manifest,
|
||||
contents,
|
||||
|
|
@ -168,26 +153,21 @@ impl change::Storage for git2::Repository {
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_trailers<'a>(
|
||||
mut trailers: impl Iterator<Item = &'a OwnedTrailer>,
|
||||
) -> Result<(Option<Oid>, Oid), error::Load> {
|
||||
let (author, resource) = trailers.try_fold((None, None), |(author, resource), trailer| {
|
||||
match trailers::AuthorCommitTrailer::try_from(trailer) {
|
||||
Ok(trailer) => Ok((Some(trailer.oid().into()), resource)),
|
||||
Err(err) => match err {
|
||||
trailers::error::InvalidAuthorTrailer::NoTrailer
|
||||
| trailers::error::InvalidAuthorTrailer::NoValue => Ok((author, resource)),
|
||||
trailers::error::InvalidAuthorTrailer::WrongToken => {
|
||||
let resource = trailers::ResourceCommitTrailer::try_from(trailer)?;
|
||||
Ok((author, Some(resource.oid().into())))
|
||||
}
|
||||
err => Err(error::Load::from(err)),
|
||||
},
|
||||
fn parse_resource_trailer<'a>(
|
||||
trailers: impl Iterator<Item = &'a OwnedTrailer>,
|
||||
) -> Result<Oid, error::Load> {
|
||||
for trailer in trailers {
|
||||
match trailers::ResourceCommitTrailer::try_from(trailer) {
|
||||
Err(trailers::error::InvalidResourceTrailer::WrongToken) => {
|
||||
continue;
|
||||
}
|
||||
Err(err) => return Err(err.into()),
|
||||
Ok(resource) => return Ok(resource.oid().into()),
|
||||
}
|
||||
})?;
|
||||
let resource = resource
|
||||
.ok_or_else(|| error::Load::from(trailers::error::InvalidResourceTrailer::NoTrailer))?;
|
||||
Ok((author, resource))
|
||||
}
|
||||
Err(error::Load::from(
|
||||
trailers::error::InvalidResourceTrailer::NoTrailer,
|
||||
))
|
||||
}
|
||||
|
||||
fn load_manifest(
|
||||
|
|
@ -223,7 +203,6 @@ fn load_contents(
|
|||
|
||||
fn write_commit<O>(
|
||||
repo: &git2::Repository,
|
||||
author: Option<O>,
|
||||
resource: O,
|
||||
tips: Vec<O>,
|
||||
message: String,
|
||||
|
|
@ -233,16 +212,12 @@ fn write_commit<O>(
|
|||
where
|
||||
O: AsRef<git2::Oid>,
|
||||
{
|
||||
let author = author.map(|author| *author.as_ref());
|
||||
let resource = *resource.as_ref();
|
||||
|
||||
let mut parents = tips.iter().map(|o| *o.as_ref()).collect::<Vec<_>>();
|
||||
parents.push(resource);
|
||||
parents.extend(author);
|
||||
|
||||
let mut trailers: Vec<OwnedTrailer> =
|
||||
vec![trailers::ResourceCommitTrailer::from(resource).into()];
|
||||
trailers.extend(author.map(|author| trailers::AuthorCommitTrailer::from(author).into()));
|
||||
let trailers: Vec<OwnedTrailer> = vec![trailers::ResourceCommitTrailer::from(resource).into()];
|
||||
|
||||
{
|
||||
let author = repo.signature()?;
|
||||
|
|
|
|||
|
|
@ -13,4 +13,4 @@ use crate::signatures::Signature;
|
|||
/// A single change in the change graph. The layout of changes in the repository
|
||||
/// is specified in the RFC (docs/rfc/0662-collaborative-objects.adoc)
|
||||
/// under "Change Commits".
|
||||
pub type Change = store::Change<Oid, Oid, Oid, Signature>;
|
||||
pub type Change = store::Change<Oid, Oid, Signature>;
|
||||
|
|
|
|||
|
|
@ -14,21 +14,16 @@ pub trait Storage {
|
|||
type LoadError: Error + Send + Sync + 'static;
|
||||
|
||||
type ObjectId;
|
||||
type Author;
|
||||
type Resource;
|
||||
type Signatures;
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn create<Signer>(
|
||||
&self,
|
||||
author: Option<Self::Author>,
|
||||
authority: Self::Resource,
|
||||
signer: &Signer,
|
||||
spec: Create<Self::ObjectId>,
|
||||
) -> Result<
|
||||
Change<Self::Author, Self::Resource, Self::ObjectId, Self::Signatures>,
|
||||
Self::CreateError,
|
||||
>
|
||||
) -> Result<Change<Self::Resource, Self::ObjectId, Self::Signatures>, Self::CreateError>
|
||||
where
|
||||
Signer: crypto::Signer;
|
||||
|
||||
|
|
@ -36,10 +31,7 @@ pub trait Storage {
|
|||
fn load(
|
||||
&self,
|
||||
id: Self::ObjectId,
|
||||
) -> Result<
|
||||
Change<Self::Author, Self::Resource, Self::ObjectId, Self::Signatures>,
|
||||
Self::LoadError,
|
||||
>;
|
||||
) -> Result<Change<Self::Resource, Self::ObjectId, Self::Signatures>, Self::LoadError>;
|
||||
}
|
||||
|
||||
pub struct Create<Id> {
|
||||
|
|
@ -51,7 +43,7 @@ pub struct Create<Id> {
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Change<Author, Resource, Id, Signature> {
|
||||
pub struct Change<Resource, Id, Signature> {
|
||||
/// The content address of the `Change` itself.
|
||||
pub id: Id,
|
||||
/// The content address of the tree of the `Change`.
|
||||
|
|
@ -59,9 +51,6 @@ pub struct Change<Author, Resource, Id, Signature> {
|
|||
/// The cryptographic signature(s) and their public keys of the
|
||||
/// authors.
|
||||
pub signature: Signature,
|
||||
/// The author of this change. The `Author` is expected to be a
|
||||
/// content address to look up the identity of the author.
|
||||
pub author: Option<Author>,
|
||||
/// The parent resource that this change lives under. For example,
|
||||
/// this change could be for a patch of a project.
|
||||
pub resource: Resource,
|
||||
|
|
@ -72,7 +61,7 @@ pub struct Change<Author, Resource, Id, Signature> {
|
|||
pub contents: Contents,
|
||||
}
|
||||
|
||||
impl<Author, Resource, Id, S> fmt::Display for Change<Author, Resource, Id, S>
|
||||
impl<Resource, Id, S> fmt::Display for Change<Resource, Id, S>
|
||||
where
|
||||
Id: fmt::Display,
|
||||
{
|
||||
|
|
@ -81,15 +70,11 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<Author, Resource, Id, Signatures> Change<Author, Resource, Id, Signatures> {
|
||||
impl<Resource, Id, Signatures> Change<Resource, Id, Signatures> {
|
||||
pub fn id(&self) -> &Id {
|
||||
&self.id
|
||||
}
|
||||
|
||||
pub fn author(&self) -> &Option<Author> {
|
||||
&self.author
|
||||
}
|
||||
|
||||
pub fn typename(&self) -> &TypeName {
|
||||
&self.manifest.typename
|
||||
}
|
||||
|
|
@ -103,7 +88,7 @@ impl<Author, Resource, Id, Signatures> Change<Author, Resource, Id, Signatures>
|
|||
}
|
||||
}
|
||||
|
||||
impl<A, R, Id> Change<A, R, Id, signatures::Signatures>
|
||||
impl<R, Id> Change<R, Id, signatures::Signatures>
|
||||
where
|
||||
Id: AsRef<[u8]>,
|
||||
{
|
||||
|
|
@ -114,7 +99,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<A, R, Id> Change<A, R, Id, signatures::Signature>
|
||||
impl<R, Id> Change<R, Id, signatures::Signature>
|
||||
where
|
||||
Id: AsRef<[u8]>,
|
||||
{
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ impl ChangeGraph {
|
|||
oid: &ObjectId,
|
||||
) -> Option<ChangeGraph>
|
||||
where
|
||||
S: change::Storage<ObjectId = Oid, Author = Oid, Resource = Oid, Signatures = Signature>,
|
||||
S: change::Storage<ObjectId = Oid, Resource = Oid, Signatures = Signature>,
|
||||
{
|
||||
log::info!("loading object '{}' '{}'", typename, oid);
|
||||
let mut builder = GraphBuilder::default();
|
||||
|
|
@ -170,7 +170,6 @@ impl GraphBuilder {
|
|||
commit: object::Commit,
|
||||
change: Change,
|
||||
) -> impl Iterator<Item = (object::Commit, Oid)> + '_ {
|
||||
let author_commit = *change.author();
|
||||
let resource_commit = *change.resource();
|
||||
let commit_id = commit.id;
|
||||
if let Entry::Vacant(e) = self.node_indices.entry(commit_id) {
|
||||
|
|
@ -178,10 +177,7 @@ impl GraphBuilder {
|
|||
e.insert(ix);
|
||||
}
|
||||
commit.parents.into_iter().filter_map(move |parent| {
|
||||
if Some(parent.id) != author_commit
|
||||
&& parent.id != resource_commit
|
||||
&& !self.has_edge(parent.id, commit_id)
|
||||
{
|
||||
if parent.id != resource_commit && !self.has_edge(parent.id, commit_id) {
|
||||
Some((parent, commit_id))
|
||||
} else {
|
||||
None
|
||||
|
|
|
|||
|
|
@ -67,7 +67,6 @@ fn evaluate_change(
|
|||
Ok(history::Entry::new(
|
||||
*change.id(),
|
||||
change.signature.key,
|
||||
*change.author(),
|
||||
change.resource,
|
||||
child_commits.iter().cloned(),
|
||||
change.contents().clone(),
|
||||
|
|
|
|||
|
|
@ -42,7 +42,6 @@ impl History {
|
|||
pub(crate) fn new_from_root<Id>(
|
||||
id: Id,
|
||||
actor: PublicKey,
|
||||
author: Option<Oid>,
|
||||
resource: Oid,
|
||||
contents: Contents,
|
||||
) -> Self
|
||||
|
|
@ -53,7 +52,6 @@ impl History {
|
|||
let root_entry = Entry {
|
||||
id,
|
||||
actor,
|
||||
author,
|
||||
resource,
|
||||
children: vec![],
|
||||
contents,
|
||||
|
|
@ -120,7 +118,6 @@ impl History {
|
|||
&mut self,
|
||||
new_id: Id,
|
||||
new_actor: PublicKey,
|
||||
new_author: Option<Oid>,
|
||||
new_resource: Oid,
|
||||
new_contents: Contents,
|
||||
) where
|
||||
|
|
@ -131,7 +128,6 @@ impl History {
|
|||
let new_entry = Entry::new(
|
||||
new_id,
|
||||
new_actor,
|
||||
new_author,
|
||||
new_resource,
|
||||
std::iter::empty::<git2::Oid>(),
|
||||
new_contents,
|
||||
|
|
|
|||
|
|
@ -45,9 +45,6 @@ pub struct Entry {
|
|||
pub(super) id: EntryId,
|
||||
/// The actor that authored this entry.
|
||||
pub(super) actor: PublicKey,
|
||||
/// The content-address for this entry's author.
|
||||
/// TODO: This shouldn't be here?
|
||||
pub(super) author: Option<Oid>,
|
||||
/// The content-address for the resource this entry lives under.
|
||||
pub(super) resource: Oid,
|
||||
/// The child entries for this entry.
|
||||
|
|
@ -60,7 +57,6 @@ impl Entry {
|
|||
pub fn new<Id1, Id2, ChildIds>(
|
||||
id: Id1,
|
||||
actor: PublicKey,
|
||||
author: Option<Oid>,
|
||||
resource: Oid,
|
||||
children: ChildIds,
|
||||
contents: Contents,
|
||||
|
|
@ -73,7 +69,6 @@ impl Entry {
|
|||
Self {
|
||||
id: id.into(),
|
||||
actor,
|
||||
author,
|
||||
resource,
|
||||
children: children.into_iter().map(|id| id.into()).collect(),
|
||||
contents,
|
||||
|
|
@ -95,11 +90,6 @@ impl Entry {
|
|||
&self.actor
|
||||
}
|
||||
|
||||
/// The `Oid` of the author that made this change.
|
||||
pub fn author(&self) -> Option<&Oid> {
|
||||
self.author.as_ref()
|
||||
}
|
||||
|
||||
/// The contents of this change
|
||||
pub fn contents(&self) -> &Contents {
|
||||
&self.contents
|
||||
|
|
|
|||
|
|
@ -8,17 +8,10 @@ use git_ext::Oid;
|
|||
/// An [`Identity`] represents a content addressed identity
|
||||
/// (i.e. expected to be stored in a git backend).
|
||||
///
|
||||
/// It should have:
|
||||
/// * A delegate system
|
||||
/// * A content addressable identifier
|
||||
/// * A unique, stable identifier
|
||||
/// It should have a unique, stable, content addressable identifier.
|
||||
pub trait Identity {
|
||||
type Identifier;
|
||||
|
||||
/// Confirm that the given [`crypto::PublicKey`] is a delegate for
|
||||
/// the identity.
|
||||
fn is_delegate(&self, delegation: &crypto::PublicKey) -> bool;
|
||||
|
||||
/// Provide the content address for the given identity. This is
|
||||
/// expected to be the latest address for the identity at the time
|
||||
/// of use.
|
||||
|
|
|
|||
|
|
@ -61,11 +61,11 @@
|
|||
//! automerge document and deserialize into an application defined
|
||||
//! object.
|
||||
//!
|
||||
//! This traversal is also the point at which the [`Entry::author`]
|
||||
//! This traversal is also the point at which the [`Entry::actor`]
|
||||
//! and [`Entry::resource`] can be retrieved to apply any kind of
|
||||
//! filtering logic. For example, a specific `author`'s change may be
|
||||
//! filtering logic. For example, a specific `actor`'s change may be
|
||||
//! egregious, spouting terrible libel about Radicle. It is at this
|
||||
//! point that the `author`'s change can be filtered out from the
|
||||
//! point that the `actor`'s change can be filtered out from the
|
||||
//! final product of the traversal.
|
||||
//!
|
||||
//! [automerge]: https://automerge.org
|
||||
|
|
@ -132,7 +132,6 @@ where
|
|||
CreateError = git::change::error::Create,
|
||||
LoadError = git::change::error::Load,
|
||||
ObjectId = git_ext::Oid,
|
||||
Author = git_ext::Oid,
|
||||
Resource = git_ext::Oid,
|
||||
Signatures = Signature,
|
||||
>,
|
||||
|
|
|
|||
|
|
@ -8,9 +8,7 @@ use crate::Store;
|
|||
use super::*;
|
||||
|
||||
/// The metadata required for creating a new [`CollaborativeObject`].
|
||||
pub struct Create<Author> {
|
||||
/// The identity of the author for this object's first change.
|
||||
pub author: Option<Author>,
|
||||
pub struct Create {
|
||||
/// The type of history that will be used for this object.
|
||||
pub history_type: String,
|
||||
/// The CRDT history to initialize this object with.
|
||||
|
|
@ -21,7 +19,7 @@ pub struct Create<Author> {
|
|||
pub message: String,
|
||||
}
|
||||
|
||||
impl<Author> Create<Author> {
|
||||
impl Create {
|
||||
fn create_spec(&self) -> change::Create<git_ext::Oid> {
|
||||
change::Create {
|
||||
typename: self.typename.clone(),
|
||||
|
|
@ -51,46 +49,31 @@ impl<Author> Create<Author> {
|
|||
///
|
||||
/// The `args` are the metadata for this [`CollaborativeObject`]. See
|
||||
/// [`Create`] for further information.
|
||||
pub fn create<S, G, Author, Resource>(
|
||||
pub fn create<S, G, Resource>(
|
||||
storage: &S,
|
||||
signer: &G,
|
||||
resource: &Resource,
|
||||
identifier: &S::Identifier,
|
||||
args: Create<Author>,
|
||||
args: Create,
|
||||
) -> Result<CollaborativeObject, error::Create>
|
||||
where
|
||||
S: Store,
|
||||
G: crypto::Signer,
|
||||
Author: Identity,
|
||||
Author::Identifier: Clone + PartialEq,
|
||||
Resource: Identity,
|
||||
{
|
||||
let Create {
|
||||
author,
|
||||
ref contents,
|
||||
ref typename,
|
||||
..
|
||||
} = &args;
|
||||
|
||||
let content = match author {
|
||||
None => None,
|
||||
Some(author) => {
|
||||
if !author.is_delegate(signer.public_key()) {
|
||||
return Err(error::Create::SignerIsNotAuthor);
|
||||
} else {
|
||||
Some(author.content_id())
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let init_change = storage
|
||||
.create(content, resource.content_id(), signer, args.create_spec())
|
||||
.create(resource.content_id(), signer, args.create_spec())
|
||||
.map_err(error::Create::from)?;
|
||||
|
||||
let history = History::new_from_root(
|
||||
*init_change.id(),
|
||||
init_change.signature.key,
|
||||
content,
|
||||
resource.content_id(),
|
||||
contents.clone(),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -11,9 +11,7 @@ use crate::{
|
|||
use super::error;
|
||||
|
||||
/// The data required to update an object
|
||||
pub struct Update<Author> {
|
||||
/// The identity of the author for the update of this object.
|
||||
pub author: Option<Author>,
|
||||
pub struct Update {
|
||||
/// The type of history that will be used for this object.
|
||||
pub history_type: String,
|
||||
/// The CRDT changes to add to the object.
|
||||
|
|
@ -44,22 +42,19 @@ pub struct Update<Author> {
|
|||
///
|
||||
/// The `args` are the metadata for this [`CollaborativeObject`]
|
||||
/// udpate. See [`Update`] for further information.
|
||||
pub fn update<S, G, Resource, Author>(
|
||||
pub fn update<S, G, Resource>(
|
||||
storage: &S,
|
||||
signer: &G,
|
||||
resource: &Resource,
|
||||
identifier: &S::Identifier,
|
||||
args: Update<Author>,
|
||||
args: Update,
|
||||
) -> Result<CollaborativeObject, error::Update>
|
||||
where
|
||||
S: Store,
|
||||
G: crypto::Signer,
|
||||
Author: Identity,
|
||||
Author::Identifier: Clone + PartialEq,
|
||||
Resource: Identity,
|
||||
{
|
||||
let Update {
|
||||
author,
|
||||
ref typename,
|
||||
object_id,
|
||||
history_type,
|
||||
|
|
@ -67,18 +62,6 @@ where
|
|||
message,
|
||||
} = args;
|
||||
|
||||
let author = match author {
|
||||
None => None,
|
||||
Some(author) => {
|
||||
// TODO: Remove?
|
||||
if !author.is_delegate(signer.public_key()) {
|
||||
return Err(error::Update::SignerIsNotAuthor);
|
||||
} else {
|
||||
Some(author.content_id())
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let existing_refs = storage
|
||||
.objects(typename, &object_id)
|
||||
.map_err(|err| error::Update::Refs { err: Box::new(err) })?;
|
||||
|
|
@ -88,7 +71,6 @@ where
|
|||
.ok_or(error::Update::NoSuchObject)?;
|
||||
|
||||
let change = storage.create(
|
||||
author,
|
||||
resource.content_id(),
|
||||
signer,
|
||||
change::Create {
|
||||
|
|
@ -99,13 +81,9 @@ where
|
|||
message,
|
||||
},
|
||||
)?;
|
||||
object.history.extend(
|
||||
change.id,
|
||||
change.signature.key,
|
||||
author,
|
||||
change.resource,
|
||||
changes,
|
||||
);
|
||||
object
|
||||
.history
|
||||
.extend(change.id, change.signature.key, change.resource, changes);
|
||||
storage
|
||||
.update(identifier, typename, &object_id, &change)
|
||||
.map_err(|err| error::Update::Refs { err: Box::new(err) })?;
|
||||
|
|
|
|||
|
|
@ -53,52 +53,14 @@ impl Person {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn key(&self) -> crypto::PublicKey {
|
||||
self.payload.key
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &Name {
|
||||
&self.payload.name
|
||||
}
|
||||
|
||||
pub fn find_by_oid(
|
||||
repo: &git2::Repository,
|
||||
id: Oid,
|
||||
) -> Result<Option<Person>, storage::error::Identity> {
|
||||
match repo.find_commit(id.into()) {
|
||||
Ok(commit) => from_commit(repo, commit),
|
||||
Err(err) if err.code() == git2::ErrorCode::NotFound => Ok(None),
|
||||
Err(err) => Err(err.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn from_commit(
|
||||
repo: &git2::Repository,
|
||||
commit: git2::Commit,
|
||||
) -> Result<Option<Person>, storage::error::Identity> {
|
||||
let tree = commit.tree()?;
|
||||
let entry = tree
|
||||
.get_name("identity")
|
||||
.ok_or_else(|| storage::error::Identity::NotFound(tree.id().into()))?;
|
||||
let blob = match entry.to_object(repo)?.into_blob() {
|
||||
Ok(blob) => blob,
|
||||
Err(other) => return Err(storage::error::Identity::NotBlob(other.kind())),
|
||||
};
|
||||
let payload = serde_json::de::from_slice(blob.content())?;
|
||||
Ok(Some(Person {
|
||||
payload,
|
||||
content_id: commit.id().into(),
|
||||
}))
|
||||
}
|
||||
|
||||
impl Identity for Person {
|
||||
type Identifier = Urn;
|
||||
|
||||
fn is_delegate(&self, delegation: &crypto::PublicKey) -> bool {
|
||||
self.key() == *delegation
|
||||
}
|
||||
|
||||
fn content_id(&self) -> Oid {
|
||||
self.content_id
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,56 +70,14 @@ impl Project {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn delegates(&self) -> &BTreeSet<crypto::PublicKey> {
|
||||
&self.payload.delegates
|
||||
}
|
||||
|
||||
pub fn delegate_check(&self, person: &test::Person) -> bool {
|
||||
self.payload.delegates.contains(&person.key())
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &Name {
|
||||
&self.payload.name
|
||||
}
|
||||
|
||||
pub fn find_by_oid(
|
||||
repo: &git2::Repository,
|
||||
id: Oid,
|
||||
) -> Result<Option<Self>, storage::error::Identity> {
|
||||
match repo.find_commit(id.into()) {
|
||||
Ok(commit) => from_commit(repo, commit),
|
||||
Err(err) if err.code() == git2::ErrorCode::NotFound => Ok(None),
|
||||
Err(err) => Err(err.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn from_commit(
|
||||
repo: &git2::Repository,
|
||||
commit: git2::Commit,
|
||||
) -> Result<Option<Project>, storage::error::Identity> {
|
||||
let tree = commit.tree()?;
|
||||
let entry = tree
|
||||
.get_name("identity")
|
||||
.ok_or_else(|| storage::error::Identity::NotFound(tree.id().into()))?;
|
||||
let blob = match entry.to_object(repo)?.into_blob() {
|
||||
Ok(blob) => blob,
|
||||
Err(other) => return Err(storage::error::Identity::NotBlob(other.kind())),
|
||||
};
|
||||
let payload = serde_json::de::from_slice(blob.content())?;
|
||||
Ok(Some(Project {
|
||||
payload,
|
||||
content_id: commit.id().into(),
|
||||
}))
|
||||
}
|
||||
|
||||
impl Identity for RemoteProject {
|
||||
type Identifier = Urn;
|
||||
|
||||
fn is_delegate(&self, delegation: &crypto::PublicKey) -> bool {
|
||||
self.project.delegates().contains(delegation)
|
||||
}
|
||||
|
||||
fn content_id(&self) -> Oid {
|
||||
self.project.content_id
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,10 +21,6 @@ pub mod error {
|
|||
Json(#[from] serde_json::Error),
|
||||
#[error(transparent)]
|
||||
Git(#[from] git2::Error),
|
||||
#[error("'identity' was not a blob, found '{0:?}'")]
|
||||
NotBlob(Option<git2::ObjectType>),
|
||||
#[error("could not find 'identity' in the tree '{0}'")]
|
||||
NotFound(git_ext::Oid),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
|
|
@ -67,31 +63,29 @@ impl change::Storage for Storage {
|
|||
type LoadError = <git2::Repository as change::Storage>::LoadError;
|
||||
|
||||
type ObjectId = <git2::Repository as change::Storage>::ObjectId;
|
||||
type Author = <git2::Repository as change::Storage>::Author;
|
||||
type Resource = <git2::Repository as change::Storage>::Resource;
|
||||
type Signatures = <git2::Repository as change::Storage>::Signatures;
|
||||
|
||||
fn create<Signer>(
|
||||
&self,
|
||||
author: Option<Self::Author>,
|
||||
authority: Self::Resource,
|
||||
signer: &Signer,
|
||||
spec: change::Create<Self::ObjectId>,
|
||||
) -> Result<
|
||||
change::store::Change<Self::Author, Self::Resource, Self::ObjectId, Self::Signatures>,
|
||||
change::store::Change<Self::Resource, Self::ObjectId, Self::Signatures>,
|
||||
Self::CreateError,
|
||||
>
|
||||
where
|
||||
Signer: crypto::Signer,
|
||||
{
|
||||
self.as_raw().create(author, authority, signer, spec)
|
||||
self.as_raw().create(authority, signer, spec)
|
||||
}
|
||||
|
||||
fn load(
|
||||
&self,
|
||||
id: Self::ObjectId,
|
||||
) -> Result<
|
||||
change::store::Change<Self::Author, Self::Resource, Self::ObjectId, Self::Signatures>,
|
||||
change::store::Change<Self::Resource, Self::ObjectId, Self::Signatures>,
|
||||
Self::LoadError,
|
||||
> {
|
||||
self.as_raw().load(id)
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ fn roundtrip() {
|
|||
let proj = test::Project::new(&storage, "discworld", *signer.public_key()).unwrap();
|
||||
let proj = test::RemoteProject {
|
||||
project: proj,
|
||||
person: terry.clone(),
|
||||
person: terry,
|
||||
};
|
||||
let typename = "xyz.rad.issue".parse::<TypeName>().unwrap();
|
||||
let cob = create(
|
||||
|
|
@ -28,7 +28,6 @@ fn roundtrip() {
|
|||
&proj,
|
||||
&proj.identifier(),
|
||||
Create {
|
||||
author: Some(terry),
|
||||
history_type: "test".to_string(),
|
||||
contents: Vec::new(),
|
||||
typename: typename.clone(),
|
||||
|
|
@ -52,7 +51,7 @@ fn list_cobs() {
|
|||
let proj = test::Project::new(&storage, "discworld", *signer.public_key()).unwrap();
|
||||
let proj = test::RemoteProject {
|
||||
project: proj,
|
||||
person: terry.clone(),
|
||||
person: terry,
|
||||
};
|
||||
let typename = "xyz.rad.issue".parse::<TypeName>().unwrap();
|
||||
let issue_1 = create(
|
||||
|
|
@ -61,7 +60,6 @@ fn list_cobs() {
|
|||
&proj,
|
||||
&proj.identifier(),
|
||||
Create {
|
||||
author: Some(terry.clone()),
|
||||
history_type: "test".to_string(),
|
||||
contents: b"issue 1".to_vec(),
|
||||
typename: typename.clone(),
|
||||
|
|
@ -76,7 +74,6 @@ fn list_cobs() {
|
|||
&proj,
|
||||
&proj.identifier(),
|
||||
Create {
|
||||
author: Some(terry),
|
||||
history_type: "test".to_string(),
|
||||
contents: b"issue 2".to_vec(),
|
||||
typename: typename.clone(),
|
||||
|
|
@ -102,7 +99,7 @@ fn update_cob() {
|
|||
let proj = test::Project::new(&storage, "discworld", *signer.public_key()).unwrap();
|
||||
let proj = test::RemoteProject {
|
||||
project: proj,
|
||||
person: terry.clone(),
|
||||
person: terry,
|
||||
};
|
||||
let typename = "xyz.rad.issue".parse::<TypeName>().unwrap();
|
||||
let cob = create(
|
||||
|
|
@ -111,7 +108,6 @@ fn update_cob() {
|
|||
&proj,
|
||||
&proj.identifier(),
|
||||
Create {
|
||||
author: Some(terry.clone()),
|
||||
history_type: "test".to_string(),
|
||||
contents: Vec::new(),
|
||||
typename: typename.clone(),
|
||||
|
|
@ -130,7 +126,6 @@ fn update_cob() {
|
|||
&proj,
|
||||
&proj.identifier(),
|
||||
Update {
|
||||
author: Some(terry),
|
||||
changes: b"issue 1".to_vec(),
|
||||
history_type: "test".to_string(),
|
||||
object_id: *cob.id(),
|
||||
|
|
@ -158,11 +153,11 @@ fn traverse_cobs() {
|
|||
let proj = test::Project::new(&storage, "discworld", *terry_signer.public_key()).unwrap();
|
||||
let terry_proj = test::RemoteProject {
|
||||
project: proj.clone(),
|
||||
person: terry.clone(),
|
||||
person: terry,
|
||||
};
|
||||
let neil_proj = test::RemoteProject {
|
||||
project: proj,
|
||||
person: neil.clone(),
|
||||
person: neil,
|
||||
};
|
||||
let typename = "xyz.rad.issue".parse::<TypeName>().unwrap();
|
||||
let cob = create(
|
||||
|
|
@ -171,7 +166,6 @@ fn traverse_cobs() {
|
|||
&terry_proj,
|
||||
&terry_proj.identifier(),
|
||||
Create {
|
||||
author: Some(terry),
|
||||
contents: b"issue 1".to_vec(),
|
||||
history_type: "test".to_string(),
|
||||
typename: typename.clone(),
|
||||
|
|
@ -194,7 +188,6 @@ fn traverse_cobs() {
|
|||
&neil_proj,
|
||||
&neil_proj.identifier(),
|
||||
Update {
|
||||
author: Some(neil),
|
||||
changes: b"issue 2".to_vec(),
|
||||
history_type: "test".to_string(),
|
||||
object_id: *cob.id(),
|
||||
|
|
@ -206,16 +199,8 @@ fn traverse_cobs() {
|
|||
|
||||
// traverse over the history and filter by changes that were only authorized by terry
|
||||
let contents = updated.history().traverse(Vec::new(), |mut acc, entry| {
|
||||
let author = match entry.author() {
|
||||
Some(author) => test::Person::find_by_oid(storage.as_raw(), *author).unwrap(),
|
||||
None => None,
|
||||
};
|
||||
let project = test::Project::find_by_oid(storage.as_raw(), entry.resource()).unwrap();
|
||||
|
||||
if let (Some(author), Some(project)) = (author, project) {
|
||||
if project.delegate_check(&author) {
|
||||
acc.push(entry.contents().to_vec())
|
||||
}
|
||||
if entry.actor() == terry_signer.public_key() {
|
||||
acc.push(entry.contents().to_vec());
|
||||
}
|
||||
ControlFlow::Continue(acc)
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,20 +3,14 @@
|
|||
// This file is part of radicle-link, distributed under the GPLv3 with Radicle
|
||||
// Linking Exception. For full terms see the included LICENSE file.
|
||||
|
||||
mod author_commit {
|
||||
super::oid_trailer! {AuthorCommitTrailer, "Rad-Author"}
|
||||
}
|
||||
mod resource_identity {
|
||||
super::oid_trailer! {ResourceCommitTrailer, "Rad-Resource"}
|
||||
}
|
||||
|
||||
pub mod error {
|
||||
pub use super::author_commit::Error as InvalidAuthorTrailer;
|
||||
|
||||
pub use super::resource_identity::Error as InvalidResourceTrailer;
|
||||
}
|
||||
|
||||
pub use author_commit::AuthorCommitTrailer;
|
||||
pub use resource_identity::ResourceCommitTrailer;
|
||||
|
||||
/// A macro for generating boilerplate From and TryFrom impls for trailers which
|
||||
|
|
|
|||
|
|
@ -13,48 +13,7 @@ pub use common::*;
|
|||
use radicle_cob as cob;
|
||||
use radicle_git_ext::Oid;
|
||||
|
||||
use crate::{
|
||||
identity::{project::Identity, Did},
|
||||
node::NodeId,
|
||||
storage::git::Repository,
|
||||
};
|
||||
|
||||
/// The `Author` of a [`create`] or [`update`].
|
||||
///
|
||||
/// **Note**: `Author` implements [`identity::Identity`], but since it
|
||||
/// is not content-addressed, the [`identity::Identity::content_id`]
|
||||
/// returns [`git2::Oid::zero`]. This means that if the `author` is
|
||||
/// set in the updates the history entries for those changes will
|
||||
/// contain the zero `Oid` for the `author` field.
|
||||
pub struct Author {
|
||||
did: Did,
|
||||
}
|
||||
|
||||
impl From<Did> for Author {
|
||||
fn from(did: Did) -> Self {
|
||||
Self { did }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NodeId> for Author {
|
||||
fn from(node_id: NodeId) -> Self {
|
||||
Self {
|
||||
did: Did::from(node_id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl identity::Identity for Author {
|
||||
type Identifier = String;
|
||||
|
||||
fn is_delegate(&self, delegation: &crypto::PublicKey) -> bool {
|
||||
*self.did == *delegation
|
||||
}
|
||||
|
||||
fn content_id(&self) -> Oid {
|
||||
git2::Oid::zero().into()
|
||||
}
|
||||
}
|
||||
use crate::{identity::project::Identity, storage::git::Repository};
|
||||
|
||||
/// Create a new [`CollaborativeObject`].
|
||||
///
|
||||
|
|
@ -62,8 +21,7 @@ impl identity::Identity for Author {
|
|||
/// stored under.
|
||||
///
|
||||
/// The `signer` is used to cryptographically sign the changes made
|
||||
/// for this update. **Note** that the public key for the signer must
|
||||
/// match the key of the `Author` -- if it is set.
|
||||
/// for this update.
|
||||
///
|
||||
/// The `project` is used to store its content-address in the history
|
||||
/// of changes for the collaborative object.
|
||||
|
|
@ -74,7 +32,7 @@ pub fn create<G>(
|
|||
repository: &Repository,
|
||||
signer: &G,
|
||||
project: &Identity<Oid>,
|
||||
args: Create<Author>,
|
||||
args: Create,
|
||||
) -> Result<CollaborativeObject, error::Create>
|
||||
where
|
||||
G: crypto::Signer,
|
||||
|
|
@ -118,8 +76,7 @@ pub fn list(
|
|||
/// stored under.
|
||||
///
|
||||
/// The `signer` is used to cryptographically sign the changes made
|
||||
/// for this update. **Note** that the public key for the signer must
|
||||
/// match the key of the `Author` -- if it is set.
|
||||
/// for this update.
|
||||
///
|
||||
/// The `project` is used to store its content-address in the history
|
||||
/// of changes for the collaborative object.
|
||||
|
|
@ -130,7 +87,7 @@ pub fn update<G>(
|
|||
repository: &Repository,
|
||||
signer: &G,
|
||||
project: &Identity<Oid>,
|
||||
args: Update<Author>,
|
||||
args: Update,
|
||||
) -> Result<CollaborativeObject, error::Update>
|
||||
where
|
||||
G: crypto::Signer,
|
||||
|
|
|
|||
|
|
@ -103,7 +103,6 @@ where
|
|||
signer,
|
||||
&self.project,
|
||||
Update {
|
||||
author: Some(cob::Author::from(*signer.public_key())),
|
||||
object_id,
|
||||
history_type: HISTORY_TYPE.to_owned(),
|
||||
typename: T::type_name().clone(),
|
||||
|
|
@ -127,7 +126,6 @@ where
|
|||
signer,
|
||||
&self.project,
|
||||
Create {
|
||||
author: Some(cob::Author::from(*signer.public_key())),
|
||||
history_type: HISTORY_TYPE.to_owned(),
|
||||
typename: T::type_name().clone(),
|
||||
message: message.to_owned(),
|
||||
|
|
|
|||
|
|
@ -406,10 +406,6 @@ pub struct Identity<I> {
|
|||
impl radicle_cob::identity::Identity for Identity<Oid> {
|
||||
type Identifier = Oid;
|
||||
|
||||
fn is_delegate(&self, delegation: &crypto::PublicKey) -> bool {
|
||||
self.doc.delegates.iter().any(|d| d.matches(delegation))
|
||||
}
|
||||
|
||||
fn content_id(&self) -> Oid {
|
||||
self.current
|
||||
}
|
||||
|
|
|
|||
|
|
@ -681,13 +681,11 @@ impl change::Storage for Repository {
|
|||
type LoadError = <git2::Repository as change::Storage>::LoadError;
|
||||
|
||||
type ObjectId = <git2::Repository as change::Storage>::ObjectId;
|
||||
type Author = <git2::Repository as change::Storage>::Author;
|
||||
type Resource = <git2::Repository as change::Storage>::Resource;
|
||||
type Signatures = <git2::Repository as change::Storage>::Signatures;
|
||||
|
||||
fn create<Signer>(
|
||||
&self,
|
||||
author: Option<Self::Author>,
|
||||
authority: Self::Resource,
|
||||
signer: &Signer,
|
||||
spec: change::Create<Self::ObjectId>,
|
||||
|
|
@ -695,7 +693,7 @@ impl change::Storage for Repository {
|
|||
where
|
||||
Signer: crypto::Signer,
|
||||
{
|
||||
self.backend.create(author, authority, signer, spec)
|
||||
self.backend.create(authority, signer, spec)
|
||||
}
|
||||
|
||||
fn load(&self, id: Self::ObjectId) -> Result<cob::Change, Self::LoadError> {
|
||||
|
|
|
|||
Loading…
Reference in New Issue