Skip to content

Commit

Permalink
Merge torrust#160: Decouple services from actix-web
Browse files Browse the repository at this point in the history
5396301 refactor: [torrust#157] extract authentication service (Jose Celano)
8101048 refactor: [torrust#157] extract service to ban users (Jose Celano)
2eb0f7c refactor: [torrust#157] extract service for user registration (Jose Celano)
1abcc4d refactor: [torrust#157] extract service: torrent (Jose Celano)
0387b97 refactor: [torrust#157] extract service: settings (Jose Celano)
c9fb249 refactor: [torrust#157] extract service: proxy (Jose Celano)
d58f3cc refactor: [torrust#157] extract service: category (Jose Celano)
baa4c7e refactor: [torrust#157] extract service: about (Jose Celano)

Pull request description:

  - [x] Extract `about` service.
  - [x] Extract `category` service.
  - [x] Extract `proxy` service.
  - [x] Extract `settings` service.
  - [x] Extract `torrent` service.
  - [x] Extract `user` registration service.
  - [x] Extract `user` ban service.
  - [x] Extract `user` authentication service.

ACKs for top commit:
  josecelano:
    ACK 5396301

Tree-SHA512: b63a45f3eb8aaab0d51c885793c94e85979273128d9cb94e36a4ed7577a399105e9dcef9624077b4ae07c8aac03f6520eb1823944e2b44554d1bd85ad8c9e281
  • Loading branch information
josecelano committed May 22, 2023
2 parents 6dd1455 + 5396301 commit 8748f93
Show file tree
Hide file tree
Showing 34 changed files with 1,930 additions and 808 deletions.
93 changes: 84 additions & 9 deletions src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,20 @@ use actix_web::dev::Server;
use actix_web::{middleware, web, App, HttpServer};
use log::info;

use crate::auth::AuthorizationService;
use crate::auth::Authentication;
use crate::bootstrap::logging;
use crate::cache::image::manager::ImageCacheService;
use crate::common::AppData;
use crate::config::Configuration;
use crate::databases::database;
use crate::services::authentication::{DbUserAuthenticationRepository, JsonWebToken, Service};
use crate::services::category::{self, DbCategoryRepository};
use crate::services::torrent::{
DbTorrentAnnounceUrlRepository, DbTorrentFileRepository, DbTorrentInfoRepository, DbTorrentListingGenerator,
DbTorrentRepository,
};
use crate::services::user::{self, DbBannedUserList, DbUserProfileRepository, DbUserRepository};
use crate::services::{proxy, settings, torrent};
use crate::tracker::statistics_importer::StatisticsImporter;
use crate::{mailer, routes, tracker};

Expand All @@ -21,15 +29,16 @@ pub struct Running {
pub tracker_data_importer_handle: tokio::task::JoinHandle<()>,
}

#[allow(clippy::too_many_lines)]
pub async fn run(configuration: Configuration) -> Running {
logging::setup();

let cfg = Arc::new(configuration);
let configuration = Arc::new(configuration);

// Get configuration settings needed to build the app dependencies and
// services: main API server and tracker torrents importer.

let settings = cfg.settings.read().await;
let settings = configuration.settings.read().await;

let database_connect_url = settings.database.connect_url.clone();
let torrent_info_update_interval = settings.tracker_statistics_importer.torrent_info_update_interval;
Expand All @@ -42,23 +51,89 @@ pub async fn run(configuration: Configuration) -> Running {
// Build app dependencies

let database = Arc::new(database::connect(&database_connect_url).await.expect("Database error."));
let auth = Arc::new(AuthorizationService::new(cfg.clone(), database.clone()));
let tracker_service = Arc::new(tracker::service::Service::new(cfg.clone(), database.clone()).await);
let json_web_token = Arc::new(JsonWebToken::new(configuration.clone()));
let auth = Arc::new(Authentication::new(json_web_token.clone()));

// Repositories
let category_repository = Arc::new(DbCategoryRepository::new(database.clone()));
let user_repository = Arc::new(DbUserRepository::new(database.clone()));
let user_authentication_repository = Arc::new(DbUserAuthenticationRepository::new(database.clone()));
let user_profile_repository = Arc::new(DbUserProfileRepository::new(database.clone()));
let torrent_repository = Arc::new(DbTorrentRepository::new(database.clone()));
let torrent_info_repository = Arc::new(DbTorrentInfoRepository::new(database.clone()));
let torrent_file_repository = Arc::new(DbTorrentFileRepository::new(database.clone()));
let torrent_announce_url_repository = Arc::new(DbTorrentAnnounceUrlRepository::new(database.clone()));
let torrent_listing_generator = Arc::new(DbTorrentListingGenerator::new(database.clone()));
let banned_user_list = Arc::new(DbBannedUserList::new(database.clone()));

// Services
let tracker_service = Arc::new(tracker::service::Service::new(configuration.clone(), database.clone()).await);
let tracker_statistics_importer =
Arc::new(StatisticsImporter::new(cfg.clone(), tracker_service.clone(), database.clone()).await);
let mailer_service = Arc::new(mailer::Service::new(cfg.clone()).await);
let image_cache_service = Arc::new(ImageCacheService::new(cfg.clone()).await);
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()));
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(), user_repository.clone()));
let torrent_index = Arc::new(torrent::Index::new(
configuration.clone(),
tracker_statistics_importer.clone(),
tracker_service.clone(),
user_repository.clone(),
category_repository.clone(),
torrent_repository.clone(),
torrent_info_repository.clone(),
torrent_file_repository.clone(),
torrent_announce_url_repository.clone(),
torrent_listing_generator.clone(),
));
let registration_service = Arc::new(user::RegistrationService::new(
configuration.clone(),
mailer_service.clone(),
user_repository.clone(),
user_profile_repository.clone(),
));
let ban_service = Arc::new(user::BanService::new(
user_repository.clone(),
user_profile_repository.clone(),
banned_user_list.clone(),
));
let authentication_service = Arc::new(Service::new(
configuration.clone(),
json_web_token.clone(),
user_repository.clone(),
user_profile_repository.clone(),
user_authentication_repository.clone(),
));

// Build app container

let app_data = Arc::new(AppData::new(
cfg.clone(),
configuration.clone(),
database.clone(),
json_web_token.clone(),
auth.clone(),
authentication_service,
tracker_service.clone(),
tracker_statistics_importer.clone(),
mailer_service,
image_cache_service,
category_repository,
user_repository,
user_authentication_repository,
user_profile_repository,
torrent_repository,
torrent_info_repository,
torrent_file_repository,
torrent_announce_url_repository,
torrent_listing_generator,
banned_user_list,
category_service,
proxy_service,
settings_service,
torrent_index,
registration_service,
ban_service,
));

// Start repeating task to import tracker torrent data and updating
Expand Down
58 changes: 14 additions & 44 deletions src/auth.rs
Original file line number Diff line number Diff line change
@@ -1,36 +1,24 @@
use std::sync::Arc;

use actix_web::HttpRequest;
use jsonwebtoken::{decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation};

use crate::config::Configuration;
use crate::databases::database::Database;
use crate::errors::ServiceError;
use crate::models::user::{UserClaims, UserCompact};
use crate::utils::clock;
use crate::models::user::{UserClaims, UserCompact, UserId};
use crate::services::authentication::JsonWebToken;

pub struct AuthorizationService {
cfg: Arc<Configuration>,
database: Arc<Box<dyn Database>>,
pub struct Authentication {
json_web_token: Arc<JsonWebToken>,
}

impl AuthorizationService {
pub fn new(cfg: Arc<Configuration>, database: Arc<Box<dyn Database>>) -> AuthorizationService {
AuthorizationService { cfg, database }
impl Authentication {
#[must_use]
pub fn new(json_web_token: Arc<JsonWebToken>) -> Self {
Self { json_web_token }
}

/// Create Json Web Token
pub async fn sign_jwt(&self, user: UserCompact) -> String {
let settings = self.cfg.settings.read().await;

// create JWT that expires in two weeks
let key = settings.auth.secret_key.as_bytes();
// TODO: create config option for setting the token validity in seconds
let exp_date = clock::now() + 1_209_600; // two weeks from now

let claims = UserClaims { user, exp: exp_date };

encode(&Header::default(), &claims, &EncodingKey::from_secret(key)).expect("argument `Header` should match `EncodingKey`")
self.json_web_token.sign(user).await
}

/// Verify Json Web Token
Expand All @@ -39,21 +27,7 @@ impl AuthorizationService {
///
/// This function will return an error if the JWT is not good or expired.
pub async fn verify_jwt(&self, token: &str) -> Result<UserClaims, ServiceError> {
let settings = self.cfg.settings.read().await;

match decode::<UserClaims>(
token,
&DecodingKey::from_secret(settings.auth.secret_key.as_bytes()),
&Validation::new(Algorithm::HS256),
) {
Ok(token_data) => {
if token_data.claims.exp < clock::now() {
return Err(ServiceError::TokenExpired);
}
Ok(token_data.claims)
}
Err(_) => Err(ServiceError::TokenInvalid),
}
self.json_web_token.verify(token).await
}

/// Get Claims from Request
Expand Down Expand Up @@ -81,17 +55,13 @@ impl AuthorizationService {
}
}

/// Get User (in compact form) from Request
/// Get User id from Request
///
/// # Errors
///
/// This function will return an `ServiceError::UserNotFound` if unable to get user from database.
pub async fn get_user_compact_from_request(&self, req: &HttpRequest) -> Result<UserCompact, ServiceError> {
/// This function will return an error if it can get claims from the request
pub async fn get_user_id_from_request(&self, req: &HttpRequest) -> Result<UserId, ServiceError> {
let claims = self.get_claims_from_request(req).await?;

self.database
.get_user_compact_from_id(claims.user.user_id)
.await
.map_err(|_| ServiceError::UserNotFound)
Ok(claims.user.user_id)
}
}
75 changes: 72 additions & 3 deletions src/common.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
use std::sync::Arc;

use crate::auth::AuthorizationService;
use crate::auth::Authentication;
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::category::{self, DbCategoryRepository};
use crate::services::torrent::{
DbTorrentAnnounceUrlRepository, DbTorrentFileRepository, DbTorrentInfoRepository, DbTorrentListingGenerator,
DbTorrentRepository,
};
use crate::services::user::{self, DbBannedUserList, DbUserProfileRepository, DbUserRepository};
use crate::services::{proxy, settings, torrent};
use crate::tracker::statistics_importer::StatisticsImporter;
use crate::{mailer, tracker};
pub type Username = String;
Expand All @@ -13,31 +21,92 @@ pub type WebAppData = actix_web::web::Data<Arc<AppData>>;
pub struct AppData {
pub cfg: Arc<Configuration>,
pub database: Arc<Box<dyn Database>>,
pub auth: Arc<AuthorizationService>,
pub json_web_token: Arc<JsonWebToken>,
pub auth: Arc<Authentication>,
pub authentication_service: Arc<Service>,
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 user_repository: Arc<DbUserRepository>,
pub user_authentication_repository: Arc<DbUserAuthenticationRepository>,
pub user_profile_repository: Arc<DbUserProfileRepository>,
pub torrent_repository: Arc<DbTorrentRepository>,
pub torrent_info_repository: Arc<DbTorrentInfoRepository>,
pub torrent_file_repository: Arc<DbTorrentFileRepository>,
pub torrent_announce_url_repository: Arc<DbTorrentAnnounceUrlRepository>,
pub torrent_listing_generator: Arc<DbTorrentListingGenerator>,
pub banned_user_list: Arc<DbBannedUserList>,
// Services
pub category_service: Arc<category::Service>,
pub proxy_service: Arc<proxy::Service>,
pub settings_service: Arc<settings::Service>,
pub torrent_service: Arc<torrent::Index>,
pub registration_service: Arc<user::RegistrationService>,
pub ban_service: Arc<user::BanService>,
}

impl AppData {
#[allow(clippy::too_many_arguments)]
pub fn new(
cfg: Arc<Configuration>,
database: Arc<Box<dyn Database>>,
auth: Arc<AuthorizationService>,
json_web_token: Arc<JsonWebToken>,
auth: Arc<Authentication>,
authentication_service: Arc<Service>,
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>,
user_repository: Arc<DbUserRepository>,
user_authentication_repository: Arc<DbUserAuthenticationRepository>,
user_profile_repository: Arc<DbUserProfileRepository>,
torrent_repository: Arc<DbTorrentRepository>,
torrent_info_repository: Arc<DbTorrentInfoRepository>,
torrent_file_repository: Arc<DbTorrentFileRepository>,
torrent_announce_url_repository: Arc<DbTorrentAnnounceUrlRepository>,
torrent_listing_generator: Arc<DbTorrentListingGenerator>,
banned_user_list: Arc<DbBannedUserList>,
// Services
category_service: Arc<category::Service>,
proxy_service: Arc<proxy::Service>,
settings_service: Arc<settings::Service>,
torrent_service: Arc<torrent::Index>,
registration_service: Arc<user::RegistrationService>,
ban_service: Arc<user::BanService>,
) -> AppData {
AppData {
cfg,
database,
json_web_token,
auth,
authentication_service,
tracker_service,
tracker_statistics_importer,
mailer,
image_cache_manager,
// Repositories
category_repository,
user_repository,
user_authentication_repository,
user_profile_repository,
torrent_repository,
torrent_info_repository,
torrent_file_repository,
torrent_announce_url_repository,
torrent_listing_generator,
banned_user_list,
// Services
category_service,
proxy_service,
settings_service,
torrent_service,
registration_service,
ban_service,
}
}
}
12 changes: 12 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,12 @@ impl Configuration {
}
}

pub async fn get_all(&self) -> TorrustBackend {
let settings_lock = self.settings.read().await;

settings_lock.clone()
}

pub async fn get_public(&self) -> ConfigurationPublic {
let settings_lock = self.settings.read().await;

Expand All @@ -331,6 +337,12 @@ impl Configuration {
email_on_signup: settings_lock.auth.email_on_signup.clone(),
}
}

pub async fn get_site_name(&self) -> String {
let settings_lock = self.settings.read().await;

settings_lock.website.name.clone()
}
}

#[derive(Debug, Clone, Serialize, Deserialize)]
Expand Down
Loading

0 comments on commit 8748f93

Please sign in to comment.