Skip to content

Commit

Permalink
Remove all static variables and replace with web data
Browse files Browse the repository at this point in the history
Release `v1.2.0-a`
  • Loading branch information
dormant-user committed Feb 27, 2024
1 parent b643427 commit 717fc17
Show file tree
Hide file tree
Showing 11 changed files with 147 additions and 99 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "RuStream"
version = "1.1.0"
version = "1.2.0-a"
description = "Self-hosted Streaming Engine, that can render media files via authenticated sessions."
license = "MIT"
documentation = "https://docs.rs/RuStream"
Expand Down
48 changes: 44 additions & 4 deletions src/constant.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::env;
use std::collections::HashMap;
use std::sync::Mutex;
use std::env;
use std::sync::{Arc, Mutex};
use fernet::Fernet;

/// Struct to store the cargo information gathered at compile time using the `env!` macro.
#[derive(Debug)]
Expand Down Expand Up @@ -43,6 +44,45 @@ pub fn build_info() -> Cargo {
cargo
}

lazy_static::lazy_static! {
pub static ref HOST_SERVE: Mutex<HashMap<String, String>> = Mutex::new(HashMap::new());
/// Struct to store the session information.
///
/// ## Fields
///
/// * `tracker` - Used to log connection and streaming information without redundancy.
/// * `mapping` - Used to store username and session token's payload as key value pairs.
///
/// ## See Also:
///
/// These fields are updated and used only for authenticated sessions.
pub struct Session {
pub tracker: Mutex<HashMap<String, String>>,
pub mapping: Mutex<HashMap<String, String>>,
}


/// Instantiates the `Session` struct with empty `HashMap` for both `tracker` and `mapping` fields.
///
/// ## See Also
///
/// Creates new `Mutex` in an unlocked state for each of the fields.
///
/// # Returns
///
/// Returns the constructed `Arc` for the `Session` struct.
pub fn session_info() -> Arc<Session> {
Arc::new(Session {
tracker: Mutex::new(HashMap::new()),
mapping: Mutex::new(HashMap::new()),
})
}

/// Create a [Fernet](https://docs.rs/fernet/latest/fernet/) object to encrypt and decrypt session token.
///
/// Generates a random key, that can be safely passed to `Fernet::new()`
///
/// # Returns
///
/// Returns the constructed `Arc` for the `Fernet` instance, with the generated key.
pub fn fernet_object() -> Arc<Fernet> {
Arc::new(Fernet::new(&Fernet::generate_key()).unwrap())
}
13 changes: 6 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,11 @@
extern crate actix_web;

use std::io;
use std::sync::Arc;

use actix_web::{App, HttpServer, middleware, web};
use fernet::Fernet;
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};

/// Module to load all the static values and required structs during startup.
/// Module for the structs and functions called during startup.
mod constant;
/// Module for all the API entry points.
mod routes;
Expand Down Expand Up @@ -50,14 +48,14 @@ pub async fn start() -> io::Result<()> {
"Secure session is turned on! This means that the server can ONLY be hosted via HTTPS or localhost"
);
}
let jinja_env = templates::environment();
// Create a dedicated clone, since it will be used within closure
let config_clone = config.clone();
let jinja_clone = jinja_env.clone();
let host = format!("{}:{}", config.media_host, config.media_port);
log::info!("{} [workers:{}] running on http://{} (Press CTRL+C to quit)",
&cargo.pkg_name, &config.workers, &host);
let fernet = Arc::new(Fernet::new(&squire::fernet_key()).unwrap());
let jinja = templates::environment();
let fernet = constant::fernet_object();
let session = constant::session_info();
/*
|| syntax is creating a closure that serves as the argument to the HttpServer::new() method.
The closure is defining the configuration for the Actix web server.
Expand All @@ -66,8 +64,9 @@ pub async fn start() -> io::Result<()> {
let application = move || {
App::new() // Creates a new Actix web application
.app_data(web::Data::new(config_clone.clone()))
.app_data(web::Data::new(jinja_clone.clone()))
.app_data(web::Data::new(jinja.clone()))
.app_data(web::Data::new(fernet.clone()))
.app_data(web::Data::new(session.clone()))
.wrap(squire::middleware::get_cors(config_clone.websites.clone()))
.wrap(middleware::Logger::default()) // Adds a default logger middleware to the application
.service(routes::basics::health) // Registers a service for handling requests
Expand Down
57 changes: 32 additions & 25 deletions src/routes/auth.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::sync::{Arc, Mutex};
use std::sync::Arc;

use actix_web::{HttpRequest, HttpResponse, web};
use actix_web::cookie::{Cookie, SameSite};
Expand Down Expand Up @@ -26,18 +26,21 @@ pub struct DetailError {
///
/// # Arguments
///
/// * `config` - Configuration data for the application.
/// * `request` - A reference to the Actix web `HttpRequest` object.
/// * `config` - Configuration data for the application.
/// * `fernet` - Fernet object to encrypt the auth payload that will be set as `session_token` cookie.
/// * `session` - Session struct that holds the `session_mapping` and `session_tracker` to handle sessions.
///
/// # Returns
///
/// * `200` - HttpResponse with a `session_token` and redirect URL to the `/home` entrypoint.
/// * `401` - HttpResponse with an error message for failed authentication.
#[post("/login")]
pub async fn login(config: web::Data<Arc<squire::settings::Config>>,
pub async fn login(request: HttpRequest,
config: web::Data<Arc<squire::settings::Config>>,
fernet: web::Data<Arc<Fernet>>,
request: HttpRequest) -> HttpResponse {
let verified = squire::authenticator::verify_login(&request, &config);
session: web::Data<Arc<constant::Session>>) -> HttpResponse {
let verified = squire::authenticator::verify_login(&request, &config, &session);
if let Err(err) = verified {
let err_message = err.to_string();
log::warn!("Error response::{}", err_message);
Expand All @@ -47,7 +50,7 @@ pub async fn login(config: web::Data<Arc<squire::settings::Config>>,
}

let mapped = verified.unwrap();
squire::logger::log_connection(&request);
squire::logger::log_connection(&request, &session);

let payload = serde_json::to_string(&mapped).unwrap();
let encrypted_payload = fernet.encrypt(payload.as_bytes());
Expand Down Expand Up @@ -79,34 +82,36 @@ pub async fn login(config: web::Data<Arc<squire::settings::Config>>,
///
/// # Arguments
///
/// * `config` - Configuration data for the application.
/// * `environment` - Configuration container for the loaded templates.
/// * `request` - A reference to the Actix web `HttpRequest` object.
/// * `config` - Configuration data for the application.
/// * `fernet` - Fernet object to encrypt the auth payload that will be set as `session_token` cookie.
/// * `template` - Configuration container for the loaded templates.
/// * `session` - Session struct that holds the `session_mapping` and `session_tracker` to handle sessions.
///
/// # Returns
///
/// Returns an `HTTPResponse` with the cookie for `session_token` reset if available.
#[get("/logout")]
pub async fn logout(config: web::Data<Arc<squire::settings::Config>>,
pub async fn logout(request: HttpRequest,
config: web::Data<Arc<squire::settings::Config>>,
fernet: web::Data<Arc<Fernet>>,
environment: web::Data<Arc<Mutex<minijinja::Environment<'static>>>>,
request: HttpRequest) -> HttpResponse {
template: web::Data<Arc<minijinja::Environment<'static>>>,
session: web::Data<Arc<constant::Session>>) -> HttpResponse {
let host = request.connection_info().host().to_owned();
let template = environment.lock().unwrap();
let logout_template = template.get_template("logout").unwrap();
let mut response = HttpResponse::build(StatusCode::OK);
response.content_type("text/html; charset=utf-8");

let rendered;
let auth_response = squire::authenticator::verify_token(&request, &config, &fernet);
let auth_response = squire::authenticator::verify_token(&request, &config, &fernet, &session);
log::debug!("Session Validation Response: {}", auth_response.detail);

if auth_response.username != "NA" {
log::info!("{} from {} attempted to log out", auth_response.username, host)
}

if auth_response.ok {
let mut tracker = constant::HOST_SERVE.lock().unwrap();
let mut tracker = session.tracker.lock().unwrap();
if tracker.get(&host).is_some() {
tracker.remove(&host);
} else {
Expand All @@ -133,28 +138,30 @@ pub async fn logout(config: web::Data<Arc<squire::settings::Config>>,
///
/// # Arguments
///
/// * `config` - Configuration data for the application.
/// * `environment` - Configuration container for the loaded templates.
/// * `request` - A reference to the Actix web `HttpRequest` object.
/// * `config` - Configuration data for the application.
/// * `fernet` - Fernet object to encrypt the auth payload that will be set as `session_token` cookie.
/// * `template` - Configuration container for the loaded templates.
/// * `session` - Session struct that holds the `session_mapping` and `session_tracker` to handle sessions.
///
/// # Returns
///
/// * `200` - Returns an `HTTPResponse` with the home/listing page if session token is valid.
/// * `401` - HttpResponse with an error message for failed authentication.
#[get("/home")]
pub async fn home(config: web::Data<Arc<squire::settings::Config>>,
pub async fn home(request: HttpRequest,
config: web::Data<Arc<squire::settings::Config>>,
fernet: web::Data<Arc<Fernet>>,
environment: web::Data<Arc<Mutex<minijinja::Environment<'static>>>>,
request: HttpRequest) -> HttpResponse {
let auth_response = squire::authenticator::verify_token(&request, &config, &fernet);
template: web::Data<Arc<minijinja::Environment<'static>>>,
session: web::Data<Arc<constant::Session>>) -> HttpResponse {
let auth_response = squire::authenticator::verify_token(&request, &config, &fernet, &session);
if !auth_response.ok {
return failed_auth(auth_response, &config);
}
squire::logger::log_connection(&request);
squire::logger::log_connection(&request, &session);
log::debug!("{}", auth_response.detail);

let listing_page = squire::content::get_all_stream_content(&config);
let template = environment.lock().unwrap();
let listing = template.get_template("listing").unwrap();

HttpResponse::build(StatusCode::OK)
Expand All @@ -170,16 +177,15 @@ pub async fn home(config: web::Data<Arc<squire::settings::Config>>,
///
/// # Arguments
///
/// * `environment` - Configuration container for the loaded templates.
/// * `template` - Configuration container for the loaded templates.
/// * `request` - A reference to the Actix web `HttpRequest` object.
///
/// # Returns
///
/// HttpResponse with either a session expiry or unauthorized message.
#[get("/error")]
pub async fn error(environment: web::Data<Arc<Mutex<minijinja::Environment<'static>>>>,
pub async fn error(template: web::Data<Arc<minijinja::Environment<'static>>>,
request: HttpRequest) -> HttpResponse {
let template = environment.lock().unwrap();
if let Some(detail) = request.cookie("detail") {
log::info!("Error response for /error: {}", detail.value());
let session = template.get_template("session").unwrap();
Expand All @@ -200,6 +206,7 @@ pub async fn error(environment: web::Data<Arc<Mutex<minijinja::Environment<'stat
/// # Arguments
///
/// * `auth_response` - The authentication response containing details of the failure.
/// * `config` - Configuration data for the application.
///
/// # Returns
///
Expand Down
15 changes: 8 additions & 7 deletions src/routes/basics.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use std::sync::{Arc, Mutex};
use std::sync::Arc;

use actix_web::{HttpRequest, HttpResponse, web};
use actix_web::http::StatusCode;

use crate::squire;
use crate::{constant, squire};

/// Handles the health endpoint, returning a JSON response indicating the server is healthy.
///
Expand All @@ -22,19 +22,20 @@ pub async fn health() -> HttpResponse {
///
/// # Arguments
///
/// * `environment` - Configuration container for the loaded templates.
/// * `request` - A reference to the Actix web `HttpRequest` object.
/// * `template` - Configuration container for the loaded templates.
/// * `session` - Session struct that holds the `session_mapping` and `session_tracker` to handle sessions.
///
/// # Returns
///
/// Returns an `HttpResponse` with the index page as its body.
#[get("/")]
pub async fn root(environment: web::Data<Arc<Mutex<minijinja::Environment<'static>>>>,
request: HttpRequest) -> HttpResponse {
pub async fn root(request: HttpRequest,
template: web::Data<Arc<minijinja::Environment<'static>>>,
session: web::Data<Arc<constant::Session>>) -> HttpResponse {
// Log the connection using the squire::logger::log_connection function.
squire::logger::log_connection(&request);
squire::logger::log_connection(&request, &session);

let template = environment.lock().unwrap();
let index = template.get_template("index").unwrap();
HttpResponse::build(StatusCode::OK)
.content_type("text/html; charset=utf-8")
Expand Down
Loading

0 comments on commit 717fc17

Please sign in to comment.