use crate::accounts::Account;
use crate::accounts::AccountRepository;
use crate::authz::AccessHierarchy;
use crate::credentials::Credentials;
use crate::credentials::CredentialsVerifier;
use crate::errors::{Error, Result};
use crate::hashing::HashingService;
use crate::hashing::argon2::Argon2Hasher;
use crate::permissions::PermissionId;
use crate::permissions::mapping::PermissionMapping;
use crate::permissions::mapping::PermissionMappingRepository;
use crate::repositories::{RepositoriesError, RepositoryOperation, RepositoryType};
use crate::secrets::Secret;
use crate::secrets::SecretRepository;
use crate::verification_result::VerificationResult;
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
use tracing::debug;
use uuid::Uuid;
#[derive(Clone)]
pub struct MemoryAccountRepository<R, G>
where
R: AccessHierarchy + Eq + Send + Sync + 'static,
G: Eq + Clone + Send + Sync + 'static,
{
accounts: Arc<RwLock<HashMap<String, Account<R, G>>>>,
}
impl<R, G> Default for MemoryAccountRepository<R, G>
where
R: AccessHierarchy + Eq + Send + Sync + 'static,
G: Eq + Clone + Send + Sync + 'static,
{
fn default() -> Self {
Self {
accounts: Arc::new(RwLock::new(HashMap::new())),
}
}
}
impl<R, G> From<Vec<Account<R, G>>> for MemoryAccountRepository<R, G>
where
R: AccessHierarchy + Eq + Send + Sync + 'static,
G: Eq + Clone + Send + Sync + 'static,
{
fn from(value: Vec<Account<R, G>>) -> Self {
let mut accounts = HashMap::new();
for val in value {
let id = val.user_id.clone();
accounts.insert(id, val);
}
let accounts = Arc::new(RwLock::new(accounts));
Self { accounts }
}
}
impl<R, G> AccountRepository<R, G> for MemoryAccountRepository<R, G>
where
Account<R, G>: Clone,
R: AccessHierarchy + Eq + Send + Sync + 'static,
G: Eq + Clone + Send + Sync + 'static,
{
async fn query_account_by_user_id(&self, user_id: &str) -> Result<Option<Account<R, G>>> {
let read = self.accounts.read().await;
Ok(read.get(user_id).cloned())
}
async fn store_account(&self, account: Account<R, G>) -> Result<Option<Account<R, G>>> {
let id = account.user_id.clone();
let mut write = self.accounts.write().await;
write.insert(id, account.clone());
Ok(Some(account))
}
async fn delete_account(&self, account_id: &str) -> Result<Option<Account<R, G>>> {
let mut write = self.accounts.write().await;
if !write.contains_key(account_id) {
return Ok(None);
}
Ok(write.remove(account_id))
}
async fn update_account(&self, account: Account<R, G>) -> Result<Option<Account<R, G>>> {
self.store_account(account).await
}
}
#[derive(Clone)]
pub struct MemorySecretRepository {
store: Arc<RwLock<HashMap<Uuid, Secret>>>,
dummy_hash: String,
}
impl MemorySecretRepository {
pub fn new_with_argon2_hasher() -> Result<Self> {
let hasher = Argon2Hasher::new_recommended()?;
let dummy_hash = hasher.hash_value("dummy_password")?;
Ok(Self {
store: Arc::new(RwLock::new(HashMap::new())),
dummy_hash,
})
}
}
impl TryFrom<Vec<Secret>> for MemorySecretRepository {
type Error = crate::errors::Error;
fn try_from(value: Vec<Secret>) -> Result<Self> {
let mut store = HashMap::with_capacity(value.len());
value.into_iter().for_each(|v| {
store.insert(v.account_id, v);
});
let store = Arc::new(RwLock::new(store));
let dummy_hash = Argon2Hasher::new_recommended()?.hash_value("dummy_password")?;
Ok(Self { store, dummy_hash })
}
}
impl SecretRepository for MemorySecretRepository {
async fn store_secret(&self, secret: Secret) -> Result<bool> {
let already_present = {
let read = self.store.read().await;
read.contains_key(&secret.account_id)
};
if already_present {
return Err(Error::Repositories(RepositoriesError::operation_failed(
RepositoryType::Secret,
RepositoryOperation::Insert,
"AccountID is already present",
None,
None,
)));
}
let mut write = self.store.write().await;
debug!("Got write lock on secret repository.");
if write.insert(secret.account_id, secret).is_some() {
return Err(Error::Repositories(RepositoriesError::operation_failed(
RepositoryType::Secret,
RepositoryOperation::Insert,
"This should never occur because it is checked if the key is already present a few lines earlier",
None,
Some("store".to_string()),
)));
};
Ok(true)
}
async fn delete_secret(&self, id: &Uuid) -> Result<Option<Secret>> {
let mut write = self.store.write().await;
Ok(write.remove(id))
}
async fn update_secret(&self, secret: Secret) -> Result<()> {
let mut write = self.store.write().await;
write.insert(secret.account_id, secret);
Ok(())
}
}
impl CredentialsVerifier<Uuid> for MemorySecretRepository {
async fn verify_credentials(
&self,
credentials: Credentials<Uuid>,
) -> Result<VerificationResult> {
use crate::hashing::HashingService;
use subtle::Choice;
let read = self.store.read().await;
let (stored_secret_str, user_exists_choice) = match read.get(&credentials.id) {
Some(stored_secret) => (stored_secret.secret.as_str(), Choice::from(1u8)),
None => (self.dummy_hash.as_str(), Choice::from(0u8)),
};
let hasher = Argon2Hasher::new_recommended()?;
let hash_verification_result =
hasher.verify_value(&credentials.secret, stored_secret_str)?;
let hash_matches_choice = Choice::from(match hash_verification_result {
VerificationResult::Ok => 1u8,
VerificationResult::Unauthorized => 0u8,
});
let final_success_choice = user_exists_choice & hash_matches_choice;
let final_result = if bool::from(final_success_choice) {
VerificationResult::Ok
} else {
VerificationResult::Unauthorized
};
Ok(final_result)
}
}
#[derive(Debug)]
pub struct MemoryPermissionMappingRepository {
mappings: Arc<RwLock<Vec<PermissionMapping>>>,
}
impl Default for MemoryPermissionMappingRepository {
fn default() -> Self {
Self {
mappings: Arc::new(RwLock::new(Vec::new())),
}
}
}
impl From<Vec<PermissionMapping>> for MemoryPermissionMappingRepository {
fn from(mappings: Vec<PermissionMapping>) -> Self {
let mut vec: Vec<PermissionMapping> = Vec::new();
for mapping in mappings {
if let Err(e) = mapping.validate() {
tracing::warn!("Skipping invalid permission mapping: {}", e);
continue;
}
let id = mapping.permission_id().as_u64();
let normalized = mapping.normalized_string();
if !vec
.iter()
.any(|m| m.permission_id().as_u64() == id || m.normalized_string() == normalized)
{
vec.push(mapping);
}
}
Self {
mappings: Arc::new(RwLock::new(vec)),
}
}
}
impl PermissionMappingRepository for MemoryPermissionMappingRepository {
async fn store_mapping(&self, mapping: PermissionMapping) -> Result<Option<PermissionMapping>> {
if let Err(e) = mapping.validate() {
return Err(Error::Repositories(RepositoriesError::operation_failed(
RepositoryType::PermissionMapping,
RepositoryOperation::Insert,
format!("Invalid permission mapping: {}", e),
None,
Some("store".to_string()),
)));
}
let id = mapping.permission_id().as_u64();
let normalized = mapping.normalized_string();
{
let vec_read = self.mappings.read().await;
if vec_read
.iter()
.any(|m| m.permission_id().as_u64() == id || m.normalized_string() == normalized)
{
return Ok(None); }
}
{
let mut vec_write = self.mappings.write().await;
vec_write.push(mapping.clone());
}
Ok(Some(mapping))
}
async fn remove_mapping_by_id(&self, id: PermissionId) -> Result<Option<PermissionMapping>> {
let id_u64 = id.as_u64();
let mut vec_write = self.mappings.write().await;
if let Some(pos) = vec_write
.iter()
.position(|m| m.permission_id().as_u64() == id_u64)
{
let removed = vec_write.swap_remove(pos);
Ok(Some(removed))
} else {
Ok(None)
}
}
async fn remove_mapping_by_string(
&self,
permission: &str,
) -> Result<Option<PermissionMapping>> {
let normalized = normalize_permission(permission);
let mut vec_write = self.mappings.write().await;
if let Some(pos) = vec_write
.iter()
.position(|m| m.normalized_string() == normalized.as_str())
{
let removed = vec_write.swap_remove(pos);
Ok(Some(removed))
} else {
Ok(None)
}
}
async fn query_mapping_by_id(&self, id: PermissionId) -> Result<Option<PermissionMapping>> {
let vec_read = self.mappings.read().await;
Ok(vec_read
.iter()
.find(|m| m.permission_id().as_u64() == id.as_u64())
.cloned())
}
async fn query_mapping_by_string(&self, permission: &str) -> Result<Option<PermissionMapping>> {
let normalized = normalize_permission(permission);
let vec_read = self.mappings.read().await;
Ok(vec_read
.iter()
.find(|m| m.normalized_string() == normalized.as_str())
.cloned())
}
async fn list_all_mappings(&self) -> Result<Vec<PermissionMapping>> {
let vec_read = self.mappings.read().await;
Ok(vec_read.iter().cloned().collect())
}
}
fn normalize_permission(input: &str) -> String {
input.trim().to_lowercase()
}