From b946e1ce985b641dd142b087882b1edc9f59db11 Mon Sep 17 00:00:00 2001 From: Jef Roosens Date: Fri, 29 Aug 2025 14:02:26 +0200 Subject: [PATCH] feat(otter): cli command to toggle admin status --- CHANGELOG.md | 7 +++++++ gpodder/src/repository/mod.rs | 4 ++++ gpodder/src/store.rs | 3 +++ gpodder_sqlite/src/models/user.rs | 2 +- gpodder_sqlite/src/repository/auth.rs | 23 +++++++++++++++++++++++ otter/src/cli/gpo.rs | 13 ++++++++++++- 6 files changed, 50 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 821d729..179b144 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased](https://git.rustybever.be/Chewing_Bever/otter) +### Added + +* Ability for an account to be an admin +* CLI command to toggle admin status of users + ## [0.3.0](https://git.rustybever.be/Chewing_Bever/otter/src/tag/0.3.0) +### Added + * Public sign-up page (disabled by default) * Private sign-up links * New CLI commands diff --git a/gpodder/src/repository/mod.rs b/gpodder/src/repository/mod.rs index fe7eeec..1f93f9a 100644 --- a/gpodder/src/repository/mod.rs +++ b/gpodder/src/repository/mod.rs @@ -83,6 +83,10 @@ impl GpodderRepository { self.store.insert_user(username, &password_hash) } + pub fn update_user(&self, user: models::User) -> Result { + self.store.update_user(user) + } + pub fn validate_credentials( &self, username: &str, diff --git a/gpodder/src/store.rs b/gpodder/src/store.rs index 598fb41..dea0449 100644 --- a/gpodder/src/store.rs +++ b/gpodder/src/store.rs @@ -51,6 +51,9 @@ pub trait GpodderAuthStore { /// Insert a new user into the data store fn insert_user(&self, username: &str, password_hash: &str) -> Result; + /// Update the user with the included ID with the new values + fn update_user(&self, user: User) -> Result; + /// Create a new session for a user with the given session ID /// /// The `last_seen` timestamp's precision should be at least accurate to the second diff --git a/gpodder_sqlite/src/models/user.rs b/gpodder_sqlite/src/models/user.rs index d0222ec..e97b5ce 100644 --- a/gpodder_sqlite/src/models/user.rs +++ b/gpodder_sqlite/src/models/user.rs @@ -2,7 +2,7 @@ use diesel::prelude::*; use crate::schema::*; -#[derive(Clone, Queryable, Selectable)] +#[derive(Clone, Queryable, Selectable, AsChangeset)] #[diesel(table_name = users)] #[diesel(check_for_backend(diesel::sqlite::Sqlite))] pub struct User { diff --git a/gpodder_sqlite/src/repository/auth.rs b/gpodder_sqlite/src/repository/auth.rs index 1988a4c..67a1314 100644 --- a/gpodder_sqlite/src/repository/auth.rs +++ b/gpodder_sqlite/src/repository/auth.rs @@ -24,6 +24,17 @@ impl From for gpodder::User { } } +impl From for User { + fn from(value: gpodder::User) -> Self { + Self { + id: value.id, + username: value.username, + password_hash: value.password_hash, + admin: value.admin, + } + } +} + impl From for gpodder::SignupLink { fn from(value: SignupLink) -> Self { Self { @@ -58,6 +69,18 @@ impl gpodder::GpodderAuthStore for SqliteRepository { .map_err(DbError::from)?) } + fn update_user(&self, user: gpodder::User) -> Result { + let conn = &mut self.pool.get().map_err(DbError::from)?; + let user: User = user.into(); + + Ok(diesel::update(users::table.filter(users::id.eq(user.id))) + .set(&user) + .returning(User::as_returning()) + .get_result(conn) + .map(gpodder::User::from) + .map_err(DbError::from)?) + } + fn get_session(&self, session_id: i64) -> Result, AuthErr> { match sessions::table .inner_join(users::table) diff --git a/otter/src/cli/gpo.rs b/otter/src/cli/gpo.rs index aaa01b5..188ed41 100644 --- a/otter/src/cli/gpo.rs +++ b/otter/src/cli/gpo.rs @@ -1,4 +1,4 @@ -use clap::{Args, Subcommand}; +use clap::{ArgAction, Args, Subcommand}; use super::CliError; @@ -22,6 +22,12 @@ pub enum UserCommand { Add { username: String, password: String }, /// Generate a signup link ID GenerateSignupLink, + /// Give or remove admin privileges to a user + SetAdmin { + username: String, + #[clap(action=ArgAction::Set)] + is_admin: bool, + }, } impl Command { @@ -67,6 +73,11 @@ impl UserCommand { println!("/signup/{}", link.id); } + Self::SetAdmin { username, is_admin } => { + let mut user = store.get_user(username)?; + user.admin = *is_admin; + store.update_user(user)?; + } } Ok(())