Skip to content

Commit

Permalink
TECH-156 - Allow adding of authorized issuers
Browse files Browse the repository at this point in the history
  • Loading branch information
TYRONEMICHAEL committed May 15, 2024
1 parent 1eaac11 commit f8cf6e2
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 19 deletions.
13 changes: 12 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,16 @@ 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
3 changes: 3 additions & 0 deletions src/civic_canister_backend/civic_canister_backend.did
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,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 @@ -77,6 +79,7 @@ type StoredCredential = record {
issuer : text;
};
service : (opt IssuerInit) -> {
init: (opt IssuerConfig) -> ();
add_credentials : (principal, vec StoredCredential) -> (text);
configure : (IssuerInit) -> ();
derivation_origin : (DerivationOriginRequest) -> (Result);
Expand Down
95 changes: 77 additions & 18 deletions src/civic_canister_backend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,13 @@ type ConfigCell = StableCell<IssuerConfig, Memory>;
const MINUTE_NS: u64 = 60 * 1_000_000_000;
const PROD_II_CANISTER_ID: &str = "rdmx6-jaaaa-aaaaa-aaadq-cai";
const VC_EXPIRATION_PERIOD_NS: u64 = 15 * MINUTE_NS;
// Authorized Civic Principal - get this from the frontend
const AUTHORIZED_PRINCIPAL: &str = "tglqb-kbqlj-to66e-3w5sg-kkz32-c6ffi-nsnta-vj2gf-vdcc5-5rzjk-jae";

lazy_static! {
// Seed and public key used for signing the credentials.
static ref CANISTER_SIG_SEED: Vec<u8> = hash_bytes("some_random_seed").to_vec();
static ref CANISTER_SIG_PK: CanisterSigPublicKey = CanisterSigPublicKey::new(ic_cdk::id(), CANISTER_SIG_SEED.clone());
}



#[derive(Debug)]
pub enum SupportedCredentialType {
VerifiedAdult,
Expand Down Expand Up @@ -95,7 +91,7 @@ fn config_memory() -> Memory {
#[cfg(target_arch = "wasm32")]
use ic_cdk::println;

#[derive(CandidType, Deserialize)]
#[derive(Clone, Debug, CandidType, Deserialize)]
struct IssuerConfig {
/// Root of trust for checking canister signatures.
ic_root_key_raw: Vec<u8>,
Expand All @@ -105,6 +101,10 @@ 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
authorized_issuers: Vec<Principal>,
}

impl Storable for IssuerConfig {
Expand All @@ -126,6 +126,8 @@ 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: [ic_cdk::api::caller()],
}
}
}
Expand All @@ -139,6 +141,8 @@ 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,
}
}

Expand All @@ -154,18 +158,67 @@ 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 {
current_config.authorized_issuers.insert(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 {
current_config.authorized_issuers.remove(&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.");
}
});
}

#[post_upgrade]
fn post_upgrade(init_arg: Option<IssuerInit>) {
init(init_arg);
Expand Down Expand Up @@ -455,24 +508,30 @@ fn get_derivation_origin(hostname: &str) -> Result<DerivationOriginData, Derivat
})
}


#[update]
#[candid_method]
#[candid_method(update)]
fn add_credentials(principal: Principal, new_credentials: Vec<StoredCredential>) -> String {
// Check if the caller is the authorized principal
if caller().to_text() != AUTHORIZED_PRINCIPAL {
let caller = ic_cdk::api::caller();

// Access the configuration and check if the caller is an authorized issuer
let is_authorized = CONFIG.with(|config_cell| {
let config = config_cell.borrow();
let current_config = config.get();
current_config.authorized_issuers.contains(&caller)
});

if !is_authorized {
return "Unauthorized: You do not have permission to add credentials.".to_string();
}

// If authorized, proceed to add credentials
CREDENTIALS.with_borrow_mut(|credentials| {
let entry = credentials.entry(principal).or_insert_with(Vec::new);
entry.extend(new_credentials.clone());
});
let credential_info = format!("Added credentials: \n{:?}", new_credentials);
credential_info
}

let entry = credentials.entry(principal).or_insert_with(Vec::new);
entry.extend(new_credentials.clone());
});

format!("Added credentials: \n{:?}", new_credentials)
}

#[query]
#[candid_method(query)]
Expand Down

0 comments on commit f8cf6e2

Please sign in to comment.