Skip to content

Commit

Permalink
WIP approve
Browse files Browse the repository at this point in the history
  • Loading branch information
eliykat committed Oct 26, 2023
1 parent e767bdd commit a1010d4
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 87 deletions.
108 changes: 41 additions & 67 deletions crates/bitwarden/src/admin_console/auth_requests/approve.rs
Original file line number Diff line number Diff line change
@@ -1,53 +1,53 @@
use base64::Engine;
use bitwarden_api_api::models::{
AdminAuthRequestUpdateRequestModel, OrganizationUserResetPasswordDetailsResponseModel,
PendingOrganizationAuthRequestResponseModel,
PendingOrganizationAuthRequestResponseModelListResponseModel,
};
use rsa::{
pkcs8::{der::Decode, DecodePrivateKey, SubjectPublicKeyInfo},
RsaPublicKey,
};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use uuid::Uuid;

use crate::{
client::Client,
crypto::{encrypt_rsa, Decryptable, EncString},
error::{CryptoError, Error, Result},
util::BASE64_ENGINE,
crypto::{encrypt_rsa, private_key_from_bytes, public_key_from_b64, Decryptable, EncString},
error::{Error, Result},
};

use super::{list_pending_requests, PendingAuthRequestResponse};

// TODO: what identifier should this take? e.g. org_user_id, request_id, etc
// using org_user_id for now
#[derive(Serialize, Deserialize, Debug, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct AuthApproveRequest {
/// ID of the auth request to approve
pub request_id: Uuid,
pub organization_user_id: Uuid,
pub organization_id: Uuid,
pub device_public_key: String,
}

pub(crate) async fn approve_auth_request(
client: &mut Client,
input: &AuthApproveRequest,
) -> Result<()> {
let device_request = get_pending_request(input.organization_id, input.organization_user_id, client).await;

// Get user reset password details
let reset_password_details =
bitwarden_api_api::apis::organization_users_api::organizations_org_id_users_id_reset_password_details_get(
&client.get_api_configurations().await.api,
&input.organization_id.to_string(),
&input.request_id.to_string(),
&device_request.id.to_string(),
)
.await?;

let encrypted_user_key = get_encrypted_user_key(&client, input, reset_password_details)?;
let encrypted_user_key = get_encrypted_user_key(
&client,
input.organization_id,
&device_request,
reset_password_details,
)?;

bitwarden_api_api::apis::organization_auth_requests_api::organizations_org_id_auth_requests_request_id_post(
&client.get_api_configurations().await.api,
input.organization_id,
input.request_id,
device_request.id,
Some(AdminAuthRequestUpdateRequestModel {
encrypted_user_key: Some(encrypted_user_key.to_string()),
request_approved: true
Expand All @@ -58,9 +58,30 @@ pub(crate) async fn approve_auth_request(
Ok(())
}

async fn get_pending_request(organization_id: Uuid, organization_user_id: Uuid, client: &mut Client) -> PendingAuthRequestResponse {
// hack: get all approval details and then find the one we want
// when we settle on an identifier then we should just give ourselves a better server API
// or do we require the caller to pass all this info in?
let all_device_requests = list_pending_requests(
client,
&super::PendingAuthRequestsRequest {
organization_id: organization_id,
},
)
.await;

all_device_requests
.unwrap()
.data
.into_iter()
.find(|r| r.organization_user_id == organization_user_id)
.unwrap() // TODO: error handling
}

fn get_encrypted_user_key(
client: &Client,
input: &AuthApproveRequest,
organization_id: Uuid,
input: &PendingAuthRequestResponse,
reset_password_details: OrganizationUserResetPasswordDetailsResponseModel,
) -> Result<EncString> {
// Decrypt organization's encrypted private key with org key
Expand All @@ -71,10 +92,10 @@ fn get_encrypted_user_key(
.encrypted_private_key
.ok_or(Error::MissingFields)?
.parse::<EncString>()?
.decrypt(enc, &Some(input.organization_id))?
.decrypt(enc, &Some(organization_id))?
.into_bytes();

rsa::RsaPrivateKey::from_pkcs8_der(&dec).map_err(|_| CryptoError::InvalidKey)?
private_key_from_bytes(&dec)?
};

// Decrypt user key with org private key
Expand All @@ -85,55 +106,8 @@ fn get_encrypted_user_key(
let dec_user_key = user_key.decrypt_with_rsa_key(&org_private_key)?;

// re-encrypt user key with device public key
let device_public_key_bytes = BASE64_ENGINE.decode(&input.device_public_key)?;
let device_public_key_info = SubjectPublicKeyInfo::from_der(&device_public_key_bytes).unwrap(); // TODO: error handling
let device_public_key = RsaPublicKey::try_from(device_public_key_info).unwrap(); // TODO: error handling

let device_public_key = public_key_from_b64(&input.public_key_b64)?;
let re_encrypted_user_key = encrypt_rsa(dec_user_key, &device_public_key)?;

EncString::from_buffer(&re_encrypted_user_key)
}

#[derive(Serialize, Deserialize, Debug, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct PendingAuthRequestsResponse {
pub data: Vec<PendingAuthRequestResponse>,
}

impl PendingAuthRequestsResponse {
pub(crate) fn process_response(
response: PendingOrganizationAuthRequestResponseModelListResponseModel,
) -> Result<PendingAuthRequestsResponse> {
Ok(PendingAuthRequestsResponse {
data: response
.data
.unwrap_or_default()
.into_iter()
.map(|r| PendingAuthRequestResponse::process_response(r))
.collect::<Result<_, _>>()?,
})
}
}

#[derive(Serialize, Deserialize, Debug, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct PendingAuthRequestResponse {
pub id: Uuid,
pub user_id: Uuid,
pub organization_user_id: Uuid,
pub email: String,
// TODO: map rest of fields
}

impl PendingAuthRequestResponse {
pub(crate) fn process_response(
response: PendingOrganizationAuthRequestResponseModel,
) -> Result<PendingAuthRequestResponse> {
Ok(PendingAuthRequestResponse {
id: response.id.ok_or(Error::MissingFields)?,
user_id: response.user_id.ok_or(Error::MissingFields)?,
organization_user_id: response.organization_user_id.ok_or(Error::MissingFields)?,
email: response.email.ok_or(Error::MissingFields)?,
})
}
}
43 changes: 23 additions & 20 deletions crates/bw/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use bitwarden::{
auth::RegisterRequest, client::client_settings::ClientSettings, tool::PasswordGeneratorRequest,
admin_console::auth_requests::{PendingAuthRequestsRequest, AuthApproveRequest}
admin_console::auth_requests::{PendingAuthRequestsRequest, AuthApproveRequest}, Client
};
use bitwarden_cli::{install_color_eyre, text_prompt_when_none, Color};
use clap::{command, Args, CommandFactory, Parser, Subcommand};
Expand Down Expand Up @@ -101,7 +101,7 @@ enum GeneratorCommands {
#[derive(Subcommand, Clone)]
enum AdminConsoleCommands {
ListDevices { organization_id: Uuid },
ApproveDevice { id: Uuid }
ApproveDevice { organization_id: Uuid, organization_user_id: Uuid }
}

#[derive(Args, Clone)]
Expand Down Expand Up @@ -134,6 +134,19 @@ async fn main() -> Result<()> {
process_commands().await
}

async fn hack_login() -> Client {
// hack login
let server = "https://vault.qa.bitwarden.pw";
let settings = ClientSettings {
api_url: format!("{}/api", server),
identity_url: format!("{}/identity", server),
..Default::default()
};
let client = bitwarden::Client::new(Some(settings));

auth::api_key_login(client, None, None).await.unwrap()
}

async fn process_commands() -> Result<()> {
let cli = Cli::parse();

Expand Down Expand Up @@ -226,31 +239,21 @@ async fn process_commands() -> Result<()> {
},
Commands::AdminConsole { command } => match command {
AdminConsoleCommands::ListDevices { organization_id } => {

// hack login
let server = "https://vault.qa.bitwarden.pw";
let settings = ClientSettings {
api_url: format!("{}/api", server),
identity_url: format!("{}/identity", server),
..Default::default()
};
let client = bitwarden::Client::new(Some(settings));

let mut client = auth::api_key_login(client, None, None).await?;

// continue
let mut client = hack_login().await;
let auth_requests = client
.client_auth_requests()
.list(&PendingAuthRequestsRequest { organization_id })
.await?;

serialize_response(auth_requests.data, cli.output, false);
},
AdminConsoleCommands::ApproveDevice { id } => {
todo!()
// client
// .client_auth_requests()
// .approve(&AuthApproveRequest { id })
AdminConsoleCommands::ApproveDevice { organization_id, organization_user_id } => {
let mut client = hack_login().await;
client
.client_auth_requests()
.approve(&AuthApproveRequest { organization_id, organization_user_id })
.await
.unwrap(); // error handling?
}
}
};
Expand Down

0 comments on commit a1010d4

Please sign in to comment.