Skip to content

Commit

Permalink
feat(server): implement authentication for delete endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
jceb committed Jun 2, 2023
1 parent 64c8838 commit 64b1db5
Show file tree
Hide file tree
Showing 3 changed files with 314 additions and 31 deletions.
21 changes: 13 additions & 8 deletions TODO.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
# Doing

- [i] Implement CLI client in JS
- [i] Implement ownership for create, update and delete endpoints
- [x] I need to observe the owner's DID first in the configuration of the
server
- [x] I need to adjust the file format for creating DIDs
- [x] I need to process the authentication request as I did for the update
method
- [x] I need to add tests that verify the behavior
- [ ] Implement delete authentication

# Do

- [ ] Add logging to service
- [ ] Create a nix package for did-web-server
- [ ] Create release of the binary
- [ ] Create release of the binary in a docker container
- [ ] Create helm chart / kustomize configuration for hosting the chart
Expand All @@ -34,9 +28,20 @@
and authentications that were used for audit purposes
- [ ] Document that credentials should have a validUntil timestamp to mitigate
"replay attacks", i.e. the same request being sent twice
- [ ] Create additional admin endpoints:
- retrieve the list of all registered identities

# Done

- [x] Implement ownership for create, update and delete endpoints
- [x] I need to observe the owner's DID first in the configuration of the
server
- [x] I need to adjust the file format for creating DIDs
- [x] I need to process the authentication request as I did for the update
method
- [x] I need to add tests that verify the behavior
- [x] Implement delete authentication

- [x] Check SCC integration .. is everything correct?
- [x] Add cliff and gh as release tools
- [x] add server ownership via a DID
Expand Down
38 changes: 16 additions & 22 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,18 +128,15 @@ async fn create(
&presentation,
)
.await?;
println!("exit1");
let proof_parameters = ProofParameters::new(config, &id)?;
let (_result, _vc, did_doc) =
verify_presentation(config, proof_parameters, presentation).await?;
println!("exit2");

// INFO: unsure how to easily convert a CredentialSubject into a Document. Via json encoding? - not beautiful!!
let did_doc = serde_json::to_string(&did_doc)
.ok()
// .map(log("json"))
.and_then(|s| serde_json::from_str::<Document>(&s).ok());
println!("exit3");
match did_doc {
Some(document) => config
.store
Expand Down Expand Up @@ -206,32 +203,29 @@ async fn update(
/// * `config` - the server configuration.
/// * `id` - path to the identity.
/// * `presentation` - verifable presentation that holds the updated DID Document.
#[delete("/<id..>", data = "<_presentation>")]
#[delete("/<id..>", data = "<presentation>")]
async fn delete(
config: &rocket::State<Config>,
id: PathBuf,
_presentation: Json<Presentation>,
) -> Result<Json<String>, DIDError> {
// verifyAuthentication(config, ) - check user's DID that was used for signing presentation and credentials
// verifyAuthorization(config, ) - check if the administrator's DID was used
// verifyIntegrity(config, )
// verifyPresentation(config, )
// retrieve proof parameters required to verify the correctness of the presentation

// verify authorization

// test existence - if not, then return 404
// try deletion - return 503 if something goes wrong, otherwise 200
let computed_did = match DIDWeb::did_from_config(config, &id) {
Ok(did) => did,
Err(e) => return Err(e),
};
presentation: Json<Presentation>,
) -> Result<Json<ProofParameters>, DIDError> {
// only the server's owner is allowed to create DIDs
let controlling_did = &config.owner;
verify_issuer(
config,
controlling_did,
VerificationRelationship::AssertionMethod,
&presentation,
)
.await?;
let proof_parameters = ProofParameters::new(config, &id)?;
verify_presentation(config, proof_parameters, presentation).await?;
config
.store
.remove(&id)
.and_then(|_| ProofParameters::new(config, &id))
.map_err(log("delete, got error:"))
// Return the DID
.map(|_| Json(computed_did.to_string()))
.map(Json)
}

#[launch]
Expand Down
286 changes: 285 additions & 1 deletion src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -855,4 +855,288 @@ async fn integration_update() {
}

#[rocket::async_test]
async fn integration_delete() {}
async fn integration_delete() {
use rocket::local::asynchronous::Client;
let config = Config {
owner: OWNER.to_string(),
..Config::default()
};
let client = Client::tracked(ship(config))
.await
.expect("valid rocket instance");

let resolver_config = Config {
owner: OWNER.to_string(),
..Config::default()
};
let std_resolvers = resolver_config.reslover_options.get_resolver();
let test_resolver = DIDWebTestResolver {
client: Some(&client),
..DIDWebTestResolver::default()
};
let resolver = SeriesResolver {
resolvers: vec![&test_resolver, &std_resolvers],
};

// create
// ------
let response = client
.get(uri!(super::get_proof_parameters(
id = PathBuf::from("valid-did/did.json"),
)))
.dispatch()
.await;
let proof_parameters = response.into_json::<ProofParameters>().await.unwrap();
// build a credential from the did document
let owner_key = utils::read_file("./src/__fixtures__/owner.jwk").unwrap();
let owner_key = JWK::from(Params::OKP(
serde_json::from_str::<OctetParams>(&owner_key).unwrap(),
));
// build a credential from the did document
let mut attributes =
utils::json_file_to_attributes_or_panic("./src/__fixtures__/valid-did.json");
let id = match attributes.remove("id").unwrap() {
rocket::serde::json::serde_json::Value::String(id) => Some(id),
_ => None,
}
.unwrap();
let credential = utils::create_credential_or_panic(
&OWNER,
&id,
"https://example.com/vc/123",
Some(attributes),
None,
None,
&resolver,
&OWNER_VERIFICATION_METHOD,
&owner_key,
)
.await;
// build a presentation from the credential
let presentation = utils::create_presentation_or_panic(
&OWNER,
OneOrMany::One(ssi::vc::CredentialOrJWT::Credential(credential)),
&LinkedDataProofOptions {
type_: Some(ProofSuiteType::Ed25519Signature2020),
domain: Some(proof_parameters.domain.to_string()),
challenge: Some(proof_parameters.challenge.unwrap()),
proof_purpose: Some(proof_parameters.proof_purpose.to_owned()),
verification_method: Some(URI::String(OWNER_VERIFICATION_METHOD.to_string())),
..LinkedDataProofOptions::default()
},
&resolver,
&owner_key,
)
.await;
let presentation_string = serde_json::to_string(&presentation).unwrap();
// update did doc via presentation
let response = client
.post(uri!(super::create(
id = PathBuf::from("valid-did/did.json"),
)))
.body(presentation_string)
.dispatch()
.await;
assert_eq!(
response.status(),
Status::Created,
"When Presentation with updated DID document is sent to store, then the document is created and 201 - created is returned."
);

// delete DID as did-owner, not server-owner
// ------
let response = client
.get(uri!(super::get_proof_parameters(
id = PathBuf::from("valid-did/did.json"),
)))
.dispatch()
.await;
let proof_parameters = response.into_json::<ProofParameters>().await.unwrap();
// build a credential from the did document
let did_owner_key = utils::read_file("./src/__fixtures__/valid-did.jwk").unwrap();
let did_owner_key = JWK::from(Params::OKP(
serde_json::from_str::<OctetParams>(&did_owner_key).unwrap(),
));
// build a credential from the did document
let mut attributes =
utils::json_file_to_attributes_or_panic("./src/__fixtures__/valid-did.json");
let id = match attributes.remove("id").unwrap() {
rocket::serde::json::serde_json::Value::String(id) => Some(id),
_ => None,
}
.unwrap();
let credential = utils::create_credential_or_panic(
&id,
&id,
"https://example.com/vc/123",
None,
None,
None,
&resolver,
"did:web:localhost%3A8000:valid-did#controller",
&did_owner_key,
)
.await;
// build a presentation from the credential
let presentation = utils::create_presentation_or_panic(
&id,
OneOrMany::One(ssi::vc::CredentialOrJWT::Credential(credential)),
&LinkedDataProofOptions {
type_: Some(ProofSuiteType::Ed25519Signature2020),
domain: Some(proof_parameters.domain.to_string()),
challenge: Some(proof_parameters.challenge.unwrap()),
proof_purpose: Some(proof_parameters.proof_purpose.to_owned()),
verification_method: Some(URI::String(
"did:web:localhost%3A8000:valid-did#controller".to_string(),
)),
..LinkedDataProofOptions::default()
},
&resolver,
&did_owner_key,
)
.await;
let presentation_string = serde_json::to_string(&presentation).unwrap();
// update did doc via presentation
let response = client
.delete(uri!(super::delete(
id = PathBuf::from("valid-did/did.json"),
)))
.body(presentation_string)
.dispatch()
.await;
assert_eq!(
response.status(),
Status::Unauthorized,
"When the owner of the DID, not the owner of the server, tries to delete it, then 403 - Unauthorized is returned."
);

// delete DID as server-owner
// ------
let response = client
.get(uri!(super::get_proof_parameters(
id = PathBuf::from("valid-did/did.json"),
)))
.dispatch()
.await;
let proof_parameters = response.into_json::<ProofParameters>().await.unwrap();
// build a credential from the did document
let owner_key = utils::read_file("./src/__fixtures__/owner.jwk").unwrap();
let owner_key = JWK::from(Params::OKP(
serde_json::from_str::<OctetParams>(&owner_key).unwrap(),
));
// build a credential from the did document
let mut attributes =
utils::json_file_to_attributes_or_panic("./src/__fixtures__/valid-did.json");
let id = match attributes.remove("id").unwrap() {
rocket::serde::json::serde_json::Value::String(id) => Some(id),
_ => None,
}
.unwrap();
let credential = utils::create_credential_or_panic(
&OWNER,
&id,
"https://example.com/vc/123",
None,
None,
None,
&resolver,
&OWNER_VERIFICATION_METHOD,
&owner_key,
)
.await;
// build a presentation from the credential
let presentation = utils::create_presentation_or_panic(
&OWNER,
OneOrMany::One(ssi::vc::CredentialOrJWT::Credential(credential)),
&LinkedDataProofOptions {
type_: Some(ProofSuiteType::Ed25519Signature2020),
domain: Some(proof_parameters.domain.to_string()),
challenge: Some(proof_parameters.challenge.unwrap()),
proof_purpose: Some(proof_parameters.proof_purpose.to_owned()),
verification_method: Some(URI::String(OWNER_VERIFICATION_METHOD.to_string())),
..LinkedDataProofOptions::default()
},
&resolver,
&owner_key,
)
.await;
let presentation_string = serde_json::to_string(&presentation).unwrap();
// update did doc via presentation
let response = client
.delete(uri!(super::delete(
id = PathBuf::from("valid-did/did.json"),
)))
.body(presentation_string)
.dispatch()
.await;
assert_eq!(
response.status(),
Status::Ok,
"When the owner of server tries to delete the DID, then 200 - OK is returned."
);

// delete non-existing DID
// ------
let response = client
.get(uri!(super::get_proof_parameters(
id = PathBuf::from("valid-did/did.json"),
)))
.dispatch()
.await;
let proof_parameters = response.into_json::<ProofParameters>().await.unwrap();
// build a credential from the did document
let owner_key = utils::read_file("./src/__fixtures__/owner.jwk").unwrap();
let owner_key = JWK::from(Params::OKP(
serde_json::from_str::<OctetParams>(&owner_key).unwrap(),
));
// build a credential from the did document
let mut attributes =
utils::json_file_to_attributes_or_panic("./src/__fixtures__/valid-did.json");
let id = match attributes.remove("id").unwrap() {
rocket::serde::json::serde_json::Value::String(id) => Some(id),
_ => None,
}
.unwrap();
let credential = utils::create_credential_or_panic(
&OWNER,
&id,
"https://example.com/vc/123",
None,
None,
None,
&resolver,
&OWNER_VERIFICATION_METHOD,
&owner_key,
)
.await;
// build a presentation from the credential
let presentation = utils::create_presentation_or_panic(
&OWNER,
OneOrMany::One(ssi::vc::CredentialOrJWT::Credential(credential)),
&LinkedDataProofOptions {
type_: Some(ProofSuiteType::Ed25519Signature2020),
domain: Some(proof_parameters.domain.to_string()),
challenge: Some(proof_parameters.challenge.unwrap()),
proof_purpose: Some(proof_parameters.proof_purpose.to_owned()),
verification_method: Some(URI::String(OWNER_VERIFICATION_METHOD.to_string())),
..LinkedDataProofOptions::default()
},
&resolver,
&owner_key,
)
.await;
let presentation_string = serde_json::to_string(&presentation).unwrap();
// update did doc via presentation
let response = client
.delete(uri!(super::delete(
id = PathBuf::from("valid-did/did.json"),
)))
.body(presentation_string)
.dispatch()
.await;
assert_eq!(
response.status(),
Status::NotFound,
"When a non-existing DID is attempted to be deleted, then 404 - Not Found is returned."
);
}

0 comments on commit 64b1db5

Please sign in to comment.