WIP - at a crossroads where we need some kind of auth mechanism

This commit is contained in:
jeff 2021-01-03 22:05:28 +00:00
parent f479da9001
commit 592c925bc1
3 changed files with 109 additions and 8 deletions

View File

@ -4,6 +4,7 @@ use rocket::response::status;
use rocket::response::{self, Responder}; use rocket::response::{self, Responder};
use rocket_contrib::templates::Template; use rocket_contrib::templates::Template;
use std::collections::HashMap; use std::collections::HashMap;
use std::convert::Into;
use thiserror::Error; use thiserror::Error;
#[derive(Error, Debug)] #[derive(Error, Debug)]
@ -48,6 +49,28 @@ impl Error {
_ => false, _ => false,
} }
} }
fn message(&self) -> String {
if self.is_sensitive() {
"internal error".to_string()
} else {
self.to_string()
}
}
}
impl From<Error> for tonic::Status {
fn from(err: Error) -> tonic::Status {
use tonic::{Code, Status};
use Error::*;
match err {
NotFound => Status::new(Code::NotFound, err.message()),
NotLoggedIn => Status::new(Code::Unauthenticated, err.message()),
NoPermission => Status::new(Code::PermissionDenied, err.message()),
InvalidInput => Status::new(Code::InvalidArgument, err.message()),
_ => Status::new(Code::Internal, err.message()),
}
}
} }
#[rocket::async_trait] #[rocket::async_trait]

View File

@ -1,7 +1,66 @@
use crate::models::proto::cofd::api::cofd_api_server::{CofdApi, CofdApiServer}; use crate::db::Dao;
use crate::errors::Error;
use crate::models::characters::Character;
use crate::models::proto::cofd::api::cofd_api_server::CofdApi;
use crate::models::proto::cofd::api::UpdateSkillValueRequest; use crate::models::proto::cofd::api::UpdateSkillValueRequest;
use crate::models::proto::cofd::cofd_sheet::Skill; use crate::models::proto::cofd::cofd_sheet::Skill;
use tonic::{transport::Server, Request, Response, Status}; use crate::models::proto::cofd::*;
use crate::models::users::User;
use std::collections::btree_map::{Entry, OccupiedEntry};
use tonic::{Request, Response, Status};
/// Load the character belonging to the given user, as long as they're
/// the owner of that character. Returns an error if user is not
/// logged in, the owner of the character is not found, or the logged
/// in user does not have the permission to access this character.
async fn load_character(
conn: &sqlx::SqlitePool,
logged_in_user: Option<&User>,
owner: &str,
character_id: i32,
) -> Result<Character, Error> {
let logged_in_user = logged_in_user.ok_or(Error::NotLoggedIn)?;
let character: Character = conn
.load_character(character_id)
.await?
.ok_or(Error::NotFound)?;
if &logged_in_user.username != owner {
return Err(Error::NoPermission);
}
Ok(character)
}
fn find_skill_entry<'a>(
sheet: &'a mut CofdSheet,
skill_name: &'a str,
) -> Option<OccupiedEntry<'a, String, Skill>> {
let all_skills = vec![
&mut sheet.mental_skills,
&mut sheet.physical_skills,
&mut sheet.social_skills,
];
// Search all skill lists for this value using "workaround" to
// break value from for loops.
let skill: Option<OccupiedEntry<_, _>> = 'l: loop {
for skill_map in all_skills {
if let Entry::Occupied(entry) = skill_map.entry(skill_name.to_owned()) {
break 'l Some(entry);
}
}
break None;
};
skill
}
fn find_skill<'a>(sheet: &'a mut CofdSheet, skill_name: &'a str) -> Option<&'a mut Skill> {
find_skill_entry(sheet, skill_name).map(|entry| entry.into_mut())
}
#[derive(Debug)] #[derive(Debug)]
pub struct CofdApiService { pub struct CofdApiService {
@ -14,8 +73,30 @@ impl CofdApi for CofdApiService {
&self, &self,
request: Request<UpdateSkillValueRequest>, // Accept request of type HelloRequest request: Request<UpdateSkillValueRequest>, // Accept request of type HelloRequest
) -> Result<Response<Skill>, Status> { ) -> Result<Response<Skill>, Status> {
// Return an instance of type HelloReply //Can use metadata map to add user id inside interceptor for auth.
println!("Got a request: {:?}", request); let request = request.into_inner();
let mut character = load_character(
&self.db,
logged_in_user,
&request.character_username,
request.character_id,
)
.await?;
let mut sheet: CofdSheet = character.try_deserialize()?;
let skill: Option<&mut Skill> = find_skill(&mut sheet, &request.skill_name);
skill
.map(|s| s.dots = request.skill_value)
.ok_or(Error::InvalidInput)?;
println!("updated skill value",);
character.update_data(sheet)?;
self.db
.update_character_sheet(&character)
.await
.map_err(|e| e.into())?; //TODO maybe use crate Error for db
let reply = Skill::default(); let reply = Skill::default();

View File

@ -1,11 +1,8 @@
use crate::db::{Dao, TenebrousDbConn}; use crate::db::{Dao, TenebrousDbConn};
use crate::errors::Error; use crate::errors::Error;
use crate::models::characters::{Character, CharacterDataType, DynCharacterData, Visibility}; use crate::models::characters::Character;
use crate::models::proto::cofd::*; use crate::models::proto::cofd::*;
use crate::models::users::User; use crate::models::users::User;
use rocket_contrib::templates::Template;
use serde::Serialize;
use std::borrow::Cow;
use std::collections::btree_map::{Entry, OccupiedEntry}; use std::collections::btree_map::{Entry, OccupiedEntry};
pub(crate) fn routes() -> Vec<rocket::Route> { pub(crate) fn routes() -> Vec<rocket::Route> {