Files
inbuxa-server/crates/directory/src/core/config.rs
T
jcoffey-dev f72e3bb85c Per-domain directories: each domain signs in against its own directory (DIR-1 to DIR-15)
The two lookups every caller uses now honor Domain.directoryId, then the
server default, then the internal directory, so sign-in, bearer routing,
recipient lookup, discovery, the PACC record and the refusal of password
changes on external accounts all follow the domain. A directoryId, or a
server default, naming a directory that doesn't exist is unavailable,
never the internal directory.

A directory speaks only for the domains it serves: an account it returns
on another directory's domain is refused, for sign-in and recipients
alike, and aliases and group claims on such domains are dropped with a
warning. A bearer token must belong to the user the client names, or the
name must be one of its aliases with alias sign-in allowed. Accounts and
groups that sync creates pass the tenant checks, limits included.
2026-09-19 10:32:57 -07:00

70 lines
2.6 KiB
Rust

/*
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <[email protected]>
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
*/
use crate::{
Directories, Directory, UnavailableDirectory,
backend::{ldap::LdapDirectory, oidc::OpenIdDirectory, sql::SqlDirectory},
};
use registry::schema::{
prelude::ObjectType,
structs::{self, Authentication},
};
use std::{collections::HashMap, sync::Arc};
use store::registry::bootstrap::Bootstrap;
impl Directories {
pub async fn build(bp: &mut Bootstrap) -> Self {
let mut directories = HashMap::default();
for directory in bp.list_infallible::<structs::Directory>().await {
let id = directory.id;
let directory_type = directory.object.object_type();
let result = match directory.object {
structs::Directory::Ldap(directory) => LdapDirectory::open(directory).await,
structs::Directory::Sql(directory) => {
SqlDirectory::open(directory, &bp.data_store).await
}
structs::Directory::Oidc(directory) => OpenIdDirectory::open(directory).await,
};
let directory = match result {
Ok(directory) => directory,
Err(err) => {
bp.build_error(id, err.clone());
Directory::Unavailable(UnavailableDirectory::new(directory_type, err))
}
};
directories.insert(id.id().id() as u32, Arc::new(directory));
}
let auth = bp.setting_infallible::<Authentication>().await;
let default_directory = if let Some(directory_id) = auth.directory_id {
match directories.get(&(directory_id.id() as u32)) {
Some(default_directory) => default_directory.clone().into(),
None => {
bp.build_error(
ObjectType::Authentication.singleton(),
format!("Default directory with ID {} not found", directory_id),
);
// inbuxa: DIR-5: a missing default is unavailable, never the
// internal directory
Some(Arc::new(Directory::Unavailable(UnavailableDirectory::new(
registry::schema::enums::DirectoryType::Ldap,
format!("Default directory with ID {} not found", directory_id),
))))
}
}
} else {
None
};
Directories {
default_directory,
directories,
}
}
}