use std::sync::Arc;
use crate::actix_web::HttpResponse;
use crate::futures::{Future, IntoFuture};
#[cfg(feature = "authorization")]
use crate::rest_api::auth::authorization::Permission;
use crate::rest_api::{
actix_web_1::{into_bytes, Method, ProtocolVersionRangeGuard, Resource},
secrets::SecretManager,
sessions::default_validation,
ErrorResponse, SPLINTER_PROTOCOL_VERSION,
};
use crate::biome::credentials::rest_api::actix_web_1::config::BiomeCredentialsRestConfig;
use crate::biome::credentials::store::{CredentialsStore, CredentialsStoreError};
use super::super::resources::authorize::AuthorizationResult;
use super::super::resources::credentials::UsernamePassword;
use super::authorize::authorize_user;
const BIOME_VERIFY_PROTOCOL_MIN: u32 = 1;
pub fn make_verify_route(
credentials_store: Arc<dyn CredentialsStore>,
rest_config: Arc<BiomeCredentialsRestConfig>,
secret_manager: Arc<dyn SecretManager>,
) -> Resource {
let resource = Resource::build("/biome/verify").add_request_guard(
ProtocolVersionRangeGuard::new(BIOME_VERIFY_PROTOCOL_MIN, SPLINTER_PROTOCOL_VERSION),
);
#[cfg(feature = "authorization")]
{
resource.add_method(
Method::Post,
Permission::AllowAuthenticated,
move |request, payload| {
let credentials_store = credentials_store.clone();
let rest_config = rest_config.clone();
let secret_manager = secret_manager.clone();
Box::new(into_bytes(payload).and_then(move |bytes| {
let username_password = match serde_json::from_slice::<UsernamePassword>(&bytes)
{
Ok(val) => val,
Err(err) => {
debug!("Error parsing payload: {}", err);
return HttpResponse::BadRequest()
.json(ErrorResponse::bad_request(&format!(
"Failed to parse payload: {}",
err
)))
.into_future();
}
};
let credentials = match credentials_store
.fetch_credential_by_username(&username_password.username)
{
Ok(credentials) => credentials,
Err(err) => {
debug!("Failed to fetch credentials: {}", err);
match err {
CredentialsStoreError::NotFoundError(_) => {
return HttpResponse::BadRequest()
.json(ErrorResponse::bad_request(&format!(
"Username not found: {}",
username_password.username
)))
.into_future()
}
_ => {
error!("Failed to fetch credentials: {}", err);
return HttpResponse::InternalServerError()
.json(ErrorResponse::internal_error())
.into_future();
}
}
}
};
let validation = default_validation(&rest_config.issuer());
match authorize_user(&request, &secret_manager, &validation) {
AuthorizationResult::Authorized(_) => {
match credentials.verify_password(&username_password.hashed_password) {
Ok(true) => HttpResponse::Ok()
.json(json!(
{
"message": "Successful verification",
"user_id": credentials.user_id
}))
.into_future(),
Ok(false) => HttpResponse::BadRequest()
.json(ErrorResponse::bad_request("Invalid password"))
.into_future(),
Err(err) => {
error!("Failed to verify password: {}", err);
HttpResponse::InternalServerError()
.json(ErrorResponse::internal_error())
.into_future()
}
}
}
AuthorizationResult::Unauthorized => HttpResponse::Unauthorized()
.json(ErrorResponse::unauthorized())
.into_future(),
AuthorizationResult::Failed => {
error!("Failed to authorize user");
HttpResponse::InternalServerError()
.json(ErrorResponse::internal_error())
.into_future()
}
}
}))
},
)
}
#[cfg(not(feature = "authorization"))]
{
resource.add_method(Method::Post, move |request, payload| {
let credentials_store = credentials_store.clone();
let rest_config = rest_config.clone();
let secret_manager = secret_manager.clone();
Box::new(into_bytes(payload).and_then(move |bytes| {
let username_password = match serde_json::from_slice::<UsernamePassword>(&bytes) {
Ok(val) => val,
Err(err) => {
debug!("Error parsing payload: {}", err);
return HttpResponse::BadRequest()
.json(ErrorResponse::bad_request(&format!(
"Failed to parse payload: {}",
err
)))
.into_future();
}
};
let credentials = match credentials_store
.fetch_credential_by_username(&username_password.username)
{
Ok(credentials) => credentials,
Err(err) => {
debug!("Failed to fetch credentials: {}", err);
match err {
CredentialsStoreError::NotFoundError(_) => {
return HttpResponse::BadRequest()
.json(ErrorResponse::bad_request(&format!(
"Username not found: {}",
username_password.username
)))
.into_future()
}
_ => {
error!("Failed to fetch credentials: {}", err);
return HttpResponse::InternalServerError()
.json(ErrorResponse::internal_error())
.into_future();
}
}
}
};
let validation = default_validation(&rest_config.issuer());
match authorize_user(&request, &secret_manager, &validation) {
AuthorizationResult::Authorized(_) => {
match credentials.verify_password(&username_password.hashed_password) {
Ok(true) => HttpResponse::Ok()
.json(json!(
{
"message": "Successful verification",
"user_id": credentials.user_id
}))
.into_future(),
Ok(false) => HttpResponse::BadRequest()
.json(ErrorResponse::bad_request("Invalid password"))
.into_future(),
Err(err) => {
error!("Failed to verify password: {}", err);
HttpResponse::InternalServerError()
.json(ErrorResponse::internal_error())
.into_future()
}
}
}
AuthorizationResult::Unauthorized => HttpResponse::Unauthorized()
.json(ErrorResponse::unauthorized())
.into_future(),
AuthorizationResult::Failed => {
error!("Failed to authorize user");
HttpResponse::InternalServerError()
.json(ErrorResponse::internal_error())
.into_future()
}
}
}))
})
}
}