Skip to content

Commit

Permalink
TECH-156 - Add support for whitelisting issuers
Browse files Browse the repository at this point in the history
- Add support for initializing with an admin
  • Loading branch information
TYRONEMICHAEL committed May 16, 2024
1 parent a7580f0 commit e996447
Show file tree
Hide file tree
Showing 6 changed files with 313 additions and 301 deletions.
5 changes: 0 additions & 5 deletions .github/workflows/ci-local.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
name: CI Build and Test (Local setup)

on: [push, pull_request]
# push:
# branches: [ "develop" ]
# pull_request:
# branches: [ "develop" ]


env:
CARGO_TERM_COLOR: always
Expand Down
12 changes: 11 additions & 1 deletion scripts/deploy-civic.sh
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ EOF

II_CANISTER_ID=
DFX_NETWORK=
ADMIN_PRINCIPAL_ID=tglqb-kbqlj-to66e-3w5sg-kkz32-c6ffi-nsnta-vj2gf-vdcc5-5rzjk-jae

while [[ $# -gt 0 ]]
do
Expand Down Expand Up @@ -123,6 +124,15 @@ mv src/civic_canister_backend/dist/.well-known/ii-alternative-origins ./ii-alter
cat ./ii-alternative-origins-template | sed "s+ISSUER_FE_HOSTNAME_PLACEHOLDER+$ALTERNATIVE_ORIGINS+g" > src/civic_canister_backend/dist/.well-known/ii-alternative-origins
rm ./ii-alternative-origins-template

dfx deploy civic_canister_backend --network "$DFX_NETWORK" --argument '(opt record { idp_canister_ids = vec{ principal "'"$II_CANISTER_ID"'" }; ic_root_key_der = vec '"$rootkey_did"'; derivation_origin = "'"$ISSUER_DERIVATION_ORIGIN"'"; frontend_hostname = "'"$ISSUER_FRONTEND_HOSTNAME"'"; })'
dfx deploy civic_canister_backend --network "$DFX_NETWORK" --argument '(
opt record {
idp_canister_ids = vec { principal "'"$II_CANISTER_ID"'" };
ic_root_key_der = vec '"$rootkey_did"';
derivation_origin = "'"$ISSUER_DERIVATION_ORIGIN"'";
frontend_hostname = "'"$ISSUER_FRONTEND_HOSTNAME"'";
admin = principal "'"$ADMIN_PRINCIPAL_ID"'";
authorized_issuers = vec { principal "'"$ADMIN_PRINCIPAL_ID"'" };
}
)'
# Revert changes
git checkout src/civic_canister_backend/dist/.well-known/ii-alternative-origins
5 changes: 5 additions & 0 deletions src/civic_canister_backend/civic_canister_backend.did
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ type IssuerInit = record {
idp_canister_ids : vec principal;
ic_root_key_der : blob;
frontend_hostname : text;
admin: principal;
authorized_issuers: vec principal;
};
type PrepareCredentialRequest = record {
signed_id_alias : SignedIdAlias;
Expand Down Expand Up @@ -103,4 +105,7 @@ service : (opt IssuerInit) -> {
prepare_credential : (PrepareCredentialRequest) -> (Result_4);
update_credential : (principal, text, StoredCredential) -> (Result);
vc_consent_message : (Icrc21VcConsentMessageRequest) -> (Result_5);
get_admin : () -> (principal) query;
add_issuer: (principal) -> ();
remove_issuer: (principal) -> ();
}
111 changes: 82 additions & 29 deletions src/civic_canister_backend/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@

use crate::credential::{update_root_hash, StoredCredential};
use canister_sig_util::signature_map::{SignatureMap, LABEL_SIG};
use std::cell::RefCell;
use std::collections::HashMap;
use canister_sig_util::signature_map::{SignatureMap, LABEL_SIG};
use crate::credential::{StoredCredential, update_root_hash};

use crate::consent_message::{get_vc_consent_message, SupportedLanguage};

use candid::{candid_method, CandidType, Deserialize, Principal};
// use ic_cdk::candid::candid_method;
use canister_sig_util::{extract_raw_root_pk_from_der, IC_ROOT_PK_DER};


use ic_cdk_macros::{init, query, update};
use ic_certification::{labeled_hash, pruned};

Expand All @@ -20,25 +18,19 @@ use include_dir::{include_dir, Dir};

use serde_bytes::ByteBuf;


use std::borrow::Cow;
use asset_util::{collect_assets, CertifiedAssets};
use std::borrow::Cow;
use vc_util::issuer_api::{
DerivationOriginData, DerivationOriginError,
DerivationOriginRequest, Icrc21ConsentInfo, Icrc21Error,
Icrc21VcConsentMessageRequest
DerivationOriginData, DerivationOriginError, DerivationOriginRequest, Icrc21ConsentInfo,
Icrc21Error, Icrc21VcConsentMessageRequest,
};

use ic_cdk::api;

use ic_cdk_macros::post_upgrade;




const PROD_II_CANISTER_ID: &str = "rdmx6-jaaaa-aaaaa-aaadq-cai";


thread_local! {
/// Static configuration of the canister set by init() or post_upgrade().
pub(crate) static CONFIG: RefCell<ConfigCell> = RefCell::new(ConfigCell::init(config_memory(), IssuerConfig::default()).expect("failed to initialize stable cell"));
Expand All @@ -54,8 +46,6 @@ thread_local! {
type Memory = RestrictedMemory<DefaultMemoryImpl>;
type ConfigCell = StableCell<IssuerConfig, Memory>;



/// Reserve the first stable memory page for the configuration stable cell.
fn config_memory() -> Memory {
RestrictedMemory::new(DefaultMemoryImpl::default(), 0..1)
Expand All @@ -64,7 +54,7 @@ fn config_memory() -> Memory {
#[cfg(target_arch = "wasm32")]
use ic_cdk::println;

#[derive(CandidType, Deserialize)]
#[derive(Clone, Debug, CandidType, Deserialize)]
pub(crate) struct IssuerConfig {
/// Root of trust for checking canister signatures.
pub(crate) ic_root_key_raw: Vec<u8>,
Expand All @@ -74,6 +64,10 @@ pub(crate) struct IssuerConfig {
derivation_origin: String,
/// Frontend hostname to be used by the issuer.
frontend_hostname: String,
// Admin who can add authorized issuers
admin: Principal,
// List of authorized issuers who can issue credentials
pub authorized_issuers: Vec<Principal>,
}

impl Storable for IssuerConfig {
Expand All @@ -95,11 +89,12 @@ impl Default for IssuerConfig {
idp_canister_ids: vec![Principal::from_text(PROD_II_CANISTER_ID).unwrap()],
derivation_origin: derivation_origin.clone(),
frontend_hostname: derivation_origin,
admin: ic_cdk::api::caller(),
authorized_issuers: vec![ic_cdk::api::caller()],
}
}
}


impl From<IssuerInit> for IssuerConfig {
fn from(init: IssuerInit) -> Self {
Self {
Expand All @@ -108,12 +103,13 @@ impl From<IssuerInit> for IssuerConfig {
idp_canister_ids: init.idp_canister_ids,
derivation_origin: init.derivation_origin,
frontend_hostname: init.frontend_hostname,
admin: init.admin,
authorized_issuers: init.authorized_issuers,
}
}

}

#[derive(CandidType, Deserialize)]
#[derive(Clone, Debug, CandidType, Deserialize)]
struct IssuerInit {
/// Root of trust for checking canister signatures.
ic_root_key_der: Vec<u8>,
Expand All @@ -123,18 +119,81 @@ struct IssuerInit {
derivation_origin: String,
/// Frontend hostname be used by the issuer.
frontend_hostname: String,
// Admin who can add authorized issuers
admin: Principal,
// List of authorized issuers who can issue credentials
authorized_issuers: Vec<Principal>,
}

#[init]
#[candid_method(init)]
fn init(init_arg: Option<IssuerInit>) {
if let Some(init) = init_arg {
apply_config(init);
};

} else {
// Initialize with default values and a specified admin
let default_config = IssuerConfig::default();
CONFIG.with(|config_cell| {
let mut config = config_cell.borrow_mut();
*config = ConfigCell::init(config_memory(), default_config)
.expect("Failed to initialize config");
});
}
init_assets();
}

#[update]
#[candid_method(update)]
fn add_issuer(new_issuer: Principal) {
let caller = ic_cdk::api::caller();
CONFIG.with(|config_cell| {
let mut config = config_cell.borrow_mut();
// Retrieve the current configuration
let mut current_config = config.get().clone(); // Clone into a mutable local variable

// Check if the caller is the admin and modify the config
if caller == current_config.admin {
// Ensure no duplicates if that's intended
if !current_config.authorized_issuers.contains(&new_issuer) {
current_config.authorized_issuers.push(new_issuer);
}
// Save the updated configuration
let _ = config.set(current_config); // Pass the modified IssuerConfig back to set
} else {
ic_cdk::api::trap("Caller is not authorized as admin.");
}
});
}

#[update]
#[candid_method(update)]
fn remove_issuer(issuer: Principal) {
let caller = ic_cdk::api::caller();
CONFIG.with(|config_cell| {
let mut config = config_cell.borrow_mut();
// Retrieve the current configuration
let mut current_config = config.get().clone(); // Clone into a mutable local variable

if caller == current_config.admin {
// Remove the issuer if they exist in the list
current_config.authorized_issuers.retain(|x| *x != issuer);
// Save the updated configuration
let _ = config.set(current_config); // Pass the modified IssuerConfig back to set
} else {
ic_cdk::api::trap("Caller is not authorized as admin.");
}
});
}

#[query]
fn get_admin() -> Principal {
CONFIG.with(|config| {
let config_borrowed = config.borrow(); // Obtain a read-only borrow
// Now you can access the config
return config_borrowed.get().admin;
})
}

#[post_upgrade]
fn post_upgrade(init_arg: Option<IssuerInit>) {
init(init_arg);
Expand Down Expand Up @@ -191,7 +250,6 @@ async fn vc_consent_message(
)
}


#[update]
#[candid_method]
async fn derivation_origin(
Expand All @@ -216,10 +274,7 @@ fn get_derivation_origin(hostname: &str) -> Result<DerivationOriginData, Derivat
})
}




// To solve the CORS error during the vc-flow
// To solve the CORS error during the vc-flow
#[query]
#[candid_method(query)]
pub fn http_request(req: HttpRequest) -> HttpResponse {
Expand Down Expand Up @@ -249,8 +304,6 @@ pub fn http_request(req: HttpRequest) -> HttpResponse {
}
}



#[derive(Clone, Debug, CandidType, Deserialize)]
pub struct HttpRequest {
pub method: String,
Expand All @@ -267,4 +320,4 @@ pub struct HttpResponse {
pub body: ByteBuf,
}

ic_cdk::export_candid!();
ic_cdk::export_candid!();
Loading

0 comments on commit e996447

Please sign in to comment.