auth options
This commit is contained in:
parent
98048ce522
commit
94ab6e050e
2 changed files with 105 additions and 17 deletions
|
@ -4,6 +4,8 @@ use serde::{Deserialize, Serialize};
|
||||||
use sqlx::FromRow;
|
use sqlx::FromRow;
|
||||||
|
|
||||||
mod user;
|
mod user;
|
||||||
|
pub use user::AdminUser;
|
||||||
|
pub use user::MaybeUser;
|
||||||
pub use user::User;
|
pub use user::User;
|
||||||
pub use user::UserRole;
|
pub use user::UserRole;
|
||||||
|
|
||||||
|
|
120
src/auth/user.rs
120
src/auth/user.rs
|
@ -6,6 +6,18 @@ use sqlx::FromRow;
|
||||||
use super::{Session, gen_token};
|
use super::{Session, gen_token};
|
||||||
use crate::{get_pg, request::api::ToAPI};
|
use crate::{get_pg, request::api::ToAPI};
|
||||||
|
|
||||||
|
/// User
|
||||||
|
///
|
||||||
|
/// # Example:
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
///
|
||||||
|
/// // Needs login
|
||||||
|
/// #[get("/myaccount")]
|
||||||
|
/// pub async fn account_page(ctx: RequestContext, user: User) -> StringResponse {
|
||||||
|
/// ...
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
|
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
/// The username chosen by the user
|
/// The username chosen by the user
|
||||||
|
@ -139,28 +151,102 @@ impl ToAPI for User {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn extract_user<'r>(request: &'r Request<'_>) -> Option<User> {
|
||||||
|
if let Some(session_id) = request.cookies().get("session_id") {
|
||||||
|
if let Some(user) = User::from_session(session_id.value()).await {
|
||||||
|
return Some(user);
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match request.headers().get_one("token") {
|
||||||
|
Some(key) => {
|
||||||
|
if let Some(user) = User::from_session(key).await {
|
||||||
|
return Some(user);
|
||||||
|
} else {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[rocket::async_trait]
|
#[rocket::async_trait]
|
||||||
impl<'r> FromRequest<'r> for User {
|
impl<'r> FromRequest<'r> for User {
|
||||||
type Error = ();
|
type Error = ();
|
||||||
|
|
||||||
async fn from_request(request: &'r Request<'_>) -> rocket::request::Outcome<Self, Self::Error> {
|
async fn from_request(request: &'r Request<'_>) -> rocket::request::Outcome<Self, Self::Error> {
|
||||||
if let Some(session_id) = request.cookies().get("session_id") {
|
if let Some(user) = extract_user(request).await {
|
||||||
if let Some(user) = User::from_session(session_id.value()).await {
|
return Outcome::Success(user);
|
||||||
return Outcome::Success(user);
|
} else {
|
||||||
} else {
|
return Outcome::Error((Status::Unauthorized, ()));
|
||||||
return Outcome::Error((Status::Unauthorized, ()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match request.headers().get_one("token") {
|
|
||||||
Some(key) => {
|
|
||||||
if let Some(user) = User::from_session(key).await {
|
|
||||||
Outcome::Success(user)
|
|
||||||
} else {
|
|
||||||
Outcome::Error((Status::Unauthorized, ()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => Outcome::Error((Status::Unauthorized, ())),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Maybe User?
|
||||||
|
///
|
||||||
|
/// This struct extracts a user if possible, but also allows anybody.
|
||||||
|
///
|
||||||
|
/// # Example:
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
///
|
||||||
|
/// // Publicly accessable
|
||||||
|
/// #[get("/")]
|
||||||
|
/// pub async fn index(ctx: RequestContext, user: MaybeUser) -> StringResponse {
|
||||||
|
/// match user {
|
||||||
|
/// MaybeUser::User(user) => println!("You are {}", user.username),
|
||||||
|
/// MaybeUser::Anonymous => println!("Who are you?")
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub enum MaybeUser {
|
||||||
|
User(User),
|
||||||
|
Anonymous,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rocket::async_trait]
|
||||||
|
impl<'r> FromRequest<'r> for MaybeUser {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
async fn from_request(request: &'r Request<'_>) -> rocket::request::Outcome<Self, Self::Error> {
|
||||||
|
if let Some(user) = extract_user(request).await {
|
||||||
|
return Outcome::Success(MaybeUser::User(user));
|
||||||
|
} else {
|
||||||
|
return Outcome::Success(MaybeUser::Anonymous);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Admin User
|
||||||
|
///
|
||||||
|
/// This struct expects an Admin User and returns `Forbidden` otherwise.
|
||||||
|
///
|
||||||
|
/// # Example:
|
||||||
|
///
|
||||||
|
/// ```ignore
|
||||||
|
///
|
||||||
|
/// // Only admin users can access this route
|
||||||
|
/// #[get("/admin")]
|
||||||
|
/// pub async fn admin_panel(ctx: RequestContext, user: AdminUser) -> StringResponse {
|
||||||
|
/// ...
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub struct AdminUser(User);
|
||||||
|
|
||||||
|
#[rocket::async_trait]
|
||||||
|
impl<'r> FromRequest<'r> for AdminUser {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
async fn from_request(request: &'r Request<'_>) -> rocket::request::Outcome<Self, Self::Error> {
|
||||||
|
if let Some(user) = extract_user(request).await {
|
||||||
|
if user.is_admin() {
|
||||||
|
return Outcome::Success(AdminUser(user));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
Outcome::Error((Status::Unauthorized, ()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue