use rocket::Route; use rocket::serde::json::Json; use serde_json::Value; use crate::{ CONFIG, api::{EmptyResult, JsonResult}, auth::Headers, db::{ DbConn, models::{Membership, MembershipType, UserId}, }, }; pub fn routes() -> Vec { routes![post_set_key_connector_key, post_convert_to_key_connector, get_confirmation_details,] } #[derive(Debug, serde::Deserialize)] #[serde(rename_all = "camelCase")] pub struct KeyPairData { encrypted_private_key: String, public_key: String, } #[derive(Debug, serde::Deserialize)] #[serde(rename_all = "camelCase")] pub struct SetKeyConnectorKeyData { key: String, keys: KeyPairData, kdf: i32, kdf_iterations: i32, kdf_memory: Option, kdf_parallelism: Option, #[allow(dead_code)] org_identifier: String, } async fn can_use_key_connector(user_uuid: &UserId, conn: &DbConn) -> EmptyResult { if Membership::find_by_user(user_uuid, conn).await.iter().any(|m| m.atype >= MembershipType::Admin) { err!("Owners and admins cannot use Key Connector and must keep a master password"); } Ok(()) } // Called by the client to finish provisioning a new SSO user whose master key // was just stored on the key connector. #[post("/accounts/set-key-connector-key", data = "")] async fn post_set_key_connector_key(data: Json, headers: Headers, conn: DbConn) -> EmptyResult { if !CONFIG.key_connector_enabled() { err!("Key Connector is not enabled on this server"); } let data = data.into_inner(); let mut user = headers.user; if user.private_key.is_some() { err!("Account already initialized, cannot set Key Connector key"); } can_use_key_connector(&user.uuid, &conn).await?; user.client_kdf_type = data.kdf; user.client_kdf_iter = data.kdf_iterations; user.client_kdf_memory = data.kdf_memory; user.client_kdf_parallelism = data.kdf_parallelism; user.akey = data.key; user.private_key = Some(data.keys.encrypted_private_key); user.public_key = Some(data.keys.public_key); // Key connector users don't have a master password user.password_hash = Vec::new(); user.uses_key_connector = true; user.save(&conn).await } // Migrates an existing password user to the key connector. The client has already // uploaded the current master key to the connector at this point. #[post("/accounts/convert-to-key-connector")] async fn post_convert_to_key_connector(headers: Headers, conn: DbConn) -> EmptyResult { if !CONFIG.key_connector_enabled() { err!("Key Connector is not enabled on this server"); } let mut user = headers.user; if user.private_key.is_none() { err!("Account is not initialized, cannot convert to Key Connector"); } can_use_key_connector(&user.uuid, &conn).await?; user.password_hash = Vec::new(); user.password_hint = None; user.uses_key_connector = true; user.save(&conn).await } #[get("/accounts/key-connector/confirmation-details/<_org_identifier>")] fn get_confirmation_details(_org_identifier: &str, _headers: Headers) -> JsonResult { if !CONFIG.key_connector_enabled() { err!("Key Connector is not enabled on this server"); } // SSO (and therefore the key connector) is global, so there is no real org to look up Ok(Json(serde_json::json!({ "OrganizationName": CONFIG.key_connector_org_name(), "Object": "keyConnectorUserDecryptionOptionConfirmationDetails" }))) } pub fn key_connector_user_decryption_option() -> Value { serde_json::json!({ "KeyConnectorUrl": CONFIG.key_connector_url() }) }