Internal directory + CLI improvements
This commit is contained in:
@@ -27,13 +27,17 @@ use store::{
|
||||
IterateParams, Store, ValueKey,
|
||||
};
|
||||
|
||||
use crate::{Principal, QueryBy};
|
||||
use crate::{Principal, QueryBy, Type};
|
||||
|
||||
use super::manage::ManageDirectory;
|
||||
use super::{manage::ManageDirectory, PrincipalIdType};
|
||||
|
||||
#[async_trait::async_trait]
|
||||
pub trait DirectoryStore: Sync + Send {
|
||||
async fn query(&self, by: QueryBy<'_>) -> crate::Result<Option<Principal<u32>>>;
|
||||
async fn query(
|
||||
&self,
|
||||
by: QueryBy<'_>,
|
||||
return_member_of: bool,
|
||||
) -> crate::Result<Option<Principal<u32>>>;
|
||||
async fn email_to_ids(&self, email: &str) -> crate::Result<Vec<u32>>;
|
||||
|
||||
async fn is_local_domain(&self, domain: &str) -> crate::Result<bool>;
|
||||
@@ -44,29 +48,28 @@ pub trait DirectoryStore: Sync + Send {
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl DirectoryStore for Store {
|
||||
async fn query(&self, by: QueryBy<'_>) -> crate::Result<Option<Principal<u32>>> {
|
||||
let (username, secret) = match by {
|
||||
QueryBy::Name(name) => (name, None),
|
||||
QueryBy::Id(account_id) => {
|
||||
return self
|
||||
.get_value::<Principal<u32>>(ValueKey::from(ValueClass::Directory(
|
||||
DirectoryClass::Principal(account_id),
|
||||
)))
|
||||
.await
|
||||
.map_err(Into::into);
|
||||
}
|
||||
async fn query(
|
||||
&self,
|
||||
by: QueryBy<'_>,
|
||||
return_member_of: bool,
|
||||
) -> crate::Result<Option<Principal<u32>>> {
|
||||
let (account_id, secret) = match by {
|
||||
QueryBy::Name(name) => (self.get_account_id(name).await?, None),
|
||||
QueryBy::Id(account_id) => (account_id.into(), None),
|
||||
QueryBy::Credentials(credentials) => match credentials {
|
||||
Credentials::Plain { username, secret } => {
|
||||
(username.as_str(), secret.as_str().into())
|
||||
(self.get_account_id(username).await?, secret.as_str().into())
|
||||
}
|
||||
Credentials::OAuthBearer { token } => {
|
||||
(self.get_account_id(token).await?, token.as_str().into())
|
||||
}
|
||||
Credentials::OAuthBearer { token } => (token.as_str(), token.as_str().into()),
|
||||
Credentials::XOauth2 { username, secret } => {
|
||||
(username.as_str(), secret.as_str().into())
|
||||
(self.get_account_id(username).await?, secret.as_str().into())
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
if let Some(account_id) = self.get_account_id(username).await? {
|
||||
if let Some(account_id) = account_id {
|
||||
match (
|
||||
self.get_value::<Principal<u32>>(ValueKey::from(ValueClass::Directory(
|
||||
DirectoryClass::Principal(account_id),
|
||||
@@ -74,10 +77,19 @@ impl DirectoryStore for Store {
|
||||
.await?,
|
||||
secret,
|
||||
) {
|
||||
(Some(principal), Some(secret)) if principal.verify_secret(secret).await => {
|
||||
(Some(mut principal), Some(secret)) if principal.verify_secret(secret).await => {
|
||||
if return_member_of {
|
||||
principal.member_of = self.get_member_of(principal.id).await?;
|
||||
}
|
||||
Ok(Some(principal))
|
||||
}
|
||||
(Some(mut principal), None) => {
|
||||
if return_member_of {
|
||||
principal.member_of = self.get_member_of(principal.id).await?;
|
||||
}
|
||||
|
||||
Ok(Some(principal))
|
||||
}
|
||||
(Some(principal), None) => Ok(Some(principal)),
|
||||
_ => Ok(None),
|
||||
}
|
||||
} else {
|
||||
@@ -86,12 +98,20 @@ impl DirectoryStore for Store {
|
||||
}
|
||||
|
||||
async fn email_to_ids(&self, email: &str) -> crate::Result<Vec<u32>> {
|
||||
self.get_value::<Vec<u32>>(ValueKey::from(ValueClass::Directory(
|
||||
DirectoryClass::EmailToId(email.as_bytes().to_vec()),
|
||||
)))
|
||||
.await
|
||||
.map(|ids| ids.unwrap_or_default())
|
||||
.map_err(Into::into)
|
||||
if let Some(ptype) = self
|
||||
.get_value::<PrincipalIdType>(ValueKey::from(ValueClass::Directory(
|
||||
DirectoryClass::EmailToId(email.as_bytes().to_vec()),
|
||||
)))
|
||||
.await?
|
||||
{
|
||||
if ptype.typ != Type::List {
|
||||
Ok(vec![ptype.account_id])
|
||||
} else {
|
||||
self.get_members(ptype.account_id).await.map_err(Into::into)
|
||||
}
|
||||
} else {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
}
|
||||
|
||||
async fn is_local_domain(&self, domain: &str) -> crate::Result<bool> {
|
||||
|
||||
@@ -23,8 +23,10 @@
|
||||
|
||||
use jmap_proto::types::collection::Collection;
|
||||
use store::{
|
||||
write::{assert::HashedValue, BatchBuilder, DirectoryClass, ValueClass},
|
||||
Deserialize, IterateParams, Serialize, Store, ValueKey,
|
||||
write::{
|
||||
assert::HashedValue, key::DeserializeBigEndian, BatchBuilder, DirectoryClass, ValueClass,
|
||||
},
|
||||
Deserialize, IterateParams, Serialize, Store, ValueKey, U32_LEN,
|
||||
};
|
||||
|
||||
use crate::{DirectoryError, ManagementError, Principal, QueryBy, Type};
|
||||
@@ -39,6 +41,8 @@ pub trait ManageDirectory: Sized {
|
||||
async fn get_account_id(&self, name: &str) -> crate::Result<Option<u32>>;
|
||||
async fn get_or_create_account_id(&self, name: &str) -> crate::Result<u32>;
|
||||
async fn get_account_name(&self, account_id: u32) -> crate::Result<Option<String>>;
|
||||
async fn get_member_of(&self, account_id: u32) -> crate::Result<Vec<u32>>;
|
||||
async fn get_members(&self, account_id: u32) -> crate::Result<Vec<u32>>;
|
||||
async fn create_account(&self, principal: Principal<String>) -> crate::Result<u32>;
|
||||
async fn update_account(
|
||||
&self,
|
||||
@@ -204,6 +208,7 @@ impl ManageDirectory for Store {
|
||||
|
||||
// Write principal
|
||||
let mut batch = BatchBuilder::new();
|
||||
let ptype = PrincipalIdType::new(principal.id, principal.typ.into_base_type()).serialize();
|
||||
batch
|
||||
.assert_value(
|
||||
ValueClass::Directory(DirectoryClass::NameToId(
|
||||
@@ -217,20 +222,32 @@ impl ManageDirectory for Store {
|
||||
)
|
||||
.set(
|
||||
ValueClass::Directory(DirectoryClass::NameToId(principal.name.into_bytes())),
|
||||
PrincipalIdType::new(principal.id, principal.typ.into_base_type()).serialize(),
|
||||
ptype.clone(),
|
||||
);
|
||||
|
||||
// Write email to id mapping
|
||||
let ids = if matches!(principal.typ, Type::List) {
|
||||
principal.member_of
|
||||
} else {
|
||||
vec![principal.id]
|
||||
};
|
||||
|
||||
for email in principal.emails {
|
||||
batch.set(
|
||||
ValueClass::Directory(DirectoryClass::EmailToId(email.into_bytes())),
|
||||
(&ids).serialize(),
|
||||
ptype.clone(),
|
||||
);
|
||||
}
|
||||
|
||||
// Write membership
|
||||
for member_of in principal.member_of {
|
||||
batch.set(
|
||||
ValueClass::Directory(DirectoryClass::MemberOf {
|
||||
principal_id: principal.id,
|
||||
member_of,
|
||||
}),
|
||||
vec![],
|
||||
);
|
||||
batch.set(
|
||||
ValueClass::Directory(DirectoryClass::Members {
|
||||
principal_id: member_of,
|
||||
has_member: principal.id,
|
||||
}),
|
||||
vec![],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -278,6 +295,28 @@ impl ManageDirectory for Store {
|
||||
batch.clear(DirectoryClass::EmailToId(email.into_bytes()));
|
||||
}
|
||||
|
||||
for member_id in self.get_member_of(account_id).await? {
|
||||
batch.clear(DirectoryClass::MemberOf {
|
||||
principal_id: account_id,
|
||||
member_of: member_id,
|
||||
});
|
||||
batch.clear(DirectoryClass::Members {
|
||||
principal_id: member_id,
|
||||
has_member: account_id,
|
||||
});
|
||||
}
|
||||
|
||||
for member_id in self.get_members(account_id).await? {
|
||||
batch.clear(DirectoryClass::MemberOf {
|
||||
principal_id: member_id,
|
||||
member_of: account_id,
|
||||
});
|
||||
batch.clear(DirectoryClass::Members {
|
||||
principal_id: account_id,
|
||||
has_member: member_id,
|
||||
});
|
||||
}
|
||||
|
||||
self.write(batch.build()).await?;
|
||||
|
||||
Ok(())
|
||||
@@ -306,14 +345,25 @@ impl ManageDirectory for Store {
|
||||
DirectoryError::Management(ManagementError::NotFound(account_id.to_string()))
|
||||
})?;
|
||||
|
||||
// Obtain members and memberOf
|
||||
let mut member_of = self.get_member_of(account_id).await?;
|
||||
let mut members = self.get_members(account_id).await?;
|
||||
|
||||
// Apply changes
|
||||
let mut batch = BatchBuilder::new();
|
||||
let is_list = matches!(principal.inner.typ, Type::List);
|
||||
let mut has_list_changes = false;
|
||||
batch.assert_value(
|
||||
ValueClass::Directory(DirectoryClass::Principal(account_id)),
|
||||
&principal,
|
||||
);
|
||||
let ptype =
|
||||
PrincipalIdType::new(account_id, principal.inner.typ.into_base_type()).serialize();
|
||||
let update_principal = !changes.is_empty()
|
||||
&& !changes
|
||||
.iter()
|
||||
.all(|c| matches!(c.field, PrincipalField::MemberOf | PrincipalField::Members));
|
||||
|
||||
if update_principal {
|
||||
batch.assert_value(
|
||||
ValueClass::Directory(DirectoryClass::Principal(account_id)),
|
||||
&principal,
|
||||
);
|
||||
}
|
||||
for change in changes {
|
||||
match (change.action, change.field, change.value) {
|
||||
(PrincipalAction::Set, PrincipalField::Name, PrincipalValue::String(new_name)) => {
|
||||
@@ -337,16 +387,20 @@ impl ManageDirectory for Store {
|
||||
|
||||
batch.set(
|
||||
ValueClass::Directory(DirectoryClass::NameToId(new_name.into_bytes())),
|
||||
PrincipalIdType::new(account_id, principal.inner.typ.into_base_type())
|
||||
.serialize(),
|
||||
ptype.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
(PrincipalAction::Set, PrincipalField::Type, PrincipalValue::Type(new_type))
|
||||
if matches!(principal.inner.typ, Type::Individual | Type::Superuser)
|
||||
&& matches!(new_type, Type::Individual | Type::Superuser) =>
|
||||
{
|
||||
principal.inner.typ = new_type;
|
||||
(PrincipalAction::Set, PrincipalField::Type, PrincipalValue::String(new_type)) => {
|
||||
if let Some(new_type) = Type::parse(&new_type) {
|
||||
if matches!(principal.inner.typ, Type::Individual | Type::Superuser)
|
||||
&& matches!(new_type, Type::Individual | Type::Superuser)
|
||||
{
|
||||
principal.inner.typ = new_type;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return Err(DirectoryError::Unsupported);
|
||||
}
|
||||
(
|
||||
PrincipalAction::Set,
|
||||
@@ -369,6 +423,8 @@ impl ManageDirectory for Store {
|
||||
(PrincipalAction::Set, PrincipalField::Quota, PrincipalValue::Integer(quota)) => {
|
||||
principal.inner.quota = quota;
|
||||
}
|
||||
|
||||
// Emails
|
||||
(
|
||||
PrincipalAction::Set,
|
||||
PrincipalField::Emails,
|
||||
@@ -396,59 +452,25 @@ impl ManageDirectory for Store {
|
||||
));
|
||||
}
|
||||
}
|
||||
if !is_list {
|
||||
batch.set(
|
||||
ValueClass::Directory(DirectoryClass::EmailToId(
|
||||
email.as_bytes().to_vec(),
|
||||
)),
|
||||
vec![account_id].serialize(),
|
||||
);
|
||||
}
|
||||
batch.set(
|
||||
ValueClass::Directory(DirectoryClass::EmailToId(
|
||||
email.as_bytes().to_vec(),
|
||||
)),
|
||||
ptype.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
if !is_list {
|
||||
for email in &principal.inner.emails {
|
||||
if !emails.contains(email) {
|
||||
batch.clear(ValueClass::Directory(DirectoryClass::EmailToId(
|
||||
email.as_bytes().to_vec(),
|
||||
)));
|
||||
}
|
||||
|
||||
for email in &principal.inner.emails {
|
||||
if !emails.contains(email) {
|
||||
batch.clear(ValueClass::Directory(DirectoryClass::EmailToId(
|
||||
email.as_bytes().to_vec(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
principal.inner.emails = emails;
|
||||
}
|
||||
(
|
||||
PrincipalAction::Set,
|
||||
PrincipalField::MemberOf,
|
||||
PrincipalValue::StringList(members),
|
||||
) => {
|
||||
if is_list {
|
||||
has_list_changes = true;
|
||||
}
|
||||
principal.inner.member_of = Vec::with_capacity(members.len());
|
||||
for member in members {
|
||||
let account_id = self.get_account_id(&member).await?.ok_or_else(|| {
|
||||
DirectoryError::Management(ManagementError::NotFound(member))
|
||||
})?;
|
||||
principal.inner.member_of.push(account_id);
|
||||
}
|
||||
}
|
||||
(
|
||||
PrincipalAction::AddItem,
|
||||
PrincipalField::MemberOf,
|
||||
PrincipalValue::String(member),
|
||||
) => {
|
||||
let account_id = self.get_account_id(&member).await?.ok_or_else(|| {
|
||||
DirectoryError::Management(ManagementError::NotFound(member))
|
||||
})?;
|
||||
if !principal.inner.member_of.contains(&account_id) {
|
||||
principal.inner.member_of.push(account_id);
|
||||
if is_list {
|
||||
has_list_changes = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
(
|
||||
PrincipalAction::AddItem,
|
||||
PrincipalField::Emails,
|
||||
@@ -471,36 +493,15 @@ impl ManageDirectory for Store {
|
||||
)));
|
||||
}
|
||||
}
|
||||
if !is_list {
|
||||
batch.set(
|
||||
ValueClass::Directory(DirectoryClass::EmailToId(
|
||||
email.as_bytes().to_vec(),
|
||||
)),
|
||||
vec![account_id].serialize(),
|
||||
);
|
||||
}
|
||||
batch.set(
|
||||
ValueClass::Directory(DirectoryClass::EmailToId(
|
||||
email.as_bytes().to_vec(),
|
||||
)),
|
||||
ptype.clone(),
|
||||
);
|
||||
principal.inner.emails.push(email);
|
||||
}
|
||||
}
|
||||
(
|
||||
PrincipalAction::RemoveItem,
|
||||
PrincipalField::MemberOf,
|
||||
PrincipalValue::String(member),
|
||||
) => {
|
||||
if let Some(account_id) = self.get_account_id(&member).await? {
|
||||
if let Some(pos) = principal
|
||||
.inner
|
||||
.member_of
|
||||
.iter()
|
||||
.position(|v| *v == account_id)
|
||||
{
|
||||
principal.inner.member_of.remove(pos);
|
||||
if is_list {
|
||||
has_list_changes = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
(
|
||||
PrincipalAction::RemoveItem,
|
||||
PrincipalField::Emails,
|
||||
@@ -508,34 +509,209 @@ impl ManageDirectory for Store {
|
||||
) => {
|
||||
let email = email.to_lowercase();
|
||||
if let Some(pos) = principal.inner.emails.iter().position(|v| *v == email) {
|
||||
if !is_list {
|
||||
batch.clear(ValueClass::Directory(DirectoryClass::EmailToId(
|
||||
email.as_bytes().to_vec(),
|
||||
)));
|
||||
}
|
||||
batch.clear(ValueClass::Directory(DirectoryClass::EmailToId(
|
||||
email.as_bytes().to_vec(),
|
||||
)));
|
||||
principal.inner.emails.remove(pos);
|
||||
}
|
||||
}
|
||||
|
||||
// MemberOf
|
||||
(
|
||||
PrincipalAction::Set,
|
||||
PrincipalField::MemberOf,
|
||||
PrincipalValue::StringList(members),
|
||||
) => {
|
||||
let mut new_member_of = Vec::new();
|
||||
for member in members {
|
||||
let member_id = self.get_account_id(&member).await?.ok_or_else(|| {
|
||||
DirectoryError::Management(ManagementError::NotFound(member))
|
||||
})?;
|
||||
if !member_of.contains(&member_id) {
|
||||
batch.set(
|
||||
ValueClass::Directory(DirectoryClass::MemberOf {
|
||||
principal_id: account_id,
|
||||
member_of: member_id,
|
||||
}),
|
||||
vec![],
|
||||
);
|
||||
batch.set(
|
||||
ValueClass::Directory(DirectoryClass::Members {
|
||||
principal_id: member_id,
|
||||
has_member: account_id,
|
||||
}),
|
||||
vec![],
|
||||
);
|
||||
}
|
||||
|
||||
new_member_of.push(member_id);
|
||||
}
|
||||
|
||||
for member_id in &member_of {
|
||||
if !new_member_of.contains(member_id) {
|
||||
batch.clear(ValueClass::Directory(DirectoryClass::MemberOf {
|
||||
principal_id: account_id,
|
||||
member_of: *member_id,
|
||||
}));
|
||||
batch.clear(ValueClass::Directory(DirectoryClass::Members {
|
||||
principal_id: *member_id,
|
||||
has_member: account_id,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
member_of = new_member_of;
|
||||
}
|
||||
(
|
||||
PrincipalAction::AddItem,
|
||||
PrincipalField::MemberOf,
|
||||
PrincipalValue::String(member),
|
||||
) => {
|
||||
let member_id = self.get_account_id(&member).await?.ok_or_else(|| {
|
||||
DirectoryError::Management(ManagementError::NotFound(member))
|
||||
})?;
|
||||
if !member_of.contains(&member_id) {
|
||||
batch.set(
|
||||
ValueClass::Directory(DirectoryClass::MemberOf {
|
||||
principal_id: account_id,
|
||||
member_of: member_id,
|
||||
}),
|
||||
vec![],
|
||||
);
|
||||
batch.set(
|
||||
ValueClass::Directory(DirectoryClass::Members {
|
||||
principal_id: member_id,
|
||||
has_member: account_id,
|
||||
}),
|
||||
vec![],
|
||||
);
|
||||
member_of.push(member_id);
|
||||
}
|
||||
}
|
||||
(
|
||||
PrincipalAction::RemoveItem,
|
||||
PrincipalField::MemberOf,
|
||||
PrincipalValue::String(member),
|
||||
) => {
|
||||
if let Some(member_id) = self.get_account_id(&member).await? {
|
||||
if let Some(pos) = member_of.iter().position(|v| *v == member_id) {
|
||||
batch.clear(ValueClass::Directory(DirectoryClass::MemberOf {
|
||||
principal_id: account_id,
|
||||
member_of: member_id,
|
||||
}));
|
||||
batch.clear(ValueClass::Directory(DirectoryClass::Members {
|
||||
principal_id: member_id,
|
||||
has_member: account_id,
|
||||
}));
|
||||
member_of.remove(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(
|
||||
PrincipalAction::Set,
|
||||
PrincipalField::Members,
|
||||
PrincipalValue::StringList(members_),
|
||||
) => {
|
||||
let mut new_members = Vec::new();
|
||||
for member in members_ {
|
||||
let member_id = self.get_account_id(&member).await?.ok_or_else(|| {
|
||||
DirectoryError::Management(ManagementError::NotFound(member))
|
||||
})?;
|
||||
if !members.contains(&member_id) {
|
||||
batch.set(
|
||||
ValueClass::Directory(DirectoryClass::MemberOf {
|
||||
principal_id: member_id,
|
||||
member_of: account_id,
|
||||
}),
|
||||
vec![],
|
||||
);
|
||||
batch.set(
|
||||
ValueClass::Directory(DirectoryClass::Members {
|
||||
principal_id: account_id,
|
||||
has_member: member_id,
|
||||
}),
|
||||
vec![],
|
||||
);
|
||||
}
|
||||
|
||||
new_members.push(member_id);
|
||||
}
|
||||
|
||||
for member_id in &members {
|
||||
if !new_members.contains(member_id) {
|
||||
batch.clear(ValueClass::Directory(DirectoryClass::MemberOf {
|
||||
principal_id: *member_id,
|
||||
member_of: account_id,
|
||||
}));
|
||||
batch.clear(ValueClass::Directory(DirectoryClass::Members {
|
||||
principal_id: account_id,
|
||||
has_member: *member_id,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
members = new_members;
|
||||
}
|
||||
(
|
||||
PrincipalAction::AddItem,
|
||||
PrincipalField::Members,
|
||||
PrincipalValue::String(member),
|
||||
) => {
|
||||
let member_id = self.get_account_id(&member).await?.ok_or_else(|| {
|
||||
DirectoryError::Management(ManagementError::NotFound(member))
|
||||
})?;
|
||||
if !members.contains(&member_id) {
|
||||
batch.set(
|
||||
ValueClass::Directory(DirectoryClass::MemberOf {
|
||||
principal_id: member_id,
|
||||
member_of: account_id,
|
||||
}),
|
||||
vec![],
|
||||
);
|
||||
batch.set(
|
||||
ValueClass::Directory(DirectoryClass::Members {
|
||||
principal_id: account_id,
|
||||
has_member: member_id,
|
||||
}),
|
||||
vec![],
|
||||
);
|
||||
members.push(member_id);
|
||||
}
|
||||
}
|
||||
(
|
||||
PrincipalAction::RemoveItem,
|
||||
PrincipalField::Members,
|
||||
PrincipalValue::String(member),
|
||||
) => {
|
||||
if let Some(member_id) = self.get_account_id(&member).await? {
|
||||
if let Some(pos) = members.iter().position(|v| *v == member_id) {
|
||||
batch.clear(ValueClass::Directory(DirectoryClass::MemberOf {
|
||||
principal_id: member_id,
|
||||
member_of: account_id,
|
||||
}));
|
||||
batch.clear(ValueClass::Directory(DirectoryClass::Members {
|
||||
principal_id: account_id,
|
||||
has_member: member_id,
|
||||
}));
|
||||
members.remove(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_ => {
|
||||
return Err(DirectoryError::Unsupported);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if has_list_changes {
|
||||
for email in &principal.inner.emails {
|
||||
batch.set(
|
||||
ValueClass::Directory(DirectoryClass::EmailToId(email.as_bytes().to_vec())),
|
||||
(&principal.inner.member_of).serialize(),
|
||||
);
|
||||
}
|
||||
if update_principal {
|
||||
batch.set(
|
||||
ValueClass::Directory(DirectoryClass::Principal(account_id)),
|
||||
principal.inner.serialize(),
|
||||
);
|
||||
}
|
||||
|
||||
batch.set(
|
||||
ValueClass::Directory(DirectoryClass::Principal(account_id)),
|
||||
principal.inner.serialize(),
|
||||
);
|
||||
|
||||
self.write(batch.build()).await?;
|
||||
|
||||
Ok(())
|
||||
@@ -683,6 +859,48 @@ impl ManageDirectory for Store {
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
async fn get_member_of(&self, account_id: u32) -> crate::Result<Vec<u32>> {
|
||||
let from_key = ValueKey::from(ValueClass::Directory(DirectoryClass::MemberOf {
|
||||
principal_id: account_id,
|
||||
member_of: 0,
|
||||
}));
|
||||
let to_key = ValueKey::from(ValueClass::Directory(DirectoryClass::MemberOf {
|
||||
principal_id: account_id,
|
||||
member_of: u32::MAX,
|
||||
}));
|
||||
let mut results = Vec::new();
|
||||
self.iterate(
|
||||
IterateParams::new(from_key, to_key).no_values(),
|
||||
|key, _| {
|
||||
results.push(key.deserialize_be_u32(key.len() - U32_LEN)?);
|
||||
Ok(true)
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
async fn get_members(&self, account_id: u32) -> crate::Result<Vec<u32>> {
|
||||
let from_key = ValueKey::from(ValueClass::Directory(DirectoryClass::Members {
|
||||
principal_id: account_id,
|
||||
has_member: 0,
|
||||
}));
|
||||
let to_key = ValueKey::from(ValueClass::Directory(DirectoryClass::Members {
|
||||
principal_id: account_id,
|
||||
has_member: u32::MAX,
|
||||
}));
|
||||
let mut results = Vec::new();
|
||||
self.iterate(
|
||||
IterateParams::new(from_key, to_key).no_values(),
|
||||
|key, _| {
|
||||
results.push(key.deserialize_be_u32(key.len() - U32_LEN)?);
|
||||
Ok(true)
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
async fn init(self) -> crate::Result<Self> {
|
||||
if let (Ok(admin_user), Ok(admin_pass)) = (
|
||||
std::env::var("SET_ADMIN_USER"),
|
||||
@@ -698,7 +916,7 @@ impl ManageDirectory for Store {
|
||||
}],
|
||||
)
|
||||
.await?;
|
||||
eprintln!("Successfully updated administrator password for {admin_user:?}.");
|
||||
eprintln!("Successfully updated password for {admin_user:?}.");
|
||||
} else {
|
||||
self.create_account(Principal {
|
||||
typ: Type::Superuser,
|
||||
|
||||
@@ -50,7 +50,6 @@ impl Serialize for &Principal<u32> {
|
||||
+ self.name.len()
|
||||
+ self.emails.iter().map(|s| s.len()).sum::<usize>()
|
||||
+ self.secrets.iter().map(|s| s.len()).sum::<usize>()
|
||||
+ self.member_of.len() * U32_LEN
|
||||
+ self.description.as_ref().map(|s| s.len()).unwrap_or(0),
|
||||
)
|
||||
.write(1u8)
|
||||
@@ -69,11 +68,6 @@ impl Serialize for &Principal<u32> {
|
||||
}
|
||||
}
|
||||
|
||||
serializer = serializer.write_leb128(self.member_of.len());
|
||||
for id in &self.member_of {
|
||||
serializer = serializer.write_leb128(*id);
|
||||
}
|
||||
|
||||
serializer.finalize()
|
||||
}
|
||||
}
|
||||
@@ -134,7 +128,7 @@ fn deserialize(bytes: &[u8]) -> Option<Principal<u32>> {
|
||||
})?,
|
||||
secrets: deserialize_string_list(&mut bytes)?,
|
||||
emails: deserialize_string_list(&mut bytes)?,
|
||||
member_of: deserialize_u32_list(&mut bytes)?,
|
||||
member_of: Vec::new(),
|
||||
}
|
||||
.into()
|
||||
}
|
||||
@@ -155,6 +149,8 @@ pub enum PrincipalField {
|
||||
Emails,
|
||||
#[serde(rename = "memberOf")]
|
||||
MemberOf,
|
||||
#[serde(rename = "members")]
|
||||
Members,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
|
||||
@@ -180,7 +176,6 @@ pub enum PrincipalValue {
|
||||
String(String),
|
||||
StringList(Vec<String>),
|
||||
Integer(u32),
|
||||
Type(Type),
|
||||
}
|
||||
|
||||
impl PrincipalUpdate {
|
||||
@@ -219,6 +214,7 @@ impl Display for PrincipalField {
|
||||
PrincipalField::Secrets => write!(f, "secrets"),
|
||||
PrincipalField::Emails => write!(f, "emails"),
|
||||
PrincipalField::MemberOf => write!(f, "memberOf"),
|
||||
PrincipalField::Members => write!(f, "members"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -241,19 +237,11 @@ fn deserialize_string_list(bytes: &mut Iter<'_, u8>) -> Option<Vec<String>> {
|
||||
Some(list)
|
||||
}
|
||||
|
||||
fn deserialize_u32_list(bytes: &mut Iter<'_, u8>) -> Option<Vec<u32>> {
|
||||
let len = bytes.next_leb128()?;
|
||||
let mut list = Vec::with_capacity(len);
|
||||
for _ in 0..len {
|
||||
list.push(bytes.next_leb128()?);
|
||||
}
|
||||
Some(list)
|
||||
}
|
||||
|
||||
impl Type {
|
||||
pub fn parse(value: &str) -> Option<Self> {
|
||||
match value {
|
||||
"individual" | "superuser" => Some(Type::Individual),
|
||||
"individual" => Some(Type::Individual),
|
||||
"superuser" => Some(Type::Superuser),
|
||||
"group" => Some(Type::Group),
|
||||
"resource" => Some(Type::Resource),
|
||||
"location" => Some(Type::Location),
|
||||
|
||||
@@ -30,7 +30,11 @@ use crate::{backend::internal::manage::ManageDirectory, DirectoryError, Principa
|
||||
use super::{LdapDirectory, LdapMappings};
|
||||
|
||||
impl LdapDirectory {
|
||||
pub async fn query(&self, by: QueryBy<'_>) -> crate::Result<Option<Principal<u32>>> {
|
||||
pub async fn query(
|
||||
&self,
|
||||
by: QueryBy<'_>,
|
||||
return_member_of: bool,
|
||||
) -> crate::Result<Option<Principal<u32>>> {
|
||||
let mut conn = self.pool.get().await?;
|
||||
let mut account_id = None;
|
||||
let account_name;
|
||||
@@ -132,7 +136,7 @@ impl LdapDirectory {
|
||||
principal.name = account_name;
|
||||
|
||||
// Obtain groups
|
||||
if !principal.member_of.is_empty() && self.has_id_store() {
|
||||
if return_member_of && !principal.member_of.is_empty() && self.has_id_store() {
|
||||
for member_of in principal.member_of.iter_mut() {
|
||||
if member_of.contains('=') {
|
||||
let (rs, _res) = conn
|
||||
@@ -165,6 +169,7 @@ impl LdapDirectory {
|
||||
.await
|
||||
.map(Some)
|
||||
} else {
|
||||
principal.member_of.clear();
|
||||
Ok(Some(principal.into()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,11 @@ use crate::{backend::internal::manage::ManageDirectory, Principal, QueryBy, Type
|
||||
use super::{SqlDirectory, SqlMappings};
|
||||
|
||||
impl SqlDirectory {
|
||||
pub async fn query(&self, by: QueryBy<'_>) -> crate::Result<Option<Principal<u32>>> {
|
||||
pub async fn query(
|
||||
&self,
|
||||
by: QueryBy<'_>,
|
||||
return_member_of: bool,
|
||||
) -> crate::Result<Option<Principal<u32>>> {
|
||||
let mut account_id = None;
|
||||
let account_name;
|
||||
let mut secret = None;
|
||||
@@ -106,7 +110,7 @@ impl SqlDirectory {
|
||||
|
||||
if self.has_id_store() {
|
||||
// Obtain members
|
||||
if !self.mappings.query_members.is_empty() {
|
||||
if return_member_of && !self.mappings.query_members.is_empty() {
|
||||
for row in self
|
||||
.store
|
||||
.query::<Rows>(
|
||||
|
||||
@@ -26,11 +26,15 @@ use crate::{
|
||||
};
|
||||
|
||||
impl Directory {
|
||||
pub async fn query(&self, by: QueryBy<'_>) -> crate::Result<Option<Principal<u32>>> {
|
||||
pub async fn query(
|
||||
&self,
|
||||
by: QueryBy<'_>,
|
||||
return_member_of: bool,
|
||||
) -> crate::Result<Option<Principal<u32>>> {
|
||||
match &self.store {
|
||||
DirectoryInner::Internal(store) => store.query(by).await,
|
||||
DirectoryInner::Ldap(store) => store.query(by).await,
|
||||
DirectoryInner::Sql(store) => store.query(by).await,
|
||||
DirectoryInner::Internal(store) => store.query(by, return_member_of).await,
|
||||
DirectoryInner::Ldap(store) => store.query(by, return_member_of).await,
|
||||
DirectoryInner::Sql(store) => store.query(by, return_member_of).await,
|
||||
DirectoryInner::Imap(store) => store.query(by).await,
|
||||
DirectoryInner::Smtp(store) => store.query(by).await,
|
||||
DirectoryInner::Memory(store) => store.query(by).await,
|
||||
|
||||
Reference in New Issue
Block a user