limit users to proper role, Fixes #135
This commit is contained in:
@ -264,6 +264,10 @@ ORDER BY departure DESC
|
||||
return Err(LogbookCreateError::BoatNotFound);
|
||||
};
|
||||
|
||||
if boat.amount_seats == 1 && log.rowers.is_empty() {
|
||||
log.rowers = vec![created_by_user.id];
|
||||
}
|
||||
|
||||
if boat.amount_seats == 1 {
|
||||
log.shipmaster = Some(log.rowers[0]);
|
||||
log.steering_person = Some(log.rowers[0]);
|
||||
|
@ -440,23 +440,20 @@ impl<'r> FromRequest<'r> for User {
|
||||
Ok(user_id) => {
|
||||
let db = req.rocket().state::<SqlitePool>().unwrap();
|
||||
let Some(user) = User::find_by_id(db, user_id).await else {
|
||||
return Outcome::Error((Status::Unauthorized, LoginError::UserNotFound));
|
||||
return Outcome::Error((Status::Forbidden, LoginError::UserNotFound));
|
||||
};
|
||||
if user.deleted {
|
||||
return Outcome::Error((Status::Unauthorized, LoginError::UserDeleted));
|
||||
return Outcome::Error((Status::Forbidden, LoginError::UserDeleted));
|
||||
}
|
||||
user.logged_in(db).await;
|
||||
|
||||
let mut cookie = Cookie::new("loggedin_user", format!("{}", user.id));
|
||||
cookie.set_expires(OffsetDateTime::now_utc() + Duration::weeks(12));
|
||||
cookie.set_expires(OffsetDateTime::now_utc() + Duration::weeks(2));
|
||||
req.cookies().add_private(cookie);
|
||||
|
||||
Outcome::Success(user)
|
||||
}
|
||||
Err(_) => {
|
||||
println!("{:?}", user_id.value());
|
||||
Outcome::Error((Status::Unauthorized, LoginError::DeserializationError))
|
||||
}
|
||||
Err(_) => Outcome::Error((Status::Unauthorized, LoginError::DeserializationError)),
|
||||
},
|
||||
None => Outcome::Error((Status::Unauthorized, LoginError::NotLoggedIn)),
|
||||
}
|
||||
@ -487,7 +484,7 @@ impl<'r> FromRequest<'r> for TechUser {
|
||||
if user.has_role(db, "tech").await {
|
||||
Outcome::Success(TechUser { user })
|
||||
} else {
|
||||
Outcome::Error((Status::Unauthorized, LoginError::NotACox))
|
||||
Outcome::Error((Status::Forbidden, LoginError::NotACox))
|
||||
}
|
||||
}
|
||||
Outcome::Error(f) => Outcome::Error(f),
|
||||
@ -530,7 +527,7 @@ impl<'r> FromRequest<'r> for CoxUser {
|
||||
if user.has_role(db, "cox").await {
|
||||
Outcome::Success(CoxUser { user })
|
||||
} else {
|
||||
Outcome::Error((Status::Unauthorized, LoginError::NotACox))
|
||||
Outcome::Error((Status::Forbidden, LoginError::NotACox))
|
||||
}
|
||||
}
|
||||
Outcome::Error(f) => Outcome::Error(f),
|
||||
@ -555,7 +552,7 @@ impl<'r> FromRequest<'r> for AdminUser {
|
||||
if user.has_role(db, "admin").await {
|
||||
Outcome::Success(AdminUser { user })
|
||||
} else {
|
||||
Outcome::Error((Status::Unauthorized, LoginError::NotACox))
|
||||
Outcome::Error((Status::Forbidden, LoginError::NotACox))
|
||||
}
|
||||
}
|
||||
Outcome::Error(f) => Outcome::Error(f),
|
||||
@ -565,22 +562,65 @@ impl<'r> FromRequest<'r> for AdminUser {
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct NonGuestUser {
|
||||
pub(crate) user: User,
|
||||
}
|
||||
pub struct AllowedForPlannedTripsUser(pub(crate) User);
|
||||
|
||||
#[async_trait]
|
||||
impl<'r> FromRequest<'r> for NonGuestUser {
|
||||
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, "scheckbuch").await {
|
||||
Outcome::Success(NonGuestUser { user })
|
||||
if user.has_role(db, "Donau Linz").await {
|
||||
Outcome::Success(AllowedForPlannedTripsUser(user))
|
||||
} else if user.has_role(db, "scheckbuch").await {
|
||||
Outcome::Success(AllowedForPlannedTripsUser(user))
|
||||
} else {
|
||||
Outcome::Error((Status::Unauthorized, LoginError::NotACox))
|
||||
Outcome::Error((Status::Forbidden, LoginError::NotACox))
|
||||
}
|
||||
}
|
||||
Outcome::Error(f) => Outcome::Error(f),
|
||||
Outcome::Forward(f) => Outcome::Forward(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<User> for AllowedForPlannedTripsUser {
|
||||
fn into(self) -> User {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct DonauLinzUser(pub(crate) User);
|
||||
|
||||
impl Into<User> for DonauLinzUser {
|
||||
fn into(self) -> User {
|
||||
self.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 {
|
||||
Outcome::Success(DonauLinzUser(user))
|
||||
} else {
|
||||
Outcome::Error((Status::Forbidden, LoginError::NotACox))
|
||||
}
|
||||
}
|
||||
Outcome::Error(f) => Outcome::Error(f),
|
||||
|
@ -13,7 +13,7 @@ use crate::{
|
||||
model::{
|
||||
boat::Boat,
|
||||
boatdamage::{BoatDamage, BoatDamageFixed, BoatDamageToAdd, BoatDamageVerified},
|
||||
user::{CoxUser, NonGuestUser, TechUser, User, UserWithRoles},
|
||||
user::{CoxUser, DonauLinzUser, TechUser, User, UserWithRoles},
|
||||
},
|
||||
tera::log::KioskCookie,
|
||||
};
|
||||
@ -45,7 +45,7 @@ async fn index_kiosk(
|
||||
async fn index(
|
||||
db: &State<SqlitePool>,
|
||||
flash: Option<FlashMessage<'_>>,
|
||||
user: NonGuestUser,
|
||||
user: DonauLinzUser,
|
||||
) -> Template {
|
||||
let boatdamages = BoatDamage::all(db).await;
|
||||
let boats = Boat::all(db).await;
|
||||
@ -59,7 +59,7 @@ async fn index(
|
||||
context.insert("boats", &boats);
|
||||
context.insert(
|
||||
"loggedin_user",
|
||||
&UserWithRoles::from_user(user.user, db).await,
|
||||
&UserWithRoles::from_user(user.into(), db).await,
|
||||
);
|
||||
|
||||
Template::render("boatdamages", context.into_json())
|
||||
@ -76,13 +76,14 @@ pub struct FormBoatDamageToAdd<'r> {
|
||||
async fn create<'r>(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<FormBoatDamageToAdd<'r>>,
|
||||
user: NonGuestUser,
|
||||
user: DonauLinzUser,
|
||||
) -> Flash<Redirect> {
|
||||
let user: User = user.into();
|
||||
let boatdamage_to_add = BoatDamageToAdd {
|
||||
boat_id: data.boat_id,
|
||||
desc: data.desc,
|
||||
lock_boat: data.lock_boat,
|
||||
user_id_created: user.user.id as i32,
|
||||
user_id_created: user.id as i32,
|
||||
};
|
||||
match BoatDamage::create(db, boatdamage_to_add).await {
|
||||
Ok(_) => Flash::success(
|
||||
|
@ -391,7 +391,7 @@ mod test {
|
||||
.body("name=cox&password=cox"); // Add the form data to the request body;
|
||||
login.dispatch().await;
|
||||
|
||||
let req = client.get("/join/1");
|
||||
let req = client.get("/planned/join/1");
|
||||
let _ = req.dispatch().await;
|
||||
|
||||
let req = client.get("/cox/join/1");
|
||||
|
@ -23,7 +23,7 @@ use crate::model::{
|
||||
LogbookUpdateError,
|
||||
},
|
||||
logtype::LogType,
|
||||
user::{NonGuestUser, User, UserWithRoles, UserWithWaterStatus},
|
||||
user::{DonauLinzUser, User, UserWithRoles, UserWithWaterStatus},
|
||||
};
|
||||
|
||||
pub struct KioskCookie(String);
|
||||
@ -44,9 +44,9 @@ impl<'r> FromRequest<'r> for KioskCookie {
|
||||
async fn index(
|
||||
db: &State<SqlitePool>,
|
||||
flash: Option<FlashMessage<'_>>,
|
||||
user: NonGuestUser,
|
||||
user: DonauLinzUser,
|
||||
) -> Template {
|
||||
let boats = Boat::for_user(db, &user.user).await;
|
||||
let boats = Boat::for_user(db, &user).await;
|
||||
|
||||
let coxes: Vec<UserWithWaterStatus> = futures::future::join_all(
|
||||
User::cox(db)
|
||||
@ -78,7 +78,7 @@ async fn index(
|
||||
context.insert("logtypes", &logtypes);
|
||||
context.insert(
|
||||
"loggedin_user",
|
||||
&UserWithRoles::from_user(user.user, db).await,
|
||||
&UserWithRoles::from_user(user.into(), db).await,
|
||||
);
|
||||
context.insert("on_water", &on_water);
|
||||
context.insert("distances", &distances);
|
||||
@ -87,12 +87,12 @@ async fn index(
|
||||
}
|
||||
|
||||
#[get("/show", rank = 2)]
|
||||
async fn show(db: &State<SqlitePool>, user: NonGuestUser) -> Template {
|
||||
async fn show(db: &State<SqlitePool>, user: DonauLinzUser) -> Template {
|
||||
let logs = Logbook::completed(db).await;
|
||||
|
||||
Template::render(
|
||||
"log.completed",
|
||||
context!(logs, loggedin_user: &UserWithRoles::from_user(user.user, db).await),
|
||||
context!(logs, loggedin_user: &UserWithRoles::from_user(user.into(), db).await),
|
||||
)
|
||||
}
|
||||
|
||||
@ -166,12 +166,12 @@ async fn kiosk(
|
||||
async fn create_logbook(
|
||||
db: &SqlitePool,
|
||||
data: Form<LogToAdd>,
|
||||
user: &NonGuestUser,
|
||||
user: &DonauLinzUser,
|
||||
) -> Flash<Redirect> {
|
||||
match Logbook::create(
|
||||
db,
|
||||
data.into_inner(),
|
||||
&user.user
|
||||
&user
|
||||
)
|
||||
.await
|
||||
{
|
||||
@ -197,14 +197,11 @@ async fn create_logbook(
|
||||
async fn create(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<LogToAdd>,
|
||||
user: NonGuestUser,
|
||||
user: DonauLinzUser,
|
||||
) -> Flash<Redirect> {
|
||||
Log::create(
|
||||
db,
|
||||
format!(
|
||||
"User {} tries to create log entry={:?}",
|
||||
user.user.name, data
|
||||
),
|
||||
format!("User {} tries to create log entry={:?}", &user.name, data),
|
||||
)
|
||||
.await;
|
||||
|
||||
@ -238,14 +235,14 @@ async fn create_kiosk(
|
||||
)
|
||||
.await;
|
||||
|
||||
create_logbook(db, data, &NonGuestUser { user: creator }).await //TODO: fixme
|
||||
create_logbook(db, data, &DonauLinzUser(creator)).await //TODO: fixme
|
||||
}
|
||||
|
||||
async fn home_logbook(
|
||||
db: &SqlitePool,
|
||||
data: Form<LogToFinalize>,
|
||||
logbook_id: i32,
|
||||
user: &NonGuestUser,
|
||||
user: &DonauLinzUser,
|
||||
) -> Flash<Redirect> {
|
||||
let logbook: Option<Logbook> = Logbook::find_by_id(db, logbook_id).await;
|
||||
let Some(logbook) = logbook else {
|
||||
@ -255,7 +252,7 @@ async fn home_logbook(
|
||||
);
|
||||
};
|
||||
|
||||
match logbook.home(db, &user.user, data.into_inner()).await {
|
||||
match logbook.home(db, &user, data.into_inner()).await {
|
||||
Ok(_) => Flash::success(Redirect::to("/log"), "Ausfahrt korrekt eingetragen"),
|
||||
Err(LogbookUpdateError::TooManyRowers(expected, actual)) => Flash::error(Redirect::to("/log"), format!("Zu viele Ruderer (Boot fasst maximal {expected}, es wurden jedoch {actual} Ruderer ausgewählt)")),
|
||||
Err(LogbookUpdateError::OnlyAllowedToEndTripsEndingToday) => Flash::error(Redirect::to("/log"), "Nur Ausfahrten, die heute enden dürfen eingetragen werden. Für einen Nachtrag schreibe alle Daten Philipp (Tel. nr. siehe Signal oder it@rudernlinz.at)."),
|
||||
@ -285,11 +282,11 @@ async fn home_kiosk(
|
||||
db,
|
||||
data,
|
||||
logbook_id,
|
||||
&NonGuestUser {
|
||||
user: User::find_by_id(db, logbook.shipmaster as i32)
|
||||
&DonauLinzUser(
|
||||
User::find_by_id(db, logbook.shipmaster as i32)
|
||||
.await
|
||||
.unwrap(), //TODO: fixme
|
||||
},
|
||||
.unwrap(),
|
||||
), //TODO: fixme
|
||||
)
|
||||
.await
|
||||
}
|
||||
@ -299,13 +296,13 @@ async fn home(
|
||||
db: &State<SqlitePool>,
|
||||
data: Form<LogToFinalize>,
|
||||
logbook_id: i32,
|
||||
user: NonGuestUser,
|
||||
user: DonauLinzUser,
|
||||
) -> Flash<Redirect> {
|
||||
Log::create(
|
||||
db,
|
||||
format!(
|
||||
"User {} tries to finish log entry {logbook_id} {data:?}",
|
||||
user.user.name
|
||||
&user.name
|
||||
),
|
||||
)
|
||||
.await;
|
||||
@ -314,12 +311,12 @@ async fn home(
|
||||
}
|
||||
|
||||
#[get("/<logbook_id>/delete", rank = 2)]
|
||||
async fn delete(db: &State<SqlitePool>, logbook_id: i32, user: User) -> Flash<Redirect> {
|
||||
async fn delete(db: &State<SqlitePool>, logbook_id: i32, user: DonauLinzUser) -> Flash<Redirect> {
|
||||
let logbook = Logbook::find_by_id(db, logbook_id).await;
|
||||
if let Some(logbook) = logbook {
|
||||
Log::create(
|
||||
db,
|
||||
format!("User {} tries to delete log entry {logbook_id}", user.name),
|
||||
format!("User {} tries to delete log entry {logbook_id}", &user.name),
|
||||
)
|
||||
.await;
|
||||
match logbook.delete(db, &user).await {
|
||||
|
262
src/tera/mod.rs
262
src/tera/mod.rs
@ -8,17 +8,12 @@ use rocket::{
|
||||
response::{Flash, Redirect},
|
||||
routes, Build, FromForm, Rocket, State,
|
||||
};
|
||||
use rocket_dyn_templates::{tera::Context, Template};
|
||||
use rocket_dyn_templates::Template;
|
||||
use serde::Deserialize;
|
||||
use sqlx::SqlitePool;
|
||||
use tera::Context;
|
||||
|
||||
use crate::model::{
|
||||
log::Log,
|
||||
tripdetails::TripDetails,
|
||||
triptype::TripType,
|
||||
user::{User, UserWithRoles},
|
||||
usertrip::{UserTrip, UserTripDeleteError, UserTripError},
|
||||
};
|
||||
use crate::model::user::{User, UserWithRoles};
|
||||
|
||||
pub(crate) mod admin;
|
||||
mod auth;
|
||||
@ -27,6 +22,7 @@ mod cox;
|
||||
mod ergo;
|
||||
mod log;
|
||||
mod misc;
|
||||
mod planned;
|
||||
mod stat;
|
||||
|
||||
#[derive(FromForm, Debug)]
|
||||
@ -35,6 +31,16 @@ struct LoginForm<'r> {
|
||||
password: &'r str,
|
||||
}
|
||||
|
||||
#[get("/")]
|
||||
async fn index(db: &State<SqlitePool>, user: User, flash: Option<FlashMessage<'_>>) -> Template {
|
||||
let mut context = Context::new();
|
||||
if let Some(msg) = flash {
|
||||
context.insert("flash", &msg.into_inner());
|
||||
}
|
||||
context.insert("loggedin_user", &UserWithRoles::from_user(user, db).await);
|
||||
Template::render("index", context.into_json())
|
||||
}
|
||||
|
||||
#[post("/", data = "<login>")]
|
||||
async fn wikiauth(db: &State<SqlitePool>, login: Form<LoginForm<'_>>) -> String {
|
||||
match User::login(db, login.name, login.password).await {
|
||||
@ -43,164 +49,16 @@ async fn wikiauth(db: &State<SqlitePool>, login: Form<LoginForm<'_>>) -> String
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/")]
|
||||
async fn index(db: &State<SqlitePool>, user: User, flash: Option<FlashMessage<'_>>) -> Template {
|
||||
let mut context = Context::new();
|
||||
|
||||
if user.has_role(db, "cox").await || user.has_role(db, "admin").await {
|
||||
let triptypes = TripType::all(db).await;
|
||||
context.insert("trip_types", &triptypes);
|
||||
}
|
||||
|
||||
let days = user.get_days(db).await;
|
||||
|
||||
if let Some(msg) = flash {
|
||||
context.insert("flash", &msg.into_inner());
|
||||
}
|
||||
context.insert("loggedin_user", &UserWithRoles::from_user(user, db).await);
|
||||
context.insert("days", &days);
|
||||
Template::render("index", context.into_json())
|
||||
}
|
||||
|
||||
#[get("/join/<trip_details_id>?<user_note>")]
|
||||
async fn join(
|
||||
db: &State<SqlitePool>,
|
||||
trip_details_id: i64,
|
||||
user: User,
|
||||
user_note: Option<String>,
|
||||
) -> Flash<Redirect> {
|
||||
let Some(trip_details) = TripDetails::find_by_id(db, trip_details_id).await else {
|
||||
return Flash::error(Redirect::to("/"), "Trip_details do not exist.");
|
||||
};
|
||||
|
||||
match UserTrip::create(db, &user, &trip_details, user_note).await {
|
||||
Ok(_) => {
|
||||
Log::create(
|
||||
db,
|
||||
format!(
|
||||
"User {} registered for trip_details.id={}",
|
||||
user.name, trip_details_id
|
||||
),
|
||||
)
|
||||
.await;
|
||||
Flash::success(Redirect::to("/"), "Erfolgreich angemeldet!")
|
||||
}
|
||||
Err(UserTripError::EventAlreadyFull) => {
|
||||
Flash::error(Redirect::to("/"), "Event bereits ausgebucht!")
|
||||
}
|
||||
Err(UserTripError::AlreadyRegistered) => {
|
||||
Flash::error(Redirect::to("/"), "Du nimmst bereits teil!")
|
||||
}
|
||||
Err(UserTripError::AlreadyRegisteredAsCox) => {
|
||||
Flash::error(Redirect::to("/"), "Du hilfst bereits als Steuerperson aus!")
|
||||
}
|
||||
Err(UserTripError::CantRegisterAtOwnEvent) => Flash::error(
|
||||
Redirect::to("/"),
|
||||
"Du kannst bei einer selbst ausgeschriebenen Fahrt nicht mitrudern ;)",
|
||||
),
|
||||
Err(UserTripError::GuestNotAllowedForThisEvent) => Flash::error(
|
||||
Redirect::to("/"),
|
||||
"Bei dieser Ausfahrt können leider keine Gäste mitfahren.",
|
||||
),
|
||||
Err(UserTripError::NotAllowedToAddGuest) => Flash::error(
|
||||
Redirect::to("/"),
|
||||
"Du darfst keine Gäste hinzufügen.",
|
||||
),
|
||||
Err(UserTripError::DetailsLocked) => Flash::error(
|
||||
Redirect::to("/"),
|
||||
"Das Boot ist bereits eingeteilt. Bitte kontaktiere den Schiffsführer (Nummern siehe Signalgruppe) falls du dich doch abmelden willst.",
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/remove/<trip_details_id>/<name>")]
|
||||
async fn remove_guest(
|
||||
db: &State<SqlitePool>,
|
||||
trip_details_id: i64,
|
||||
user: User,
|
||||
name: String,
|
||||
) -> Flash<Redirect> {
|
||||
let Some(trip_details) = TripDetails::find_by_id(db, trip_details_id).await else {
|
||||
return Flash::error(Redirect::to("/"), "TripDetailsId does not exist");
|
||||
};
|
||||
|
||||
match UserTrip::delete(db, &user, &trip_details, Some(name)).await {
|
||||
Ok(_) => {
|
||||
Log::create(
|
||||
db,
|
||||
format!(
|
||||
"User {} unregistered for trip_details.id={}",
|
||||
user.name, trip_details_id
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
Flash::success(Redirect::to("/"), "Erfolgreich abgemeldet!")
|
||||
}
|
||||
Err(UserTripDeleteError::DetailsLocked) => {
|
||||
Log::create(
|
||||
db,
|
||||
format!(
|
||||
"User {} tried to unregister for locked trip_details.id={}",
|
||||
user.name, trip_details_id
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
Flash::error(Redirect::to("/"), "Das Boot ist bereits eingeteilt. Bitte kontaktiere den Schiffsführer (Nummern siehe Signalgruppe) falls du dich doch abmelden willst.")
|
||||
}
|
||||
Err(UserTripDeleteError::GuestNotParticipating) => {
|
||||
Flash::error(Redirect::to("/"), "Gast nicht angemeldet.")
|
||||
}
|
||||
Err(UserTripDeleteError::NotAllowedToDeleteGuest) => Flash::error(
|
||||
Redirect::to("/"),
|
||||
"Keine Berechtigung um den Gast zu entfernen.",
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/remove/<trip_details_id>")]
|
||||
async fn remove(db: &State<SqlitePool>, trip_details_id: i64, user: User) -> Flash<Redirect> {
|
||||
let Some(trip_details) = TripDetails::find_by_id(db, trip_details_id).await else {
|
||||
return Flash::error(Redirect::to("/"), "TripDetailsId does not exist");
|
||||
};
|
||||
|
||||
match UserTrip::delete(db, &user, &trip_details, None).await {
|
||||
Ok(_) => {
|
||||
Log::create(
|
||||
db,
|
||||
format!(
|
||||
"User {} unregistered for trip_details.id={}",
|
||||
user.name, trip_details_id
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
Flash::success(Redirect::to("/"), "Erfolgreich abgemeldet!")
|
||||
}
|
||||
Err(UserTripDeleteError::DetailsLocked) => {
|
||||
Log::create(
|
||||
db,
|
||||
format!(
|
||||
"User {} tried to unregister for locked trip_details.id={}",
|
||||
user.name, trip_details_id
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
Flash::error(Redirect::to("/"), "Das Boot ist bereits eingeteilt. Bitte kontaktiere den Schiffsführer (Nummern siehe Signalgruppe) falls du dich doch abmelden willst.")
|
||||
}
|
||||
Err(_) => {
|
||||
panic!("Not possible to be here");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[catch(401)] //unauthorized
|
||||
#[catch(401)] //Unauthorized
|
||||
fn unauthorized_error() -> Redirect {
|
||||
Redirect::to("/auth")
|
||||
}
|
||||
|
||||
#[catch(403)] //forbidden
|
||||
fn forbidden_error() -> Flash<Redirect> {
|
||||
Flash::error(Redirect::to("/"), "Keine Berechtigung für diese Aktion. Wenn du der Meinung bist, dass du das machen darfst, melde dich bitte bei it@rudernlinz.at.")
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
pub struct Config {
|
||||
@ -210,10 +68,11 @@ pub struct Config {
|
||||
|
||||
pub fn config(rocket: Rocket<Build>) -> Rocket<Build> {
|
||||
rocket
|
||||
.mount("/", routes![index, join, remove, remove_guest])
|
||||
.mount("/", routes![index])
|
||||
.mount("/auth", auth::routes())
|
||||
.mount("/wikiauth", routes![wikiauth])
|
||||
.mount("/log", log::routes())
|
||||
.mount("/planned", planned::routes())
|
||||
.mount("/ergo", ergo::routes())
|
||||
.mount("/stat", stat::routes())
|
||||
.mount("/boatdamage", boatdamage::routes())
|
||||
@ -221,7 +80,7 @@ pub fn config(rocket: Rocket<Build>) -> Rocket<Build> {
|
||||
.mount("/admin", admin::routes())
|
||||
.mount("/", misc::routes())
|
||||
.mount("/public", FileServer::from("static/"))
|
||||
.register("/", catchers![unauthorized_error])
|
||||
.register("/", catchers![unauthorized_error, forbidden_error])
|
||||
.attach(Template::fairing())
|
||||
.attach(AdHoc::config::<Config>())
|
||||
}
|
||||
@ -255,7 +114,11 @@ mod test {
|
||||
|
||||
assert_eq!(response.status(), Status::Ok);
|
||||
|
||||
assert!(response.into_string().await.unwrap().contains("Ausfahrten"));
|
||||
assert!(response
|
||||
.into_string()
|
||||
.await
|
||||
.unwrap()
|
||||
.contains("Ruderassistent"));
|
||||
}
|
||||
|
||||
#[sqlx::test]
|
||||
@ -274,75 +137,6 @@ mod test {
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/auth"));
|
||||
}
|
||||
|
||||
#[sqlx::test]
|
||||
fn test_join_and_remove() {
|
||||
let db = testdb!();
|
||||
|
||||
let rocket = rocket::build().manage(db.clone());
|
||||
let rocket = crate::tera::config(rocket);
|
||||
|
||||
let client = Client::tracked(rocket).await.unwrap();
|
||||
let login = client
|
||||
.post("/auth")
|
||||
.header(ContentType::Form) // Set the content type to form
|
||||
.body("name=rower&password=rower"); // Add the form data to the request body;
|
||||
login.dispatch().await;
|
||||
|
||||
let req = client.get("/join/1");
|
||||
let response = req.dispatch().await;
|
||||
|
||||
assert_eq!(response.status(), Status::SeeOther);
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/"));
|
||||
|
||||
let flash_cookie = response
|
||||
.cookies()
|
||||
.get("_flash")
|
||||
.expect("Expected flash cookie");
|
||||
|
||||
assert_eq!(flash_cookie.value(), "7:successErfolgreich angemeldet!");
|
||||
|
||||
let req = client.get("/remove/1");
|
||||
let response = req.dispatch().await;
|
||||
|
||||
assert_eq!(response.status(), Status::SeeOther);
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/"));
|
||||
|
||||
let flash_cookie = response
|
||||
.cookies()
|
||||
.get("_flash")
|
||||
.expect("Expected flash cookie");
|
||||
|
||||
assert_eq!(flash_cookie.value(), "7:successErfolgreich abgemeldet!");
|
||||
}
|
||||
|
||||
#[sqlx::test]
|
||||
fn test_join_invalid_event() {
|
||||
let db = testdb!();
|
||||
|
||||
let rocket = rocket::build().manage(db.clone());
|
||||
let rocket = crate::tera::config(rocket);
|
||||
|
||||
let client = Client::tracked(rocket).await.unwrap();
|
||||
let login = client
|
||||
.post("/auth")
|
||||
.header(ContentType::Form) // Set the content type to form
|
||||
.body("name=rower&password=rower"); // Add the form data to the request body;
|
||||
login.dispatch().await;
|
||||
|
||||
let req = client.get("/join/9999");
|
||||
let response = req.dispatch().await;
|
||||
|
||||
assert_eq!(response.status(), Status::SeeOther);
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/"));
|
||||
|
||||
let flash_cookie = response
|
||||
.cookies()
|
||||
.get("_flash")
|
||||
.expect("Expected flash cookie");
|
||||
|
||||
assert_eq!(flash_cookie.value(), "5:errorTrip_details do not exist.");
|
||||
}
|
||||
|
||||
#[sqlx::test]
|
||||
fn test_public() {
|
||||
let db = testdb!();
|
||||
|
273
src/tera/planned.rs
Normal file
273
src/tera/planned.rs
Normal file
@ -0,0 +1,273 @@
|
||||
use rocket::{
|
||||
get,
|
||||
request::FlashMessage,
|
||||
response::{Flash, Redirect},
|
||||
routes, Route, State,
|
||||
};
|
||||
use rocket_dyn_templates::Template;
|
||||
use sqlx::SqlitePool;
|
||||
use tera::Context;
|
||||
|
||||
use crate::model::{
|
||||
log::Log,
|
||||
tripdetails::TripDetails,
|
||||
triptype::TripType,
|
||||
user::{AllowedForPlannedTripsUser, User, UserWithRoles},
|
||||
usertrip::{UserTrip, UserTripDeleteError, UserTripError},
|
||||
};
|
||||
|
||||
#[get("/")]
|
||||
async fn index(
|
||||
db: &State<SqlitePool>,
|
||||
user: AllowedForPlannedTripsUser,
|
||||
flash: Option<FlashMessage<'_>>,
|
||||
) -> Template {
|
||||
let user: User = user.into();
|
||||
|
||||
let mut context = Context::new();
|
||||
|
||||
if user.has_role(db, "cox").await || user.has_role(db, "admin").await {
|
||||
let triptypes = TripType::all(db).await;
|
||||
context.insert("trip_types", &triptypes);
|
||||
}
|
||||
|
||||
let days = user.get_days(db).await;
|
||||
|
||||
if let Some(msg) = flash {
|
||||
context.insert("flash", &msg.into_inner());
|
||||
}
|
||||
context.insert(
|
||||
"loggedin_user",
|
||||
&UserWithRoles::from_user(user.into(), db).await,
|
||||
);
|
||||
context.insert("days", &days);
|
||||
Template::render("planned", context.into_json())
|
||||
}
|
||||
|
||||
#[get("/join/<trip_details_id>?<user_note>")]
|
||||
async fn join(
|
||||
db: &State<SqlitePool>,
|
||||
trip_details_id: i64,
|
||||
user: AllowedForPlannedTripsUser,
|
||||
user_note: Option<String>,
|
||||
) -> Flash<Redirect> {
|
||||
let user: User = user.into();
|
||||
|
||||
let Some(trip_details) = TripDetails::find_by_id(db, trip_details_id).await else {
|
||||
return Flash::error(Redirect::to("/"), "Trip_details do not exist.");
|
||||
};
|
||||
|
||||
match UserTrip::create(db, &user, &trip_details, user_note).await {
|
||||
Ok(_) => {
|
||||
Log::create(
|
||||
db,
|
||||
format!(
|
||||
"User {} registered for trip_details.id={}",
|
||||
user.name, trip_details_id
|
||||
),
|
||||
)
|
||||
.await;
|
||||
Flash::success(Redirect::to("/"), "Erfolgreich angemeldet!")
|
||||
}
|
||||
Err(UserTripError::EventAlreadyFull) => {
|
||||
Flash::error(Redirect::to("/"), "Event bereits ausgebucht!")
|
||||
}
|
||||
Err(UserTripError::AlreadyRegistered) => {
|
||||
Flash::error(Redirect::to("/"), "Du nimmst bereits teil!")
|
||||
}
|
||||
Err(UserTripError::AlreadyRegisteredAsCox) => {
|
||||
Flash::error(Redirect::to("/"), "Du hilfst bereits als Steuerperson aus!")
|
||||
}
|
||||
Err(UserTripError::CantRegisterAtOwnEvent) => Flash::error(
|
||||
Redirect::to("/"),
|
||||
"Du kannst bei einer selbst ausgeschriebenen Fahrt nicht mitrudern ;)",
|
||||
),
|
||||
Err(UserTripError::GuestNotAllowedForThisEvent) => Flash::error(
|
||||
Redirect::to("/"),
|
||||
"Bei dieser Ausfahrt können leider keine Gäste mitfahren.",
|
||||
),
|
||||
Err(UserTripError::NotAllowedToAddGuest) => Flash::error(
|
||||
Redirect::to("/"),
|
||||
"Du darfst keine Gäste hinzufügen.",
|
||||
),
|
||||
Err(UserTripError::DetailsLocked) => Flash::error(
|
||||
Redirect::to("/"),
|
||||
"Das Boot ist bereits eingeteilt. Bitte kontaktiere den Schiffsführer (Nummern siehe Signalgruppe) falls du dich doch abmelden willst.",
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/remove/<trip_details_id>/<name>")]
|
||||
async fn remove_guest(
|
||||
db: &State<SqlitePool>,
|
||||
trip_details_id: i64,
|
||||
user: AllowedForPlannedTripsUser,
|
||||
name: String,
|
||||
) -> Flash<Redirect> {
|
||||
let user: User = user.into();
|
||||
|
||||
let Some(trip_details) = TripDetails::find_by_id(db, trip_details_id).await else {
|
||||
return Flash::error(Redirect::to("/"), "TripDetailsId does not exist");
|
||||
};
|
||||
|
||||
match UserTrip::delete(db, &user, &trip_details, Some(name)).await {
|
||||
Ok(_) => {
|
||||
Log::create(
|
||||
db,
|
||||
format!(
|
||||
"User {} unregistered for trip_details.id={}",
|
||||
user.name, trip_details_id
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
Flash::success(Redirect::to("/"), "Erfolgreich abgemeldet!")
|
||||
}
|
||||
Err(UserTripDeleteError::DetailsLocked) => {
|
||||
Log::create(
|
||||
db,
|
||||
format!(
|
||||
"User {} tried to unregister for locked trip_details.id={}",
|
||||
user.name, trip_details_id
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
Flash::error(Redirect::to("/"), "Das Boot ist bereits eingeteilt. Bitte kontaktiere den Schiffsführer (Nummern siehe Signalgruppe) falls du dich doch abmelden willst.")
|
||||
}
|
||||
Err(UserTripDeleteError::GuestNotParticipating) => {
|
||||
Flash::error(Redirect::to("/"), "Gast nicht angemeldet.")
|
||||
}
|
||||
Err(UserTripDeleteError::NotAllowedToDeleteGuest) => Flash::error(
|
||||
Redirect::to("/"),
|
||||
"Keine Berechtigung um den Gast zu entfernen.",
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/remove/<trip_details_id>")]
|
||||
async fn remove(
|
||||
db: &State<SqlitePool>,
|
||||
trip_details_id: i64,
|
||||
user: AllowedForPlannedTripsUser,
|
||||
) -> Flash<Redirect> {
|
||||
let user: User = user.into();
|
||||
|
||||
let Some(trip_details) = TripDetails::find_by_id(db, trip_details_id).await else {
|
||||
return Flash::error(Redirect::to("/"), "TripDetailsId does not exist");
|
||||
};
|
||||
|
||||
match UserTrip::delete(db, &user, &trip_details, None).await {
|
||||
Ok(_) => {
|
||||
Log::create(
|
||||
db,
|
||||
format!(
|
||||
"User {} unregistered for trip_details.id={}",
|
||||
user.name, trip_details_id
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
Flash::success(Redirect::to("/"), "Erfolgreich abgemeldet!")
|
||||
}
|
||||
Err(UserTripDeleteError::DetailsLocked) => {
|
||||
Log::create(
|
||||
db,
|
||||
format!(
|
||||
"User {} tried to unregister for locked trip_details.id={}",
|
||||
user.name, trip_details_id
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
Flash::error(Redirect::to("/"), "Das Boot ist bereits eingeteilt. Bitte kontaktiere den Schiffsführer (Nummern siehe Signalgruppe) falls du dich doch abmelden willst.")
|
||||
}
|
||||
Err(_) => {
|
||||
panic!("Not possible to be here");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn routes() -> Vec<Route> {
|
||||
routes![index, join, remove, remove_guest]
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use rocket::{
|
||||
http::{ContentType, Status},
|
||||
local::asynchronous::Client,
|
||||
};
|
||||
use sqlx::SqlitePool;
|
||||
|
||||
use crate::testdb;
|
||||
|
||||
#[sqlx::test]
|
||||
fn test_join_and_remove() {
|
||||
let db = testdb!();
|
||||
|
||||
let rocket = rocket::build().manage(db.clone());
|
||||
let rocket = crate::tera::config(rocket);
|
||||
|
||||
let client = Client::tracked(rocket).await.unwrap();
|
||||
let login = client
|
||||
.post("/auth")
|
||||
.header(ContentType::Form) // Set the content type to form
|
||||
.body("name=rower&password=rower"); // Add the form data to the request body;
|
||||
login.dispatch().await;
|
||||
|
||||
let req = client.get("/planned/join/1");
|
||||
let response = req.dispatch().await;
|
||||
|
||||
assert_eq!(response.status(), Status::SeeOther);
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/"));
|
||||
|
||||
let flash_cookie = response
|
||||
.cookies()
|
||||
.get("_flash")
|
||||
.expect("Expected flash cookie");
|
||||
|
||||
assert_eq!(flash_cookie.value(), "7:successErfolgreich angemeldet!");
|
||||
|
||||
let req = client.get("/planned/remove/1");
|
||||
let response = req.dispatch().await;
|
||||
|
||||
assert_eq!(response.status(), Status::SeeOther);
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/"));
|
||||
|
||||
let flash_cookie = response
|
||||
.cookies()
|
||||
.get("_flash")
|
||||
.expect("Expected flash cookie");
|
||||
|
||||
assert_eq!(flash_cookie.value(), "7:successErfolgreich abgemeldet!");
|
||||
}
|
||||
|
||||
#[sqlx::test]
|
||||
fn test_join_invalid_event() {
|
||||
let db = testdb!();
|
||||
|
||||
let rocket = rocket::build().manage(db.clone());
|
||||
let rocket = crate::tera::config(rocket);
|
||||
|
||||
let client = Client::tracked(rocket).await.unwrap();
|
||||
let login = client
|
||||
.post("/auth")
|
||||
.header(ContentType::Form) // Set the content type to form
|
||||
.body("name=rower&password=rower"); // Add the form data to the request body;
|
||||
login.dispatch().await;
|
||||
|
||||
let req = client.get("/planned/join/9999");
|
||||
let response = req.dispatch().await;
|
||||
|
||||
assert_eq!(response.status(), Status::SeeOther);
|
||||
assert_eq!(response.headers().get("Location").next(), Some("/"));
|
||||
|
||||
let flash_cookie = response
|
||||
.cookies()
|
||||
.get("_flash")
|
||||
.expect("Expected flash cookie");
|
||||
|
||||
assert_eq!(flash_cookie.value(), "5:errorTrip_details do not exist.");
|
||||
}
|
||||
}
|
@ -4,19 +4,19 @@ use sqlx::SqlitePool;
|
||||
|
||||
use crate::model::{
|
||||
stat::{self, Stat},
|
||||
user::{NonGuestUser, UserWithRoles},
|
||||
user::{DonauLinzUser, UserWithRoles},
|
||||
};
|
||||
|
||||
use super::log::KioskCookie;
|
||||
|
||||
#[get("/boats?<year>", rank = 2)]
|
||||
async fn index_boat(db: &State<SqlitePool>, user: NonGuestUser, year: Option<i32>) -> Template {
|
||||
async fn index_boat(db: &State<SqlitePool>, user: DonauLinzUser, year: Option<i32>) -> Template {
|
||||
let stat = Stat::boats(db, year).await;
|
||||
let kiosk = false;
|
||||
|
||||
Template::render(
|
||||
"stat.boats",
|
||||
context!(loggedin_user: &UserWithRoles::from_user(user.user, db).await, stat, kiosk),
|
||||
context!(loggedin_user: &UserWithRoles::from_user(user.into(), db).await, stat, kiosk),
|
||||
)
|
||||
}
|
||||
|
||||
@ -33,15 +33,15 @@ async fn index_boat_kiosk(
|
||||
}
|
||||
|
||||
#[get("/?<year>", rank = 2)]
|
||||
async fn index(db: &State<SqlitePool>, user: NonGuestUser, year: Option<i32>) -> Template {
|
||||
async fn index(db: &State<SqlitePool>, user: DonauLinzUser, year: Option<i32>) -> Template {
|
||||
let stat = Stat::people(db, year).await;
|
||||
let guest_km = Stat::guest(db, year).await;
|
||||
let personal = stat::get_personal(db, &user.user).await;
|
||||
let personal = stat::get_personal(db, &user).await;
|
||||
let kiosk = false;
|
||||
|
||||
Template::render(
|
||||
"stat.people",
|
||||
context!(loggedin_user: &UserWithRoles::from_user(user.user, db).await, stat, personal, kiosk, guest_km),
|
||||
context!(loggedin_user: &UserWithRoles::from_user(user.into(), db).await, stat, personal, kiosk, guest_km),
|
||||
)
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user