diff options
author | metamuffin <metamuffin@disroot.org> | 2023-10-01 00:38:29 +0200 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2023-10-01 00:38:29 +0200 |
commit | fc5e13ae525cb74e77a5bc51204f44476115cea9 (patch) | |
tree | a20b6d296d67735a2c8d42a0dc31b44c0bb53cb7 | |
parent | d546caa3f5053ade763430490911fefd6257af9f (diff) | |
download | jellything-fc5e13ae525cb74e77a5bc51204f44476115cea9.tar jellything-fc5e13ae525cb74e77a5bc51204f44476115cea9.tar.bz2 jellything-fc5e13ae525cb74e77a5bc51204f44476115cea9.tar.zst |
draft for permission framework
-rw-r--r-- | Cargo.lock | 1 | ||||
-rw-r--r-- | base/Cargo.toml | 3 | ||||
-rw-r--r-- | base/src/lib.rs | 4 | ||||
-rw-r--r-- | base/src/permission.rs | 27 | ||||
-rw-r--r-- | common/src/config.rs | 3 | ||||
-rw-r--r-- | common/src/lib.rs | 3 | ||||
-rw-r--r-- | common/src/user.rs | 30 | ||||
-rw-r--r-- | server/src/database.rs | 15 | ||||
-rw-r--r-- | server/src/routes/ui/account/mod.rs | 6 | ||||
-rw-r--r-- | server/src/routes/ui/account/session/mod.rs | 3 | ||||
-rw-r--r-- | server/src/routes/ui/account/session/token.rs | 17 |
11 files changed, 93 insertions, 19 deletions
@@ -1391,6 +1391,7 @@ dependencies = [ "base64", "jellycommon", "log", + "serde", "serde_json", "sha2", "tokio", diff --git a/base/Cargo.toml b/base/Cargo.toml index f49db80..3081262 100644 --- a/base/Cargo.toml +++ b/base/Cargo.toml @@ -5,9 +5,10 @@ edition = "2021" [dependencies] jellycommon = { path = "../common" } +serde = { version = "1.0.188", features = ["derive"] } serde_json = "1.0.107" log = { workspace = true } sha2 = "0.10.8" base64 = "0.21.4" -tokio = {workspace=true} +tokio = { workspace = true } anyhow = "1.0.75" diff --git a/base/src/lib.rs b/base/src/lib.rs index b97f924..5a98be5 100644 --- a/base/src/lib.rs +++ b/base/src/lib.rs @@ -4,8 +4,10 @@ Copyright (C) 2023 metamuffin <metamuffin.org> */ #![feature(lazy_cell)] +pub mod permission; + use base64::Engine; -use jellycommon::{config::GlobalConfig, AssetLocation}; +use jellycommon::{AssetLocation, config::GlobalConfig}; use std::{fs::File, future::Future, path::PathBuf, sync::LazyLock}; use tokio::sync::Mutex; diff --git a/base/src/permission.rs b/base/src/permission.rs new file mode 100644 index 0000000..8993154 --- /dev/null +++ b/base/src/permission.rs @@ -0,0 +1,27 @@ +use crate::CONF; +use anyhow::anyhow; +use jellycommon::user::{PermissionSet, UserPermission}; + +pub trait PermissionSetExt { + fn check(&self, perm: UserPermission) -> bool; + fn assert(&self, perm: UserPermission) -> Result<(), anyhow::Error>; +} + +impl PermissionSetExt for PermissionSet { + fn check(&self, perm: UserPermission) -> bool { + *self + .0 + .get(&perm) + .or(CONF.default_permission_set.0.get(&perm)) + .unwrap_or(&perm.default_value()) + } + fn assert(&self, perm: UserPermission) -> Result<(), anyhow::Error> { + if self.check(perm) { + Ok(()) + } else { + Err(anyhow!( + "sorry, you need special permission {perm:?} for this action." + )) + } + } +} diff --git a/common/src/config.rs b/common/src/config.rs index da1cfb5..467dc02 100644 --- a/common/src/config.rs +++ b/common/src/config.rs @@ -7,6 +7,8 @@ use serde::{Deserialize, Serialize}; use std::{collections::HashMap, path::PathBuf}; +use crate::user::PermissionSet; + #[rustfmt::skip] #[derive(Debug, Deserialize, Serialize, Default)] pub struct GlobalConfig { @@ -22,6 +24,7 @@ pub struct GlobalConfig { #[serde(default)] pub session_key: Option<String>, #[serde(default = "default::login_expire")] pub login_expire: i64, #[serde(default)] pub remote_credentials: HashMap<String, (String, String, bool)>, + #[serde(default)] pub default_permission_set: PermissionSet, } #[rustfmt::skip] diff --git a/common/src/lib.rs b/common/src/lib.rs index ec6e0df..c842924 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -6,8 +6,9 @@ pub mod config; pub mod helpers; pub mod r#impl; -pub mod stream; pub mod seek_index; +pub mod stream; +pub mod user; #[cfg(feature = "rocket")] use rocket::{FromFormField, UriDisplayQuery}; diff --git a/common/src/user.rs b/common/src/user.rs new file mode 100644 index 0000000..543162c --- /dev/null +++ b/common/src/user.rs @@ -0,0 +1,30 @@ +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct User { + pub name: String, + pub display_name: String, + pub password: Vec<u8>, + pub admin: bool, + pub permissions: PermissionSet, +} + +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct PermissionSet(pub HashMap<UserPermission, bool>); + +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)] +#[serde(rename_all = "snake_case")] +pub enum UserPermission { + OriginalStream, + Transcode, + ManageUsers, + GenerateInvite, +} + +impl UserPermission { + pub fn default_value(&self) -> bool { + use UserPermission::*; + matches!(self, Transcode) + } +} diff --git a/server/src/database.rs b/server/src/database.rs index d5a435f..6c3b938 100644 --- a/server/src/database.rs +++ b/server/src/database.rs @@ -6,9 +6,11 @@ use crate::routes::ui::account::hash_password; use anyhow::Context; use jellybase::CONF; -use jellycommon::Node; +use jellycommon::{ + user::{PermissionSet, User}, + Node, +}; use log::info; -use serde::{Deserialize, Serialize}; use std::path::Path; use typed_sled::Tree; @@ -20,14 +22,6 @@ pub struct Database { pub node: Tree<String, Node>, } -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct User { - pub name: String, - pub display_name: String, - pub password: Vec<u8>, - pub admin: bool, -} - impl Database { pub fn open(path: &Path) -> Result<Self, anyhow::Error> { info!("opening database… (might take up to O(n) time)"); @@ -51,6 +45,7 @@ impl Database { display_name: "Admin".to_string(), name: CONF.admin_username.clone(), password: hash_password(&CONF.admin_username, &CONF.admin_password), + permissions: PermissionSet::default(), }, ) .unwrap(); diff --git a/server/src/routes/ui/account/mod.rs b/server/src/routes/ui/account/mod.rs index b7ba332..a4aa2dd 100644 --- a/server/src/routes/ui/account/mod.rs +++ b/server/src/routes/ui/account/mod.rs @@ -8,7 +8,7 @@ pub mod settings; use super::{error::MyError, layout::LayoutPage}; use crate::{ - database::{Database, User}, + database::Database, routes::ui::{error::MyResult, home::rocket_uri_macro_r_home, layout::DynLayoutPage}, uri, }; @@ -16,6 +16,7 @@ use anyhow::anyhow; use argon2::{password_hash::Salt, Argon2, PasswordHasher}; use chrono::Duration; use jellybase::CONF; +use jellycommon::user::{PermissionSet, User}; use rocket::{ form::{Contextual, Form}, get, @@ -131,6 +132,7 @@ pub fn r_account_register_post<'a>( name: form.username.clone(), password: hash_password(&form.username, &form.password), admin: false, + permissions: PermissionSet::default(), }), ) .unwrap() @@ -188,7 +190,7 @@ pub fn login_logic(database: &Database, username: &str, password: &str) -> MyRes } Ok(session::token::create( - user.name, + &user, Duration::days(CONF.login_expire), )) } diff --git a/server/src/routes/ui/account/session/mod.rs b/server/src/routes/ui/account/session/mod.rs index b13f157..0de15c4 100644 --- a/server/src/routes/ui/account/session/mod.rs +++ b/server/src/routes/ui/account/session/mod.rs @@ -3,8 +3,8 @@ which is licensed under the GNU Affero General Public License (version 3); see /COPYING. Copyright (C) 2023 metamuffin <metamuffin.org> */ -use crate::database::User; use chrono::{DateTime, Utc}; +use jellycommon::user::{PermissionSet, User}; use serde::{Deserialize, Serialize}; pub mod guard; @@ -20,4 +20,5 @@ pub struct AdminSession(pub Session); pub struct SessionData { username: String, expire: DateTime<Utc>, + permissions: PermissionSet, } diff --git a/server/src/routes/ui/account/session/token.rs b/server/src/routes/ui/account/session/token.rs index e5e4baf..baec665 100644 --- a/server/src/routes/ui/account/session/token.rs +++ b/server/src/routes/ui/account/session/token.rs @@ -12,6 +12,7 @@ use anyhow::anyhow; use base64::Engine; use chrono::{Duration, Utc}; use jellybase::CONF; +use jellycommon::user::User; use log::warn; use std::sync::LazyLock; @@ -28,10 +29,11 @@ static SESSION_KEY: LazyLock<[u8; 32]> = LazyLock::new(|| { } }); -pub fn create(username: String, expire: Duration) -> String { +pub fn create(user: &User, expire: Duration) -> String { let session_data = SessionData { expire: Utc::now() + expire, - username, + username: user.name.to_owned(), + permissions: user.permissions.clone(), }; let mut plaintext = bincode::serde::encode_to_vec(&session_data, bincode::config::standard()).unwrap(); @@ -70,7 +72,16 @@ pub fn validate(token: &str) -> anyhow::Result<String> { #[test] fn test() { - let tok = create("blub".to_string(), Duration::days(1)); + let tok = create( + &User { + name: "blub".to_string(), + display_name: "blub".to_owned(), + password: vec![], + admin: false, + permissions: jellycommon::user::PermissionSet::default(), + }, + Duration::days(1), + ); validate(&tok).unwrap(); } |