Skip to content

Commit

Permalink
Take an optional argument to secure the session-token
Browse files Browse the repository at this point in the history
Update HTML templates and README.md
  • Loading branch information
dormant-user committed Feb 19, 2024
1 parent 93e774e commit e59bc92
Show file tree
Hide file tree
Showing 9 changed files with 83 additions and 28 deletions.
21 changes: 20 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,25 @@ async fn main() {
#### Config file
[RuStream][repo] requires a JSON file with secrets loaded as key-value paris.

**Mandatory**
- **authorization**: Dictionary of key-value pairs with `username` as key and `password` as value.
- **video_source**: Source path for video files.
> Files starting with `_` _(underscore)_ and `.` _(dot)_ will be ignored
**Optional**
- **video_host**: IP address to host the video. Defaults to `127.0.0.1` / `localhost`
- **video_port**: Port number to host the application. Defaults to `8000`
- **session_duration**: Time _(in seconds)_ each authenticated session should last. Defaults to `3600`
- **file_formats**: Vector of supported video file formats. Defaults to `[.mp4, .mov]`
- **workers**: Number of workers to spin up for the server. Defaults to the number of physical cores.
- **max_connections**: Maximum number of concurrent connections per worker. Defaults to `3`
- **websites**: Vector of websites (_supports regex_) to add to CORS configuration. _Required only if tunneled via CDN_
- **key_file**: Path to the private key file for SSL certificate. Defaults to `None`
- **cert_file**: Path to the full chain file for SSL certificate. Defaults to `None`
- **secure_session**: Boolean flag to secure the cookie `session_token`. Defaults to `false`
> If `SECURE_SESSION` to set to `true`, the cookie `session_token` will only be sent via HTTPS<br>
> This means that the server can **ONLY** be hosted via `HTTPS` or `localhost`
<details>
<summary><i><strong>Sample content of JSON file</strong></i></summary>

Expand All @@ -89,7 +108,7 @@ rustup component add clippy
```
### Usage
```shell
cargo clippy --no-deps --fix --allow-dirty
cargo clippy --no-deps --fix
```

## Docs
Expand Down
7 changes: 6 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ pub async fn start() -> io::Result<()> {
println!("{}", arts.choose(&mut rand::thread_rng()).unwrap());

let config = squire::startup::get_config(args);
if config.secure_session {
log::warn!(
"Secure session is turned on! This means that the server can ONLY be hosted via HTTPS or localhost"
);
}
let template = jinja::environment();
// Create a dedicated clone, since it will be used within closure
let config_clone = config.clone();
Expand All @@ -64,7 +69,7 @@ pub async fn start() -> io::Result<()> {
App::new() // Creates a new Actix web application
.app_data(web::Data::new(config_clone.clone()))
.app_data(web::Data::new(template_clone.clone()))
.wrap(squire::middleware::get_cors(config_clone.website.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
.service(routes::basics::root)
Expand Down
29 changes: 21 additions & 8 deletions src/routes/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,19 @@ pub async fn login(config: web::Data<Arc<squire::settings::Config>>, request: Ht

let cookie_duration = Duration::seconds(config.session_duration as i64);
let expiration = OffsetDateTime::now_utc() + cookie_duration;
let cookie = Cookie::build("session_token", encrypted_payload)
let base_cookie = Cookie::build("session_token", encrypted_payload)
.http_only(true)
.same_site(SameSite::Strict)
.max_age(cookie_duration)
.expires(expiration)
.finish();
.expires(expiration);

let cookie;
if config.secure_session {
log::info!("Marking 'session_token' cookie as secure!!");
cookie = base_cookie.secure(true).finish();
} else {
cookie = base_cookie.finish();
}
log::info!("Session for '{}' will be valid until {}", mapped.get("username").unwrap(), expiration);

let mut response = HttpResponse::Ok().json(RedirectResponse {
Expand Down Expand Up @@ -136,7 +142,7 @@ pub async fn home(config: web::Data<Arc<squire::settings::Config>>,
request: HttpRequest) -> HttpResponse {
let auth_response = squire::authenticator::verify_token(&request, &config);
if !auth_response.ok {
return failed_auth(auth_response);
return failed_auth(auth_response, &config);
}
squire::logger::log_connection(&request);
log::debug!("{}", auth_response.detail);
Expand Down Expand Up @@ -191,16 +197,23 @@ pub async fn error(environment: web::Data<Arc<Mutex<minijinja::Environment<'stat
/// # Returns
///
/// Returns an `HttpResponse` with a redirect, setting a cookie with the failure detail.
pub fn failed_auth(auth_response: squire::authenticator::AuthToken) -> HttpResponse {
pub fn failed_auth(auth_response: squire::authenticator::AuthToken,
config: &squire::settings::Config) -> HttpResponse {
let mut response = HttpResponse::build(StatusCode::FOUND);
let detail = auth_response.detail;
let age = Duration::new(3, 0);
let cookie = Cookie::build("detail", detail)
let base_cookie = Cookie::build("detail", detail)
.path("/error")
.http_only(true)
.same_site(SameSite::Strict)
.max_age(age)
.finish();
.max_age(age);
let cookie;
if config.secure_session {
log::debug!("Marking 'detail' cookie as secure!!");
cookie = base_cookie.secure(true).finish();
} else {
cookie = base_cookie.finish();
}
response.cookie(cookie);
response.append_header(("Location", "/error"));
response.finish()
Expand Down
6 changes: 3 additions & 3 deletions src/routes/video.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ pub async fn track(config: web::Data<Arc<squire::settings::Config>>,
request: HttpRequest, info: web::Query<Payload>) -> HttpResponse {
let auth_response = squire::authenticator::verify_token(&request, &config);
if !auth_response.ok {
return routes::auth::failed_auth(auth_response);
return routes::auth::failed_auth(auth_response, &config);
}
squire::logger::log_connection(&request);
log::debug!("{}", auth_response.detail);
Expand Down Expand Up @@ -116,7 +116,7 @@ pub async fn stream(config: web::Data<Arc<squire::settings::Config>>,
request: HttpRequest, video_path: web::Path<String>) -> HttpResponse {
let auth_response = squire::authenticator::verify_token(&request, &config);
if !auth_response.ok {
return routes::auth::failed_auth(auth_response);
return routes::auth::failed_auth(auth_response, &config);
}
squire::logger::log_connection(&request);
log::debug!("{}", auth_response.detail);
Expand Down Expand Up @@ -212,7 +212,7 @@ pub async fn streaming_endpoint(config: web::Data<Arc<squire::settings::Config>>
request: HttpRequest, info: web::Query<Payload>) -> HttpResponse {
let auth_response = squire::authenticator::verify_token(&request, &config);
if !auth_response.ok {
return routes::auth::failed_auth(auth_response);
return routes::auth::failed_auth(auth_response, &config);
}
squire::logger::log_connection(&request);
let host = request.connection_info().host().to_owned();
Expand Down
8 changes: 4 additions & 4 deletions src/squire/middleware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ use actix_web::http::header;
///
/// # Arguments
///
/// * `website` - A vector of allowed website origins for CORS.
/// * `websites` - A vector of allowed website origins for CORS.
///
/// # Returns
///
/// A configured `Cors` middleware instance.
pub fn get_cors(website: Vec<String>) -> Cors {
pub fn get_cors(websites: Vec<String>) -> Cors {
let mut origins = vec!["http://localhost.com".to_string(), "https://localhost.com".to_string()];
if !website.is_empty() {
origins.extend_from_slice(&website);
if !websites.is_empty() {
origins.extend_from_slice(&websites);
}
// Create a clone to append /* to each endpoint, and further extend the same vector
let cloned = origins.clone().into_iter().map(|x| format!("{}/{}", x, "*"));
Expand Down
22 changes: 14 additions & 8 deletions src/squire/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,19 @@ pub struct Config {
#[serde(default = "default_max_connections")]
pub max_connections: i32,
/// List of websites (supports regex) to add to CORS configuration.
#[serde(default = "default_website")]
pub website: Vec<String>,
#[serde(default = "default_websites")]
pub websites: Vec<String>,

// Boolean flag to restrict session_token to be sent only via HTTPS
#[serde(default = "default_secure_session")]
pub secure_session: bool,

// Path to the private key file for SSL certificate
#[serde(default = "default_ssl")]
pub key_file: path::PathBuf,
// Path to the full certificate chain file for SSL certificate
#[serde(default = "default_ssl")]
pub cert_file: path::PathBuf,
// Path to the private key file for SSL certificate
#[serde(default = "default_ssl")]
pub key_file: path::PathBuf
}

/// Returns the default value for ssl files
Expand Down Expand Up @@ -91,12 +95,14 @@ fn default_workers() -> i32 {
}
}

/// Returns the default maximum number of concurrent connections (300).
/// Returns the default maximum number of concurrent connections (3).
fn default_max_connections() -> i32 {
300
3
}

/// Returns an empty list as the default website (CORS configuration).
fn default_website() -> Vec<String> {
fn default_websites() -> Vec<String> {
Vec::new()
}

fn default_secure_session() -> bool { false }
6 changes: 5 additions & 1 deletion src/templates/logout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,12 @@ pub fn get_content() -> String {
r###"<!DOCTYPE html>
<html lang="en">
<head>
<title>Rustic video streaming</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Rustic video streaming</title>
<meta property="og:type" content="VideoStreaming">
<meta name="keywords" content="Python, streaming, fastapi, JavaScript, HTML, CSS">
<meta name="author" content="Vignesh Rao">
<meta content="width=device-width, initial-scale=1" name="viewport">
<style>
img {
display: block;
Expand Down
6 changes: 5 additions & 1 deletion src/templates/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@ pub fn get_content() -> String {
r###"<!DOCTYPE html>
<html lang="en">
<head>
<title>Rustic video streaming</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Rustic video streaming</title>
<meta property="og:type" content="VideoStreaming">
<meta name="keywords" content="Python, streaming, fastapi, JavaScript, HTML, CSS">
<meta name="author" content="Vignesh Rao">
<meta content="width=device-width, initial-scale=1" name="viewport">
<style>
img {
display: block;
Expand Down
6 changes: 5 additions & 1 deletion src/templates/unauthorized.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@ pub fn get_content() -> String {
r###"<!DOCTYPE html>
<html lang="en">
<head>
<title>Rustic video streaming</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Rustic video streaming</title>
<meta property="og:type" content="VideoStreaming">
<meta name="keywords" content="Python, streaming, fastapi, JavaScript, HTML, CSS">
<meta name="author" content="Vignesh Rao">
<meta content="width=device-width, initial-scale=1" name="viewport">
<style>
img {
display: block;
Expand Down

0 comments on commit e59bc92

Please sign in to comment.