Added user_id checking to commands

+ Refactored to make CommandRequest a trait that all the parameter structs implment
+ Created handle_command to handle all the preprocessing needed before running the command
main
Joey Hines 2021-10-24 13:20:15 -06:00
parent b3c0e2dcb0
commit 8e6b652d5e
No known key found for this signature in database
GPG Key ID: 80F567B5C968F91B
9 changed files with 103 additions and 51 deletions

View File

@ -1,19 +1,17 @@
use crate::commands::{Command, RequestType}; use crate::commands::{Command, RequestType};
use crate::context::Context; use crate::context::Context;
use crate::helper::get_player_from_req;
use crate::Result; use crate::Result;
use geoffrey_db::helper::load_location; use geoffrey_db::helper::load_location;
use geoffrey_models::models::locations::{Location, LocationDb}; use geoffrey_models::models::locations::{Location, LocationDb};
use geoffrey_models::models::parameters::add_location_params::AddLocationParams; use geoffrey_models::models::parameters::add_location_params::AddLocationParams;
use geoffrey_models::models::parameters::CommandRequest;
use geoffrey_models::models::response::api_error::GeoffreyAPIError;
use geoffrey_models::models::CommandLevel; use geoffrey_models::models::CommandLevel;
use std::sync::Arc; use std::sync::Arc;
use geoffrey_models::models::player::Player;
pub struct AddLocation {} pub struct AddLocation {}
impl Command for AddLocation { impl Command for AddLocation {
type Req = CommandRequest<AddLocationParams>; type Req = AddLocationParams;
type Resp = Location; type Resp = Location;
fn command_name() -> String { fn command_name() -> String {
@ -28,22 +26,19 @@ impl Command for AddLocation {
CommandLevel::REGISTERED CommandLevel::REGISTERED
} }
fn run_command(ctx: Arc<Context>, req: Self::Req) -> Result<Self::Resp> { fn run_command(ctx: Arc<Context>, req: Self::Req, user: Option<Player>) -> Result<Self::Resp> {
if let Some(player) = get_player_from_req(&ctx.db, &req)? { let user = user.unwrap();
let args = &req.arguments;
let location = LocationDb::new(
args.name.as_str(),
args.position,
player.id.unwrap(),
args.tunnel.clone(),
args.loc_type.into(),
);
let location = ctx.db.insert(location)?; let location = LocationDb::new(
req.name.as_str(),
req.position,
user.id.unwrap(),
req.tunnel.clone(),
req.loc_type.into(),
);
load_location(&ctx.db, &location).map_err(|err| err.into()) let location = ctx.db.insert(location)?;
} else {
Err(GeoffreyAPIError::PlayerNotRegistered) load_location(&ctx.db, &location).map_err(|err| err.into())
}
} }
} }

View File

@ -4,7 +4,6 @@ use crate::Result;
use geoffrey_db::helper::load_location; use geoffrey_db::helper::load_location;
use geoffrey_models::models::locations::{Location, LocationDb}; use geoffrey_models::models::locations::{Location, LocationDb};
use geoffrey_models::models::parameters::find_params::FindParams; use geoffrey_models::models::parameters::find_params::FindParams;
use geoffrey_models::models::parameters::CommandRequest;
use geoffrey_models::models::player::Player; use geoffrey_models::models::player::Player;
use geoffrey_models::models::response::api_error::GeoffreyAPIError; use geoffrey_models::models::response::api_error::GeoffreyAPIError;
use geoffrey_models::models::CommandLevel; use geoffrey_models::models::CommandLevel;
@ -13,7 +12,7 @@ use std::sync::Arc;
pub struct FindCommand {} pub struct FindCommand {}
impl Command for FindCommand { impl Command for FindCommand {
type Req = CommandRequest<FindParams>; type Req = FindParams;
type Resp = Vec<Location>; type Resp = Vec<Location>;
fn command_name() -> String { fn command_name() -> String {
@ -28,8 +27,8 @@ impl Command for FindCommand {
CommandLevel::ALL CommandLevel::ALL
} }
fn run_command(ctx: Arc<Context>, req: Self::Req) -> Result<Self::Resp> { fn run_command(ctx: Arc<Context>, req: Self::Req, _: Option<Player>) -> Result<Self::Resp> {
let query = req.arguments.query.to_lowercase(); let query = req.query.to_lowercase();
let players: Vec<u64> = ctx let players: Vec<u64> = ctx
.db .db
.filter(|_, player: &Player| { .filter(|_, player: &Player| {

View File

@ -11,6 +11,11 @@ use std::fmt::Debug;
use std::sync::Arc; use std::sync::Arc;
use warp::filters::BoxedFilter; use warp::filters::BoxedFilter;
use warp::Filter; use warp::Filter;
use geoffrey_models::models::player::Player;
use crate::helper::get_player_from_req;
use geoffrey_models::models::response::api_error::GeoffreyAPIError;
use geoffrey_models::models::parameters::CommandRequest;
pub mod add_location; pub mod add_location;
pub mod find; pub mod find;
@ -24,13 +29,44 @@ pub enum RequestType {
} }
pub trait Command { pub trait Command {
type Req: Serialize + DeserializeOwned + Send + 'static + Debug; type Req: CommandRequest;
type Resp: Serialize + DeserializeOwned + Send; type Resp: Serialize + DeserializeOwned + Send;
fn command_name() -> String; fn command_name() -> String;
fn request_type() -> RequestType; fn request_type() -> RequestType;
fn command_level() -> CommandLevel; fn command_level() -> CommandLevel;
fn run_command(ctx: Arc<Context>, req: Self::Req) -> Result<Self::Resp>; fn run_command(ctx: Arc<Context>, req: Self::Req, user: Option<Player>) -> Result<Self::Resp>;
fn user_is_authorized(user: &Option<Player>) -> Result<()> {
if Self::command_level() == CommandLevel::ALL {
Ok(())
}
else {
if let Some(user) = user {
if user.auth_level >= Self::command_level() {
Ok(())
}
else {
Err(GeoffreyAPIError::PermissionInsufficient)
}
}
else {
Err(GeoffreyAPIError::PlayerNotRegistered)
}
}
}
}
pub fn handle_command<T: Command>(ctx: Arc<Context>, req: T::Req) -> Result<T::Resp> {
log::info!("Running command {}", T::command_name());
log::debug!("Request: {:?}", req);
let user = get_player_from_req(&ctx.db, &req)?;
match T::user_is_authorized(&user) {
Ok(_) => T::run_command(ctx, req, user),
Err(e) => Err(e)
}
} }
#[allow(clippy::needless_return)] #[allow(clippy::needless_return)]
@ -39,10 +75,7 @@ pub fn create_command_filter<T: Command>(ctx: Arc<Context>) -> BoxedFilter<(impl
.and(warp::any().map(move || ctx.clone())) .and(warp::any().map(move || ctx.clone()))
.and(warp::body::json()) .and(warp::body::json())
.map(|ctx: Arc<Context>, req: T::Req| { .map(|ctx: Arc<Context>, req: T::Req| {
log::info!("Running command {}", T::command_name()); let reply = handle_command::<T>(ctx, req);
log::debug!("Request: {:?}", req);
let reply = T::run_command(ctx, req);
if let Ok(reply) = reply { if let Ok(reply) = reply {
log::debug!("Successfully processed command"); log::debug!("Successfully processed command");
warp::reply::json(&APIResponse::Response::<T::Resp>(reply)) warp::reply::json(&APIResponse::Response::<T::Resp>(reply))

View File

@ -1,7 +1,6 @@
use crate::commands::{Command, RequestType}; use crate::commands::{Command, RequestType};
use crate::context::Context; use crate::context::Context;
use geoffrey_models::models::parameters::register_params::RegisterParameters; use geoffrey_models::models::parameters::register_params::RegisterParameters;
use geoffrey_models::models::parameters::CommandRequest;
use geoffrey_models::models::player::Player; use geoffrey_models::models::player::Player;
use geoffrey_models::models::response::api_error::GeoffreyAPIError; use geoffrey_models::models::response::api_error::GeoffreyAPIError;
use geoffrey_models::models::CommandLevel; use geoffrey_models::models::CommandLevel;
@ -10,7 +9,7 @@ use std::sync::Arc;
pub struct Register {} pub struct Register {}
impl Command for Register { impl Command for Register {
type Req = CommandRequest<RegisterParameters>; type Req = RegisterParameters;
type Resp = Player; type Resp = Player;
fn command_name() -> String { fn command_name() -> String {
@ -25,11 +24,11 @@ impl Command for Register {
CommandLevel::ALL CommandLevel::ALL
} }
fn run_command(ctx: Arc<Context>, req: Self::Req) -> crate::Result<Self::Resp> { fn run_command(ctx: Arc<Context>, req: Self::Req, _: Option<Player>) -> crate::Result<Self::Resp> {
let player = Player::new(req.arguments.username.as_str(), req.arguments.user_id); let player = Player::new(req.username.as_str(), req.new_user_id);
ctx.db ctx.db
.insert(player) .insert(player)
.map_err(|err| GeoffreyAPIError::DatabaseError(err.to_string())) .map_err(GeoffreyAPIError::from)
} }
} }

View File

@ -3,12 +3,12 @@ use geoffrey_db::database::Database;
use geoffrey_models::models::parameters::CommandRequest; use geoffrey_models::models::parameters::CommandRequest;
use geoffrey_models::models::player::Player; use geoffrey_models::models::player::Player;
pub fn get_player_from_req<T>(db: &Database, req: &CommandRequest<T>) -> Result<Option<Player>> { pub fn get_player_from_req<T: CommandRequest>(db: &Database, req: &T) -> Result<Option<Player>> {
if let Some(user_id) = &req.user { if let Some(user_id) = req.user_id() {
Ok(db Ok(db
.filter(|_, player: &Player| player.has_user_id(user_id))? .filter(|_, player: &Player| player.has_user_id(&user_id))?
.next()) .next())
} else { } else {
Ok(None) Ok(None)
} }
} }

View File

@ -1,11 +1,25 @@
use crate::models::locations::LocationType; use crate::models::locations::LocationType;
use crate::models::{Position, Tunnel}; use crate::models::{Position, Tunnel};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::models::player::UserID;
use crate::models::parameters::CommandRequest;
#[derive(Debug, Serialize, Deserialize, Clone)] #[derive(Debug, Serialize, Deserialize, Clone)]
pub struct AddLocationParams { pub struct AddLocationParams {
token: u64,
user_id: UserID,
pub name: String, pub name: String,
pub position: Position, pub position: Position,
pub loc_type: LocationType, pub loc_type: LocationType,
pub tunnel: Option<Tunnel>, pub tunnel: Option<Tunnel>,
} }
impl CommandRequest for AddLocationParams {
fn token(&self) -> u64 {
self.token
}
fn user_id(&self) -> Option<UserID> {
Some(self.user_id.clone())
}
}

View File

@ -1,6 +1,14 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::models::parameters::CommandRequest;
#[derive(Debug, Serialize, Deserialize, Clone)] #[derive(Debug, Serialize, Deserialize, Clone)]
pub struct FindParams { pub struct FindParams {
pub token: u64,
pub query: String, pub query: String,
} }
impl CommandRequest for FindParams {
fn token(&self) -> u64 {
self.token
}
}

View File

@ -5,18 +5,14 @@ pub mod register_params;
use crate::models::player::{Player, UserID}; use crate::models::player::{Player, UserID};
use crate::models::token::{Permissions, Token}; use crate::models::token::{Permissions, Token};
use crate::models::CommandLevel; use crate::models::CommandLevel;
use serde::{Deserialize, Serialize}; use serde::Serialize;
use std::fmt::Debug;
use serde::de::DeserializeOwned;
#[derive(Debug, Serialize, Deserialize, Clone)] pub trait CommandRequest: Serialize + DeserializeOwned + Debug + Clone + Send + 'static {
pub struct CommandRequest<T> { fn token(&self) -> u64;
pub user: Option<UserID>, fn user_id(&self) -> Option<UserID> {
pub arguments: T, None
pub token: u64,
}
impl<T> CommandRequest<T> {
fn has_user_id(&self) -> bool {
self.user.is_some()
} }
fn check_permission( fn check_permission(
@ -35,4 +31,4 @@ impl<T> CommandRequest<T> {
false false
} }
} }
} }

View File

@ -1,8 +1,16 @@
use crate::models::player::UserID; use crate::models::player::UserID;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::models::parameters::CommandRequest;
#[derive(Debug, Serialize, Deserialize, Clone)] #[derive(Debug, Serialize, Deserialize, Clone)]
pub struct RegisterParameters { pub struct RegisterParameters {
pub token: u64,
pub new_user_id: UserID,
pub username: String, pub username: String,
pub user_id: UserID, }
impl CommandRequest for RegisterParameters {
fn token(&self) -> u64 {
self.token
}
} }