Merge branch '29-implement-admin-user-permission-flag' into 'main'

Implement admin user permission flag

Closes #29

See merge request libretunes/libretunes!28
This commit is contained in:
Ethan Girouard 2024-05-19 12:43:51 -04:00
commit 4513335784
6 changed files with 42 additions and 1 deletions

View File

@ -0,0 +1 @@
ALTER TABLE users DROP COLUMN admin;

View File

@ -0,0 +1 @@
ALTER TABLE users ADD COLUMN admin BOOLEAN DEFAULT FALSE NOT NULL;

View File

@ -21,9 +21,10 @@ use crate::users::UserCredentials;
pub async fn signup(new_user: User) -> Result<(), ServerFnError> { pub async fn signup(new_user: User) -> Result<(), ServerFnError> {
use crate::users::create_user; use crate::users::create_user;
// Ensure the user has no id // Ensure the user has no id, and is not a self-proclaimed admin
let new_user = User { let new_user = User {
id: None, id: None,
admin: false,
..new_user ..new_user
}; };
@ -120,3 +121,37 @@ pub async fn require_auth() -> Result<(), ServerFnError> {
} }
}) })
} }
/// Check if a user is an admin
/// Returns a Result with a boolean indicating if the user is logged in and an admin
#[server(endpoint = "check_admin")]
pub async fn check_admin() -> Result<bool, ServerFnError> {
let auth_session = extract::<AuthSession<AuthBackend>>().await
.map_err(|e| ServerFnError::<NoCustomError>::ServerError(format!("Error getting auth session: {}", e)))?;
Ok(auth_session.user.as_ref().map(|u| u.admin).unwrap_or(false))
}
/// Require that a user is logged in and an admin
/// Returns a Result with the error message if the user is not logged in or is not an admin
/// Intended to be used at the start of a protected route, to ensure the user is logged in and an admin:
/// ```rust
/// use leptos::*;
/// use libretunes::auth::require_admin;
/// #[server(endpoint = "protected_admin_route")]
/// pub async fn protected_admin_route() -> Result<(), ServerFnError> {
/// require_admin().await?;
/// // Continue with protected route
/// Ok(())
/// }
/// ```
#[cfg(feature = "ssr")]
pub async fn require_admin() -> Result<(), ServerFnError> {
check_admin().await.and_then(|is_admin| {
if is_admin {
Ok(())
} else {
Err(ServerFnError::<NoCustomError>::ServerError(format!("Unauthorized")))
}
})
}

View File

@ -41,6 +41,8 @@ pub struct User {
/// The time the user was created /// The time the user was created
#[cfg_attr(feature = "ssr", diesel(deserialize_as = SystemTime))] #[cfg_attr(feature = "ssr", diesel(deserialize_as = SystemTime))]
pub created_at: Option<SystemTime>, pub created_at: Option<SystemTime>,
/// Whether the user is an admin
pub admin: bool,
} }
/// Model for an artist /// Model for an artist

View File

@ -25,6 +25,7 @@ pub fn Signup() -> impl IntoView {
email: email.get(), email: email.get(),
password: Some(password.get()), password: Some(password.get()),
created_at: None, created_at: None,
admin: false,
}; };
log!("new user: {:?}", new_user); log!("new user: {:?}", new_user);

View File

@ -50,6 +50,7 @@ diesel::table! {
email -> Varchar, email -> Varchar,
password -> Varchar, password -> Varchar,
created_at -> Timestamp, created_at -> Timestamp,
admin -> Bool,
} }
} }