use pdk_core::logger;
use super::error::AuthorizationError;
use crate::api::credentials::ClientId;
use crate::api::ClientData;
use crate::implementation::model::contracts_storage::ContractsStorage;
pub fn authorize(
contracts_storage: &dyn ContractsStorage,
client_id: &ClientId,
) -> Result<ClientData, AuthorizationError> {
if contracts_storage.last_update().is_none() {
logger::debug!("Authorization: Contracts are unavailable.");
return Err(AuthorizationError::UnavailableContracts);
}
let Some(contracts) = contracts_storage.get_contract_by_client(client_id) else {
logger::debug!("Authorization: Client {client_id} does not exist for this API");
return Err(AuthorizationError::InvalidClientId);
};
logger::debug!("Authorization: Credentials match with a Client of this API");
Ok(contracts.cast_to_client_information())
}
#[cfg(test)]
mod tests {
use std::rc::Rc;
use super::authorize;
use crate::implementation::hashing::hash;
use crate::implementation::model::contracts_storage::{
ContractsLocalStorage, ContractsStorage,
};
use crate::implementation::platform::shared::Contract;
use crate::mocks::{ManualClock, MapSharedData};
use crate::{ClientId, ClientSecret};
const INVALID_CLIENT_ID: &str = "invalid_client_id";
const VALID_CLIENT_ID: &str = "api";
const VALID_CLIENT_SECRET: &str = "gateway";
const API_ID: &str = "1234";
const SALT: &str = "someSalt";
fn invalid_client_id() -> ClientId {
ClientId::new(INVALID_CLIENT_ID.to_string())
}
fn valid_client_id() -> ClientId {
ClientId::new(VALID_CLIENT_ID.to_string())
}
fn valid_client_secret() -> ClientSecret {
ClientSecret::new(VALID_CLIENT_SECRET.to_string())
}
fn contract(id: &str, client_id: &ClientId, client_secret: &ClientSecret) -> Contract {
let hashed_secret = hash(SALT, client_secret);
Contract {
contract_id: id.to_string(),
api_id: API_ID.to_string(),
version_id: "".to_string(),
sla_tier_id: None,
client_id: client_id.as_str().to_string(),
client_secret: String::from_utf8(hashed_secret)
.expect("Could not map secret to String"),
client_secret_salt: SALT.to_string(),
client_name: client_id.to_string(),
removed: false,
}
}
#[test]
fn nonexistent_client_is_rejected() {
let clock = Rc::new(ManualClock::default());
let shared_data = Rc::new(MapSharedData::default());
let storage = ContractsLocalStorage::new(API_ID, clock, shared_data);
let result = authorize(&storage, &invalid_client_id());
assert!(result.is_err());
}
#[test]
#[cfg(not(fips))]
fn valid_client_is_accepted() {
let clock = Rc::new(ManualClock::default());
let shared_data = Rc::new(MapSharedData::default());
let storage = ContractsLocalStorage::new(API_ID, clock, shared_data);
let client_id = &valid_client_id();
let contract = contract("1", client_id, &valid_client_secret());
storage.save_contract(contract);
storage.update_last();
let validation = authorize(&storage, client_id);
assert!(validation.is_ok());
let client_data = validation.unwrap();
assert_eq!(client_data.client_id, VALID_CLIENT_ID);
}
#[test]
#[cfg(not(fips))]
fn removed_client_is_rejected() {
let clock = Rc::new(ManualClock::default());
let shared_data = Rc::new(MapSharedData::default());
let storage = ContractsLocalStorage::new(API_ID, clock, shared_data);
let client_id = &valid_client_id();
let contract = contract("1", client_id, &valid_client_secret());
storage.save_contract(contract);
storage.remove_contract(VALID_CLIENT_ID);
let validation = authorize(&storage, client_id);
assert!(validation.is_err());
}
}