Skip to content

Commit

Permalink
refactor: [torrust#448] refactor services
Browse files Browse the repository at this point in the history
  • Loading branch information
mario-nt committed Mar 22, 2024
1 parent 51eaa94 commit 2800f6d
Show file tree
Hide file tree
Showing 12 changed files with 289 additions and 192 deletions.
17 changes: 7 additions & 10 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,14 @@ use crate::common::AppData;
use crate::config::Configuration;
use crate::databases::database;
use crate::services::authentication::{DbUserAuthenticationRepository, JsonWebToken, Service};
use crate::services::authorization::{AuthorizeService, DbUserAuthorizationRepository};
use crate::services::category::{self, DbCategoryRepository};
use crate::services::tag::{self, DbTagRepository};
use crate::services::torrent::{
DbCanonicalInfoHashGroupRepository, DbTorrentAnnounceUrlRepository, DbTorrentFileRepository, DbTorrentInfoRepository,
DbTorrentListingGenerator, DbTorrentRepository, DbTorrentTagRepository,
};
use crate::services::user::{self, DbBannedUserList, DbUserProfileRepository, DbUserRepository};
use crate::services::{proxy, settings, torrent};
use crate::services::user::{self, DbBannedUserList, DbUserProfileRepository, DbUserRepository, Repository};
use crate::services::{authorization, proxy, settings, torrent};
use crate::tracker::statistics_importer::StatisticsImporter;
use crate::web::api::server::v1::auth::Authentication;
use crate::web::api::Version;
Expand Down Expand Up @@ -71,9 +70,8 @@ pub async fn run(configuration: Configuration, api_version: &Version) -> Running
// Repositories
let category_repository = Arc::new(DbCategoryRepository::new(database.clone()));
let tag_repository = Arc::new(DbTagRepository::new(database.clone()));
let user_repository = Arc::new(DbUserRepository::new(database.clone()));
let user_repository: Arc<Box<dyn Repository>> = Arc::new(Box::new(DbUserRepository::new(database.clone())));
let user_authentication_repository = Arc::new(DbUserAuthenticationRepository::new(database.clone()));
let user_authorization_repository = Arc::new(DbUserAuthorizationRepository::new(database.clone()));
let user_profile_repository = Arc::new(DbUserProfileRepository::new(database.clone()));
let torrent_repository = Arc::new(DbTorrentRepository::new(database.clone()));
let canonical_info_hash_group_repository = Arc::new(DbCanonicalInfoHashGroupRepository::new(database.clone()));
Expand All @@ -85,19 +83,20 @@ pub async fn run(configuration: Configuration, api_version: &Version) -> Running
let banned_user_list = Arc::new(DbBannedUserList::new(database.clone()));

// Services
let authorization_service = Arc::new(AuthorizeService::new(user_authorization_repository.clone()));
let authorization_service = Arc::new(authorization::Service::new(user_repository.clone()));
let tracker_service = Arc::new(tracker::service::Service::new(configuration.clone(), database.clone()).await);
let tracker_statistics_importer =
Arc::new(StatisticsImporter::new(configuration.clone(), tracker_service.clone(), database.clone()).await);
let mailer_service = Arc::new(mailer::Service::new(configuration.clone()).await);
let image_cache_service: Arc<ImageCacheService> = Arc::new(ImageCacheService::new(configuration.clone()).await);
let category_service = Arc::new(category::Service::new(
category_repository.clone(),
user_repository.clone(),
authorization_service.clone(),
));
let tag_service = Arc::new(tag::Service::new(tag_repository.clone(), authorization_service.clone()));
let tag_service = Arc::new(tag::Service::new(tag_repository.clone(), user_repository.clone()));
let proxy_service = Arc::new(proxy::Service::new(image_cache_service.clone(), user_repository.clone()));
let settings_service = Arc::new(settings::Service::new(configuration.clone(), authorization_service.clone()));
let settings_service = Arc::new(settings::Service::new(configuration.clone(), user_repository.clone()));
let torrent_index = Arc::new(torrent::Index::new(
configuration.clone(),
tracker_statistics_importer.clone(),
Expand Down Expand Up @@ -139,7 +138,6 @@ pub async fn run(configuration: Configuration, api_version: &Version) -> Running
json_web_token.clone(),
auth.clone(),
authentication_service,
authorization_service,
tracker_service.clone(),
tracker_statistics_importer.clone(),
mailer_service,
Expand All @@ -148,7 +146,6 @@ pub async fn run(configuration: Configuration, api_version: &Version) -> Running
tag_repository,
user_repository,
user_authentication_repository,
user_authorization_repository,
user_profile_repository,
torrent_repository,
canonical_info_hash_group_repository,
Expand Down
14 changes: 4 additions & 10 deletions src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,18 @@ use crate::cache::image::manager::ImageCacheService;
use crate::config::Configuration;
use crate::databases::database::Database;
use crate::services::authentication::{DbUserAuthenticationRepository, JsonWebToken, Service};
use crate::services::authorization::{AuthorizeService, DbUserAuthorizationRepository};
use crate::services::category::{self, DbCategoryRepository};
use crate::services::tag::{self, DbTagRepository};
use crate::services::torrent::{
DbCanonicalInfoHashGroupRepository, DbTorrentAnnounceUrlRepository, DbTorrentFileRepository, DbTorrentInfoRepository,
DbTorrentListingGenerator, DbTorrentRepository, DbTorrentTagRepository,
};
use crate::services::user::{self, DbBannedUserList, DbUserProfileRepository, DbUserRepository};
use crate::services::user::{self, DbBannedUserList, DbUserProfileRepository, Repository};
use crate::services::{proxy, settings, torrent};
use crate::tracker::statistics_importer::StatisticsImporter;
use crate::web::api::server::v1::auth::Authentication;
use crate::{mailer, tracker};

pub type Username = String;

pub struct AppData {
Expand All @@ -24,17 +24,15 @@ pub struct AppData {
pub json_web_token: Arc<JsonWebToken>,
pub auth: Arc<Authentication>,
pub authentication_service: Arc<Service>,
pub authorization_service: Arc<AuthorizeService>,
pub tracker_service: Arc<tracker::service::Service>,
pub tracker_statistics_importer: Arc<StatisticsImporter>,
pub mailer: Arc<mailer::Service>,
pub image_cache_manager: Arc<ImageCacheService>,
// Repositories
pub category_repository: Arc<DbCategoryRepository>,
pub tag_repository: Arc<DbTagRepository>,
pub user_repository: Arc<DbUserRepository>,
pub user_repository: Arc<Box<dyn Repository>>,
pub user_authentication_repository: Arc<DbUserAuthenticationRepository>,
pub user_authorization_repository: Arc<DbUserAuthorizationRepository>,
pub user_profile_repository: Arc<DbUserProfileRepository>,
pub torrent_repository: Arc<DbTorrentRepository>,
pub torrent_info_hash_repository: Arc<DbCanonicalInfoHashGroupRepository>,
Expand Down Expand Up @@ -62,17 +60,15 @@ impl AppData {
json_web_token: Arc<JsonWebToken>,
auth: Arc<Authentication>,
authentication_service: Arc<Service>,
authorization_service: Arc<AuthorizeService>,
tracker_service: Arc<tracker::service::Service>,
tracker_statistics_importer: Arc<StatisticsImporter>,
mailer: Arc<mailer::Service>,
image_cache_manager: Arc<ImageCacheService>,
// Repositories
category_repository: Arc<DbCategoryRepository>,
tag_repository: Arc<DbTagRepository>,
user_repository: Arc<DbUserRepository>,
user_repository: Arc<Box<dyn Repository>>,
user_authentication_repository: Arc<DbUserAuthenticationRepository>,
user_authorization_repository: Arc<DbUserAuthorizationRepository>,
user_profile_repository: Arc<DbUserProfileRepository>,
torrent_repository: Arc<DbTorrentRepository>,
torrent_info_hash_repository: Arc<DbCanonicalInfoHashGroupRepository>,
Expand All @@ -97,7 +93,6 @@ impl AppData {
json_web_token,
auth,
authentication_service,
authorization_service,
tracker_service,
tracker_statistics_importer,
mailer,
Expand All @@ -107,7 +102,6 @@ impl AppData {
tag_repository,
user_repository,
user_authentication_repository,
user_authorization_repository,
user_profile_repository,
torrent_repository,
torrent_info_hash_repository,
Expand Down
7 changes: 4 additions & 3 deletions src/services/authentication.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,18 @@ use argon2::{Argon2, PasswordHash, PasswordVerifier};
use jsonwebtoken::{decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation};
use pbkdf2::Pbkdf2;

use super::user::{DbUserProfileRepository, DbUserRepository};
use super::user::DbUserProfileRepository;
use crate::config::Configuration;
use crate::databases::database::{Database, Error};
use crate::errors::ServiceError;
use crate::models::user::{UserAuthentication, UserClaims, UserCompact, UserId};
use crate::services::user::Repository;
use crate::utils::clock;

pub struct Service {
configuration: Arc<Configuration>,
json_web_token: Arc<JsonWebToken>,
user_repository: Arc<DbUserRepository>,
user_repository: Arc<Box<dyn Repository>>,
user_profile_repository: Arc<DbUserProfileRepository>,
user_authentication_repository: Arc<DbUserAuthenticationRepository>,
}
Expand All @@ -24,7 +25,7 @@ impl Service {
pub fn new(
configuration: Arc<Configuration>,
json_web_token: Arc<JsonWebToken>,
user_repository: Arc<DbUserRepository>,
user_repository: Arc<Box<dyn Repository>>,
user_profile_repository: Arc<DbUserProfileRepository>,
user_authentication_repository: Arc<DbUserAuthenticationRepository>,
) -> Self {
Expand Down
87 changes: 35 additions & 52 deletions src/services/authorization.rs
Original file line number Diff line number Diff line change
@@ -1,69 +1,52 @@
//! Authorization service.
use std::sync::Arc;

use crate::databases::database::{Database, Error};
use super::user::Repository;
use crate::errors::ServiceError;
use crate::models::user::{UserAuthorization, UserId};
use crate::models::user::{UserCompact, UserId};

pub struct AuthorizeService {
user_authorization_repository: Arc<DbUserAuthorizationRepository>,
pub enum ACTION {
AddCategory,
DeleteCategory,
}

impl AuthorizeService {
pub struct Service {
user_repository: Arc<Box<dyn Repository>>,
}

impl Service {
#[must_use]
pub fn new(user_authorization_repository: Arc<DbUserAuthorizationRepository>) -> Self {
Self {
user_authorization_repository,
}
pub fn new(user_repository: Arc<Box<dyn Repository>>) -> Self {
Self { user_repository }
}

/// Checks if the user has the right privileges to perform the requested action.
///
/// # Errors
///
/// This function will return an error if unable to get the user
/// authorization data from the database or if the user
/// does not have the right privileges to perform the action.
pub async fn authorize_user(&self, user_id: UserId, admin_required: bool) -> Result<(), ServiceError> {
// Checks if the user exists in the database
let authorization_info = self
.user_authorization_repository
.get_user_authorization_from_id(&user_id)
.await?;

//If admin privilages are required, it checks if the user is an admin
if admin_required {
Self::authorize_admin_user(&authorization_info)
} else {
Ok(())
/// Will return an error if:
///
/// - There is not any user with the provided `UserId` (when the user id is some).
/// - The user is not authorized to perform the action.
pub async fn authorize(&self, action: ACTION, maybe_user_id: Option<UserId>) -> Result<(), ServiceError> {
match action {
ACTION::AddCategory | ACTION::DeleteCategory => match maybe_user_id {
Some(user_id) => {
let user = self.get_user(user_id).await?;

if !user.administrator {
return Err(ServiceError::Unauthorized);
}

Ok(())
}
None => Err(ServiceError::Unauthorized),
},
}
}

fn authorize_admin_user(user_authorization_info: &UserAuthorization) -> Result<(), ServiceError> {
if user_authorization_info.administrator {
Ok(())
} else {
Err(ServiceError::Unauthorized)
}
async fn get_user(&self, user_id: UserId) -> Result<UserCompact, ServiceError> {
self.user_repository.get_compact(&user_id).await
}
}

pub struct DbUserAuthorizationRepository {
database: Arc<Box<dyn Database>>,
}

impl DbUserAuthorizationRepository {
#[must_use]
pub fn new(database: Arc<Box<dyn Database>>) -> Self {
Self { database }
}

/// Get user authorization data from user id.
///
/// # Errors
///
/// This function will return an error if unable to get the user
/// authorization data from the database.
pub async fn get_user_authorization_from_id(&self, user_id: &UserId) -> Result<UserAuthorization, Error> {
self.database.get_user_authorization_from_id(*user_id).await
}
}
#[cfg(test)]
mod tests {}
33 changes: 28 additions & 5 deletions src/services/category.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,29 @@
//! Category service.
use std::sync::Arc;

use super::authorization::AuthorizeService;
use super::authorization::{self, ACTION};
use crate::databases::database::{Category, Database, Error as DatabaseError};
use crate::errors::ServiceError;
use crate::models::category::CategoryId;
use crate::models::user::UserId;
use crate::services::user::Repository;

pub struct Service {
category_repository: Arc<DbCategoryRepository>,
authorization_service: Arc<AuthorizeService>,
user_repository: Arc<Box<dyn Repository>>,
authorization_service: Arc<authorization::Service>,
}

impl Service {
#[must_use]
pub fn new(category_repository: Arc<DbCategoryRepository>, authorization_service: Arc<AuthorizeService>) -> Service {
pub fn new(
category_repository: Arc<DbCategoryRepository>,
user_repository: Arc<Box<dyn Repository>>,
authorization_service: Arc<authorization::Service>,
) -> Service {
Service {
category_repository,
user_repository,
authorization_service,
}
}
Expand All @@ -32,7 +39,17 @@ impl Service {
/// * The category already exists.
/// * There is a database error.
pub async fn add_category(&self, category_name: &str, user_id: &UserId) -> Result<i64, ServiceError> {
self.authorization_service.authorize_user(*user_id, true).await?;
/*let user = self.user_repository.get_compact(user_id).await?;
// Check if user is administrator
// todo: extract authorization service
if !user.administrator {
return Err(ServiceError::Unauthorized);
}*/

self.authorization_service
.authorize(ACTION::AddCategory, Some(*user_id))
.await?;

let trimmed_name = category_name.trim();

Expand Down Expand Up @@ -64,7 +81,13 @@ impl Service {
/// * The user does not have the required permissions.
/// * There is a database error.
pub async fn delete_category(&self, category_name: &str, user_id: &UserId) -> Result<(), ServiceError> {
self.authorization_service.authorize_user(*user_id, true).await?;
let user = self.user_repository.get_compact(user_id).await?;

// Check if user is administrator
// todo: extract authorization service
if !user.administrator {
return Err(ServiceError::Unauthorized);
}

match self.category_repository.delete(category_name).await {
Ok(()) => Ok(()),
Expand Down
6 changes: 3 additions & 3 deletions src/services/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,18 @@ use std::sync::Arc;

use bytes::Bytes;

use super::user::DbUserRepository;
use crate::cache::image::manager::{Error, ImageCacheService};
use crate::models::user::UserId;
use crate::services::user::Repository;

pub struct Service {
image_cache_service: Arc<ImageCacheService>,
user_repository: Arc<DbUserRepository>,
user_repository: Arc<Box<dyn Repository>>,
}

impl Service {
#[must_use]
pub fn new(image_cache_service: Arc<ImageCacheService>, user_repository: Arc<DbUserRepository>) -> Self {
pub fn new(image_cache_service: Arc<ImageCacheService>, user_repository: Arc<Box<dyn Repository>>) -> Self {
Self {
image_cache_service,
user_repository,
Expand Down
Loading

0 comments on commit 2800f6d

Please sign in to comment.