use super::capabilities::Capabilities;
use crate::cnf;
use crate::dbs::Notification;
use crate::err::Error;
use crate::iam::{Action, Auth, ResourceKind, Role};
use crate::sql::Base;
use channel::Sender;
use std::sync::Arc;
use uuid::Uuid;
#[derive(Clone, Debug)]
pub struct Options {
id: Option<Uuid>,
ns: Option<Arc<str>>,
db: Option<Arc<str>>,
dive: u8,
pub auth: Arc<Auth>,
pub auth_enabled: bool,
pub live: bool,
pub force: bool,
pub perms: bool,
pub strict: bool,
pub fields: bool,
pub events: bool,
pub tables: bool,
pub indexes: bool,
pub futures: bool,
pub projections: bool,
pub sender: Option<Sender<Notification>>,
pub capabilities: Arc<Capabilities>,
}
impl Default for Options {
fn default() -> Self {
Options::new()
}
}
impl Options {
pub fn new() -> Options {
Options {
id: None,
ns: None,
db: None,
dive: 0,
live: false,
perms: true,
force: false,
strict: false,
fields: true,
events: true,
tables: true,
indexes: true,
futures: false,
projections: false,
auth_enabled: true,
sender: None,
auth: Arc::new(Auth::default()),
capabilities: Arc::new(Capabilities::default()),
}
}
pub fn set_ns(&mut self, ns: Option<Arc<str>>) {
self.ns = ns
}
pub fn set_db(&mut self, db: Option<Arc<str>>) {
self.db = db
}
pub fn with_required(
mut self,
node_id: uuid::Uuid,
ns: Option<Arc<str>>,
db: Option<Arc<str>>,
auth: Arc<Auth>,
) -> Self {
self.id = Some(node_id);
self.ns = ns;
self.db = db;
self.auth = auth;
self
}
pub fn with_id(mut self, id: Uuid) -> Self {
self.id = Some(id);
self
}
pub fn with_ns(mut self, ns: Option<Arc<str>>) -> Self {
self.ns = ns;
self
}
pub fn with_db(mut self, db: Option<Arc<str>>) -> Self {
self.db = db;
self
}
pub fn with_auth(mut self, auth: Arc<Auth>) -> Self {
self.auth = auth;
self
}
pub fn with_live(mut self, live: bool) -> Self {
self.live = live;
self
}
pub fn with_perms(mut self, perms: bool) -> Self {
self.perms = perms;
self
}
pub fn with_force(mut self, force: bool) -> Self {
self.force = force;
self
}
pub fn with_strict(mut self, strict: bool) -> Self {
self.strict = strict;
self
}
pub fn with_fields(mut self, fields: bool) -> Self {
self.fields = fields;
self
}
pub fn with_events(mut self, events: bool) -> Self {
self.events = events;
self
}
pub fn with_tables(mut self, tables: bool) -> Self {
self.tables = tables;
self
}
pub fn with_indexes(mut self, indexes: bool) -> Self {
self.indexes = indexes;
self
}
pub fn with_futures(mut self, futures: bool) -> Self {
self.futures = futures;
self
}
pub fn with_projections(mut self, projections: bool) -> Self {
self.projections = projections;
self
}
pub fn with_import(mut self, import: bool) -> Self {
self.fields = !import;
self.events = !import;
self.tables = !import;
self
}
pub fn with_auth_enabled(mut self, auth_enabled: bool) -> Self {
self.auth_enabled = auth_enabled;
self
}
pub fn with_capabilities(mut self, capabilities: Arc<Capabilities>) -> Self {
self.capabilities = capabilities;
self
}
pub fn new_with_perms(&self, perms: bool) -> Self {
Self {
sender: self.sender.clone(),
auth: self.auth.clone(),
capabilities: self.capabilities.clone(),
ns: self.ns.clone(),
db: self.db.clone(),
perms,
..*self
}
}
pub fn new_with_force(&self, force: bool) -> Self {
Self {
sender: self.sender.clone(),
auth: self.auth.clone(),
capabilities: self.capabilities.clone(),
ns: self.ns.clone(),
db: self.db.clone(),
force,
..*self
}
}
pub fn new_with_strict(&self, strict: bool) -> Self {
Self {
sender: self.sender.clone(),
auth: self.auth.clone(),
capabilities: self.capabilities.clone(),
ns: self.ns.clone(),
db: self.db.clone(),
strict,
..*self
}
}
pub fn new_with_fields(&self, fields: bool) -> Self {
Self {
sender: self.sender.clone(),
auth: self.auth.clone(),
capabilities: self.capabilities.clone(),
ns: self.ns.clone(),
db: self.db.clone(),
fields,
..*self
}
}
pub fn new_with_events(&self, events: bool) -> Self {
Self {
sender: self.sender.clone(),
auth: self.auth.clone(),
capabilities: self.capabilities.clone(),
ns: self.ns.clone(),
db: self.db.clone(),
events,
..*self
}
}
pub fn new_with_tables(&self, tables: bool) -> Self {
Self {
sender: self.sender.clone(),
auth: self.auth.clone(),
capabilities: self.capabilities.clone(),
ns: self.ns.clone(),
db: self.db.clone(),
tables,
..*self
}
}
pub fn new_with_indexes(&self, indexes: bool) -> Self {
Self {
sender: self.sender.clone(),
auth: self.auth.clone(),
capabilities: self.capabilities.clone(),
ns: self.ns.clone(),
db: self.db.clone(),
indexes,
..*self
}
}
pub fn new_with_futures(&self, futures: bool) -> Self {
Self {
sender: self.sender.clone(),
auth: self.auth.clone(),
capabilities: self.capabilities.clone(),
ns: self.ns.clone(),
db: self.db.clone(),
futures,
..*self
}
}
pub fn new_with_projections(&self, projections: bool) -> Self {
Self {
sender: self.sender.clone(),
auth: self.auth.clone(),
capabilities: self.capabilities.clone(),
ns: self.ns.clone(),
db: self.db.clone(),
projections,
..*self
}
}
pub fn new_with_import(&self, import: bool) -> Self {
Self {
sender: self.sender.clone(),
auth: self.auth.clone(),
capabilities: self.capabilities.clone(),
ns: self.ns.clone(),
db: self.db.clone(),
fields: !import,
events: !import,
tables: !import,
..*self
}
}
pub fn new_with_sender(&self, sender: Sender<Notification>) -> Self {
Self {
auth: self.auth.clone(),
capabilities: self.capabilities.clone(),
ns: self.ns.clone(),
db: self.db.clone(),
sender: Some(sender),
..*self
}
}
pub fn selected_base(&self) -> Result<Base, Error> {
match (self.ns.as_ref(), self.db.as_ref()) {
(None, None) => Ok(Base::Root),
(Some(_), None) => Ok(Base::Ns),
(Some(_), Some(_)) => Ok(Base::Db),
(None, Some(_)) => Err(Error::NsEmpty),
}
}
pub fn dive(&self, cost: u8) -> Result<Self, Error> {
let dive = self.dive.saturating_add(cost);
if dive <= *cnf::MAX_COMPUTATION_DEPTH {
Ok(Self {
sender: self.sender.clone(),
auth: self.auth.clone(),
capabilities: self.capabilities.clone(),
ns: self.ns.clone(),
db: self.db.clone(),
dive,
..*self
})
} else {
Err(Error::ComputationDepthExceeded)
}
}
pub fn id(&self) -> Result<Uuid, Error> {
self.id.ok_or(Error::Unreachable)
}
pub fn ns(&self) -> &str {
self.ns.as_ref().map(AsRef::as_ref).unwrap()
}
pub fn db(&self) -> &str {
self.db.as_ref().map(AsRef::as_ref).unwrap()
}
pub fn realtime(&self) -> Result<(), Error> {
if !self.live {
return Err(Error::RealtimeDisabled);
}
Ok(())
}
pub fn valid_for_ns(&self) -> Result<(), Error> {
if self.ns.is_none() {
return Err(Error::NsEmpty);
}
Ok(())
}
pub fn valid_for_db(&self) -> Result<(), Error> {
self.valid_for_ns()?;
if self.db.is_none() {
return Err(Error::DbEmpty);
}
Ok(())
}
pub fn is_allowed(&self, action: Action, res: ResourceKind, base: &Base) -> Result<(), Error> {
let res = match base {
Base::Root => res.on_root(),
Base::Ns => {
self.valid_for_ns()?;
res.on_ns(self.ns())
}
Base::Db => {
self.valid_for_db()?;
res.on_db(self.ns(), self.db())
}
Base::Sc(sc) => {
self.valid_for_db()?;
res.on_scope(self.ns(), self.db(), sc)
}
};
if !self.auth_enabled && self.auth.is_anon() {
return Ok(());
}
self.auth.is_allowed(action, &res).map_err(Error::IamError)
}
pub fn check_perms(&self, action: Action) -> bool {
if !self.perms {
return false;
}
if !self.auth_enabled && self.auth.is_anon() {
return false;
}
let can_view =
[Role::Viewer, Role::Editor, Role::Owner].iter().any(|r| self.auth.has_role(r));
let can_edit = [Role::Editor, Role::Owner].iter().any(|r| self.auth.has_role(r));
let db_in_actor_level = self.auth.is_root()
|| self.auth.is_ns() && self.auth.level().ns().unwrap() == self.ns()
|| self.auth.is_db()
&& self.auth.level().ns().unwrap() == self.ns()
&& self.auth.level().db().unwrap() == self.db();
let is_allowed = match action {
Action::View => {
can_view && db_in_actor_level
}
Action::Edit => {
can_edit && db_in_actor_level
}
};
!is_allowed
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn is_allowed() {
{
let opts = Options::default().with_auth_enabled(false);
opts.is_allowed(Action::View, ResourceKind::Any, &Base::Ns).unwrap_err();
opts.is_allowed(Action::View, ResourceKind::Any, &Base::Db).unwrap_err();
opts.clone()
.with_db(Some("db".into()))
.is_allowed(Action::View, ResourceKind::Any, &Base::Db)
.unwrap_err();
opts.is_allowed(Action::View, ResourceKind::Any, &Base::Root).unwrap();
opts.clone()
.with_ns(Some("ns".into()))
.is_allowed(Action::View, ResourceKind::Any, &Base::Ns)
.unwrap();
opts.clone()
.with_ns(Some("ns".into()))
.with_db(Some("db".into()))
.is_allowed(Action::View, ResourceKind::Any, &Base::Db)
.unwrap();
}
{
let opts = Options::default()
.with_auth_enabled(true)
.with_auth(Auth::for_root(Role::Owner).into());
opts.is_allowed(Action::View, ResourceKind::Any, &Base::Ns).unwrap_err();
opts.is_allowed(Action::View, ResourceKind::Any, &Base::Db).unwrap_err();
opts.clone()
.with_db(Some("db".into()))
.is_allowed(Action::View, ResourceKind::Any, &Base::Db)
.unwrap_err();
opts.is_allowed(Action::View, ResourceKind::Any, &Base::Root).unwrap();
opts.clone()
.with_ns(Some("ns".into()))
.is_allowed(Action::View, ResourceKind::Any, &Base::Ns)
.unwrap();
opts.clone()
.with_ns(Some("ns".into()))
.with_db(Some("db".into()))
.is_allowed(Action::View, ResourceKind::Any, &Base::Db)
.unwrap();
}
}
}