use std::sync::Arc;
use uuid::Uuid;
use tracing::{error, warn};
use acme_proxy_core::error::Problem;
use acme_proxy_store::account::Account;
use acme_proxy_store::authz::Authorization;
use acme_proxy_store::authz::Challenge;
use acme_proxy_store::db::Database;
use acme_proxy_store::nonce::now_secs;
use acme_proxy_store::order::Order;
use acme_proxy_store::status::OrderStatus;
pub(crate) async fn signer_account(
cached: Option<Account>,
profile: &str,
pubkey: &[u8],
database: &Arc<Database>,
) -> Result<Account, Problem> {
let account = match cached {
Some(account) => account,
None => Account::find_by_pubkey(profile, pubkey, database)
.await
.map_err(|error| {
error!(event = "account_lookup_failed", outcome = "failure", error = %error);
Problem::server_internal("Account lookup failed")
})?
.ok_or_else(|| Problem::account_does_not_exist("Unknown account"))?,
};
if account.is_deactivated() {
warn!(event = "account_deactivated_request_refused", outcome = "failure", account_id = %account.id);
return Err(Problem::unauthorized("Account is deactivated"));
}
Ok(account)
}
pub(crate) async fn load_owned_order(
id: &str,
account: &Account,
database: &Arc<Database>,
) -> Result<Order, Problem> {
owned_order(id, account, database, "Unknown order").await
}
async fn owned_order(
id: &str,
account: &Account,
database: &Arc<Database>,
unknown: &'static str,
) -> Result<Order, Problem> {
let order = Order::find_by_id(id, database)
.await
.map_err(|error| {
error!(event = "order_lookup_failed", outcome = "failure", order_id = %id, error = %error);
Problem::server_internal("Order lookup failed")
})?
.ok_or_else(|| Problem::malformed(unknown))?;
if order.profile != account.profile {
warn!(
event = "order_profile_mismatch",
outcome = "failure",
order_id = %id,
order_profile = %order.profile,
request_profile = %account.profile
);
return Err(Problem::malformed(unknown));
}
if order.account_id != account.id {
warn!(event = "order_ownership_mismatch", outcome = "failure", order_id = %id, account_id = %account.id);
return Err(Problem::malformed(unknown));
}
if order.status != OrderStatus::Valid && order.expires <= now_secs() {
warn!(event = "order_expired", outcome = "failure", order_id = %id, expires = order.expires);
return Err(Problem::malformed("Order has expired"));
}
Ok(order)
}
pub(crate) async fn load_owned_authz(
id: &str,
account: &Account,
database: &Arc<Database>,
) -> Result<(Authorization, Order), Problem> {
owned_authz(id, account, database, "Unknown authorization").await
}
async fn owned_authz(
id: &str,
account: &Account,
database: &Arc<Database>,
unknown: &'static str,
) -> Result<(Authorization, Order), Problem> {
let authz = Authorization::find_by_id(id, database)
.await
.map_err(|error| {
error!(event = "authz_lookup_failed", outcome = "failure", authz_id = %id, error = %error);
Problem::server_internal("Authorization lookup failed")
})?
.ok_or_else(|| Problem::malformed(unknown))?;
let order = owned_order(&authz.order_id.to_string(), account, database, unknown).await?;
Ok((authz, order))
}
pub(crate) async fn load_owned_challenge(
id: &str,
account: &Account,
database: &Arc<Database>,
) -> Result<(Challenge, Authorization, Order), Problem> {
let challenge = Challenge::find_by_id(id, database)
.await
.map_err(|error| {
error!(event = "challenge_lookup_failed", outcome = "failure", challenge_id = %id, error = %error);
Problem::server_internal("Challenge lookup failed")
})?
.ok_or_else(|| Problem::malformed("Unknown challenge"))?;
let (authz, order) = owned_authz(
&challenge.authz_id.to_string(),
account,
database,
"Unknown challenge",
)
.await?;
Ok((challenge, authz, order))
}
pub(crate) async fn order_authz_ids(
order_id: Uuid,
database: &Arc<Database>,
) -> Result<Vec<Uuid>, Problem> {
Ok(Authorization::find_by_order(order_id, database)
.await
.map_err(|error| {
error!(event = "authz_list_failed", outcome = "failure", order_id = %order_id, error = %error);
Problem::server_internal("Authorization lookup failed")
})?
.into_iter()
.map(|authz| authz.id)
.collect())
}