--- /dev/null
+// Copyright 2020 Ian Jackson
+// SPDX-License-Identifier: AGPL-3.0-or-later
+// There is NO WARRANTY.
+
+use crate::imports::*;
+
+#[derive(Debug,Clone,Deserialize,Serialize)]
+#[derive(Eq,PartialEq,Ord,PartialOrd,Hash)]
+pub enum AccountScope {
+ Server,
+ Unix { user : String },
+}
+
+type AS = AccountScope;
+
+#[derive(Debug)]
+#[derive(Eq,PartialEq,Ord,PartialOrd,Hash)]
+#[derive(DeserializeFromStr,SerializeDisplay)]
+pub struct ScopedName {
+ pub scope: AccountScope,
+ pub scoped_name: String,
+}
+
+impl Display for ScopedName {
+ #[throws(fmt::Error)]
+ /// Return value is parseable but not filesystem- or html-safe
+ fn fmt(&self, f: &mut fmt::Formatter) {
+ match &self.scope {
+ AS::Server => {
+ write!(f, "server")?
+ },
+ AS::Unix { user } => {
+ assert_eq!(user.find('.'), None);
+ write!(f, "unix:{}", user)?;
+ },
+ };
+ match self.scoped_name.as_str() {
+ "" => {},
+ suffix => write!(f, ":{}", &suffix)?,
+ };
+ }
+}
+
+#[derive(Error,Debug)]
+pub enum InvalidScopedName {
+ #[error("Unknown scope kind (expected unix or server)")]
+ UnknownScopeKind,
+}
+
+impl FromStr for ScopedName {
+ type Err = InvalidScopedName;
+
+ #[throws(InvalidScopedName)]
+ fn from_str(s: &str) -> Self {
+ let (kind, rhs) = s.split_at_delim(':');
+ let (scope, scoped_name) = match kind {
+ "server" => {
+ (AccountScope::Server, rhs)
+ },
+ "unix" => {
+ let (user, rhs) = s.split_at_delim(':');
+ let user = user.to_owned();
+ (AccountScope::Unix { user }, rhs)
+ },
+ _ => {
+ throw!(InvalidScopedName::UnknownScopeKind)
+ },
+ };
+ let scoped_name = scoped_name.to_string();
+ ScopedName { scope, scoped_name }
+ }
+}
type E = anyhow::Error;
type Insn = MgmtGameInstruction;
type Resp = MgmtGameResponse;
+type AS = AccountScope;
use argparse::action::ParseResult::Parsed;
#[derive(Debug,Default)]
struct MainOpts {
- scope: Option<ManagementScope>,
+ scope: Option<AccountScope>,
socket_path: Option<String>,
verbose: i32,
}
let mut scope = ap.refer(&mut ma.opts.scope);
scope.add_option(&["--scope-server"],
- StoreConst(Some(ManagementScope::Server)),
+ StoreConst(Some(AS::Server)),
"use Server scope");
scope.metavar("USER").add_option(&["--scope-unix-user"],
- MapStore(|user| Ok(Some(ManagementScope::Unix {
+ MapStore(|user| Ok(Some(AS::Unix {
user: user.into()
}))),
"use specified unix user scope");
let user = env::var("USER").map_err(|e| ArgumentParseError(
format!("--scope-unix needs USER env var: {}", &e)
))?;
- *scope = Some(ManagementScope::Unix { user });
+ *scope = Some(AS::Unix { user });
}
if ma.config_filename.is_some() || ma.opts.socket_path.is_none() {
ServerConfig::read(ma.config_filename.as_ref().map(String::as_str))
type ME = MgmtError;
from_instance_lock_error!{MgmtError}
+type AS = AccountScope;
+
const USERLIST : &str = "/etc/userlist";
const CREATE_PIECES_MAX : u32 = 300;
max_z: default(),
};
- let name = InstanceName {
+ let name = ScopedName {
scope : cs.get_scope()?.clone(),
scoped_name : name,
};
ListGames { all } => {
let scope = if all == Some(true) {
let _authorise : AuthorisedSatisfactory =
- authorise_scope(cs, &ManagementScope::Server)?;
+ authorise_scope(cs, &AS::Server)?;
None
} else {
let scope = cs.get_scope()?;
},
MgmtCommand::AlterGame { name, insns, how} => {
- let name = InstanceName {
+ let name = ScopedName {
scope: cs.get_scope()?.clone(),
scoped_name: name
};
SetFixedPlayerAccess { player, token } => {
let authorised : AuthorisedSatisfactory =
- authorise_scope(cs, &ManagementScope::Server)?;
+ authorise_scope(cs, &AS::Server)?;
let authorised = match authorised.into_inner() {
- ManagementScope::Server => Authorised::<RawToken>::authorise(),
+ AS::Server => Authorised::<RawToken>::authorise(),
_ => panic!(),
};
ig.player_access_register_fixed(
struct CommandStream<'d> {
euid : Result<Uid, ConnectionEuidDiscoverEerror>,
desc : &'d str,
- scope : Option<ManagementScope>,
+ scope : Option<AccountScope>,
chan : MgmtChannel,
who: Who,
}
}
#[throws(MgmtError)]
- fn get_scope(&self) -> &ManagementScope {
+ fn get_scope(&self) -> &AccountScope {
self.scope.as_ref().ok_or(NoScope)?
}
}
}
#[throws(MgmtError)]
-fn authorise_scope(cs: &CommandStream, wanted: &ManagementScope)
+fn authorise_scope(cs: &CommandStream, wanted: &AccountScope)
-> AuthorisedSatisfactory {
do_authorise_scope(cs, wanted)
.map_err(|e| cs.map_auth_err(e))?
}
#[throws(AuthorisationError)]
-fn do_authorise_scope(cs: &CommandStream, wanted: &ManagementScope)
+fn do_authorise_scope(cs: &CommandStream, wanted: &AccountScope)
-> AuthorisedSatisfactory {
- type AS<T> = (T, ManagementScope);
+ type AS<T> = (T, AccountScope);
match &wanted {
- ManagementScope::Server => {
+ AccountScope::Server => {
let y : AS<
Authorised<(Passwd,Uid)>,
> = {
let ok = cs.authorised_uid(None,None)?;
(ok,
- ManagementScope::Server)
+ AccountScope::Server)
};
return y.into()
},
- ManagementScope::Unix { user: wanted } => {
+ AccountScope::Unix { user: wanted } => {
let y : AS<
Authorised<(Passwd,Uid)>,
> = {
let info = xinfo.as_deref();
let ok = cs.authorised_uid(authorised_for, info)?;
(ok,
- ManagementScope::Unix { user: pwent.name })
+ AccountScope::Unix { user: pwent.name })
};
y.into()
},
pub struct Authorised<A> (PhantomData<A>);
//struct AuthorisedScope<A> (Authorised<A>, ManagementScope);
- pub struct AuthorisedSatisfactory (ManagementScope);
+ pub struct AuthorisedSatisfactory (AccountScope);
impl AuthorisedSatisfactory {
- pub fn into_inner(self) -> ManagementScope { self.0 }
+ pub fn into_inner(self) -> AccountScope { self.0 }
}
impl<T> Authorised<T> {
pub fn authorise() -> Authorised<T> { Authorised(PhantomData) }
}
- impl<T> From<(Authorised<T>, ManagementScope)> for AuthorisedSatisfactory {
- fn from((_,s): (Authorised<T>, ManagementScope)) -> Self { Self(s) }
+ impl<T> From<(Authorised<T>, AccountScope)> for AuthorisedSatisfactory {
+ fn from((_,s): (Authorised<T>, AccountScope)) -> Self { Self(s) }
}
- impl<T,U> From<((Authorised<T>, Authorised<U>), ManagementScope)> for AuthorisedSatisfactory {
- fn from(((..),s): ((Authorised<T>, Authorised<U>), ManagementScope)) -> Self { Self(s) }
+ impl<T,U> From<((Authorised<T>, Authorised<U>), AccountScope)> for AuthorisedSatisfactory {
+ fn from(((..),s): ((Authorised<T>, Authorised<U>), AccountScope)) -> Self { Self(s) }
}
impl From<anyhow::Error> for AuthorisationError {
#[derive(Debug,Serialize,Deserialize)]
pub enum MgmtCommand {
Noop,
- SetScope(ManagementScope),
+ SetScope(AccountScope),
CreateGame { name: String, insns: Vec<MgmtGameInstruction> },
ListGames { all: Option<bool>, },
AlterGame {
// ---------- public data structure ----------
-#[derive(Debug,Serialize,Deserialize)]
-#[derive(Eq,PartialEq,Ord,PartialOrd,Hash)]
-pub struct InstanceName {
- pub scope: ManagementScope,
- pub scoped_name: String,
-}
+pub type InstanceName = ScopedName;
#[derive(Debug,Clone)]
pub struct InstanceRef (Arc<Mutex<InstanceContainer>>);
pub struct Pieces (pub(in crate::global) ActualPieces);
type ActualPieces = DenseSlotMap<PieceId,PieceState>;
-#[derive(Debug,Clone,Deserialize,Serialize)]
-#[derive(Eq,PartialEq,Ord,PartialOrd,Hash)]
-pub enum ManagementScope {
- Server,
- Unix { user : String /* username, so filename-safe */ },
-}
-
#[derive(Debug)]
pub struct Client {
pub player : PlayerId,
);
}
- pub fn list_names(scope: Option<&ManagementScope>)
+ pub fn list_names(scope: Option<&AccountScope>)
-> Vec<Arc<InstanceName>> {
let games = GLOBAL.games.read().unwrap();
let out : Vec<Arc<InstanceName>> =
}
fn savefilename(name: &InstanceName, prefix: &str, suffix: &str) -> String {
- let scope_prefix = { use ManagementScope::*; match &name.scope {
- Server => format!(""),
- Unix{user} => { format!("{}:", user) },
- } };
- [ config().save_directory.as_str(), &"/", prefix, scope_prefix.as_ref() ]
+ const ENCODE : percent_encoding::AsciiSet =
+ percent_encoding::NON_ALPHANUMERIC.remove(b':');
+
+ [ config().save_directory.as_str(), &"/", prefix ]
.iter().map(Deref::deref)
- .chain( utf8_percent_encode(&name.scoped_name,
- &percent_encoding::NON_ALPHANUMERIC) )
+ .chain( utf8_percent_encode(&format!("{}", name), &ENCODE ))
.chain([ suffix ].iter().map(Deref::deref))
.collect()
}
};
let after_ftype_prefix = rhs;
let rhs = str::from_utf8(rhs)?;
- let (rhs, scope) = match rhs.find(':') {
- None => {
- (rhs, ManagementScope::Server)
- },
- Some(colon) => {
- let (lhs, rhs) = rhs.split_at(colon);
- assert_eq!(rhs.chars().next(), Some(':'));
- (rhs, ManagementScope::Unix { user: lhs.to_owned() })
- },
- };
if rhs.rfind('.').is_some() { return TempToDelete }
- let scoped_name = percent_decode_str(rhs).decode_utf8()?.into();
+
+ let name : String = percent_decode_str(rhs).decode_utf8()?.into();
+ let name = ScopedName::from_str(&name)?;
+
GameFile {
access_leaf : [ b"a-", after_ftype_prefix ].concat(),
- name : InstanceName { scope, scoped_name },
+ name,
}
}
pub use crate::debugreader::DebugReader;
pub use crate::shapelib;
pub use crate::tz::*;
+pub use crate::accounts::*;
pub use zcoord::{self, ZCoord};
pub mod debugreader;
pub mod shapelib;
pub mod tz;
+pub mod accounts;
#[path="slotmap-slot-idx.rs"] pub mod slotmap_slot_idx;
}
impl<T> OrdExt for T where T : Ord + Sized + Clone {
}
+
+pub trait SplitAtDelim<Delim> {
+ fn split_at_delim(&self, delim: Delim) -> (&Self, &Self);
+}
+
+impl SplitAtDelim<char> for str {
+ fn split_at_delim(&self, delim: char) -> (&Self, &Self) {
+ match self.find(delim) {
+ Some(index) => self.split_at(index),
+ None => (self, ""),
+ }
+ }
+}