Fixed registry JMAP helpers
This commit is contained in:
@@ -8,8 +8,8 @@ use super::AccessToken;
|
||||
use crate::{
|
||||
Server,
|
||||
auth::{
|
||||
AccessScope, AccessTo, AccessTokenInner, AccountTenantIds, FALLBACK_ADMIN_ID, Permissions,
|
||||
permissions::{BuildPermissions, build_permissions_list},
|
||||
AccessScope, AccessTo, AccessTokenInner, AccountTenantIds, RECOVERY_ADMIN_ID, Permissions,
|
||||
permissions::{BuildPermissions, PermissionsListBuilder},
|
||||
},
|
||||
network::limiter::{ConcurrencyLimiter, LimiterResult},
|
||||
};
|
||||
@@ -286,7 +286,7 @@ impl Server {
|
||||
self.build_access_token(account, account_id, revision, revision_account)
|
||||
.await?
|
||||
.into()
|
||||
} else if account_id == FALLBACK_ADMIN_ID {
|
||||
} else if account_id == RECOVERY_ADMIN_ID {
|
||||
AccessTokenInner::new_admin().into()
|
||||
} else {
|
||||
return Err(trc::SecurityEvent::Unauthorized
|
||||
@@ -627,12 +627,17 @@ impl AccessToken {
|
||||
|
||||
pub fn permissions(&self) -> Vec<Permission> {
|
||||
if let Some(scope) = self.inner.scopes.get(self.scope_idx) {
|
||||
build_permissions_list(&scope.permissions)
|
||||
scope.permissions.build_permissions_list()
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn access_scope(&self) -> Option<&AccessScope> {
|
||||
self.inner.scopes.get(self.scope_idx)
|
||||
}
|
||||
|
||||
pub(crate) fn permissions_bits(&self) -> &Permissions {
|
||||
&self
|
||||
.inner
|
||||
@@ -773,7 +778,7 @@ impl AccessTokenInner {
|
||||
|
||||
pub fn new_admin() -> Self {
|
||||
AccessTokenInner {
|
||||
account_id: FALLBACK_ADMIN_ID,
|
||||
account_id: RECOVERY_ADMIN_ID,
|
||||
tenant_id: Default::default(),
|
||||
member_of: Default::default(),
|
||||
access_to: Default::default(),
|
||||
|
||||
@@ -32,7 +32,7 @@ pub mod oauth;
|
||||
pub mod permissions;
|
||||
pub mod rate_limit;
|
||||
|
||||
pub const FALLBACK_ADMIN_ID: u32 = u32::MAX;
|
||||
pub const RECOVERY_ADMIN_ID: u32 = u32::MAX;
|
||||
const PERMISSIONS_BITSET_SIZE: usize = Permission::COUNT.div_ceil(std::mem::size_of::<usize>());
|
||||
pub type Permissions = Bitset<PERMISSIONS_BITSET_SIZE>;
|
||||
|
||||
@@ -141,7 +141,7 @@ pub struct AccessTokenInner {
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Hash, Clone)]
|
||||
pub(crate) struct AccessScope {
|
||||
pub struct AccessScope {
|
||||
pub permissions: Permissions,
|
||||
pub credential_id: u32,
|
||||
pub expires_at: u64,
|
||||
|
||||
@@ -154,30 +154,36 @@ impl AccessToken {
|
||||
if requested_permissions.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(build_permissions_list(&requested_permissions))
|
||||
Err(requested_permissions.build_permissions_list())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn build_permissions_list(permissions_in: &Permissions) -> Vec<Permission> {
|
||||
const USIZE_BITS: usize = std::mem::size_of::<usize>() * 8;
|
||||
const USIZE_MASK: u32 = USIZE_BITS as u32 - 1;
|
||||
let mut permissions = Vec::new();
|
||||
pub trait PermissionsListBuilder {
|
||||
fn build_permissions_list(&self) -> Vec<Permission>;
|
||||
}
|
||||
|
||||
for (block_num, bytes) in permissions_in.inner().iter().enumerate() {
|
||||
let mut bytes = *bytes;
|
||||
impl PermissionsListBuilder for Permissions {
|
||||
fn build_permissions_list(&self) -> Vec<Permission> {
|
||||
const USIZE_BITS: usize = std::mem::size_of::<usize>() * 8;
|
||||
const USIZE_MASK: u32 = USIZE_BITS as u32 - 1;
|
||||
let mut permissions = Vec::new();
|
||||
|
||||
while bytes != 0 {
|
||||
let item = USIZE_MASK - bytes.leading_zeros();
|
||||
bytes ^= 1 << item;
|
||||
if let Some(permission) =
|
||||
Permission::from_id(((block_num * USIZE_BITS) + item as usize) as u16)
|
||||
{
|
||||
permissions.push(permission);
|
||||
for (block_num, bytes) in self.inner().iter().enumerate() {
|
||||
let mut bytes = *bytes;
|
||||
|
||||
while bytes != 0 {
|
||||
let item = USIZE_MASK - bytes.leading_zeros();
|
||||
bytes ^= 1 << item;
|
||||
if let Some(permission) =
|
||||
Permission::from_id(((block_num * USIZE_BITS) + item as usize) as u16)
|
||||
{
|
||||
permissions.push(permission);
|
||||
}
|
||||
}
|
||||
}
|
||||
permissions
|
||||
}
|
||||
permissions
|
||||
}
|
||||
|
||||
pub struct DefaultPermissions {
|
||||
|
||||
8
crates/common/src/cache/principals.rs
vendored
8
crates/common/src/cache/principals.rs
vendored
@@ -11,8 +11,8 @@ use crate::{
|
||||
ACCOUNT_FLAG_ENCRYPT_APPEND, ACCOUNT_FLAG_ENCRYPT_METHOD_PGP,
|
||||
ACCOUNT_FLAG_ENCRYPT_METHOD_SMIME, ACCOUNT_FLAG_ENCRYPT_TRAIN_SPAM_FILTER, ACCOUNT_IS_USER,
|
||||
AccountCache, AccountInfo, AccountTenantIds, DOMAIN_FLAG_RELAY, DOMAIN_FLAG_SUB_ADDRESSING,
|
||||
DomainCache, EmailAddress, EmailAddressRef, EmailCache, FALLBACK_ADMIN_ID,
|
||||
MailingListCache, PermissionsGroup, RoleCache, TenantCache, permissions::BuildPermissions,
|
||||
DomainCache, EmailAddress, EmailAddressRef, EmailCache, MailingListCache, PermissionsGroup,
|
||||
RECOVERY_ADMIN_ID, RoleCache, TenantCache, permissions::BuildPermissions,
|
||||
},
|
||||
config::smtp::auth::DkimSigner,
|
||||
expr::if_block::BootstrapExprExt,
|
||||
@@ -281,7 +281,7 @@ impl Server {
|
||||
pub async fn account(&self, account_id: u32) -> trc::Result<Arc<AccountCache>> {
|
||||
if let Some(account) = self.try_account(account_id).await? {
|
||||
Ok(account)
|
||||
} else if account_id == FALLBACK_ADMIN_ID {
|
||||
} else if account_id == RECOVERY_ADMIN_ID {
|
||||
Ok(Arc::new(AccountCache {
|
||||
name: self
|
||||
.registry()
|
||||
@@ -289,7 +289,7 @@ impl Server {
|
||||
.map(|(name, _)| name.as_str())
|
||||
.unwrap_or("recovery-admin")
|
||||
.into(),
|
||||
id: FALLBACK_ADMIN_ID,
|
||||
id: RECOVERY_ADMIN_ID,
|
||||
addresses: Default::default(),
|
||||
id_tenant: Default::default(),
|
||||
id_member_of: Default::default(),
|
||||
|
||||
@@ -89,8 +89,9 @@ impl EmailConfig {
|
||||
let auth = bp.setting_infallible::<Authentication>().await;
|
||||
|
||||
// Obtain default domain name
|
||||
let default_domain_name = if let Some(default_domain) =
|
||||
bp.get_infallible::<Domain>(system.default_domain_id).await
|
||||
let default_domain_name = if system.default_domain_id.is_valid()
|
||||
&& let Some(default_domain) =
|
||||
bp.get_infallible::<Domain>(system.default_domain_id).await
|
||||
{
|
||||
default_domain.name
|
||||
} else {
|
||||
|
||||
@@ -136,6 +136,12 @@ impl BootManager {
|
||||
.await
|
||||
.failed("⚠️ Startup failed");
|
||||
let mut bootstrap = Bootstrap::new(registry).await;
|
||||
|
||||
if matches!(import_export, StoreOp::None) {
|
||||
// Add safe defaults if missing
|
||||
bootstrap.insert_safe_defaults().await;
|
||||
}
|
||||
|
||||
let todo = "implement recovery mode, check env_recovery_mode in RegistryStoreInner";
|
||||
|
||||
// Start listeners
|
||||
@@ -150,9 +156,6 @@ impl BootManager {
|
||||
|
||||
match import_export {
|
||||
StoreOp::None => {
|
||||
// Add safe defaults if missing
|
||||
bootstrap.insert_safe_defaults().await;
|
||||
|
||||
// Parse components
|
||||
let core = Box::pin(Core::parse(&mut bootstrap, storage)).await;
|
||||
let data = Data::parse(&mut bootstrap).await;
|
||||
|
||||
@@ -24,6 +24,7 @@ use store::{
|
||||
bootstrap::Bootstrap,
|
||||
write::{RegistryWrite, RegistryWriteResult},
|
||||
},
|
||||
write::BatchBuilder,
|
||||
};
|
||||
use types::id::Id;
|
||||
|
||||
@@ -381,88 +382,12 @@ async fn insert_safe_defaults(bp: &mut Bootstrap) -> trc::Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
let mut default_domain_id = None;
|
||||
if bp.registry.count_object(ObjectType::Domain).await? == 0 {
|
||||
match bp
|
||||
.registry
|
||||
.write(RegistryWrite::insert(
|
||||
&Domain {
|
||||
name: psl::domain_str(bp.registry.local_hostname())
|
||||
.unwrap_or("localhost.localdomain")
|
||||
.to_string(),
|
||||
is_enabled: true,
|
||||
..Default::default()
|
||||
}
|
||||
.into(),
|
||||
))
|
||||
.await?
|
||||
{
|
||||
RegistryWriteResult::Success(id) => {
|
||||
default_domain_id = Some(id);
|
||||
}
|
||||
err => {
|
||||
bp.build_error(
|
||||
ObjectType::Domain.singleton(),
|
||||
format!("Failed to insert default domain: {err}"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let todo = "review";
|
||||
//#[cfg(not(feature = "test_mode"))]
|
||||
/*if let Some(domain_id) = default_domain_id {
|
||||
let now = store::write::now();
|
||||
let signature_rsa = DkimSignature::Dkim1RsaSha256(Dkim1Signature {
|
||||
domain_id,
|
||||
stage: DkimSignatureStage::Testing,
|
||||
selector: format!("rsa-{now}"),
|
||||
private_key: DkimPrivateKey::Value(SecretTextValue {
|
||||
secret: crate::network::dkim::generate_dkim_private_key(
|
||||
DkimSignatureType::Dkim1RsaSha256,
|
||||
)
|
||||
.await?
|
||||
.map_err(|err| {
|
||||
trc::EventType::Dkim(trc::DkimEvent::BuildError)
|
||||
.into_err()
|
||||
.reason(err)
|
||||
.caused_by(trc::location!())
|
||||
})?,
|
||||
}),
|
||||
..Default::default()
|
||||
});
|
||||
let signature_ed = DkimSignature::Dkim1Ed25519Sha256(Dkim1Signature {
|
||||
domain_id,
|
||||
enabled: true,
|
||||
selector: format!("ed-{now}"),
|
||||
private_key: DkimPrivateKey::Value(SecretTextValue {
|
||||
secret: crate::network::dkim::generate_dkim_private_key(
|
||||
DkimSignatureType::Dkim1Ed25519Sha256,
|
||||
)
|
||||
.await?
|
||||
.map_err(|err| {
|
||||
trc::EventType::Dkim(trc::DkimEvent::BuildError)
|
||||
.into_err()
|
||||
.reason(err)
|
||||
.caused_by(trc::location!())
|
||||
})?,
|
||||
}),
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
for signature in [signature_rsa, signature_ed] {
|
||||
bp.registry
|
||||
.write(RegistryWrite::insert(&signature.into()))
|
||||
.await?;
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
#[cfg(not(any(feature = "dev_mode", feature = "test_mode")))]
|
||||
if bp.registry.count_object(ObjectType::SystemSettings).await? == 0 {
|
||||
bp.registry
|
||||
.write(RegistryWrite::insert(
|
||||
&SystemSettings {
|
||||
default_hostname: bp.registry.local_hostname().to_string(),
|
||||
default_domain_id: default_domain_id.unwrap_or(Id::new(0)),
|
||||
..Default::default()
|
||||
}
|
||||
.into(),
|
||||
@@ -548,5 +473,20 @@ async fn insert_safe_defaults(bp: &mut Bootstrap) -> trc::Result<()> {
|
||||
.await?;
|
||||
}
|
||||
|
||||
if bp.registry.count_object(ObjectType::SpamRule).await? == 0
|
||||
&& bp
|
||||
.registry
|
||||
.object::<SpamSettings>(Id::singleton())
|
||||
.await?
|
||||
.is_none_or(|spam| spam.spam_filter_rules_url.is_some())
|
||||
{
|
||||
let mut batch = BatchBuilder::new();
|
||||
batch.schedule_task(Task::SpamFilterMaintenance(TaskSpamFilterMaintenance {
|
||||
maintenance_type: TaskSpamFilterMaintenanceType::UpdateRules,
|
||||
status: TaskStatus::now(),
|
||||
}));
|
||||
bp.data_store.write(batch.build_all()).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user