support multiple channel ids

This commit is contained in:
jb-alvarado 2024-06-18 16:44:48 +02:00
parent afc94b3b7a
commit 9f422706ea
7 changed files with 71 additions and 49 deletions

View File

@ -15,17 +15,17 @@ const JWT_EXPIRATION_DAYS: i64 = 7;
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] #[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
pub struct Claims { pub struct Claims {
pub id: i32, pub id: i32,
pub channel: i32, pub channels: Vec<i32>,
pub username: String, pub username: String,
pub role: Role, pub role: Role,
exp: i64, exp: i64,
} }
impl Claims { impl Claims {
pub fn new(id: i32, channel: i32, username: String, role: Role) -> Self { pub fn new(id: i32, channels: Vec<i32>, username: String, role: Role) -> Self {
Self { Self {
id, id,
channel, channels,
username, username,
role, role,
exp: (Utc::now() + TimeDelta::try_days(JWT_EXPIRATION_DAYS).unwrap()).timestamp(), exp: (Utc::now() + TimeDelta::try_days(JWT_EXPIRATION_DAYS).unwrap()).timestamp(),

View File

@ -186,7 +186,7 @@ pub async fn login(pool: web::Data<Pool<Sqlite>>, credentials: web::Json<User>)
if verified_password.is_ok() { if verified_password.is_ok() {
let claims = Claims::new( let claims = Claims::new(
user.id, user.id,
user.channel_id.unwrap_or_default(), user.channel_ids.clone(),
username.clone(), username.clone(),
role.clone(), role.clone(),
); );
@ -417,7 +417,7 @@ async fn remove_user(
#[protect( #[protect(
any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"), any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"),
ty = "Role", ty = "Role",
expr = "*id == user.channel || role.has_authority(&Role::GlobalAdmin)" expr = "user.channels.contains(&*id) || role.has_authority(&Role::GlobalAdmin)"
)] )]
async fn get_channel( async fn get_channel(
pool: web::Data<Pool<Sqlite>>, pool: web::Data<Pool<Sqlite>>,
@ -459,7 +459,7 @@ async fn get_all_channels(pool: web::Data<Pool<Sqlite>>) -> Result<impl Responde
"Role::GlobalAdmin", "Role::GlobalAdmin",
"Role::ChannelAdmin", "Role::ChannelAdmin",
ty = "Role", ty = "Role",
expr = "*id == user.channel || role.has_authority(&Role::GlobalAdmin)" expr = "user.channels.contains(&*id) || role.has_authority(&Role::GlobalAdmin)"
)] )]
async fn patch_channel( async fn patch_channel(
pool: web::Data<Pool<Sqlite>>, pool: web::Data<Pool<Sqlite>>,
@ -537,7 +537,7 @@ async fn remove_channel(
#[protect( #[protect(
any("Role::GlobalAdmin", "Role::ChannelAdmin"), any("Role::GlobalAdmin", "Role::ChannelAdmin"),
ty = "Role", ty = "Role",
expr = "*id == user.channel || role.has_authority(&Role::GlobalAdmin)" expr = "user.channels.contains(&*id) || role.has_authority(&Role::GlobalAdmin)"
)] )]
async fn get_advanced_config( async fn get_advanced_config(
id: web::Path<i32>, id: web::Path<i32>,
@ -562,7 +562,7 @@ async fn get_advanced_config(
"Role::GlobalAdmin", "Role::GlobalAdmin",
"Role::ChannelAdmin", "Role::ChannelAdmin",
ty = "Role", ty = "Role",
expr = "*id == user.channel || role.has_authority(&Role::GlobalAdmin)" expr = "user.channels.contains(&*id) || role.has_authority(&Role::GlobalAdmin)"
)] )]
async fn update_advanced_config( async fn update_advanced_config(
pool: web::Data<Pool<Sqlite>>, pool: web::Data<Pool<Sqlite>>,
@ -682,7 +682,7 @@ async fn update_advanced_config(
#[protect( #[protect(
any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"), any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"),
ty = "Role", ty = "Role",
expr = "*id == user.channel || role.has_authority(&Role::GlobalAdmin)" expr = "user.channels.contains(&*id) || role.has_authority(&Role::GlobalAdmin)"
)] )]
async fn get_playout_config( async fn get_playout_config(
id: web::Path<i32>, id: web::Path<i32>,
@ -707,7 +707,7 @@ async fn get_playout_config(
"Role::GlobalAdmin", "Role::GlobalAdmin",
"Role::ChannelAdmin", "Role::ChannelAdmin",
ty = "Role", ty = "Role",
expr = "*id == user.channel || role.has_authority(&Role::GlobalAdmin)" expr = "user.channels.contains(&*id) || role.has_authority(&Role::GlobalAdmin)"
)] )]
async fn update_playout_config( async fn update_playout_config(
pool: web::Data<Pool<Sqlite>>, pool: web::Data<Pool<Sqlite>>,
@ -826,7 +826,7 @@ async fn update_playout_config(
#[protect( #[protect(
any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"), any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"),
ty = "Role", ty = "Role",
expr = "*id == user.channel || role.has_authority(&Role::GlobalAdmin)" expr = "user.channels.contains(&*id) || role.has_authority(&Role::GlobalAdmin)"
)] )]
async fn get_presets( async fn get_presets(
pool: web::Data<Pool<Sqlite>>, pool: web::Data<Pool<Sqlite>>,
@ -852,7 +852,7 @@ async fn get_presets(
#[protect( #[protect(
any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"), any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"),
ty = "Role", ty = "Role",
expr = "*id == user.channel || role.has_authority(&Role::GlobalAdmin)" expr = "user.channels.contains(&*id) || role.has_authority(&Role::GlobalAdmin)"
)] )]
async fn update_preset( async fn update_preset(
pool: web::Data<Pool<Sqlite>>, pool: web::Data<Pool<Sqlite>>,
@ -882,7 +882,7 @@ async fn update_preset(
#[protect( #[protect(
any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"), any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"),
ty = "Role", ty = "Role",
expr = "*id == user.channel || role.has_authority(&Role::GlobalAdmin)" expr = "user.channels.contains(&*id) || role.has_authority(&Role::GlobalAdmin)"
)] )]
async fn add_preset( async fn add_preset(
pool: web::Data<Pool<Sqlite>>, pool: web::Data<Pool<Sqlite>>,
@ -911,7 +911,7 @@ async fn add_preset(
#[protect( #[protect(
any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"), any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"),
ty = "Role", ty = "Role",
expr = "*id == user.channel || role.has_authority(&Role::GlobalAdmin)" expr = "user.channels.contains(&*id) || role.has_authority(&Role::GlobalAdmin)"
)] )]
async fn delete_preset( async fn delete_preset(
pool: web::Data<Pool<Sqlite>>, pool: web::Data<Pool<Sqlite>>,
@ -948,7 +948,7 @@ async fn delete_preset(
#[protect( #[protect(
any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"), any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"),
ty = "Role", ty = "Role",
expr = "*id == user.channel || role.has_authority(&Role::GlobalAdmin)" expr = "user.channels.contains(&*id) || role.has_authority(&Role::GlobalAdmin)"
)] )]
pub async fn send_text_message( pub async fn send_text_message(
id: web::Path<i32>, id: web::Path<i32>,
@ -979,7 +979,7 @@ pub async fn send_text_message(
#[protect( #[protect(
any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"), any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"),
ty = "Role", ty = "Role",
expr = "*id == user.channel || role.has_authority(&Role::GlobalAdmin)" expr = "user.channels.contains(&*id) || role.has_authority(&Role::GlobalAdmin)"
)] )]
pub async fn control_playout( pub async fn control_playout(
pool: web::Data<Pool<Sqlite>>, pool: web::Data<Pool<Sqlite>>,
@ -1025,7 +1025,7 @@ pub async fn control_playout(
#[protect( #[protect(
any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"), any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"),
ty = "Role", ty = "Role",
expr = "*id == user.channel || role.has_authority(&Role::GlobalAdmin)" expr = "user.channels.contains(&*id) || role.has_authority(&Role::GlobalAdmin)"
)] )]
pub async fn media_current( pub async fn media_current(
id: web::Path<i32>, id: web::Path<i32>,
@ -1056,7 +1056,7 @@ pub async fn media_current(
#[protect( #[protect(
any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"), any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"),
ty = "Role", ty = "Role",
expr = "*id == user.channel || role.has_authority(&Role::GlobalAdmin)" expr = "user.channels.contains(&*id) || role.has_authority(&Role::GlobalAdmin)"
)] )]
pub async fn process_control( pub async fn process_control(
id: web::Path<i32>, id: web::Path<i32>,
@ -1103,7 +1103,7 @@ pub async fn process_control(
#[protect( #[protect(
any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"), any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"),
ty = "Role", ty = "Role",
expr = "*id == user.channel || role.has_authority(&Role::GlobalAdmin)" expr = "user.channels.contains(&*id) || role.has_authority(&Role::GlobalAdmin)"
)] )]
pub async fn get_playlist( pub async fn get_playlist(
id: web::Path<i32>, id: web::Path<i32>,
@ -1132,7 +1132,7 @@ pub async fn get_playlist(
#[protect( #[protect(
any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"), any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"),
ty = "Role", ty = "Role",
expr = "*id == user.channel || role.has_authority(&Role::GlobalAdmin)" expr = "user.channels.contains(&*id) || role.has_authority(&Role::GlobalAdmin)"
)] )]
pub async fn save_playlist( pub async fn save_playlist(
id: web::Path<i32>, id: web::Path<i32>,
@ -1172,7 +1172,7 @@ pub async fn save_playlist(
#[protect( #[protect(
any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"), any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"),
ty = "Role", ty = "Role",
expr = "*id == user.channel || role.has_authority(&Role::GlobalAdmin)" expr = "user.channels.contains(&*id) || role.has_authority(&Role::GlobalAdmin)"
)] )]
pub async fn gen_playlist( pub async fn gen_playlist(
id: web::Path<i32>, id: web::Path<i32>,
@ -1224,7 +1224,7 @@ pub async fn gen_playlist(
#[protect( #[protect(
any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"), any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"),
ty = "Role", ty = "Role",
expr = "*id == user.channel || role.has_authority(&Role::GlobalAdmin)" expr = "user.channels.contains(&*id) || role.has_authority(&Role::GlobalAdmin)"
)] )]
pub async fn del_playlist( pub async fn del_playlist(
id: web::Path<i32>, id: web::Path<i32>,
@ -1254,7 +1254,7 @@ pub async fn del_playlist(
#[protect( #[protect(
any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"), any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"),
ty = "Role", ty = "Role",
expr = "*id == user.channel || role.has_authority(&Role::GlobalAdmin)" expr = "user.channels.contains(&*id) || role.has_authority(&Role::GlobalAdmin)"
)] )]
pub async fn get_log( pub async fn get_log(
id: web::Path<i32>, id: web::Path<i32>,
@ -1277,7 +1277,7 @@ pub async fn get_log(
#[protect( #[protect(
any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"), any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"),
ty = "Role", ty = "Role",
expr = "*id == user.channel || role.has_authority(&Role::GlobalAdmin)" expr = "user.channels.contains(&*id) || role.has_authority(&Role::GlobalAdmin)"
)] )]
pub async fn file_browser( pub async fn file_browser(
id: web::Path<i32>, id: web::Path<i32>,
@ -1306,7 +1306,7 @@ pub async fn file_browser(
#[protect( #[protect(
any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"), any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"),
ty = "Role", ty = "Role",
expr = "*id == user.channel || role.has_authority(&Role::GlobalAdmin)" expr = "user.channels.contains(&*id) || role.has_authority(&Role::GlobalAdmin)"
)] )]
pub async fn add_dir( pub async fn add_dir(
id: web::Path<i32>, id: web::Path<i32>,
@ -1331,7 +1331,7 @@ pub async fn add_dir(
#[protect( #[protect(
any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"), any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"),
ty = "Role", ty = "Role",
expr = "*id == user.channel || role.has_authority(&Role::GlobalAdmin)" expr = "user.channels.contains(&*id) || role.has_authority(&Role::GlobalAdmin)"
)] )]
pub async fn move_rename( pub async fn move_rename(
id: web::Path<i32>, id: web::Path<i32>,
@ -1359,7 +1359,7 @@ pub async fn move_rename(
#[protect( #[protect(
any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"), any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"),
ty = "Role", ty = "Role",
expr = "*id == user.channel || role.has_authority(&Role::GlobalAdmin)" expr = "user.channels.contains(&*id) || role.has_authority(&Role::GlobalAdmin)"
)] )]
pub async fn remove( pub async fn remove(
id: web::Path<i32>, id: web::Path<i32>,
@ -1388,7 +1388,7 @@ pub async fn remove(
#[protect( #[protect(
any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"), any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"),
ty = "Role", ty = "Role",
expr = "*id == user.channel || role.has_authority(&Role::GlobalAdmin)" expr = "user.channels.contains(&*id) || role.has_authority(&Role::GlobalAdmin)"
)] )]
async fn save_file( async fn save_file(
id: web::Path<i32>, id: web::Path<i32>,
@ -1483,7 +1483,7 @@ async fn get_public(public: web::Path<String>) -> Result<actix_files::NamedFile,
#[protect( #[protect(
any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"), any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"),
ty = "Role", ty = "Role",
expr = "*id == user.channel || role.has_authority(&Role::GlobalAdmin)" expr = "user.channels.contains(&*id) || role.has_authority(&Role::GlobalAdmin)"
)] )]
async fn import_playlist( async fn import_playlist(
id: web::Path<i32>, id: web::Path<i32>,
@ -1544,7 +1544,7 @@ async fn import_playlist(
#[protect( #[protect(
any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"), any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"),
ty = "Role", ty = "Role",
expr = "*id == user.channel || role.has_authority(&Role::GlobalAdmin)" expr = "user.channels.contains(&*id) || role.has_authority(&Role::GlobalAdmin)"
)] )]
async fn get_program( async fn get_program(
id: web::Path<i32>, id: web::Path<i32>,
@ -1642,7 +1642,7 @@ async fn get_program(
#[protect( #[protect(
any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"), any("Role::GlobalAdmin", "Role::ChannelAdmin", "Role::User"),
ty = "Role", ty = "Role",
expr = "*id == user.channel || role.has_authority(&Role::GlobalAdmin)" expr = "user.channels.contains(&*id) || role.has_authority(&Role::GlobalAdmin)"
)] )]
pub async fn get_system_stat( pub async fn get_system_stat(
id: web::Path<i32>, id: web::Path<i32>,

View File

@ -306,13 +306,13 @@ pub async fn select_role(conn: &Pool<Sqlite>, id: &i32) -> Result<Role, sqlx::Er
pub async fn select_login(conn: &Pool<Sqlite>, user: &str) -> Result<User, sqlx::Error> { pub async fn select_login(conn: &Pool<Sqlite>, user: &str) -> Result<User, sqlx::Error> {
let query = let query =
"SELECT id, mail, username, password, role_id, channel_id FROM user WHERE username = $1"; "SELECT id, mail, username, password, role_id, channel_ids FROM user WHERE username = $1";
sqlx::query_as(query).bind(user).fetch_one(conn).await sqlx::query_as(query).bind(user).fetch_one(conn).await
} }
pub async fn select_user(conn: &Pool<Sqlite>, id: i32) -> Result<User, sqlx::Error> { pub async fn select_user(conn: &Pool<Sqlite>, id: i32) -> Result<User, sqlx::Error> {
let query = "SELECT id, mail, username, role_id, channel_id FROM user WHERE id = $1"; let query = "SELECT id, mail, username, role_id, channel_ids FROM user WHERE id = $1";
sqlx::query_as(query).bind(id).fetch_one(conn).await sqlx::query_as(query).bind(id).fetch_one(conn).await
} }
@ -338,13 +338,20 @@ pub async fn insert_user(
.await .await
.unwrap(); .unwrap();
let query = "INSERT INTO user (mail, username, password, role_id) VALUES($1, $2, $3, $4)"; let query = "INSERT INTO user (mail, username, password, role_id, channel_ids) VALUES($1, $2, $3, $4, $5)";
sqlx::query(query) sqlx::query(query)
.bind(user.mail) .bind(user.mail)
.bind(user.username) .bind(user.username)
.bind(password_hash) .bind(password_hash)
.bind(user.role_id) .bind(user.role_id)
.bind(
user.channel_ids
.iter()
.map(|i| i.to_string())
.collect::<Vec<String>>()
.join(","),
)
.execute(conn) .execute(conn)
.await .await
} }

View File

@ -6,6 +6,7 @@ use serde::{
de::{self, Visitor}, de::{self, Visitor},
Deserialize, Serialize, Deserialize, Serialize,
}; };
use serde_with::{formats::CommaSeparator, serde_as, StringWithSeparator};
use sqlx::{sqlite::SqliteRow, FromRow, Pool, Row, Sqlite}; use sqlx::{sqlite::SqliteRow, FromRow, Pool, Row, Sqlite};
use crate::db::handles; use crate::db::handles;
@ -52,29 +53,44 @@ pub async fn init_globales(conn: &Pool<Sqlite>) {
INSTANCE.set(config).unwrap(); INSTANCE.set(config).unwrap();
} }
#[derive(Debug, Deserialize, Serialize, sqlx::FromRow)] #[serde_as]
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct User { pub struct User {
#[sqlx(default)]
#[serde(skip_deserializing)] #[serde(skip_deserializing)]
pub id: i32, pub id: i32,
#[sqlx(default)]
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub mail: Option<String>, pub mail: Option<String>,
pub username: String, pub username: String,
#[sqlx(default)]
#[serde(skip_serializing, default = "empty_string")] #[serde(skip_serializing, default = "empty_string")]
pub password: String, pub password: String,
#[sqlx(default)]
#[serde(skip_serializing)] #[serde(skip_serializing)]
pub role_id: Option<i32>, pub role_id: Option<i32>,
#[sqlx(default)]
#[serde(skip_serializing)] #[serde(skip_serializing)]
pub channel_id: Option<i32>, #[serde_as(as = "StringWithSeparator::<CommaSeparator, i32>")]
#[sqlx(default)] pub channel_ids: Vec<i32>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub token: Option<String>, pub token: Option<String>,
} }
impl FromRow<'_, SqliteRow> for User {
fn from_row(row: &SqliteRow) -> sqlx::Result<Self> {
Ok(Self {
id: row.try_get("id").unwrap_or_default(),
mail: row.get("mail"),
username: row.try_get("username").unwrap_or_default(),
password: row.try_get("password").unwrap_or_default(),
role_id: row.get("role_id"),
channel_ids: row
.try_get::<String, &str>("channel_ids")
.unwrap_or_default()
.split(',')
.map(|i| i.parse::<i32>().unwrap_or_default())
.collect(),
token: row.get("token"),
})
}
}
fn empty_string() -> String { fn empty_string() -> String {
"".to_string() "".to_string()
} }
@ -82,12 +98,12 @@ fn empty_string() -> String {
#[derive(Debug, Deserialize, Serialize, Clone)] #[derive(Debug, Deserialize, Serialize, Clone)]
pub struct UserMeta { pub struct UserMeta {
pub id: i32, pub id: i32,
pub channel: i32, pub channels: Vec<i32>,
} }
impl UserMeta { impl UserMeta {
pub fn new(id: i32, channel: i32) -> Self { pub fn new(id: i32, channels: Vec<i32>) -> Self {
Self { id, channel } Self { id, channels }
} }
} }

View File

@ -59,7 +59,7 @@ async fn validator(
req.attach(vec![claims.role]); req.attach(vec![claims.role]);
req.extensions_mut() req.extensions_mut()
.insert(UserMeta::new(claims.id, claims.channel)); .insert(UserMeta::new(claims.id, claims.channels));
Ok(req) Ok(req)
} }

View File

@ -250,7 +250,7 @@ pub async fn run_args(pool: &Pool<Sqlite>) -> Result<(), i32> {
username: username.clone(), username: username.clone(),
password: args.password.unwrap(), password: args.password.unwrap(),
role_id: Some(1), role_id: Some(1),
channel_id: Some(1), channel_ids: vec![1],
token: None, token: None,
}; };

View File

@ -57,9 +57,8 @@ CREATE TABLE
username TEXT NOT NULL, username TEXT NOT NULL,
password TEXT NOT NULL, password TEXT NOT NULL,
role_id INTEGER NOT NULL DEFAULT 3, role_id INTEGER NOT NULL DEFAULT 3,
channel_id INTEGER NOT NULL DEFAULT 1, channel_ids TEXT NOT NULL DEFAULT "1",
FOREIGN KEY (role_id) REFERENCES roles (id) ON UPDATE SET NULL ON DELETE SET DEFAULT, FOREIGN KEY (role_id) REFERENCES roles (id) ON UPDATE SET NULL ON DELETE SET DEFAULT,
FOREIGN KEY (channel_id) REFERENCES channels (id) ON UPDATE CASCADE ON DELETE SET DEFAULT,
UNIQUE (mail, username) UNIQUE (mail, username)
); );