mirror of
https://github.com/euzu/tuliprox.git
synced 2026-10-19 06:12:24 +02:00
Web Ui Autrhentication
This commit is contained in:
@@ -5,7 +5,7 @@ use actix_web::{HttpRequest, HttpResponse, web};
|
||||
use log::{debug, error};
|
||||
use url::Url;
|
||||
use crate::api::api_model::{AppState, UserApiRequest};
|
||||
use crate::model::api_proxy::{ApiProxyServerInfo, UserCredentials};
|
||||
use crate::model::api_proxy::{ApiProxyServerInfo, ProxyUserCredentials};
|
||||
use crate::model::config::{Config, ConfigTarget, ConfigInput};
|
||||
use crate::utils::request_utils;
|
||||
|
||||
@@ -23,7 +23,7 @@ pub(crate) async fn serve_file(file_path: &Path, req: &HttpRequest, mime_type: m
|
||||
}
|
||||
|
||||
pub(crate) fn get_user_target_by_credentials<'a>(username: &str, password: &str, api_req: &'a UserApiRequest,
|
||||
app_state: &'a web::Data<AppState>) -> Option<(UserCredentials, &'a ConfigTarget)> {
|
||||
app_state: &'a web::Data<AppState>) -> Option<(ProxyUserCredentials, &'a ConfigTarget)> {
|
||||
if !username.is_empty() && !password.is_empty() {
|
||||
app_state.config.get_target_for_user(username, password)
|
||||
} else {
|
||||
@@ -36,13 +36,13 @@ pub(crate) fn get_user_target_by_credentials<'a>(username: &str, password: &str,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_user_target<'a>(api_req: &'a UserApiRequest, app_state: &'a web::Data<AppState>) -> Option<(UserCredentials, &'a ConfigTarget)> {
|
||||
pub(crate) fn get_user_target<'a>(api_req: &'a UserApiRequest, app_state: &'a web::Data<AppState>) -> Option<(ProxyUserCredentials, &'a ConfigTarget)> {
|
||||
let username = api_req.username.as_str().trim();
|
||||
let password = api_req.password.as_str().trim();
|
||||
get_user_target_by_credentials(username, password, api_req, app_state)
|
||||
}
|
||||
|
||||
pub(crate) fn get_user_server_info(cfg: &Config, user: &UserCredentials) -> ApiProxyServerInfo {
|
||||
pub(crate) fn get_user_server_info(cfg: &Config, user: &ProxyUserCredentials) -> ApiProxyServerInfo {
|
||||
let server_info_list = cfg._api_proxy.read().unwrap().as_ref().unwrap().server.clone();
|
||||
let server_info_name = match &user.server {
|
||||
Some(server_name) => server_name.as_str(),
|
||||
|
||||
+7
-10
@@ -5,8 +5,7 @@ use std::sync::{Arc, Mutex, RwLock};
|
||||
|
||||
use actix_cors::Cors;
|
||||
use actix_web::{App, HttpServer, web};
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::middleware::{ErrorHandlers, Logger};
|
||||
use actix_web::middleware::{Logger};
|
||||
use log::info;
|
||||
|
||||
use crate::api::api_model::{AppState, DownloadQueue, SharedLocks};
|
||||
@@ -16,7 +15,6 @@ use crate::api::v1_api::v1_api_register;
|
||||
use crate::api::web_index::index_register;
|
||||
use crate::api::xmltv_api::xmltv_api_register;
|
||||
use crate::api::xtream_api::xtream_api_register;
|
||||
use crate::auth::authenticator::handle_unauthorized;
|
||||
use crate::model::config::{Config, ProcessTargets};
|
||||
use crate::processing::playlist_processor;
|
||||
|
||||
@@ -74,7 +72,6 @@ pub(crate) async fn start_server(cfg: Arc<Config>, targets: Arc<ProcessTargets>)
|
||||
if web_ui_enabled {
|
||||
info!("Web root: {:?}", &web_dir_path);
|
||||
}
|
||||
let web_auth_enabled = cfg.web_auth_enabled;
|
||||
|
||||
// Web Server
|
||||
HttpServer::new(move || {
|
||||
@@ -87,19 +84,19 @@ pub(crate) async fn start_server(cfg: Arc<Config>, targets: Arc<ProcessTargets>)
|
||||
.allow_any_header()
|
||||
.max_age(3600))
|
||||
.app_data(shared_data.clone())
|
||||
.wrap(ErrorHandlers::new().handler(StatusCode::UNAUTHORIZED, handle_unauthorized))
|
||||
.configure(|cfg| {
|
||||
// .wrap(Condition::new(web_auth_enabled, ErrorHandlers::new().handler(StatusCode::UNAUTHORIZED, handle_unauthorized)))
|
||||
.configure(|srvcfg| {
|
||||
if web_ui_enabled {
|
||||
cfg.service(actix_files::Files::new("/static", web_dir_path.join("static")));
|
||||
cfg.configure(v1_api_register(web_auth_enabled));
|
||||
srvcfg.service(actix_files::Files::new("/static", web_dir_path.join("static")));
|
||||
srvcfg.configure(v1_api_register(cfg.web_auth.as_ref().unwrap().clone()));
|
||||
}
|
||||
})
|
||||
.configure(xtream_api_register)
|
||||
.configure(m3u_api_register)
|
||||
.configure(xmltv_api_register)
|
||||
.configure(|cfg| {
|
||||
.configure(|srvcfg| {
|
||||
if web_ui_enabled {
|
||||
cfg.configure(index_register(&web_dir_path, web_auth_enabled));
|
||||
srvcfg.configure(index_register(&web_dir_path, cfg.web_auth.as_ref().unwrap().clone()));
|
||||
}
|
||||
})
|
||||
}).bind(format!("{}:{}", host, port))?.run().await
|
||||
|
||||
+4
-3
@@ -11,7 +11,7 @@ use crate::api::download_api;
|
||||
use crate::auth::authenticator::validator;
|
||||
use crate::m3u_filter_error::M3uFilterError;
|
||||
use crate::model::api_proxy::{ApiProxyConfig, ApiProxyServerInfo, TargetUser};
|
||||
use crate::model::config::{Config, ConfigDto, ConfigInput, ConfigInputOptions, ConfigSource, ConfigTarget, InputType, validate_targets};
|
||||
use crate::model::config::{Config, ConfigDto, ConfigInput, ConfigInputOptions, ConfigSource, ConfigTarget, InputType, validate_targets, WebAuthConfig};
|
||||
use crate::processing::playlist_processor;
|
||||
use crate::utils::{config_reader, download};
|
||||
|
||||
@@ -212,7 +212,7 @@ pub(crate) async fn config(
|
||||
app_state.config._config_file_path.as_str(),
|
||||
app_state.config._sources_file_path.as_str()) {
|
||||
Ok(mut cfg) => {
|
||||
let _ = cfg.prepare();
|
||||
let _ = cfg.prepare(true);
|
||||
map_config(&cfg)
|
||||
}
|
||||
Err(_) => map_config(&app_state.config)
|
||||
@@ -226,7 +226,8 @@ pub(crate) async fn config(
|
||||
HttpResponse::Ok().json(result)
|
||||
}
|
||||
|
||||
pub(crate) fn v1_api_register(web_auth_enabled: bool) -> impl Fn(&mut web::ServiceConfig) -> () {
|
||||
pub(crate) fn v1_api_register(web_auth_config: WebAuthConfig) -> impl Fn(&mut web::ServiceConfig) -> () {
|
||||
let web_auth_enabled = web_auth_config.enabled;
|
||||
return move |cfg: &mut web::ServiceConfig| {
|
||||
cfg.service(web::scope("/api/v1")
|
||||
.wrap(Condition::new(web_auth_enabled, HttpAuthentication::with_fn(validator)))
|
||||
|
||||
+55
-11
@@ -1,27 +1,71 @@
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use actix_files::NamedFile;
|
||||
use actix_web::{HttpRequest, web};
|
||||
use actix_web::middleware::Condition;
|
||||
use actix_web_httpauth::middleware::HttpAuthentication;
|
||||
use actix_web::{HttpRequest, HttpResponse, web};
|
||||
use actix_web_httpauth::extractors::bearer::BearerAuth;
|
||||
|
||||
use crate::api::api_model::AppState;
|
||||
use crate::auth::authenticator::validator;
|
||||
use crate::auth::authenticator::{create_jwt, verify_token};
|
||||
use crate::auth::password::verify_password;
|
||||
use crate::auth::user::UserCredential;
|
||||
use crate::model::config::WebAuthConfig;
|
||||
|
||||
async fn index(
|
||||
async fn token(
|
||||
mut req: web::Json<UserCredential>,
|
||||
app_state: web::Data<AppState>,
|
||||
) -> HttpResponse {
|
||||
let username = req.username.as_str();
|
||||
let password = req.password.as_str();
|
||||
|
||||
if username.len() > 0 && password.len() > 0 {
|
||||
let web_auth = app_state.config.web_auth.as_ref().unwrap();
|
||||
if let Some(hash) = web_auth.get_user_password(username) {
|
||||
if verify_password(hash, &password.as_bytes()) {
|
||||
req.zeroize();
|
||||
if let Ok(token) = create_jwt(web_auth) {
|
||||
return HttpResponse::Ok().json(HashMap::from([("token", token)]));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
req.zeroize();
|
||||
HttpResponse::BadRequest().finish()
|
||||
}
|
||||
|
||||
async fn token_refresh(
|
||||
_req: HttpRequest,
|
||||
_app_state: web::Data<AppState>,
|
||||
) -> std::io::Result<NamedFile> {
|
||||
let path: PathBuf = [&_app_state.config.api.web_root, "index.html"].iter().collect();
|
||||
NamedFile::open(path)
|
||||
credentials: Option<BearerAuth>,
|
||||
app_state: web::Data<AppState>,
|
||||
) -> HttpResponse {
|
||||
let secret_key = app_state.config.web_auth.as_ref().unwrap().secret.as_ref();
|
||||
if verify_token(credentials, secret_key) {
|
||||
if let Ok(token) = create_jwt(app_state.config.web_auth.as_ref().unwrap()) {
|
||||
return HttpResponse::Ok().json(HashMap::from([("token", token)]));
|
||||
}
|
||||
}
|
||||
HttpResponse::BadRequest().finish()
|
||||
}
|
||||
|
||||
|
||||
pub(crate) fn index_register(web_dir_path: &PathBuf, web_auth_enabled: bool) -> impl Fn(&mut web::ServiceConfig) -> () {
|
||||
async fn index(
|
||||
_req: HttpRequest,
|
||||
app_state: web::Data<AppState>,
|
||||
) -> std::io::Result<NamedFile> {
|
||||
let path: PathBuf = [&app_state.config.api.web_root, "index.html"].iter().collect();
|
||||
NamedFile::open(path)
|
||||
}
|
||||
|
||||
pub(crate) fn index_register(web_dir_path: &PathBuf, web_auth_config: WebAuthConfig) -> impl Fn(&mut web::ServiceConfig) -> () {
|
||||
let wdp = web_dir_path.clone();
|
||||
let web_auth_enabled = web_auth_config.enabled;
|
||||
return move |cfg: &mut web::ServiceConfig| {
|
||||
if web_auth_enabled {
|
||||
cfg.service(web::scope("/auth")
|
||||
.route("/token", web::post().to(token))
|
||||
.route("/refresh", web::post().to(token_refresh)));
|
||||
}
|
||||
cfg.service(web::scope("/")
|
||||
.wrap(Condition::new(web_auth_enabled, HttpAuthentication::with_fn(validator)))
|
||||
.route("", web::get().to(index))
|
||||
.service(actix_files::Files::new("/", &wdp)));
|
||||
};
|
||||
|
||||
@@ -12,7 +12,7 @@ use url::Url;
|
||||
|
||||
use crate::api::api_model::{AppState, UserApiRequest, XtreamAuthorizationResponse, XtreamServerInfo, XtreamUserInfo};
|
||||
use crate::api::api_utils::{get_user_server_info, get_user_target, get_user_target_by_credentials, serve_file, stream_response};
|
||||
use crate::model::api_proxy::{ProxyType, UserCredentials};
|
||||
use crate::model::api_proxy::{ProxyType, ProxyUserCredentials};
|
||||
use crate::model::config::{Config, ConfigInput, InputType};
|
||||
use crate::model::config::TargetType;
|
||||
use crate::model::playlist::XtreamCluster;
|
||||
@@ -112,7 +112,7 @@ fn get_xtream_player_api_stream_url(input: &ConfigInput, context: &str, action_p
|
||||
}
|
||||
|
||||
|
||||
fn get_user_info(user: &UserCredentials, cfg: &Config) -> XtreamAuthorizationResponse {
|
||||
fn get_user_info(user: &ProxyUserCredentials, cfg: &Config) -> XtreamAuthorizationResponse {
|
||||
let server_info = get_user_server_info(cfg, user);
|
||||
|
||||
let now = Local::now();
|
||||
@@ -262,7 +262,7 @@ async fn xtream_get_stream_info(app_state: &AppState, target_name: &str, stream_
|
||||
Err(Error::new(std::io::ErrorKind::Other, format!("Cant find stream with id: {}/{}/{}", target_name, &cluster, stream_id)))
|
||||
}
|
||||
|
||||
async fn xtream_get_stream_info_response(app_state: &AppState, user: &UserCredentials,
|
||||
async fn xtream_get_stream_info_response(app_state: &AppState, user: &ProxyUserCredentials,
|
||||
target_name: &str, stream_id: &str,
|
||||
cluster: &XtreamCluster) -> HttpResponse {
|
||||
match FromStr::from_str(stream_id) {
|
||||
@@ -284,7 +284,7 @@ async fn xtream_get_stream_info_response(app_state: &AppState, user: &UserCreden
|
||||
}
|
||||
}
|
||||
|
||||
async fn xtream_get_short_epg(app_state: &AppState, user: &UserCredentials, target_name: &str, stream_id: &str, limit: &str) -> HttpResponse {
|
||||
async fn xtream_get_short_epg(app_state: &AppState, user: &ProxyUserCredentials, target_name: &str, stream_id: &str, limit: &str) -> HttpResponse {
|
||||
if !stream_id.is_empty() {
|
||||
if let Some(target_input) = app_state.config.get_input_for_target(target_name, &InputType::Xtream) {
|
||||
if let Some(action_url) = get_xtream_player_api_action_url(target_input, "get_short_epg") {
|
||||
|
||||
Reference in New Issue
Block a user