From: Ian Jackson Date: Wed, 30 Dec 2020 11:11:25 +0000 (+0000) Subject: wip untangle X-Git-Tag: otter-0.2.0~77 X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=commitdiff_plain;h=41e7942e6bbd5d3d0b5bbd52dda290d2da7a5449;p=otter.git wip untangle Signed-off-by: Ian Jackson --- diff --git a/Cargo.lock.example b/Cargo.lock.example index deb67a24..b9c9a462 100644 --- a/Cargo.lock.example +++ b/Cargo.lock.example @@ -1551,15 +1551,13 @@ dependencies = [ "regex", "rmp", "rmp-serde 0.15.0", - "rocket", - "rocket_contrib", - "rocket_cors", "serde", "serde_json", "serde_with", "slotmap", "strum", "tempfile", + "tera", "thiserror", "toml 0.5.8", "typetag", diff --git a/Cargo.toml b/Cargo.toml index af19ac15..18f41a94 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,15 +58,8 @@ slotmap = { version = "0.4", features = ['serde'] } strum = { version = "0.20", features = ['derive'] } thiserror = "1" tempfile = "3" +tera = "0.11" toml = "0.5" typetag = "0.1.6" uds = "0.2" vecdeque-stableix = "1" - -rocket = { version = "^0.4.6", features=["sse"] } -rocket_cors = "0.5" - -[dependencies.rocket_contrib] -version = "0.4" -default-features = false -features = ["tera_templates","helmet","json","serve"] diff --git a/src/api.rs b/daemon/api.rs similarity index 82% rename from src/api.rs rename to daemon/api.rs index 943edfa7..fbaec774 100644 --- a/src/api.rs +++ b/daemon/api.rs @@ -2,46 +2,21 @@ // SPDX-License-Identifier: AGPL-3.0-or-later // There is NO WARRANTY. -use crate::imports::*; +use super::*; type WRC = WhatResponseToClientOp; -#[derive(Clone,Copy,Debug,Eq,PartialEq,Serialize,Deserialize,EnumString)] -pub enum PresentationLayout { - Portrait, - Landscape, -} - type PL = PresentationLayout; -impl<'r> FromParam<'r> for PresentationLayout { +pub struct AbbrevPresentationLayout(pub PresentationLayout); + +impl<'r> FromParam<'r> for AbbrevPresentationLayout { type Error = strum::ParseError; fn from_param(param: &'r RawStr) -> Result { - param.as_str().parse() + AbbrevPresentationLayout(param.as_str().parse()) } } -impl PresentationLayout { - pub fn template(self) -> &'static str { - match self { - PL::Portrait => "session", - PL::Landscape => "landscape", - } - } - pub fn abbreviate_timestamps(self) -> bool { - match self { - PL::Portrait => false, - PL::Landscape => true, - } - } -} - -impl Default for PresentationLayout { - fn default() -> Self { PL::Portrait } -} - -pub struct AbbrevPresentationLayout(pub PresentationLayout); - impl<'r> FromParam<'r> for AbbrevPresentationLayout { type Error = (); #[throws(Self::Error)] @@ -54,6 +29,26 @@ impl<'r> FromParam<'r> for AbbrevPresentationLayout { } } +#[derive(Clone,Debug)] +pub struct InstanceAccess<'i, Id> { + pub raw_token: &'i RawTokenVal, + pub i: InstanceAccessDetails, +} + +impl<'r, Id> FromFormValue<'r> for InstanceAccess<'r, Id> + where Id : AccessId, OE : From +{ + type Error = OE; + #[throws(OE)] + fn from_form_value(param: &'r RawStr) -> Self { + let g = Id::global_tokens(PRIVATE_Y).read().unwrap(); + let token = RawTokenVal::from_str(param.as_str()); + let i = g.get(token).ok_or(Id::ERROR)?; + InstanceAccess { raw_token : token, i : i.clone() } + } +} + + #[derive(Debug,Serialize,Deserialize)] struct ApiPiece { ctoken : RawToken, @@ -83,57 +78,6 @@ trait ApiPieceOp : Debug { } } -#[derive(Error,Debug)] -pub enum ApiPieceOpError { - ReportViaResponse(#[from] OnlineError), - ReportViaUpdate(#[from] PieceOpError), - PartiallyProcessed(PieceOpError, Vec), -} -display_as_debug!(ApiPieceOpError); - -impl From for ApiPieceOpError { - fn from(x: PlayerNotFound) -> ApiPieceOpError { - ApiPieceOpError::ReportViaResponse(x.into()) - } -} - -pub trait Lens : Debug { - fn pieceid2visible(&self, piece: PieceId) -> VisiblePieceId; - fn log_pri(&self, piece: PieceId, pc: &PieceState) - -> PieceRenderInstructions; - fn svg_pri(&self, piece: PieceId, pc: &PieceState, player: PlayerId) - -> PieceRenderInstructions; - fn massage_prep_piecestate(&self, ns : &mut PreparedPieceState); - fn decode_visible_pieceid(&self, vpiece: VisiblePieceId, player: PlayerId) - -> PieceId; -} -#[derive(Debug)] -pub struct TransparentLens { - // when lenses become nontrivial, make this nonconstructable - // to find all the places where a TransparentLens was bodged -} -impl Lens for TransparentLens { - fn pieceid2visible(&self, piece: PieceId) -> VisiblePieceId { - let kd : slotmap::KeyData = piece.into(); - VisiblePieceId(kd) - } - fn log_pri(&self, piece: PieceId, pc: &PieceState) - -> PieceRenderInstructions { - let id = self.pieceid2visible(piece); - PieceRenderInstructions { id, face : pc.face } - } - fn svg_pri(&self, piece: PieceId, pc: &PieceState, _player: PlayerId) - -> PieceRenderInstructions { - self.log_pri(piece, pc) - } - fn decode_visible_pieceid(&self, vpiece: VisiblePieceId, _player: PlayerId) - -> PieceId { - let kd : slotmap::KeyData = vpiece.into(); - PieceId::from(kd) - } - fn massage_prep_piecestate(&self, _ns : &mut PreparedPieceState) { } -} - impl From<&OnlineError> for rocket::http::Status { fn from(oe: &OnlineError) -> rocket::http::Status { use OnlineError::*; diff --git a/src/cmdlistener.rs b/daemon/cmdlistener.rs similarity index 93% rename from src/cmdlistener.rs rename to daemon/cmdlistener.rs index 892bc520..bb1ecec7 100644 --- a/src/cmdlistener.rs +++ b/daemon/cmdlistener.rs @@ -4,7 +4,9 @@ // management API implementation -use crate::imports::*; +use super::*; + +use authproofs::*; // ---------- newtypes, type aliases, basic definitions ---------- @@ -13,9 +15,6 @@ use std::os::unix::io::AsRawFd; use std::os::unix::net::UnixListener; use uds::UnixStreamExt; -pub use crate::from_instance_lock_error; -pub use std::os::unix::net::UnixStream; - type SE = SpecError; type CSE = anyhow::Error; @@ -23,7 +22,6 @@ use MgmtCommand::*; use MgmtResponse::*; type ME = MgmtError; -from_instance_lock_error!{MgmtError} type AS = AccountScope; type TP = TablePermission; @@ -34,8 +32,6 @@ const CREATE_PIECES_MAX: u32 = 300; const DEFAULT_POS_START: Pos = PosC([20,20]); const DEFAULT_POS_DELTA: Pos = PosC([5,5]); -pub type AuthorisationSuperuser = Authorisation; - pub struct CommandListener { listener: UnixListener, } @@ -156,7 +152,7 @@ fn execute(cs: &mut CommandStream, cmd: MgmtCommand) -> MgmtResponse { let mut games = games_lock(); let auth = authorise_by_account(cs, &ag, &game)?; - let gs = crate::gamestate::GameState { + let gs = otter::gamestate::GameState { table_colour: Html::lit("green"), table_size: DEFAULT_TABLE_SIZE, pieces: default(), @@ -908,6 +904,12 @@ impl CommandListener { #[error("connection euid lookup failed (at connection initiation): {0}")] pub struct ConnectionEuidDiscoverEerror(String); +impl From for AuthorisationError { + fn from(e: ConnectionEuidDiscoverEerror) -> AuthorisationError { + AuthorisationError(format!("{}", e)) + } +} + impl CommandStream<'_> { #[throws(AuthorisationError)] fn authorised_uid(&self, wanted: Option, xinfo: Option<&str>) @@ -1172,87 +1174,3 @@ fn do_authorise_scope(cs: &CommandStream, wanted: &AccountScope) } } - -use authproofs::*; -use authproofs::AuthorisationError; - -pub use authproofs::Authorisation; -pub use authproofs::Unauthorised; - -mod authproofs { - use crate::imports::*; - - #[derive(Copy,Clone,Debug)] - pub struct Global; - - #[derive(Debug,Copy,Clone)] - pub struct Unauthorised (T, PhantomData); - impl Unauthorised { - pub fn of(t: T) -> Self { Unauthorised(t, PhantomData) } - pub fn by(self, _auth: Authorisation) -> T { self.0 } - pub fn by_ref(&self, _auth: Authorisation) -> & T { & self.0 } - pub fn by_mut(&mut self, _auth: Authorisation) -> &mut T { &mut self.0 } - } - impl From for Unauthorised { - fn from(t: T) -> Self { Self::of(t) } - } - - #[derive(Error,Debug)] - #[error("internal AuthorisationError {0}")] - pub struct AuthorisationError(pub String); - - #[derive(Debug)] - pub struct Authorisation (PhantomData<*const A>); - impl Clone for Authorisation { fn clone(&self) -> Self { *self } } - impl Copy for Authorisation { } - - impl Authorisation { - pub const fn authorised(_v: &T) -> Authorisation { - Authorisation(PhantomData) - } - pub fn map(self, _f: fn(&T) -> &U) -> Authorisation { - self.therefore_ok() - } - pub fn therefore_ok(self) -> Authorisation { - Authorisation(PhantomData) - } - pub const fn authorise_any() -> Authorisation { - Authorisation(PhantomData) - } - } - - impl From> for Authorisation { - // ^ we need a bound not met by Global or we conflict with From for T - fn from(global: Authorisation) -> Self { - global.therefore_ok() - } - } - - impl From for AuthorisationError { - fn from(a: anyhow::Error) -> AuthorisationError { - AuthorisationError(format!("{}", a)) - } - } - impl From for AuthorisationError { - fn from(e: ConnectionEuidDiscoverEerror) -> AuthorisationError { - AuthorisationError(format!("{}", e)) - } - } - - pub trait AuthorisationCombine: Sized { - type Output; - fn combine(self) -> Authorisation { - Authorisation(PhantomData) - } - } - impl AuthorisationCombine - for (Authorisation, Authorisation) { - type Output = (A, B); - } - impl AuthorisationCombine - for (Authorisation, Authorisation, Authorisation) { - type Output = (A, B, C); - } -} - - diff --git a/daemon/main.rs b/daemon/main.rs index 7626d799..23730617 100644 --- a/daemon/main.rs +++ b/daemon/main.rs @@ -4,8 +4,30 @@ #![feature(proc_macro_hygiene, decl_macro)] +pub mod api; +pub mod cmdlistener; +pub mod session; + +pub use rocket::http::Status; +pub use rocket::http::{ContentType, RawStr}; +pub use rocket::request::Request; +pub use rocket::request::{FromFormValue, FromParam, FromRequest, LenientForm}; +pub use rocket::response; +pub use rocket::response::NamedFile; +pub use rocket::response::{Responder, Response}; +pub use rocket::{get, post, routes}; +pub use rocket::{Rocket, State}; +pub use rocket_contrib::helmet::*; +pub use rocket_contrib::json::Json; +pub use rocket_contrib::templates::tera::{self, Value}; +pub use rocket_contrib::templates::Engines; +pub use rocket_contrib::templates::Template; + +pub use crate::api::{AbbrevPresentationLayout}; +pub use crate::api::{ApiPieceOpError, Lens, TransparentLens}; +pub use crate::cmdlistener::*; + use rocket::fairing; -use rocket::{get, routes}; use rocket::response::Content; use rocket_contrib::serve::StaticFiles; @@ -295,8 +317,8 @@ fn main() { r = r.attach(ReportStartup); } - let r = otter::session::mount(r); - let r = otter::api::mount(r); + let r = crate::session::mount(r); + let r = crate::api::mount(r); thread::spawn(client_periodic_expiry); thread::spawn(logs_periodic_expiry); diff --git a/src/session.rs b/daemon/session.rs similarity index 99% rename from src/session.rs rename to daemon/session.rs index 9ed70cf0..db515a20 100644 --- a/src/session.rs +++ b/daemon/session.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: AGPL-3.0-or-later // There is NO WARRANTY. -use crate::imports::*; +use super::*; #[derive(Serialize,Debug)] struct SessionRenderContext { diff --git a/src/accounts.rs b/src/accounts.rs index 38fa32aa..1ede9b9c 100644 --- a/src/accounts.rs +++ b/src/accounts.rs @@ -6,6 +6,8 @@ use crate::imports::*; use parking_lot::{Mutex, const_mutex, MutexGuard}; +use authproofs::*; + //---------- simple types ---------- slotmap::new_key_type!{ @@ -433,6 +435,7 @@ pub fn load_accounts() { pub mod loaded_acl { use crate::imports::*; + use authproofs::*; pub trait Perm : FromPrimitive + ToPrimitive + Copy + Eq + Hash + Sync + Send + 'static diff --git a/src/authproofs.rs b/src/authproofs.rs new file mode 100644 index 00000000..c4728385 --- /dev/null +++ b/src/authproofs.rs @@ -0,0 +1,74 @@ +// Copyright 2020 Ian Jackson +// SPDX-License-Identifier: AGPL-3.0-or-later +// There is NO WARRANTY. + +use crate::imports::*; + +#[derive(Copy,Clone,Debug)] +pub struct Global; + +#[derive(Debug,Copy,Clone)] +pub struct Unauthorised (T, PhantomData); +impl Unauthorised { + pub fn of(t: T) -> Self { Unauthorised(t, PhantomData) } + pub fn by(self, _auth: Authorisation) -> T { self.0 } + pub fn by_ref(&self, _auth: Authorisation) -> & T { & self.0 } + pub fn by_mut(&mut self, _auth: Authorisation) -> &mut T { &mut self.0 } +} +impl From for Unauthorised { + fn from(t: T) -> Self { Self::of(t) } +} + +#[derive(Error,Debug)] +#[error("internal AuthorisationError {0}")] +pub struct AuthorisationError(pub String); + +#[derive(Debug)] +pub struct Authorisation (PhantomData<*const A>); +impl Clone for Authorisation { fn clone(&self) -> Self { *self } } +impl Copy for Authorisation { } + +pub type AuthorisationSuperuser = Authorisation; + +impl Authorisation { + pub const fn authorised(_v: &T) -> Authorisation { + Authorisation(PhantomData) + } + pub fn map(self, _f: fn(&T) -> &U) -> Authorisation { + self.therefore_ok() + } + pub fn therefore_ok(self) -> Authorisation { + Authorisation(PhantomData) + } + pub const fn authorise_any() -> Authorisation { + Authorisation(PhantomData) + } +} + +impl From> for Authorisation { + // ^ we need a bound not met by Global or we conflict with From for T + fn from(global: Authorisation) -> Self { + global.therefore_ok() + } +} + +impl From for AuthorisationError { + fn from(a: anyhow::Error) -> AuthorisationError { + AuthorisationError(format!("{}", a)) + } +} + +pub trait AuthorisationCombine: Sized { + type Output; + fn combine(self) -> Authorisation { + Authorisation(PhantomData) + } +} +impl AuthorisationCombine + for (Authorisation, Authorisation) { + type Output = (A, B); +} +impl AuthorisationCombine + for (Authorisation, Authorisation, Authorisation) { + type Output = (A, B, C); +} diff --git a/src/error.rs b/src/error.rs index e0c93066..3f786208 100644 --- a/src/error.rs +++ b/src/error.rs @@ -74,6 +74,21 @@ impl From for SpecError { SpecError::InternalError(format!("{:?}", ie)) } } + +#[derive(Error,Debug)] +pub enum ApiPieceOpError { + ReportViaResponse(#[from] OnlineError), + ReportViaUpdate(#[from] PieceOpError), + PartiallyProcessed(PieceOpError, Vec), +} +display_as_debug!(ApiPieceOpError); + +impl From for ApiPieceOpError { + fn from(x: PlayerNotFound) -> ApiPieceOpError { + ApiPieceOpError::ReportViaResponse(x.into()) + } +} + #[derive(Error,Debug,Serialize,Clone)] pub enum ErrorSignaledViaUpdate { InternalError, diff --git a/src/global.rs b/src/global.rs index 2c07faea..df1d1324 100644 --- a/src/global.rs +++ b/src/global.rs @@ -168,12 +168,6 @@ pub struct InstanceAccessDetails { pub acctid: AccountId, } -#[derive(Clone,Debug)] -pub struct InstanceAccess<'i, Id> { - pub raw_token: &'i RawTokenVal, - pub i: InstanceAccessDetails, -} - // ========== internal data structures ========== lazy_static! { @@ -225,6 +219,7 @@ display_as_debug!{InstanceLockError} impl From> for InstanceLockError { fn from(_: PoisonError) -> Self { Self::GameCorrupted } } +from_instance_lock_error!{MgmtError} pub struct PrivateCaller(()); // outsiders cannot construct this @@ -1144,19 +1139,6 @@ pub fn lookup_token(s : &RawTokenVal) .ok_or(Id::ERROR) } -impl<'r, Id> FromFormValue<'r> for InstanceAccess<'r, Id> - where Id : AccessId, OE : From -{ - type Error = OE; - #[throws(OE)] - fn from_form_value(param: &'r RawStr) -> Self { - let g = Id::global_tokens(PRIVATE_Y).read().unwrap(); - let token = RawTokenVal::from_str(param.as_str()); - let i = g.get(token).ok_or(Id::ERROR)?; - InstanceAccess { raw_token : token, i : i.clone() } - } -} - #[throws(OE)] pub fn record_token ( ig: &mut InstanceGuard, diff --git a/src/imports.rs b/src/imports.rs index 6cf4562a..394f3659 100644 --- a/src/imports.rs +++ b/src/imports.rs @@ -28,6 +28,7 @@ pub use std::num::{TryFromIntError, Wrapping}; pub use std::ops::{Deref, DerefMut}; pub use std::os::unix; pub use std::os::unix::ffi::OsStrExt; +pub use std::os::unix::net::UnixStream; pub use std::os::unix::process::CommandExt; pub use std::path::PathBuf; pub use std::process::{exit, Command}; @@ -64,20 +65,6 @@ pub use rand::distributions::Alphanumeric; pub use rand::thread_rng; pub use rand::Rng; pub use regex::Regex; -pub use rocket::http::Status; -pub use rocket::http::{ContentType, RawStr}; -pub use rocket::request::Request; -pub use rocket::request::{FromFormValue, FromParam, FromRequest, LenientForm}; -pub use rocket::response; -pub use rocket::response::NamedFile; -pub use rocket::response::{Responder, Response}; -pub use rocket::{get, post, routes}; -pub use rocket::{Rocket, State}; -pub use rocket_contrib::helmet::*; -pub use rocket_contrib::json::Json; -pub use rocket_contrib::templates::tera::{self, Value}; -pub use rocket_contrib::templates::Engines; -pub use rocket_contrib::templates::Template; pub use serde::ser::SerializeTuple; pub use serde::{de::DeserializeOwned, Deserialize, Serialize}; pub use serde::{Deserializer, Serializer}; @@ -89,11 +76,12 @@ pub use thiserror::Error; pub use vecdeque_stableix::Deque as StableIndexVecDeque; pub use zcoord::{self, ZCoord}; +pub use crate::from_instance_lock_error; + pub use crate::accounts::loaded_acl::{self, EffectiveACL, LoadedAcl, PermSet}; pub use crate::accounts::*; -pub use crate::api::{AbbrevPresentationLayout, PresentationLayout}; -pub use crate::api::{ApiPieceOpError, Lens, TransparentLens}; -pub use crate::cmdlistener::*; +pub use crate::authproofs::{self, Authorisation, Unauthorised}; +pub use crate::authproofs::AuthorisationSuperuser; pub use crate::commands::*; pub use crate::config::*; pub use crate::debugreader::DebugReader; @@ -101,6 +89,7 @@ pub use crate::error::*; pub use crate::gamestate::*; pub use crate::global::*; pub use crate::keydata::*; +pub use crate::lens::*; pub use crate::mgmtchannel::*; pub use crate::nwtemplates; pub use crate::pieces::*; @@ -112,6 +101,7 @@ pub use crate::toml_de; pub use crate::tz::*; pub use crate::updates::*; pub use crate::utils::*; +pub use crate::ui::*; pub type SecondarySlotMap = slotmap::secondary::SecondaryMap; pub type SvgData = Vec; diff --git a/src/lens.rs b/src/lens.rs new file mode 100644 index 00000000..f5bd2f38 --- /dev/null +++ b/src/lens.rs @@ -0,0 +1,43 @@ +// Copyright 2020 Ian Jackson +// SPDX-License-Identifier: AGPL-3.0-or-later +// There is NO WARRANTY. + +use crate::imports::*; + +pub trait Lens : Debug { + fn pieceid2visible(&self, piece: PieceId) -> VisiblePieceId; + fn log_pri(&self, piece: PieceId, pc: &PieceState) + -> PieceRenderInstructions; + fn svg_pri(&self, piece: PieceId, pc: &PieceState, player: PlayerId) + -> PieceRenderInstructions; + fn massage_prep_piecestate(&self, ns : &mut PreparedPieceState); + fn decode_visible_pieceid(&self, vpiece: VisiblePieceId, player: PlayerId) + -> PieceId; +} +#[derive(Debug)] +pub struct TransparentLens { + // when lenses become nontrivial, make this nonconstructable + // to find all the places where a TransparentLens was bodged +} +impl Lens for TransparentLens { + fn pieceid2visible(&self, piece: PieceId) -> VisiblePieceId { + let kd : slotmap::KeyData = piece.into(); + VisiblePieceId(kd) + } + fn log_pri(&self, piece: PieceId, pc: &PieceState) + -> PieceRenderInstructions { + let id = self.pieceid2visible(piece); + PieceRenderInstructions { id, face : pc.face } + } + fn svg_pri(&self, piece: PieceId, pc: &PieceState, _player: PlayerId) + -> PieceRenderInstructions { + self.log_pri(piece, pc) + } + fn decode_visible_pieceid(&self, vpiece: VisiblePieceId, _player: PlayerId) + -> PieceId { + let kd : slotmap::KeyData = vpiece.into(); + PieceId::from(kd) + } + fn massage_prep_piecestate(&self, _ns : &mut PreparedPieceState) { } +} + diff --git a/src/lib.rs b/src/lib.rs index f4e29f7c..3586336a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,8 +8,7 @@ #![allow(clippy::redundant_closure_call)] pub mod accounts; -pub mod api; -pub mod cmdlistener; +pub mod authproofs; pub mod commands; pub mod config; pub mod debugreader; @@ -18,15 +17,16 @@ pub mod gamestate; pub mod global; pub mod imports; pub mod keydata; +pub mod lens; pub mod mgmtchannel; pub mod nwtemplates; pub mod pieces; -pub mod session; pub mod shapelib; pub mod spec; pub mod sse; pub mod tz; pub mod updates; +pub mod ui; pub mod utils; #[path = "slotmap-slot-idx.rs"] pub mod slotmap_slot_idx; diff --git a/src/ui.rs b/src/ui.rs new file mode 100644 index 00000000..237afe60 --- /dev/null +++ b/src/ui.rs @@ -0,0 +1,33 @@ +// Copyright 2020 Ian Jackson +// SPDX-License-Identifier: AGPL-3.0-or-later +// There is NO WARRANTY. + +use crate::imports::*; + +#[derive(Clone,Copy,Debug,Eq,PartialEq,Serialize,Deserialize,EnumString)] +pub enum PresentationLayout { + Portrait, + Landscape, +} + +type PL = PresentationLayout; + +impl PresentationLayout { + pub fn template(self) -> &'static str { + match self { + PL::Portrait => "session", + PL::Landscape => "landscape", + } + } + pub fn abbreviate_timestamps(self) -> bool { + match self { + PL::Portrait => false, + PL::Landscape => true, + } + } +} + +impl Default for PresentationLayout { + fn default() -> Self { PL::Portrait } +} +