feat: integrate tracing for structured logging
- Added `tracing` and `tracing-subscriber` for improved logging, replacing `println` statements with `info`, `debug`, `warn`, and `error`. - Annotated key methods with `#[instrument]` for better tracing of function calls and arguments. - Configured logging initialization in `main.rs` with `EnvFilter` to control log verbosity.
This commit is contained in:
parent
f11b83ddf4
commit
8f28cc1af2
|
@ -13,6 +13,5 @@ axum = { version = "0.6.18", features = ["multipart"] }
|
|||
uuid = { version = "1.3.0", features = ["v4", "serde"] }
|
||||
sha2 = "0.10.6"
|
||||
base64 = "0.21.0"
|
||||
tower = "0.4.13"
|
||||
tower-http = { version = "0.4.0", features = ["cors"] }
|
||||
futures = "0.3.28"
|
||||
tracing = "0.1.37"
|
||||
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
|
||||
|
|
39
src/api.rs
39
src/api.rs
|
@ -10,6 +10,7 @@ use axum::{
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::sync::Arc;
|
||||
use tokio::net::TcpListener;
|
||||
use tracing::{info, error, debug, instrument};
|
||||
|
||||
use crate::document_service::{Document, DocumentService, SignatureVerification};
|
||||
use crate::vault_setup::VaultClient;
|
||||
|
@ -48,6 +49,7 @@ pub struct SignDocumentRequest {
|
|||
// API response implementations
|
||||
impl IntoResponse for ApiError {
|
||||
fn into_response(self) -> Response {
|
||||
error!("API error: {}", self.0);
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Error: {}", self.0),
|
||||
|
@ -66,12 +68,13 @@ where
|
|||
}
|
||||
|
||||
// Start the API server
|
||||
#[instrument(skip(vault_addr, root_token))]
|
||||
pub async fn start_api(
|
||||
vault_addr: &str,
|
||||
root_token: &str,
|
||||
api_port: u16,
|
||||
) -> Result<()> {
|
||||
println!("Starting API server on port {}...", api_port);
|
||||
info!("Starting API server on port {}...", api_port);
|
||||
|
||||
// Initialize Vault client
|
||||
let vault_client = VaultClient::new(vault_addr, root_token);
|
||||
|
@ -101,14 +104,17 @@ pub async fn start_api(
|
|||
.route("/api/documents/:id/verify", get(verify_document))
|
||||
.with_state(state);
|
||||
|
||||
info!("API routes configured");
|
||||
|
||||
// Start server
|
||||
let listener = TcpListener::bind(format!("0.0.0.0:{}", api_port)).await?;
|
||||
println!("API server started on port {}", api_port);
|
||||
info!("API server started on port {}", api_port);
|
||||
|
||||
// Get the socket address
|
||||
let addr = listener.local_addr()?;
|
||||
|
||||
// Bind and serve
|
||||
info!("Serving API at {}", addr);
|
||||
Server::bind(&addr)
|
||||
.serve(app.into_make_service())
|
||||
.await?;
|
||||
|
@ -117,27 +123,36 @@ pub async fn start_api(
|
|||
}
|
||||
|
||||
// Health check endpoint
|
||||
#[instrument]
|
||||
async fn health_check() -> &'static str {
|
||||
debug!("Health check endpoint called");
|
||||
"OK"
|
||||
}
|
||||
|
||||
// Login endpoint
|
||||
#[instrument(skip(state, request), fields(username = %request.username))]
|
||||
async fn login(
|
||||
State(state): State<ApiState>,
|
||||
Json(request): Json<LoginRequest>,
|
||||
) -> Result<Json<LoginResponse>, ApiError> {
|
||||
info!("Login attempt for user: {}", request.username);
|
||||
|
||||
let token = state.vault_client
|
||||
.login_user(&request.username, &request.password)
|
||||
.await?;
|
||||
|
||||
info!("User {} successfully authenticated", request.username);
|
||||
Ok(Json(LoginResponse { token }))
|
||||
}
|
||||
|
||||
// Upload document endpoint
|
||||
#[instrument(skip(state, multipart))]
|
||||
async fn upload_document(
|
||||
State(state): State<ApiState>,
|
||||
mut multipart: Multipart,
|
||||
) -> Result<Json<Document>, ApiError> {
|
||||
info!("Document upload request received");
|
||||
|
||||
let mut document_name = String::new();
|
||||
let mut document_content = Vec::new();
|
||||
|
||||
|
@ -147,12 +162,15 @@ async fn upload_document(
|
|||
|
||||
if name == "name" {
|
||||
document_name = field.text().await?;
|
||||
debug!("Received document name: {}", document_name);
|
||||
} else if name == "file" {
|
||||
document_content = field.bytes().await?.to_vec();
|
||||
debug!("Received document content: {} bytes", document_content.len());
|
||||
}
|
||||
}
|
||||
|
||||
if document_name.is_empty() || document_content.is_empty() {
|
||||
error!("Missing document name or content");
|
||||
return Err(anyhow::anyhow!("Missing document name or content").into());
|
||||
}
|
||||
|
||||
|
@ -166,27 +184,37 @@ async fn upload_document(
|
|||
.get_document(&document_id)
|
||||
.await?;
|
||||
|
||||
info!("Document uploaded successfully with ID: {}", document_id);
|
||||
Ok(Json(document))
|
||||
}
|
||||
|
||||
// Get document endpoint
|
||||
#[instrument(skip(state))]
|
||||
async fn get_document(
|
||||
State(state): State<ApiState>,
|
||||
Path(document_id): Path<String>,
|
||||
) -> Result<Json<Document>, ApiError> {
|
||||
info!("Fetching document: {}", document_id);
|
||||
|
||||
let document = state.document_service
|
||||
.get_document(&document_id)
|
||||
.await?;
|
||||
|
||||
debug!("Retrieved document {} with {} signatures",
|
||||
document.id, document.signatures.len());
|
||||
|
||||
Ok(Json(document))
|
||||
}
|
||||
|
||||
// Sign document endpoint
|
||||
#[instrument(skip(state, request), fields(document_id = %document_id, username = %request.username))]
|
||||
async fn sign_document(
|
||||
State(state): State<ApiState>,
|
||||
Path(document_id): Path<String>,
|
||||
Json(request): Json<SignDocumentRequest>,
|
||||
) -> Result<Json<Document>, ApiError> {
|
||||
info!("Signing request for document {} by user {}", document_id, request.username);
|
||||
|
||||
state.document_service
|
||||
.sign_document(&document_id, &request.username, &request.token)
|
||||
.await?;
|
||||
|
@ -195,17 +223,24 @@ async fn sign_document(
|
|||
.get_document(&document_id)
|
||||
.await?;
|
||||
|
||||
info!("Document {} successfully signed by {}", document_id, request.username);
|
||||
Ok(Json(document))
|
||||
}
|
||||
|
||||
// Verify document endpoint
|
||||
#[instrument(skip(state))]
|
||||
async fn verify_document(
|
||||
State(state): State<ApiState>,
|
||||
Path(document_id): Path<String>,
|
||||
) -> Result<Json<SignatureVerification>, ApiError> {
|
||||
info!("Verifying document signatures: {}", document_id);
|
||||
|
||||
let verification = state.document_service
|
||||
.verify_document_signatures(&document_id)
|
||||
.await?;
|
||||
|
||||
info!("Document {} verification result: {}",
|
||||
document_id, if verification.is_verified { "VERIFIED" } else { "PENDING" });
|
||||
|
||||
Ok(Json(verification))
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ use sha2::{Sha256, Digest};
|
|||
use std::collections::HashMap;
|
||||
use uuid::Uuid;
|
||||
use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64};
|
||||
use tracing::{info, error, debug, instrument};
|
||||
|
||||
use crate::vault_setup::{Department, User, VaultClient};
|
||||
|
||||
|
@ -52,14 +53,19 @@ impl DocumentService {
|
|||
}
|
||||
|
||||
// Upload a new document and store its metadata
|
||||
#[instrument(skip(self, content), fields(document_name = %name))]
|
||||
pub async fn upload_document(&self, name: &str, content: &[u8]) -> Result<String> {
|
||||
info!("Uploading new document: {}", name);
|
||||
|
||||
// Generate a unique ID
|
||||
let id = Uuid::new_v4().to_string();
|
||||
debug!("Generated document ID: {}", id);
|
||||
|
||||
// Calculate document hash
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(content);
|
||||
let hash = format!("{:x}", hasher.finalize());
|
||||
debug!("Document hash: {}", hash);
|
||||
|
||||
// Create document metadata
|
||||
let document = Document {
|
||||
|
@ -73,12 +79,15 @@ impl DocumentService {
|
|||
// Store document metadata in Vault
|
||||
self.store_document_metadata(&document).await?;
|
||||
|
||||
println!("Document uploaded with ID: {}", id);
|
||||
info!("Document uploaded with ID: {}", id);
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
// Store document metadata in Vault
|
||||
#[instrument(skip(self, document), fields(document_id = %document.id))]
|
||||
async fn store_document_metadata(&self, document: &Document) -> Result<()> {
|
||||
debug!("Storing document metadata for {}", document.id);
|
||||
|
||||
let url = format!("{}/v1/documents/data/docs/{}",
|
||||
self.vault_client.addr, document.id);
|
||||
|
||||
|
@ -104,18 +113,22 @@ impl DocumentService {
|
|||
|
||||
match response.status() {
|
||||
StatusCode::OK | StatusCode::NO_CONTENT => {
|
||||
println!("Successfully stored document metadata for {}", document.id);
|
||||
info!("Successfully stored document metadata for {}", document.id);
|
||||
Ok(())
|
||||
}
|
||||
status => {
|
||||
let error_text = response.text().await?;
|
||||
error!("Failed to store document metadata: {} - {}", status, error_text);
|
||||
Err(anyhow::anyhow!("Failed to store document metadata: {} - {}", status, error_text))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get document metadata
|
||||
#[instrument(skip(self))]
|
||||
pub async fn get_document(&self, document_id: &str) -> Result<Document> {
|
||||
debug!("Getting document metadata for {}", document_id);
|
||||
|
||||
let url = format!("{}/v1/documents/data/docs/{}",
|
||||
self.vault_client.addr, document_id);
|
||||
|
||||
|
@ -137,7 +150,10 @@ impl DocumentService {
|
|||
let status = match status_str {
|
||||
"pending" => DocumentStatus::Pending,
|
||||
"verified" => DocumentStatus::Verified,
|
||||
_ => return Err(anyhow::anyhow!("Unknown status: {}", status_str)),
|
||||
_ => {
|
||||
error!("Unknown document status: {}", status_str);
|
||||
return Err(anyhow::anyhow!("Unknown status: {}", status_str));
|
||||
}
|
||||
};
|
||||
|
||||
// Extract signatures
|
||||
|
@ -160,17 +176,22 @@ impl DocumentService {
|
|||
signatures,
|
||||
};
|
||||
|
||||
debug!("Retrieved document: {} with {} signatures", document.id, document.signatures.len());
|
||||
Ok(document)
|
||||
}
|
||||
status => {
|
||||
let error_text = response.text().await?;
|
||||
error!("Failed to get document: {} - {}", status, error_text);
|
||||
Err(anyhow::anyhow!("Failed to get document: {} - {}", status, error_text))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sign a document with the user's key
|
||||
#[instrument(skip(self, user_token), fields(document_id = %document_id, username = %username))]
|
||||
pub async fn sign_document(&self, document_id: &str, username: &str, user_token: &str) -> Result<()> {
|
||||
info!("Signing document {} by user {}", document_id, username);
|
||||
|
||||
// Get document metadata
|
||||
let document = self.get_document(document_id).await?;
|
||||
|
||||
|
@ -200,6 +221,8 @@ impl DocumentService {
|
|||
.context("Failed to extract signature")?
|
||||
.to_string();
|
||||
|
||||
debug!("Generated signature for document {}", document_id);
|
||||
|
||||
// Update document with signature
|
||||
self.add_signature(document_id, username, &signature).await?;
|
||||
|
||||
|
@ -209,18 +232,22 @@ impl DocumentService {
|
|||
// Check if document now has enough signatures
|
||||
self.update_document_status(document_id).await?;
|
||||
|
||||
println!("Document {} signed by {}", document_id, username);
|
||||
info!("Document {} signed by {}", document_id, username);
|
||||
Ok(())
|
||||
}
|
||||
status => {
|
||||
let error_text = response.text().await?;
|
||||
error!("Failed to sign document: {} - {}", status, error_text);
|
||||
Err(anyhow::anyhow!("Failed to sign document: {} - {}", status, error_text))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add a signature to a document
|
||||
#[instrument(skip(self, signature), fields(document_id = %document_id, username = %username))]
|
||||
async fn add_signature(&self, document_id: &str, username: &str, signature: &str) -> Result<()> {
|
||||
debug!("Adding signature from {} to document {}", username, document_id);
|
||||
|
||||
// Get current document
|
||||
let mut document = self.get_document(document_id).await?;
|
||||
|
||||
|
@ -230,16 +257,20 @@ impl DocumentService {
|
|||
// Store updated document
|
||||
self.store_document_metadata(&document).await?;
|
||||
|
||||
debug!("Added signature from {} to document {}", username, document_id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Record department signature
|
||||
#[instrument(skip(self), fields(document_id = %document_id, department = ?user.department, username = %user.username))]
|
||||
async fn record_department_signature(&self, document_id: &str, user: &User) -> Result<()> {
|
||||
let dept_str = match user.department {
|
||||
Department::Legal => "legal",
|
||||
Department::Finance => "finance",
|
||||
};
|
||||
|
||||
debug!("Recording {} department signature for document {}", dept_str, document_id);
|
||||
|
||||
let url = format!("{}/v1/documents/data/dept/{}/signatures/{}",
|
||||
self.vault_client.addr, dept_str, document_id);
|
||||
|
||||
|
@ -287,18 +318,22 @@ impl DocumentService {
|
|||
|
||||
match response.status() {
|
||||
StatusCode::OK | StatusCode::NO_CONTENT => {
|
||||
println!("Recorded signature for {} in {} department", user.username, dept_str);
|
||||
info!("Recorded signature for {} in {} department", user.username, dept_str);
|
||||
Ok(())
|
||||
}
|
||||
status => {
|
||||
let error_text = response.text().await?;
|
||||
error!("Failed to record department signature: {} - {}", status, error_text);
|
||||
Err(anyhow::anyhow!("Failed to record department signature: {} - {}", status, error_text))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update document status if it has enough signatures
|
||||
#[instrument(skip(self))]
|
||||
async fn update_document_status(&self, document_id: &str) -> Result<()> {
|
||||
debug!("Checking if document {} has enough signatures", document_id);
|
||||
|
||||
// Verify signatures
|
||||
let verification = self.verify_document_signatures(document_id).await?;
|
||||
|
||||
|
@ -312,14 +347,26 @@ impl DocumentService {
|
|||
// Store updated document
|
||||
self.store_document_metadata(&document).await?;
|
||||
|
||||
println!("Document {} marked as verified", document_id);
|
||||
info!("Document {} marked as verified", document_id);
|
||||
} else {
|
||||
debug!(
|
||||
"Document {} not yet verified. Has {}/{} signatures ({} legal, {} finance)",
|
||||
document_id,
|
||||
verification.signatures_count,
|
||||
verification.required_signatures,
|
||||
verification.legal_signatures,
|
||||
verification.required_finance
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Verify document signatures
|
||||
#[instrument(skip(self))]
|
||||
pub async fn verify_document_signatures(&self, document_id: &str) -> Result<SignatureVerification> {
|
||||
info!("Verifying signatures for document {}", document_id);
|
||||
|
||||
// Get document
|
||||
let document = self.get_document(document_id).await?;
|
||||
|
||||
|
@ -337,6 +384,7 @@ impl DocumentService {
|
|||
StatusCode::OK => response.json::<serde_json::Value>().await?,
|
||||
status => {
|
||||
let error_text = response.text().await?;
|
||||
error!("Failed to get signing requirements: {} - {}", status, error_text);
|
||||
return Err(anyhow::anyhow!("Failed to get signing requirements: {} - {}", status, error_text));
|
||||
}
|
||||
};
|
||||
|
@ -384,11 +432,26 @@ impl DocumentService {
|
|||
required_finance,
|
||||
};
|
||||
|
||||
info!(
|
||||
"Verification result for document {}: verified={}, signatures={}/{}, legal={}/{}, finance={}/{}",
|
||||
document_id,
|
||||
verification.is_verified,
|
||||
verification.signatures_count,
|
||||
verification.required_signatures,
|
||||
verification.legal_signatures,
|
||||
verification.required_legal,
|
||||
verification.finance_signatures,
|
||||
verification.required_finance
|
||||
);
|
||||
|
||||
Ok(verification)
|
||||
}
|
||||
|
||||
// Get department signatures for a document
|
||||
#[instrument(skip(self))]
|
||||
async fn get_department_signatures(&self, document_id: &str, department: &str) -> Result<Vec<String>> {
|
||||
debug!("Getting {} department signatures for document {}", department, document_id);
|
||||
|
||||
let url = format!("{}/v1/documents/data/dept/{}/signatures/{}",
|
||||
self.vault_client.addr, department, document_id);
|
||||
|
||||
|
@ -414,6 +477,9 @@ impl DocumentService {
|
|||
}
|
||||
}
|
||||
|
||||
debug!("Found {} signatures for {} department on document {}",
|
||||
signatures.len(), department, document_id);
|
||||
|
||||
Ok(signatures)
|
||||
}
|
||||
}
|
||||
|
|
131
src/main.rs
131
src/main.rs
|
@ -9,6 +9,8 @@ use std::{
|
|||
time::Duration,
|
||||
};
|
||||
use tokio::time::sleep;
|
||||
use tracing::{info, warn, error, debug, instrument};
|
||||
use tracing_subscriber::{fmt, EnvFilter};
|
||||
|
||||
// Import our library
|
||||
use vault_hier::start_api;
|
||||
|
@ -52,7 +54,7 @@ fn save_credentials(response: &InitResponse, file_path: &str) -> Result<()> {
|
|||
|
||||
let mut file = File::create(Path::new(file_path))?;
|
||||
file.write_all(serde_json::to_string_pretty(&json)?.as_bytes())?;
|
||||
println!("Credentials saved to JSON file: {}", file_path);
|
||||
info!("Credentials saved to JSON file: {}", file_path);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
|
@ -69,13 +71,14 @@ fn save_credentials(response: &InitResponse, file_path: &str) -> Result<()> {
|
|||
writeln!(file)?;
|
||||
writeln!(file, "Root Token: {}", response.root_token)?;
|
||||
|
||||
println!("Credentials saved to {}", file_path);
|
||||
info!("Credentials saved to {}", file_path);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Wait for Vault to become available
|
||||
#[instrument(skip(addr))]
|
||||
async fn wait_for_vault(addr: &str) -> Result<()> {
|
||||
println!("Waiting for Vault to be ready...");
|
||||
info!("Waiting for Vault to be ready...");
|
||||
|
||||
let client = Client::new();
|
||||
|
||||
|
@ -87,30 +90,32 @@ async fn wait_for_vault(addr: &str) -> Result<()> {
|
|||
let status = response.status().as_u16();
|
||||
// Accept any of these status codes as "available"
|
||||
if matches!(status, 200 | 429 | 472 | 473 | 501 | 503) {
|
||||
println!("Vault is available! Status code: {}", status);
|
||||
info!("Vault is available! Status code: {}", status);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
println!("Vault returned unexpected status code: {}", status);
|
||||
debug!("Vault returned unexpected status code: {}", status);
|
||||
},
|
||||
Err(e) => {
|
||||
println!("Error connecting to Vault: {}", e);
|
||||
debug!("Error connecting to Vault: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
if i == 30 {
|
||||
error!("Timed out waiting for Vault to become available");
|
||||
return Err(anyhow::anyhow!("Timed out waiting for Vault to become available"));
|
||||
}
|
||||
|
||||
println!("Vault is unavailable - sleeping (attempt {}/30)", i);
|
||||
info!("Vault is unavailable - sleeping (attempt {}/30)", i);
|
||||
sleep(Duration::from_secs(2)).await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[instrument(skip(client, addr))]
|
||||
async fn check_init_status(client: &Client, addr: &str) -> Result<bool> {
|
||||
println!("Checking if Vault is already initialized...");
|
||||
info!("Checking if Vault is already initialized...");
|
||||
|
||||
let response = client
|
||||
.get(format!("{}/v1/sys/init", addr))
|
||||
|
@ -128,8 +133,9 @@ async fn check_init_status(client: &Client, addr: &str) -> Result<bool> {
|
|||
Ok(false)
|
||||
}
|
||||
|
||||
#[instrument(skip(client, addr))]
|
||||
async fn check_seal_status(client: &Client, addr: &str) -> Result<SealStatusResponse> {
|
||||
println!("Checking Vault seal status...");
|
||||
info!("Checking Vault seal status...");
|
||||
|
||||
let response = client
|
||||
.get(format!("{}/v1/sys/seal-status", addr))
|
||||
|
@ -138,24 +144,27 @@ async fn check_seal_status(client: &Client, addr: &str) -> Result<SealStatusResp
|
|||
|
||||
if response.status().is_success() {
|
||||
let status = response.json::<SealStatusResponse>().await?;
|
||||
println!("Seal status: sealed={}, threshold={}, shares={}, progress={}",
|
||||
info!("Seal status: sealed={}, threshold={}, shares={}, progress={}",
|
||||
status.sealed, status.t, status.n, status.progress);
|
||||
return Ok(status);
|
||||
} else {
|
||||
let error_text = response.text().await?;
|
||||
error!("Failed to get seal status: {}", error_text);
|
||||
anyhow::bail!("Failed to get seal status: {}", error_text);
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(client, addr))]
|
||||
async fn init_vault(client: &Client, addr: &str) -> Result<InitResponse> {
|
||||
// First check if already initialized
|
||||
let initialized = check_init_status(client, addr).await?;
|
||||
|
||||
if initialized {
|
||||
error!("Vault is already initialized. Cannot re-initialize.");
|
||||
anyhow::bail!("Vault is already initialized. Cannot re-initialize.");
|
||||
}
|
||||
|
||||
println!("Initializing Vault...");
|
||||
info!("Initializing Vault...");
|
||||
|
||||
// Configure with 5 key shares and a threshold of 3
|
||||
// This is a standard production configuration, requiring 3 out of 5 keys to unseal
|
||||
|
@ -173,32 +182,39 @@ async fn init_vault(client: &Client, addr: &str) -> Result<InitResponse> {
|
|||
match response.status() {
|
||||
StatusCode::OK => {
|
||||
let init_response = response.json::<InitResponse>().await?;
|
||||
println!("Vault initialized successfully!");
|
||||
info!("Vault initialized successfully!");
|
||||
Ok(init_response)
|
||||
}
|
||||
status => {
|
||||
let error_text = response.text().await?;
|
||||
error!("Failed to initialize Vault: {} - {}", status, error_text);
|
||||
anyhow::bail!("Failed to initialize Vault: {} - {}", status, error_text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(client, addr, unseal_keys))]
|
||||
async fn unseal_vault(client: &Client, addr: &str, unseal_keys: &[String]) -> Result<()> {
|
||||
// First check the current seal status
|
||||
let mut seal_status = check_seal_status(client, addr).await?;
|
||||
|
||||
if !seal_status.sealed {
|
||||
println!("Vault is already unsealed!");
|
||||
info!("Vault is already unsealed!");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
println!("Unsealing Vault...");
|
||||
info!("Unsealing Vault...");
|
||||
|
||||
// We need to provide enough keys to meet the threshold
|
||||
// The threshold is in seal_status.t
|
||||
let required_keys = seal_status.t as usize;
|
||||
|
||||
if unseal_keys.len() < required_keys {
|
||||
error!(
|
||||
"Not enough unseal keys provided. Need {} keys, but only have {}",
|
||||
required_keys,
|
||||
unseal_keys.len()
|
||||
);
|
||||
anyhow::bail!(
|
||||
"Not enough unseal keys provided. Need {} keys, but only have {}",
|
||||
required_keys,
|
||||
|
@ -208,7 +224,7 @@ async fn unseal_vault(client: &Client, addr: &str, unseal_keys: &[String]) -> Re
|
|||
|
||||
// Apply each key one at a time until unsealed
|
||||
for (i, key) in unseal_keys.iter().take(required_keys).enumerate() {
|
||||
println!("Applying unseal key {}/{}...", i + 1, required_keys);
|
||||
info!("Applying unseal key {}/{}...", i + 1, required_keys);
|
||||
|
||||
let unseal_req = UnsealRequest {
|
||||
key: key.clone(),
|
||||
|
@ -222,6 +238,7 @@ async fn unseal_vault(client: &Client, addr: &str, unseal_keys: &[String]) -> Re
|
|||
|
||||
if !response.status().is_success() {
|
||||
let error_text = response.text().await?;
|
||||
error!("Failed to apply unseal key: {}", error_text);
|
||||
anyhow::bail!("Failed to apply unseal key: {}", error_text);
|
||||
}
|
||||
|
||||
|
@ -229,13 +246,14 @@ async fn unseal_vault(client: &Client, addr: &str, unseal_keys: &[String]) -> Re
|
|||
seal_status = check_seal_status(client, addr).await?;
|
||||
|
||||
if !seal_status.sealed {
|
||||
println!("Vault unsealed successfully after applying {} keys!", i + 1);
|
||||
info!("Vault unsealed successfully after applying {} keys!", i + 1);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
// If we get here, we've applied all keys but Vault is still sealed
|
||||
if seal_status.sealed {
|
||||
error!("Applied all available unseal keys, but Vault is still sealed");
|
||||
anyhow::bail!("Applied all available unseal keys, but Vault is still sealed");
|
||||
}
|
||||
|
||||
|
@ -244,6 +262,12 @@ async fn unseal_vault(client: &Client, addr: &str, unseal_keys: &[String]) -> Re
|
|||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
// Initialize tracing
|
||||
fmt()
|
||||
.with_env_filter(EnvFilter::from_default_env().add_directive("vault_hier=info".parse()?))
|
||||
.with_target(false)
|
||||
.init();
|
||||
|
||||
// Get Vault address from env var or use default
|
||||
let vault_addr = env::var("VAULT_ADDR").unwrap_or_else(|_| "http://127.0.0.1:8200".to_string());
|
||||
let client = Client::new();
|
||||
|
@ -254,8 +278,8 @@ async fn main() -> Result<()> {
|
|||
.parse::<u16>()
|
||||
.unwrap_or(3000);
|
||||
|
||||
println!("Vault address: {}", vault_addr);
|
||||
println!("Connecting to Vault at: {}", vault_addr);
|
||||
info!("Vault address: {}", vault_addr);
|
||||
info!("Connecting to Vault at: {}", vault_addr);
|
||||
|
||||
// Wait for Vault to be available
|
||||
wait_for_vault(&vault_addr).await?;
|
||||
|
@ -266,10 +290,10 @@ async fn main() -> Result<()> {
|
|||
Ok(response) => {
|
||||
if response.status().is_success() {
|
||||
let status_text = response.text().await?;
|
||||
println!("Vault status: {}", status_text);
|
||||
info!("Vault status: {}", status_text);
|
||||
}
|
||||
},
|
||||
Err(e) => println!("Error getting Vault status: {}", e),
|
||||
Err(e) => warn!("Error getting Vault status: {}", e),
|
||||
}
|
||||
|
||||
// First check if Vault is already initialized
|
||||
|
@ -277,44 +301,44 @@ async fn main() -> Result<()> {
|
|||
let mut root_token = String::new();
|
||||
|
||||
if initialized {
|
||||
println!("Vault is already initialized.");
|
||||
info!("Vault is already initialized.");
|
||||
|
||||
// Check if Vault is sealed
|
||||
let seal_status = check_seal_status(&client, &vault_addr).await?;
|
||||
|
||||
if seal_status.sealed {
|
||||
println!("Vault is sealed. Looking for unseal keys...");
|
||||
info!("Vault is sealed. Looking for unseal keys...");
|
||||
|
||||
// Try to load unseal keys from environment variables
|
||||
let mut unseal_keys = Vec::new();
|
||||
for i in 1..=5 {
|
||||
match env::var(format!("VAULT_UNSEAL_KEY_{}", i)) {
|
||||
Ok(key) => {
|
||||
println!("Found unseal key {} from environment", i);
|
||||
info!("Found unseal key {} from environment", i);
|
||||
unseal_keys.push(key);
|
||||
},
|
||||
Err(_) => {
|
||||
println!("Unseal key {} not found in environment", i);
|
||||
debug!("Unseal key {} not found in environment", i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we have unseal keys, try to unseal
|
||||
if !unseal_keys.is_empty() {
|
||||
println!("Found {} unseal keys. Attempting to unseal...", unseal_keys.len());
|
||||
info!("Found {} unseal keys. Attempting to unseal...", unseal_keys.len());
|
||||
unseal_vault(&client, &vault_addr, &unseal_keys).await?;
|
||||
} else {
|
||||
println!("No unseal keys found. Vault remains sealed.");
|
||||
println!("To unseal, set VAULT_UNSEAL_KEY_1, VAULT_UNSEAL_KEY_2, etc. environment variables.");
|
||||
warn!("No unseal keys found. Vault remains sealed.");
|
||||
info!("To unseal, set VAULT_UNSEAL_KEY_1, VAULT_UNSEAL_KEY_2, etc. environment variables.");
|
||||
}
|
||||
} else {
|
||||
println!("Vault is already unsealed.");
|
||||
info!("Vault is already unsealed.");
|
||||
}
|
||||
|
||||
// Try to load root token from environment or credentials file
|
||||
match env::var("VAULT_TOKEN") {
|
||||
Ok(token) => {
|
||||
println!("Found root token from environment");
|
||||
info!("Found root token from environment");
|
||||
root_token = token;
|
||||
},
|
||||
Err(_) => {
|
||||
|
@ -322,7 +346,7 @@ async fn main() -> Result<()> {
|
|||
if let Ok(contents) = std::fs::read_to_string("vault-credentials.json") {
|
||||
if let Ok(creds) = serde_json::from_str::<serde_json::Value>(&contents) {
|
||||
if let Some(token) = creds["root_token"].as_str() {
|
||||
println!("Found root token from credentials file");
|
||||
info!("Found root token from credentials file");
|
||||
root_token = token.to_string();
|
||||
}
|
||||
}
|
||||
|
@ -331,47 +355,48 @@ async fn main() -> Result<()> {
|
|||
}
|
||||
|
||||
if root_token.is_empty() {
|
||||
error!("Unable to find root token. Please set VAULT_TOKEN environment variable or provide vault-credentials.json file.");
|
||||
anyhow::bail!("Unable to find root token. Please set VAULT_TOKEN environment variable or provide vault-credentials.json file.");
|
||||
}
|
||||
} else {
|
||||
// Initialize Vault
|
||||
println!("Vault is not initialized. Proceeding with initialization...");
|
||||
info!("Vault is not initialized. Proceeding with initialization...");
|
||||
let init_response = init_vault(&client, &vault_addr).await?;
|
||||
|
||||
// Save credentials to files
|
||||
println!("Saving credentials to files...");
|
||||
info!("Saving credentials to files...");
|
||||
let current_dir = std::env::current_dir().context("Failed to get current directory")?;
|
||||
|
||||
// Save as JSON (new format)
|
||||
let json_path = current_dir.join("vault-credentials.json");
|
||||
save_credentials(&init_response, json_path.to_str().unwrap())?;
|
||||
println!("JSON credentials saved to: {}", json_path.display());
|
||||
info!("JSON credentials saved to: {}", json_path.display());
|
||||
|
||||
// Save as text (for backward compatibility)
|
||||
let text_path = current_dir.join("vault-credentials.txt");
|
||||
save_credentials(&init_response, text_path.to_str().unwrap())?;
|
||||
println!("Text credentials saved to: {}", text_path.display());
|
||||
info!("Text credentials saved to: {}", text_path.display());
|
||||
|
||||
// Also save to /app/data as a backup for Docker volume mounting
|
||||
if let Ok(()) = std::fs::create_dir_all("/app/data") {
|
||||
let docker_json_path = "/app/data/vault-credentials.json";
|
||||
save_credentials(&init_response, docker_json_path)?;
|
||||
println!("Backup JSON credentials saved to Docker volume at: {}", docker_json_path);
|
||||
info!("Backup JSON credentials saved to Docker volume at: {}", docker_json_path);
|
||||
|
||||
let docker_text_path = "/app/data/vault-credentials.txt";
|
||||
save_credentials(&init_response, docker_text_path)?;
|
||||
println!("Backup text credentials saved to Docker volume at: {}", docker_text_path);
|
||||
info!("Backup text credentials saved to Docker volume at: {}", docker_text_path);
|
||||
}
|
||||
|
||||
println!("=========================================");
|
||||
println!("IMPORTANT: SAVE THESE CREDENTIALS SECURELY");
|
||||
println!("=========================================");
|
||||
println!("Root Token: {}", init_response.root_token);
|
||||
println!("Unseal Keys (first 3 of 5 needed to unseal):");
|
||||
info!("=========================================");
|
||||
info!("IMPORTANT: SAVE THESE CREDENTIALS SECURELY");
|
||||
info!("=========================================");
|
||||
info!("Root Token: {}", init_response.root_token);
|
||||
info!("Unseal Keys (first 3 of 5 needed to unseal):");
|
||||
for (i, key) in init_response.keys_base64.iter().enumerate() {
|
||||
println!("Key {}: {}", i + 1, key);
|
||||
info!("Key {}: {}", i + 1, key);
|
||||
}
|
||||
println!("=========================================");
|
||||
info!("=========================================");
|
||||
|
||||
// Unseal Vault using the first three keys
|
||||
let unseal_keys = init_response.keys_base64.iter()
|
||||
|
@ -381,7 +406,7 @@ async fn main() -> Result<()> {
|
|||
|
||||
unseal_vault(&client, &vault_addr, &unseal_keys).await?;
|
||||
|
||||
println!("Vault initialization and unseal complete!");
|
||||
info!("Vault initialization and unseal complete!");
|
||||
|
||||
// Set root token
|
||||
root_token = init_response.root_token;
|
||||
|
@ -390,11 +415,11 @@ async fn main() -> Result<()> {
|
|||
// Look for any existing credentials and copy them to the mounted volume
|
||||
if let Ok(metadata) = std::fs::metadata("vault-credentials.json") {
|
||||
if metadata.is_file() {
|
||||
println!("Found JSON credentials file, ensuring it's saved to Docker volume...");
|
||||
info!("Found JSON credentials file, ensuring it's saved to Docker volume...");
|
||||
if let Ok(()) = std::fs::create_dir_all("/app/data") {
|
||||
match std::fs::copy("vault-credentials.json", "/app/data/vault-credentials.json") {
|
||||
Ok(_) => println!("JSON credentials saved to Docker volume"),
|
||||
Err(e) => println!("Failed to copy JSON credentials: {}", e),
|
||||
Ok(_) => info!("JSON credentials saved to Docker volume"),
|
||||
Err(e) => warn!("Failed to copy JSON credentials: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -402,23 +427,23 @@ async fn main() -> Result<()> {
|
|||
|
||||
if let Ok(metadata) = std::fs::metadata("vault-credentials.txt") {
|
||||
if metadata.is_file() {
|
||||
println!("Found text credentials file, ensuring it's saved to Docker volume...");
|
||||
info!("Found text credentials file, ensuring it's saved to Docker volume...");
|
||||
if let Ok(()) = std::fs::create_dir_all("/app/data") {
|
||||
match std::fs::copy("vault-credentials.txt", "/app/data/vault-credentials.txt") {
|
||||
Ok(_) => println!("Text credentials saved to Docker volume"),
|
||||
Err(e) => println!("Failed to copy text credentials: {}", e),
|
||||
Ok(_) => info!("Text credentials saved to Docker volume"),
|
||||
Err(e) => warn!("Failed to copy text credentials: {}", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
println!("Vault setup complete!");
|
||||
println!("Starting hierarchical document signing API...");
|
||||
info!("Vault setup complete!");
|
||||
info!("Starting hierarchical document signing API...");
|
||||
|
||||
// Start the hierarchical signing API
|
||||
start_api(&vault_addr, &root_token, api_port).await?;
|
||||
|
||||
println!("API server shutdown. Exiting.");
|
||||
info!("API server shutdown. Exiting.");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ use anyhow::{Context, Result};
|
|||
use reqwest::{Client, StatusCode};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use tracing::{info, error, debug, instrument};
|
||||
|
||||
// Department types for organizational structure
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
@ -35,8 +36,9 @@ impl VaultClient {
|
|||
}
|
||||
|
||||
// Enable required secrets engines
|
||||
#[instrument(skip(self))]
|
||||
pub async fn setup_secrets_engines(&self) -> Result<()> {
|
||||
println!("Setting up Vault secrets engines...");
|
||||
info!("Setting up Vault secrets engines...");
|
||||
|
||||
// Enable Transit for document signing
|
||||
self.enable_secrets_engine("transit", "transit").await?;
|
||||
|
@ -47,13 +49,14 @@ impl VaultClient {
|
|||
// Enable userpass for authentication
|
||||
self.enable_auth_method("userpass").await?;
|
||||
|
||||
println!("Secrets engines setup complete!");
|
||||
info!("Secrets engines setup complete!");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Enable a secrets engine
|
||||
#[instrument(skip(self))]
|
||||
async fn enable_secrets_engine(&self, engine_type: &str, path: &str) -> Result<()> {
|
||||
println!("Enabling {} secrets engine at {}", engine_type, path);
|
||||
info!("Enabling {} secrets engine at {}", engine_type, path);
|
||||
|
||||
let url = format!("{}/v1/sys/mounts/{}", self.addr, path);
|
||||
let payload = json!({
|
||||
|
@ -69,29 +72,32 @@ impl VaultClient {
|
|||
|
||||
match response.status() {
|
||||
StatusCode::NO_CONTENT | StatusCode::OK => {
|
||||
println!("Successfully enabled {} engine at {}", engine_type, path);
|
||||
info!("Successfully enabled {} engine at {}", engine_type, path);
|
||||
Ok(())
|
||||
}
|
||||
StatusCode::BAD_REQUEST => {
|
||||
// Check if already exists
|
||||
let error_text = response.text().await?;
|
||||
if error_text.contains("path is already in use") {
|
||||
println!("Secrets engine already enabled at {}", path);
|
||||
info!("Secrets engine already enabled at {}", path);
|
||||
Ok(())
|
||||
} else {
|
||||
error!("Failed to enable secrets engine: {}", error_text);
|
||||
Err(anyhow::anyhow!("Failed to enable secrets engine: {}", error_text))
|
||||
}
|
||||
}
|
||||
status => {
|
||||
let error_text = response.text().await?;
|
||||
error!("Failed to enable secrets engine: {} - {}", status, error_text);
|
||||
Err(anyhow::anyhow!("Failed to enable secrets engine: {} - {}", status, error_text))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Enable an auth method
|
||||
#[instrument(skip(self))]
|
||||
async fn enable_auth_method(&self, method: &str) -> Result<()> {
|
||||
println!("Enabling {} auth method", method);
|
||||
info!("Enabling {} auth method", method);
|
||||
|
||||
let url = format!("{}/v1/sys/auth/{}", self.addr, method);
|
||||
let payload = json!({
|
||||
|
@ -107,29 +113,32 @@ impl VaultClient {
|
|||
|
||||
match response.status() {
|
||||
StatusCode::NO_CONTENT | StatusCode::OK => {
|
||||
println!("Successfully enabled {} auth method", method);
|
||||
info!("Successfully enabled {} auth method", method);
|
||||
Ok(())
|
||||
}
|
||||
StatusCode::BAD_REQUEST => {
|
||||
// Check if already exists
|
||||
let error_text = response.text().await?;
|
||||
if error_text.contains("path is already in use") {
|
||||
println!("Auth method already enabled at {}", method);
|
||||
info!("Auth method already enabled at {}", method);
|
||||
Ok(())
|
||||
} else {
|
||||
error!("Failed to enable auth method: {}", error_text);
|
||||
Err(anyhow::anyhow!("Failed to enable auth method: {}", error_text))
|
||||
}
|
||||
}
|
||||
status => {
|
||||
let error_text = response.text().await?;
|
||||
error!("Failed to enable auth method: {} - {}", status, error_text);
|
||||
Err(anyhow::anyhow!("Failed to enable auth method: {} - {}", status, error_text))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new user in Vault and associate with department
|
||||
#[instrument(skip(self, password))]
|
||||
pub async fn create_user(&self, username: &str, password: &str, department: Department) -> Result<()> {
|
||||
println!("Creating user {} in department {:?}", username, department);
|
||||
info!("Creating user {} in department {:?}", username, department);
|
||||
|
||||
// Step 1: Create a policy for the user
|
||||
let policy_name = format!("{}-policy", username);
|
||||
|
@ -151,7 +160,7 @@ impl VaultClient {
|
|||
|
||||
match response.status() {
|
||||
StatusCode::NO_CONTENT | StatusCode::OK => {
|
||||
println!("Successfully created user {}", username);
|
||||
info!("Successfully created user {}", username);
|
||||
|
||||
// Step 3: Create a signing key for the user
|
||||
self.create_signing_key(username).await?;
|
||||
|
@ -163,12 +172,14 @@ impl VaultClient {
|
|||
}
|
||||
status => {
|
||||
let error_text = response.text().await?;
|
||||
error!("Failed to create user: {} - {}", status, error_text);
|
||||
Err(anyhow::anyhow!("Failed to create user: {} - {}", status, error_text))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create a signing policy for a user based on their department
|
||||
#[instrument(skip(self))]
|
||||
async fn create_signing_policy(&self, policy_name: &str, department: Department) -> Result<()> {
|
||||
let dept_name = match department {
|
||||
Department::Legal => "legal",
|
||||
|
@ -212,17 +223,19 @@ impl VaultClient {
|
|||
|
||||
match response.status() {
|
||||
StatusCode::NO_CONTENT | StatusCode::OK => {
|
||||
println!("Successfully created policy {}", policy_name);
|
||||
info!("Successfully created policy {}", policy_name);
|
||||
Ok(())
|
||||
}
|
||||
status => {
|
||||
let error_text = response.text().await?;
|
||||
error!("Failed to create policy: {} - {}", status, error_text);
|
||||
Err(anyhow::anyhow!("Failed to create policy: {} - {}", status, error_text))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create a signing key for a user in the Transit engine
|
||||
#[instrument(skip(self))]
|
||||
async fn create_signing_key(&self, username: &str) -> Result<()> {
|
||||
let url = format!("{}/v1/transit/keys/{}", self.addr, username);
|
||||
let payload = json!({
|
||||
|
@ -238,17 +251,19 @@ impl VaultClient {
|
|||
|
||||
match response.status() {
|
||||
StatusCode::NO_CONTENT | StatusCode::OK => {
|
||||
println!("Successfully created signing key for {}", username);
|
||||
info!("Successfully created signing key for {}", username);
|
||||
Ok(())
|
||||
}
|
||||
status => {
|
||||
let error_text = response.text().await?;
|
||||
error!("Failed to create signing key: {} - {}", status, error_text);
|
||||
Err(anyhow::anyhow!("Failed to create signing key: {} - {}", status, error_text))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Store user metadata in the KV store
|
||||
#[instrument(skip(self))]
|
||||
async fn store_user_metadata(&self, username: &str, department: Department) -> Result<()> {
|
||||
let dept_str = match department {
|
||||
Department::Legal => "legal",
|
||||
|
@ -272,22 +287,27 @@ impl VaultClient {
|
|||
|
||||
match response.status() {
|
||||
StatusCode::NO_CONTENT | StatusCode::OK => {
|
||||
println!("Successfully stored metadata for user {}", username);
|
||||
info!("Successfully stored metadata for user {}", username);
|
||||
Ok(())
|
||||
}
|
||||
status => {
|
||||
let error_text = response.text().await?;
|
||||
error!("Failed to store user metadata: {} - {}", status, error_text);
|
||||
Err(anyhow::anyhow!("Failed to store user metadata: {} - {}", status, error_text))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create 10 users with departmental hierarchy - 5 in each department
|
||||
#[instrument(skip(self))]
|
||||
pub async fn setup_hierarchical_users(&self) -> Result<()> {
|
||||
info!("Setting up hierarchical user structure");
|
||||
|
||||
// Create 5 users in Legal department
|
||||
for i in 1..=5 {
|
||||
let username = format!("legal{}", i);
|
||||
let password = format!("legal{}pass", i);
|
||||
debug!(username, "Creating Legal department user");
|
||||
self.create_user(&username, &password, Department::Legal).await?;
|
||||
}
|
||||
|
||||
|
@ -295,18 +315,22 @@ impl VaultClient {
|
|||
for i in 1..=5 {
|
||||
let username = format!("finance{}", i);
|
||||
let password = format!("finance{}pass", i);
|
||||
debug!(username, "Creating Finance department user");
|
||||
self.create_user(&username, &password, Department::Finance).await?;
|
||||
}
|
||||
|
||||
// Setup document signing requirements
|
||||
self.setup_signing_requirements().await?;
|
||||
|
||||
println!("Successfully created 10 users in hierarchical structure!");
|
||||
info!("Successfully created 10 users in hierarchical structure!");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Configure document signing requirements
|
||||
#[instrument(skip(self))]
|
||||
async fn setup_signing_requirements(&self) -> Result<()> {
|
||||
info!("Setting up document signing requirements");
|
||||
|
||||
let url = format!("{}/v1/documents/data/config/signing_requirements", self.addr);
|
||||
let payload = json!({
|
||||
"data": {
|
||||
|
@ -336,18 +360,22 @@ impl VaultClient {
|
|||
|
||||
match response.status() {
|
||||
StatusCode::NO_CONTENT | StatusCode::OK => {
|
||||
println!("Successfully configured signing requirements");
|
||||
info!("Successfully configured signing requirements");
|
||||
Ok(())
|
||||
}
|
||||
status => {
|
||||
let error_text = response.text().await?;
|
||||
error!("Failed to configure signing requirements: {} - {}", status, error_text);
|
||||
Err(anyhow::anyhow!("Failed to configure signing requirements: {} - {}", status, error_text))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Login a user and get their token
|
||||
#[instrument(skip(self, password))]
|
||||
pub async fn login_user(&self, username: &str, password: &str) -> Result<String> {
|
||||
info!("Attempting user login: {}", username);
|
||||
|
||||
let url = format!("{}/v1/auth/userpass/login/{}", self.addr, username);
|
||||
let payload = json!({
|
||||
"password": password,
|
||||
|
@ -367,18 +395,22 @@ impl VaultClient {
|
|||
.context("Failed to extract client token")?
|
||||
.to_string();
|
||||
|
||||
println!("User {} successfully logged in", username);
|
||||
info!("User {} successfully logged in", username);
|
||||
Ok(token)
|
||||
}
|
||||
status => {
|
||||
let error_text = response.text().await?;
|
||||
error!("Failed to login: {} - {}", status, error_text);
|
||||
Err(anyhow::anyhow!("Failed to login: {} - {}", status, error_text))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get user info including department
|
||||
#[instrument(skip(self))]
|
||||
pub async fn get_user_info(&self, username: &str) -> Result<User> {
|
||||
debug!("Getting user info for: {}", username);
|
||||
|
||||
let url = format!("{}/v1/documents/data/users/{}", self.addr, username);
|
||||
|
||||
let response = self.client
|
||||
|
@ -397,9 +429,13 @@ impl VaultClient {
|
|||
let department = match department_str {
|
||||
"legal" => Department::Legal,
|
||||
"finance" => Department::Finance,
|
||||
_ => return Err(anyhow::anyhow!("Unknown department: {}", department_str)),
|
||||
_ => {
|
||||
error!("Unknown department: {}", department_str);
|
||||
return Err(anyhow::anyhow!("Unknown department: {}", department_str));
|
||||
}
|
||||
};
|
||||
|
||||
debug!("Retrieved user info for {} in {:?} department", username, department);
|
||||
Ok(User {
|
||||
username: username.to_string(),
|
||||
department,
|
||||
|
@ -407,6 +443,7 @@ impl VaultClient {
|
|||
}
|
||||
status => {
|
||||
let error_text = response.text().await?;
|
||||
error!("Failed to get user info: {} - {}", status, error_text);
|
||||
Err(anyhow::anyhow!("Failed to get user info: {} - {}", status, error_text))
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue