Skip to content

Commit

Permalink
cli did create web (#263)
Browse files Browse the repository at this point in the history
  • Loading branch information
Diane Huxley authored Jul 16, 2024
1 parent 70199ba commit b1ca55c
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 10 deletions.
84 changes: 78 additions & 6 deletions crates/web5/src/dids/methods/did_web/mod.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
mod resolver;

use super::{MethodError, Result};
use crate::dids::{
data_model::document::Document,
did::Did,
resolution::{
resolution_metadata::{ResolutionMetadata, ResolutionMetadataError},
resolution_result::ResolutionResult,
use crate::{
crypto::jwk::Jwk,
dids::{
data_model::{document::Document, verification_method::VerificationMethod},
did::Did,
resolution::{
resolution_metadata::{ResolutionMetadata, ResolutionMetadataError},
resolution_result::ResolutionResult,
},
},
};
use resolver::Resolver;
use url::Url;

#[derive(Clone)]
pub struct DidWeb {
Expand All @@ -18,6 +22,74 @@ pub struct DidWeb {
}

impl DidWeb {
pub fn new(domain: &str, public_jwk: Jwk) -> Result<Self> {
let domain = &domain.to_string();
let valid_url = if domain.starts_with("http://") || domain.starts_with("https://") {
let url = Url::parse(domain)
.map_err(|e| MethodError::DidCreationFailure(format!("url parse failure {}", e)))?;

// Ensure "http://" is only allowed for localhost or 127.0.0.1
if url.scheme() == "http"
&& !(url.host_str() == Some("localhost") || url.host_str() == Some("127.0.0.1"))
{
return Err(MethodError::DidCreationFailure(
"only https is allowed except for localhost or 127.0.0.1 with http".to_string(),
));
}

// Get the trimmed URL string without the scheme
let trimmed_url = url[url::Position::BeforeHost..].to_string();

// Remove the scheme
let normalized = if let Some(trimmed) = trimmed_url.strip_prefix("//") {
trimmed
} else {
&trimmed_url
};

normalized.to_string()
} else {
Url::parse(&format!("https://{}", domain))
.map_err(|e| MethodError::DidCreationFailure(format!("url parse failure {}", e)))?;
domain.clone()
};

let mut normalized = valid_url.clone();
if normalized.ends_with('/') {
normalized = normalized.trim_end_matches('/').to_string()
}
if normalized.ends_with("/did.json") {
normalized = normalized.trim_end_matches("/did.json").to_string()
}
if normalized.ends_with("/.well-known") {
normalized = normalized.trim_end_matches("/.well-known").to_string()
}

let encoded_domain = normalized.replace(':', "%3A");
let encoded_domain = encoded_domain.replace('/', ":");

let did = format!("did:web:{}", encoded_domain);

let verification_method = VerificationMethod {
id: format!("{}#key-0", did),
r#type: "JsonWebKey".to_string(),
controller: did.clone(),
public_key_jwk: public_jwk,
};

let document = Document {
id: did.clone(),
context: Some(vec!["https://www.w3.org/ns/did/v1".to_string()]),
verification_method: vec![verification_method],
..Default::default()
};

Ok(DidWeb {
did: Did::new(&did)?,
document,
})
}

pub async fn from_uri(uri: &str) -> Result<Self> {
let resolution_result = DidWeb::resolve(uri);
match resolution_result.document {
Expand Down
1 change: 1 addition & 0 deletions crates/web5_cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ chrono = { workspace = true }
clap = { version = "4.5.7", features = ["derive"] }
serde_json = { workspace = true }
web5 = { path = "../web5" }
url = "2.5.2"
uuid = { workspace = true }
25 changes: 22 additions & 3 deletions crates/web5_cli/src/dids/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::sync::Arc;
use web5::{
crypto::dsa::ed25519::{Ed25519Generator, Ed25519Signer},
dids::{
methods::{did_dht::DidDht, did_jwk::DidJwk},
methods::{did_dht::DidDht, did_jwk::DidJwk, did_web::DidWeb},
portable_did::PortableDid,
},
};
Expand All @@ -18,6 +18,10 @@ pub enum Commands {
},
Web {
domain: String,
#[arg(long)]
no_indent: bool,
#[arg(long)]
json_escape: bool,
},
Dht {
#[arg(long)]
Expand Down Expand Up @@ -63,8 +67,23 @@ impl Commands {

print_portable_did(portable_did, no_indent, json_escape);
}
Commands::Web { domain: _ } => {
println!("🚧 not currently supported 🚧");
Commands::Web {
domain,
no_indent,
json_escape,
} => {
let private_jwk = Ed25519Generator::generate();
let mut public_jwk = private_jwk.clone();
public_jwk.d = None;

let did_web = DidWeb::new(domain, public_jwk).unwrap();
let portable_did = PortableDid {
did_uri: did_web.did.uri,
document: did_web.document,
private_jwks: vec![private_jwk],
};

print_portable_did(portable_did, no_indent, json_escape)
}
Commands::Dht {
no_publish,
Expand Down
7 changes: 6 additions & 1 deletion docs/API_DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

**Last Updated** May 30, 2024

**Version** 2.0.0
**Version** 2.1.0

**[Custom DSL](./CUSTOM_DSL.md) Version**: 0.1.0

Expand Down Expand Up @@ -582,8 +582,13 @@ resolution_result = DidJwk.resolve(uri)

### `DidWeb`

> [!NOTE]
>
> The `CONSTRUCTOR(domain: string, public_jwk: Jwk)` does not publish the DID Document to a host, but merely creates the instance of the `did:web` in the local scope.
```pseudocode!
CLASS DidWeb
CONSTRUCTOR(domain: string, public_jwk: Jwk)
CONSTRUCTOR(uri: string)
STATIC METHOD resolve(uri: string): ResolutionResult
```
Expand Down

0 comments on commit b1ca55c

Please sign in to comment.