switch to macro for special user
Some checks failed
CI/CD Pipeline / test (push) Failing after 15m6s
CI/CD Pipeline / deploy-staging (push) Has been skipped
CI/CD Pipeline / deploy-main (push) Has been skipped

This commit is contained in:
2024-08-19 10:34:37 +02:00
parent c41dc0853a
commit 799e94a50f
13 changed files with 92 additions and 287 deletions

View File

@ -918,266 +918,85 @@ impl<'r> FromRequest<'r> for User {
}
}
pub struct TechUser {
pub(crate) user: User,
}
/// Creates a struct named $name. Allows to be created from a user, if one of the specified $roles are active for the user.
macro_rules! special_user {
($name:ident, $($role:tt)*) => {
#[derive(Debug)]
pub struct $name {
pub(crate) user: User,
}
impl Deref for TechUser {
type Target = User;
impl Deref for $name {
type Target = User;
fn deref(&self) -> &Self::Target {
&self.user
}
}
fn deref(&self) -> &Self::Target {
&self.user
}
}
impl $name {
pub fn into_inner(self) -> User {
self.user
}
}
#[async_trait]
impl<'r> FromRequest<'r> for TechUser {
type Error = LoginError;
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
let db = req.rocket().state::<SqlitePool>().unwrap();
match User::from_request(req).await {
Outcome::Success(user) => {
if user.has_role(db, "tech").await {
Outcome::Success(TechUser { user })
} else {
Outcome::Error((Status::Forbidden, LoginError::NotACox))
#[async_trait]
impl<'r> FromRequest<'r> for $name {
type Error = LoginError;
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
let db = req.rocket().state::<SqlitePool>().unwrap();
match User::from_request(req).await {
Outcome::Success(user) => {
if special_user!(@check_roles user, db, $($role)*) {
Outcome::Success($name { user })
} else {
Outcome::Forward(Status::Forbidden)
}
}
Outcome::Error(f) => Outcome::Error(f),
Outcome::Forward(f) => Outcome::Forward(f),
}
}
Outcome::Error(f) => Outcome::Error(f),
Outcome::Forward(f) => Outcome::Forward(f),
}
}
}
pub struct CoxUser {
pub(crate) user: User,
}
impl Deref for CoxUser {
type Target = User;
fn deref(&self) -> &Self::Target {
&self.user
}
}
impl CoxUser {
pub async fn new(db: &SqlitePool, user: User) -> Option<Self> {
if user.has_role(db, "cox").await {
Some(CoxUser { user })
} else {
None
}
}
}
#[async_trait]
impl<'r> FromRequest<'r> for CoxUser {
type Error = LoginError;
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
let db = req.rocket().state::<SqlitePool>().unwrap();
match User::from_request(req).await {
Outcome::Success(user) => {
if user.has_role(db, "cox").await {
Outcome::Success(CoxUser { user })
impl $name {
pub async fn new(db: &SqlitePool, user: User) -> Option<Self> {
if special_user!(@check_roles user, db, $($role)*) {
Some($name { user })
} else {
Outcome::Error((Status::Forbidden, LoginError::NotACox))
None
}
}
Outcome::Error(f) => Outcome::Error(f),
Outcome::Forward(f) => Outcome::Forward(f),
}
}
}
};
#[derive(Debug, Serialize, Deserialize)]
pub struct AdminUser {
pub(crate) user: User,
}
#[async_trait]
impl<'r> FromRequest<'r> for AdminUser {
type Error = LoginError;
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
let db = req.rocket().state::<SqlitePool>().unwrap();
match User::from_request(req).await {
Outcome::Success(user) => {
if user.has_role(db, "admin").await {
Outcome::Success(AdminUser { user })
} else {
Outcome::Forward(Status::Forbidden)
}
(@check_roles $user:ident, $db:ident, $(+$role:expr),* $(,-$neg_role:expr)*) => {
{
$(
if !$user.has_role($db, $role).await {
false
} else
)*
$(
if $user.has_role($db, $neg_role).await {
false
} else
)*
{
true
}
Outcome::Error(f) => Outcome::Error(f),
Outcome::Forward(f) => Outcome::Forward(f),
}
}
};
}
#[derive(Debug, Serialize, Deserialize)]
pub struct AllowedForPlannedTripsUser(pub(crate) User);
special_user!(TechUser, +"tech");
special_user!(CoxUser, +"cox");
special_user!(AdminUser, +"admin");
special_user!(AllowedForPlannedTripsUser, +"Donau Linz", +"scheckbuch");
special_user!(DonauLinzUser, +"Donau Linz", -"Unterstützend", -"Förderndes Mitglied");
special_user!(SchnupperBetreuerUser, +"schnupper-betreuer");
special_user!(VorstandUser, +"Vorstand");
special_user!(EventUser, +"manage_events");
#[async_trait]
impl<'r> FromRequest<'r> for AllowedForPlannedTripsUser {
type Error = LoginError;
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
let db = req.rocket().state::<SqlitePool>().unwrap();
match User::from_request(req).await {
Outcome::Success(user) => {
if user.has_role(db, "Donau Linz").await | user.has_role(db, "scheckbuch").await {
Outcome::Success(AllowedForPlannedTripsUser(user))
} else {
Outcome::Error((Status::Forbidden, LoginError::NotACox))
}
}
Outcome::Error(f) => Outcome::Error(f),
Outcome::Forward(f) => Outcome::Forward(f),
}
}
}
impl From<AllowedForPlannedTripsUser> for User {
fn from(val: AllowedForPlannedTripsUser) -> Self {
val.0
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct DonauLinzUser(pub(crate) User);
impl From<DonauLinzUser> for User {
fn from(val: DonauLinzUser) -> Self {
val.0
}
}
impl Deref for DonauLinzUser {
type Target = User;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[async_trait]
impl<'r> FromRequest<'r> for DonauLinzUser {
type Error = LoginError;
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
let db = req.rocket().state::<SqlitePool>().unwrap();
match User::from_request(req).await {
Outcome::Success(user) => {
if user.has_role(db, "Donau Linz").await
&& !user.has_role(db, "Unterstützend").await
&& !user.has_role(db, "Förderndes Mitglied").await
{
Outcome::Success(DonauLinzUser(user))
} else {
Outcome::Error((Status::Forbidden, LoginError::NotACox))
}
}
Outcome::Error(f) => Outcome::Error(f),
Outcome::Forward(f) => Outcome::Forward(f),
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct SchnupperBetreuerUser(pub(crate) User);
impl From<SchnupperBetreuerUser> for User {
fn from(val: SchnupperBetreuerUser) -> Self {
val.0
}
}
impl Deref for SchnupperBetreuerUser {
type Target = User;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[async_trait]
impl<'r> FromRequest<'r> for SchnupperBetreuerUser {
type Error = LoginError;
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
let db = req.rocket().state::<SqlitePool>().unwrap();
match User::from_request(req).await {
Outcome::Success(user) => {
if user.has_role(db, "schnupper-betreuer").await {
Outcome::Success(SchnupperBetreuerUser(user))
} else {
Outcome::Forward(Status::Forbidden)
}
}
Outcome::Error(f) => Outcome::Error(f),
Outcome::Forward(f) => Outcome::Forward(f),
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct VorstandUser(pub(crate) User);
impl From<VorstandUser> for User {
fn from(val: VorstandUser) -> Self {
val.0
}
}
impl Deref for VorstandUser {
type Target = User;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[async_trait]
impl<'r> FromRequest<'r> for VorstandUser {
type Error = LoginError;
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
let db = req.rocket().state::<SqlitePool>().unwrap();
match User::from_request(req).await {
Outcome::Success(user) => {
if user.has_role(db, "Vorstand").await {
Outcome::Success(VorstandUser(user))
} else {
Outcome::Forward(Status::Forbidden)
}
}
Outcome::Error(f) => Outcome::Error(f),
Outcome::Forward(f) => Outcome::Forward(f),
}
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct EventUser(pub(crate) User);
impl From<EventUser> for User {
fn from(val: EventUser) -> Self {
val.0
}
}
impl Deref for EventUser {
type Target = User;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(FromRow, Serialize, Deserialize, Clone, Debug)]
pub struct UserWithRolesAndMembershipPdf {
#[serde(flatten)]
@ -1221,25 +1040,6 @@ impl UserWithMembershipPdf {
}
}
#[async_trait]
impl<'r> FromRequest<'r> for EventUser {
type Error = LoginError;
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
let db = req.rocket().state::<SqlitePool>().unwrap();
match User::from_request(req).await {
Outcome::Success(user) => {
if user.has_role(db, "manage_events").await {
Outcome::Success(EventUser(user))
} else {
Outcome::Error((Status::Forbidden, LoginError::NotACox))
}
}
Outcome::Error(f) => Outcome::Error(f),
Outcome::Forward(f) => Outcome::Forward(f),
}
}
}
#[cfg(test)]
mod test {
use std::collections::HashMap;