Skip to content

Commit

Permalink
Add support to reconfigure derivation_origin for VC issuer (#2528)
Browse files Browse the repository at this point in the history
* Add support to reconfigure derivation_origin for VC issuer

This PR allows to reconfigure the expected frontend hostname as well as
the derivation origin. This will be used in the future to simplify the
e2e tests.

Since this is most useful in e2e tests if the configuration is _not_
guarded by the controller, the guard is removed for the `configure`
method too for consistency.

* Remove test for controller access gate on configure

* Add test

* 🤖 cargo-fmt auto-update

* Assert previous value

* 🤖 npm run generate auto-update

---------

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
  • Loading branch information
1 parent a76730d commit 83cf3f8
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 33 deletions.
1 change: 1 addition & 0 deletions demos/vc_issuer/app/generated/vc_issuer_idl.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ export const idlFactory = ({ IDL }) => {
[],
),
'set_alternative_origins' : IDL.Func([IDL.Text], [], []),
'set_derivation_origin' : IDL.Func([IDL.Text, IDL.Text], [], []),
'vc_consent_message' : IDL.Func(
[Icrc21VcConsentMessageRequest],
[IDL.Variant({ 'Ok' : Icrc21ConsentInfo, 'Err' : Icrc21Error })],
Expand Down
1 change: 1 addition & 0 deletions demos/vc_issuer/app/generated/vc_issuer_types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export interface _SERVICE {
{ 'Err' : IssueCredentialError }
>,
'set_alternative_origins' : ActorMethod<[string], undefined>,
'set_derivation_origin' : ActorMethod<[string, string], undefined>,
'vc_consent_message' : ActorMethod<
[Icrc21VcConsentMessageRequest],
{ 'Ok' : Icrc21ConsentInfo } |
Expand Down
24 changes: 14 additions & 10 deletions demos/vc_issuer/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ fn config_memory() -> Memory {
#[cfg(target_arch = "wasm32")]
use ic_cdk::println;

#[derive(CandidType, Deserialize)]
#[derive(CandidType, Deserialize, Clone)]
struct IssuerConfig {
/// Root of trust for checking canister signatures.
ic_root_key_raw: Vec<u8>,
Expand Down Expand Up @@ -143,7 +143,7 @@ struct IssuerInit {
#[candid_method(init)]
fn init(init_arg: Option<IssuerInit>) {
if let Some(init) = init_arg {
apply_config(init);
apply_config(IssuerConfig::from(init));
};

init_assets();
Expand All @@ -156,17 +156,21 @@ fn post_upgrade(init_arg: Option<IssuerInit>) {

#[update]
#[candid_method]
fn configure(config: IssuerInit) {
if ic_cdk::api::is_controller(&caller()) {
apply_config(config);
} else {
panic!("Only a controller can call configure().");
}
fn configure(init: IssuerInit) {
apply_config(IssuerConfig::from(init));
}

#[update]
fn set_derivation_origin(frontend_hostname: String, derivation_origin: String) {
let mut config: IssuerConfig = CONFIG.with_borrow(|config| config.get().clone());
config.derivation_origin = derivation_origin;
config.frontend_hostname = frontend_hostname;
apply_config(config);
}

fn apply_config(init: IssuerInit) {
fn apply_config(config: IssuerConfig) {
CONFIG
.with_borrow_mut(|config_cell| config_cell.set(IssuerConfig::from(init)))
.with_borrow_mut(|config_cell| config_cell.set(config))
.expect("failed to apply issuer config");
}

Expand Down
72 changes: 49 additions & 23 deletions demos/vc_issuer/tests/issue_credential.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ use canister_sig_util::{extract_raw_root_pk_from_der, CanisterSigPublicKey};
use canister_tests::api::http_request;
use canister_tests::api::internet_identity::vc_mvp as ii_api;
use canister_tests::flows;
use canister_tests::framework::{
env, get_wasm_path, principal_1, principal_2, test_principal, time, II_WASM,
};
use canister_tests::framework::{env, get_wasm_path, principal_1, test_principal, time, II_WASM};
use ic_cdk::api::management_canister::provisional::CanisterId;
use ic_response_verification::types::VerificationInfo;
use ic_response_verification::verify_request_response_pair;
Expand Down Expand Up @@ -113,10 +111,9 @@ mod api {
pub fn configure(
env: &StateMachine,
canister_id: CanisterId,
sender: Principal,
config: &IssuerInit,
) -> Result<(), CallError> {
call_candid_as(env, canister_id, sender, "configure", (config,))
call_candid(env, canister_id, "configure", (config,))
}

pub fn vc_consent_message(
Expand All @@ -138,19 +135,31 @@ mod api {
pub fn derivation_origin(
env: &StateMachine,
canister_id: CanisterId,
sender: Principal,
derivation_origin_req: &DerivationOriginRequest,
) -> Result<Result<DerivationOriginData, DerivationOriginError>, CallError> {
call_candid_as(
call_candid(
env,
canister_id,
sender,
"derivation_origin",
(derivation_origin_req,),
)
.map(|(x,)| x)
}

pub fn set_derivation_origin(
env: &StateMachine,
canister_id: CanisterId,
frontend_hostname: &str,
derivation_origin: &str,
) -> Result<(), CallError> {
call_candid(
env,
canister_id,
"set_derivation_origin",
(frontend_hostname, derivation_origin),
)
}

pub fn set_alternative_origins(
env: &StateMachine,
canister_id: CanisterId,
Expand Down Expand Up @@ -366,12 +375,41 @@ fn should_return_derivation_origin() {
let canister_id = install_canister(&env, VC_ISSUER_WASM.clone());
let frontend_hostname = format!("https://{}.icp0.io", canister_id.to_text());
let req = DerivationOriginRequest { frontend_hostname };
let response = api::derivation_origin(&env, canister_id, principal_1(), &req)
let response = api::derivation_origin(&env, canister_id, &req)
.expect("API call failed")
.expect("derivation_origin error");
assert_eq!(response.origin, req.frontend_hostname);
}

#[test]
fn should_set_derivation_origin() {
let env = env();
let canister_id = install_canister(&env, VC_ISSUER_WASM.clone());
let req = DerivationOriginRequest {
frontend_hostname: "frontend_hostname.com".to_string(),
};

let response = api::derivation_origin(&env, canister_id, &req)
.expect("API call failed")
.expect("derivation_origin error");
let default_derivation_origin = format!("https://{}.icp0.io", canister_id.to_text());
assert_eq!(response.origin, default_derivation_origin);

let derivation_origin = "https://derivation.origin";
api::set_derivation_origin(
&env,
canister_id,
"frontend_hostname.com".to_string().as_str(),
derivation_origin,
)
.expect("failed to set derivation_origin");

let response = api::derivation_origin(&env, canister_id, &req)
.expect("API call failed")
.expect("derivation_origin error");
assert_eq!(response.origin, derivation_origin);
}

#[test]
fn should_return_derivation_origin_with_custom_init() {
let env = env();
Expand All @@ -385,7 +423,6 @@ fn should_return_derivation_origin_with_custom_init() {
let response = api::derivation_origin(
&env,
issuer_id,
principal_1(),
&DerivationOriginRequest {
frontend_hostname: custom_init.frontend_hostname.clone(),
},
Expand Down Expand Up @@ -730,19 +767,8 @@ fn should_issue_credential_e2e() -> Result<(), CallError> {
#[test]
fn should_configure() {
let env = env();
let controller = principal_1();
let issuer_id = install_canister_as(&env, VC_ISSUER_WASM.clone(), Some(controller));
api::configure(&env, issuer_id, controller, &DUMMY_ISSUER_INIT).expect("API call failed");
}

#[test]
fn should_fail_configure_if_not_controller() {
let env = env();
let controller = principal_1();
let not_controller = principal_2();
let issuer_id = install_canister_as(&env, VC_ISSUER_WASM.clone(), Some(controller));
let result = api::configure(&env, issuer_id, not_controller, &DUMMY_ISSUER_INIT);
assert_matches!(result, Err(e) if format!("{:?}", e).contains("Only a controller can call configure"));
let issuer_id = install_canister(&env, VC_ISSUER_WASM.clone());
api::configure(&env, issuer_id, &DUMMY_ISSUER_INIT).expect("API call failed");
}

#[test]
Expand Down
1 change: 1 addition & 0 deletions demos/vc_issuer/vc_demo_issuer.did
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ service: (opt IssuerConfig) -> {

/// Configure the issuer (e.g. set the root key), used for deployment/testing.
configure: (IssuerConfig) -> ();
set_derivation_origin: (frontend_hostname: text, derivation_origin: text) -> ();
// Sets the content of the alternative origins file.
set_alternative_origins: (alternative_origins: text) -> ();

Expand Down
1 change: 1 addition & 0 deletions src/frontend/generated/vc_issuer_idl.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ export const idlFactory = ({ IDL }) => {
[],
),
'set_alternative_origins' : IDL.Func([IDL.Text], [], []),
'set_derivation_origin' : IDL.Func([IDL.Text, IDL.Text], [], []),
'vc_consent_message' : IDL.Func(
[Icrc21VcConsentMessageRequest],
[IDL.Variant({ 'Ok' : Icrc21ConsentInfo, 'Err' : Icrc21Error })],
Expand Down
1 change: 1 addition & 0 deletions src/vc-api/src/generated/vc_issuer_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ export interface _SERVICE {
{ 'Ok' : PreparedCredentialData } |
{ 'Err' : IssueCredentialError }
>,
'set_derivation_origin' : ActorMethod<[string, string], undefined>,
'set_alternative_origins' : ActorMethod<[string], undefined>,
'vc_consent_message' : ActorMethod<
[Icrc21VcConsentMessageRequest],
Expand Down

0 comments on commit 83cf3f8

Please sign in to comment.