aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2023-10-01 00:38:29 +0200
committermetamuffin <metamuffin@disroot.org>2023-10-01 00:38:29 +0200
commitfc5e13ae525cb74e77a5bc51204f44476115cea9 (patch)
treea20b6d296d67735a2c8d42a0dc31b44c0bb53cb7
parentd546caa3f5053ade763430490911fefd6257af9f (diff)
downloadjellything-fc5e13ae525cb74e77a5bc51204f44476115cea9.tar
jellything-fc5e13ae525cb74e77a5bc51204f44476115cea9.tar.bz2
jellything-fc5e13ae525cb74e77a5bc51204f44476115cea9.tar.zst
draft for permission framework
-rw-r--r--Cargo.lock1
-rw-r--r--base/Cargo.toml3
-rw-r--r--base/src/lib.rs4
-rw-r--r--base/src/permission.rs27
-rw-r--r--common/src/config.rs3
-rw-r--r--common/src/lib.rs3
-rw-r--r--common/src/user.rs30
-rw-r--r--server/src/database.rs15
-rw-r--r--server/src/routes/ui/account/mod.rs6
-rw-r--r--server/src/routes/ui/account/session/mod.rs3
-rw-r--r--server/src/routes/ui/account/session/token.rs17
11 files changed, 93 insertions, 19 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 11a476d..5147a18 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -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();
}