Create and use capabilities
In this tutorial we'll create Write capabilities for communal and owned namespaces, and use them to create AuthorisedEntries.
Prerequisites
A basic knowledge of the Rust programming language and executing commands in the terminal will be helpful for completing this tutorial. Some of the steps below also require cargo to be installed.
Additionally, knowledge of the Area API would be helpful. If you're not yet familiar, please see our dedicated tutorial for groupings.
Setup
- Create a new directory on your filesystem and name it something like
caps. - Using your terminal, run
cargo initwithin the newly created directory. - After than, run
cargo add willow25 rand@0.8.0.
Communal capabilities
First we'll create a WriteCapability for a communal namespace.
Open src/main.rs, delete its contents, and enter the following:
use rand::rngs::OsRng;
use willow25::prelude::*;
fn main() {
let mut csprng = OsRng;
let (alfie_id, alfie_secret) =
randomly_generate_subspace(&mut csprng);
let communal_namespace_id =
NamespaceId::from_bytes(&[17; 32]);
// Create a new communal capability.
// To use it, we'll need Alfie's secret.
let communal_cap =
WriteCapability::new_communal(
communal_namespace_id.clone(),
alfie_id.clone(),
);
println!("A communal capability: {:#?}", communal_cap);
}In your terminal, run cargo run, and you should see the following output:
A communal capability: WriteCapability {
genesis: Communal(
CommunalGenesis {
access_mode: Write,
namespace_key: NamespaceId(
-1111111111111111111111111111111111111111111111111111111111111111,
),
user_key: SubspaceId(
e28e9da138838ebed5eabdbaa7d4ed8bfc10213be3c3df1b2cb38a7585e38957,
),
},
),
delegations: [],
}Authorising an entry
Next we'll use the WriteCapability we created and use it to turn an Entry into an AuthorisedEntry.
Make the following changes tosrc/main.rs:
use rand::rngs::OsRng;
use willow25::prelude::*;
fn main() {
let mut csprng = OsRng;
let (alfie_id, alfie_secret) =
randomly_generate_subspace(&mut csprng);
let communal_namespace_id =
NamespaceId::from_bytes(&[17; 32]);
// Create a new communal capability.
// To use it, we'll need Alfie's secret.
let communal_cap =
WriteCapability::new_communal(
communal_namespace_id.clone(),
alfie_id.clone(),
);
println!("A communal capability: {:#?}", communal_cap);
let entry_communal = Entry::builder()
.namespace_id(communal_namespace_id.clone())
.subspace_id(alfie_id.clone())
.path(path!("/ideas"))
.timestamp(12345)
.payload(b"chocolate with mustard")
.build().unwrap();
// Authorise the entry using the communal
// capability and Alfie's secret.
let communal_authed = entry_communal
.into_authorised_entry(
&communal_cap,
&alfie_secret,
);
assert!(communal_authed.is_ok());
println!("Entry for communal namespace was authorised!");
}In your terminal, run cargo run, and you should see the following output:
A communal capability: WriteCapability {
genesis: Communal(
CommunalGenesis {
access_mode: Write,
namespace_key: NamespaceId(
-1111111111111111111111111111111111111111111111111111111111111111,
),
user_key: SubspaceId(
e28e9da138838ebed5eabdbaa7d4ed8bfc10213be3c3df1b2cb38a7585e38957,
),
},
),
delegations: [],
}
Entry for communal namespace was authorised!Owned capabilities and delegation
Finally we'll create a WriteCapability for an owned namespace, delegate it, and use it to produce an AuthorisedEntry.
Make the following changes tosrc/main.rs:
use rand::rngs::OsRng;
use willow25::prelude::*;
fn main() {
let mut csprng = OsRng;
let (alfie_id, alfie_secret) =
randomly_generate_subspace(&mut csprng);
let communal_namespace_id =
NamespaceId::from_bytes(&[17; 32]);
// Create a new communal capability.
// To use it, we'll need Alfie's secret.
let communal_cap =
WriteCapability::new_communal(
communal_namespace_id.clone(),
alfie_id.clone(),
);
println!("A communal capability: {:#?}", communal_cap);
let entry_communal = Entry::builder()
.namespace_id(communal_namespace_id.clone())
.subspace_id(alfie_id.clone())
.path(path!("/ideas"))
.timestamp(12345)
.payload(b"chocolate with mustard")
.build().unwrap();
// Authorise the entry using the communal
// capability and Alfie's secret.
let communal_authed = entry_communal
.into_authorised_entry(
&communal_cap,
&alfie_secret,
);
assert!(communal_authed.is_ok());
println!("Entry for communal namespace was authorised!");
// Create the keypair of an owned namespace.
let (owned_namespace_id, namespace_secret) =
randomly_generate_owned_namespace(&mut csprng);
// Create a new owned capability by using
// the namespace secret.
// To delegate it, we'll need Alfie's secret.
let mut owned_cap = WriteCapability::new_owned(&namespace_secret, alfie_id);
// Create a keypair for Betty.
let (betty_id, betty_secret) =
randomly_generate_subspace(&mut csprng);
// Delegate our owned cap to Betty,
// restricting her to her own subspace.
// To use it, we'll need Betty's secret.
owned_cap.delegate(
&alfie_secret,
Area::new_subspace_area(betty_id.clone()),
betty_id.clone(),
);
println!(
"A delegated owned capability: {:#?}",
owned_cap,
);
let entry_owned = Entry::builder()
.namespace_id(owned_namespace_id.clone())
.subspace_id(betty_id)
.path(path!("/blog"))
.timestamp(45689)
.payload(b"worried about alfie...")
.build().unwrap();
// Authorise the entry using the owned
// capability and Betty's secret.
let owned_authed = entry_owned
.into_authorised_entry(&owned_cap, &betty_secret);
assert!(owned_authed.is_ok());
println!("Entry for owned namespace was authorised!")
}In your terminal, run cargo run, and you should see the following output:
A communal capability: WriteCapability {
genesis: Communal(
CommunalGenesis {
access_mode: Write,
namespace_key: NamespaceId(
-1111111111111111111111111111111111111111111111111111111111111111,
),
user_key: SubspaceId(
e28e9da138838ebed5eabdbaa7d4ed8bfc10213be3c3df1b2cb38a7585e38957,
),
},
),
delegations: [],
}
Entry for communal namespace was authorised!
A delegated owned capability: WriteCapability {
genesis: Owned(
OwnedGenesis {
access_mode: Write,
namespace_key: NamespaceId(
-7e9f5c443cd2b20e3cda33f1e9deb614f8a5215526cd84a69b34b8254f458011,
),
user_key: SubspaceId(
e28e9da138838ebed5eabdbaa7d4ed8bfc10213be3c3df1b2cb38a7585e38957,
),
initial_authorisation: ed25519::Signature {
R: 0x9f88df74d7b9218ad350c311df15b18a5d5918e67a5d0cf449240ab2ba46ee19,
s: 0x611df642f680487ae3b86dfbe6ca7521f7d0a863a73cae6f489bcb03c4823205,
},
},
),
delegations: [
Delegation {
area: Area {
subspace: Some(
SubspaceId(
7f010d66a791de15d84c3d8f6973ba4fcf8581874b497b036d1b20d46fd7d60f,
),
),
path: Path(
<empty>,
),
times: Timestamp(
0,
)..,
},
user: SubspaceId(
7f010d66a791de15d84c3d8f6973ba4fcf8581874b497b036d1b20d46fd7d60f,
),
signature: ed25519::Signature {
R: 0x4fca226c8b31334a260225e51f7c932ebfbe4404b8c9d8d889914059648a0f6f,
s: 0x179e00882bddea4d658cbc33079cbec2e5254829c00b46baa3043ecbba78740f,
},
},
],
}
Entry for owned namespace was authorised!Summary
In this tutorial, we explored Willow’25's authorisation APIs:
- We created an communal WriteCapability.
- We then used it to turn an Entry into an AuthorisedEntry.
- Finally, we created an owned WriteCapability, delegated it, and used it to turn an Entry into an AuthorisedEntry