from_instance_lock_error!{MgmtError}
type AS = AccountScope;
+type TP = TablePermission;
const USERLIST : &str = "/etc/userlist";
const CREATE_PIECES_MAX : u32 = 300;
struct AccountSpecified {
account: AccountName,
cooked: String, // account.to_string()
- auth: Authorisation<AccountScope>,
+ auth: Authorisation<AccountName>,
}
// ========== management API ==========
Noop => Fine,
SetAccount(wanted_account) => {
- let authorised = authorise_scope_direct(cs, &wanted_account.scope)?;
- cs.account = Some((
- wanted_account,
- authorised.therefore_ok(),
- ));
+ let auth = authorise_scope_direct(cs, &wanted_account.scope)?;
+ cs.account = Some(AccountSpecified {
+ account: wanted_account,
+ cooked: wanted_account.to_string(),
+ auth: auth.therefore_ok(),
+ });
Fine
},
CreateGame { game, insns } => {
- let authorised = authorise_by_account(cs, &game.scope)?;
+ let authorised = authorise_by_account(cs, &game)?;
let gs = crate::gamestate::GameState {
table_size : DEFAULT_TABLE_SIZE,
max_z: default(),
};
- let gref = Instance::new(game, gs, authorised)?;
+ let acl = default();
+ let gref = Instance::new(game, gs, acl, authorised)?;
let ig = gref.lock()?;
execute_for_game(cs, &mut Unauthorised::of(ig),
ListGames { all } => {
let (scope, auth) = if all == Some(true) {
let auth = authorise_scope_direct(cs, &AS::Server)?;
- (None, auth)
+ (None, auth.therefore_ok())
} else {
- let (account, auth) = cs.account.as_ref()
+ let AccountSpecified { account, auth, .. } = cs.account.as_ref()
.ok_or(ME::SpecifyAccount)?;
- (Some(&account.scope), *auth)
+ (Some(account), *auth)
};
let mut games = Instance::list_names(scope, auth);
games.sort_unstable();
MgmtGameResponse,
);
-#[throws(ME)]
+//#[throws(ME)]
fn execute_game_insn(cs: &CommandStream,
ig: &mut Unauthorised<InstanceGuard, InstanceName>,
update: MgmtGameInstruction)
- -> ExecuteGameInsnResults {
+ -> Result<ExecuteGameInsnResults,ME> {
type U = ExecuteGameChangeUpdates;
use MgmtGameInstruction::*;
use MgmtGameResponse::*;
type Resp = MgmtGameResponse;
let who = &cs.who;
- fn readonly<F: FnOnce(&mut InstanceGuard) -> ExecuteGameInsnResults>(
- cs: &CommandStream,
- ig: &Unauthorised<InstanceGuard, InstanceName>,
- p: PermSet<TablePermission>,
- f: F) -> ExecuteGameInsnResults
+ fn readonly<F: FnOnce(&InstanceGuard) -> MgmtGameResponse,
+ P: Into<PermSet<TablePermission>>>
+
+ (
+ cs: &CommandStream,
+ ig: &Unauthorised<InstanceGuard, InstanceName>,
+ p: P,
+ f: F
+ ) -> ExecuteGameInsnResults
{
- let ig = ig.check_acl(&cs.account.as_ref()?.cooked)?;
+ let ig = cs.check_acl(p)?;
let resp = f(ig);
(U{ pcs: vec![], log: vec![], raw: None }, resp)
}
- match update {
- Noop { } => readonly(ig, Fine),
+ let y = match update {
+ Noop { } => readonly(cs,ig, &[], |ig| Fine),
Insn::SetTableSize(size) => {
+ let ig = cs.check_acl(ig, &[TP::ChangePieces])?;
ig.gs.table_size = size;
(U{ pcs: vec![],
log: vec![ LogEntry {
Fine)
}
- Insn::AddPlayer(pl) => {
- if ig.gs.players.values().any(|p| p.nick == pl.st.nick) {
- Err(ME::AlreadyExists)?;
- }
+ Insn::AddPlayer(add) => {
+ // todo some kind of permissions check for player too
+ let ig = cs.check_acl(ig, &[TP::AddPlayer])?;
+ let nick = add.nick.ok_or(ME::ParameterMissing)?;
let logentry = LogEntry {
html: Html(format!("{} added a player: {}", &who,
- htmlescape::encode_minimal(&pl.st.nick))),
+ htmlescape::encode_minimal(&nick))),
};
- let timezone = pl.timezone.as_ref().map(String::as_str)
+ let timezone = add.timezone.as_ref().map(String::as_str)
.unwrap_or("");
let tz = match Timezone::from_str(timezone) {
Ok(tz) => tz,
Err(x) => match x { },
};
- let (player, logentry) = ig.player_new(pl.st, tz, logentry)?;
+ let st = PlayerState {
+ tz,
+ account: add.account,
+ nick: nick.to_string(),
+ };
+ let (player, logentry) = ig.player_new(st, tz, logentry)?;
(U{ pcs: vec![],
log: vec![ logentry ],
raw: None },
Resp::AddPlayer(player))
},
- Insn::ListPieces => readonly(ig, {
+ Insn::ListPieces => readonly(cs,ig, &[TP::ViewPublic], |ig|{
// xxx put something in log
let pieces = ig.gs.pieces.iter().map(|(piece,p)|{
let &PieceState { pos, face, .. } = p;
- let pinfo = ig.pieces.get(piece)?;
+ let pinfo = ig.ipieces.get(piece)?;
let desc_html = pinfo.describe_html(None);
let itemname = pinfo.itemname().to_string();
let bbox = pinfo.bbox_approx();
- Some(MgmtGamePieceInfo { piece, pos, face, desc_html, bbox, itemname })
+ let lens = TransparentLens { };
+ Some(MgmtGamePieceInfo {
+ piece, itemname,
+ visible:
+ if let TransparentLens { } = lens {
+ Some(MgmtGamePieceVisibleInfo {
+ pos, face, desc_html, bbox
+ })
+ } else {
+ None
+ }
+ })
}).flatten().collect();
Resp::Pieces(pieces)
}),
- RemovePlayer(player) => {
- let old_state = ig.player_remove(player)?;
+ RemovePlayer(account) => {
+ // todo let you remove yourself unconditionally
+ let ig = cs.check_acl(ig, &[TP::RemovePlayer])?;
+ let player = ig.gs.players.iter()
+ .filter_map(|(k,v)| if v == &account { Some(k) } else { None })
+ .next()
+ .ok_or(ME::PlayerNotFound)?;
+ let (_, old_state) = ig.player_remove(player)?;
(U{ pcs: vec![],
log: old_state.iter().map(|pl| LogEntry {
html: Html(format!("{} removed a player: {}", &who,
Fine)
},
- Insn::Info => readonly(ig, {
- let players = ig.gs.players.clone();
+ Insn::Info => readonly(cs,ig, &[TP::ViewPublic], |ig|{
+ let players = ig.gs.players.iter().map(
+ |(player, &account)| {
+ let account = account.clone();
+ let info = match ig.iplayers.get(player) {
+ Some(pl) => MgmtPlayerInfo {
+ account,
+ nick: pl.pst.nick.clone(),
+ },
+ None => MgmtPlayerInfo {
+ account,
+ nick: format!("<missing from iplayers table!>"),
+ },
+ };
+ (player, info)
+ }).collect();
let table_size = ig.gs.table_size;
let info = MgmtGameResponseGameInfo { table_size, players };
Resp::Info(info)
}),
ResetPlayerAccess(player) => {
- let token = ig.players_access_reset(player)?;
+ let token = ig.player_access_reset(player)?;
(U{ pcs: vec![],
log: vec![],
raw: None },
}
RedeliverPlayerAccess(player) => {
- let token = ig.players_access_redeliver(player)?;
+ let token = ig.player_access_redeliver(player)?;
(U{ pcs: vec![],
log: vec![],
raw: None },
raw: None },
Fine)
},
- }
+ };
+ Ok(y)
}
// ---------- how to execute game commands & handle their updates ----------
#[throws(MgmtError)]
- pub fn check_acl(&mut self,
- ig: &mut Unauthorised<InstanceRef, InstanceName>,
- p: PermSet<TablePermission>) -> &mut InstanceGuard {
+ pub fn check_acl<P: Into<PermSet<TablePermission>>>(
+ &mut self,
+ ig: &mut Unauthorised<InstanceGuard, InstanceName>,
+ p: P,
+ ) -> &mut InstanceGuard {
+ let p = p.into();
let auth = {
+ let subject = &self.account.as_ref()?.cooked;
let acl = self.by(Authorisation::authorise_any()).acl;
let eacl = EffectiveAcl {
owner_account : &self.account?.to_string(),
acl : &acl,
};
- eacl.check(p)?;
+ eacl.check(subject, p)?
};
self.by_mut(auth);
}
}
#[throws(MgmtError)]
-fn authorise_by_account(cs: &CommandStream, wanted: &AccountScope)
- -> Authorisation<AccountScope> {
+fn authorise_by_account(cs: &CommandStream, wanted: &InstanceName)
+ -> Authorisation<InstanceName> {
let currently = &cs.account.as_ref()?.account;
- if currently == wanted {
- return Authorisation::authorised(currently);
+ if currently == wanted.account {
+ return Authorisation::authorised(wanted);
}
throw!(MgmtError::AuthorisationError)
}
#[error("internal AuthorisationError {0}")]
pub struct AuthorisationError(pub String);
+ #[derive(Debug,Copy,Clone)]
pub struct Authorisation<A> (PhantomData<A>);
impl<T> Authorisation<T> {
#[derive(Clone,Debug,Hash,Eq,PartialEq,Ord,PartialOrd)]
pub struct InstanceName {
- account: AccountName,
- game: String,
+ pub account: AccountName,
+ pub game: String,
}
#[derive(Debug,Clone)]
ipieces: PiecesLoadedRef,
tokens_players: Vec<(RawTokenStr, PlayerId)>,
aplayers: SecondarySlotMap<PlayerId, PlayerState>,
+ acl: Acl<TablePermission>,
}
display_as_debug!{InstanceLockError}
#[throws(MgmtError)]
pub fn lookup_by_name_unauth(name: &InstanceName)
- -> (Unauthorised<InstanceRef, InstanceName>,
- &InstanceName)
+ -> Unauthorised<InstanceRef, InstanceName>
{
- (Unauthorised::of(
+ Unauthorised::of(
GLOBAL.games.read().unwrap()
.get(name)
.ok_or(MgmtError::GameNotFound)?
.clone()
.into()
- ),
- name)
+ )
}
#[throws(MgmtError)]
pub fn lookup_by_name(name: &InstanceName, auth: Authorisation<InstanceName>)
-> InstanceRef {
- Self::lookup_by_name_unauth(name)?.0.by(auth)
+ Self::lookup_by_name_unauth(name)?.by(auth)
}
#[throws(InternalError)]
|(player, PlayerRecord { pst, .. })|
(player, pst.clone())
).collect();
- let isa = InstanceSaveAccesses { ipieces, tokens_players, aplayers };
+ let acl = s.c.g.acl.into();
+ let isa = InstanceSaveAccesses {
+ ipieces, tokens_players, aplayers, acl
+ };
rmp_serde::encode::write_named(w, &isa)
})?;
self.c.access_dirty = false;
}
#[throws(StartupError)]
- fn load_game(name: InstanceName) -> InstanceRef {
+ fn load_game(name: InstanceName) -> Option<InstanceRef> {
{
let mut st = GLOBAL.save_area_lock.lock().unwrap();
let st = &mut *st;
})().context(lockfile).context("lock global save area")?);
}
}
+
let InstanceSaveAccesses::<String,ActualPiecesLoaded>
- { mut tokens_players, mut ipieces, mut aplayers }
- = Self::load_something(&name, "a-")
- .or_else(|e| {
- if let InternalError::Anyhow(ae) = &e {
- if let Some(ioe) = ae.downcast_ref::<io::Error>() {
- if ioe.kind() == io::ErrorKind::NotFound {
- return Ok(Default::default())
- }
- }
- }
- Err(e)
- })?;
+ { mut tokens_players, mut ipieces, mut aplayers, acl }
+ = match Self::load_something(&name, "a-") {
+ Ok(data) => data,
+ Err(e) => if (||{
+ let ae = match &e { InternalError::Anyhow(ae) => Some(ae), _=>None }?;
+ let ioe = ae.downcast_ref::<io::Error>()?;
+ let is_enoent = ioe.kind() == io::ErrorKind::NotFound;
+ is_enoent.as_option()
+ })().is_some() {
+ return None;
+ } else {
+ throw!(e);
+ },
+ };
let mut gs : GameState = Self::load_something(&name, "g-")?;
let g = Instance {
gs, iplayers,
+ acl: acl.into(),
ipieces: PiecesLoaded(ipieces),
name: name.clone(),
clients : Default::default(),
drop(g);
GLOBAL.games.write().unwrap().insert(name.clone(), gref.clone());
info!("loadewd {:?}", &name);
- gref
+ Some(gref)
}
}