summaryrefslogtreecommitdiff
path: root/crates/directory/src/core/principal.rs
diff options
context:
space:
mode:
authormdecimus <mauro@stalw.art>2024-09-10 18:44:44 +0200
committermdecimus <mauro@stalw.art>2024-09-10 18:44:44 +0200
commitfbcf55d8e1891b72499fed7d5ff54964c8a2f256 (patch)
treec6f18add7f0b62ce3bb6848c1b8b9e68c4eb4e5e /crates/directory/src/core/principal.rs
parent08a95ae58b451dbc2284ed7ac2d413185bc879fe (diff)
Access token permissions
Diffstat (limited to 'crates/directory/src/core/principal.rs')
-rw-r--r--crates/directory/src/core/principal.rs490
1 files changed, 490 insertions, 0 deletions
diff --git a/crates/directory/src/core/principal.rs b/crates/directory/src/core/principal.rs
new file mode 100644
index 00000000..bdec5d7a
--- /dev/null
+++ b/crates/directory/src/core/principal.rs
@@ -0,0 +1,490 @@
+/*
+ * SPDX-FileCopyrightText: 2020 Stalwart Labs Ltd <hello@stalw.art>
+ *
+ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
+ */
+
+use std::collections::hash_map::Entry;
+
+use store::U64_LEN;
+
+use crate::{
+ backend::internal::{PrincipalField, PrincipalValue},
+ Principal, Type,
+};
+
+impl Principal {
+ pub fn new(id: u32, typ: Type) -> Self {
+ Self {
+ id,
+ typ,
+ ..Default::default()
+ }
+ }
+
+ pub fn id(&self) -> u32 {
+ self.id
+ }
+
+ pub fn typ(&self) -> Type {
+ self.typ
+ }
+
+ pub fn name(&self) -> &str {
+ self.get_str(PrincipalField::Name).unwrap_or_default()
+ }
+
+ pub fn has_name(&self) -> bool {
+ self.fields.contains_key(&PrincipalField::Name)
+ }
+
+ pub fn quota(&self) -> u64 {
+ self.get_int(PrincipalField::Quota).unwrap_or_default()
+ }
+
+ pub fn description(&self) -> Option<&str> {
+ self.get_str(PrincipalField::Description)
+ }
+
+ pub fn get_str(&self, key: PrincipalField) -> Option<&str> {
+ self.fields.get(&key).and_then(|v| v.as_str())
+ }
+
+ pub fn get_int(&self, key: PrincipalField) -> Option<u64> {
+ self.fields.get(&key).and_then(|v| v.as_int())
+ }
+
+ pub fn take(&mut self, key: PrincipalField) -> Option<PrincipalValue> {
+ self.fields.remove(&key)
+ }
+
+ pub fn take_str(&mut self, key: PrincipalField) -> Option<String> {
+ self.take(key).and_then(|v| match v {
+ PrincipalValue::String(s) => Some(s),
+ PrincipalValue::StringList(l) => l.into_iter().next(),
+ PrincipalValue::Integer(i) => Some(i.to_string()),
+ PrincipalValue::IntegerList(l) => l.into_iter().next().map(|i| i.to_string()),
+ })
+ }
+
+ pub fn take_str_array(&mut self, key: PrincipalField) -> Option<Vec<String>> {
+ self.take(key).map(|v| v.into_str_array())
+ }
+
+ pub fn take_int_array(&mut self, key: PrincipalField) -> Option<Vec<u64>> {
+ self.take(key).map(|v| v.into_int_array())
+ }
+
+ pub fn iter_str(
+ &self,
+ key: PrincipalField,
+ ) -> Box<dyn Iterator<Item = &String> + Sync + Send + '_> {
+ self.fields
+ .get(&key)
+ .map(|v| v.iter_str())
+ .unwrap_or_else(|| Box::new(std::iter::empty()))
+ }
+
+ pub fn iter_mut_str(
+ &mut self,
+ key: PrincipalField,
+ ) -> Box<dyn Iterator<Item = &mut String> + Sync + Send + '_> {
+ self.fields
+ .get_mut(&key)
+ .map(|v| v.iter_mut_str())
+ .unwrap_or_else(|| Box::new(std::iter::empty()))
+ }
+
+ pub fn iter_int(
+ &self,
+ key: PrincipalField,
+ ) -> Box<dyn Iterator<Item = u64> + Sync + Send + '_> {
+ self.fields
+ .get(&key)
+ .map(|v| v.iter_int())
+ .unwrap_or_else(|| Box::new(std::iter::empty()))
+ }
+
+ pub fn iter_mut_int(
+ &mut self,
+ key: PrincipalField,
+ ) -> Box<dyn Iterator<Item = &mut u64> + Sync + Send + '_> {
+ self.fields
+ .get_mut(&key)
+ .map(|v| v.iter_mut_int())
+ .unwrap_or_else(|| Box::new(std::iter::empty()))
+ }
+
+ pub fn append_int(&mut self, key: PrincipalField, value: impl Into<u64>) -> &mut Self {
+ let value = value.into();
+ match self.fields.entry(key) {
+ Entry::Occupied(v) => {
+ let v = v.into_mut();
+
+ match v {
+ PrincipalValue::IntegerList(v) => {
+ v.push(value);
+ }
+ PrincipalValue::Integer(i) => {
+ *v = PrincipalValue::IntegerList(vec![*i, value]);
+ }
+ PrincipalValue::String(s) => {
+ *v =
+ PrincipalValue::IntegerList(vec![s.parse().unwrap_or_default(), value]);
+ }
+ PrincipalValue::StringList(l) => {
+ *v = PrincipalValue::IntegerList(
+ l.iter()
+ .map(|s| s.parse().unwrap_or_default())
+ .chain(std::iter::once(value))
+ .collect(),
+ );
+ }
+ }
+ }
+ Entry::Vacant(v) => {
+ v.insert(PrincipalValue::IntegerList(vec![value]));
+ }
+ }
+
+ self
+ }
+
+ pub fn append_str(&mut self, key: PrincipalField, value: impl Into<String>) -> &mut Self {
+ let value = value.into();
+ match self.fields.entry(key) {
+ Entry::Occupied(v) => {
+ let v = v.into_mut();
+
+ match v {
+ PrincipalValue::StringList(v) => {
+ v.push(value);
+ }
+ PrincipalValue::String(s) => {
+ *v = PrincipalValue::StringList(vec![std::mem::take(s), value]);
+ }
+ PrincipalValue::Integer(i) => {
+ *v = PrincipalValue::StringList(vec![i.to_string(), value]);
+ }
+ PrincipalValue::IntegerList(l) => {
+ *v = PrincipalValue::StringList(
+ l.iter()
+ .map(|i| i.to_string())
+ .chain(std::iter::once(value))
+ .collect(),
+ );
+ }
+ }
+ }
+ Entry::Vacant(v) => {
+ v.insert(PrincipalValue::StringList(vec![value]));
+ }
+ }
+ self
+ }
+
+ pub fn prepend_str(&mut self, key: PrincipalField, value: impl Into<String>) -> &mut Self {
+ let value = value.into();
+ match self.fields.entry(key) {
+ Entry::Occupied(v) => {
+ let v = v.into_mut();
+
+ match v {
+ PrincipalValue::StringList(v) => {
+ v.insert(0, value);
+ }
+ PrincipalValue::String(s) => {
+ *v = PrincipalValue::StringList(vec![value, std::mem::take(s)]);
+ }
+ PrincipalValue::Integer(i) => {
+ *v = PrincipalValue::StringList(vec![value, i.to_string()]);
+ }
+ PrincipalValue::IntegerList(l) => {
+ *v = PrincipalValue::StringList(
+ std::iter::once(value)
+ .chain(l.iter().map(|i| i.to_string()))
+ .collect(),
+ );
+ }
+ }
+ }
+ Entry::Vacant(v) => {
+ v.insert(PrincipalValue::StringList(vec![value]));
+ }
+ }
+ self
+ }
+
+ pub fn set(&mut self, key: PrincipalField, value: impl Into<PrincipalValue>) -> &mut Self {
+ self.fields.insert(key, value.into());
+ self
+ }
+
+ pub fn with_field(mut self, key: PrincipalField, value: impl Into<PrincipalValue>) -> Self {
+ self.set(key, value);
+ self
+ }
+
+ pub fn with_opt_field(
+ mut self,
+ key: PrincipalField,
+ value: Option<impl Into<PrincipalValue>>,
+ ) -> Self {
+ if let Some(value) = value {
+ self.set(key, value);
+ }
+ self
+ }
+
+ pub fn has_field(&self, key: PrincipalField) -> bool {
+ self.fields.contains_key(&key)
+ }
+
+ pub fn has_str_value(&self, key: PrincipalField, value: &str) -> bool {
+ self.fields.get(&key).map_or(false, |v| match v {
+ PrincipalValue::String(v) => v == value,
+ PrincipalValue::StringList(l) => l.iter().any(|v| v == value),
+ PrincipalValue::Integer(_) | PrincipalValue::IntegerList(_) => false,
+ })
+ }
+
+ pub fn has_int_value(&self, key: PrincipalField, value: u64) -> bool {
+ self.fields.get(&key).map_or(false, |v| match v {
+ PrincipalValue::Integer(v) => *v == value,
+ PrincipalValue::IntegerList(l) => l.iter().any(|v| *v == value),
+ PrincipalValue::String(_) | PrincipalValue::StringList(_) => false,
+ })
+ }
+
+ pub fn field_len(&self, key: PrincipalField) -> usize {
+ self.fields.get(&key).map_or(0, |v| match v {
+ PrincipalValue::String(_) => 1,
+ PrincipalValue::StringList(l) => l.len(),
+ PrincipalValue::Integer(_) => 1,
+ PrincipalValue::IntegerList(l) => l.len(),
+ })
+ }
+
+ pub fn remove(&mut self, key: PrincipalField) -> Option<PrincipalValue> {
+ self.fields.remove(&key)
+ }
+
+ pub fn retain_str<F>(&mut self, key: PrincipalField, mut f: F)
+ where
+ F: FnMut(&String) -> bool,
+ {
+ if let Some(value) = self.fields.get_mut(&key) {
+ match value {
+ PrincipalValue::String(s) => {
+ if !f(s) {
+ self.fields.remove(&key);
+ }
+ }
+ PrincipalValue::StringList(l) => {
+ l.retain(f);
+ if l.is_empty() {
+ self.fields.remove(&key);
+ }
+ }
+ _ => {}
+ }
+ }
+ }
+
+ pub fn retain_int<F>(&mut self, key: PrincipalField, mut f: F)
+ where
+ F: FnMut(&u64) -> bool,
+ {
+ if let Some(value) = self.fields.get_mut(&key) {
+ match value {
+ PrincipalValue::Integer(i) => {
+ if !f(i) {
+ self.fields.remove(&key);
+ }
+ }
+ PrincipalValue::IntegerList(l) => {
+ l.retain(f);
+ if l.is_empty() {
+ self.fields.remove(&key);
+ }
+ }
+ _ => {}
+ }
+ }
+ }
+
+ pub fn fallback_admin(fallback_pass: impl Into<String>) -> Self {
+ Principal {
+ id: u32::MAX,
+ typ: Type::Individual,
+ ..Default::default()
+ }
+ .with_field(PrincipalField::Name, "Fallback Administrator")
+ .with_field(
+ PrincipalField::Secrets,
+ PrincipalValue::String(fallback_pass.into()),
+ )
+ .into_superuser()
+ }
+
+ pub fn into_superuser(mut self) -> Self {
+ let todo = "add role";
+ self
+ }
+}
+
+impl PrincipalValue {
+ pub fn as_str(&self) -> Option<&str> {
+ match self {
+ PrincipalValue::String(v) => Some(v.as_str()),
+ PrincipalValue::StringList(v) => v.first().map(|s| s.as_str()),
+ _ => None,
+ }
+ }
+
+ pub fn as_int(&self) -> Option<u64> {
+ match self {
+ PrincipalValue::Integer(v) => Some(*v),
+ PrincipalValue::IntegerList(v) => v.first().copied(),
+ _ => None,
+ }
+ }
+
+ pub fn iter_str(&self) -> Box<dyn Iterator<Item = &String> + Sync + Send + '_> {
+ match self {
+ PrincipalValue::String(v) => Box::new(std::iter::once(v)),
+ PrincipalValue::StringList(v) => Box::new(v.iter()),
+ _ => Box::new(std::iter::empty()),
+ }
+ }
+
+ pub fn iter_mut_str(&mut self) -> Box<dyn Iterator<Item = &mut String> + Sync + Send + '_> {
+ match self {
+ PrincipalValue::String(v) => Box::new(std::iter::once(v)),
+ PrincipalValue::StringList(v) => Box::new(v.iter_mut()),
+ _ => Box::new(std::iter::empty()),
+ }
+ }
+
+ pub fn iter_int(&self) -> Box<dyn Iterator<Item = u64> + Sync + Send + '_> {
+ match self {
+ PrincipalValue::Integer(v) => Box::new(std::iter::once(*v)),
+ PrincipalValue::IntegerList(v) => Box::new(v.iter().copied()),
+ _ => Box::new(std::iter::empty()),
+ }
+ }
+
+ pub fn iter_mut_int(&mut self) -> Box<dyn Iterator<Item = &mut u64> + Sync + Send + '_> {
+ match self {
+ PrincipalValue::Integer(v) => Box::new(std::iter::once(v)),
+ PrincipalValue::IntegerList(v) => Box::new(v.iter_mut()),
+ _ => Box::new(std::iter::empty()),
+ }
+ }
+
+ pub fn into_array(self) -> Self {
+ match self {
+ PrincipalValue::String(v) => PrincipalValue::StringList(vec![v]),
+ PrincipalValue::Integer(v) => PrincipalValue::IntegerList(vec![v]),
+ v => v,
+ }
+ }
+
+ pub fn into_str_array(self) -> Vec<String> {
+ match self {
+ PrincipalValue::StringList(v) => v,
+ PrincipalValue::String(v) => vec![v],
+ PrincipalValue::Integer(v) => vec![v.to_string()],
+ PrincipalValue::IntegerList(v) => v.into_iter().map(|v| v.to_string()).collect(),
+ }
+ }
+
+ pub fn into_int_array(self) -> Vec<u64> {
+ match self {
+ PrincipalValue::IntegerList(v) => v,
+ PrincipalValue::Integer(v) => vec![v],
+ PrincipalValue::String(v) => vec![v.parse().unwrap_or_default()],
+ PrincipalValue::StringList(v) => v
+ .into_iter()
+ .map(|v| v.parse().unwrap_or_default())
+ .collect(),
+ }
+ }
+
+ pub fn serialized_size(&self) -> usize {
+ match self {
+ PrincipalValue::String(s) => s.len() + 2,
+ PrincipalValue::StringList(s) => s.iter().map(|s| s.len() + 2).sum(),
+ PrincipalValue::Integer(_) => U64_LEN,
+ PrincipalValue::IntegerList(l) => l.len() * U64_LEN,
+ }
+ }
+}
+
+impl From<u64> for PrincipalValue {
+ fn from(v: u64) -> Self {
+ Self::Integer(v)
+ }
+}
+
+impl From<String> for PrincipalValue {
+ fn from(v: String) -> Self {
+ Self::String(v)
+ }
+}
+
+impl From<&str> for PrincipalValue {
+ fn from(v: &str) -> Self {
+ Self::String(v.to_string())
+ }
+}
+
+impl From<Vec<String>> for PrincipalValue {
+ fn from(v: Vec<String>) -> Self {
+ Self::StringList(v)
+ }
+}
+
+impl From<Vec<u64>> for PrincipalValue {
+ fn from(v: Vec<u64>) -> Self {
+ Self::IntegerList(v)
+ }
+}
+
+impl From<u32> for PrincipalValue {
+ fn from(v: u32) -> Self {
+ Self::Integer(v as u64)
+ }
+}
+
+impl From<Vec<u32>> for PrincipalValue {
+ fn from(v: Vec<u32>) -> Self {
+ Self::IntegerList(v.into_iter().map(|v| v as u64).collect())
+ }
+}
+
+impl Type {
+ pub fn to_jmap(&self) -> &'static str {
+ match self {
+ Self::Individual => "individual",
+ Self::Group => "group",
+ Self::Resource => "resource",
+ Self::Location => "location",
+ Self::Other => "other",
+ Self::List => "list",
+ Self::Tenant => "tenant",
+ }
+ }
+
+ pub fn as_str(&self) -> &'static str {
+ match self {
+ Self::Individual => "Individual",
+ Self::Group => "Group",
+ Self::Resource => "Resource",
+ Self::Location => "Location",
+ Self::Tenant => "Tenant",
+ Self::List => "List",
+ Self::Other => "Other",
+ }
+ }
+}