mirror of
https://github.com/kanidm/kanidm.git
synced 2025-05-20 07:53:55 +02:00
Merge 82ca92cd4e
into 0e0e8ff844
This commit is contained in:
commit
8c0de4cd01
Cargo.lockCargo.toml
libs/client/src
proto/src/internal
server
core/src
daemon
lib-macros
lib/src
testkit-macros
testkit
Cargo.toml
defunct_examples
defunct_tests
src
tests
tools
unix_integration/resolver
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -3247,7 +3247,6 @@ dependencies = [
|
|||
"escargot",
|
||||
"fantoccini",
|
||||
"futures",
|
||||
"http 1.2.0",
|
||||
"jsonschema",
|
||||
"kanidm_build_profiles",
|
||||
"kanidm_client",
|
||||
|
@ -3255,6 +3254,7 @@ dependencies = [
|
|||
"kanidm_proto",
|
||||
"kanidmd_core",
|
||||
"kanidmd_lib",
|
||||
"ldap3_client",
|
||||
"oauth2 4.4.2",
|
||||
"openssl",
|
||||
"petgraph",
|
||||
|
|
|
@ -241,6 +241,7 @@ reqwest = { version = "0.12.12", default-features = false, features = [
|
|||
"json",
|
||||
"gzip",
|
||||
"rustls-tls-native-roots",
|
||||
"rustls-tls-native-roots-no-provider",
|
||||
] }
|
||||
rusqlite = { version = "^0.28.0", features = ["array", "bundled"] }
|
||||
rustls = { version = "0.23.21", default-features = false, features = [
|
||||
|
|
|
@ -27,6 +27,7 @@ use std::time::Duration;
|
|||
|
||||
use compact_jwt::Jwk;
|
||||
|
||||
pub use http;
|
||||
use kanidm_proto::constants::uri::V1_AUTH_VALID;
|
||||
use kanidm_proto::constants::{
|
||||
ATTR_DOMAIN_DISPLAY_NAME, ATTR_DOMAIN_LDAP_BASEDN, ATTR_DOMAIN_SSID, ATTR_ENTRY_MANAGED_BY,
|
||||
|
@ -137,6 +138,7 @@ pub struct KanidmClientBuilder {
|
|||
use_system_proxies: bool,
|
||||
/// Where to store auth tokens, only use in testing!
|
||||
token_cache_path: Option<String>,
|
||||
disable_system_ca_store: bool,
|
||||
}
|
||||
|
||||
impl Display for KanidmClientBuilder {
|
||||
|
@ -170,33 +172,6 @@ impl Display for KanidmClientBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_kanidmclientbuilder_display() {
|
||||
let defaultclient = KanidmClientBuilder::default();
|
||||
println!("{}", defaultclient);
|
||||
assert!(defaultclient.to_string().contains("verify_ca"));
|
||||
|
||||
let testclient = KanidmClientBuilder {
|
||||
address: Some("https://example.com".to_string()),
|
||||
verify_ca: true,
|
||||
verify_hostnames: true,
|
||||
ca: None,
|
||||
connect_timeout: Some(420),
|
||||
request_timeout: Some(69),
|
||||
use_system_proxies: true,
|
||||
token_cache_path: Some(CLIENT_TOKEN_CACHE.to_string()),
|
||||
};
|
||||
println!("testclient {}", testclient);
|
||||
assert!(testclient.to_string().contains("verify_ca: true"));
|
||||
assert!(testclient.to_string().contains("verify_hostnames: true"));
|
||||
|
||||
let badness = testclient.danger_accept_invalid_hostnames(true);
|
||||
let badness = badness.danger_accept_invalid_certs(true);
|
||||
println!("badness: {}", badness);
|
||||
assert!(badness.to_string().contains("verify_ca: false"));
|
||||
assert!(badness.to_string().contains("verify_hostnames: false"));
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct KanidmClient {
|
||||
pub(crate) client: reqwest::Client,
|
||||
|
@ -233,6 +208,7 @@ impl KanidmClientBuilder {
|
|||
request_timeout: None,
|
||||
use_system_proxies: true,
|
||||
token_cache_path: None,
|
||||
disable_system_ca_store: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -290,6 +266,7 @@ impl KanidmClientBuilder {
|
|||
request_timeout,
|
||||
use_system_proxies,
|
||||
token_cache_path,
|
||||
disable_system_ca_store,
|
||||
} = self;
|
||||
// Process and apply all our options if they exist.
|
||||
let address = match kcc.uri {
|
||||
|
@ -316,6 +293,7 @@ impl KanidmClientBuilder {
|
|||
request_timeout,
|
||||
use_system_proxies,
|
||||
token_cache_path,
|
||||
disable_system_ca_store,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -416,6 +394,16 @@ impl KanidmClientBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
/// Enable or disable the native ca roots. By default these roots are enabled.
|
||||
pub fn enable_native_ca_roots(self, enable: bool) -> Self {
|
||||
KanidmClientBuilder {
|
||||
// We have to flip the bool state here due to Default on bool being false
|
||||
// and we want our options to be positive to a native speaker.
|
||||
disable_system_ca_store: !enable,
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn danger_accept_invalid_hostnames(self, accept_invalid_hostnames: bool) -> Self {
|
||||
KanidmClientBuilder {
|
||||
// We have to flip the bool state here due to english language.
|
||||
|
@ -527,6 +515,7 @@ impl KanidmClientBuilder {
|
|||
// implement sticky sessions with cookies.
|
||||
.cookie_store(true)
|
||||
.cookie_provider(client_cookies.clone())
|
||||
.tls_built_in_native_certs(!self.disable_system_ca_store)
|
||||
.danger_accept_invalid_hostnames(!self.verify_hostnames)
|
||||
.danger_accept_invalid_certs(!self.verify_ca);
|
||||
|
||||
|
@ -579,32 +568,6 @@ impl KanidmClientBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_make_url() {
|
||||
use kanidm_proto::constants::DEFAULT_SERVER_ADDRESS;
|
||||
let client: KanidmClient = KanidmClientBuilder::new()
|
||||
.address(format!("https://{}", DEFAULT_SERVER_ADDRESS))
|
||||
.build()
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
client.get_url(),
|
||||
Url::parse(&format!("https://{}", DEFAULT_SERVER_ADDRESS)).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
client.make_url("/hello"),
|
||||
Url::parse(&format!("https://{}/hello", DEFAULT_SERVER_ADDRESS)).unwrap()
|
||||
);
|
||||
|
||||
let client: KanidmClient = KanidmClientBuilder::new()
|
||||
.address(format!("https://{}/cheese/", DEFAULT_SERVER_ADDRESS))
|
||||
.build()
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
client.make_url("hello"),
|
||||
Url::parse(&format!("https://{}/cheese/hello", DEFAULT_SERVER_ADDRESS)).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
/// This is probably pretty jank but it works and was pulled from here:
|
||||
/// <https://github.com/seanmonstar/reqwest/issues/1602#issuecomment-1220996681>
|
||||
fn find_reqwest_error_source<E: std::error::Error + 'static>(
|
||||
|
@ -623,6 +586,11 @@ fn find_reqwest_error_source<E: std::error::Error + 'static>(
|
|||
}
|
||||
|
||||
impl KanidmClient {
|
||||
/// Access the underlying reqwest client that has been configured for this Kanidm server
|
||||
pub fn client(&self) -> &reqwest::Client {
|
||||
&self.client
|
||||
}
|
||||
|
||||
pub fn get_origin(&self) -> &Url {
|
||||
&self.origin
|
||||
}
|
||||
|
@ -2174,31 +2142,97 @@ impl KanidmClient {
|
|||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_no_client_version_check_on_502() {
|
||||
let res = reqwest::Response::from(
|
||||
http::Response::builder()
|
||||
.status(StatusCode::GATEWAY_TIMEOUT)
|
||||
.body("")
|
||||
.unwrap(),
|
||||
);
|
||||
let client = KanidmClientBuilder::new()
|
||||
.address("http://localhost:8080".to_string())
|
||||
.build()
|
||||
.expect("Failed to build client");
|
||||
eprintln!("This should pass because we are returning 504 and shouldn't check version...");
|
||||
client.expect_version(&res).await;
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{KanidmClient, KanidmClientBuilder};
|
||||
use kanidm_proto::constants::CLIENT_TOKEN_CACHE;
|
||||
use reqwest::StatusCode;
|
||||
use url::Url;
|
||||
|
||||
let res = reqwest::Response::from(
|
||||
http::Response::builder()
|
||||
.status(StatusCode::BAD_GATEWAY)
|
||||
.body("")
|
||||
.unwrap(),
|
||||
);
|
||||
let client = KanidmClientBuilder::new()
|
||||
.address("http://localhost:8080".to_string())
|
||||
.build()
|
||||
.expect("Failed to build client");
|
||||
eprintln!("This should pass because we are returning 502 and shouldn't check version...");
|
||||
client.expect_version(&res).await;
|
||||
#[tokio::test]
|
||||
async fn test_no_client_version_check_on_502() {
|
||||
let res = reqwest::Response::from(
|
||||
http::Response::builder()
|
||||
.status(StatusCode::GATEWAY_TIMEOUT)
|
||||
.body("")
|
||||
.unwrap(),
|
||||
);
|
||||
let client = KanidmClientBuilder::new()
|
||||
.address("http://localhost:8080".to_string())
|
||||
.enable_native_ca_roots(false)
|
||||
.build()
|
||||
.expect("Failed to build client");
|
||||
eprintln!("This should pass because we are returning 504 and shouldn't check version...");
|
||||
client.expect_version(&res).await;
|
||||
|
||||
let res = reqwest::Response::from(
|
||||
http::Response::builder()
|
||||
.status(StatusCode::BAD_GATEWAY)
|
||||
.body("")
|
||||
.unwrap(),
|
||||
);
|
||||
let client = KanidmClientBuilder::new()
|
||||
.address("http://localhost:8080".to_string())
|
||||
.enable_native_ca_roots(false)
|
||||
.build()
|
||||
.expect("Failed to build client");
|
||||
eprintln!("This should pass because we are returning 502 and shouldn't check version...");
|
||||
client.expect_version(&res).await;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_make_url() {
|
||||
use kanidm_proto::constants::DEFAULT_SERVER_ADDRESS;
|
||||
let client: KanidmClient = KanidmClientBuilder::new()
|
||||
.address(format!("https://{}", DEFAULT_SERVER_ADDRESS))
|
||||
.enable_native_ca_roots(false)
|
||||
.build()
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
client.get_url(),
|
||||
Url::parse(&format!("https://{}", DEFAULT_SERVER_ADDRESS)).unwrap()
|
||||
);
|
||||
assert_eq!(
|
||||
client.make_url("/hello"),
|
||||
Url::parse(&format!("https://{}/hello", DEFAULT_SERVER_ADDRESS)).unwrap()
|
||||
);
|
||||
|
||||
let client: KanidmClient = KanidmClientBuilder::new()
|
||||
.address(format!("https://{}/cheese/", DEFAULT_SERVER_ADDRESS))
|
||||
.enable_native_ca_roots(false)
|
||||
.build()
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
client.make_url("hello"),
|
||||
Url::parse(&format!("https://{}/cheese/hello", DEFAULT_SERVER_ADDRESS)).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_kanidmclientbuilder_display() {
|
||||
let defaultclient = KanidmClientBuilder::default();
|
||||
println!("{}", defaultclient);
|
||||
assert!(defaultclient.to_string().contains("verify_ca"));
|
||||
|
||||
let testclient = KanidmClientBuilder {
|
||||
address: Some("https://example.com".to_string()),
|
||||
verify_ca: true,
|
||||
verify_hostnames: true,
|
||||
ca: None,
|
||||
connect_timeout: Some(420),
|
||||
request_timeout: Some(69),
|
||||
use_system_proxies: true,
|
||||
token_cache_path: Some(CLIENT_TOKEN_CACHE.to_string()),
|
||||
disable_system_ca_store: false,
|
||||
};
|
||||
println!("testclient {}", testclient);
|
||||
assert!(testclient.to_string().contains("verify_ca: true"));
|
||||
assert!(testclient.to_string().contains("verify_hostnames: true"));
|
||||
|
||||
let badness = testclient.danger_accept_invalid_hostnames(true);
|
||||
let badness = badness.danger_accept_invalid_certs(true);
|
||||
println!("badness: {}", badness);
|
||||
assert!(badness.to_string().contains("verify_ca: false"));
|
||||
assert!(badness.to_string().contains("verify_hostnames: false"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -222,6 +222,8 @@ pub enum OperationError {
|
|||
MG0006SKConstraintsNotMet,
|
||||
MG0007Oauth2StrictConstraintsNotMet,
|
||||
MG0008SkipUpgradeAttempted,
|
||||
MG0009InvalidTargetLevelForBootstrap,
|
||||
MG0010MigrationDataMissingUuid,
|
||||
//
|
||||
KP0001KeyProviderNotLoaded,
|
||||
KP0002KeyProviderInvalidClass,
|
||||
|
@ -462,6 +464,8 @@ impl OperationError {
|
|||
Self::MG0006SKConstraintsNotMet => Some("Migration Constraints Not Met - Security Keys should not be present.".into()),
|
||||
Self::MG0007Oauth2StrictConstraintsNotMet => Some("Migration Constraints Not Met - All OAuth2 clients must have strict-redirect-uri mode enabled.".into()),
|
||||
Self::MG0008SkipUpgradeAttempted => Some("Skip Upgrade Attempted.".into()),
|
||||
Self::MG0009InvalidTargetLevelForBootstrap => Some("The request target domain level was not valid for bootstrapping a new server instance".into()),
|
||||
Self::MG0010MigrationDataMissingUuid => Some("A migration entry was found to be invalid and missing a uuid.".into()),
|
||||
Self::PL0001GidOverlapsSystemRange => None,
|
||||
Self::SC0001IncomingSshPublicKey => None,
|
||||
Self::SC0002ReferenceSyntaxInvalid => Some("A SCIM Reference Set contained invalid syntax and can not be processed.".into()),
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
use std::net;
|
||||
use std::pin::Pin;
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::actors::QueryServerReadV1;
|
||||
use crate::CoreAction;
|
||||
use futures_util::sink::SinkExt;
|
||||
use futures_util::stream::StreamExt;
|
||||
use kanidmd_lib::idm::ldap::{LdapBoundToken, LdapResponseState};
|
||||
|
@ -10,13 +7,15 @@ use kanidmd_lib::prelude::*;
|
|||
use ldap3_proto::proto::LdapMsg;
|
||||
use ldap3_proto::LdapCodec;
|
||||
use openssl::ssl::{Ssl, SslAcceptor};
|
||||
use std::net;
|
||||
use std::pin::Pin;
|
||||
use std::str::FromStr;
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio_openssl::SslStream;
|
||||
use tokio_util::codec::{FramedRead, FramedWrite};
|
||||
|
||||
use crate::CoreAction;
|
||||
use tokio::sync::broadcast;
|
||||
use tokio::sync::mpsc;
|
||||
use tokio_openssl::SslStream;
|
||||
use tokio_util::codec::{FramedRead, FramedWrite};
|
||||
|
||||
struct LdapSession {
|
||||
uat: Option<LdapBoundToken>,
|
||||
|
@ -49,28 +48,14 @@ async fn client_process_msg(
|
|||
.await
|
||||
}
|
||||
|
||||
async fn client_process(
|
||||
tcpstream: TcpStream,
|
||||
tls_acceptor: SslAcceptor,
|
||||
async fn client_process<STREAM>(
|
||||
stream: STREAM,
|
||||
client_address: net::SocketAddr,
|
||||
qe_r_ref: &'static QueryServerReadV1,
|
||||
) {
|
||||
// Start the event
|
||||
// From the parameters we need to create an SslContext.
|
||||
let mut tlsstream = match Ssl::new(tls_acceptor.context())
|
||||
.and_then(|tls_obj| SslStream::new(tls_obj, tcpstream))
|
||||
{
|
||||
Ok(ta) => ta,
|
||||
Err(e) => {
|
||||
error!("LDAP TLS setup error, continuing -> {:?}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
if let Err(e) = SslStream::accept(Pin::new(&mut tlsstream)).await {
|
||||
error!("LDAP TLS accept error, continuing -> {:?}", e);
|
||||
return;
|
||||
};
|
||||
let (r, w) = tokio::io::split(tlsstream);
|
||||
) where
|
||||
STREAM: AsyncRead + AsyncWrite,
|
||||
{
|
||||
let (r, w) = tokio::io::split(stream);
|
||||
let mut r = FramedRead::new(r, LdapCodec::default());
|
||||
let mut w = FramedWrite::new(w, LdapCodec::default());
|
||||
|
||||
|
@ -126,7 +111,32 @@ async fn client_process(
|
|||
}
|
||||
}
|
||||
|
||||
/// TLS LDAP Listener, hands off to [client_process]
|
||||
async fn client_tls_accept(
|
||||
tcpstream: TcpStream,
|
||||
tls_acceptor: SslAcceptor,
|
||||
client_socket_addr: net::SocketAddr,
|
||||
qe_r_ref: &'static QueryServerReadV1,
|
||||
) {
|
||||
// Start the event
|
||||
// From the parameters we need to create an SslContext.
|
||||
let mut tlsstream = match Ssl::new(tls_acceptor.context())
|
||||
.and_then(|tls_obj| SslStream::new(tls_obj, tcpstream))
|
||||
{
|
||||
Ok(ta) => ta,
|
||||
Err(err) => {
|
||||
error!(?err, %client_socket_addr, "LDAP TLS setup error");
|
||||
return;
|
||||
}
|
||||
};
|
||||
if let Err(err) = SslStream::accept(Pin::new(&mut tlsstream)).await {
|
||||
error!(?err, %client_socket_addr, "LDAP TLS accept error");
|
||||
return;
|
||||
};
|
||||
|
||||
tokio::spawn(client_process(tlsstream, client_socket_addr, qe_r_ref));
|
||||
}
|
||||
|
||||
/// TLS LDAP Listener, hands off to [client_tls_accept]
|
||||
async fn ldap_tls_acceptor(
|
||||
listener: TcpListener,
|
||||
mut tls_acceptor: SslAcceptor,
|
||||
|
@ -145,10 +155,10 @@ async fn ldap_tls_acceptor(
|
|||
match accept_result {
|
||||
Ok((tcpstream, client_socket_addr)) => {
|
||||
let clone_tls_acceptor = tls_acceptor.clone();
|
||||
tokio::spawn(client_process(tcpstream, clone_tls_acceptor, client_socket_addr, qe_r_ref));
|
||||
tokio::spawn(client_tls_accept(tcpstream, clone_tls_acceptor, client_socket_addr, qe_r_ref));
|
||||
}
|
||||
Err(e) => {
|
||||
error!("LDAP acceptor error, continuing -> {:?}", e);
|
||||
Err(err) => {
|
||||
warn!(?err, "LDAP acceptor error, continuing");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -161,6 +171,34 @@ async fn ldap_tls_acceptor(
|
|||
info!("Stopped {}", super::TaskName::LdapActor);
|
||||
}
|
||||
|
||||
/// TLS LDAP Listener, hands off to [client_process]
|
||||
async fn ldap_plaintext_acceptor(
|
||||
listener: TcpListener,
|
||||
qe_r_ref: &'static QueryServerReadV1,
|
||||
mut rx: broadcast::Receiver<CoreAction>,
|
||||
) {
|
||||
loop {
|
||||
tokio::select! {
|
||||
Ok(action) = rx.recv() => {
|
||||
match action {
|
||||
CoreAction::Shutdown => break,
|
||||
}
|
||||
}
|
||||
accept_result = listener.accept() => {
|
||||
match accept_result {
|
||||
Ok((tcpstream, client_socket_addr)) => {
|
||||
tokio::spawn(client_process(tcpstream, client_socket_addr, qe_r_ref));
|
||||
}
|
||||
Err(e) => {
|
||||
error!("LDAP acceptor error, continuing -> {:?}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
info!("Stopped {}", super::TaskName::LdapActor);
|
||||
}
|
||||
|
||||
pub(crate) async fn create_ldap_server(
|
||||
address: &str,
|
||||
opt_ssl_acceptor: Option<SslAcceptor>,
|
||||
|
@ -197,10 +235,7 @@ pub(crate) async fn create_ldap_server(
|
|||
tls_acceptor_reload_rx,
|
||||
))
|
||||
}
|
||||
None => {
|
||||
error!("The server won't run without TLS!");
|
||||
return Err(());
|
||||
}
|
||||
None => tokio::spawn(ldap_plaintext_acceptor(listener, qe_r_ref, rx)),
|
||||
};
|
||||
|
||||
info!("Created LDAP interface");
|
||||
|
|
|
@ -115,9 +115,9 @@ async fn setup_qs_idms(
|
|||
.await?;
|
||||
|
||||
// We generate a SINGLE idms only!
|
||||
|
||||
let is_integration_test = config.integration_test_config.is_some();
|
||||
let (idms, idms_delayed, idms_audit) =
|
||||
IdmServer::new(query_server.clone(), &config.origin).await?;
|
||||
IdmServer::new(query_server.clone(), &config.origin, is_integration_test).await?;
|
||||
|
||||
Ok((query_server, idms, idms_delayed, idms_audit))
|
||||
}
|
||||
|
@ -1080,20 +1080,15 @@ pub async fn create_server_core(
|
|||
Some(la) => {
|
||||
let opt_ldap_ssl_acceptor = maybe_tls_acceptor.clone();
|
||||
|
||||
if !config_test {
|
||||
// ⚠️ only start the sockets and listeners in non-config-test modes.
|
||||
let h = ldaps::create_ldap_server(
|
||||
la.as_str(),
|
||||
opt_ldap_ssl_acceptor,
|
||||
server_read_ref,
|
||||
broadcast_tx.subscribe(),
|
||||
ldap_tls_acceptor_reload_rx,
|
||||
)
|
||||
.await?;
|
||||
Some(h)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
let h = ldaps::create_ldap_server(
|
||||
la.as_str(),
|
||||
opt_ldap_ssl_acceptor,
|
||||
server_read_ref,
|
||||
broadcast_tx.subscribe(),
|
||||
ldap_tls_acceptor_reload_rx,
|
||||
)
|
||||
.await?;
|
||||
Some(h)
|
||||
}
|
||||
None => {
|
||||
debug!("LDAP not requested, skipping");
|
||||
|
|
|
@ -16,7 +16,7 @@ repository = { workspace = true }
|
|||
[[bin]]
|
||||
name = "kanidmd"
|
||||
path = "src/main.rs"
|
||||
test = true
|
||||
test = false
|
||||
doctest = false
|
||||
|
||||
[features]
|
||||
|
|
|
@ -13,7 +13,7 @@ repository = { workspace = true }
|
|||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
test = true
|
||||
test = false
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
|
|
|
@ -34,7 +34,7 @@ fn parse_attributes(
|
|||
});
|
||||
|
||||
if !args_are_allowed {
|
||||
let msg = "Invalid test config attribute. The following are allow";
|
||||
let msg = "Invalid test config attribute. The following are allowed";
|
||||
return Err(syn::Error::new_spanned(
|
||||
input.sig.fn_token,
|
||||
format!("{}: {}", msg, ALLOWED_ATTRIBUTES.join(", ")),
|
||||
|
|
|
@ -18,7 +18,8 @@ use crate::be::idl_sqlite::{
|
|||
IdlSqlite, IdlSqliteReadTransaction, IdlSqliteTransaction, IdlSqliteWriteTransaction,
|
||||
};
|
||||
use crate::be::idxkey::{
|
||||
IdlCacheKey, IdlCacheKeyRef, IdlCacheKeyToRef, IdxKey, IdxKeyRef, IdxKeyToRef, IdxSlope,
|
||||
IdlCacheKey, IdlCacheKeyRef, IdlCacheKeyToRef, IdxKey, IdxKeyRef, IdxKeyToRef, IdxNameKey,
|
||||
IdxSlope,
|
||||
};
|
||||
use crate::be::keystorage::{KeyHandle, KeyHandleId};
|
||||
use crate::be::{BackendConfig, IdList, IdRawEntry};
|
||||
|
@ -35,6 +36,10 @@ const DEFAULT_NAME_CACHE_RATIO: usize = 8;
|
|||
const DEFAULT_CACHE_RMISS: usize = 0;
|
||||
const DEFAULT_CACHE_WMISS: usize = 0;
|
||||
|
||||
const DEFAULT_IDX_CACHE_RMISS: usize = 8;
|
||||
const DEFAULT_IDX_CACHE_WMISS: usize = 16;
|
||||
const DEFAULT_IDX_EXISTS_TARGET: usize = 256;
|
||||
|
||||
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
|
||||
enum NameCacheKey {
|
||||
Name2Uuid(String),
|
||||
|
@ -55,6 +60,9 @@ pub struct IdlArcSqlite {
|
|||
entry_cache: ARCache<u64, Arc<EntrySealedCommitted>>,
|
||||
idl_cache: ARCache<IdlCacheKey, Box<IDLBitRange>>,
|
||||
name_cache: ARCache<NameCacheKey, NameCacheValue>,
|
||||
|
||||
idx_exists_cache: ARCache<IdxNameKey, bool>,
|
||||
|
||||
op_ts_max: CowCell<Option<Duration>>,
|
||||
allids: CowCell<IDLBitRange>,
|
||||
maxid: CowCell<u64>,
|
||||
|
@ -66,6 +74,8 @@ pub struct IdlArcSqliteReadTransaction<'a> {
|
|||
entry_cache: ARCacheReadTxn<'a, u64, Arc<EntrySealedCommitted>, ()>,
|
||||
idl_cache: ARCacheReadTxn<'a, IdlCacheKey, Box<IDLBitRange>, ()>,
|
||||
name_cache: ARCacheReadTxn<'a, NameCacheKey, NameCacheValue, ()>,
|
||||
|
||||
idx_exists_cache: ARCacheReadTxn<'a, IdxNameKey, bool, ()>,
|
||||
allids: CowCellReadTxn<IDLBitRange>,
|
||||
}
|
||||
|
||||
|
@ -74,6 +84,9 @@ pub struct IdlArcSqliteWriteTransaction<'a> {
|
|||
entry_cache: ARCacheWriteTxn<'a, u64, Arc<EntrySealedCommitted>, ()>,
|
||||
idl_cache: ARCacheWriteTxn<'a, IdlCacheKey, Box<IDLBitRange>, ()>,
|
||||
name_cache: ARCacheWriteTxn<'a, NameCacheKey, NameCacheValue, ()>,
|
||||
|
||||
idx_exists_cache: ARCacheWriteTxn<'a, IdxNameKey, bool, ()>,
|
||||
|
||||
op_ts_max: CowCellWriteTxn<'a, Option<Duration>>,
|
||||
allids: CowCellWriteTxn<'a, IDLBitRange>,
|
||||
maxid: CowCellWriteTxn<'a, u64>,
|
||||
|
@ -178,8 +191,8 @@ macro_rules! get_idl {
|
|||
// or smaller type. Perhaps even a small cache of the IdlCacheKeys that
|
||||
// are allocated to reduce some allocs? Probably over thinking it at
|
||||
// this point.
|
||||
//
|
||||
// First attempt to get from this cache.
|
||||
|
||||
// Now attempt to get from this cache.
|
||||
let cache_key = IdlCacheKeyRef {
|
||||
a: $attr,
|
||||
i: $itype,
|
||||
|
@ -195,16 +208,47 @@ macro_rules! get_idl {
|
|||
);
|
||||
return Ok(Some(data.as_ref().clone()));
|
||||
}
|
||||
|
||||
// If it was a miss, does the actually exist in the DB?
|
||||
let idx_key = IdxNameKey {
|
||||
a: $attr.clone(),
|
||||
i: $itype,
|
||||
};
|
||||
let idx_r = $self.idx_exists_cache.get(&idx_key);
|
||||
if idx_r == Some(&false) {
|
||||
// The idx does not exist - bail early.
|
||||
return Ok(None)
|
||||
}
|
||||
|
||||
// The table either exists and we don't have data on it yet,
|
||||
// or it does not exist and we need to hear back from the lower level
|
||||
|
||||
// If miss, get from db *and* insert to the cache.
|
||||
let db_r = $self.db.get_idl($attr, $itype, $idx_key)?;
|
||||
|
||||
if let Some(ref idl) = db_r {
|
||||
if idx_r == None {
|
||||
// It exists, so track that data, because we weren't
|
||||
// previously tracking it.
|
||||
$self.idx_exists_cache.insert(idx_key, true)
|
||||
}
|
||||
|
||||
let ncache_key = IdlCacheKey {
|
||||
a: $attr.clone(),
|
||||
i: $itype.clone(),
|
||||
k: $idx_key.into(),
|
||||
};
|
||||
$self.idl_cache.insert(ncache_key, Box::new(idl.clone()))
|
||||
}
|
||||
} else {
|
||||
// The DB was unable to return this idx because table backing the
|
||||
// idx does not exist. We should cache this to prevent repeat hits
|
||||
// on sqlite until the db does exist, at which point the cache is
|
||||
// cleared anyway.
|
||||
//
|
||||
// NOTE: If the db idx misses it returns Some(empty_set), so this
|
||||
// only caches missing index tables.
|
||||
$self.idx_exists_cache.insert(idx_key, false)
|
||||
};
|
||||
Ok(db_r)
|
||||
}};
|
||||
}
|
||||
|
@ -593,6 +637,7 @@ impl IdlArcSqliteWriteTransaction<'_> {
|
|||
*/
|
||||
self.entry_cache.clear();
|
||||
self.idl_cache.clear();
|
||||
self.idx_exists_cache.clear();
|
||||
self.name_cache.clear();
|
||||
Ok(())
|
||||
}
|
||||
|
@ -604,6 +649,7 @@ impl IdlArcSqliteWriteTransaction<'_> {
|
|||
mut entry_cache,
|
||||
mut idl_cache,
|
||||
mut name_cache,
|
||||
idx_exists_cache,
|
||||
op_ts_max,
|
||||
allids,
|
||||
maxid,
|
||||
|
@ -677,6 +723,7 @@ impl IdlArcSqliteWriteTransaction<'_> {
|
|||
// Can no longer fail from this point.
|
||||
op_ts_max.commit();
|
||||
name_cache.commit();
|
||||
idx_exists_cache.commit();
|
||||
idl_cache.commit();
|
||||
allids.commit();
|
||||
maxid.commit();
|
||||
|
@ -708,6 +755,7 @@ impl IdlArcSqliteWriteTransaction<'_> {
|
|||
*self.maxid = mid;
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip_all)]
|
||||
pub fn write_identries<'b, I>(&'b mut self, mut entries: I) -> Result<(), OperationError>
|
||||
where
|
||||
I: Iterator<Item = &'b Entry<EntrySealed, EntryCommitted>>,
|
||||
|
@ -757,6 +805,7 @@ impl IdlArcSqliteWriteTransaction<'_> {
|
|||
})
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip_all)]
|
||||
pub fn write_idl(
|
||||
&mut self,
|
||||
attr: &Attribute,
|
||||
|
@ -1127,9 +1176,17 @@ impl IdlArcSqliteWriteTransaction<'_> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn create_idx(&self, attr: &Attribute, itype: IndexType) -> Result<(), OperationError> {
|
||||
// We don't need to affect this, so pass it down.
|
||||
self.db.create_idx(attr, itype)
|
||||
pub fn create_idx(&mut self, attr: &Attribute, itype: IndexType) -> Result<(), OperationError> {
|
||||
self.db.create_idx(attr, itype)?;
|
||||
|
||||
// Cache that this exists since we just made it.
|
||||
let idx_key = IdxNameKey {
|
||||
a: attr.clone(),
|
||||
i: itype,
|
||||
};
|
||||
self.idx_exists_cache.insert(idx_key, true);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// ⚠️ - This function will destroy all indexes in the database.
|
||||
|
@ -1141,6 +1198,7 @@ impl IdlArcSqliteWriteTransaction<'_> {
|
|||
debug!("CLEARING CACHE");
|
||||
self.db.danger_purge_idxs().map(|()| {
|
||||
self.idl_cache.clear();
|
||||
self.idx_exists_cache.clear();
|
||||
self.name_cache.clear();
|
||||
})
|
||||
}
|
||||
|
@ -1266,6 +1324,21 @@ impl IdlArcSqlite {
|
|||
OperationError::InvalidState
|
||||
})?;
|
||||
|
||||
let idx_exists_cache = ARCacheBuilder::new()
|
||||
.set_expected_workload(
|
||||
DEFAULT_IDX_EXISTS_TARGET,
|
||||
cfg.pool_size as usize,
|
||||
DEFAULT_IDX_CACHE_RMISS,
|
||||
DEFAULT_IDX_CACHE_WMISS,
|
||||
true,
|
||||
)
|
||||
.set_reader_quiesce(true)
|
||||
.build()
|
||||
.ok_or_else(|| {
|
||||
admin_error!("Failed to construct idx_exists_cache");
|
||||
OperationError::InvalidState
|
||||
})?;
|
||||
|
||||
let allids = CowCell::new(IDLBitRange::new());
|
||||
|
||||
let maxid = CowCell::new(0);
|
||||
|
@ -1279,6 +1352,7 @@ impl IdlArcSqlite {
|
|||
entry_cache,
|
||||
idl_cache,
|
||||
name_cache,
|
||||
idx_exists_cache,
|
||||
op_ts_max,
|
||||
allids,
|
||||
maxid,
|
||||
|
@ -1298,6 +1372,7 @@ impl IdlArcSqlite {
|
|||
let db_read = self.db.read()?;
|
||||
let idl_cache_read = self.idl_cache.read();
|
||||
let name_cache_read = self.name_cache.read();
|
||||
let idx_exists_cache_read = self.idx_exists_cache.read();
|
||||
let allids_read = self.allids.read();
|
||||
|
||||
Ok(IdlArcSqliteReadTransaction {
|
||||
|
@ -1305,6 +1380,7 @@ impl IdlArcSqlite {
|
|||
entry_cache: entry_cache_read,
|
||||
idl_cache: idl_cache_read,
|
||||
name_cache: name_cache_read,
|
||||
idx_exists_cache: idx_exists_cache_read,
|
||||
allids: allids_read,
|
||||
})
|
||||
}
|
||||
|
@ -1315,6 +1391,7 @@ impl IdlArcSqlite {
|
|||
let db_write = self.db.write()?;
|
||||
let idl_cache_write = self.idl_cache.write();
|
||||
let name_cache_write = self.name_cache.write();
|
||||
let idx_exists_cache_write = self.idx_exists_cache.write();
|
||||
let op_ts_max_write = self.op_ts_max.write();
|
||||
let allids_write = self.allids.write();
|
||||
let maxid_write = self.maxid.write();
|
||||
|
@ -1325,6 +1402,7 @@ impl IdlArcSqlite {
|
|||
entry_cache: entry_cache_write,
|
||||
idl_cache: idl_cache_write,
|
||||
name_cache: name_cache_write,
|
||||
idx_exists_cache: idx_exists_cache_write,
|
||||
op_ts_max: op_ts_max_write,
|
||||
allids: allids_write,
|
||||
maxid: maxid_write,
|
||||
|
|
|
@ -205,12 +205,15 @@ pub(crate) trait IdlSqliteTransaction {
|
|||
let mut stmt = self
|
||||
.get_conn()?
|
||||
.prepare(&format!(
|
||||
"SELECT COUNT(name) from {}.sqlite_master where name = :tname",
|
||||
"SELECT rowid from {}.sqlite_master where name = :tname LIMIT 1",
|
||||
self.get_db_name()
|
||||
))
|
||||
.map_err(sqlite_error)?;
|
||||
|
||||
let i: Option<i64> = stmt
|
||||
.query_row(&[(":tname", tname)], |row| row.get(0))
|
||||
// If the row doesn't exist, we don't mind.
|
||||
.optional()
|
||||
.map_err(sqlite_error)?;
|
||||
|
||||
match i {
|
||||
|
|
|
@ -155,3 +155,9 @@ impl Ord for (dyn IdlCacheKeyToRef + '_) {
|
|||
self.keyref().cmp(&other.keyref())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
|
||||
pub struct IdxNameKey {
|
||||
pub a: Attribute,
|
||||
pub i: IndexType,
|
||||
}
|
||||
|
|
|
@ -1,20 +1,12 @@
|
|||
//! Constant Entries for the IDM
|
||||
use std::fmt::Display;
|
||||
|
||||
use crate::constants::groups::idm_builtin_admin_groups;
|
||||
use crate::constants::uuids::*;
|
||||
use crate::entry::{Entry, EntryInit, EntryInitNew, EntryNew};
|
||||
use crate::idm::account::Account;
|
||||
use crate::value::PartialValue;
|
||||
use crate::value::Value;
|
||||
use crate::valueset::{ValueSet, ValueSetIutf8};
|
||||
pub use kanidm_proto::attribute::Attribute;
|
||||
use kanidm_proto::constants::*;
|
||||
use kanidm_proto::internal::OperationError;
|
||||
use kanidm_proto::scim_v1::JsonValue;
|
||||
use kanidm_proto::v1::AccountType;
|
||||
|
||||
use uuid::Uuid;
|
||||
|
||||
//TODO: This would do well in the proto lib
|
||||
// together with all the other definitions.
|
||||
|
@ -202,158 +194,9 @@ impl EntryClass {
|
|||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
/// Builtin System Admin account.
|
||||
pub static ref BUILTIN_ACCOUNT_IDM_ADMIN: BuiltinAccount = BuiltinAccount {
|
||||
account_type: AccountType::ServiceAccount,
|
||||
entry_managed_by: None,
|
||||
name: "idm_admin",
|
||||
uuid: UUID_IDM_ADMIN,
|
||||
description: "Builtin IDM Admin account.",
|
||||
displayname: "IDM Administrator",
|
||||
};
|
||||
|
||||
pub static ref E_SYSTEM_INFO_V1: EntryInitNew = entry_init!(
|
||||
(Attribute::Class, EntryClass::Object.to_value()),
|
||||
(Attribute::Class, EntryClass::SystemInfo.to_value()),
|
||||
(Attribute::Class, EntryClass::System.to_value()),
|
||||
(Attribute::Uuid, Value::Uuid(UUID_SYSTEM_INFO)),
|
||||
(
|
||||
Attribute::Description,
|
||||
Value::new_utf8s("System (local) info and metadata object.")
|
||||
),
|
||||
(Attribute::Version, Value::Uint32(20))
|
||||
);
|
||||
|
||||
pub static ref E_DOMAIN_INFO_DL6: EntryInitNew = entry_init!(
|
||||
(Attribute::Class, EntryClass::Object.to_value()),
|
||||
(Attribute::Class, EntryClass::DomainInfo.to_value()),
|
||||
(Attribute::Class, EntryClass::System.to_value()),
|
||||
(Attribute::Class, EntryClass::KeyObject.to_value()),
|
||||
(Attribute::Class, EntryClass::KeyObjectJwtEs256.to_value()),
|
||||
(Attribute::Class, EntryClass::KeyObjectJweA128GCM.to_value()),
|
||||
(Attribute::Name, Value::new_iname("domain_local")),
|
||||
(Attribute::Uuid, Value::Uuid(UUID_DOMAIN_INFO)),
|
||||
(
|
||||
Attribute::Description,
|
||||
Value::new_utf8s("This local domain's info and metadata object.")
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// Built in accounts such as anonymous, idm_admin and admin
|
||||
pub struct BuiltinAccount {
|
||||
pub account_type: kanidm_proto::v1::AccountType,
|
||||
pub entry_managed_by: Option<uuid::Uuid>,
|
||||
pub name: &'static str,
|
||||
pub uuid: Uuid,
|
||||
pub description: &'static str,
|
||||
pub displayname: &'static str,
|
||||
}
|
||||
|
||||
impl Default for BuiltinAccount {
|
||||
fn default() -> Self {
|
||||
BuiltinAccount {
|
||||
account_type: AccountType::ServiceAccount,
|
||||
entry_managed_by: None,
|
||||
name: "",
|
||||
uuid: Uuid::new_v4(),
|
||||
description: "<set description>",
|
||||
displayname: "<set displayname>",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BuiltinAccount> for Account {
|
||||
fn from(value: BuiltinAccount) -> Self {
|
||||
#[allow(clippy::panic)]
|
||||
if value.uuid >= DYNAMIC_RANGE_MINIMUM_UUID {
|
||||
panic!("Builtin ACP has invalid UUID! {:?}", value);
|
||||
}
|
||||
Account {
|
||||
name: value.name.to_string(),
|
||||
uuid: value.uuid,
|
||||
displayname: value.displayname.to_string(),
|
||||
spn: format!("{}@example.com", value.name),
|
||||
mail_primary: None,
|
||||
mail: Vec::with_capacity(0),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BuiltinAccount> for EntryInitNew {
|
||||
fn from(value: BuiltinAccount) -> Self {
|
||||
let mut entry = EntryInitNew::new();
|
||||
entry.add_ava(Attribute::Name, Value::new_iname(value.name));
|
||||
#[allow(clippy::panic)]
|
||||
if value.uuid >= DYNAMIC_RANGE_MINIMUM_UUID {
|
||||
panic!("Builtin ACP has invalid UUID! {:?}", value);
|
||||
}
|
||||
entry.add_ava(Attribute::Uuid, Value::Uuid(value.uuid));
|
||||
entry.add_ava(Attribute::Description, Value::new_utf8s(value.description));
|
||||
entry.add_ava(Attribute::DisplayName, Value::new_utf8s(value.displayname));
|
||||
|
||||
if let Some(entry_manager) = value.entry_managed_by {
|
||||
entry.add_ava(Attribute::EntryManagedBy, Value::Refer(entry_manager));
|
||||
}
|
||||
|
||||
entry.set_ava(
|
||||
Attribute::Class,
|
||||
vec![
|
||||
EntryClass::Account.to_value(),
|
||||
EntryClass::MemberOf.to_value(),
|
||||
EntryClass::Object.to_value(),
|
||||
],
|
||||
);
|
||||
match value.account_type {
|
||||
AccountType::Person => entry.add_ava(Attribute::Class, EntryClass::Person.to_value()),
|
||||
AccountType::ServiceAccount => {
|
||||
entry.add_ava(Attribute::Class, EntryClass::ServiceAccount.to_value())
|
||||
}
|
||||
}
|
||||
entry
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
/// Builtin System Admin account.
|
||||
pub static ref BUILTIN_ACCOUNT_ADMIN: BuiltinAccount = BuiltinAccount {
|
||||
account_type: AccountType::ServiceAccount,
|
||||
entry_managed_by: None,
|
||||
name: "admin",
|
||||
uuid: UUID_ADMIN,
|
||||
description: "Builtin System Admin account.",
|
||||
displayname: "System Administrator",
|
||||
};
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
pub static ref BUILTIN_ACCOUNT_ANONYMOUS_DL6: BuiltinAccount = BuiltinAccount {
|
||||
account_type: AccountType::ServiceAccount,
|
||||
entry_managed_by: Some(UUID_IDM_ADMINS),
|
||||
name: "anonymous",
|
||||
uuid: UUID_ANONYMOUS,
|
||||
description: "Anonymous access account.",
|
||||
displayname: "Anonymous",
|
||||
};
|
||||
}
|
||||
|
||||
pub fn builtin_accounts() -> Vec<&'static BuiltinAccount> {
|
||||
vec![
|
||||
&BUILTIN_ACCOUNT_ADMIN,
|
||||
&BUILTIN_ACCOUNT_IDM_ADMIN,
|
||||
&BUILTIN_ACCOUNT_ANONYMOUS_DL6,
|
||||
]
|
||||
}
|
||||
|
||||
// ============ TEST DATA ============
|
||||
#[cfg(test)]
|
||||
pub const UUID_TESTPERSON_1: Uuid = ::uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930");
|
||||
|
||||
#[cfg(test)]
|
||||
pub const UUID_TESTPERSON_2: Uuid = ::uuid::uuid!("538faac7-4d29-473b-a59d-23023ac19955");
|
||||
use crate::entry::{Entry, EntryInit, EntryInitNew, EntryNew};
|
||||
|
||||
#[cfg(test)]
|
||||
lazy_static! {
|
||||
|
@ -363,7 +206,10 @@ lazy_static! {
|
|||
(Attribute::Class, EntryClass::Person.to_value()),
|
||||
(Attribute::Name, Value::new_iname("testperson1")),
|
||||
(Attribute::DisplayName, Value::new_utf8s("Test Person 1")),
|
||||
(Attribute::Uuid, Value::Uuid(UUID_TESTPERSON_1))
|
||||
(
|
||||
Attribute::Uuid,
|
||||
Value::Uuid(super::uuids::UUID_TESTPERSON_1)
|
||||
)
|
||||
);
|
||||
pub static ref E_TESTPERSON_2: EntryInitNew = entry_init!(
|
||||
(Attribute::Class, EntryClass::Object.to_value()),
|
||||
|
@ -371,28 +217,9 @@ lazy_static! {
|
|||
(Attribute::Class, EntryClass::Person.to_value()),
|
||||
(Attribute::Name, Value::new_iname("testperson2")),
|
||||
(Attribute::DisplayName, Value::new_utf8s("Test Person 2")),
|
||||
(Attribute::Uuid, Value::Uuid(UUID_TESTPERSON_2))
|
||||
(
|
||||
Attribute::Uuid,
|
||||
Value::Uuid(super::uuids::UUID_TESTPERSON_2)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// ⚠️ DOMAIN LEVEL 1 ENTRIES ⚠️
|
||||
// Future entries need to be added via migrations.
|
||||
//
|
||||
// DO NOT MODIFY THIS DEFINITION
|
||||
|
||||
/// Build a list of internal admin entries
|
||||
pub fn idm_builtin_admin_entries() -> Result<Vec<EntryInitNew>, OperationError> {
|
||||
let mut res: Vec<EntryInitNew> = vec![
|
||||
BUILTIN_ACCOUNT_ADMIN.clone().into(),
|
||||
BUILTIN_ACCOUNT_IDM_ADMIN.clone().into(),
|
||||
];
|
||||
for group in idm_builtin_admin_groups() {
|
||||
let g: EntryInitNew = group.clone().try_into()?;
|
||||
res.push(g);
|
||||
}
|
||||
|
||||
// We need to push anonymous *after* groups due to entry-managed-by
|
||||
res.push(BUILTIN_ACCOUNT_ANONYMOUS_DL6.clone().into());
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
|
|
@ -1,20 +1,10 @@
|
|||
// Re-export as needed
|
||||
|
||||
pub mod acp;
|
||||
pub mod entries;
|
||||
pub mod groups;
|
||||
mod key_providers;
|
||||
pub mod schema;
|
||||
pub mod system_config;
|
||||
pub mod uuids;
|
||||
pub mod values;
|
||||
|
||||
pub use self::acp::*;
|
||||
pub use self::entries::*;
|
||||
pub use self::groups::*;
|
||||
pub use self::key_providers::*;
|
||||
pub use self::schema::*;
|
||||
pub use self::system_config::*;
|
||||
pub use self::uuids::*;
|
||||
pub use self::values::*;
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ use uuid::{uuid, Uuid};
|
|||
pub const STR_UUID_ADMIN: &str = "00000000-0000-0000-0000-000000000000";
|
||||
pub const UUID_ADMIN: Uuid = uuid!("00000000-0000-0000-0000-000000000000");
|
||||
pub const UUID_IDM_ADMINS: Uuid = uuid!("00000000-0000-0000-0000-000000000001");
|
||||
pub const NAME_IDM_ADMINS: &str = "idm_admins";
|
||||
pub const UUID_IDM_PEOPLE_PII_READ: Uuid = uuid!("00000000-0000-0000-0000-000000000002");
|
||||
pub const UUID_IDM_PEOPLE_WRITE_PRIV: Uuid = uuid!("00000000-0000-0000-0000-000000000003");
|
||||
pub const UUID_IDM_GROUP_WRITE_PRIV: Uuid = uuid!("00000000-0000-0000-0000-000000000004");
|
||||
|
@ -26,6 +27,8 @@ pub const UUID_IDM_ADMIN: Uuid = uuid!("00000000-0000-0000-0000-000000000018");
|
|||
|
||||
pub const STR_UUID_SYSTEM_ADMINS: &str = "00000000-0000-0000-0000-000000000019";
|
||||
pub const UUID_SYSTEM_ADMINS: Uuid = uuid!("00000000-0000-0000-0000-000000000019");
|
||||
pub const NAME_SYSTEM_ADMINS: &str = "system_admins";
|
||||
|
||||
pub const UUID_DOMAIN_ADMINS: Uuid = uuid!("00000000-0000-0000-0000-000000000020");
|
||||
pub const UUID_IDM_ACCOUNT_UNIX_EXTEND_PRIV: Uuid = uuid!("00000000-0000-0000-0000-000000000021");
|
||||
pub const UUID_IDM_GROUP_UNIX_EXTEND_PRIV: Uuid = uuid!("00000000-0000-0000-0000-000000000022");
|
||||
|
@ -50,6 +53,7 @@ pub const UUID_IDM_HP_SERVICE_ACCOUNT_INTO_PERSON_MIGRATE_PRIV: Uuid =
|
|||
pub const UUID_IDM_ALL_PERSONS: Uuid = uuid!("00000000-0000-0000-0000-000000000035");
|
||||
pub const STR_UUID_IDM_ALL_ACCOUNTS: &str = "00000000-0000-0000-0000-000000000036";
|
||||
pub const UUID_IDM_ALL_ACCOUNTS: Uuid = uuid!("00000000-0000-0000-0000-000000000036");
|
||||
pub const NAME_IDM_ALL_ACCOUNTS: &str = "idm_all_accounts";
|
||||
|
||||
pub const UUID_IDM_HP_SYNC_ACCOUNT_MANAGE_PRIV: Uuid =
|
||||
uuid!("00000000-0000-0000-0000-000000000037");
|
||||
|
@ -452,3 +456,9 @@ pub const UUID_DOES_NOT_EXIST: Uuid = uuid!("00000000-0000-0000-0000-fffffffffff
|
|||
pub const UUID_ANONYMOUS: Uuid = uuid!("00000000-0000-0000-0000-ffffffffffff");
|
||||
|
||||
pub const DYNAMIC_RANGE_MINIMUM_UUID: Uuid = uuid!("00000000-0000-0000-0001-000000000000");
|
||||
|
||||
// ======= test data ======
|
||||
#[cfg(test)]
|
||||
pub const UUID_TESTPERSON_1: Uuid = uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930");
|
||||
#[cfg(test)]
|
||||
pub const UUID_TESTPERSON_2: Uuid = uuid!("538faac7-4d29-473b-a59d-23023ac19955");
|
||||
|
|
|
@ -636,23 +636,6 @@ impl ModifyEvent {
|
|||
}
|
||||
}
|
||||
|
||||
/// ⚠️ - Bypass the schema state machine and force the filter to be considered valid.
|
||||
/// This is a TEST ONLY method and will never be exposed in production.
|
||||
#[cfg(test)]
|
||||
pub fn new_impersonate_entry_ser(
|
||||
e: BuiltinAccount,
|
||||
filter: Filter<FilterInvalid>,
|
||||
modlist: ModifyList<ModifyInvalid>,
|
||||
) -> Self {
|
||||
let ei: EntryInitNew = e.into();
|
||||
ModifyEvent {
|
||||
ident: Identity::from_impersonate_entry_readwrite(Arc::new(ei.into_sealed_committed())),
|
||||
filter: filter.clone().into_valid(),
|
||||
filter_orig: filter.into_valid(),
|
||||
modlist: modlist.into_valid(),
|
||||
}
|
||||
}
|
||||
|
||||
/// ⚠️ - Bypass the schema state machine and force the filter to be considered valid.
|
||||
/// This is a TEST ONLY method and will never be exposed in production.
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -1041,18 +1041,10 @@ impl IdmServerProxyReadTransaction<'_> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::idm::account::Account;
|
||||
use crate::idm::accountpolicy::ResolvedAccountPolicy;
|
||||
use crate::prelude::*;
|
||||
use kanidm_proto::internal::UiHint;
|
||||
|
||||
#[test]
|
||||
fn test_idm_account_from_anonymous() {
|
||||
let account: Account = BUILTIN_ACCOUNT_ANONYMOUS_DL6.clone().into();
|
||||
debug!("{:?}", account);
|
||||
// I think that's it? we may want to check anonymous mech ...
|
||||
}
|
||||
|
||||
#[idm_test]
|
||||
async fn test_idm_account_ui_hints(idms: &IdmServer, _idms_delayed: &mut IdmServerDelayed) {
|
||||
let ct = duration_from_epoch_now();
|
||||
|
|
|
@ -1716,6 +1716,7 @@ mod tests {
|
|||
};
|
||||
use crate::idm::delayed::DelayedAction;
|
||||
use crate::idm::AuthState;
|
||||
use crate::migration_data::{BUILTIN_ACCOUNT_ANONYMOUS, BUILTIN_ACCOUNT_TEST_PERSON};
|
||||
use crate::prelude::*;
|
||||
use crate::server::keys::KeyObjectInternal;
|
||||
use crate::utils::readable_password_from_random;
|
||||
|
@ -1742,7 +1743,7 @@ mod tests {
|
|||
|
||||
let webauthn = create_webauthn();
|
||||
|
||||
let anon_account: Account = BUILTIN_ACCOUNT_ANONYMOUS_DL6.clone().into();
|
||||
let anon_account: Account = BUILTIN_ACCOUNT_ANONYMOUS.clone().into();
|
||||
|
||||
let asd = AuthSessionData {
|
||||
account: anon_account,
|
||||
|
@ -1819,7 +1820,7 @@ mod tests {
|
|||
fn start_session_simple_password_mech(privileged: bool) -> UserAuthToken {
|
||||
let webauthn = create_webauthn();
|
||||
// create the ent
|
||||
let mut account: Account = BUILTIN_ACCOUNT_ADMIN.clone().into();
|
||||
let mut account: Account = BUILTIN_ACCOUNT_TEST_PERSON.clone().into();
|
||||
// manually load in a cred
|
||||
let p = CryptoPolicy::minimum();
|
||||
let cred = Credential::new_password_only(&p, "test_password").unwrap();
|
||||
|
@ -1920,7 +1921,7 @@ mod tests {
|
|||
sketching::test_init();
|
||||
let webauthn = create_webauthn();
|
||||
// create the ent
|
||||
let mut account: Account = BUILTIN_ACCOUNT_ADMIN.clone().into();
|
||||
let mut account: Account = BUILTIN_ACCOUNT_TEST_PERSON.clone().into();
|
||||
// manually load in a cred
|
||||
let p = CryptoPolicy::minimum();
|
||||
let cred = Credential::new_password_only(&p, "list@no3IBTyqHu$bad").unwrap();
|
||||
|
@ -2087,7 +2088,7 @@ mod tests {
|
|||
sketching::test_init();
|
||||
let webauthn = create_webauthn();
|
||||
// create the ent
|
||||
let mut account: Account = BUILTIN_ACCOUNT_ADMIN.clone().into();
|
||||
let mut account: Account = BUILTIN_ACCOUNT_TEST_PERSON.clone().into();
|
||||
|
||||
// Setup a fake time stamp for consistency.
|
||||
let ts = Duration::from_secs(12345);
|
||||
|
@ -2264,7 +2265,7 @@ mod tests {
|
|||
sketching::test_init();
|
||||
let webauthn = create_webauthn();
|
||||
// create the ent
|
||||
let mut account: Account = BUILTIN_ACCOUNT_ADMIN.clone().into();
|
||||
let mut account: Account = BUILTIN_ACCOUNT_TEST_PERSON.clone().into();
|
||||
|
||||
// Setup a fake time stamp for consistency.
|
||||
let ts = Duration::from_secs(12345);
|
||||
|
@ -2440,7 +2441,7 @@ mod tests {
|
|||
let (audit_tx, mut audit_rx) = unbounded();
|
||||
let ts = duration_from_epoch_now();
|
||||
// create the ent
|
||||
let mut account: Account = BUILTIN_ACCOUNT_ADMIN.clone().into();
|
||||
let mut account: Account = BUILTIN_ACCOUNT_TEST_PERSON.clone().into();
|
||||
|
||||
let (webauthn, mut wa, wan_cred) = setup_webauthn_passkey(account.name.as_str());
|
||||
|
||||
|
@ -2594,7 +2595,7 @@ mod tests {
|
|||
let (audit_tx, mut audit_rx) = unbounded();
|
||||
let ts = duration_from_epoch_now();
|
||||
// create the ent
|
||||
let mut account: Account = BUILTIN_ACCOUNT_ADMIN.clone().into();
|
||||
let mut account: Account = BUILTIN_ACCOUNT_TEST_PERSON.clone().into();
|
||||
|
||||
let (webauthn, mut wa, wan_cred) = setup_webauthn_securitykey(account.name.as_str());
|
||||
let pw_good = "test_password";
|
||||
|
@ -2787,7 +2788,7 @@ mod tests {
|
|||
let (audit_tx, mut audit_rx) = unbounded();
|
||||
let ts = duration_from_epoch_now();
|
||||
// create the ent
|
||||
let mut account: Account = BUILTIN_ACCOUNT_ADMIN.clone().into();
|
||||
let mut account: Account = BUILTIN_ACCOUNT_TEST_PERSON.clone().into();
|
||||
|
||||
let (webauthn, mut wa, wan_cred) = setup_webauthn_securitykey(account.name.as_str());
|
||||
|
||||
|
@ -3053,7 +3054,7 @@ mod tests {
|
|||
sketching::test_init();
|
||||
let webauthn = create_webauthn();
|
||||
// create the ent
|
||||
let mut account: Account = BUILTIN_ACCOUNT_ADMIN.clone().into();
|
||||
let mut account: Account = BUILTIN_ACCOUNT_TEST_PERSON.clone().into();
|
||||
|
||||
// Setup a fake time stamp for consistency.
|
||||
let ts = Duration::from_secs(12345);
|
||||
|
@ -3262,7 +3263,7 @@ mod tests {
|
|||
sketching::test_init();
|
||||
let webauthn = create_webauthn();
|
||||
// create the ent
|
||||
let mut account: Account = BUILTIN_ACCOUNT_ADMIN.clone().into();
|
||||
let mut account: Account = BUILTIN_ACCOUNT_TEST_PERSON.clone().into();
|
||||
|
||||
// Setup a fake time stamp for consistency.
|
||||
let ts = Duration::from_secs(12345);
|
||||
|
|
|
@ -1989,7 +1989,7 @@ mod tests {
|
|||
let me = ModifyEvent::new_internal_invalid(
|
||||
filter!(f_eq(
|
||||
Attribute::Name,
|
||||
PartialValue::new_iname(BUILTIN_GROUP_PEOPLE_PII_READ.name)
|
||||
PartialValue::new_iname("idm_people_pii_read")
|
||||
)),
|
||||
ModifyList::new_list(vec![Modify::Present(
|
||||
Attribute::Member,
|
||||
|
|
|
@ -143,8 +143,9 @@ impl IdmServer {
|
|||
pub async fn new(
|
||||
qs: QueryServer,
|
||||
origin: &str,
|
||||
is_integration_test: bool,
|
||||
) -> Result<(IdmServer, IdmServerDelayed, IdmServerAudit), OperationError> {
|
||||
let crypto_policy = if cfg!(test) {
|
||||
let crypto_policy = if cfg!(test) || is_integration_test {
|
||||
CryptoPolicy::danger_test_minimum()
|
||||
} else {
|
||||
// This is calculated back from:
|
||||
|
|
|
@ -50,6 +50,12 @@ pub mod credential;
|
|||
pub mod entry;
|
||||
pub mod event;
|
||||
pub mod filter;
|
||||
|
||||
// If this module is ever made public outside of this crate, firstyear will be extremely sad.
|
||||
// This is *purely migration data*. Don't even think about using it in test cases for anything
|
||||
// else.
|
||||
pub(crate) mod migration_data;
|
||||
|
||||
pub mod modify;
|
||||
pub mod time;
|
||||
pub(crate) mod utils;
|
||||
|
|
35
server/lib/src/migration_data/dl10/accounts.rs
Normal file
35
server/lib/src/migration_data/dl10/accounts.rs
Normal file
|
@ -0,0 +1,35 @@
|
|||
//! Constant Entries for the IDM
|
||||
use crate::constants::uuids::*;
|
||||
use crate::migration_data::types::BuiltinAccount;
|
||||
use kanidm_proto::v1::AccountType;
|
||||
|
||||
lazy_static! {
|
||||
/// Builtin System Admin account.
|
||||
pub static ref BUILTIN_ACCOUNT_IDM_ADMIN: BuiltinAccount = BuiltinAccount {
|
||||
account_type: AccountType::ServiceAccount,
|
||||
entry_managed_by: None,
|
||||
name: "idm_admin",
|
||||
uuid: UUID_IDM_ADMIN,
|
||||
description: "Builtin IDM Admin account.",
|
||||
displayname: "IDM Administrator",
|
||||
};
|
||||
|
||||
/// Builtin System Admin account.
|
||||
pub static ref BUILTIN_ACCOUNT_ADMIN: BuiltinAccount = BuiltinAccount {
|
||||
account_type: AccountType::ServiceAccount,
|
||||
entry_managed_by: None,
|
||||
name: "admin",
|
||||
uuid: UUID_ADMIN,
|
||||
description: "Builtin System Admin account.",
|
||||
displayname: "System Administrator",
|
||||
};
|
||||
|
||||
pub static ref BUILTIN_ACCOUNT_ANONYMOUS_DL6: BuiltinAccount = BuiltinAccount {
|
||||
account_type: AccountType::ServiceAccount,
|
||||
entry_managed_by: Some(UUID_IDM_ADMINS),
|
||||
name: "anonymous",
|
||||
uuid: UUID_ANONYMOUS,
|
||||
description: "Anonymous access account.",
|
||||
displayname: "Anonymous",
|
||||
};
|
||||
}
|
408
server/lib/src/migration_data/dl10/groups.rs
Normal file
408
server/lib/src/migration_data/dl10/groups.rs
Normal file
|
@ -0,0 +1,408 @@
|
|||
use crate::entry::EntryInitNew;
|
||||
use crate::prelude::*;
|
||||
use crate::value::CredentialType;
|
||||
|
||||
use kanidm_proto::internal::{Filter, OperationError, UiHint};
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
/// Built-in group definitions
|
||||
pub struct BuiltinGroup {
|
||||
pub name: &'static str,
|
||||
pub description: &'static str,
|
||||
pub uuid: uuid::Uuid,
|
||||
pub members: Vec<uuid::Uuid>,
|
||||
pub entry_managed_by: Option<uuid::Uuid>,
|
||||
pub dyngroup: bool,
|
||||
pub dyngroup_filter: Option<Filter>,
|
||||
pub extra_attributes: Vec<(Attribute, Value)>,
|
||||
}
|
||||
|
||||
impl TryFrom<BuiltinGroup> for EntryInitNew {
|
||||
type Error = OperationError;
|
||||
|
||||
fn try_from(val: BuiltinGroup) -> Result<Self, OperationError> {
|
||||
let mut entry = EntryInitNew::new();
|
||||
|
||||
if val.uuid >= DYNAMIC_RANGE_MINIMUM_UUID {
|
||||
error!("Builtin ACP has invalid UUID! {:?}", val);
|
||||
return Err(OperationError::InvalidUuid);
|
||||
}
|
||||
|
||||
entry.add_ava(Attribute::Name, Value::new_iname(val.name));
|
||||
entry.add_ava(Attribute::Description, Value::new_utf8s(val.description));
|
||||
// classes for groups
|
||||
entry.set_ava(
|
||||
Attribute::Class,
|
||||
vec![EntryClass::Group.into(), EntryClass::Object.into()],
|
||||
);
|
||||
if val.dyngroup {
|
||||
if !val.members.is_empty() {
|
||||
return Err(OperationError::InvalidSchemaState(format!(
|
||||
"Builtin dyngroup {} has members specified, this is not allowed",
|
||||
val.name
|
||||
)));
|
||||
}
|
||||
entry.add_ava(Attribute::Class, EntryClass::DynGroup.to_value());
|
||||
match val.dyngroup_filter {
|
||||
Some(filter) => entry.add_ava(Attribute::DynGroupFilter, Value::JsonFilt(filter)),
|
||||
None => {
|
||||
error!(
|
||||
"No filter specified for dyngroup '{}' this is going to break things!",
|
||||
val.name
|
||||
);
|
||||
return Err(OperationError::FilterGeneration);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if let Some(entry_manager) = val.entry_managed_by {
|
||||
entry.add_ava(Attribute::EntryManagedBy, Value::Refer(entry_manager));
|
||||
}
|
||||
|
||||
entry.add_ava(Attribute::Uuid, Value::Uuid(val.uuid));
|
||||
entry.set_ava(
|
||||
Attribute::Member,
|
||||
val.members
|
||||
.into_iter()
|
||||
.map(Value::Refer)
|
||||
.collect::<Vec<Value>>(),
|
||||
);
|
||||
// add any extra attributes
|
||||
val.extra_attributes
|
||||
.into_iter()
|
||||
.for_each(|(attr, val)| entry.add_ava(attr, val));
|
||||
// all done!
|
||||
Ok(entry)
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
// There are our built in "roles". They encapsulate some higher level collections
|
||||
// of roles. The intent is to allow a pretty generic and correct by default set
|
||||
// of these use cases.
|
||||
pub static ref BUILTIN_GROUP_SYSTEM_ADMINS_V1: BuiltinGroup = BuiltinGroup {
|
||||
name: NAME_SYSTEM_ADMINS,
|
||||
description: "Builtin System Administrators Group.",
|
||||
uuid: UUID_SYSTEM_ADMINS,
|
||||
entry_managed_by: Some(UUID_SYSTEM_ADMINS),
|
||||
members: vec![UUID_ADMIN],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
pub static ref BUILTIN_GROUP_IDM_ADMINS_V1: BuiltinGroup = BuiltinGroup {
|
||||
name: NAME_IDM_ADMINS,
|
||||
description: "Builtin IDM Administrators Group.",
|
||||
uuid: UUID_IDM_ADMINS,
|
||||
entry_managed_by: Some(UUID_IDM_ADMINS),
|
||||
members: vec![UUID_IDM_ADMIN],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
pub static ref BUILTIN_GROUP_SERVICE_DESK: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_service_desk",
|
||||
description: "Builtin Service Desk Group.",
|
||||
uuid: UUID_IDM_SERVICE_DESK,
|
||||
entry_managed_by: Some(UUID_IDM_ADMINS),
|
||||
members: vec![],
|
||||
..Default::default()
|
||||
};
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
// These are the "finer" roles. They encapsulate different concepts in the system.
|
||||
// The next section is the "system style" roles. These adjust the operation of
|
||||
// kanidm and relate to it's internals and how it functions.
|
||||
pub static ref BUILTIN_GROUP_RECYCLE_BIN_ADMINS: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_recycle_bin_admins",
|
||||
description: "Builtin Recycle Bin Administrators Group.",
|
||||
uuid: UUID_IDM_RECYCLE_BIN_ADMINS,
|
||||
entry_managed_by: Some(UUID_SYSTEM_ADMINS),
|
||||
members: vec![UUID_SYSTEM_ADMINS],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
/// Builtin IDM Group for granting local domain administration rights and trust administration rights
|
||||
pub static ref BUILTIN_GROUP_DOMAIN_ADMINS: BuiltinGroup = BuiltinGroup {
|
||||
name: "domain_admins",
|
||||
description: "Builtin IDM Group for granting local domain administration rights and trust administration rights.",
|
||||
uuid: UUID_DOMAIN_ADMINS,
|
||||
entry_managed_by: Some(UUID_SYSTEM_ADMINS),
|
||||
members: vec![UUID_SYSTEM_ADMINS],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
pub static ref BUILTIN_GROUP_SCHEMA_ADMINS: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_schema_admins",
|
||||
description: "Builtin Schema Administration Group.",
|
||||
uuid: UUID_IDM_SCHEMA_ADMINS,
|
||||
entry_managed_by: Some(UUID_SYSTEM_ADMINS),
|
||||
members: vec![UUID_SYSTEM_ADMINS],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
pub static ref BUILTIN_GROUP_ACCESS_CONTROL_ADMINS: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_access_control_admins",
|
||||
description: "Builtin Access Control Administration Group.",
|
||||
entry_managed_by: Some(UUID_SYSTEM_ADMINS),
|
||||
uuid: UUID_IDM_ACCESS_CONTROL_ADMINS,
|
||||
members: vec![UUID_SYSTEM_ADMINS],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// These are the IDM roles. They concern application integration, user permissions
|
||||
// and credential security management.
|
||||
|
||||
/// Builtin IDM Group for managing persons and their account details
|
||||
pub static ref BUILTIN_GROUP_PEOPLE_ADMINS: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_people_admins",
|
||||
description: "Builtin People Administration Group.",
|
||||
uuid: UUID_IDM_PEOPLE_ADMINS,
|
||||
entry_managed_by: Some(UUID_IDM_ADMINS),
|
||||
members: vec![UUID_IDM_ADMINS],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
pub static ref BUILTIN_GROUP_PEOPLE_ON_BOARDING: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_people_on_boarding",
|
||||
description: "Builtin People On Boarding Group.",
|
||||
uuid: UUID_IDM_PEOPLE_ON_BOARDING,
|
||||
entry_managed_by: Some(UUID_IDM_ADMINS),
|
||||
members: vec![],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
/// Builtin IDM Group for granting elevated people (personal data) read permissions.
|
||||
pub static ref BUILTIN_GROUP_PEOPLE_PII_READ: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_people_pii_read",
|
||||
description: "Builtin IDM Group for granting elevated people (personal data) read permissions.",
|
||||
uuid: UUID_IDM_PEOPLE_PII_READ,
|
||||
entry_managed_by: Some(UUID_IDM_ADMINS),
|
||||
members: vec![],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
/// Builtin IDM Group for granting people the ability to write to their own name attributes.
|
||||
pub static ref BUILTIN_GROUP_PEOPLE_SELF_NAME_WRITE_DL7: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_people_self_name_write",
|
||||
description: "Builtin IDM Group denoting users that can write to their own name attributes.",
|
||||
uuid: UUID_IDM_PEOPLE_SELF_NAME_WRITE,
|
||||
entry_managed_by: Some(UUID_IDM_ADMINS),
|
||||
members: vec![
|
||||
UUID_IDM_ALL_PERSONS
|
||||
],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
pub static ref BUILTIN_GROUP_SERVICE_ACCOUNT_ADMINS: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_service_account_admins",
|
||||
description: "Builtin Service Account Administration Group.",
|
||||
uuid: UUID_IDM_SERVICE_ACCOUNT_ADMINS,
|
||||
entry_managed_by: Some(UUID_IDM_ADMINS),
|
||||
members: vec![UUID_IDM_ADMINS],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
/// Builtin IDM Group for managing oauth2 resource server integrations to this authentication domain.
|
||||
pub static ref BUILTIN_GROUP_OAUTH2_ADMINS: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_oauth2_admins",
|
||||
description: "Builtin Oauth2 Integration Administration Group.",
|
||||
uuid: UUID_IDM_OAUTH2_ADMINS,
|
||||
entry_managed_by: Some(UUID_IDM_ADMINS),
|
||||
members: vec![UUID_IDM_ADMINS],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
pub static ref BUILTIN_GROUP_RADIUS_SERVICE_ADMINS: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_radius_service_admins",
|
||||
description: "Builtin Radius Administration Group.",
|
||||
uuid: UUID_IDM_RADIUS_ADMINS,
|
||||
entry_managed_by: Some(UUID_IDM_ADMINS),
|
||||
members: vec![UUID_IDM_ADMINS],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
/// Builtin IDM Group for RADIUS server access delegation.
|
||||
pub static ref BUILTIN_IDM_RADIUS_SERVERS_V1: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_radius_servers",
|
||||
description: "Builtin IDM Group for RADIUS server access delegation.",
|
||||
uuid: UUID_IDM_RADIUS_SERVERS,
|
||||
entry_managed_by: Some(UUID_IDM_RADIUS_ADMINS),
|
||||
members: vec![
|
||||
],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
pub static ref BUILTIN_GROUP_MAIL_SERVICE_ADMINS_DL8: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_mail_service_admins",
|
||||
description: "Builtin Mail Server Administration Group.",
|
||||
uuid: UUID_IDM_MAIL_ADMINS,
|
||||
entry_managed_by: Some(UUID_IDM_ADMINS),
|
||||
members: vec![UUID_IDM_ADMINS],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
/// Builtin IDM Group for MAIL server Access delegation.
|
||||
pub static ref BUILTIN_IDM_MAIL_SERVERS_DL8: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_mail_servers",
|
||||
description: "Builtin IDM Group for MAIL server access delegation.",
|
||||
uuid: UUID_IDM_MAIL_SERVERS,
|
||||
entry_managed_by: Some(UUID_IDM_MAIL_ADMINS),
|
||||
members: vec![
|
||||
],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
pub static ref BUILTIN_GROUP_ACCOUNT_POLICY_ADMINS: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_account_policy_admins",
|
||||
description: "Builtin Account Policy Administration Group.",
|
||||
uuid: UUID_IDM_ACCOUNT_POLICY_ADMINS,
|
||||
entry_managed_by: Some(UUID_IDM_ADMINS),
|
||||
members: vec![UUID_IDM_ADMINS],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
/// Builtin IDM Group for managing posix/unix attributes on groups and users.
|
||||
pub static ref BUILTIN_GROUP_UNIX_ADMINS: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_unix_admins",
|
||||
description: "Builtin Unix Administration Group.",
|
||||
uuid: UUID_IDM_UNIX_ADMINS,
|
||||
entry_managed_by: Some(UUID_IDM_ADMINS),
|
||||
members: vec![UUID_IDM_ADMINS],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
/// Builtin IDM Group for managing client authentication certificates.
|
||||
pub static ref BUILTIN_GROUP_CLIENT_CERTIFICATE_ADMINS_DL7: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_client_certificate_admins",
|
||||
description: "Builtin Client Certificate Administration Group.",
|
||||
uuid: UUID_IDM_CLIENT_CERTIFICATE_ADMINS,
|
||||
entry_managed_by: Some(UUID_IDM_ADMINS),
|
||||
members: vec![UUID_IDM_ADMINS],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
/// Builtin IDM Group for granting elevated group write and lifecycle permissions.
|
||||
pub static ref IDM_GROUP_ADMINS_V1: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_group_admins",
|
||||
description: "Builtin IDM Group for granting elevated group write and lifecycle permissions.",
|
||||
uuid: UUID_IDM_GROUP_ADMINS,
|
||||
entry_managed_by: Some(UUID_IDM_ADMINS),
|
||||
members: vec![UUID_IDM_ADMINS],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
/// Self-write of mail
|
||||
pub static ref IDM_PEOPLE_SELF_MAIL_WRITE_DL7: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_people_self_mail_write",
|
||||
description: "Builtin IDM Group for people accounts to update their own mail.",
|
||||
uuid: UUID_IDM_PEOPLE_SELF_MAIL_WRITE,
|
||||
members: Vec::with_capacity(0),
|
||||
..Default::default()
|
||||
};
|
||||
}
|
||||
|
||||
// at some point vs code just gives up on syntax highlighting inside lazy_static...
|
||||
lazy_static! {
|
||||
pub static ref IDM_ALL_PERSONS: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_all_persons",
|
||||
description: "Builtin IDM dynamic group containing all persons.",
|
||||
uuid: UUID_IDM_ALL_PERSONS,
|
||||
members: Vec::with_capacity(0),
|
||||
dyngroup: true,
|
||||
dyngroup_filter: Some(
|
||||
Filter::And(vec![
|
||||
Filter::Eq(Attribute::Class.to_string(), EntryClass::Person.to_string()),
|
||||
Filter::Eq(Attribute::Class.to_string(), EntryClass::Account.to_string()),
|
||||
])
|
||||
),
|
||||
extra_attributes: vec![
|
||||
// Enable account policy by default
|
||||
(Attribute::Class, EntryClass::AccountPolicy.to_value()),
|
||||
// Enforce this is a system protected object
|
||||
(Attribute::Class, EntryClass::System.to_value()),
|
||||
// MFA By Default
|
||||
(Attribute::CredentialTypeMinimum, CredentialType::Mfa.into()),
|
||||
],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
pub static ref IDM_ALL_ACCOUNTS: BuiltinGroup = BuiltinGroup {
|
||||
name: NAME_IDM_ALL_ACCOUNTS,
|
||||
description: "Builtin IDM dynamic group containing all entries that can authenticate.",
|
||||
uuid: UUID_IDM_ALL_ACCOUNTS,
|
||||
members: Vec::with_capacity(0),
|
||||
dyngroup: true,
|
||||
dyngroup_filter: Some(
|
||||
Filter::Eq(Attribute::Class.to_string(), EntryClass::Account.to_string()),
|
||||
),
|
||||
extra_attributes: vec![
|
||||
// Enable account policy by default
|
||||
(Attribute::Class, EntryClass::AccountPolicy.to_value()),
|
||||
// Enforce this is a system protected object
|
||||
(Attribute::Class, EntryClass::System.to_value()),
|
||||
],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
|
||||
pub static ref IDM_UI_ENABLE_EXPERIMENTAL_FEATURES: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_ui_enable_experimental_features",
|
||||
description: "Members of this group will have access to experimental web UI features.",
|
||||
uuid: UUID_IDM_UI_ENABLE_EXPERIMENTAL_FEATURES,
|
||||
entry_managed_by: Some(UUID_IDM_ADMINS),
|
||||
extra_attributes: vec![
|
||||
(Attribute::GrantUiHint, Value::UiHint(UiHint::ExperimentalFeatures))
|
||||
],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
/// Members of this group will have access to read the mail attribute of all persons and service accounts.
|
||||
pub static ref IDM_ACCOUNT_MAIL_READ: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_account_mail_read",
|
||||
description: "Members of this group will have access to read the mail attribute of all persons and service accounts.",
|
||||
entry_managed_by: Some(UUID_IDM_ACCESS_CONTROL_ADMINS),
|
||||
uuid: UUID_IDM_ACCOUNT_MAIL_READ,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
/// This must be the last group to init to include the UUID of the other high priv groups.
|
||||
pub static ref IDM_HIGH_PRIVILEGE_DL8: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_high_privilege",
|
||||
uuid: UUID_IDM_HIGH_PRIVILEGE,
|
||||
entry_managed_by: Some(UUID_IDM_ACCESS_CONTROL_ADMINS),
|
||||
description: "Builtin IDM provided groups with high levels of access that should be audited and limited in modification.",
|
||||
members: vec![
|
||||
UUID_SYSTEM_ADMINS,
|
||||
UUID_IDM_ADMINS,
|
||||
UUID_DOMAIN_ADMINS,
|
||||
UUID_IDM_SERVICE_DESK,
|
||||
UUID_IDM_RECYCLE_BIN_ADMINS,
|
||||
UUID_IDM_SCHEMA_ADMINS,
|
||||
UUID_IDM_ACCESS_CONTROL_ADMINS,
|
||||
UUID_IDM_OAUTH2_ADMINS,
|
||||
UUID_IDM_RADIUS_ADMINS,
|
||||
UUID_IDM_ACCOUNT_POLICY_ADMINS,
|
||||
UUID_IDM_RADIUS_SERVERS,
|
||||
UUID_IDM_GROUP_ADMINS,
|
||||
UUID_IDM_UNIX_ADMINS,
|
||||
UUID_IDM_PEOPLE_PII_READ,
|
||||
UUID_IDM_PEOPLE_ADMINS,
|
||||
UUID_IDM_PEOPLE_ON_BOARDING,
|
||||
UUID_IDM_SERVICE_ACCOUNT_ADMINS,
|
||||
UUID_IDM_CLIENT_CERTIFICATE_ADMINS,
|
||||
UUID_IDM_APPLICATION_ADMINS,
|
||||
UUID_IDM_MAIL_ADMINS,
|
||||
UUID_IDM_HIGH_PRIVILEGE,
|
||||
],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
pub static ref BUILTIN_GROUP_APPLICATION_ADMINS_DL8: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_application_admins",
|
||||
uuid: UUID_IDM_APPLICATION_ADMINS,
|
||||
description: "Builtin Application Administration Group.",
|
||||
entry_managed_by: Some(UUID_IDM_ADMINS),
|
||||
members: vec![UUID_IDM_ADMINS],
|
||||
..Default::default()
|
||||
};
|
||||
}
|
267
server/lib/src/migration_data/dl10/mod.rs
Normal file
267
server/lib/src/migration_data/dl10/mod.rs
Normal file
|
@ -0,0 +1,267 @@
|
|||
mod access;
|
||||
pub(super) mod accounts;
|
||||
mod groups;
|
||||
mod key_providers;
|
||||
mod schema;
|
||||
mod system_config;
|
||||
|
||||
use self::access::*;
|
||||
use self::accounts::*;
|
||||
use self::groups::*;
|
||||
use self::key_providers::*;
|
||||
use self::schema::*;
|
||||
use self::system_config::*;
|
||||
|
||||
use crate::prelude::EntryInitNew;
|
||||
use kanidm_proto::internal::OperationError;
|
||||
|
||||
pub fn phase_1_schema_attrs() -> Vec<EntryInitNew> {
|
||||
vec![
|
||||
SCHEMA_ATTR_SYNC_CREDENTIAL_PORTAL.clone().into(),
|
||||
SCHEMA_ATTR_SYNC_YIELD_AUTHORITY.clone().into(),
|
||||
SCHEMA_ATTR_ACCOUNT_EXPIRE.clone().into(),
|
||||
SCHEMA_ATTR_ACCOUNT_VALID_FROM.clone().into(),
|
||||
SCHEMA_ATTR_API_TOKEN_SESSION.clone().into(),
|
||||
SCHEMA_ATTR_AUTH_SESSION_EXPIRY.clone().into(),
|
||||
SCHEMA_ATTR_AUTH_PRIVILEGE_EXPIRY.clone().into(),
|
||||
SCHEMA_ATTR_AUTH_PASSWORD_MINIMUM_LENGTH.clone().into(),
|
||||
SCHEMA_ATTR_BADLIST_PASSWORD.clone().into(),
|
||||
SCHEMA_ATTR_CREDENTIAL_UPDATE_INTENT_TOKEN.clone().into(),
|
||||
SCHEMA_ATTR_ATTESTED_PASSKEYS.clone().into(),
|
||||
SCHEMA_ATTR_DOMAIN_DISPLAY_NAME.clone().into(),
|
||||
SCHEMA_ATTR_DOMAIN_LDAP_BASEDN.clone().into(),
|
||||
SCHEMA_ATTR_DOMAIN_NAME.clone().into(),
|
||||
SCHEMA_ATTR_LDAP_ALLOW_UNIX_PW_BIND.clone().into(),
|
||||
SCHEMA_ATTR_DOMAIN_SSID.clone().into(),
|
||||
SCHEMA_ATTR_DOMAIN_TOKEN_KEY.clone().into(),
|
||||
SCHEMA_ATTR_DOMAIN_UUID.clone().into(),
|
||||
SCHEMA_ATTR_DYNGROUP_FILTER.clone().into(),
|
||||
SCHEMA_ATTR_EC_KEY_PRIVATE.clone().into(),
|
||||
SCHEMA_ATTR_ES256_PRIVATE_KEY_DER.clone().into(),
|
||||
SCHEMA_ATTR_FERNET_PRIVATE_KEY_STR.clone().into(),
|
||||
SCHEMA_ATTR_GIDNUMBER.clone().into(),
|
||||
SCHEMA_ATTR_GRANT_UI_HINT.clone().into(),
|
||||
SCHEMA_ATTR_JWS_ES256_PRIVATE_KEY.clone().into(),
|
||||
SCHEMA_ATTR_LOGINSHELL.clone().into(),
|
||||
SCHEMA_ATTR_NAME_HISTORY.clone().into(),
|
||||
SCHEMA_ATTR_NSUNIQUEID.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_ALLOW_INSECURE_CLIENT_DISABLE_PKCE
|
||||
.clone()
|
||||
.into(),
|
||||
SCHEMA_ATTR_OAUTH2_CONSENT_SCOPE_MAP.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_JWT_LEGACY_CRYPTO_ENABLE.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_PREFER_SHORT_USERNAME.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_RS_BASIC_SECRET.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_RS_IMPLICIT_SCOPES.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_RS_NAME.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_RS_ORIGIN_LANDING.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_RS_SCOPE_MAP.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_RS_SUP_SCOPE_MAP.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_RS_TOKEN_KEY.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_SESSION.clone().into(),
|
||||
SCHEMA_ATTR_PASSKEYS.clone().into(),
|
||||
SCHEMA_ATTR_PRIMARY_CREDENTIAL.clone().into(),
|
||||
SCHEMA_ATTR_PRIVATE_COOKIE_KEY.clone().into(),
|
||||
SCHEMA_ATTR_RADIUS_SECRET.clone().into(),
|
||||
SCHEMA_ATTR_RS256_PRIVATE_KEY_DER.clone().into(),
|
||||
SCHEMA_ATTR_SSH_PUBLICKEY.clone().into(),
|
||||
SCHEMA_ATTR_SYNC_COOKIE.clone().into(),
|
||||
SCHEMA_ATTR_SYNC_TOKEN_SESSION.clone().into(),
|
||||
SCHEMA_ATTR_UNIX_PASSWORD.clone().into(),
|
||||
SCHEMA_ATTR_USER_AUTH_TOKEN_SESSION.clone().into(),
|
||||
SCHEMA_ATTR_CREDENTIAL_TYPE_MINIMUM.clone().into(),
|
||||
SCHEMA_ATTR_WEBAUTHN_ATTESTATION_CA_LIST.clone().into(),
|
||||
// DL4
|
||||
SCHEMA_ATTR_OAUTH2_RS_CLAIM_MAP_DL4.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_ALLOW_LOCALHOST_REDIRECT_DL4
|
||||
.clone()
|
||||
.into(),
|
||||
// DL5
|
||||
// DL6
|
||||
SCHEMA_ATTR_LIMIT_SEARCH_MAX_RESULTS_DL6.clone().into(),
|
||||
SCHEMA_ATTR_LIMIT_SEARCH_MAX_FILTER_TEST_DL6.clone().into(),
|
||||
SCHEMA_ATTR_KEY_INTERNAL_DATA_DL6.clone().into(),
|
||||
SCHEMA_ATTR_KEY_PROVIDER_DL6.clone().into(),
|
||||
SCHEMA_ATTR_KEY_ACTION_ROTATE_DL6.clone().into(),
|
||||
SCHEMA_ATTR_KEY_ACTION_REVOKE_DL6.clone().into(),
|
||||
SCHEMA_ATTR_KEY_ACTION_IMPORT_JWS_ES256_DL6.clone().into(),
|
||||
// DL7
|
||||
SCHEMA_ATTR_PATCH_LEVEL_DL7.clone().into(),
|
||||
SCHEMA_ATTR_DOMAIN_DEVELOPMENT_TAINT_DL7.clone().into(),
|
||||
SCHEMA_ATTR_REFERS_DL7.clone().into(),
|
||||
SCHEMA_ATTR_CERTIFICATE_DL7.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_RS_ORIGIN_DL7.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_STRICT_REDIRECT_URI_DL7.clone().into(),
|
||||
SCHEMA_ATTR_MAIL_DL7.clone().into(),
|
||||
SCHEMA_ATTR_LEGALNAME_DL7.clone().into(),
|
||||
SCHEMA_ATTR_DISPLAYNAME_DL7.clone().into(),
|
||||
// DL8
|
||||
SCHEMA_ATTR_LINKED_GROUP_DL8.clone().into(),
|
||||
SCHEMA_ATTR_APPLICATION_PASSWORD_DL8.clone().into(),
|
||||
SCHEMA_ATTR_ALLOW_PRIMARY_CRED_FALLBACK_DL8.clone().into(),
|
||||
// DL9
|
||||
SCHEMA_ATTR_OAUTH2_DEVICE_FLOW_ENABLE_DL9.clone().into(),
|
||||
SCHEMA_ATTR_DOMAIN_ALLOW_EASTER_EGGS_DL9.clone().into(),
|
||||
// DL10
|
||||
SCHEMA_ATTR_DENIED_NAME_DL10.clone().into(),
|
||||
SCHEMA_ATTR_LDAP_MAXIMUM_QUERYABLE_ATTRIBUTES.clone().into(),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn phase_2_schema_classes() -> Vec<EntryInitNew> {
|
||||
vec![
|
||||
SCHEMA_CLASS_DYNGROUP.clone().into(),
|
||||
SCHEMA_CLASS_ORGPERSON.clone().into(),
|
||||
SCHEMA_CLASS_POSIXACCOUNT.clone().into(),
|
||||
SCHEMA_CLASS_POSIXGROUP.clone().into(),
|
||||
SCHEMA_CLASS_SYSTEM_CONFIG.clone().into(),
|
||||
// DL4
|
||||
SCHEMA_CLASS_OAUTH2_RS_PUBLIC_DL4.clone().into(),
|
||||
// DL5
|
||||
SCHEMA_CLASS_ACCOUNT_DL5.clone().into(),
|
||||
SCHEMA_CLASS_OAUTH2_RS_BASIC_DL5.clone().into(),
|
||||
// DL6
|
||||
SCHEMA_CLASS_GROUP_DL6.clone().into(),
|
||||
SCHEMA_CLASS_KEY_PROVIDER_DL6.clone().into(),
|
||||
SCHEMA_CLASS_KEY_PROVIDER_INTERNAL_DL6.clone().into(),
|
||||
SCHEMA_CLASS_KEY_OBJECT_DL6.clone().into(),
|
||||
SCHEMA_CLASS_KEY_OBJECT_JWT_ES256_DL6.clone().into(),
|
||||
SCHEMA_CLASS_KEY_OBJECT_JWE_A128GCM_DL6.clone().into(),
|
||||
SCHEMA_CLASS_KEY_OBJECT_INTERNAL_DL6.clone().into(),
|
||||
// DL7
|
||||
SCHEMA_CLASS_SERVICE_ACCOUNT_DL7.clone().into(),
|
||||
SCHEMA_CLASS_SYNC_ACCOUNT_DL7.clone().into(),
|
||||
SCHEMA_CLASS_CLIENT_CERTIFICATE_DL7.clone().into(),
|
||||
// DL8
|
||||
SCHEMA_CLASS_ACCOUNT_POLICY_DL8.clone().into(),
|
||||
SCHEMA_CLASS_APPLICATION_DL8.clone().into(),
|
||||
SCHEMA_CLASS_PERSON_DL8.clone().into(),
|
||||
// DL9
|
||||
SCHEMA_CLASS_OAUTH2_RS_DL9.clone().into(),
|
||||
// DL10
|
||||
SCHEMA_CLASS_DOMAIN_INFO_DL10.clone().into(),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn phase_3_key_provider() -> Vec<EntryInitNew> {
|
||||
vec![E_KEY_PROVIDER_INTERNAL_DL6.clone()]
|
||||
}
|
||||
|
||||
pub fn phase_4_system_entries() -> Vec<EntryInitNew> {
|
||||
vec![
|
||||
E_SYSTEM_INFO_V1.clone(),
|
||||
E_DOMAIN_INFO_DL6.clone(),
|
||||
E_SYSTEM_CONFIG_V1.clone(),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn phase_5_builtin_admin_entries() -> Result<Vec<EntryInitNew>, OperationError> {
|
||||
Ok(vec![
|
||||
BUILTIN_ACCOUNT_ADMIN.clone().into(),
|
||||
BUILTIN_ACCOUNT_IDM_ADMIN.clone().into(),
|
||||
BUILTIN_GROUP_SYSTEM_ADMINS_V1.clone().try_into()?,
|
||||
BUILTIN_GROUP_IDM_ADMINS_V1.clone().try_into()?,
|
||||
// We need to push anonymous *after* groups due to entry-managed-by
|
||||
BUILTIN_ACCOUNT_ANONYMOUS_DL6.clone().into(),
|
||||
])
|
||||
}
|
||||
|
||||
pub fn phase_6_builtin_non_admin_entries() -> Result<Vec<EntryInitNew>, OperationError> {
|
||||
Ok(vec![
|
||||
BUILTIN_GROUP_DOMAIN_ADMINS.clone().try_into()?,
|
||||
BUILTIN_GROUP_SCHEMA_ADMINS.clone().try_into()?,
|
||||
BUILTIN_GROUP_ACCESS_CONTROL_ADMINS.clone().try_into()?,
|
||||
BUILTIN_GROUP_UNIX_ADMINS.clone().try_into()?,
|
||||
BUILTIN_GROUP_RECYCLE_BIN_ADMINS.clone().try_into()?,
|
||||
BUILTIN_GROUP_SERVICE_DESK.clone().try_into()?,
|
||||
BUILTIN_GROUP_OAUTH2_ADMINS.clone().try_into()?,
|
||||
BUILTIN_GROUP_RADIUS_SERVICE_ADMINS.clone().try_into()?,
|
||||
BUILTIN_GROUP_ACCOUNT_POLICY_ADMINS.clone().try_into()?,
|
||||
BUILTIN_GROUP_PEOPLE_ADMINS.clone().try_into()?,
|
||||
BUILTIN_GROUP_PEOPLE_PII_READ.clone().try_into()?,
|
||||
BUILTIN_GROUP_PEOPLE_ON_BOARDING.clone().try_into()?,
|
||||
BUILTIN_GROUP_SERVICE_ACCOUNT_ADMINS.clone().try_into()?,
|
||||
BUILTIN_GROUP_MAIL_SERVICE_ADMINS_DL8.clone().try_into()?,
|
||||
IDM_GROUP_ADMINS_V1.clone().try_into()?,
|
||||
IDM_ALL_PERSONS.clone().try_into()?,
|
||||
IDM_ALL_ACCOUNTS.clone().try_into()?,
|
||||
BUILTIN_IDM_RADIUS_SERVERS_V1.clone().try_into()?,
|
||||
BUILTIN_IDM_MAIL_SERVERS_DL8.clone().try_into()?,
|
||||
BUILTIN_GROUP_PEOPLE_SELF_NAME_WRITE_DL7
|
||||
.clone()
|
||||
.try_into()?,
|
||||
IDM_PEOPLE_SELF_MAIL_WRITE_DL7.clone().try_into()?,
|
||||
BUILTIN_GROUP_CLIENT_CERTIFICATE_ADMINS_DL7
|
||||
.clone()
|
||||
.try_into()?,
|
||||
BUILTIN_GROUP_APPLICATION_ADMINS_DL8.clone().try_into()?,
|
||||
// Write deps on read.clone().try_into()?, so write must be added first.
|
||||
// All members must exist before we write HP
|
||||
IDM_HIGH_PRIVILEGE_DL8.clone().try_into()?,
|
||||
// other things
|
||||
IDM_UI_ENABLE_EXPERIMENTAL_FEATURES.clone().try_into()?,
|
||||
IDM_ACCOUNT_MAIL_READ.clone().try_into()?,
|
||||
])
|
||||
}
|
||||
|
||||
pub fn phase_7_builtin_access_control_profiles() -> Vec<EntryInitNew> {
|
||||
vec![
|
||||
// Built in access controls.
|
||||
IDM_ACP_RECYCLE_BIN_SEARCH_V1.clone().into(),
|
||||
IDM_ACP_RECYCLE_BIN_REVIVE_V1.clone().into(),
|
||||
IDM_ACP_SCHEMA_WRITE_ATTRS_V1.clone().into(),
|
||||
IDM_ACP_SCHEMA_WRITE_CLASSES_V1.clone().into(),
|
||||
IDM_ACP_ACP_MANAGE_V1.clone().into(),
|
||||
IDM_ACP_GROUP_ENTRY_MANAGED_BY_MODIFY_V1.clone().into(),
|
||||
IDM_ACP_GROUP_ENTRY_MANAGER_V1.clone().into(),
|
||||
IDM_ACP_SYNC_ACCOUNT_MANAGE_V1.clone().into(),
|
||||
IDM_ACP_RADIUS_SERVERS_V1.clone().into(),
|
||||
IDM_ACP_RADIUS_SECRET_MANAGE_V1.clone().into(),
|
||||
IDM_ACP_PEOPLE_SELF_WRITE_MAIL_V1.clone().into(),
|
||||
IDM_ACP_ACCOUNT_SELF_WRITE_V1.clone().into(),
|
||||
IDM_ACP_ALL_ACCOUNTS_POSIX_READ_V1.clone().into(),
|
||||
IDM_ACP_SYSTEM_CONFIG_ACCOUNT_POLICY_MANAGE_V1
|
||||
.clone()
|
||||
.into(),
|
||||
IDM_ACP_GROUP_UNIX_MANAGE_V1.clone().into(),
|
||||
IDM_ACP_HP_GROUP_UNIX_MANAGE_V1.clone().into(),
|
||||
IDM_ACP_GROUP_READ_V1.clone().into(),
|
||||
IDM_ACP_ACCOUNT_UNIX_EXTEND_V1.clone().into(),
|
||||
IDM_ACP_PEOPLE_PII_READ_V1.clone().into(),
|
||||
IDM_ACP_PEOPLE_PII_MANAGE_V1.clone().into(),
|
||||
IDM_ACP_PEOPLE_READ_V1.clone().into(),
|
||||
IDM_ACP_PEOPLE_MANAGE_V1.clone().into(),
|
||||
IDM_ACP_PEOPLE_DELETE_V1.clone().into(),
|
||||
IDM_ACP_PEOPLE_CREDENTIAL_RESET_V1.clone().into(),
|
||||
IDM_ACP_HP_PEOPLE_CREDENTIAL_RESET_V1.clone().into(),
|
||||
IDM_ACP_SERVICE_ACCOUNT_CREATE_V1.clone().into(),
|
||||
IDM_ACP_SERVICE_ACCOUNT_DELETE_V1.clone().into(),
|
||||
IDM_ACP_SERVICE_ACCOUNT_ENTRY_MANAGER_V1.clone().into(),
|
||||
IDM_ACP_SERVICE_ACCOUNT_ENTRY_MANAGED_BY_MODIFY_V1
|
||||
.clone()
|
||||
.into(),
|
||||
IDM_ACP_HP_SERVICE_ACCOUNT_ENTRY_MANAGED_BY_MODIFY_V1
|
||||
.clone()
|
||||
.into(),
|
||||
IDM_ACP_SERVICE_ACCOUNT_MANAGE_V1.clone().into(),
|
||||
// DL4
|
||||
// DL5
|
||||
// DL6
|
||||
IDM_ACP_PEOPLE_CREATE_DL6.clone().into(),
|
||||
IDM_ACP_ACCOUNT_MAIL_READ_DL6.clone().into(),
|
||||
// DL7
|
||||
IDM_ACP_SELF_NAME_WRITE_DL7.clone().into(),
|
||||
IDM_ACP_HP_CLIENT_CERTIFICATE_MANAGER_DL7.clone().into(),
|
||||
// DL8
|
||||
IDM_ACP_SELF_READ_DL8.clone().into(),
|
||||
IDM_ACP_SELF_WRITE_DL8.clone().into(),
|
||||
IDM_ACP_APPLICATION_MANAGE_DL8.clone().into(),
|
||||
IDM_ACP_APPLICATION_ENTRY_MANAGER_DL8.clone().into(),
|
||||
IDM_ACP_MAIL_SERVERS_DL8.clone().into(),
|
||||
IDM_ACP_GROUP_ACCOUNT_POLICY_MANAGE_DL8.clone().into(),
|
||||
// DL9
|
||||
IDM_ACP_OAUTH2_MANAGE_DL9.clone().into(),
|
||||
IDM_ACP_GROUP_MANAGE_DL9.clone().into(),
|
||||
IDM_ACP_DOMAIN_ADMIN_DL9.clone().into(),
|
||||
]
|
||||
}
|
|
@ -1,7 +1,4 @@
|
|||
//! Core Constants
|
||||
//!
|
||||
//! Schema uuids start at `00000000-0000-0000-0000-ffff00000000`
|
||||
//!
|
||||
//! Schema Entries
|
||||
use crate::constants::entries::{Attribute, EntryClass};
|
||||
use crate::constants::uuids::*;
|
||||
use crate::schema::{SchemaAttribute, SchemaClass};
|
|
@ -8,6 +8,31 @@ use crate::value::Value;
|
|||
// This is separated because the password badlist section may become very long
|
||||
|
||||
lazy_static! {
|
||||
pub static ref E_SYSTEM_INFO_V1: EntryInitNew = entry_init!(
|
||||
(Attribute::Class, EntryClass::Object.to_value()),
|
||||
(Attribute::Class, EntryClass::SystemInfo.to_value()),
|
||||
(Attribute::Class, EntryClass::System.to_value()),
|
||||
(Attribute::Uuid, Value::Uuid(UUID_SYSTEM_INFO)),
|
||||
(
|
||||
Attribute::Description,
|
||||
Value::new_utf8s("System (local) info and metadata object.")
|
||||
),
|
||||
(Attribute::Version, Value::Uint32(20))
|
||||
);
|
||||
pub static ref E_DOMAIN_INFO_DL6: EntryInitNew = entry_init!(
|
||||
(Attribute::Class, EntryClass::Object.to_value()),
|
||||
(Attribute::Class, EntryClass::DomainInfo.to_value()),
|
||||
(Attribute::Class, EntryClass::System.to_value()),
|
||||
(Attribute::Class, EntryClass::KeyObject.to_value()),
|
||||
(Attribute::Class, EntryClass::KeyObjectJwtEs256.to_value()),
|
||||
(Attribute::Class, EntryClass::KeyObjectJweA128GCM.to_value()),
|
||||
(Attribute::Name, Value::new_iname("domain_local")),
|
||||
(Attribute::Uuid, Value::Uuid(UUID_DOMAIN_INFO)),
|
||||
(
|
||||
Attribute::Description,
|
||||
Value::new_utf8s("This local domain's info and metadata object.")
|
||||
)
|
||||
);
|
||||
pub static ref E_SYSTEM_CONFIG_V1: EntryInitNew = entry_init!(
|
||||
(Attribute::Class, EntryClass::Object.to_value()),
|
||||
(Attribute::Class, EntryClass::SystemConfig.to_value()),
|
2637
server/lib/src/migration_data/dl8/access.rs
Normal file
2637
server/lib/src/migration_data/dl8/access.rs
Normal file
File diff suppressed because it is too large
Load diff
35
server/lib/src/migration_data/dl8/accounts.rs
Normal file
35
server/lib/src/migration_data/dl8/accounts.rs
Normal file
|
@ -0,0 +1,35 @@
|
|||
//! Constant Entries for the IDM
|
||||
use crate::constants::uuids::*;
|
||||
use crate::migration_data::types::BuiltinAccount;
|
||||
use kanidm_proto::v1::AccountType;
|
||||
|
||||
lazy_static! {
|
||||
/// Builtin System Admin account.
|
||||
pub static ref BUILTIN_ACCOUNT_IDM_ADMIN: BuiltinAccount = BuiltinAccount {
|
||||
account_type: AccountType::ServiceAccount,
|
||||
entry_managed_by: None,
|
||||
name: "idm_admin",
|
||||
uuid: UUID_IDM_ADMIN,
|
||||
description: "Builtin IDM Admin account.",
|
||||
displayname: "IDM Administrator",
|
||||
};
|
||||
|
||||
/// Builtin System Admin account.
|
||||
pub static ref BUILTIN_ACCOUNT_ADMIN: BuiltinAccount = BuiltinAccount {
|
||||
account_type: AccountType::ServiceAccount,
|
||||
entry_managed_by: None,
|
||||
name: "admin",
|
||||
uuid: UUID_ADMIN,
|
||||
description: "Builtin System Admin account.",
|
||||
displayname: "System Administrator",
|
||||
};
|
||||
|
||||
pub static ref BUILTIN_ACCOUNT_ANONYMOUS_DL6: BuiltinAccount = BuiltinAccount {
|
||||
account_type: AccountType::ServiceAccount,
|
||||
entry_managed_by: Some(UUID_IDM_ADMINS),
|
||||
name: "anonymous",
|
||||
uuid: UUID_ANONYMOUS,
|
||||
description: "Anonymous access account.",
|
||||
displayname: "Anonymous",
|
||||
};
|
||||
}
|
|
@ -106,7 +106,9 @@ lazy_static! {
|
|||
members: vec![],
|
||||
..Default::default()
|
||||
};
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
// These are the "finer" roles. They encapsulate different concepts in the system.
|
||||
// The next section is the "system style" roles. These adjust the operation of
|
||||
// kanidm and relate to it's internals and how it functions.
|
||||
|
@ -404,46 +406,3 @@ lazy_static! {
|
|||
..Default::default()
|
||||
};
|
||||
}
|
||||
|
||||
/// Make a list of all the non-admin BuiltinGroup's that are created by default, doing it in a standard-ish way so we can use it around the platform
|
||||
pub fn idm_builtin_non_admin_groups() -> Vec<&'static BuiltinGroup> {
|
||||
// Create any system default schema entries.
|
||||
vec![
|
||||
&BUILTIN_GROUP_DOMAIN_ADMINS,
|
||||
&BUILTIN_GROUP_SCHEMA_ADMINS,
|
||||
&BUILTIN_GROUP_ACCESS_CONTROL_ADMINS,
|
||||
&BUILTIN_GROUP_UNIX_ADMINS,
|
||||
&BUILTIN_GROUP_RECYCLE_BIN_ADMINS,
|
||||
&BUILTIN_GROUP_SERVICE_DESK,
|
||||
&BUILTIN_GROUP_OAUTH2_ADMINS,
|
||||
&BUILTIN_GROUP_RADIUS_SERVICE_ADMINS,
|
||||
&BUILTIN_GROUP_ACCOUNT_POLICY_ADMINS,
|
||||
&BUILTIN_GROUP_PEOPLE_ADMINS,
|
||||
&BUILTIN_GROUP_PEOPLE_PII_READ,
|
||||
&BUILTIN_GROUP_PEOPLE_ON_BOARDING,
|
||||
&BUILTIN_GROUP_SERVICE_ACCOUNT_ADMINS,
|
||||
&BUILTIN_GROUP_MAIL_SERVICE_ADMINS_DL8,
|
||||
&IDM_GROUP_ADMINS_V1,
|
||||
&IDM_ALL_PERSONS,
|
||||
&IDM_ALL_ACCOUNTS,
|
||||
&BUILTIN_IDM_RADIUS_SERVERS_V1,
|
||||
&BUILTIN_IDM_MAIL_SERVERS_DL8,
|
||||
&BUILTIN_GROUP_PEOPLE_SELF_NAME_WRITE_DL7,
|
||||
&IDM_PEOPLE_SELF_MAIL_WRITE_DL7,
|
||||
&BUILTIN_GROUP_CLIENT_CERTIFICATE_ADMINS_DL7,
|
||||
&BUILTIN_GROUP_APPLICATION_ADMINS_DL8,
|
||||
// Write deps on read, so write must be added first.
|
||||
// All members must exist before we write HP
|
||||
&IDM_HIGH_PRIVILEGE_DL8,
|
||||
// other things
|
||||
&IDM_UI_ENABLE_EXPERIMENTAL_FEATURES,
|
||||
&IDM_ACCOUNT_MAIL_READ,
|
||||
]
|
||||
}
|
||||
|
||||
pub fn idm_builtin_admin_groups() -> Vec<&'static BuiltinGroup> {
|
||||
vec![
|
||||
&BUILTIN_GROUP_SYSTEM_ADMINS_V1,
|
||||
&BUILTIN_GROUP_IDM_ADMINS_V1,
|
||||
]
|
||||
}
|
18
server/lib/src/migration_data/dl8/key_providers.rs
Normal file
18
server/lib/src/migration_data/dl8/key_providers.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
use crate::constants::entries::{Attribute, EntryClass};
|
||||
use crate::constants::uuids::UUID_KEY_PROVIDER_INTERNAL;
|
||||
use crate::entry::{Entry, EntryInit, EntryInitNew, EntryNew};
|
||||
use crate::value::Value;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref E_KEY_PROVIDER_INTERNAL_DL6: EntryInitNew = entry_init!(
|
||||
(Attribute::Class, EntryClass::Object.to_value()),
|
||||
(Attribute::Class, EntryClass::KeyProvider.to_value()),
|
||||
(Attribute::Class, EntryClass::KeyProviderInternal.to_value()),
|
||||
(Attribute::Uuid, Value::Uuid(UUID_KEY_PROVIDER_INTERNAL)),
|
||||
(Attribute::Name, Value::new_iname("key_provider_internal")),
|
||||
(
|
||||
Attribute::Description,
|
||||
Value::new_utf8s("The default database internal cryptographic key provider.")
|
||||
)
|
||||
);
|
||||
}
|
273
server/lib/src/migration_data/dl8/mod.rs
Normal file
273
server/lib/src/migration_data/dl8/mod.rs
Normal file
|
@ -0,0 +1,273 @@
|
|||
mod access;
|
||||
mod accounts;
|
||||
mod groups;
|
||||
mod key_providers;
|
||||
mod schema;
|
||||
mod system_config;
|
||||
|
||||
use self::access::*;
|
||||
use self::accounts::*;
|
||||
use self::groups::*;
|
||||
use self::key_providers::*;
|
||||
use self::schema::*;
|
||||
use self::system_config::*;
|
||||
|
||||
use crate::prelude::EntryInitNew;
|
||||
use kanidm_proto::internal::OperationError;
|
||||
|
||||
pub fn phase_1_schema_attrs() -> Vec<EntryInitNew> {
|
||||
vec![
|
||||
SCHEMA_ATTR_SYNC_CREDENTIAL_PORTAL.clone().into(),
|
||||
SCHEMA_ATTR_SYNC_YIELD_AUTHORITY.clone().into(),
|
||||
SCHEMA_ATTR_ACCOUNT_EXPIRE.clone().into(),
|
||||
SCHEMA_ATTR_ACCOUNT_VALID_FROM.clone().into(),
|
||||
SCHEMA_ATTR_API_TOKEN_SESSION.clone().into(),
|
||||
SCHEMA_ATTR_AUTH_SESSION_EXPIRY.clone().into(),
|
||||
SCHEMA_ATTR_AUTH_PRIVILEGE_EXPIRY.clone().into(),
|
||||
SCHEMA_ATTR_AUTH_PASSWORD_MINIMUM_LENGTH.clone().into(),
|
||||
SCHEMA_ATTR_BADLIST_PASSWORD.clone().into(),
|
||||
SCHEMA_ATTR_CREDENTIAL_UPDATE_INTENT_TOKEN.clone().into(),
|
||||
SCHEMA_ATTR_ATTESTED_PASSKEYS.clone().into(),
|
||||
SCHEMA_ATTR_DOMAIN_DISPLAY_NAME.clone().into(),
|
||||
SCHEMA_ATTR_DOMAIN_LDAP_BASEDN.clone().into(),
|
||||
SCHEMA_ATTR_DOMAIN_NAME.clone().into(),
|
||||
SCHEMA_ATTR_LDAP_ALLOW_UNIX_PW_BIND.clone().into(),
|
||||
SCHEMA_ATTR_DOMAIN_SSID.clone().into(),
|
||||
SCHEMA_ATTR_DOMAIN_TOKEN_KEY.clone().into(),
|
||||
SCHEMA_ATTR_DOMAIN_UUID.clone().into(),
|
||||
SCHEMA_ATTR_DYNGROUP_FILTER.clone().into(),
|
||||
SCHEMA_ATTR_EC_KEY_PRIVATE.clone().into(),
|
||||
SCHEMA_ATTR_ES256_PRIVATE_KEY_DER.clone().into(),
|
||||
SCHEMA_ATTR_FERNET_PRIVATE_KEY_STR.clone().into(),
|
||||
SCHEMA_ATTR_GIDNUMBER.clone().into(),
|
||||
SCHEMA_ATTR_GRANT_UI_HINT.clone().into(),
|
||||
SCHEMA_ATTR_JWS_ES256_PRIVATE_KEY.clone().into(),
|
||||
SCHEMA_ATTR_LOGINSHELL.clone().into(),
|
||||
SCHEMA_ATTR_NAME_HISTORY.clone().into(),
|
||||
SCHEMA_ATTR_NSUNIQUEID.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_ALLOW_INSECURE_CLIENT_DISABLE_PKCE
|
||||
.clone()
|
||||
.into(),
|
||||
SCHEMA_ATTR_OAUTH2_CONSENT_SCOPE_MAP.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_JWT_LEGACY_CRYPTO_ENABLE.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_PREFER_SHORT_USERNAME.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_RS_BASIC_SECRET.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_RS_IMPLICIT_SCOPES.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_RS_NAME.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_RS_ORIGIN_LANDING.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_RS_SCOPE_MAP.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_RS_SUP_SCOPE_MAP.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_RS_TOKEN_KEY.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_SESSION.clone().into(),
|
||||
SCHEMA_ATTR_PASSKEYS.clone().into(),
|
||||
SCHEMA_ATTR_PRIMARY_CREDENTIAL.clone().into(),
|
||||
SCHEMA_ATTR_PRIVATE_COOKIE_KEY.clone().into(),
|
||||
SCHEMA_ATTR_RADIUS_SECRET.clone().into(),
|
||||
SCHEMA_ATTR_RS256_PRIVATE_KEY_DER.clone().into(),
|
||||
SCHEMA_ATTR_SSH_PUBLICKEY.clone().into(),
|
||||
SCHEMA_ATTR_SYNC_COOKIE.clone().into(),
|
||||
SCHEMA_ATTR_SYNC_TOKEN_SESSION.clone().into(),
|
||||
SCHEMA_ATTR_UNIX_PASSWORD.clone().into(),
|
||||
SCHEMA_ATTR_USER_AUTH_TOKEN_SESSION.clone().into(),
|
||||
SCHEMA_ATTR_DENIED_NAME.clone().into(),
|
||||
SCHEMA_ATTR_CREDENTIAL_TYPE_MINIMUM.clone().into(),
|
||||
SCHEMA_ATTR_WEBAUTHN_ATTESTATION_CA_LIST.clone().into(),
|
||||
// DL4
|
||||
SCHEMA_ATTR_OAUTH2_RS_CLAIM_MAP_DL4.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_ALLOW_LOCALHOST_REDIRECT_DL4
|
||||
.clone()
|
||||
.into(),
|
||||
// DL5
|
||||
// DL6
|
||||
SCHEMA_ATTR_LIMIT_SEARCH_MAX_RESULTS_DL6.clone().into(),
|
||||
SCHEMA_ATTR_LIMIT_SEARCH_MAX_FILTER_TEST_DL6.clone().into(),
|
||||
SCHEMA_ATTR_KEY_INTERNAL_DATA_DL6.clone().into(),
|
||||
SCHEMA_ATTR_KEY_PROVIDER_DL6.clone().into(),
|
||||
SCHEMA_ATTR_KEY_ACTION_ROTATE_DL6.clone().into(),
|
||||
SCHEMA_ATTR_KEY_ACTION_REVOKE_DL6.clone().into(),
|
||||
SCHEMA_ATTR_KEY_ACTION_IMPORT_JWS_ES256_DL6.clone().into(),
|
||||
// DL7
|
||||
SCHEMA_ATTR_PATCH_LEVEL_DL7.clone().into(),
|
||||
SCHEMA_ATTR_DOMAIN_DEVELOPMENT_TAINT_DL7.clone().into(),
|
||||
SCHEMA_ATTR_REFERS_DL7.clone().into(),
|
||||
SCHEMA_ATTR_CERTIFICATE_DL7.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_RS_ORIGIN_DL7.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_STRICT_REDIRECT_URI_DL7.clone().into(),
|
||||
SCHEMA_ATTR_MAIL_DL7.clone().into(),
|
||||
SCHEMA_ATTR_LEGALNAME_DL7.clone().into(),
|
||||
SCHEMA_ATTR_DISPLAYNAME_DL7.clone().into(),
|
||||
// DL8
|
||||
SCHEMA_ATTR_LINKED_GROUP_DL8.clone().into(),
|
||||
SCHEMA_ATTR_APPLICATION_PASSWORD_DL8.clone().into(),
|
||||
SCHEMA_ATTR_ALLOW_PRIMARY_CRED_FALLBACK_DL8.clone().into(),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn phase_2_schema_classes() -> Vec<EntryInitNew> {
|
||||
vec![
|
||||
SCHEMA_CLASS_DYNGROUP.clone().into(),
|
||||
SCHEMA_CLASS_ORGPERSON.clone().into(),
|
||||
SCHEMA_CLASS_POSIXACCOUNT.clone().into(),
|
||||
SCHEMA_CLASS_POSIXGROUP.clone().into(),
|
||||
SCHEMA_CLASS_SYSTEM_CONFIG.clone().into(),
|
||||
// DL4
|
||||
SCHEMA_CLASS_OAUTH2_RS_PUBLIC_DL4.clone().into(),
|
||||
// DL5
|
||||
// SCHEMA_CLASS_PERSON_DL5.clone().into(),
|
||||
SCHEMA_CLASS_ACCOUNT_DL5.clone().into(),
|
||||
// SCHEMA_CLASS_OAUTH2_RS_DL5.clone().into(),
|
||||
SCHEMA_CLASS_OAUTH2_RS_BASIC_DL5.clone().into(),
|
||||
// DL6
|
||||
// SCHEMA_CLASS_ACCOUNT_POLICY_DL6.clone().into(),
|
||||
// SCHEMA_CLASS_SERVICE_ACCOUNT_DL6.clone().into(),
|
||||
// SCHEMA_CLASS_SYNC_ACCOUNT_DL6.clone().into(),
|
||||
SCHEMA_CLASS_GROUP_DL6.clone().into(),
|
||||
SCHEMA_CLASS_KEY_PROVIDER_DL6.clone().into(),
|
||||
SCHEMA_CLASS_KEY_PROVIDER_INTERNAL_DL6.clone().into(),
|
||||
SCHEMA_CLASS_KEY_OBJECT_DL6.clone().into(),
|
||||
SCHEMA_CLASS_KEY_OBJECT_JWT_ES256_DL6.clone().into(),
|
||||
SCHEMA_CLASS_KEY_OBJECT_JWE_A128GCM_DL6.clone().into(),
|
||||
SCHEMA_CLASS_KEY_OBJECT_INTERNAL_DL6.clone().into(),
|
||||
// SCHEMA_CLASS_DOMAIN_INFO_DL6.clone().into(),
|
||||
// DL7
|
||||
// SCHEMA_CLASS_DOMAIN_INFO_DL7.clone().into(),
|
||||
SCHEMA_CLASS_SERVICE_ACCOUNT_DL7.clone().into(),
|
||||
SCHEMA_CLASS_SYNC_ACCOUNT_DL7.clone().into(),
|
||||
SCHEMA_CLASS_CLIENT_CERTIFICATE_DL7.clone().into(),
|
||||
SCHEMA_CLASS_OAUTH2_RS_DL7.clone().into(),
|
||||
// DL8
|
||||
SCHEMA_CLASS_ACCOUNT_POLICY_DL8.clone().into(),
|
||||
SCHEMA_CLASS_APPLICATION_DL8.clone().into(),
|
||||
SCHEMA_CLASS_PERSON_DL8.clone().into(),
|
||||
SCHEMA_CLASS_DOMAIN_INFO_DL8.clone().into(),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn phase_3_key_provider() -> Vec<EntryInitNew> {
|
||||
vec![E_KEY_PROVIDER_INTERNAL_DL6.clone()]
|
||||
}
|
||||
|
||||
pub fn phase_4_system_entries() -> Vec<EntryInitNew> {
|
||||
vec![
|
||||
E_SYSTEM_INFO_V1.clone(),
|
||||
E_DOMAIN_INFO_DL6.clone(),
|
||||
E_SYSTEM_CONFIG_V1.clone(),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn phase_5_builtin_admin_entries() -> Result<Vec<EntryInitNew>, OperationError> {
|
||||
Ok(vec![
|
||||
BUILTIN_ACCOUNT_ADMIN.clone().into(),
|
||||
BUILTIN_ACCOUNT_IDM_ADMIN.clone().into(),
|
||||
BUILTIN_GROUP_SYSTEM_ADMINS_V1.clone().try_into()?,
|
||||
BUILTIN_GROUP_IDM_ADMINS_V1.clone().try_into()?,
|
||||
// We need to push anonymous *after* groups due to entry-managed-by
|
||||
BUILTIN_ACCOUNT_ANONYMOUS_DL6.clone().into(),
|
||||
])
|
||||
}
|
||||
|
||||
pub fn phase_6_builtin_non_admin_entries() -> Result<Vec<EntryInitNew>, OperationError> {
|
||||
Ok(vec![
|
||||
BUILTIN_GROUP_DOMAIN_ADMINS.clone().try_into()?,
|
||||
BUILTIN_GROUP_SCHEMA_ADMINS.clone().try_into()?,
|
||||
BUILTIN_GROUP_ACCESS_CONTROL_ADMINS.clone().try_into()?,
|
||||
BUILTIN_GROUP_UNIX_ADMINS.clone().try_into()?,
|
||||
BUILTIN_GROUP_RECYCLE_BIN_ADMINS.clone().try_into()?,
|
||||
BUILTIN_GROUP_SERVICE_DESK.clone().try_into()?,
|
||||
BUILTIN_GROUP_OAUTH2_ADMINS.clone().try_into()?,
|
||||
BUILTIN_GROUP_RADIUS_SERVICE_ADMINS.clone().try_into()?,
|
||||
BUILTIN_GROUP_ACCOUNT_POLICY_ADMINS.clone().try_into()?,
|
||||
BUILTIN_GROUP_PEOPLE_ADMINS.clone().try_into()?,
|
||||
BUILTIN_GROUP_PEOPLE_PII_READ.clone().try_into()?,
|
||||
BUILTIN_GROUP_PEOPLE_ON_BOARDING.clone().try_into()?,
|
||||
BUILTIN_GROUP_SERVICE_ACCOUNT_ADMINS.clone().try_into()?,
|
||||
BUILTIN_GROUP_MAIL_SERVICE_ADMINS_DL8.clone().try_into()?,
|
||||
IDM_GROUP_ADMINS_V1.clone().try_into()?,
|
||||
IDM_ALL_PERSONS.clone().try_into()?,
|
||||
IDM_ALL_ACCOUNTS.clone().try_into()?,
|
||||
BUILTIN_IDM_RADIUS_SERVERS_V1.clone().try_into()?,
|
||||
BUILTIN_IDM_MAIL_SERVERS_DL8.clone().try_into()?,
|
||||
BUILTIN_GROUP_PEOPLE_SELF_NAME_WRITE_DL7
|
||||
.clone()
|
||||
.try_into()?,
|
||||
IDM_PEOPLE_SELF_MAIL_WRITE_DL7.clone().try_into()?,
|
||||
BUILTIN_GROUP_CLIENT_CERTIFICATE_ADMINS_DL7
|
||||
.clone()
|
||||
.try_into()?,
|
||||
BUILTIN_GROUP_APPLICATION_ADMINS_DL8.clone().try_into()?,
|
||||
// Write deps on read.clone().try_into()?, so write must be added first.
|
||||
// All members must exist before we write HP
|
||||
IDM_HIGH_PRIVILEGE_DL8.clone().try_into()?,
|
||||
// other things
|
||||
IDM_UI_ENABLE_EXPERIMENTAL_FEATURES.clone().try_into()?,
|
||||
IDM_ACCOUNT_MAIL_READ.clone().try_into()?,
|
||||
])
|
||||
}
|
||||
|
||||
pub fn phase_7_builtin_access_control_profiles() -> Vec<EntryInitNew> {
|
||||
vec![
|
||||
// Built in access controls.
|
||||
IDM_ACP_RECYCLE_BIN_SEARCH_V1.clone().into(),
|
||||
IDM_ACP_RECYCLE_BIN_REVIVE_V1.clone().into(),
|
||||
IDM_ACP_SCHEMA_WRITE_ATTRS_V1.clone().into(),
|
||||
IDM_ACP_SCHEMA_WRITE_CLASSES_V1.clone().into(),
|
||||
IDM_ACP_ACP_MANAGE_V1.clone().into(),
|
||||
IDM_ACP_GROUP_ENTRY_MANAGED_BY_MODIFY_V1.clone().into(),
|
||||
IDM_ACP_GROUP_ENTRY_MANAGER_V1.clone().into(),
|
||||
IDM_ACP_SYNC_ACCOUNT_MANAGE_V1.clone().into(),
|
||||
IDM_ACP_RADIUS_SERVERS_V1.clone().into(),
|
||||
IDM_ACP_RADIUS_SECRET_MANAGE_V1.clone().into(),
|
||||
IDM_ACP_PEOPLE_SELF_WRITE_MAIL_V1.clone().into(),
|
||||
// IDM_ACP_SELF_READ_V1.clone(),
|
||||
// IDM_ACP_SELF_WRITE_V1.clone(),
|
||||
IDM_ACP_ACCOUNT_SELF_WRITE_V1.clone().into(),
|
||||
// IDM_ACP_SELF_NAME_WRITE_V1.clone(),
|
||||
IDM_ACP_ALL_ACCOUNTS_POSIX_READ_V1.clone().into(),
|
||||
IDM_ACP_SYSTEM_CONFIG_ACCOUNT_POLICY_MANAGE_V1
|
||||
.clone()
|
||||
.into(),
|
||||
IDM_ACP_GROUP_UNIX_MANAGE_V1.clone().into(),
|
||||
IDM_ACP_HP_GROUP_UNIX_MANAGE_V1.clone().into(),
|
||||
IDM_ACP_GROUP_READ_V1.clone().into(),
|
||||
IDM_ACP_ACCOUNT_UNIX_EXTEND_V1.clone().into(),
|
||||
IDM_ACP_PEOPLE_PII_READ_V1.clone().into(),
|
||||
IDM_ACP_PEOPLE_PII_MANAGE_V1.clone().into(),
|
||||
IDM_ACP_PEOPLE_READ_V1.clone().into(),
|
||||
IDM_ACP_PEOPLE_MANAGE_V1.clone().into(),
|
||||
IDM_ACP_PEOPLE_DELETE_V1.clone().into(),
|
||||
IDM_ACP_PEOPLE_CREDENTIAL_RESET_V1.clone().into(),
|
||||
IDM_ACP_HP_PEOPLE_CREDENTIAL_RESET_V1.clone().into(),
|
||||
IDM_ACP_SERVICE_ACCOUNT_CREATE_V1.clone().into(),
|
||||
IDM_ACP_SERVICE_ACCOUNT_DELETE_V1.clone().into(),
|
||||
IDM_ACP_SERVICE_ACCOUNT_ENTRY_MANAGER_V1.clone().into(),
|
||||
IDM_ACP_SERVICE_ACCOUNT_ENTRY_MANAGED_BY_MODIFY_V1
|
||||
.clone()
|
||||
.into(),
|
||||
IDM_ACP_HP_SERVICE_ACCOUNT_ENTRY_MANAGED_BY_MODIFY_V1
|
||||
.clone()
|
||||
.into(),
|
||||
IDM_ACP_SERVICE_ACCOUNT_MANAGE_V1.clone().into(),
|
||||
// DL4
|
||||
// DL5
|
||||
// IDM_ACP_OAUTH2_MANAGE_DL5.clone(),
|
||||
// DL6
|
||||
// IDM_ACP_GROUP_ACCOUNT_POLICY_MANAGE_DL6.clone(),
|
||||
IDM_ACP_PEOPLE_CREATE_DL6.clone().into(),
|
||||
IDM_ACP_GROUP_MANAGE_DL6.clone().into(),
|
||||
IDM_ACP_ACCOUNT_MAIL_READ_DL6.clone().into(),
|
||||
// IDM_ACP_DOMAIN_ADMIN_DL6.clone(),
|
||||
// DL7
|
||||
// IDM_ACP_SELF_WRITE_DL7.clone(),
|
||||
IDM_ACP_SELF_NAME_WRITE_DL7.clone().into(),
|
||||
IDM_ACP_HP_CLIENT_CERTIFICATE_MANAGER_DL7.clone().into(),
|
||||
IDM_ACP_OAUTH2_MANAGE_DL7.clone().into(),
|
||||
// DL8
|
||||
IDM_ACP_SELF_READ_DL8.clone().into(),
|
||||
IDM_ACP_SELF_WRITE_DL8.clone().into(),
|
||||
IDM_ACP_APPLICATION_MANAGE_DL8.clone().into(),
|
||||
IDM_ACP_APPLICATION_ENTRY_MANAGER_DL8.clone().into(),
|
||||
IDM_ACP_MAIL_SERVERS_DL8.clone().into(),
|
||||
IDM_ACP_DOMAIN_ADMIN_DL8.clone().into(),
|
||||
IDM_ACP_GROUP_ACCOUNT_POLICY_MANAGE_DL8.clone().into(),
|
||||
]
|
||||
}
|
1506
server/lib/src/migration_data/dl8/schema.rs
Normal file
1506
server/lib/src/migration_data/dl8/schema.rs
Normal file
File diff suppressed because it is too large
Load diff
1073
server/lib/src/migration_data/dl8/system_config.rs
Normal file
1073
server/lib/src/migration_data/dl8/system_config.rs
Normal file
File diff suppressed because it is too large
Load diff
2637
server/lib/src/migration_data/dl9/access.rs
Normal file
2637
server/lib/src/migration_data/dl9/access.rs
Normal file
File diff suppressed because it is too large
Load diff
35
server/lib/src/migration_data/dl9/accounts.rs
Normal file
35
server/lib/src/migration_data/dl9/accounts.rs
Normal file
|
@ -0,0 +1,35 @@
|
|||
//! Constant Entries for the IDM
|
||||
use crate::constants::uuids::*;
|
||||
use crate::migration_data::types::BuiltinAccount;
|
||||
use kanidm_proto::v1::AccountType;
|
||||
|
||||
lazy_static! {
|
||||
/// Builtin System Admin account.
|
||||
pub static ref BUILTIN_ACCOUNT_IDM_ADMIN: BuiltinAccount = BuiltinAccount {
|
||||
account_type: AccountType::ServiceAccount,
|
||||
entry_managed_by: None,
|
||||
name: "idm_admin",
|
||||
uuid: UUID_IDM_ADMIN,
|
||||
description: "Builtin IDM Admin account.",
|
||||
displayname: "IDM Administrator",
|
||||
};
|
||||
|
||||
/// Builtin System Admin account.
|
||||
pub static ref BUILTIN_ACCOUNT_ADMIN: BuiltinAccount = BuiltinAccount {
|
||||
account_type: AccountType::ServiceAccount,
|
||||
entry_managed_by: None,
|
||||
name: "admin",
|
||||
uuid: UUID_ADMIN,
|
||||
description: "Builtin System Admin account.",
|
||||
displayname: "System Administrator",
|
||||
};
|
||||
|
||||
pub static ref BUILTIN_ACCOUNT_ANONYMOUS_DL6: BuiltinAccount = BuiltinAccount {
|
||||
account_type: AccountType::ServiceAccount,
|
||||
entry_managed_by: Some(UUID_IDM_ADMINS),
|
||||
name: "anonymous",
|
||||
uuid: UUID_ANONYMOUS,
|
||||
description: "Anonymous access account.",
|
||||
displayname: "Anonymous",
|
||||
};
|
||||
}
|
408
server/lib/src/migration_data/dl9/groups.rs
Normal file
408
server/lib/src/migration_data/dl9/groups.rs
Normal file
|
@ -0,0 +1,408 @@
|
|||
use crate::entry::EntryInitNew;
|
||||
use crate::prelude::*;
|
||||
use crate::value::CredentialType;
|
||||
|
||||
use kanidm_proto::internal::{Filter, OperationError, UiHint};
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
/// Built-in group definitions
|
||||
pub struct BuiltinGroup {
|
||||
pub name: &'static str,
|
||||
pub description: &'static str,
|
||||
pub uuid: uuid::Uuid,
|
||||
pub members: Vec<uuid::Uuid>,
|
||||
pub entry_managed_by: Option<uuid::Uuid>,
|
||||
pub dyngroup: bool,
|
||||
pub dyngroup_filter: Option<Filter>,
|
||||
pub extra_attributes: Vec<(Attribute, Value)>,
|
||||
}
|
||||
|
||||
impl TryFrom<BuiltinGroup> for EntryInitNew {
|
||||
type Error = OperationError;
|
||||
|
||||
fn try_from(val: BuiltinGroup) -> Result<Self, OperationError> {
|
||||
let mut entry = EntryInitNew::new();
|
||||
|
||||
if val.uuid >= DYNAMIC_RANGE_MINIMUM_UUID {
|
||||
error!("Builtin ACP has invalid UUID! {:?}", val);
|
||||
return Err(OperationError::InvalidUuid);
|
||||
}
|
||||
|
||||
entry.add_ava(Attribute::Name, Value::new_iname(val.name));
|
||||
entry.add_ava(Attribute::Description, Value::new_utf8s(val.description));
|
||||
// classes for groups
|
||||
entry.set_ava(
|
||||
Attribute::Class,
|
||||
vec![EntryClass::Group.into(), EntryClass::Object.into()],
|
||||
);
|
||||
if val.dyngroup {
|
||||
if !val.members.is_empty() {
|
||||
return Err(OperationError::InvalidSchemaState(format!(
|
||||
"Builtin dyngroup {} has members specified, this is not allowed",
|
||||
val.name
|
||||
)));
|
||||
}
|
||||
entry.add_ava(Attribute::Class, EntryClass::DynGroup.to_value());
|
||||
match val.dyngroup_filter {
|
||||
Some(filter) => entry.add_ava(Attribute::DynGroupFilter, Value::JsonFilt(filter)),
|
||||
None => {
|
||||
error!(
|
||||
"No filter specified for dyngroup '{}' this is going to break things!",
|
||||
val.name
|
||||
);
|
||||
return Err(OperationError::FilterGeneration);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if let Some(entry_manager) = val.entry_managed_by {
|
||||
entry.add_ava(Attribute::EntryManagedBy, Value::Refer(entry_manager));
|
||||
}
|
||||
|
||||
entry.add_ava(Attribute::Uuid, Value::Uuid(val.uuid));
|
||||
entry.set_ava(
|
||||
Attribute::Member,
|
||||
val.members
|
||||
.into_iter()
|
||||
.map(Value::Refer)
|
||||
.collect::<Vec<Value>>(),
|
||||
);
|
||||
// add any extra attributes
|
||||
val.extra_attributes
|
||||
.into_iter()
|
||||
.for_each(|(attr, val)| entry.add_ava(attr, val));
|
||||
// all done!
|
||||
Ok(entry)
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
// There are our built in "roles". They encapsulate some higher level collections
|
||||
// of roles. The intent is to allow a pretty generic and correct by default set
|
||||
// of these use cases.
|
||||
pub static ref BUILTIN_GROUP_SYSTEM_ADMINS_V1: BuiltinGroup = BuiltinGroup {
|
||||
name: "system_admins",
|
||||
description: "Builtin System Administrators Group.",
|
||||
uuid: UUID_SYSTEM_ADMINS,
|
||||
entry_managed_by: Some(UUID_SYSTEM_ADMINS),
|
||||
members: vec![UUID_ADMIN],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
pub static ref BUILTIN_GROUP_IDM_ADMINS_V1: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_admins",
|
||||
description: "Builtin IDM Administrators Group.",
|
||||
uuid: UUID_IDM_ADMINS,
|
||||
entry_managed_by: Some(UUID_IDM_ADMINS),
|
||||
members: vec![UUID_IDM_ADMIN],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
pub static ref BUILTIN_GROUP_SERVICE_DESK: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_service_desk",
|
||||
description: "Builtin Service Desk Group.",
|
||||
uuid: UUID_IDM_SERVICE_DESK,
|
||||
entry_managed_by: Some(UUID_IDM_ADMINS),
|
||||
members: vec![],
|
||||
..Default::default()
|
||||
};
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
// These are the "finer" roles. They encapsulate different concepts in the system.
|
||||
// The next section is the "system style" roles. These adjust the operation of
|
||||
// kanidm and relate to it's internals and how it functions.
|
||||
pub static ref BUILTIN_GROUP_RECYCLE_BIN_ADMINS: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_recycle_bin_admins",
|
||||
description: "Builtin Recycle Bin Administrators Group.",
|
||||
uuid: UUID_IDM_RECYCLE_BIN_ADMINS,
|
||||
entry_managed_by: Some(UUID_SYSTEM_ADMINS),
|
||||
members: vec![UUID_SYSTEM_ADMINS],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
/// Builtin IDM Group for granting local domain administration rights and trust administration rights
|
||||
pub static ref BUILTIN_GROUP_DOMAIN_ADMINS: BuiltinGroup = BuiltinGroup {
|
||||
name: "domain_admins",
|
||||
description: "Builtin IDM Group for granting local domain administration rights and trust administration rights.",
|
||||
uuid: UUID_DOMAIN_ADMINS,
|
||||
entry_managed_by: Some(UUID_SYSTEM_ADMINS),
|
||||
members: vec![UUID_SYSTEM_ADMINS],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
pub static ref BUILTIN_GROUP_SCHEMA_ADMINS: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_schema_admins",
|
||||
description: "Builtin Schema Administration Group.",
|
||||
uuid: UUID_IDM_SCHEMA_ADMINS,
|
||||
entry_managed_by: Some(UUID_SYSTEM_ADMINS),
|
||||
members: vec![UUID_SYSTEM_ADMINS],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
pub static ref BUILTIN_GROUP_ACCESS_CONTROL_ADMINS: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_access_control_admins",
|
||||
description: "Builtin Access Control Administration Group.",
|
||||
entry_managed_by: Some(UUID_SYSTEM_ADMINS),
|
||||
uuid: UUID_IDM_ACCESS_CONTROL_ADMINS,
|
||||
members: vec![UUID_SYSTEM_ADMINS],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
// These are the IDM roles. They concern application integration, user permissions
|
||||
// and credential security management.
|
||||
|
||||
/// Builtin IDM Group for managing persons and their account details
|
||||
pub static ref BUILTIN_GROUP_PEOPLE_ADMINS: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_people_admins",
|
||||
description: "Builtin People Administration Group.",
|
||||
uuid: UUID_IDM_PEOPLE_ADMINS,
|
||||
entry_managed_by: Some(UUID_IDM_ADMINS),
|
||||
members: vec![UUID_IDM_ADMINS],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
pub static ref BUILTIN_GROUP_PEOPLE_ON_BOARDING: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_people_on_boarding",
|
||||
description: "Builtin People On Boarding Group.",
|
||||
uuid: UUID_IDM_PEOPLE_ON_BOARDING,
|
||||
entry_managed_by: Some(UUID_IDM_ADMINS),
|
||||
members: vec![],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
/// Builtin IDM Group for granting elevated people (personal data) read permissions.
|
||||
pub static ref BUILTIN_GROUP_PEOPLE_PII_READ: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_people_pii_read",
|
||||
description: "Builtin IDM Group for granting elevated people (personal data) read permissions.",
|
||||
uuid: UUID_IDM_PEOPLE_PII_READ,
|
||||
entry_managed_by: Some(UUID_IDM_ADMINS),
|
||||
members: vec![],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
/// Builtin IDM Group for granting people the ability to write to their own name attributes.
|
||||
pub static ref BUILTIN_GROUP_PEOPLE_SELF_NAME_WRITE_DL7: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_people_self_name_write",
|
||||
description: "Builtin IDM Group denoting users that can write to their own name attributes.",
|
||||
uuid: UUID_IDM_PEOPLE_SELF_NAME_WRITE,
|
||||
entry_managed_by: Some(UUID_IDM_ADMINS),
|
||||
members: vec![
|
||||
UUID_IDM_ALL_PERSONS
|
||||
],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
pub static ref BUILTIN_GROUP_SERVICE_ACCOUNT_ADMINS: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_service_account_admins",
|
||||
description: "Builtin Service Account Administration Group.",
|
||||
uuid: UUID_IDM_SERVICE_ACCOUNT_ADMINS,
|
||||
entry_managed_by: Some(UUID_IDM_ADMINS),
|
||||
members: vec![UUID_IDM_ADMINS],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
/// Builtin IDM Group for managing oauth2 resource server integrations to this authentication domain.
|
||||
pub static ref BUILTIN_GROUP_OAUTH2_ADMINS: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_oauth2_admins",
|
||||
description: "Builtin Oauth2 Integration Administration Group.",
|
||||
uuid: UUID_IDM_OAUTH2_ADMINS,
|
||||
entry_managed_by: Some(UUID_IDM_ADMINS),
|
||||
members: vec![UUID_IDM_ADMINS],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
pub static ref BUILTIN_GROUP_RADIUS_SERVICE_ADMINS: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_radius_service_admins",
|
||||
description: "Builtin Radius Administration Group.",
|
||||
uuid: UUID_IDM_RADIUS_ADMINS,
|
||||
entry_managed_by: Some(UUID_IDM_ADMINS),
|
||||
members: vec![UUID_IDM_ADMINS],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
/// Builtin IDM Group for RADIUS server access delegation.
|
||||
pub static ref BUILTIN_IDM_RADIUS_SERVERS_V1: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_radius_servers",
|
||||
description: "Builtin IDM Group for RADIUS server access delegation.",
|
||||
uuid: UUID_IDM_RADIUS_SERVERS,
|
||||
entry_managed_by: Some(UUID_IDM_RADIUS_ADMINS),
|
||||
members: vec![
|
||||
],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
pub static ref BUILTIN_GROUP_MAIL_SERVICE_ADMINS_DL8: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_mail_service_admins",
|
||||
description: "Builtin Mail Server Administration Group.",
|
||||
uuid: UUID_IDM_MAIL_ADMINS,
|
||||
entry_managed_by: Some(UUID_IDM_ADMINS),
|
||||
members: vec![UUID_IDM_ADMINS],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
/// Builtin IDM Group for MAIL server Access delegation.
|
||||
pub static ref BUILTIN_IDM_MAIL_SERVERS_DL8: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_mail_servers",
|
||||
description: "Builtin IDM Group for MAIL server access delegation.",
|
||||
uuid: UUID_IDM_MAIL_SERVERS,
|
||||
entry_managed_by: Some(UUID_IDM_MAIL_ADMINS),
|
||||
members: vec![
|
||||
],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
pub static ref BUILTIN_GROUP_ACCOUNT_POLICY_ADMINS: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_account_policy_admins",
|
||||
description: "Builtin Account Policy Administration Group.",
|
||||
uuid: UUID_IDM_ACCOUNT_POLICY_ADMINS,
|
||||
entry_managed_by: Some(UUID_IDM_ADMINS),
|
||||
members: vec![UUID_IDM_ADMINS],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
/// Builtin IDM Group for managing posix/unix attributes on groups and users.
|
||||
pub static ref BUILTIN_GROUP_UNIX_ADMINS: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_unix_admins",
|
||||
description: "Builtin Unix Administration Group.",
|
||||
uuid: UUID_IDM_UNIX_ADMINS,
|
||||
entry_managed_by: Some(UUID_IDM_ADMINS),
|
||||
members: vec![UUID_IDM_ADMINS],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
/// Builtin IDM Group for managing client authentication certificates.
|
||||
pub static ref BUILTIN_GROUP_CLIENT_CERTIFICATE_ADMINS_DL7: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_client_certificate_admins",
|
||||
description: "Builtin Client Certificate Administration Group.",
|
||||
uuid: UUID_IDM_CLIENT_CERTIFICATE_ADMINS,
|
||||
entry_managed_by: Some(UUID_IDM_ADMINS),
|
||||
members: vec![UUID_IDM_ADMINS],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
/// Builtin IDM Group for granting elevated group write and lifecycle permissions.
|
||||
pub static ref IDM_GROUP_ADMINS_V1: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_group_admins",
|
||||
description: "Builtin IDM Group for granting elevated group write and lifecycle permissions.",
|
||||
uuid: UUID_IDM_GROUP_ADMINS,
|
||||
entry_managed_by: Some(UUID_IDM_ADMINS),
|
||||
members: vec![UUID_IDM_ADMINS],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
/// Self-write of mail
|
||||
pub static ref IDM_PEOPLE_SELF_MAIL_WRITE_DL7: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_people_self_mail_write",
|
||||
description: "Builtin IDM Group for people accounts to update their own mail.",
|
||||
uuid: UUID_IDM_PEOPLE_SELF_MAIL_WRITE,
|
||||
members: Vec::with_capacity(0),
|
||||
..Default::default()
|
||||
};
|
||||
}
|
||||
|
||||
// at some point vs code just gives up on syntax highlighting inside lazy_static...
|
||||
lazy_static! {
|
||||
pub static ref IDM_ALL_PERSONS: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_all_persons",
|
||||
description: "Builtin IDM dynamic group containing all persons.",
|
||||
uuid: UUID_IDM_ALL_PERSONS,
|
||||
members: Vec::with_capacity(0),
|
||||
dyngroup: true,
|
||||
dyngroup_filter: Some(
|
||||
Filter::And(vec![
|
||||
Filter::Eq(Attribute::Class.to_string(), EntryClass::Person.to_string()),
|
||||
Filter::Eq(Attribute::Class.to_string(), EntryClass::Account.to_string()),
|
||||
])
|
||||
),
|
||||
extra_attributes: vec![
|
||||
// Enable account policy by default
|
||||
(Attribute::Class, EntryClass::AccountPolicy.to_value()),
|
||||
// Enforce this is a system protected object
|
||||
(Attribute::Class, EntryClass::System.to_value()),
|
||||
// MFA By Default
|
||||
(Attribute::CredentialTypeMinimum, CredentialType::Mfa.into()),
|
||||
],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
pub static ref IDM_ALL_ACCOUNTS: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_all_accounts",
|
||||
description: "Builtin IDM dynamic group containing all entries that can authenticate.",
|
||||
uuid: UUID_IDM_ALL_ACCOUNTS,
|
||||
members: Vec::with_capacity(0),
|
||||
dyngroup: true,
|
||||
dyngroup_filter: Some(
|
||||
Filter::Eq(Attribute::Class.to_string(), EntryClass::Account.to_string()),
|
||||
),
|
||||
extra_attributes: vec![
|
||||
// Enable account policy by default
|
||||
(Attribute::Class, EntryClass::AccountPolicy.to_value()),
|
||||
// Enforce this is a system protected object
|
||||
(Attribute::Class, EntryClass::System.to_value()),
|
||||
],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
|
||||
pub static ref IDM_UI_ENABLE_EXPERIMENTAL_FEATURES: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_ui_enable_experimental_features",
|
||||
description: "Members of this group will have access to experimental web UI features.",
|
||||
uuid: UUID_IDM_UI_ENABLE_EXPERIMENTAL_FEATURES,
|
||||
entry_managed_by: Some(UUID_IDM_ADMINS),
|
||||
extra_attributes: vec![
|
||||
(Attribute::GrantUiHint, Value::UiHint(UiHint::ExperimentalFeatures))
|
||||
],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
/// Members of this group will have access to read the mail attribute of all persons and service accounts.
|
||||
pub static ref IDM_ACCOUNT_MAIL_READ: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_account_mail_read",
|
||||
description: "Members of this group will have access to read the mail attribute of all persons and service accounts.",
|
||||
entry_managed_by: Some(UUID_IDM_ACCESS_CONTROL_ADMINS),
|
||||
uuid: UUID_IDM_ACCOUNT_MAIL_READ,
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
/// This must be the last group to init to include the UUID of the other high priv groups.
|
||||
pub static ref IDM_HIGH_PRIVILEGE_DL8: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_high_privilege",
|
||||
uuid: UUID_IDM_HIGH_PRIVILEGE,
|
||||
entry_managed_by: Some(UUID_IDM_ACCESS_CONTROL_ADMINS),
|
||||
description: "Builtin IDM provided groups with high levels of access that should be audited and limited in modification.",
|
||||
members: vec![
|
||||
UUID_SYSTEM_ADMINS,
|
||||
UUID_IDM_ADMINS,
|
||||
UUID_DOMAIN_ADMINS,
|
||||
UUID_IDM_SERVICE_DESK,
|
||||
UUID_IDM_RECYCLE_BIN_ADMINS,
|
||||
UUID_IDM_SCHEMA_ADMINS,
|
||||
UUID_IDM_ACCESS_CONTROL_ADMINS,
|
||||
UUID_IDM_OAUTH2_ADMINS,
|
||||
UUID_IDM_RADIUS_ADMINS,
|
||||
UUID_IDM_ACCOUNT_POLICY_ADMINS,
|
||||
UUID_IDM_RADIUS_SERVERS,
|
||||
UUID_IDM_GROUP_ADMINS,
|
||||
UUID_IDM_UNIX_ADMINS,
|
||||
UUID_IDM_PEOPLE_PII_READ,
|
||||
UUID_IDM_PEOPLE_ADMINS,
|
||||
UUID_IDM_PEOPLE_ON_BOARDING,
|
||||
UUID_IDM_SERVICE_ACCOUNT_ADMINS,
|
||||
UUID_IDM_CLIENT_CERTIFICATE_ADMINS,
|
||||
UUID_IDM_APPLICATION_ADMINS,
|
||||
UUID_IDM_MAIL_ADMINS,
|
||||
UUID_IDM_HIGH_PRIVILEGE,
|
||||
],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
pub static ref BUILTIN_GROUP_APPLICATION_ADMINS_DL8: BuiltinGroup = BuiltinGroup {
|
||||
name: "idm_application_admins",
|
||||
uuid: UUID_IDM_APPLICATION_ADMINS,
|
||||
description: "Builtin Application Administration Group.",
|
||||
entry_managed_by: Some(UUID_IDM_ADMINS),
|
||||
members: vec![UUID_IDM_ADMINS],
|
||||
..Default::default()
|
||||
};
|
||||
}
|
18
server/lib/src/migration_data/dl9/key_providers.rs
Normal file
18
server/lib/src/migration_data/dl9/key_providers.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
use crate::constants::entries::{Attribute, EntryClass};
|
||||
use crate::constants::uuids::UUID_KEY_PROVIDER_INTERNAL;
|
||||
use crate::entry::{Entry, EntryInit, EntryInitNew, EntryNew};
|
||||
use crate::value::Value;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref E_KEY_PROVIDER_INTERNAL_DL6: EntryInitNew = entry_init!(
|
||||
(Attribute::Class, EntryClass::Object.to_value()),
|
||||
(Attribute::Class, EntryClass::KeyProvider.to_value()),
|
||||
(Attribute::Class, EntryClass::KeyProviderInternal.to_value()),
|
||||
(Attribute::Uuid, Value::Uuid(UUID_KEY_PROVIDER_INTERNAL)),
|
||||
(Attribute::Name, Value::new_iname("key_provider_internal")),
|
||||
(
|
||||
Attribute::Description,
|
||||
Value::new_utf8s("The default database internal cryptographic key provider.")
|
||||
)
|
||||
);
|
||||
}
|
264
server/lib/src/migration_data/dl9/mod.rs
Normal file
264
server/lib/src/migration_data/dl9/mod.rs
Normal file
|
@ -0,0 +1,264 @@
|
|||
mod access;
|
||||
mod accounts;
|
||||
mod groups;
|
||||
mod key_providers;
|
||||
mod schema;
|
||||
mod system_config;
|
||||
|
||||
use self::access::*;
|
||||
use self::accounts::*;
|
||||
use self::groups::*;
|
||||
use self::key_providers::*;
|
||||
use self::schema::*;
|
||||
use self::system_config::*;
|
||||
|
||||
use crate::prelude::EntryInitNew;
|
||||
use kanidm_proto::internal::OperationError;
|
||||
|
||||
pub fn phase_1_schema_attrs() -> Vec<EntryInitNew> {
|
||||
vec![
|
||||
SCHEMA_ATTR_SYNC_CREDENTIAL_PORTAL.clone().into(),
|
||||
SCHEMA_ATTR_SYNC_YIELD_AUTHORITY.clone().into(),
|
||||
SCHEMA_ATTR_ACCOUNT_EXPIRE.clone().into(),
|
||||
SCHEMA_ATTR_ACCOUNT_VALID_FROM.clone().into(),
|
||||
SCHEMA_ATTR_API_TOKEN_SESSION.clone().into(),
|
||||
SCHEMA_ATTR_AUTH_SESSION_EXPIRY.clone().into(),
|
||||
SCHEMA_ATTR_AUTH_PRIVILEGE_EXPIRY.clone().into(),
|
||||
SCHEMA_ATTR_AUTH_PASSWORD_MINIMUM_LENGTH.clone().into(),
|
||||
SCHEMA_ATTR_BADLIST_PASSWORD.clone().into(),
|
||||
SCHEMA_ATTR_CREDENTIAL_UPDATE_INTENT_TOKEN.clone().into(),
|
||||
SCHEMA_ATTR_ATTESTED_PASSKEYS.clone().into(),
|
||||
SCHEMA_ATTR_DOMAIN_DISPLAY_NAME.clone().into(),
|
||||
SCHEMA_ATTR_DOMAIN_LDAP_BASEDN.clone().into(),
|
||||
SCHEMA_ATTR_DOMAIN_NAME.clone().into(),
|
||||
SCHEMA_ATTR_LDAP_ALLOW_UNIX_PW_BIND.clone().into(),
|
||||
SCHEMA_ATTR_DOMAIN_SSID.clone().into(),
|
||||
SCHEMA_ATTR_DOMAIN_TOKEN_KEY.clone().into(),
|
||||
SCHEMA_ATTR_DOMAIN_UUID.clone().into(),
|
||||
SCHEMA_ATTR_DYNGROUP_FILTER.clone().into(),
|
||||
SCHEMA_ATTR_EC_KEY_PRIVATE.clone().into(),
|
||||
SCHEMA_ATTR_ES256_PRIVATE_KEY_DER.clone().into(),
|
||||
SCHEMA_ATTR_FERNET_PRIVATE_KEY_STR.clone().into(),
|
||||
SCHEMA_ATTR_GIDNUMBER.clone().into(),
|
||||
SCHEMA_ATTR_GRANT_UI_HINT.clone().into(),
|
||||
SCHEMA_ATTR_JWS_ES256_PRIVATE_KEY.clone().into(),
|
||||
SCHEMA_ATTR_LOGINSHELL.clone().into(),
|
||||
SCHEMA_ATTR_NAME_HISTORY.clone().into(),
|
||||
SCHEMA_ATTR_NSUNIQUEID.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_ALLOW_INSECURE_CLIENT_DISABLE_PKCE
|
||||
.clone()
|
||||
.into(),
|
||||
SCHEMA_ATTR_OAUTH2_CONSENT_SCOPE_MAP.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_JWT_LEGACY_CRYPTO_ENABLE.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_PREFER_SHORT_USERNAME.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_RS_BASIC_SECRET.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_RS_IMPLICIT_SCOPES.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_RS_NAME.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_RS_ORIGIN_LANDING.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_RS_SCOPE_MAP.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_RS_SUP_SCOPE_MAP.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_RS_TOKEN_KEY.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_SESSION.clone().into(),
|
||||
SCHEMA_ATTR_PASSKEYS.clone().into(),
|
||||
SCHEMA_ATTR_PRIMARY_CREDENTIAL.clone().into(),
|
||||
SCHEMA_ATTR_PRIVATE_COOKIE_KEY.clone().into(),
|
||||
SCHEMA_ATTR_RADIUS_SECRET.clone().into(),
|
||||
SCHEMA_ATTR_RS256_PRIVATE_KEY_DER.clone().into(),
|
||||
SCHEMA_ATTR_SSH_PUBLICKEY.clone().into(),
|
||||
SCHEMA_ATTR_SYNC_COOKIE.clone().into(),
|
||||
SCHEMA_ATTR_SYNC_TOKEN_SESSION.clone().into(),
|
||||
SCHEMA_ATTR_UNIX_PASSWORD.clone().into(),
|
||||
SCHEMA_ATTR_USER_AUTH_TOKEN_SESSION.clone().into(),
|
||||
SCHEMA_ATTR_DENIED_NAME.clone().into(),
|
||||
SCHEMA_ATTR_CREDENTIAL_TYPE_MINIMUM.clone().into(),
|
||||
SCHEMA_ATTR_WEBAUTHN_ATTESTATION_CA_LIST.clone().into(),
|
||||
// DL4
|
||||
SCHEMA_ATTR_OAUTH2_RS_CLAIM_MAP_DL4.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_ALLOW_LOCALHOST_REDIRECT_DL4
|
||||
.clone()
|
||||
.into(),
|
||||
// DL5
|
||||
// DL6
|
||||
SCHEMA_ATTR_LIMIT_SEARCH_MAX_RESULTS_DL6.clone().into(),
|
||||
SCHEMA_ATTR_LIMIT_SEARCH_MAX_FILTER_TEST_DL6.clone().into(),
|
||||
SCHEMA_ATTR_KEY_INTERNAL_DATA_DL6.clone().into(),
|
||||
SCHEMA_ATTR_KEY_PROVIDER_DL6.clone().into(),
|
||||
SCHEMA_ATTR_KEY_ACTION_ROTATE_DL6.clone().into(),
|
||||
SCHEMA_ATTR_KEY_ACTION_REVOKE_DL6.clone().into(),
|
||||
SCHEMA_ATTR_KEY_ACTION_IMPORT_JWS_ES256_DL6.clone().into(),
|
||||
// DL7
|
||||
SCHEMA_ATTR_PATCH_LEVEL_DL7.clone().into(),
|
||||
SCHEMA_ATTR_DOMAIN_DEVELOPMENT_TAINT_DL7.clone().into(),
|
||||
SCHEMA_ATTR_REFERS_DL7.clone().into(),
|
||||
SCHEMA_ATTR_CERTIFICATE_DL7.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_RS_ORIGIN_DL7.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_STRICT_REDIRECT_URI_DL7.clone().into(),
|
||||
SCHEMA_ATTR_MAIL_DL7.clone().into(),
|
||||
SCHEMA_ATTR_LEGALNAME_DL7.clone().into(),
|
||||
SCHEMA_ATTR_DISPLAYNAME_DL7.clone().into(),
|
||||
// DL8
|
||||
SCHEMA_ATTR_LINKED_GROUP_DL8.clone().into(),
|
||||
SCHEMA_ATTR_APPLICATION_PASSWORD_DL8.clone().into(),
|
||||
SCHEMA_ATTR_ALLOW_PRIMARY_CRED_FALLBACK_DL8.clone().into(),
|
||||
// DL9
|
||||
SCHEMA_ATTR_OAUTH2_DEVICE_FLOW_ENABLE_DL9.clone().into(),
|
||||
SCHEMA_ATTR_DOMAIN_ALLOW_EASTER_EGGS_DL9.clone().into(),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn phase_2_schema_classes() -> Vec<EntryInitNew> {
|
||||
vec![
|
||||
SCHEMA_CLASS_DYNGROUP.clone().into(),
|
||||
SCHEMA_CLASS_ORGPERSON.clone().into(),
|
||||
SCHEMA_CLASS_POSIXACCOUNT.clone().into(),
|
||||
SCHEMA_CLASS_POSIXGROUP.clone().into(),
|
||||
SCHEMA_CLASS_SYSTEM_CONFIG.clone().into(),
|
||||
// DL4
|
||||
SCHEMA_CLASS_OAUTH2_RS_PUBLIC_DL4.clone().into(),
|
||||
// DL5
|
||||
SCHEMA_CLASS_ACCOUNT_DL5.clone().into(),
|
||||
SCHEMA_CLASS_OAUTH2_RS_BASIC_DL5.clone().into(),
|
||||
// DL6
|
||||
SCHEMA_CLASS_GROUP_DL6.clone().into(),
|
||||
SCHEMA_CLASS_KEY_PROVIDER_DL6.clone().into(),
|
||||
SCHEMA_CLASS_KEY_PROVIDER_INTERNAL_DL6.clone().into(),
|
||||
SCHEMA_CLASS_KEY_OBJECT_DL6.clone().into(),
|
||||
SCHEMA_CLASS_KEY_OBJECT_JWT_ES256_DL6.clone().into(),
|
||||
SCHEMA_CLASS_KEY_OBJECT_JWE_A128GCM_DL6.clone().into(),
|
||||
SCHEMA_CLASS_KEY_OBJECT_INTERNAL_DL6.clone().into(),
|
||||
// DL7
|
||||
SCHEMA_CLASS_SERVICE_ACCOUNT_DL7.clone().into(),
|
||||
SCHEMA_CLASS_SYNC_ACCOUNT_DL7.clone().into(),
|
||||
SCHEMA_CLASS_CLIENT_CERTIFICATE_DL7.clone().into(),
|
||||
// DL8
|
||||
SCHEMA_CLASS_ACCOUNT_POLICY_DL8.clone().into(),
|
||||
SCHEMA_CLASS_APPLICATION_DL8.clone().into(),
|
||||
SCHEMA_CLASS_PERSON_DL8.clone().into(),
|
||||
// DL9
|
||||
SCHEMA_CLASS_OAUTH2_RS_DL9.clone().into(),
|
||||
SCHEMA_CLASS_DOMAIN_INFO_DL9.clone().into(),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn phase_3_key_provider() -> Vec<EntryInitNew> {
|
||||
vec![E_KEY_PROVIDER_INTERNAL_DL6.clone()]
|
||||
}
|
||||
|
||||
pub fn phase_4_system_entries() -> Vec<EntryInitNew> {
|
||||
vec![
|
||||
E_SYSTEM_INFO_V1.clone(),
|
||||
E_DOMAIN_INFO_DL6.clone(),
|
||||
E_SYSTEM_CONFIG_V1.clone(),
|
||||
]
|
||||
}
|
||||
|
||||
pub fn phase_5_builtin_admin_entries() -> Result<Vec<EntryInitNew>, OperationError> {
|
||||
Ok(vec![
|
||||
BUILTIN_ACCOUNT_ADMIN.clone().into(),
|
||||
BUILTIN_ACCOUNT_IDM_ADMIN.clone().into(),
|
||||
BUILTIN_GROUP_SYSTEM_ADMINS_V1.clone().try_into()?,
|
||||
BUILTIN_GROUP_IDM_ADMINS_V1.clone().try_into()?,
|
||||
// We need to push anonymous *after* groups due to entry-managed-by
|
||||
BUILTIN_ACCOUNT_ANONYMOUS_DL6.clone().into(),
|
||||
])
|
||||
}
|
||||
|
||||
pub fn phase_6_builtin_non_admin_entries() -> Result<Vec<EntryInitNew>, OperationError> {
|
||||
Ok(vec![
|
||||
BUILTIN_GROUP_DOMAIN_ADMINS.clone().try_into()?,
|
||||
BUILTIN_GROUP_SCHEMA_ADMINS.clone().try_into()?,
|
||||
BUILTIN_GROUP_ACCESS_CONTROL_ADMINS.clone().try_into()?,
|
||||
BUILTIN_GROUP_UNIX_ADMINS.clone().try_into()?,
|
||||
BUILTIN_GROUP_RECYCLE_BIN_ADMINS.clone().try_into()?,
|
||||
BUILTIN_GROUP_SERVICE_DESK.clone().try_into()?,
|
||||
BUILTIN_GROUP_OAUTH2_ADMINS.clone().try_into()?,
|
||||
BUILTIN_GROUP_RADIUS_SERVICE_ADMINS.clone().try_into()?,
|
||||
BUILTIN_GROUP_ACCOUNT_POLICY_ADMINS.clone().try_into()?,
|
||||
BUILTIN_GROUP_PEOPLE_ADMINS.clone().try_into()?,
|
||||
BUILTIN_GROUP_PEOPLE_PII_READ.clone().try_into()?,
|
||||
BUILTIN_GROUP_PEOPLE_ON_BOARDING.clone().try_into()?,
|
||||
BUILTIN_GROUP_SERVICE_ACCOUNT_ADMINS.clone().try_into()?,
|
||||
BUILTIN_GROUP_MAIL_SERVICE_ADMINS_DL8.clone().try_into()?,
|
||||
IDM_GROUP_ADMINS_V1.clone().try_into()?,
|
||||
IDM_ALL_PERSONS.clone().try_into()?,
|
||||
IDM_ALL_ACCOUNTS.clone().try_into()?,
|
||||
BUILTIN_IDM_RADIUS_SERVERS_V1.clone().try_into()?,
|
||||
BUILTIN_IDM_MAIL_SERVERS_DL8.clone().try_into()?,
|
||||
BUILTIN_GROUP_PEOPLE_SELF_NAME_WRITE_DL7
|
||||
.clone()
|
||||
.try_into()?,
|
||||
IDM_PEOPLE_SELF_MAIL_WRITE_DL7.clone().try_into()?,
|
||||
BUILTIN_GROUP_CLIENT_CERTIFICATE_ADMINS_DL7
|
||||
.clone()
|
||||
.try_into()?,
|
||||
BUILTIN_GROUP_APPLICATION_ADMINS_DL8.clone().try_into()?,
|
||||
// Write deps on read.clone().try_into()?, so write must be added first.
|
||||
// All members must exist before we write HP
|
||||
IDM_HIGH_PRIVILEGE_DL8.clone().try_into()?,
|
||||
// other things
|
||||
IDM_UI_ENABLE_EXPERIMENTAL_FEATURES.clone().try_into()?,
|
||||
IDM_ACCOUNT_MAIL_READ.clone().try_into()?,
|
||||
])
|
||||
}
|
||||
|
||||
pub fn phase_7_builtin_access_control_profiles() -> Vec<EntryInitNew> {
|
||||
vec![
|
||||
// Built in access controls.
|
||||
IDM_ACP_RECYCLE_BIN_SEARCH_V1.clone().into(),
|
||||
IDM_ACP_RECYCLE_BIN_REVIVE_V1.clone().into(),
|
||||
IDM_ACP_SCHEMA_WRITE_ATTRS_V1.clone().into(),
|
||||
IDM_ACP_SCHEMA_WRITE_CLASSES_V1.clone().into(),
|
||||
IDM_ACP_ACP_MANAGE_V1.clone().into(),
|
||||
IDM_ACP_GROUP_ENTRY_MANAGED_BY_MODIFY_V1.clone().into(),
|
||||
IDM_ACP_GROUP_ENTRY_MANAGER_V1.clone().into(),
|
||||
IDM_ACP_SYNC_ACCOUNT_MANAGE_V1.clone().into(),
|
||||
IDM_ACP_RADIUS_SERVERS_V1.clone().into(),
|
||||
IDM_ACP_RADIUS_SECRET_MANAGE_V1.clone().into(),
|
||||
IDM_ACP_PEOPLE_SELF_WRITE_MAIL_V1.clone().into(),
|
||||
IDM_ACP_ACCOUNT_SELF_WRITE_V1.clone().into(),
|
||||
IDM_ACP_ALL_ACCOUNTS_POSIX_READ_V1.clone().into(),
|
||||
IDM_ACP_SYSTEM_CONFIG_ACCOUNT_POLICY_MANAGE_V1
|
||||
.clone()
|
||||
.into(),
|
||||
IDM_ACP_GROUP_UNIX_MANAGE_V1.clone().into(),
|
||||
IDM_ACP_HP_GROUP_UNIX_MANAGE_V1.clone().into(),
|
||||
IDM_ACP_GROUP_READ_V1.clone().into(),
|
||||
IDM_ACP_ACCOUNT_UNIX_EXTEND_V1.clone().into(),
|
||||
IDM_ACP_PEOPLE_PII_READ_V1.clone().into(),
|
||||
IDM_ACP_PEOPLE_PII_MANAGE_V1.clone().into(),
|
||||
IDM_ACP_PEOPLE_READ_V1.clone().into(),
|
||||
IDM_ACP_PEOPLE_MANAGE_V1.clone().into(),
|
||||
IDM_ACP_PEOPLE_DELETE_V1.clone().into(),
|
||||
IDM_ACP_PEOPLE_CREDENTIAL_RESET_V1.clone().into(),
|
||||
IDM_ACP_HP_PEOPLE_CREDENTIAL_RESET_V1.clone().into(),
|
||||
IDM_ACP_SERVICE_ACCOUNT_CREATE_V1.clone().into(),
|
||||
IDM_ACP_SERVICE_ACCOUNT_DELETE_V1.clone().into(),
|
||||
IDM_ACP_SERVICE_ACCOUNT_ENTRY_MANAGER_V1.clone().into(),
|
||||
IDM_ACP_SERVICE_ACCOUNT_ENTRY_MANAGED_BY_MODIFY_V1
|
||||
.clone()
|
||||
.into(),
|
||||
IDM_ACP_HP_SERVICE_ACCOUNT_ENTRY_MANAGED_BY_MODIFY_V1
|
||||
.clone()
|
||||
.into(),
|
||||
IDM_ACP_SERVICE_ACCOUNT_MANAGE_V1.clone().into(),
|
||||
// DL4
|
||||
// DL5
|
||||
// DL6
|
||||
IDM_ACP_PEOPLE_CREATE_DL6.clone().into(),
|
||||
IDM_ACP_ACCOUNT_MAIL_READ_DL6.clone().into(),
|
||||
// DL7
|
||||
IDM_ACP_SELF_NAME_WRITE_DL7.clone().into(),
|
||||
IDM_ACP_HP_CLIENT_CERTIFICATE_MANAGER_DL7.clone().into(),
|
||||
// DL8
|
||||
IDM_ACP_SELF_READ_DL8.clone().into(),
|
||||
IDM_ACP_SELF_WRITE_DL8.clone().into(),
|
||||
IDM_ACP_APPLICATION_MANAGE_DL8.clone().into(),
|
||||
IDM_ACP_APPLICATION_ENTRY_MANAGER_DL8.clone().into(),
|
||||
IDM_ACP_MAIL_SERVERS_DL8.clone().into(),
|
||||
IDM_ACP_GROUP_ACCOUNT_POLICY_MANAGE_DL8.clone().into(),
|
||||
// DL9
|
||||
IDM_ACP_OAUTH2_MANAGE_DL9.clone().into(),
|
||||
IDM_ACP_GROUP_MANAGE_DL9.clone().into(),
|
||||
IDM_ACP_DOMAIN_ADMIN_DL9.clone().into(),
|
||||
]
|
||||
}
|
1506
server/lib/src/migration_data/dl9/schema.rs
Normal file
1506
server/lib/src/migration_data/dl9/schema.rs
Normal file
File diff suppressed because it is too large
Load diff
1073
server/lib/src/migration_data/dl9/system_config.rs
Normal file
1073
server/lib/src/migration_data/dl9/system_config.rs
Normal file
File diff suppressed because it is too large
Load diff
24
server/lib/src/migration_data/mod.rs
Normal file
24
server/lib/src/migration_data/mod.rs
Normal file
|
@ -0,0 +1,24 @@
|
|||
pub(crate) mod dl10;
|
||||
pub(crate) mod dl8;
|
||||
pub(crate) mod dl9;
|
||||
|
||||
mod types;
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) use dl10::accounts::BUILTIN_ACCOUNT_ANONYMOUS_DL6 as BUILTIN_ACCOUNT_ANONYMOUS;
|
||||
|
||||
#[cfg(test)]
|
||||
use self::types::BuiltinAccount;
|
||||
|
||||
#[cfg(test)]
|
||||
lazy_static! {
|
||||
/// Builtin System Admin account.
|
||||
pub static ref BUILTIN_ACCOUNT_TEST_PERSON: BuiltinAccount = BuiltinAccount {
|
||||
account_type: kanidm_proto::v1::AccountType::Person,
|
||||
entry_managed_by: None,
|
||||
name: "test_person",
|
||||
uuid: crate::constants::uuids::UUID_TESTPERSON_1,
|
||||
description: "Test Person",
|
||||
displayname: "Test Person",
|
||||
};
|
||||
}
|
83
server/lib/src/migration_data/types.rs
Normal file
83
server/lib/src/migration_data/types.rs
Normal file
|
@ -0,0 +1,83 @@
|
|||
//! Constant Entries for the IDM
|
||||
use crate::constants::uuids::*;
|
||||
use crate::entry::EntryInitNew;
|
||||
use crate::prelude::EntryClass;
|
||||
use crate::value::Value;
|
||||
pub use kanidm_proto::attribute::Attribute;
|
||||
use kanidm_proto::v1::AccountType;
|
||||
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
/// Built in accounts such as anonymous, idm_admin and admin
|
||||
pub struct BuiltinAccount {
|
||||
pub account_type: kanidm_proto::v1::AccountType,
|
||||
pub entry_managed_by: Option<uuid::Uuid>,
|
||||
pub name: &'static str,
|
||||
pub uuid: Uuid,
|
||||
pub description: &'static str,
|
||||
pub displayname: &'static str,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl Default for BuiltinAccount {
|
||||
fn default() -> Self {
|
||||
BuiltinAccount {
|
||||
account_type: AccountType::ServiceAccount,
|
||||
entry_managed_by: None,
|
||||
name: "",
|
||||
uuid: Uuid::new_v4(),
|
||||
description: "<set description>",
|
||||
displayname: "<set displayname>",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl From<BuiltinAccount> for crate::idm::account::Account {
|
||||
fn from(value: BuiltinAccount) -> Self {
|
||||
Self {
|
||||
name: value.name.to_string(),
|
||||
uuid: value.uuid,
|
||||
displayname: value.displayname.to_string(),
|
||||
spn: format!("{}@example.com", value.name),
|
||||
mail_primary: None,
|
||||
mail: Vec::with_capacity(0),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BuiltinAccount> for EntryInitNew {
|
||||
fn from(value: BuiltinAccount) -> Self {
|
||||
let mut entry = EntryInitNew::new();
|
||||
entry.add_ava(Attribute::Name, Value::new_iname(value.name));
|
||||
#[allow(clippy::panic)]
|
||||
if value.uuid >= DYNAMIC_RANGE_MINIMUM_UUID {
|
||||
panic!("Builtin ACP has invalid UUID! {:?}", value);
|
||||
}
|
||||
entry.add_ava(Attribute::Uuid, Value::Uuid(value.uuid));
|
||||
entry.add_ava(Attribute::Description, Value::new_utf8s(value.description));
|
||||
entry.add_ava(Attribute::DisplayName, Value::new_utf8s(value.displayname));
|
||||
|
||||
if let Some(entry_manager) = value.entry_managed_by {
|
||||
entry.add_ava(Attribute::EntryManagedBy, Value::Refer(entry_manager));
|
||||
}
|
||||
|
||||
entry.set_ava(
|
||||
Attribute::Class,
|
||||
vec![
|
||||
EntryClass::Account.to_value(),
|
||||
EntryClass::MemberOf.to_value(),
|
||||
EntryClass::Object.to_value(),
|
||||
],
|
||||
);
|
||||
match value.account_type {
|
||||
AccountType::Person => entry.add_ava(Attribute::Class, EntryClass::Person.to_value()),
|
||||
AccountType::ServiceAccount => {
|
||||
entry.add_ava(Attribute::Class, EntryClass::ServiceAccount.to_value())
|
||||
}
|
||||
}
|
||||
entry
|
||||
}
|
||||
}
|
|
@ -571,35 +571,43 @@ impl Plugin for MemberOf {
|
|||
Err(e) => return vec![e],
|
||||
};
|
||||
|
||||
// First we have to build a direct membership map. This saves us
|
||||
// needing to run queries since we already have every entry on hand
|
||||
// from the all_cand search.
|
||||
let mut direct_membership_map: BTreeMap<Uuid, BTreeSet<Uuid>> = Default::default();
|
||||
|
||||
let pv_class: PartialValue = EntryClass::Group.into();
|
||||
|
||||
for entry in all_cand.iter() {
|
||||
if !entry.attribute_equality(Attribute::Class, &pv_class) {
|
||||
// Not a group, move on.
|
||||
continue;
|
||||
}
|
||||
|
||||
let group_uuid = entry.get_uuid();
|
||||
|
||||
let member_iter = entry
|
||||
.get_ava_refer(Attribute::Member)
|
||||
.into_iter()
|
||||
.flat_map(|set| set.iter())
|
||||
.chain(
|
||||
entry
|
||||
.get_ava_refer(Attribute::DynMember)
|
||||
.into_iter()
|
||||
.flat_map(|set| set.iter()),
|
||||
);
|
||||
|
||||
for member_uuid in member_iter {
|
||||
let member_groups = direct_membership_map.entry(*member_uuid).or_default();
|
||||
member_groups.insert(group_uuid);
|
||||
}
|
||||
}
|
||||
|
||||
// for each entry in the DB (live).
|
||||
for e in all_cand {
|
||||
let uuid = e.get_uuid();
|
||||
let filt_in = filter!(f_and!([
|
||||
f_eq(Attribute::Class, EntryClass::Group.into()),
|
||||
f_or!([
|
||||
f_eq(Attribute::Member, PartialValue::Refer(uuid)),
|
||||
f_eq(Attribute::DynMember, PartialValue::Refer(uuid))
|
||||
])
|
||||
]));
|
||||
|
||||
// what groups is this entry a direct member of?
|
||||
let direct_memberof = match qs
|
||||
.internal_search(filt_in)
|
||||
.map_err(|_| ConsistencyError::QueryServerSearchFailure)
|
||||
{
|
||||
Ok(d_mo) => d_mo,
|
||||
Err(e) => return vec![Err(e)],
|
||||
};
|
||||
|
||||
// for all direct -> add uuid to map
|
||||
let d_groups_set: BTreeSet<Uuid> =
|
||||
direct_memberof.iter().map(|e| e.get_uuid()).collect();
|
||||
|
||||
let d_groups_set = if d_groups_set.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(d_groups_set)
|
||||
};
|
||||
let d_groups_set: Option<&BTreeSet<Uuid>> = direct_membership_map.get(&uuid);
|
||||
|
||||
trace!(
|
||||
"DMO search groups {:?} -> {:?}",
|
||||
|
@ -607,12 +615,16 @@ impl Plugin for MemberOf {
|
|||
d_groups_set
|
||||
);
|
||||
|
||||
// Remember, we only need to check direct memberships, because when memberof
|
||||
// it applies it clones dmo -> mo, so validation of all dmo sets implies mo is
|
||||
// valid (and a subset) of dmo.
|
||||
|
||||
match (e.get_ava_set(Attribute::DirectMemberOf), d_groups_set) {
|
||||
(Some(edmos), Some(b)) => {
|
||||
// Can they both be reference sets?
|
||||
match edmos.as_refer_set() {
|
||||
Some(a) => {
|
||||
let diff: Vec<_> = a.symmetric_difference(&b).collect();
|
||||
let diff: Vec<_> = a.symmetric_difference(b).collect();
|
||||
if !diff.is_empty() {
|
||||
error!(
|
||||
"MemberOfInvalid: Entry {}, DMO has inconsistencies",
|
||||
|
@ -636,7 +648,7 @@ impl Plugin for MemberOf {
|
|||
}
|
||||
(entry_direct_member_of, expected_direct_groups) => {
|
||||
error!(
|
||||
"MemberOfInvalid directmemberof set and DMO search set differ in size: {}",
|
||||
"MemberOfInvalid directmemberof set and DMO search set differ in presence: {}",
|
||||
e.get_display_id()
|
||||
);
|
||||
// trace!(?e);
|
||||
|
@ -645,19 +657,6 @@ impl Plugin for MemberOf {
|
|||
r.push(Err(ConsistencyError::MemberOfInvalid(e.get_id())));
|
||||
}
|
||||
}
|
||||
|
||||
// Could check all dmos in mos?
|
||||
|
||||
/* To check nested! */
|
||||
// add all direct to a stack
|
||||
// for all in stack
|
||||
// check their direct memberships
|
||||
// if not in map
|
||||
// add to map
|
||||
// push to stack
|
||||
|
||||
// check mo == map set
|
||||
// if not, consistency error!
|
||||
}
|
||||
|
||||
r
|
||||
|
|
|
@ -1159,6 +1159,7 @@ mod tests {
|
|||
},
|
||||
Access, AccessClass, AccessControls, AccessControlsTransaction, AccessEffectivePermission,
|
||||
};
|
||||
use crate::migration_data::BUILTIN_ACCOUNT_ANONYMOUS;
|
||||
use crate::prelude::*;
|
||||
use crate::valueset::ValueSetIname;
|
||||
|
||||
|
@ -2511,6 +2512,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_access_enforce_scope_delete() {
|
||||
sketching::test_init();
|
||||
let ev1 = E_TESTPERSON_1.clone().into_sealed_committed();
|
||||
let r_set = vec![Arc::new(ev1)];
|
||||
|
||||
|
@ -3052,7 +3054,7 @@ mod tests {
|
|||
test_acp_search_reduce!(&se_a, vec![], r_set.clone(), ex_a_reduced);
|
||||
|
||||
// Check that anonymous is denied even though it's a member of the group.
|
||||
let anon: EntryInitNew = BUILTIN_ACCOUNT_ANONYMOUS_DL6.clone().into();
|
||||
let anon: EntryInitNew = BUILTIN_ACCOUNT_ANONYMOUS.clone().into();
|
||||
let mut anon = anon.into_invalid_new();
|
||||
anon.set_ava_set(&Attribute::MemberOf, ValueSetRefer::new(UUID_TEST_GROUP_1));
|
||||
|
||||
|
@ -3352,7 +3354,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_access_delete_protect_system_ranges() {
|
||||
let ev1: EntryInitNew = BUILTIN_ACCOUNT_ANONYMOUS_DL6.clone().into();
|
||||
let ev1: EntryInitNew = BUILTIN_ACCOUNT_ANONYMOUS.clone().into();
|
||||
let ev1 = ev1.into_sealed_committed();
|
||||
let r_set = vec![Arc::new(ev1)];
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use super::ServerPhase;
|
||||
use crate::migration_data;
|
||||
use crate::prelude::*;
|
||||
|
||||
use kanidm_proto::internal::{
|
||||
DomainUpgradeCheckItem as ProtoDomainUpgradeCheckItem,
|
||||
DomainUpgradeCheckReport as ProtoDomainUpgradeCheckReport,
|
||||
DomainUpgradeCheckStatus as ProtoDomainUpgradeCheckStatus,
|
||||
};
|
||||
|
||||
use super::ServerPhase;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
impl QueryServer {
|
||||
#[instrument(level = "info", name = "system_initialisation", skip_all)]
|
||||
|
@ -48,17 +48,24 @@ impl QueryServer {
|
|||
debug!(?db_domain_version, "Before setting internal domain info");
|
||||
|
||||
if db_domain_version == 0 {
|
||||
// No domain info was present, so neither was the rest of the IDM. We need to bootstrap
|
||||
// the base-schema here.
|
||||
write_txn.initialise_schema_idm()?;
|
||||
// This is here to catch when we increase domain levels but didn't create the migration
|
||||
// hooks. If this fails it probably means you need to add another migration hook
|
||||
// in the above.
|
||||
debug_assert!(domain_target_level <= DOMAIN_MAX_LEVEL);
|
||||
|
||||
write_txn.reload()?;
|
||||
|
||||
// Since we just loaded in a ton of schema, lets reindex it to make
|
||||
// sure that some base IDM operations are fast. Since this is still
|
||||
// very early in the bootstrap process, and very few entries exist,
|
||||
// reindexing is very fast here.
|
||||
write_txn.reindex(false)?;
|
||||
// No domain info was present, so neither was the rest of the IDM. Bring up the
|
||||
// full IDM here.
|
||||
match domain_target_level {
|
||||
DOMAIN_LEVEL_8 => write_txn.migrate_domain_7_to_8()?,
|
||||
DOMAIN_LEVEL_9 => write_txn.migrate_domain_8_to_9()?,
|
||||
DOMAIN_LEVEL_10 => write_txn.migrate_domain_9_to_10()?,
|
||||
DOMAIN_LEVEL_11 => write_txn.migrate_domain_10_to_11()?,
|
||||
_ => {
|
||||
error!("Invalid requested domain target level for server bootstrap");
|
||||
debug_assert!(false);
|
||||
return Err(OperationError::MG0009InvalidTargetLevelForBootstrap);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Domain info was present, so we need to reflect that in our server
|
||||
// domain structures. If we don't do this, the in memory domain level
|
||||
|
@ -71,23 +78,11 @@ impl QueryServer {
|
|||
write_txn.force_domain_reload();
|
||||
|
||||
write_txn.reload()?;
|
||||
}
|
||||
|
||||
// Indicate the schema is now ready, which allows dyngroups to work when they
|
||||
// are created in the next phase of migrations.
|
||||
write_txn.set_phase(ServerPhase::SchemaReady);
|
||||
// Indicate the schema is now ready, which allows dyngroups to work when they
|
||||
// are created in the next phase of migrations.
|
||||
write_txn.set_phase(ServerPhase::SchemaReady);
|
||||
|
||||
// No domain info was present, so neither was the rest of the IDM. We need to bootstrap
|
||||
// the base entries here.
|
||||
if db_domain_version == 0 {
|
||||
// Init idm will now set the system config version and minimum domain
|
||||
// level if none was present
|
||||
write_txn.initialise_domain_info()?;
|
||||
|
||||
// In this path because we create the dyn groups they are immediately added to the
|
||||
// dyngroup cache and begin to operate.
|
||||
write_txn.initialise_idm()?;
|
||||
} else {
|
||||
// #2756 - if we *aren't* creating the base IDM entries, then we
|
||||
// need to force dyn groups to reload since we're now at schema
|
||||
// ready. This is done indirectly by ... reloading the schema again.
|
||||
|
@ -97,13 +92,13 @@ impl QueryServer {
|
|||
// itself or a change to schema reloading. Since we aren't changing the
|
||||
// dyngroup here, we have to go via the schema reload path.
|
||||
write_txn.force_schema_reload();
|
||||
};
|
||||
|
||||
// Reload as init idm affects access controls.
|
||||
write_txn.reload()?;
|
||||
// Reload as init idm affects access controls.
|
||||
write_txn.reload()?;
|
||||
|
||||
// Domain info is now ready and reloaded, we can proceed.
|
||||
write_txn.set_phase(ServerPhase::DomainInfoReady);
|
||||
// Domain info is now ready and reloaded, we can proceed.
|
||||
write_txn.set_phase(ServerPhase::DomainInfoReady);
|
||||
}
|
||||
|
||||
// This is the start of domain info related migrations which we will need in future
|
||||
// to handle replication. Due to the access control rework, and the addition of "managed by"
|
||||
|
@ -126,20 +121,18 @@ impl QueryServer {
|
|||
// If the database domain info is a lower version than our target level, we reload.
|
||||
if domain_info_version < domain_target_level {
|
||||
write_txn
|
||||
.internal_modify_uuid(
|
||||
UUID_DOMAIN_INFO,
|
||||
&ModifyList::new_purge_and_set(
|
||||
Attribute::Version,
|
||||
Value::new_uint32(domain_target_level),
|
||||
),
|
||||
)
|
||||
.internal_apply_domain_migration(domain_target_level)
|
||||
.map(|()| {
|
||||
warn!("Domain level has been raised to {}", domain_target_level);
|
||||
})?;
|
||||
|
||||
// Reload if anything in migrations requires it - this triggers the domain migrations
|
||||
// which in turn can trigger schema reloads etc.
|
||||
reload_required = true;
|
||||
// which in turn can trigger schema reloads etc. If the server was just brought up
|
||||
// then we don't need the extra reload since we are already at the correct
|
||||
// version of the server, and this call to set the target level is just for persistance
|
||||
// of the value.
|
||||
if domain_info_version != 0 {
|
||||
reload_required = true;
|
||||
}
|
||||
} else if domain_development_taint {
|
||||
// This forces pre-release versions to re-migrate each start up. This solves
|
||||
// the domain-version-sprawl issue so that during a development cycle we can
|
||||
|
@ -182,9 +175,6 @@ impl QueryServer {
|
|||
// we would have skipped the patch level fix which needs to have occurred *first*.
|
||||
if reload_required {
|
||||
write_txn.reload()?;
|
||||
// We are not yet at the schema phase where reindexes will auto-trigger
|
||||
// so if one was required, do it now.
|
||||
write_txn.reindex(false)?;
|
||||
}
|
||||
|
||||
// Now set the db/domain devel taint flag to match our current release status
|
||||
|
@ -206,7 +196,8 @@ impl QueryServer {
|
|||
// We are ready to run
|
||||
write_txn.set_phase(ServerPhase::Running);
|
||||
|
||||
// Commit all changes, this also triggers the reload.
|
||||
// Commit all changes, this also triggers the final reload, this should be a no-op
|
||||
// since we already did all the needed loads above.
|
||||
write_txn.commit()?;
|
||||
|
||||
debug!("Database version check and migrations success! ☀️ ");
|
||||
|
@ -217,7 +208,6 @@ impl QueryServer {
|
|||
impl QueryServerWriteTransaction<'_> {
|
||||
/// Apply a domain migration `to_level`. Panics if `to_level` is not greater than the active
|
||||
/// level.
|
||||
#[cfg(test)]
|
||||
pub(crate) fn internal_apply_domain_migration(
|
||||
&mut self,
|
||||
to_level: u32,
|
||||
|
@ -230,28 +220,160 @@ impl QueryServerWriteTransaction<'_> {
|
|||
.and_then(|()| self.reload())
|
||||
}
|
||||
|
||||
/// Given a set of entries, create entries that do not exist, and migrate entries
|
||||
/// that do exist. This operation always applies the create step first, and
|
||||
/// migrations second.
|
||||
///
|
||||
/// This means if you have *ordering* requirements for the entries in the migration
|
||||
/// then you *MUST* express that ordering requirement in multiple subsequent calls
|
||||
/// to this function.
|
||||
#[instrument(level = "debug", skip_all)]
|
||||
fn internal_migrate_or_create_batch(
|
||||
&mut self,
|
||||
_msg: &str,
|
||||
entries: Vec<EntryInitNew>,
|
||||
) -> Result<(), OperationError> {
|
||||
// Pull out the uuids of all the entries.
|
||||
let mut entries_w_uuid: Vec<(Uuid, EntryInitNew)> = Vec::with_capacity(entries.len());
|
||||
|
||||
for entry in entries.into_iter() {
|
||||
let entry_uuid = entry
|
||||
.get_uuid()
|
||||
.ok_or(OperationError::MG0010MigrationDataMissingUuid)?;
|
||||
|
||||
entries_w_uuid.push((entry_uuid, entry));
|
||||
}
|
||||
|
||||
// Now we can search for the entries.
|
||||
let inner: Vec<_> = entries_w_uuid
|
||||
.iter()
|
||||
.map(|(u, _)| f_eq(Attribute::Uuid, PartialValue::Uuid(*u)))
|
||||
.collect();
|
||||
|
||||
let filter = filter!(f_or(inner));
|
||||
|
||||
let mut entries = self.internal_search(filter)?;
|
||||
|
||||
// Now we have to partition the entries_w_uuid into entries that
|
||||
// need creation, and ones that need to be migrated.
|
||||
//
|
||||
// To do this, we sort both lists by uuid and then look through them.
|
||||
entries.sort_unstable_by_key(|e| e.get_uuid());
|
||||
entries_w_uuid.sort_unstable_by_key(|(u, _)| *u);
|
||||
|
||||
let mut entries_to_create = Vec::with_capacity(entries_w_uuid.len() - entries.len());
|
||||
let mut entries_to_migrate = Vec::with_capacity(entries.len());
|
||||
|
||||
let mut entry_iter = entries.into_iter();
|
||||
let mut next_entry = entry_iter.next();
|
||||
// This looks at both lists like:
|
||||
//
|
||||
// migrate: [ A, B, C, D, E ]
|
||||
// db: [ B, C, E ]
|
||||
//
|
||||
// As we iter we start in the migrate list:
|
||||
//
|
||||
// v
|
||||
// migrate: [ A, B, C, D, E ]
|
||||
// db: [ B, C, E ]
|
||||
// ^
|
||||
//
|
||||
// Since uuid A != B, and A < B, we push A to the create set.
|
||||
//
|
||||
// v
|
||||
// migrate: [ A, B, C, D, E ]
|
||||
// db: [ B, C, E ]
|
||||
// ^
|
||||
//
|
||||
// Now B == B, so we push this to the migrate set and advance both.
|
||||
//
|
||||
// This normally will continue, but we have to account for *one* case
|
||||
// that should be impossible, but paranoia is good.
|
||||
//
|
||||
// That case is a uuid in db that isn't in migrate.
|
||||
//
|
||||
// v
|
||||
// migrate: [ A, B, D, E ]
|
||||
// db: [ B, C, E ]
|
||||
// ^
|
||||
// In this case, we have to advance db iter, but not the entries_w_uuid iter
|
||||
// so that D is still considered correctly.
|
||||
'outer: for (u, entry) in entries_w_uuid.into_iter() {
|
||||
// There is something here to compare against - is it our entry?
|
||||
'inner: loop {
|
||||
if let Some(ref db_entry) = next_entry {
|
||||
match u.cmp(&db_entry.get_uuid()) {
|
||||
Ordering::Equal => {
|
||||
// This entry needs migration.
|
||||
entries_to_migrate.push((u, entry));
|
||||
// Advanced the db_entry iter.
|
||||
next_entry = entry_iter.next();
|
||||
continue 'outer;
|
||||
}
|
||||
Ordering::Less => {
|
||||
// Since our uuid is less than db_entry, iterate only the
|
||||
// entries_w_uuid set
|
||||
entries_to_create.push(entry);
|
||||
continue 'outer;
|
||||
}
|
||||
Ordering::Greater => {
|
||||
// IMPOSSIBLE CASE
|
||||
debug_assert!(false);
|
||||
next_entry = entry_iter.next();
|
||||
continue 'inner;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Nothing left to compare, must just need create
|
||||
entries_to_create.push(entry);
|
||||
continue 'outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Given the set of entries to create, perform that now.
|
||||
if !entries_to_create.is_empty() {
|
||||
self.internal_create(entries_to_create)?;
|
||||
}
|
||||
|
||||
// Apply batched modifications now.
|
||||
if !entries_to_migrate.is_empty() {
|
||||
let mut modifications: Vec<(Uuid, _)> = Vec::with_capacity(entries_to_migrate.len());
|
||||
|
||||
for (u, entry) in entries_to_migrate {
|
||||
/*
|
||||
// If we need to ignore attrs, do so here.
|
||||
for attr in attrs.iter() {
|
||||
e.remove_ava(attr);
|
||||
}
|
||||
*/
|
||||
|
||||
let modlist = entry
|
||||
.gen_modlist_assert(&self.schema)
|
||||
.map_err(OperationError::SchemaViolation)?;
|
||||
|
||||
modifications.push((u, modlist));
|
||||
}
|
||||
|
||||
self.internal_batch_modify(modifications.into_iter())?;
|
||||
}
|
||||
|
||||
// Complete!!!
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// - If the thing exists:
|
||||
/// - Ensure the set of attributes match and are present
|
||||
/// (but don't delete multivalue, or extended attributes in the situation.
|
||||
/// - If not:
|
||||
/// - Create the entry
|
||||
///
|
||||
/// This will extra classes an attributes alone!
|
||||
/// This will ignore the specified list of attributes, so that if an admin has
|
||||
/// modified those values then we don't stomp them.
|
||||
///
|
||||
/// NOTE: `gen_modlist*` IS schema aware and will handle multivalue correctly!
|
||||
pub fn internal_migrate_or_create(
|
||||
&mut self,
|
||||
e: Entry<EntryInit, EntryNew>,
|
||||
) -> Result<(), OperationError> {
|
||||
self.internal_migrate_or_create_ignore_attrs(e, &[])
|
||||
}
|
||||
|
||||
/// This is the same as [QueryServerWriteTransaction::internal_migrate_or_create] but it will ignore the specified
|
||||
/// list of attributes, so that if an admin has modified those values then we don't
|
||||
/// stomp them.
|
||||
#[instrument(level = "trace", skip_all)]
|
||||
pub fn internal_migrate_or_create_ignore_attrs(
|
||||
fn internal_migrate_or_create_ignore_attrs(
|
||||
&mut self,
|
||||
mut e: Entry<EntryInit, EntryNew>,
|
||||
attrs: &[Attribute],
|
||||
|
@ -294,6 +416,75 @@ impl QueryServerWriteTransaction<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Migration domain level 7 to 8 (1.4.0)
|
||||
#[instrument(level = "info", skip_all)]
|
||||
pub(crate) fn migrate_domain_7_to_8(&mut self) -> Result<(), OperationError> {
|
||||
if !cfg!(test) && DOMAIN_TGT_LEVEL < DOMAIN_LEVEL_9 {
|
||||
error!("Unable to raise domain level from 8 to 9.");
|
||||
return Err(OperationError::MG0004DomainLevelInDevelopment);
|
||||
}
|
||||
|
||||
// =========== Apply changes ==============
|
||||
self.internal_migrate_or_create_batch(
|
||||
"phase 1 - schema attrs",
|
||||
migration_data::dl8::phase_1_schema_attrs(),
|
||||
)?;
|
||||
|
||||
self.internal_migrate_or_create_batch(
|
||||
"phase 2 - schema classes",
|
||||
migration_data::dl8::phase_2_schema_classes(),
|
||||
)?;
|
||||
|
||||
// Reload for the new schema.
|
||||
self.reload()?;
|
||||
|
||||
// Reindex?
|
||||
self.reindex(false)?;
|
||||
|
||||
// Set Phase
|
||||
self.set_phase(ServerPhase::SchemaReady);
|
||||
|
||||
self.internal_migrate_or_create_batch(
|
||||
"phase 3 - key provider",
|
||||
migration_data::dl8::phase_3_key_provider(),
|
||||
)?;
|
||||
|
||||
// Reload for the new key providers
|
||||
self.reload()?;
|
||||
|
||||
self.internal_migrate_or_create_batch(
|
||||
"phase 4 - system entries",
|
||||
migration_data::dl8::phase_4_system_entries(),
|
||||
)?;
|
||||
|
||||
// Reload for the new system entries
|
||||
self.reload()?;
|
||||
|
||||
// Domain info is now ready and reloaded, we can proceed.
|
||||
self.set_phase(ServerPhase::DomainInfoReady);
|
||||
|
||||
// Bring up the IDM entries.
|
||||
self.internal_migrate_or_create_batch(
|
||||
"phase 5 - builtin admin entries",
|
||||
migration_data::dl8::phase_5_builtin_admin_entries()?,
|
||||
)?;
|
||||
|
||||
self.internal_migrate_or_create_batch(
|
||||
"phase 6 - builtin not admin entries",
|
||||
migration_data::dl8::phase_6_builtin_non_admin_entries()?,
|
||||
)?;
|
||||
|
||||
self.internal_migrate_or_create_batch(
|
||||
"phase 7 - builtin access control profiles",
|
||||
migration_data::dl8::phase_7_builtin_access_control_profiles(),
|
||||
)?;
|
||||
|
||||
// Reload for all new access controls.
|
||||
self.reload()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Migration domain level 8 to 9 (1.5.0)
|
||||
#[instrument(level = "info", skip_all)]
|
||||
pub(crate) fn migrate_domain_8_to_9(&mut self) -> Result<(), OperationError> {
|
||||
|
@ -303,39 +494,61 @@ impl QueryServerWriteTransaction<'_> {
|
|||
}
|
||||
|
||||
// =========== Apply changes ==============
|
||||
self.internal_migrate_or_create_batch(
|
||||
"phase 1 - schema attrs",
|
||||
migration_data::dl9::phase_1_schema_attrs(),
|
||||
)?;
|
||||
|
||||
// Now update schema
|
||||
let idm_schema_changes = [
|
||||
SCHEMA_ATTR_OAUTH2_DEVICE_FLOW_ENABLE_DL9.clone().into(),
|
||||
SCHEMA_ATTR_DOMAIN_ALLOW_EASTER_EGGS_DL9.clone().into(),
|
||||
SCHEMA_CLASS_OAUTH2_RS_DL9.clone().into(),
|
||||
SCHEMA_CLASS_DOMAIN_INFO_DL9.clone().into(),
|
||||
];
|
||||
|
||||
idm_schema_changes
|
||||
.into_iter()
|
||||
.try_for_each(|entry| self.internal_migrate_or_create(entry))
|
||||
.map_err(|err| {
|
||||
error!(?err, "migrate_domain_8_to_9 -> Error");
|
||||
err
|
||||
})?;
|
||||
self.internal_migrate_or_create_batch(
|
||||
"phase 2 - schema classes",
|
||||
migration_data::dl9::phase_2_schema_classes(),
|
||||
)?;
|
||||
|
||||
// Reload for the new schema.
|
||||
self.reload()?;
|
||||
|
||||
let idm_data = [
|
||||
IDM_ACP_OAUTH2_MANAGE_DL9.clone().into(),
|
||||
IDM_ACP_GROUP_MANAGE_DL9.clone().into(),
|
||||
IDM_ACP_DOMAIN_ADMIN_DL9.clone().into(),
|
||||
];
|
||||
// Reindex?
|
||||
self.reindex(false)?;
|
||||
|
||||
idm_data
|
||||
.into_iter()
|
||||
.try_for_each(|entry| self.internal_migrate_or_create(entry))
|
||||
.map_err(|err| {
|
||||
error!(?err, "migrate_domain_8_to_9 -> Error");
|
||||
err
|
||||
})?;
|
||||
// Set Phase
|
||||
self.set_phase(ServerPhase::SchemaReady);
|
||||
|
||||
self.internal_migrate_or_create_batch(
|
||||
"phase 3 - key provider",
|
||||
migration_data::dl9::phase_3_key_provider(),
|
||||
)?;
|
||||
|
||||
// Reload for the new key providers
|
||||
self.reload()?;
|
||||
|
||||
self.internal_migrate_or_create_batch(
|
||||
"phase 4 - system entries",
|
||||
migration_data::dl9::phase_4_system_entries(),
|
||||
)?;
|
||||
|
||||
// Reload for the new system entries
|
||||
self.reload()?;
|
||||
|
||||
// Domain info is now ready and reloaded, we can proceed.
|
||||
self.set_phase(ServerPhase::DomainInfoReady);
|
||||
|
||||
// Bring up the IDM entries.
|
||||
self.internal_migrate_or_create_batch(
|
||||
"phase 5 - builtin admin entries",
|
||||
migration_data::dl9::phase_5_builtin_admin_entries()?,
|
||||
)?;
|
||||
|
||||
self.internal_migrate_or_create_batch(
|
||||
"phase 6 - builtin not admin entries",
|
||||
migration_data::dl9::phase_6_builtin_non_admin_entries()?,
|
||||
)?;
|
||||
|
||||
self.internal_migrate_or_create_batch(
|
||||
"phase 7 - builtin access control profiles",
|
||||
migration_data::dl9::phase_7_builtin_access_control_profiles(),
|
||||
)?;
|
||||
|
||||
// Reload for all new access controls.
|
||||
self.reload()?;
|
||||
|
||||
Ok(())
|
||||
|
@ -350,66 +563,10 @@ impl QueryServerWriteTransaction<'_> {
|
|||
|
||||
debug_assert!(*self.phase >= ServerPhase::SchemaReady);
|
||||
|
||||
let idm_data = [
|
||||
IDM_ACP_ACCOUNT_MAIL_READ_DL6.clone().into(),
|
||||
IDM_ACP_ACCOUNT_SELF_WRITE_V1.clone().into(),
|
||||
IDM_ACP_ACCOUNT_UNIX_EXTEND_V1.clone().into(),
|
||||
IDM_ACP_ACP_MANAGE_V1.clone().into(),
|
||||
IDM_ACP_ALL_ACCOUNTS_POSIX_READ_V1.clone().into(),
|
||||
IDM_ACP_APPLICATION_ENTRY_MANAGER_DL8.clone().into(),
|
||||
IDM_ACP_APPLICATION_MANAGE_DL8.clone().into(),
|
||||
IDM_ACP_DOMAIN_ADMIN_DL8.clone().into(),
|
||||
IDM_ACP_GROUP_ACCOUNT_POLICY_MANAGE_DL8.clone().into(),
|
||||
IDM_ACP_GROUP_ENTRY_MANAGED_BY_MODIFY_V1.clone().into(),
|
||||
IDM_ACP_GROUP_ENTRY_MANAGER_V1.clone().into(),
|
||||
IDM_ACP_GROUP_MANAGE_DL6.clone().into(),
|
||||
IDM_ACP_GROUP_READ_V1.clone().into(),
|
||||
IDM_ACP_GROUP_UNIX_MANAGE_V1.clone().into(),
|
||||
IDM_ACP_HP_CLIENT_CERTIFICATE_MANAGER_DL7.clone().into(),
|
||||
IDM_ACP_HP_GROUP_UNIX_MANAGE_V1.clone().into(),
|
||||
IDM_ACP_HP_PEOPLE_CREDENTIAL_RESET_V1.clone().into(),
|
||||
IDM_ACP_HP_SERVICE_ACCOUNT_ENTRY_MANAGED_BY_MODIFY_V1
|
||||
.clone()
|
||||
.into(),
|
||||
IDM_ACP_MAIL_SERVERS_DL8.clone().into(),
|
||||
IDM_ACP_OAUTH2_MANAGE_DL7.clone().into(),
|
||||
IDM_ACP_PEOPLE_CREATE_DL6.clone().into(),
|
||||
IDM_ACP_PEOPLE_CREDENTIAL_RESET_V1.clone().into(),
|
||||
IDM_ACP_PEOPLE_DELETE_V1.clone().into(),
|
||||
IDM_ACP_PEOPLE_MANAGE_V1.clone().into(),
|
||||
IDM_ACP_PEOPLE_PII_MANAGE_V1.clone().into(),
|
||||
IDM_ACP_PEOPLE_PII_READ_V1.clone().into(),
|
||||
IDM_ACP_PEOPLE_READ_V1.clone().into(),
|
||||
IDM_ACP_PEOPLE_SELF_WRITE_MAIL_V1.clone().into(),
|
||||
IDM_ACP_RADIUS_SECRET_MANAGE_V1.clone().into(),
|
||||
IDM_ACP_RADIUS_SERVERS_V1.clone().into(),
|
||||
IDM_ACP_RECYCLE_BIN_REVIVE_V1.clone().into(),
|
||||
IDM_ACP_RECYCLE_BIN_SEARCH_V1.clone().into(),
|
||||
IDM_ACP_SCHEMA_WRITE_ATTRS_V1.clone().into(),
|
||||
IDM_ACP_SCHEMA_WRITE_CLASSES_V1.clone().into(),
|
||||
IDM_ACP_SELF_NAME_WRITE_DL7.clone().into(),
|
||||
IDM_ACP_SELF_READ_DL8.clone().into(),
|
||||
IDM_ACP_SELF_WRITE_DL8.clone().into(),
|
||||
IDM_ACP_SERVICE_ACCOUNT_CREATE_V1.clone().into(),
|
||||
IDM_ACP_SERVICE_ACCOUNT_DELETE_V1.clone().into(),
|
||||
IDM_ACP_SERVICE_ACCOUNT_ENTRY_MANAGED_BY_MODIFY_V1
|
||||
.clone()
|
||||
.into(),
|
||||
IDM_ACP_SERVICE_ACCOUNT_ENTRY_MANAGER_V1.clone().into(),
|
||||
IDM_ACP_SERVICE_ACCOUNT_MANAGE_V1.clone().into(),
|
||||
IDM_ACP_SYNC_ACCOUNT_MANAGE_V1.clone().into(),
|
||||
IDM_ACP_SYSTEM_CONFIG_ACCOUNT_POLICY_MANAGE_V1
|
||||
.clone()
|
||||
.into(),
|
||||
];
|
||||
|
||||
idm_data
|
||||
.into_iter()
|
||||
.try_for_each(|entry| self.internal_migrate_or_create(entry))
|
||||
.map_err(|err| {
|
||||
error!(?err, "migrate_domain_patch_level_2 -> Error");
|
||||
err
|
||||
})?;
|
||||
self.internal_migrate_or_create_batch(
|
||||
"patch level 2 - access control profiles",
|
||||
migration_data::dl9::phase_7_builtin_access_control_profiles(),
|
||||
)?;
|
||||
|
||||
self.reload()?;
|
||||
|
||||
|
@ -425,21 +582,62 @@ impl QueryServerWriteTransaction<'_> {
|
|||
}
|
||||
|
||||
// =========== Apply changes ==============
|
||||
self.internal_migrate_or_create_batch(
|
||||
"phase 1 - schema attrs",
|
||||
migration_data::dl10::phase_1_schema_attrs(),
|
||||
)?;
|
||||
|
||||
// Now update schema
|
||||
let idm_schema_changes = [
|
||||
SCHEMA_ATTR_DENIED_NAME_DL10.clone().into(),
|
||||
SCHEMA_CLASS_DOMAIN_INFO_DL10.clone().into(),
|
||||
SCHEMA_ATTR_LDAP_MAXIMUM_QUERYABLE_ATTRIBUTES.clone().into(),
|
||||
];
|
||||
self.internal_migrate_or_create_batch(
|
||||
"phase 2 - schema classes",
|
||||
migration_data::dl10::phase_2_schema_classes(),
|
||||
)?;
|
||||
|
||||
idm_schema_changes
|
||||
.into_iter()
|
||||
.try_for_each(|entry| self.internal_migrate_or_create(entry))
|
||||
.map_err(|err| {
|
||||
error!(?err, "migrate_domain_9_to_10 -> Error");
|
||||
err
|
||||
})?;
|
||||
// Reload for the new schema.
|
||||
self.reload()?;
|
||||
|
||||
// Since we just loaded in a ton of schema, lets reindex it incase we added
|
||||
// new indexes, or this is a bootstrap and we have no indexes yet.
|
||||
self.reindex(false)?;
|
||||
|
||||
// Set Phase
|
||||
// Indicate the schema is now ready, which allows dyngroups to work when they
|
||||
// are created in the next phase of migrations.
|
||||
self.set_phase(ServerPhase::SchemaReady);
|
||||
|
||||
self.internal_migrate_or_create_batch(
|
||||
"phase 3 - key provider",
|
||||
migration_data::dl10::phase_3_key_provider(),
|
||||
)?;
|
||||
|
||||
// Reload for the new key providers
|
||||
self.reload()?;
|
||||
|
||||
self.internal_migrate_or_create_batch(
|
||||
"phase 4 - system entries",
|
||||
migration_data::dl10::phase_4_system_entries(),
|
||||
)?;
|
||||
|
||||
// Reload for the new system entries
|
||||
self.reload()?;
|
||||
|
||||
// Domain info is now ready and reloaded, we can proceed.
|
||||
self.set_phase(ServerPhase::DomainInfoReady);
|
||||
|
||||
// Bring up the IDM entries.
|
||||
self.internal_migrate_or_create_batch(
|
||||
"phase 5 - builtin admin entries",
|
||||
migration_data::dl10::phase_5_builtin_admin_entries()?,
|
||||
)?;
|
||||
|
||||
self.internal_migrate_or_create_batch(
|
||||
"phase 6 - builtin not admin entries",
|
||||
migration_data::dl10::phase_6_builtin_non_admin_entries()?,
|
||||
)?;
|
||||
|
||||
self.internal_migrate_or_create_batch(
|
||||
"phase 7 - builtin access control profiles",
|
||||
migration_data::dl10::phase_7_builtin_access_control_profiles(),
|
||||
)?;
|
||||
|
||||
self.reload()?;
|
||||
|
||||
|
@ -458,341 +656,21 @@ impl QueryServerWriteTransaction<'_> {
|
|||
}
|
||||
|
||||
#[instrument(level = "info", skip_all)]
|
||||
pub fn initialise_schema_core(&mut self) -> Result<(), OperationError> {
|
||||
admin_debug!("initialise_schema_core -> start ...");
|
||||
pub(crate) fn initialise_schema_core(&mut self) -> Result<(), OperationError> {
|
||||
// Load in all the "core" schema, that we already have in "memory".
|
||||
let entries = self.schema.to_entries();
|
||||
|
||||
// admin_debug!("Dumping schemas: {:?}", entries);
|
||||
let r = self
|
||||
.internal_migrate_or_create_batch("initialise schema core", self.schema.to_entries());
|
||||
|
||||
// internal_migrate_or_create.
|
||||
let r: Result<_, _> = entries.into_iter().try_for_each(|e| {
|
||||
trace!(?e, "init schema entry");
|
||||
self.internal_migrate_or_create(e)
|
||||
});
|
||||
if r.is_ok() {
|
||||
admin_debug!("initialise_schema_core -> Ok!");
|
||||
if let Err(err) = &r {
|
||||
error!(?err);
|
||||
debug_assert!(false);
|
||||
} else {
|
||||
admin_error!(?r, "initialise_schema_core -> Error");
|
||||
debug!("Ok!");
|
||||
}
|
||||
// why do we have error handling if it's always supposed to be `Ok`?
|
||||
debug_assert!(r.is_ok());
|
||||
r
|
||||
}
|
||||
|
||||
#[instrument(level = "info", skip_all)]
|
||||
pub fn initialise_schema_idm(&mut self) -> Result<(), OperationError> {
|
||||
admin_debug!("initialise_schema_idm -> start ...");
|
||||
|
||||
// ⚠️ DOMAIN LEVEL 1 SCHEMA ATTRIBUTES ⚠️
|
||||
// Future schema attributes need to be added via migrations.
|
||||
//
|
||||
// DO NOT MODIFY THIS DEFINITION
|
||||
let idm_schema_attrs = [
|
||||
SCHEMA_ATTR_SYNC_CREDENTIAL_PORTAL.clone().into(),
|
||||
SCHEMA_ATTR_SYNC_YIELD_AUTHORITY.clone().into(),
|
||||
];
|
||||
|
||||
let r: Result<(), _> = idm_schema_attrs
|
||||
.into_iter()
|
||||
.try_for_each(|entry| self.internal_migrate_or_create(entry));
|
||||
|
||||
if r.is_err() {
|
||||
error!(res = ?r, "initialise_schema_idm -> Error");
|
||||
}
|
||||
debug_assert!(r.is_ok());
|
||||
|
||||
// ⚠️ DOMAIN LEVEL 1 SCHEMA ATTRIBUTES ⚠️
|
||||
// Future schema classes need to be added via migrations.
|
||||
//
|
||||
// DO NOT MODIFY THIS DEFINITION
|
||||
let idm_schema: Vec<EntryInitNew> = vec![
|
||||
// SCHEMA_ATTR_MAIL.clone().into(),
|
||||
SCHEMA_ATTR_ACCOUNT_EXPIRE.clone().into(),
|
||||
SCHEMA_ATTR_ACCOUNT_VALID_FROM.clone().into(),
|
||||
SCHEMA_ATTR_API_TOKEN_SESSION.clone().into(),
|
||||
SCHEMA_ATTR_AUTH_SESSION_EXPIRY.clone().into(),
|
||||
SCHEMA_ATTR_AUTH_PRIVILEGE_EXPIRY.clone().into(),
|
||||
SCHEMA_ATTR_AUTH_PASSWORD_MINIMUM_LENGTH.clone().into(),
|
||||
SCHEMA_ATTR_BADLIST_PASSWORD.clone().into(),
|
||||
SCHEMA_ATTR_CREDENTIAL_UPDATE_INTENT_TOKEN.clone().into(),
|
||||
SCHEMA_ATTR_ATTESTED_PASSKEYS.clone().into(),
|
||||
// SCHEMA_ATTR_DISPLAYNAME.clone().into(),
|
||||
SCHEMA_ATTR_DOMAIN_DISPLAY_NAME.clone().into(),
|
||||
SCHEMA_ATTR_DOMAIN_LDAP_BASEDN.clone().into(),
|
||||
SCHEMA_ATTR_DOMAIN_NAME.clone().into(),
|
||||
SCHEMA_ATTR_LDAP_ALLOW_UNIX_PW_BIND.clone().into(),
|
||||
SCHEMA_ATTR_DOMAIN_SSID.clone().into(),
|
||||
SCHEMA_ATTR_DOMAIN_TOKEN_KEY.clone().into(),
|
||||
SCHEMA_ATTR_DOMAIN_UUID.clone().into(),
|
||||
SCHEMA_ATTR_DYNGROUP_FILTER.clone().into(),
|
||||
SCHEMA_ATTR_EC_KEY_PRIVATE.clone().into(),
|
||||
SCHEMA_ATTR_ES256_PRIVATE_KEY_DER.clone().into(),
|
||||
SCHEMA_ATTR_FERNET_PRIVATE_KEY_STR.clone().into(),
|
||||
SCHEMA_ATTR_GIDNUMBER.clone().into(),
|
||||
SCHEMA_ATTR_GRANT_UI_HINT.clone().into(),
|
||||
SCHEMA_ATTR_JWS_ES256_PRIVATE_KEY.clone().into(),
|
||||
// SCHEMA_ATTR_LEGALNAME.clone().into(),
|
||||
SCHEMA_ATTR_LOGINSHELL.clone().into(),
|
||||
SCHEMA_ATTR_NAME_HISTORY.clone().into(),
|
||||
SCHEMA_ATTR_NSUNIQUEID.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_ALLOW_INSECURE_CLIENT_DISABLE_PKCE
|
||||
.clone()
|
||||
.into(),
|
||||
SCHEMA_ATTR_OAUTH2_CONSENT_SCOPE_MAP.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_JWT_LEGACY_CRYPTO_ENABLE.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_PREFER_SHORT_USERNAME.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_RS_BASIC_SECRET.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_RS_IMPLICIT_SCOPES.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_RS_NAME.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_RS_ORIGIN_LANDING.clone().into(),
|
||||
// SCHEMA_ATTR_OAUTH2_RS_ORIGIN.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_RS_SCOPE_MAP.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_RS_SUP_SCOPE_MAP.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_RS_TOKEN_KEY.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_SESSION.clone().into(),
|
||||
SCHEMA_ATTR_PASSKEYS.clone().into(),
|
||||
SCHEMA_ATTR_PRIMARY_CREDENTIAL.clone().into(),
|
||||
SCHEMA_ATTR_PRIVATE_COOKIE_KEY.clone().into(),
|
||||
SCHEMA_ATTR_RADIUS_SECRET.clone().into(),
|
||||
SCHEMA_ATTR_RS256_PRIVATE_KEY_DER.clone().into(),
|
||||
SCHEMA_ATTR_SSH_PUBLICKEY.clone().into(),
|
||||
SCHEMA_ATTR_SYNC_COOKIE.clone().into(),
|
||||
SCHEMA_ATTR_SYNC_TOKEN_SESSION.clone().into(),
|
||||
SCHEMA_ATTR_UNIX_PASSWORD.clone().into(),
|
||||
SCHEMA_ATTR_USER_AUTH_TOKEN_SESSION.clone().into(),
|
||||
SCHEMA_ATTR_DENIED_NAME.clone().into(),
|
||||
SCHEMA_ATTR_CREDENTIAL_TYPE_MINIMUM.clone().into(),
|
||||
SCHEMA_ATTR_WEBAUTHN_ATTESTATION_CA_LIST.clone().into(),
|
||||
// DL4
|
||||
SCHEMA_ATTR_OAUTH2_RS_CLAIM_MAP_DL4.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_ALLOW_LOCALHOST_REDIRECT_DL4
|
||||
.clone()
|
||||
.into(),
|
||||
// DL5
|
||||
// DL6
|
||||
SCHEMA_ATTR_LIMIT_SEARCH_MAX_RESULTS_DL6.clone().into(),
|
||||
SCHEMA_ATTR_LIMIT_SEARCH_MAX_FILTER_TEST_DL6.clone().into(),
|
||||
SCHEMA_ATTR_KEY_INTERNAL_DATA_DL6.clone().into(),
|
||||
SCHEMA_ATTR_KEY_PROVIDER_DL6.clone().into(),
|
||||
SCHEMA_ATTR_KEY_ACTION_ROTATE_DL6.clone().into(),
|
||||
SCHEMA_ATTR_KEY_ACTION_REVOKE_DL6.clone().into(),
|
||||
SCHEMA_ATTR_KEY_ACTION_IMPORT_JWS_ES256_DL6.clone().into(),
|
||||
// DL7
|
||||
SCHEMA_ATTR_PATCH_LEVEL_DL7.clone().into(),
|
||||
SCHEMA_ATTR_DOMAIN_DEVELOPMENT_TAINT_DL7.clone().into(),
|
||||
SCHEMA_ATTR_REFERS_DL7.clone().into(),
|
||||
SCHEMA_ATTR_CERTIFICATE_DL7.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_RS_ORIGIN_DL7.clone().into(),
|
||||
SCHEMA_ATTR_OAUTH2_STRICT_REDIRECT_URI_DL7.clone().into(),
|
||||
SCHEMA_ATTR_MAIL_DL7.clone().into(),
|
||||
SCHEMA_ATTR_LEGALNAME_DL7.clone().into(),
|
||||
SCHEMA_ATTR_DISPLAYNAME_DL7.clone().into(),
|
||||
// DL8
|
||||
SCHEMA_ATTR_LINKED_GROUP_DL8.clone().into(),
|
||||
SCHEMA_ATTR_APPLICATION_PASSWORD_DL8.clone().into(),
|
||||
SCHEMA_ATTR_ALLOW_PRIMARY_CRED_FALLBACK_DL8.clone().into(),
|
||||
];
|
||||
|
||||
let r = idm_schema
|
||||
.into_iter()
|
||||
// Each item individually logs it's result
|
||||
.try_for_each(|entry| self.internal_migrate_or_create(entry));
|
||||
|
||||
if r.is_err() {
|
||||
error!(res = ?r, "initialise_schema_idm -> Error");
|
||||
}
|
||||
|
||||
debug_assert!(r.is_ok());
|
||||
|
||||
// ⚠️ DOMAIN LEVEL 1 SCHEMA CLASSES ⚠️
|
||||
// Future schema classes need to be added via migrations.
|
||||
//
|
||||
// DO NOT MODIFY THIS DEFINITION
|
||||
let idm_schema_classes_dl1: Vec<EntryInitNew> = vec![
|
||||
SCHEMA_CLASS_DYNGROUP.clone().into(),
|
||||
SCHEMA_CLASS_ORGPERSON.clone().into(),
|
||||
SCHEMA_CLASS_POSIXACCOUNT.clone().into(),
|
||||
SCHEMA_CLASS_POSIXGROUP.clone().into(),
|
||||
SCHEMA_CLASS_SYSTEM_CONFIG.clone().into(),
|
||||
// DL4
|
||||
SCHEMA_CLASS_OAUTH2_RS_PUBLIC_DL4.clone().into(),
|
||||
// DL5
|
||||
// SCHEMA_CLASS_PERSON_DL5.clone().into(),
|
||||
SCHEMA_CLASS_ACCOUNT_DL5.clone().into(),
|
||||
// SCHEMA_CLASS_OAUTH2_RS_DL5.clone().into(),
|
||||
SCHEMA_CLASS_OAUTH2_RS_BASIC_DL5.clone().into(),
|
||||
// DL6
|
||||
// SCHEMA_CLASS_ACCOUNT_POLICY_DL6.clone().into(),
|
||||
// SCHEMA_CLASS_SERVICE_ACCOUNT_DL6.clone().into(),
|
||||
// SCHEMA_CLASS_SYNC_ACCOUNT_DL6.clone().into(),
|
||||
SCHEMA_CLASS_GROUP_DL6.clone().into(),
|
||||
SCHEMA_CLASS_KEY_PROVIDER_DL6.clone().into(),
|
||||
SCHEMA_CLASS_KEY_PROVIDER_INTERNAL_DL6.clone().into(),
|
||||
SCHEMA_CLASS_KEY_OBJECT_DL6.clone().into(),
|
||||
SCHEMA_CLASS_KEY_OBJECT_JWT_ES256_DL6.clone().into(),
|
||||
SCHEMA_CLASS_KEY_OBJECT_JWE_A128GCM_DL6.clone().into(),
|
||||
SCHEMA_CLASS_KEY_OBJECT_INTERNAL_DL6.clone().into(),
|
||||
// SCHEMA_CLASS_DOMAIN_INFO_DL6.clone().into(),
|
||||
// DL7
|
||||
// SCHEMA_CLASS_DOMAIN_INFO_DL7.clone().into(),
|
||||
SCHEMA_CLASS_SERVICE_ACCOUNT_DL7.clone().into(),
|
||||
SCHEMA_CLASS_SYNC_ACCOUNT_DL7.clone().into(),
|
||||
SCHEMA_CLASS_CLIENT_CERTIFICATE_DL7.clone().into(),
|
||||
SCHEMA_CLASS_OAUTH2_RS_DL7.clone().into(),
|
||||
// DL8
|
||||
SCHEMA_CLASS_ACCOUNT_POLICY_DL8.clone().into(),
|
||||
SCHEMA_CLASS_APPLICATION_DL8.clone().into(),
|
||||
SCHEMA_CLASS_PERSON_DL8.clone().into(),
|
||||
SCHEMA_CLASS_DOMAIN_INFO_DL8.clone().into(),
|
||||
];
|
||||
|
||||
let r: Result<(), _> = idm_schema_classes_dl1
|
||||
.into_iter()
|
||||
.try_for_each(|entry| self.internal_migrate_or_create(entry));
|
||||
|
||||
if r.is_err() {
|
||||
error!(res = ?r, "initialise_schema_idm -> Error");
|
||||
}
|
||||
debug_assert!(r.is_ok());
|
||||
|
||||
debug!("initialise_schema_idm -> Ok!");
|
||||
|
||||
r
|
||||
}
|
||||
|
||||
#[instrument(level = "info", skip_all)]
|
||||
/// This function is idempotent, runs all the startup functionality and checks
|
||||
pub fn initialise_domain_info(&mut self) -> Result<(), OperationError> {
|
||||
// Configure the default key provider. This needs to exist *before* the
|
||||
// domain info!
|
||||
self.internal_migrate_or_create(E_KEY_PROVIDER_INTERNAL_DL6.clone())
|
||||
.and_then(|_| self.reload())
|
||||
.map_err(|err| {
|
||||
error!(?err, "initialise_domain_info::E_KEY_PROVIDER_INTERNAL_DL6");
|
||||
debug_assert!(false);
|
||||
err
|
||||
})?;
|
||||
|
||||
self.internal_migrate_or_create(E_SYSTEM_INFO_V1.clone())
|
||||
.and_then(|_| self.internal_migrate_or_create(E_DOMAIN_INFO_DL6.clone()))
|
||||
.and_then(|_| self.internal_migrate_or_create(E_SYSTEM_CONFIG_V1.clone()))
|
||||
.map_err(|err| {
|
||||
error!(?err, "initialise_domain_info");
|
||||
debug_assert!(false);
|
||||
err
|
||||
})
|
||||
}
|
||||
|
||||
#[instrument(level = "info", skip_all)]
|
||||
/// This function is idempotent, runs all the startup functionality and checks
|
||||
pub fn initialise_idm(&mut self) -> Result<(), OperationError> {
|
||||
// The domain info now exists, we should be able to do these migrations as they will
|
||||
// cause SPN regenerations to occur
|
||||
|
||||
// Delete entries that no longer need to exist.
|
||||
// TODO: Shouldn't this be a migration?
|
||||
// Check the admin object exists (migrations).
|
||||
// Create the default idm_admin group.
|
||||
let admin_entries: Vec<EntryInitNew> = idm_builtin_admin_entries()?;
|
||||
let res: Result<(), _> = admin_entries
|
||||
.into_iter()
|
||||
// Each item individually logs it's result
|
||||
.try_for_each(|ent| self.internal_migrate_or_create(ent));
|
||||
if res.is_ok() {
|
||||
debug!("initialise_idm p1 -> result Ok!");
|
||||
} else {
|
||||
error!(?res, "initialise_idm p1 -> result");
|
||||
}
|
||||
debug_assert!(res.is_ok());
|
||||
res?;
|
||||
|
||||
let res: Result<(), _> = idm_builtin_non_admin_groups()
|
||||
.into_iter()
|
||||
.try_for_each(|e| self.internal_migrate_or_create(e.clone().try_into()?));
|
||||
if res.is_ok() {
|
||||
debug!("initialise_idm p2 -> result Ok!");
|
||||
} else {
|
||||
error!(?res, "initialise_idm p2 -> result");
|
||||
}
|
||||
debug_assert!(res.is_ok());
|
||||
res?;
|
||||
|
||||
// ⚠️ DOMAIN LEVEL 1 ENTRIES ⚠️
|
||||
// Future entries need to be added via migrations.
|
||||
//
|
||||
// DO NOT MODIFY THIS DEFINITION
|
||||
let idm_entries: Vec<BuiltinAcp> = vec![
|
||||
// Built in access controls.
|
||||
IDM_ACP_RECYCLE_BIN_SEARCH_V1.clone(),
|
||||
IDM_ACP_RECYCLE_BIN_REVIVE_V1.clone(),
|
||||
IDM_ACP_SCHEMA_WRITE_ATTRS_V1.clone(),
|
||||
IDM_ACP_SCHEMA_WRITE_CLASSES_V1.clone(),
|
||||
IDM_ACP_ACP_MANAGE_V1.clone(),
|
||||
IDM_ACP_GROUP_ENTRY_MANAGED_BY_MODIFY_V1.clone(),
|
||||
IDM_ACP_GROUP_ENTRY_MANAGER_V1.clone(),
|
||||
IDM_ACP_SYNC_ACCOUNT_MANAGE_V1.clone(),
|
||||
IDM_ACP_RADIUS_SERVERS_V1.clone(),
|
||||
IDM_ACP_RADIUS_SECRET_MANAGE_V1.clone(),
|
||||
IDM_ACP_PEOPLE_SELF_WRITE_MAIL_V1.clone(),
|
||||
// IDM_ACP_SELF_READ_V1.clone(),
|
||||
// IDM_ACP_SELF_WRITE_V1.clone(),
|
||||
IDM_ACP_ACCOUNT_SELF_WRITE_V1.clone(),
|
||||
// IDM_ACP_SELF_NAME_WRITE_V1.clone(),
|
||||
IDM_ACP_ALL_ACCOUNTS_POSIX_READ_V1.clone(),
|
||||
IDM_ACP_SYSTEM_CONFIG_ACCOUNT_POLICY_MANAGE_V1.clone(),
|
||||
IDM_ACP_GROUP_UNIX_MANAGE_V1.clone(),
|
||||
IDM_ACP_HP_GROUP_UNIX_MANAGE_V1.clone(),
|
||||
IDM_ACP_GROUP_READ_V1.clone(),
|
||||
IDM_ACP_ACCOUNT_UNIX_EXTEND_V1.clone(),
|
||||
IDM_ACP_PEOPLE_PII_READ_V1.clone(),
|
||||
IDM_ACP_PEOPLE_PII_MANAGE_V1.clone(),
|
||||
IDM_ACP_PEOPLE_READ_V1.clone(),
|
||||
IDM_ACP_PEOPLE_MANAGE_V1.clone(),
|
||||
IDM_ACP_PEOPLE_DELETE_V1.clone(),
|
||||
IDM_ACP_PEOPLE_CREDENTIAL_RESET_V1.clone(),
|
||||
IDM_ACP_HP_PEOPLE_CREDENTIAL_RESET_V1.clone(),
|
||||
IDM_ACP_SERVICE_ACCOUNT_CREATE_V1.clone(),
|
||||
IDM_ACP_SERVICE_ACCOUNT_DELETE_V1.clone(),
|
||||
IDM_ACP_SERVICE_ACCOUNT_ENTRY_MANAGER_V1.clone(),
|
||||
IDM_ACP_SERVICE_ACCOUNT_ENTRY_MANAGED_BY_MODIFY_V1.clone(),
|
||||
IDM_ACP_HP_SERVICE_ACCOUNT_ENTRY_MANAGED_BY_MODIFY_V1.clone(),
|
||||
IDM_ACP_SERVICE_ACCOUNT_MANAGE_V1.clone(),
|
||||
// DL4
|
||||
// DL5
|
||||
// IDM_ACP_OAUTH2_MANAGE_DL5.clone(),
|
||||
// DL6
|
||||
// IDM_ACP_GROUP_ACCOUNT_POLICY_MANAGE_DL6.clone(),
|
||||
IDM_ACP_PEOPLE_CREATE_DL6.clone(),
|
||||
IDM_ACP_GROUP_MANAGE_DL6.clone(),
|
||||
IDM_ACP_ACCOUNT_MAIL_READ_DL6.clone(),
|
||||
// IDM_ACP_DOMAIN_ADMIN_DL6.clone(),
|
||||
// DL7
|
||||
// IDM_ACP_SELF_WRITE_DL7.clone(),
|
||||
IDM_ACP_SELF_NAME_WRITE_DL7.clone(),
|
||||
IDM_ACP_HP_CLIENT_CERTIFICATE_MANAGER_DL7.clone(),
|
||||
IDM_ACP_OAUTH2_MANAGE_DL7.clone(),
|
||||
// DL8
|
||||
IDM_ACP_SELF_READ_DL8.clone(),
|
||||
IDM_ACP_SELF_WRITE_DL8.clone(),
|
||||
IDM_ACP_APPLICATION_MANAGE_DL8.clone(),
|
||||
IDM_ACP_APPLICATION_ENTRY_MANAGER_DL8.clone(),
|
||||
IDM_ACP_MAIL_SERVERS_DL8.clone(),
|
||||
IDM_ACP_DOMAIN_ADMIN_DL8.clone(),
|
||||
IDM_ACP_GROUP_ACCOUNT_POLICY_MANAGE_DL8.clone(),
|
||||
];
|
||||
|
||||
let res: Result<(), _> = idm_entries
|
||||
.into_iter()
|
||||
.try_for_each(|entry| self.internal_migrate_or_create(entry.into()));
|
||||
if res.is_ok() {
|
||||
admin_debug!("initialise_idm p3 -> result Ok!");
|
||||
} else {
|
||||
admin_error!(?res, "initialise_idm p3 -> result");
|
||||
}
|
||||
debug_assert!(res.is_ok());
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl QueryServerReadTransaction<'_> {
|
||||
|
|
|
@ -2420,8 +2420,17 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
debug!(domain_previous_version = ?previous_version, domain_target_version = ?domain_info_version);
|
||||
debug!(domain_previous_patch_level = ?previous_patch_level, domain_target_patch_level = ?domain_info_patch_level);
|
||||
|
||||
// We have to check for DL0 since that's the initialisation level.
|
||||
if previous_version < DOMAIN_MIN_REMIGRATION_LEVEL && previous_version != DOMAIN_LEVEL_0 {
|
||||
// We have to check for DL0 since that's the initialisation level. If we are at DL0 then
|
||||
// the server was just brought up and there are no other actions to take since we are
|
||||
// now at TGT level.
|
||||
if previous_version == DOMAIN_LEVEL_0 {
|
||||
debug!(
|
||||
"Server was just brought up, skipping migrations as we are already at target level"
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if previous_version < DOMAIN_MIN_REMIGRATION_LEVEL {
|
||||
error!("UNABLE TO PROCEED. You are attempting a Skip update which is NOT SUPPORTED. You must upgrade one-version of Kanidm at a time.");
|
||||
error!("For more see: https://kanidm.github.io/kanidm/stable/support.html#upgrade-policy and https://kanidm.github.io/kanidm/stable/server_updates.html");
|
||||
error!(domain_previous_version = ?previous_version, domain_target_version = ?domain_info_version);
|
||||
|
@ -2434,7 +2443,10 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
self.migrate_domain_8_to_9()?;
|
||||
}
|
||||
|
||||
if previous_patch_level < PATCH_LEVEL_2 && domain_info_patch_level >= PATCH_LEVEL_2 {
|
||||
if previous_patch_level < PATCH_LEVEL_2
|
||||
&& domain_info_patch_level >= PATCH_LEVEL_2
|
||||
&& domain_info_version == DOMAIN_LEVEL_9
|
||||
{
|
||||
self.migrate_domain_patch_level_2()?;
|
||||
}
|
||||
|
||||
|
@ -2573,7 +2585,10 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
}
|
||||
|
||||
fn set_phase(&mut self, phase: ServerPhase) {
|
||||
*self.phase = phase
|
||||
// Phase changes are one way
|
||||
if phase > *self.phase {
|
||||
*self.phase = phase
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_phase(&mut self) -> ServerPhase {
|
||||
|
|
|
@ -615,9 +615,11 @@ mod tests {
|
|||
Err(OperationError::EmptyRequest)
|
||||
);
|
||||
|
||||
let idm_admin = server_txn.internal_search_uuid(UUID_IDM_ADMIN).unwrap();
|
||||
|
||||
// Mod changes no objects
|
||||
let me_nochg = ModifyEvent::new_impersonate_entry_ser(
|
||||
BUILTIN_ACCOUNT_IDM_ADMIN.clone(),
|
||||
let me_nochg = ModifyEvent::new_impersonate_entry(
|
||||
idm_admin,
|
||||
filter!(f_eq(
|
||||
Attribute::Name,
|
||||
PartialValue::new_iname("flarbalgarble")
|
||||
|
|
|
@ -89,7 +89,7 @@ pub async fn setup_idm_test(
|
|||
) -> (IdmServer, IdmServerDelayed, IdmServerAudit) {
|
||||
let qs = setup_test(config).await;
|
||||
|
||||
IdmServer::new(qs, "https://idm.example.com")
|
||||
IdmServer::new(qs, "https://idm.example.com", true)
|
||||
.await
|
||||
.expect("Failed to setup idms")
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ edition = { workspace = true }
|
|||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
test = true
|
||||
test = false
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
|
|
|
@ -14,87 +14,73 @@ const ALLOWED_ATTRIBUTES: &[&str] = &[
|
|||
"role",
|
||||
"output_mode",
|
||||
"log_level",
|
||||
"ldap",
|
||||
];
|
||||
|
||||
fn parse_knobs(
|
||||
input: &syn::ItemFn,
|
||||
server_config: &Punctuated<ExprAssign, syn::token::Comma>,
|
||||
) -> TokenStream {
|
||||
// If type mismatch occurs, the current rustc points to the last statement.
|
||||
let (last_stmt_start_span, _last_stmt_end_span) = {
|
||||
let mut last_stmt = input
|
||||
.block
|
||||
.stmts
|
||||
.last()
|
||||
.map(ToTokens::into_token_stream)
|
||||
.unwrap_or_default()
|
||||
.into_iter();
|
||||
// `Span` on stable Rust has a limitation that only points to the first
|
||||
// token, not the whole tokens. We can work around this limitation by
|
||||
// using the first/last span of the tokens like
|
||||
// `syn::Error::new_spanned` does.
|
||||
let start = last_stmt.next().map_or_else(Span::call_site, |t| t.span());
|
||||
let end = last_stmt.last().map_or(start, |t| t.span());
|
||||
(start, end)
|
||||
};
|
||||
#[derive(Default)]
|
||||
struct Flags {
|
||||
ldap: bool,
|
||||
}
|
||||
|
||||
// here we gather all the provided configuration in a struct like declaration
|
||||
// By now we have already checked that the configurations provided belong to the allowed subset
|
||||
let mut field_modifications = quote! {};
|
||||
server_config.pairs().for_each(|p| {
|
||||
let field_name = p.value().left.to_token_stream(); // here we can use to_token_stream as we know we're iterating over ExprAssigns
|
||||
let field_value = p.value().right.to_token_stream();
|
||||
field_modifications.extend(quote! {
|
||||
#field_name: #field_value,})
|
||||
fn parse_attributes(
|
||||
args: &TokenStream,
|
||||
input: &syn::ItemFn,
|
||||
) -> Result<(proc_macro2::TokenStream, Flags), syn::Error> {
|
||||
let args: Punctuated<ExprAssign, syn::token::Comma> =
|
||||
Punctuated::<ExprAssign, Token![,]>::parse_terminated.parse(args.clone())?;
|
||||
|
||||
let args_are_allowed = args.pairs().all(|p| {
|
||||
ALLOWED_ATTRIBUTES.to_vec().contains(
|
||||
&p.value()
|
||||
.left
|
||||
.span()
|
||||
.source_text()
|
||||
.unwrap_or_default()
|
||||
.as_str(),
|
||||
)
|
||||
});
|
||||
|
||||
// Setup the config filling the remaining fields with the default values
|
||||
let default_config_struct = quote!(kanidmd_core::config::Configuration {
|
||||
if !args_are_allowed {
|
||||
let msg = "Invalid test config attribute. The following are allowed";
|
||||
return Err(syn::Error::new_spanned(
|
||||
input.sig.fn_token,
|
||||
format!("{}: {}", msg, ALLOWED_ATTRIBUTES.join(", ")),
|
||||
));
|
||||
}
|
||||
|
||||
let mut flags = Flags::default();
|
||||
let mut field_modifications = quote! {};
|
||||
|
||||
args.pairs().for_each(|p| {
|
||||
match p
|
||||
.value()
|
||||
.left
|
||||
.span()
|
||||
.source_text()
|
||||
.unwrap_or_default()
|
||||
.as_str()
|
||||
{
|
||||
"ldap" => {
|
||||
flags.ldap = true;
|
||||
field_modifications.extend(quote! {
|
||||
ldapaddress: Some("on".to_string()),})
|
||||
}
|
||||
_ => {
|
||||
let field_name = p.value().left.to_token_stream(); // here we can use to_token_stream as we know we're iterating over ExprAssigns
|
||||
let field_value = p.value().right.to_token_stream();
|
||||
// This is printing out struct members.
|
||||
field_modifications.extend(quote! {
|
||||
#field_name: #field_value,})
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let ts = quote!(kanidmd_core::config::Configuration {
|
||||
#field_modifications
|
||||
..kanidmd_core::config::Configuration::new_for_test()
|
||||
});
|
||||
|
||||
let rt = quote_spanned! {last_stmt_start_span=>
|
||||
tokio::runtime::Builder::new_current_thread()
|
||||
};
|
||||
|
||||
let header = quote! {
|
||||
#[::core::prelude::v1::test]
|
||||
};
|
||||
|
||||
let fn_name = &input.sig.ident;
|
||||
let test_driver = Ident::new(&format!("tk_{}", fn_name), input.sig.span());
|
||||
|
||||
// Effectively we are just injecting a real test function around this which we will
|
||||
// call.
|
||||
|
||||
let result = quote! {
|
||||
#input
|
||||
|
||||
#header
|
||||
fn #test_driver() {
|
||||
let body = async {
|
||||
let (rsclient, mut core_handle) = kanidmd_testkit::setup_async_test(#default_config_struct).await;
|
||||
#fn_name(rsclient).await;
|
||||
core_handle.shutdown().await;
|
||||
};
|
||||
#[allow(clippy::expect_used, clippy::diverging_sub_expression)]
|
||||
{
|
||||
return #rt
|
||||
.enable_all()
|
||||
.build()
|
||||
.expect("Failed building the Runtime")
|
||||
.block_on(body);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
result.into()
|
||||
}
|
||||
|
||||
fn token_stream_with_error(mut tokens: TokenStream, error: syn::Error) -> TokenStream {
|
||||
tokens.extend(TokenStream::from(error.into_compile_error()));
|
||||
tokens
|
||||
Ok((ts, flags))
|
||||
}
|
||||
|
||||
pub(crate) fn test(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
|
@ -115,31 +101,80 @@ pub(crate) fn test(args: TokenStream, item: TokenStream) -> TokenStream {
|
|||
let msg = "the `async` keyword is missing from the function declaration";
|
||||
return token_stream_with_error(item, syn::Error::new_spanned(input.sig.fn_token, msg));
|
||||
}
|
||||
let args: Punctuated<ExprAssign, syn::token::Comma> =
|
||||
match Punctuated::<ExprAssign, Token![,]>::parse_terminated.parse(args.clone()) {
|
||||
Ok(it) => it,
|
||||
Err(e) => return token_stream_with_error(args, e),
|
||||
};
|
||||
let args_are_allowed = args.pairs().all(|p| {
|
||||
ALLOWED_ATTRIBUTES.to_vec().contains(
|
||||
&p.value()
|
||||
.left
|
||||
.span()
|
||||
.source_text()
|
||||
.unwrap_or_default()
|
||||
.as_str(),
|
||||
)
|
||||
});
|
||||
if !args_are_allowed {
|
||||
let msg =
|
||||
"Currently only a subset of all the server configs can be set. Here is the full list";
|
||||
return token_stream_with_error(
|
||||
item,
|
||||
syn::Error::new_spanned(
|
||||
input.sig.fn_token,
|
||||
format!("{}: {}", msg, ALLOWED_ATTRIBUTES.join(", ")),
|
||||
),
|
||||
);
|
||||
}
|
||||
parse_knobs(&input, &args)
|
||||
|
||||
// If type mismatch occurs, the current rustc points to the last statement.
|
||||
let (last_stmt_start_span, _last_stmt_end_span) = {
|
||||
let mut last_stmt = input
|
||||
.block
|
||||
.stmts
|
||||
.last()
|
||||
.map(ToTokens::into_token_stream)
|
||||
.unwrap_or_default()
|
||||
.into_iter();
|
||||
// `Span` on stable Rust has a limitation that only points to the first
|
||||
// token, not the whole tokens. We can work around this limitation by
|
||||
// using the first/last span of the tokens like
|
||||
// `syn::Error::new_spanned` does.
|
||||
let start = last_stmt.next().map_or_else(Span::call_site, |t| t.span());
|
||||
let end = last_stmt.last().map_or(start, |t| t.span());
|
||||
(start, end)
|
||||
};
|
||||
|
||||
// Setup the config filling the remaining fields with the default values
|
||||
let (default_config_struct, flags) = match parse_attributes(&args, &input) {
|
||||
Ok(dc) => dc,
|
||||
Err(e) => return token_stream_with_error(args, e),
|
||||
};
|
||||
|
||||
let rt = quote_spanned! {last_stmt_start_span=>
|
||||
tokio::runtime::Builder::new_current_thread()
|
||||
};
|
||||
|
||||
let header = quote! {
|
||||
#[::core::prelude::v1::test]
|
||||
};
|
||||
|
||||
let test_fn_args = if flags.ldap {
|
||||
quote! {
|
||||
&test_env
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
&test_env.rsclient
|
||||
}
|
||||
};
|
||||
|
||||
let test_fn = &input.sig.ident;
|
||||
let test_driver = Ident::new(&format!("tk_{}", test_fn), input.sig.span());
|
||||
|
||||
// Effectively we are just injecting a real test function around this which we will
|
||||
// call.
|
||||
let result = quote! {
|
||||
#input
|
||||
|
||||
#header
|
||||
fn #test_driver() {
|
||||
let body = async {
|
||||
let mut test_env = kanidmd_testkit::setup_async_test(#default_config_struct).await;
|
||||
|
||||
#test_fn(#test_fn_args).await;
|
||||
test_env.core_handle.shutdown().await;
|
||||
};
|
||||
#[allow(clippy::expect_used, clippy::diverging_sub_expression)]
|
||||
{
|
||||
return #rt
|
||||
.enable_all()
|
||||
.build()
|
||||
.expect("Failed building the Runtime")
|
||||
.block_on(body);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
result.into()
|
||||
}
|
||||
|
||||
fn token_stream_with_error(mut tokens: TokenStream, error: syn::Error) -> TokenStream {
|
||||
tokens.extend(TokenStream::from(error.into_compile_error()));
|
||||
tokens
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ repository = { workspace = true }
|
|||
[lib]
|
||||
name = "kanidmd_testkit"
|
||||
path = "src/lib.rs"
|
||||
test = true
|
||||
test = false
|
||||
doctest = false
|
||||
|
||||
[features]
|
||||
|
@ -25,7 +25,6 @@ webdriver = []
|
|||
dev-oauth2-device-flow = []
|
||||
|
||||
[dependencies]
|
||||
http = { workspace = true }
|
||||
kanidm_client = { workspace = true }
|
||||
kanidm_proto = { workspace = true }
|
||||
kanidmd_core = { workspace = true }
|
||||
|
@ -54,10 +53,10 @@ escargot = "0.5.13"
|
|||
# used for webdriver testing
|
||||
fantoccini = { version = "0.21.4" }
|
||||
futures = { workspace = true }
|
||||
ldap3_client = { workspace = true }
|
||||
oauth2_ext = { workspace = true, default-features = false, features = [
|
||||
"reqwest",
|
||||
] }
|
||||
openssl = { workspace = true }
|
||||
petgraph = { version = "0.7.1", features = ["serde"] }
|
||||
serde_json = { workspace = true }
|
||||
time = { workspace = true }
|
||||
|
|
|
@ -4,7 +4,9 @@
|
|||
//!
|
||||
|
||||
use kanidmd_lib::constants::entries::Attribute;
|
||||
use kanidmd_lib::constants::groups::{idm_builtin_admin_groups, idm_builtin_non_admin_groups};
|
||||
use kanidmd_lib::migration_data::current::{
|
||||
phase_5_builtin_admin_entries, phase_6_builtin_non_admin_entries
|
||||
};
|
||||
use kanidmd_lib::prelude::{builtin_accounts, EntryInitNew};
|
||||
use petgraph::graphmap::{AllEdges, GraphMap, NodeTrait};
|
||||
use petgraph::Directed;
|
||||
|
@ -106,8 +108,8 @@ async fn enumerate_default_groups(/*_client: KanidmClient*/) {
|
|||
graph.add_node(account.uuid);
|
||||
});
|
||||
|
||||
let mut groups = idm_builtin_admin_groups();
|
||||
groups.extend(idm_builtin_non_admin_groups());
|
||||
let mut groups = phase_5_builtin_admin_entries();
|
||||
groups.extend(phase_6_builtin_non_admin_entries());
|
||||
|
||||
groups.into_iter().for_each(|group| {
|
||||
uuidmap.insert(group.uuid, group.clone().try_into().unwrap());
|
|
@ -12,10 +12,9 @@ use kanidm_proto::oauth2::{
|
|||
AccessTokenRequest, AccessTokenResponse, AuthorisationResponse, GrantTypeReq,
|
||||
};
|
||||
|
||||
use kanidmd_lib::constants::NAME_IDM_ALL_ACCOUNTS;
|
||||
use kanidmd_lib::prelude::uri::{OAUTH2_AUTHORISE_DEVICE, OAUTH2_TOKEN_ENDPOINT};
|
||||
use kanidmd_lib::prelude::{
|
||||
Attribute, IDM_ALL_ACCOUNTS, OAUTH2_SCOPE_EMAIL, OAUTH2_SCOPE_OPENID, OAUTH2_SCOPE_READ,
|
||||
};
|
||||
use kanidmd_lib::prelude::{Attribute, OAUTH2_SCOPE_EMAIL, OAUTH2_SCOPE_OPENID, OAUTH2_SCOPE_READ};
|
||||
use kanidmd_testkit::{
|
||||
assert_no_cache, ADMIN_TEST_PASSWORD, ADMIN_TEST_USER, IDM_ADMIN_TEST_PASSWORD,
|
||||
IDM_ADMIN_TEST_USER, NOT_ADMIN_TEST_EMAIL, NOT_ADMIN_TEST_PASSWORD, NOT_ADMIN_TEST_USERNAME,
|
||||
|
@ -159,7 +158,7 @@ async fn oauth2_device_flow(rsclient: KanidmClient) {
|
|||
rsclient
|
||||
.idm_oauth2_rs_update_scope_map(
|
||||
TEST_INTEGRATION_RS_ID,
|
||||
IDM_ALL_ACCOUNTS.name,
|
||||
NAME_IDM_ALL_ACCOUNTS,
|
||||
vec![OAUTH2_SCOPE_READ, OAUTH2_SCOPE_EMAIL, OAUTH2_SCOPE_OPENID],
|
||||
)
|
||||
.await
|
||||
|
@ -168,7 +167,7 @@ async fn oauth2_device_flow(rsclient: KanidmClient) {
|
|||
rsclient
|
||||
.idm_oauth2_rs_update_sup_scope_map(
|
||||
TEST_INTEGRATION_RS_ID,
|
||||
IDM_ALL_ACCOUNTS.name,
|
||||
NAME_IDM_ALL_ACCOUNTS,
|
||||
vec![ADMIN_TEST_USER],
|
||||
)
|
||||
.await
|
||||
|
@ -179,7 +178,7 @@ async fn oauth2_device_flow(rsclient: KanidmClient) {
|
|||
.idm_oauth2_rs_update_claim_map(
|
||||
TEST_INTEGRATION_RS_ID,
|
||||
"test_claim",
|
||||
IDM_ALL_ACCOUNTS.name,
|
||||
NAME_IDM_ALL_ACCOUNTS,
|
||||
&["claim_a".to_string(), "claim_b".to_string()],
|
||||
)
|
||||
.await
|
|
@ -10,16 +10,16 @@
|
|||
#![deny(clippy::needless_pass_by_value)]
|
||||
#![deny(clippy::trivially_copy_pass_by_ref)]
|
||||
|
||||
use std::net::TcpStream;
|
||||
use std::sync::atomic::{AtomicU16, Ordering};
|
||||
|
||||
use kanidm_client::{KanidmClient, KanidmClientBuilder};
|
||||
|
||||
use kanidm_proto::internal::{Filter, Modify, ModifyList};
|
||||
use kanidmd_core::config::{Configuration, IntegrationTestConfig};
|
||||
use kanidmd_core::{create_server_core, CoreHandle};
|
||||
use kanidmd_lib::prelude::{Attribute, BUILTIN_GROUP_IDM_ADMINS_V1};
|
||||
use kanidmd_lib::prelude::Attribute;
|
||||
use std::net::TcpStream;
|
||||
use std::sync::atomic::{AtomicU16, Ordering};
|
||||
use tokio::task;
|
||||
use tracing::error;
|
||||
use url::Url;
|
||||
|
||||
pub const ADMIN_TEST_USER: &str = "admin";
|
||||
pub const ADMIN_TEST_PASSWORD: &str = "integration test admin password";
|
||||
|
@ -46,14 +46,9 @@ pub fn is_free_port(port: u16) -> bool {
|
|||
}
|
||||
|
||||
// Test external behaviours of the service.
|
||||
|
||||
// allowed because the use of this function is behind a test gate
|
||||
#[allow(dead_code)]
|
||||
pub async fn setup_async_test(mut config: Configuration) -> (KanidmClient, CoreHandle) {
|
||||
sketching::test_init();
|
||||
|
||||
fn port_loop() -> u16 {
|
||||
let mut counter = 0;
|
||||
let port = loop {
|
||||
loop {
|
||||
let possible_port = PORT_ALLOC.fetch_add(1, Ordering::SeqCst);
|
||||
if is_free_port(possible_port) {
|
||||
break possible_port;
|
||||
|
@ -64,7 +59,21 @@ pub async fn setup_async_test(mut config: Configuration) -> (KanidmClient, CoreH
|
|||
tracing::error!("Unable to allocate port!");
|
||||
panic!();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AsyncTestEnvironment {
|
||||
pub rsclient: KanidmClient,
|
||||
pub core_handle: CoreHandle,
|
||||
pub ldap_url: Option<Url>,
|
||||
}
|
||||
|
||||
// allowed because the use of this function is behind a test gate
|
||||
#[allow(dead_code)]
|
||||
pub async fn setup_async_test(mut config: Configuration) -> AsyncTestEnvironment {
|
||||
sketching::test_init();
|
||||
|
||||
let port = port_loop();
|
||||
|
||||
let int_config = Box::new(IntegrationTestConfig {
|
||||
admin_user: ADMIN_TEST_USER.to_string(),
|
||||
|
@ -75,6 +84,16 @@ pub async fn setup_async_test(mut config: Configuration) -> (KanidmClient, CoreH
|
|||
|
||||
let addr = format!("http://localhost:{}", port);
|
||||
|
||||
let ldap_url = if config.ldapaddress.is_some() {
|
||||
let ldapport = port_loop();
|
||||
config.ldapaddress = Some(format!("127.0.0.1:{}", ldapport));
|
||||
Url::parse(&format!("ldap://127.0.0.1:{}", ldapport))
|
||||
.inspect_err(|err| error!(?err, "ldap address setup"))
|
||||
.ok()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Setup the address and origin..
|
||||
config.address = format!("127.0.0.1:{}", port);
|
||||
config.integration_test_config = Some(int_config);
|
||||
|
@ -92,6 +111,7 @@ pub async fn setup_async_test(mut config: Configuration) -> (KanidmClient, CoreH
|
|||
#[allow(clippy::panic)]
|
||||
let rsclient = match KanidmClientBuilder::new()
|
||||
.address(addr.clone())
|
||||
.enable_native_ca_roots(false)
|
||||
.no_proxy()
|
||||
.build()
|
||||
{
|
||||
|
@ -101,7 +121,11 @@ pub async fn setup_async_test(mut config: Configuration) -> (KanidmClient, CoreH
|
|||
|
||||
tracing::info!("Testkit server setup complete - {}", addr);
|
||||
|
||||
(rsclient, core_handle)
|
||||
AsyncTestEnvironment {
|
||||
rsclient,
|
||||
core_handle,
|
||||
ldap_url,
|
||||
}
|
||||
}
|
||||
|
||||
/// creates a user (username: `id`) and puts them into a group, creating it if need be.
|
||||
|
@ -385,7 +409,7 @@ pub async fn login_put_admin_idm_admins(rsclient: &KanidmClient) {
|
|||
|
||||
#[allow(clippy::expect_used)]
|
||||
rsclient
|
||||
.idm_group_add_members(BUILTIN_GROUP_IDM_ADMINS_V1.name, &[ADMIN_TEST_USER])
|
||||
.idm_group_add_members("system_admins", &[ADMIN_TEST_USER])
|
||||
.await
|
||||
.expect("Failed to add admin user to idm_admins")
|
||||
}
|
||||
|
@ -396,7 +420,7 @@ macro_rules! assert_no_cache {
|
|||
// Check we have correct nocache headers.
|
||||
let cache_header: &str = $response
|
||||
.headers()
|
||||
.get(http::header::CACHE_CONTROL)
|
||||
.get(kanidm_client::http::header::CACHE_CONTROL)
|
||||
.expect("missing cache-control header")
|
||||
.to_str()
|
||||
.expect("invalid cache-control header");
|
||||
|
|
11
server/testkit/tests/DO_NOT_ADD_MORE_TESTS_HERE
Normal file
11
server/testkit/tests/DO_NOT_ADD_MORE_TESTS_HERE
Normal file
|
@ -0,0 +1,11 @@
|
|||
|
||||
DO NOT ADD MORE TESTS TO THIS DIRECTORY
|
||||
|
||||
Add them to the `testkit` directory.
|
||||
|
||||
Each new test in this folder is a separate binary, that must be complied linked and executed. This
|
||||
takes HUGES AMOUNTS OF TIME. It makes tests unbelievably slow.
|
||||
|
||||
If you want to add an integration test, put it into the testkit dir so it becomes part of the
|
||||
single larger integration test runner.
|
||||
|
3
server/testkit/tests/integration_test.rs
Normal file
3
server/testkit/tests/integration_test.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
#![deny(warnings)]
|
||||
|
||||
mod testkit;
|
|
@ -1,57 +0,0 @@
|
|||
use kanidm_client::KanidmClient;
|
||||
|
||||
/// This literally tests that the thing exists and responds in a way we expect, probably worth testing it better...
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_v1_self_applinks(rsclient: KanidmClient) {
|
||||
// We need to do manual reqwests here.
|
||||
let client = reqwest::ClientBuilder::new()
|
||||
.danger_accept_invalid_certs(true)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let response = match client
|
||||
.get(rsclient.make_url("/v1/self/_applinks"))
|
||||
.send()
|
||||
.await
|
||||
{
|
||||
Ok(value) => value,
|
||||
Err(error) => {
|
||||
panic!(
|
||||
"Failed to query {:?} : {:#?}",
|
||||
rsclient.make_url("/v1/self/_applinks"),
|
||||
error
|
||||
);
|
||||
}
|
||||
};
|
||||
eprintln!("response: {:#?}", response);
|
||||
assert_eq!(response.status(), 401);
|
||||
|
||||
let body = response.text().await.unwrap();
|
||||
eprintln!("{}", body);
|
||||
}
|
||||
|
||||
/// This literally tests that the thing exists and responds in a way we expect, probably worth testing it better...
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_v1_self_whoami_uat(rsclient: KanidmClient) {
|
||||
// We need to do manual reqwests here.
|
||||
let client = reqwest::ClientBuilder::new()
|
||||
.danger_accept_invalid_certs(true)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let response = match client.get(rsclient.make_url("/v1/self/_uat")).send().await {
|
||||
Ok(value) => value,
|
||||
Err(error) => {
|
||||
panic!(
|
||||
"Failed to query {:?} : {:#?}",
|
||||
rsclient.make_url("/v1/self/_uat"),
|
||||
error
|
||||
);
|
||||
}
|
||||
};
|
||||
eprintln!("response: {:#?}", response);
|
||||
assert_eq!(response.status(), 401);
|
||||
|
||||
let body = response.text().await.unwrap();
|
||||
eprintln!("{}", body);
|
||||
}
|
|
@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
|
|||
use tracing::info;
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn check_that_the_swagger_api_loads(rsclient: kanidm_client::KanidmClient) {
|
||||
async fn check_that_the_swagger_api_loads(rsclient: &kanidm_client::KanidmClient) {
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct OpenAPIResponse {
|
||||
pub openapi: String,
|
||||
|
@ -12,21 +12,18 @@ async fn check_that_the_swagger_api_loads(rsclient: kanidm_client::KanidmClient)
|
|||
rsclient.set_token("".into()).await;
|
||||
info!("Running test: check_that_the_swagger_api_loads");
|
||||
let url = rsclient.make_url("/docs/v1/openapi.json");
|
||||
let openapi_response: OpenAPIResponse = reqwest::get(url.clone())
|
||||
|
||||
let openapi_response = rsclient
|
||||
.perform_get_request::<OpenAPIResponse>(url.as_str())
|
||||
.await
|
||||
.expect("Failed to get openapi.json")
|
||||
.json()
|
||||
.await
|
||||
.unwrap();
|
||||
.expect("Failed to get openapi.json");
|
||||
assert_eq!(openapi_response.openapi, "3.0.3");
|
||||
|
||||
// this validates that it's valid JSON schema, but not that it's valid openapi... but it's a start.
|
||||
let schema: serde_json::Value = reqwest::get(url)
|
||||
let schema = rsclient
|
||||
.perform_get_request::<serde_json::Value>(url.as_str())
|
||||
.await
|
||||
.expect("Failed to get openapi.json")
|
||||
.json()
|
||||
.await
|
||||
.unwrap();
|
||||
.expect("Failed to get openapi.json");
|
||||
|
||||
let instance = serde_json::json!("foo");
|
||||
let compiled = Validator::new(&schema).expect("A valid schema");
|
|
@ -3,7 +3,7 @@ use kanidm_proto::constants::ATTR_DOMAIN_DISPLAY_NAME;
|
|||
use kanidmd_testkit::{ADMIN_TEST_PASSWORD, ADMIN_TEST_USER};
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_idm_set_ldap_allow_unix_password_bind(rsclient: KanidmClient) {
|
||||
async fn test_idm_set_ldap_allow_unix_password_bind(rsclient: &KanidmClient) {
|
||||
rsclient
|
||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
||||
.await
|
||||
|
@ -13,8 +13,9 @@ async fn test_idm_set_ldap_allow_unix_password_bind(rsclient: KanidmClient) {
|
|||
.await
|
||||
.expect("Failed to set LDAP allow unix password bind to true");
|
||||
}
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_idm_domain_set_ldap_basedn(rsclient: KanidmClient) {
|
||||
async fn test_idm_domain_set_ldap_basedn(rsclient: &KanidmClient) {
|
||||
rsclient
|
||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
||||
.await
|
||||
|
@ -27,7 +28,7 @@ async fn test_idm_domain_set_ldap_basedn(rsclient: KanidmClient) {
|
|||
}
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_idm_domain_set_ldap_max_queryable_attrs(rsclient: KanidmClient) {
|
||||
async fn test_idm_domain_set_ldap_max_queryable_attrs(rsclient: &KanidmClient) {
|
||||
rsclient
|
||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
||||
.await
|
||||
|
@ -40,7 +41,7 @@ async fn test_idm_domain_set_ldap_max_queryable_attrs(rsclient: KanidmClient) {
|
|||
}
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_idm_domain_set_display_name(rsclient: KanidmClient) {
|
||||
async fn test_idm_domain_set_display_name(rsclient: &KanidmClient) {
|
||||
rsclient
|
||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
||||
.await
|
|
@ -1,10 +1,10 @@
|
|||
use kanidm_client::{ClientError, KanidmClient};
|
||||
use kanidm_client::{ClientError, KanidmClient, StatusCode};
|
||||
use kanidm_proto::constants::ATTR_DESCRIPTION;
|
||||
use kanidmd_testkit::{create_user, ADMIN_TEST_PASSWORD, ADMIN_TEST_USER};
|
||||
use serde_json::Value;
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_v1_group_id_patch(rsclient: KanidmClient) {
|
||||
async fn test_v1_group_id_patch(rsclient: &KanidmClient) {
|
||||
let res = rsclient
|
||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
||||
.await;
|
||||
|
@ -25,7 +25,7 @@ async fn test_v1_group_id_patch(rsclient: KanidmClient) {
|
|||
}
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_v1_group_id_attr_post(rsclient: KanidmClient) {
|
||||
async fn test_v1_group_id_attr_post(rsclient: &KanidmClient) {
|
||||
let res = rsclient
|
||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
||||
.await;
|
||||
|
@ -48,6 +48,6 @@ async fn test_v1_group_id_attr_post(rsclient: KanidmClient) {
|
|||
eprintln!("response: {:#?}", response);
|
||||
assert!(matches!(
|
||||
response,
|
||||
ClientError::Http(reqwest::StatusCode::BAD_REQUEST, _, _)
|
||||
ClientError::Http(StatusCode::BAD_REQUEST, _, _)
|
||||
));
|
||||
}
|
|
@ -1,11 +1,16 @@
|
|||
use kanidm_client::KanidmClient;
|
||||
use kanidm_client::{http::header, KanidmClient};
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_https_manifest(rsclient: KanidmClient) {
|
||||
async fn test_https_manifest(rsclient: &KanidmClient) {
|
||||
// We need to do manual reqwests here.
|
||||
let client = rsclient.client();
|
||||
|
||||
// here we test the /ui/ endpoint which should have the headers
|
||||
let response = match reqwest::get(rsclient.make_url("/manifest.webmanifest")).await {
|
||||
let response = match client
|
||||
.get(rsclient.make_url("/manifest.webmanifest"))
|
||||
.send()
|
||||
.await
|
||||
{
|
||||
Ok(value) => value,
|
||||
Err(error) => {
|
||||
panic!(
|
||||
|
@ -20,8 +25,6 @@ async fn test_https_manifest(rsclient: KanidmClient) {
|
|||
|
||||
eprintln!(
|
||||
"csp headers: {:#?}",
|
||||
response
|
||||
.headers()
|
||||
.get(http::header::CONTENT_SECURITY_POLICY)
|
||||
response.headers().get(header::CONTENT_SECURITY_POLICY)
|
||||
);
|
||||
}
|
|
@ -11,11 +11,9 @@ const DEFAULT_IP_ADDRESS: IpAddr = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
|
|||
// *test where we don't trust the x-forwarded-for header
|
||||
|
||||
#[kanidmd_testkit::test(trust_x_forward_for = false)]
|
||||
async fn dont_trust_xff_send_header(rsclient: KanidmClient) {
|
||||
let client = reqwest::ClientBuilder::new()
|
||||
.danger_accept_invalid_certs(true)
|
||||
.build()
|
||||
.unwrap();
|
||||
async fn dont_trust_xff_send_header(rsclient: &KanidmClient) {
|
||||
let client = rsclient.client();
|
||||
|
||||
let res = client
|
||||
.get(rsclient.make_url("/v1/debug/ipinfo"))
|
||||
.header(
|
||||
|
@ -34,11 +32,9 @@ async fn dont_trust_xff_send_header(rsclient: KanidmClient) {
|
|||
}
|
||||
|
||||
#[kanidmd_testkit::test(trust_x_forward_for = false)]
|
||||
async fn dont_trust_xff_dont_send_header(rsclient: KanidmClient) {
|
||||
let client = reqwest::ClientBuilder::new()
|
||||
.danger_accept_invalid_certs(true)
|
||||
.build()
|
||||
.unwrap();
|
||||
async fn dont_trust_xff_dont_send_header(rsclient: &KanidmClient) {
|
||||
let client = rsclient.client();
|
||||
|
||||
let res = client
|
||||
.get(rsclient.make_url("/v1/debug/ipinfo"))
|
||||
.header(
|
||||
|
@ -62,11 +58,9 @@ async fn dont_trust_xff_dont_send_header(rsclient: KanidmClient) {
|
|||
// *test where we trust the x-forwarded-for header
|
||||
|
||||
#[kanidmd_testkit::test(trust_x_forward_for = true)]
|
||||
async fn trust_xff_send_invalid_header_single_value(rsclient: KanidmClient) {
|
||||
let client = reqwest::ClientBuilder::new()
|
||||
.danger_accept_invalid_certs(true)
|
||||
.build()
|
||||
.unwrap();
|
||||
async fn trust_xff_send_invalid_header_single_value(rsclient: &KanidmClient) {
|
||||
let client = rsclient.client();
|
||||
|
||||
let res = client
|
||||
.get(rsclient.make_url("/v1/debug/ipinfo"))
|
||||
.header(
|
||||
|
@ -84,11 +78,9 @@ async fn trust_xff_send_invalid_header_single_value(rsclient: KanidmClient) {
|
|||
// with a valid leftmost address and an invalid address later in the list. Right now it wouldn't work.
|
||||
//
|
||||
#[kanidmd_testkit::test(trust_x_forward_for = true)]
|
||||
async fn trust_xff_send_invalid_header_multiple_values(rsclient: KanidmClient) {
|
||||
let client = reqwest::ClientBuilder::new()
|
||||
.danger_accept_invalid_certs(true)
|
||||
.build()
|
||||
.unwrap();
|
||||
async fn trust_xff_send_invalid_header_multiple_values(rsclient: &KanidmClient) {
|
||||
let client = rsclient.client();
|
||||
|
||||
let res = client
|
||||
.get(rsclient.make_url("/v1/debug/ipinfo"))
|
||||
.header(
|
||||
|
@ -103,13 +95,11 @@ async fn trust_xff_send_invalid_header_multiple_values(rsclient: KanidmClient) {
|
|||
}
|
||||
|
||||
#[kanidmd_testkit::test(trust_x_forward_for = true)]
|
||||
async fn trust_xff_send_valid_header_single_ipv4_address(rsclient: KanidmClient) {
|
||||
async fn trust_xff_send_valid_header_single_ipv4_address(rsclient: &KanidmClient) {
|
||||
let ip_addr = "2001:db8:85a3:8d3:1319:8a2e:370:7348";
|
||||
|
||||
let client = reqwest::ClientBuilder::new()
|
||||
.danger_accept_invalid_certs(true)
|
||||
.build()
|
||||
.unwrap();
|
||||
let client = rsclient.client();
|
||||
|
||||
let res = client
|
||||
.get(rsclient.make_url("/v1/debug/ipinfo"))
|
||||
.header(X_FORWARDED_FOR, ip_addr)
|
||||
|
@ -125,13 +115,11 @@ async fn trust_xff_send_valid_header_single_ipv4_address(rsclient: KanidmClient)
|
|||
}
|
||||
|
||||
#[kanidmd_testkit::test(trust_x_forward_for = true)]
|
||||
async fn trust_xff_send_valid_header_single_ipv6_address(rsclient: KanidmClient) {
|
||||
async fn trust_xff_send_valid_header_single_ipv6_address(rsclient: &KanidmClient) {
|
||||
let ip_addr = "203.0.113.195";
|
||||
|
||||
let client = reqwest::ClientBuilder::new()
|
||||
.danger_accept_invalid_certs(true)
|
||||
.build()
|
||||
.unwrap();
|
||||
let client = rsclient.client();
|
||||
|
||||
let res = client
|
||||
.get(rsclient.make_url("/v1/debug/ipinfo"))
|
||||
.header(X_FORWARDED_FOR, ip_addr)
|
||||
|
@ -147,13 +135,11 @@ async fn trust_xff_send_valid_header_single_ipv6_address(rsclient: KanidmClient)
|
|||
}
|
||||
|
||||
#[kanidmd_testkit::test(trust_x_forward_for = true)]
|
||||
async fn trust_xff_send_valid_header_multiple_address(rsclient: KanidmClient) {
|
||||
async fn trust_xff_send_valid_header_multiple_address(rsclient: &KanidmClient) {
|
||||
let first_ip_addr = "203.0.113.195, 2001:db8:85a3:8d3:1319:8a2e:370:7348";
|
||||
|
||||
let client = reqwest::ClientBuilder::new()
|
||||
.danger_accept_invalid_certs(true)
|
||||
.build()
|
||||
.unwrap();
|
||||
let client = rsclient.client();
|
||||
|
||||
let res = client
|
||||
.get(rsclient.make_url("/v1/debug/ipinfo"))
|
||||
.header(X_FORWARDED_FOR, first_ip_addr)
|
||||
|
@ -172,10 +158,6 @@ async fn trust_xff_send_valid_header_multiple_address(rsclient: KanidmClient) {
|
|||
|
||||
let second_ip_addr = "2001:db8:85a3:8d3:1319:8a2e:370:7348, 198.51.100.178, 203.0.113.195";
|
||||
|
||||
let client = reqwest::ClientBuilder::new()
|
||||
.danger_accept_invalid_certs(true)
|
||||
.build()
|
||||
.unwrap();
|
||||
let res = client
|
||||
.get(rsclient.make_url("/v1/debug/ipinfo"))
|
||||
.header(X_FORWARDED_FOR, second_ip_addr)
|
||||
|
@ -194,11 +176,9 @@ async fn trust_xff_send_valid_header_multiple_address(rsclient: KanidmClient) {
|
|||
}
|
||||
|
||||
#[kanidmd_testkit::test(trust_x_forward_for = true)]
|
||||
async fn trust_xff_dont_send_header(rsclient: KanidmClient) {
|
||||
let client = reqwest::ClientBuilder::new()
|
||||
.danger_accept_invalid_certs(true)
|
||||
.build()
|
||||
.unwrap();
|
||||
async fn trust_xff_dont_send_header(rsclient: &KanidmClient) {
|
||||
let client = rsclient.client();
|
||||
|
||||
let res = client
|
||||
.get(rsclient.make_url("/v1/debug/ipinfo"))
|
||||
.send()
|
|
@ -1,11 +1,13 @@
|
|||
use kanidm_client::http::header;
|
||||
use kanidm_client::KanidmClient;
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_https_middleware_headers(rsclient: KanidmClient) {
|
||||
async fn test_https_middleware_headers(rsclient: &KanidmClient) {
|
||||
// We need to do manual reqwests here.
|
||||
let client = rsclient.client();
|
||||
|
||||
// here we test the /ui/ endpoint which should have the headers
|
||||
let response = match reqwest::get(rsclient.make_url("/ui")).await {
|
||||
let response = match client.get(rsclient.make_url("/ui")).send().await {
|
||||
Ok(value) => value,
|
||||
Err(error) => {
|
||||
panic!(
|
||||
|
@ -19,19 +21,15 @@ async fn test_https_middleware_headers(rsclient: KanidmClient) {
|
|||
assert_eq!(response.status(), 200);
|
||||
eprintln!(
|
||||
"csp headers: {:#?}",
|
||||
response
|
||||
.headers()
|
||||
.get(http::header::CONTENT_SECURITY_POLICY)
|
||||
response.headers().get(header::CONTENT_SECURITY_POLICY)
|
||||
);
|
||||
assert_ne!(
|
||||
response
|
||||
.headers()
|
||||
.get(http::header::CONTENT_SECURITY_POLICY),
|
||||
response.headers().get(header::CONTENT_SECURITY_POLICY),
|
||||
None
|
||||
);
|
||||
|
||||
// here we test the /ui/login endpoint which should have the headers
|
||||
let response = match reqwest::get(rsclient.make_url("/ui/login")).await {
|
||||
let response = match client.get(rsclient.make_url("/ui/login")).send().await {
|
||||
Ok(value) => value,
|
||||
Err(error) => {
|
||||
panic!(
|
||||
|
@ -46,14 +44,10 @@ async fn test_https_middleware_headers(rsclient: KanidmClient) {
|
|||
|
||||
eprintln!(
|
||||
"csp headers: {:#?}",
|
||||
response
|
||||
.headers()
|
||||
.get(http::header::CONTENT_SECURITY_POLICY)
|
||||
response.headers().get(header::CONTENT_SECURITY_POLICY)
|
||||
);
|
||||
assert_ne!(
|
||||
response
|
||||
.headers()
|
||||
.get(http::header::CONTENT_SECURITY_POLICY),
|
||||
response.headers().get(header::CONTENT_SECURITY_POLICY),
|
||||
None
|
||||
);
|
||||
}
|
|
@ -1,11 +1,9 @@
|
|||
use core::result::Result::Err;
|
||||
use kanidm_client::KanidmClient;
|
||||
use kanidm_client::{KanidmClient, StatusCode};
|
||||
use kanidm_proto::internal::OperationError;
|
||||
use kanidm_proto::internal::{IdentifyUserRequest, IdentifyUserResponse};
|
||||
|
||||
use kanidmd_lib::prelude::Attribute;
|
||||
use kanidmd_testkit::ADMIN_TEST_PASSWORD;
|
||||
use reqwest::StatusCode;
|
||||
|
||||
static UNIVERSAL_PW: &str = "eicieY7ahchaoCh0eeTa";
|
||||
|
||||
|
@ -17,7 +15,7 @@ static USER_B_NAME: &str = "valid_user_b";
|
|||
// These tests check that invalid requests return the expected error
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_not_authenticated(rsclient: KanidmClient) {
|
||||
async fn test_not_authenticated(rsclient: &KanidmClient) {
|
||||
// basically here we try a bit of all the possible combinations while unauthenticated to check it's not working
|
||||
setup_server(&rsclient).await;
|
||||
create_user(&rsclient, USER_A_NAME).await;
|
||||
|
@ -26,14 +24,14 @@ async fn test_not_authenticated(rsclient: KanidmClient) {
|
|||
.idm_person_identify_user(USER_A_NAME, IdentifyUserRequest::Start)
|
||||
.await;
|
||||
assert!(
|
||||
matches!(res, Err(err) if matches!(err, kanidm_client::ClientError::Http(reqwest::StatusCode::UNAUTHORIZED, ..)))
|
||||
matches!(res, Err(err) if matches!(err, kanidm_client::ClientError::Http(StatusCode::UNAUTHORIZED, ..)))
|
||||
);
|
||||
|
||||
let res = rsclient
|
||||
.idm_person_identify_user(USER_A_NAME, IdentifyUserRequest::DisplayCode)
|
||||
.await;
|
||||
assert!(
|
||||
matches!(res, Err(err) if matches!(err, kanidm_client::ClientError::Http(reqwest::StatusCode::UNAUTHORIZED, ..)))
|
||||
matches!(res, Err(err) if matches!(err, kanidm_client::ClientError::Http(StatusCode::UNAUTHORIZED, ..)))
|
||||
);
|
||||
let res = rsclient
|
||||
.idm_person_identify_user(
|
||||
|
@ -43,12 +41,12 @@ async fn test_not_authenticated(rsclient: KanidmClient) {
|
|||
.await;
|
||||
|
||||
assert!(
|
||||
matches!(res, Err(err) if matches!(err, kanidm_client::ClientError::Http(reqwest::StatusCode::UNAUTHORIZED, ..)))
|
||||
matches!(res, Err(err) if matches!(err, kanidm_client::ClientError::Http(StatusCode::UNAUTHORIZED, ..)))
|
||||
);
|
||||
}
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_non_existing_user_id(rsclient: KanidmClient) {
|
||||
async fn test_non_existing_user_id(rsclient: &KanidmClient) {
|
||||
setup_server(&rsclient).await;
|
||||
create_user(&rsclient, USER_A_NAME).await;
|
||||
create_user(&rsclient, USER_B_NAME).await;
|
||||
|
@ -88,7 +86,7 @@ async fn test_non_existing_user_id(rsclient: KanidmClient) {
|
|||
// error cases have already been tested in the previous section!
|
||||
// Each tests is named like `test_{api input}_response_{expected api output}_or_{expected api output}`
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_start_response_identity_verification_available(rsclient: KanidmClient) {
|
||||
async fn test_start_response_identity_verification_available(rsclient: &KanidmClient) {
|
||||
setup_server(&rsclient).await;
|
||||
create_user(&rsclient, USER_A_NAME).await;
|
||||
login_with_user(&rsclient, USER_A_NAME).await;
|
||||
|
@ -107,7 +105,7 @@ async fn test_start_response_identity_verification_available(rsclient: KanidmCli
|
|||
// this function tests both possible POSITIVE outcomes if we start from
|
||||
// `Start`, that is WaitForCode or ProvideCode
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_start_response_wait_for_code_or_provide_code(rsclient: KanidmClient) {
|
||||
async fn test_start_response_wait_for_code_or_provide_code(rsclient: &KanidmClient) {
|
||||
setup_server(&rsclient).await;
|
||||
let user_a_uuid = create_user(&rsclient, USER_A_NAME).await;
|
||||
let user_b_uuid = create_user(&rsclient, USER_B_NAME).await;
|
||||
|
@ -131,7 +129,7 @@ async fn test_start_response_wait_for_code_or_provide_code(rsclient: KanidmClien
|
|||
}
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_provide_code_response_code_failure_or_provide_code(rsclient: KanidmClient) {
|
||||
async fn test_provide_code_response_code_failure_or_provide_code(rsclient: &KanidmClient) {
|
||||
setup_server(&rsclient).await;
|
||||
let user_a_uuid = create_user(&rsclient, USER_A_NAME).await;
|
||||
let user_b_uuid = create_user(&rsclient, USER_B_NAME).await;
|
||||
|
@ -159,7 +157,7 @@ async fn test_provide_code_response_code_failure_or_provide_code(rsclient: Kanid
|
|||
|
||||
// here we actually test the full idm flow by duplicating the server
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_full_identification_flow(rsclient: KanidmClient) {
|
||||
async fn test_full_identification_flow(rsclient: &KanidmClient) {
|
||||
setup_server(&rsclient).await;
|
||||
let user_a_uuid = create_user(&rsclient, USER_A_NAME).await;
|
||||
let user_b_uuid = create_user(&rsclient, USER_B_NAME).await;
|
||||
|
@ -177,12 +175,12 @@ async fn test_full_identification_flow(rsclient: KanidmClient) {
|
|||
(
|
||||
valid_user_a_client,
|
||||
USER_A_NAME,
|
||||
valid_user_b_client,
|
||||
&valid_user_b_client,
|
||||
USER_B_NAME,
|
||||
)
|
||||
} else {
|
||||
(
|
||||
valid_user_b_client,
|
||||
&valid_user_b_client,
|
||||
USER_B_NAME,
|
||||
valid_user_a_client,
|
||||
USER_A_NAME,
|
|
@ -66,7 +66,7 @@ async fn get_webdriver_client() -> fantoccini::Client {
|
|||
|
||||
#[kanidmd_testkit::test]
|
||||
#[cfg(feature = "webdriver")]
|
||||
async fn test_webdriver_user_login(rsclient: kanidm_client::KanidmClient) {
|
||||
async fn test_webdriver_user_login(rsclient: &KanidmClient) {
|
||||
if !cfg!(feature = "webdriver") {
|
||||
println!("Skipping test as webdriver feature is not enabled!");
|
||||
return;
|
||||
|
@ -206,7 +206,7 @@ async fn test_webdriver_user_login(rsclient: kanidm_client::KanidmClient) {
|
|||
}
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_domain_reset_token_key(rsclient: KanidmClient) {
|
||||
async fn test_domain_reset_token_key(rsclient: &KanidmClient) {
|
||||
login_put_admin_idm_admins(&rsclient).await;
|
||||
|
||||
let token = rsclient.get_token().await.expect("No bearer token present");
|
||||
|
@ -219,7 +219,7 @@ async fn test_domain_reset_token_key(rsclient: KanidmClient) {
|
|||
}
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_idm_domain_set_ldap_basedn(rsclient: KanidmClient) {
|
||||
async fn test_idm_domain_set_ldap_basedn(rsclient: &KanidmClient) {
|
||||
login_put_admin_idm_admins(&rsclient).await;
|
||||
assert!(rsclient
|
||||
.idm_domain_set_ldap_basedn("dc=krabsarekool,dc=example,dc=com")
|
||||
|
@ -232,7 +232,7 @@ async fn test_idm_domain_set_ldap_basedn(rsclient: KanidmClient) {
|
|||
}
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_idm_domain_set_ldap_max_queryable_attrs(rsclient: KanidmClient) {
|
||||
async fn test_idm_domain_set_ldap_max_queryable_attrs(rsclient: &KanidmClient) {
|
||||
login_put_admin_idm_admins(&rsclient).await;
|
||||
assert!(rsclient
|
||||
.idm_domain_set_ldap_max_queryable_attrs(20)
|
||||
|
@ -246,7 +246,7 @@ async fn test_idm_domain_set_ldap_max_queryable_attrs(rsclient: KanidmClient) {
|
|||
|
||||
#[kanidmd_testkit::test]
|
||||
/// Checks that a built-in group idm_all_persons has the "builtin" class as expected.
|
||||
async fn test_all_persons_has_builtin_class(rsclient: KanidmClient) {
|
||||
async fn test_all_persons_has_builtin_class(rsclient: &KanidmClient) {
|
||||
login_put_admin_idm_admins(&rsclient).await;
|
||||
let res = rsclient
|
||||
.idm_group_get("idm_all_persons")
|
16
server/testkit/tests/testkit/ldap_basic.rs
Normal file
16
server/testkit/tests/testkit/ldap_basic.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
use kanidmd_testkit::AsyncTestEnvironment;
|
||||
use ldap3_client::LdapClientBuilder;
|
||||
|
||||
#[kanidmd_testkit::test(ldap = true)]
|
||||
async fn test_ldap_basic_unix_bind(test_env: &AsyncTestEnvironment) {
|
||||
let ldap_url = test_env.ldap_url.as_ref().unwrap();
|
||||
|
||||
let mut ldap_client = LdapClientBuilder::new(ldap_url).build().await.unwrap();
|
||||
|
||||
// Bind as anonymous
|
||||
ldap_client.bind("".into(), "".into()).await.unwrap();
|
||||
|
||||
let whoami = ldap_client.whoami().await.unwrap();
|
||||
|
||||
assert_eq!(whoami, Some("u: anonymous@localhost".to_string()));
|
||||
}
|
17
server/testkit/tests/testkit/mod.rs
Normal file
17
server/testkit/tests/testkit/mod.rs
Normal file
|
@ -0,0 +1,17 @@
|
|||
mod apidocs;
|
||||
mod domain;
|
||||
mod group;
|
||||
mod http_manifest;
|
||||
mod https_extractors;
|
||||
mod https_middleware;
|
||||
mod identity_verification_tests;
|
||||
mod integration;
|
||||
mod ldap_basic;
|
||||
mod mtls_test;
|
||||
mod oauth2_test;
|
||||
mod person;
|
||||
mod proto_v1_test;
|
||||
mod scim_test;
|
||||
mod service_account;
|
||||
mod system;
|
||||
mod unix;
|
|
@ -12,14 +12,14 @@ use kanidm_proto::oauth2::{
|
|||
AccessTokenResponse, AccessTokenType, AuthorisationResponse, GrantTypeReq,
|
||||
OidcDiscoveryResponse,
|
||||
};
|
||||
use kanidmd_lib::prelude::{Attribute, IDM_ALL_ACCOUNTS};
|
||||
use kanidmd_lib::constants::NAME_IDM_ALL_ACCOUNTS;
|
||||
use kanidmd_lib::prelude::Attribute;
|
||||
use oauth2_ext::PkceCodeChallenge;
|
||||
use reqwest::header::{HeaderValue, CONTENT_TYPE};
|
||||
use reqwest::StatusCode;
|
||||
use uri::{OAUTH2_TOKEN_ENDPOINT, OAUTH2_TOKEN_INTROSPECT_ENDPOINT, OAUTH2_TOKEN_REVOKE_ENDPOINT};
|
||||
use url::{form_urlencoded::parse as query_parse, Url};
|
||||
|
||||
use kanidm_client::KanidmClient;
|
||||
use kanidm_client::{http::header, KanidmClient, StatusCode};
|
||||
use kanidmd_testkit::{
|
||||
assert_no_cache, ADMIN_TEST_PASSWORD, ADMIN_TEST_USER, NOT_ADMIN_TEST_EMAIL,
|
||||
NOT_ADMIN_TEST_PASSWORD, NOT_ADMIN_TEST_USERNAME, TEST_INTEGRATION_RS_DISPLAY,
|
||||
|
@ -40,7 +40,7 @@ use kanidmd_testkit::{
|
|||
/// If `true`, use the `code` passed in the callback URI's fragment, and
|
||||
/// require the query parameter to be empty.
|
||||
async fn test_oauth2_openid_basic_flow_impl(
|
||||
rsclient: KanidmClient,
|
||||
rsclient: &KanidmClient,
|
||||
response_mode: Option<&str>,
|
||||
response_in_fragment: bool,
|
||||
) {
|
||||
|
@ -98,7 +98,7 @@ async fn test_oauth2_openid_basic_flow_impl(
|
|||
rsclient
|
||||
.idm_oauth2_rs_update_scope_map(
|
||||
TEST_INTEGRATION_RS_ID,
|
||||
IDM_ALL_ACCOUNTS.name,
|
||||
NAME_IDM_ALL_ACCOUNTS,
|
||||
vec![OAUTH2_SCOPE_READ, OAUTH2_SCOPE_EMAIL, OAUTH2_SCOPE_OPENID],
|
||||
)
|
||||
.await
|
||||
|
@ -107,7 +107,7 @@ async fn test_oauth2_openid_basic_flow_impl(
|
|||
rsclient
|
||||
.idm_oauth2_rs_update_sup_scope_map(
|
||||
TEST_INTEGRATION_RS_ID,
|
||||
IDM_ALL_ACCOUNTS.name,
|
||||
NAME_IDM_ALL_ACCOUNTS,
|
||||
vec![ADMIN_TEST_USER],
|
||||
)
|
||||
.await
|
||||
|
@ -136,6 +136,7 @@ async fn test_oauth2_openid_basic_flow_impl(
|
|||
// from here, we can now begin what would be a "interaction" to the oauth server.
|
||||
// Create a new reqwest client - we'll be using this manually.
|
||||
let client = reqwest::Client::builder()
|
||||
.tls_built_in_native_certs(false)
|
||||
.redirect(reqwest::redirect::Policy::none())
|
||||
.no_proxy()
|
||||
.build()
|
||||
|
@ -151,11 +152,11 @@ async fn test_oauth2_openid_basic_flow_impl(
|
|||
.await
|
||||
.expect("Failed to send discovery preflight request.");
|
||||
|
||||
assert_eq!(response.status(), reqwest::StatusCode::OK);
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
|
||||
let cors_header: &str = response
|
||||
.headers()
|
||||
.get(http::header::ACCESS_CONTROL_ALLOW_ORIGIN)
|
||||
.get(header::ACCESS_CONTROL_ALLOW_ORIGIN)
|
||||
.expect("missing access-control-allow-origin header")
|
||||
.to_str()
|
||||
.expect("invalid access-control-allow-origin header");
|
||||
|
@ -167,12 +168,12 @@ async fn test_oauth2_openid_basic_flow_impl(
|
|||
.await
|
||||
.expect("Failed to send request.");
|
||||
|
||||
assert_eq!(response.status(), reqwest::StatusCode::OK);
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
|
||||
// Assert CORS on the GET too.
|
||||
let cors_header: &str = response
|
||||
.headers()
|
||||
.get(http::header::ACCESS_CONTROL_ALLOW_ORIGIN)
|
||||
.get(header::ACCESS_CONTROL_ALLOW_ORIGIN)
|
||||
.expect("missing access-control-allow-origin header")
|
||||
.to_str()
|
||||
.expect("invalid access-control-allow-origin header");
|
||||
|
@ -220,7 +221,7 @@ async fn test_oauth2_openid_basic_flow_impl(
|
|||
.await
|
||||
.expect("Failed to send request.");
|
||||
|
||||
assert_eq!(response.status(), reqwest::StatusCode::OK);
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_no_cache!(response);
|
||||
|
||||
let mut jwk_set: JwkKeySet = response
|
||||
|
@ -263,7 +264,7 @@ async fn test_oauth2_openid_basic_flow_impl(
|
|||
.await
|
||||
.expect("Failed to send request.");
|
||||
|
||||
assert_eq!(response.status(), reqwest::StatusCode::OK);
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_no_cache!(response);
|
||||
|
||||
let consent_req: AuthorisationResponse = response
|
||||
|
@ -297,7 +298,7 @@ async fn test_oauth2_openid_basic_flow_impl(
|
|||
.expect("Failed to send request.");
|
||||
|
||||
// This should yield a 302 redirect with some query params.
|
||||
assert_eq!(response.status(), reqwest::StatusCode::FOUND);
|
||||
assert_eq!(response.status(), StatusCode::FOUND);
|
||||
assert_no_cache!(response);
|
||||
|
||||
// And we should have a URL in the location header.
|
||||
|
@ -342,11 +343,11 @@ async fn test_oauth2_openid_basic_flow_impl(
|
|||
.await
|
||||
.expect("Failed to send code exchange request.");
|
||||
|
||||
assert_eq!(response.status(), reqwest::StatusCode::OK);
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
|
||||
let cors_header: &str = response
|
||||
.headers()
|
||||
.get(http::header::ACCESS_CONTROL_ALLOW_ORIGIN)
|
||||
.get(header::ACCESS_CONTROL_ALLOW_ORIGIN)
|
||||
.expect("missing access-control-allow-origin header")
|
||||
.to_str()
|
||||
.expect("invalid access-control-allow-origin header");
|
||||
|
@ -378,7 +379,7 @@ async fn test_oauth2_openid_basic_flow_impl(
|
|||
.await
|
||||
.expect("Failed to send token introspect request.");
|
||||
|
||||
assert_eq!(response.status(), reqwest::StatusCode::OK);
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
tracing::trace!("{:?}", response.headers());
|
||||
assert!(
|
||||
response.headers().get(CONTENT_TYPE) == Some(&HeaderValue::from_static(APPLICATION_JSON))
|
||||
|
@ -484,7 +485,7 @@ async fn test_oauth2_openid_basic_flow_impl(
|
|||
.await
|
||||
.expect("Failed to send client credentials request.");
|
||||
|
||||
assert_eq!(response.status(), reqwest::StatusCode::OK);
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
|
||||
let atr = response
|
||||
.json::<AccessTokenResponse>()
|
||||
|
@ -505,7 +506,7 @@ async fn test_oauth2_openid_basic_flow_impl(
|
|||
.await
|
||||
.expect("Failed to send token introspect request.");
|
||||
|
||||
assert_eq!(response.status(), reqwest::StatusCode::OK);
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
|
||||
let tir = response
|
||||
.json::<AccessTokenIntrospectResponse>()
|
||||
|
@ -534,7 +535,7 @@ async fn test_oauth2_openid_basic_flow_impl(
|
|||
///
|
||||
/// The response should be returned as a query parameter.
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_oauth2_openid_basic_flow_mode_unset(rsclient: KanidmClient) {
|
||||
async fn test_oauth2_openid_basic_flow_mode_unset(rsclient: &KanidmClient) {
|
||||
test_oauth2_openid_basic_flow_impl(rsclient, None, false).await;
|
||||
}
|
||||
|
||||
|
@ -543,7 +544,7 @@ async fn test_oauth2_openid_basic_flow_mode_unset(rsclient: KanidmClient) {
|
|||
///
|
||||
/// The response should be returned as a query parameter.
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_oauth2_openid_basic_flow_mode_query(rsclient: KanidmClient) {
|
||||
async fn test_oauth2_openid_basic_flow_mode_query(rsclient: &KanidmClient) {
|
||||
test_oauth2_openid_basic_flow_impl(rsclient, Some("query"), false).await;
|
||||
}
|
||||
|
||||
|
@ -552,7 +553,7 @@ async fn test_oauth2_openid_basic_flow_mode_query(rsclient: KanidmClient) {
|
|||
///
|
||||
/// The response should be returned in the URI's fragment.
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_oauth2_openid_basic_flow_mode_fragment(rsclient: KanidmClient) {
|
||||
async fn test_oauth2_openid_basic_flow_mode_fragment(rsclient: &KanidmClient) {
|
||||
test_oauth2_openid_basic_flow_impl(rsclient, Some("fragment"), true).await;
|
||||
}
|
||||
|
||||
|
@ -569,7 +570,7 @@ async fn test_oauth2_openid_basic_flow_mode_fragment(rsclient: KanidmClient) {
|
|||
/// If `true`, use the `code` passed in the callback URI's fragment, and
|
||||
/// require the query parameter to be empty.
|
||||
async fn test_oauth2_openid_public_flow_impl(
|
||||
rsclient: KanidmClient,
|
||||
rsclient: &KanidmClient,
|
||||
response_mode: Option<&str>,
|
||||
response_in_fragment: bool,
|
||||
) {
|
||||
|
@ -627,7 +628,7 @@ async fn test_oauth2_openid_public_flow_impl(
|
|||
rsclient
|
||||
.idm_oauth2_rs_update_scope_map(
|
||||
TEST_INTEGRATION_RS_ID,
|
||||
IDM_ALL_ACCOUNTS.name,
|
||||
NAME_IDM_ALL_ACCOUNTS,
|
||||
vec![OAUTH2_SCOPE_READ, OAUTH2_SCOPE_EMAIL, OAUTH2_SCOPE_OPENID],
|
||||
)
|
||||
.await
|
||||
|
@ -636,7 +637,7 @@ async fn test_oauth2_openid_public_flow_impl(
|
|||
rsclient
|
||||
.idm_oauth2_rs_update_sup_scope_map(
|
||||
TEST_INTEGRATION_RS_ID,
|
||||
IDM_ALL_ACCOUNTS.name,
|
||||
NAME_IDM_ALL_ACCOUNTS,
|
||||
vec![ADMIN_TEST_USER],
|
||||
)
|
||||
.await
|
||||
|
@ -647,7 +648,7 @@ async fn test_oauth2_openid_public_flow_impl(
|
|||
.idm_oauth2_rs_update_claim_map(
|
||||
TEST_INTEGRATION_RS_ID,
|
||||
"test_claim",
|
||||
IDM_ALL_ACCOUNTS.name,
|
||||
NAME_IDM_ALL_ACCOUNTS,
|
||||
&["claim_a".to_string(), "claim_b".to_string()],
|
||||
)
|
||||
.await
|
||||
|
@ -680,6 +681,7 @@ async fn test_oauth2_openid_public_flow_impl(
|
|||
// Create a new reqwest client - we'll be using this manually.
|
||||
let client = reqwest::Client::builder()
|
||||
.redirect(reqwest::redirect::Policy::none())
|
||||
.tls_built_in_native_certs(false)
|
||||
.no_proxy()
|
||||
.build()
|
||||
.expect("Failed to create client.");
|
||||
|
@ -691,7 +693,7 @@ async fn test_oauth2_openid_public_flow_impl(
|
|||
.await
|
||||
.expect("Failed to send request.");
|
||||
|
||||
assert_eq!(response.status(), reqwest::StatusCode::OK);
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_no_cache!(response);
|
||||
|
||||
let mut jwk_set: JwkKeySet = response
|
||||
|
@ -732,7 +734,7 @@ async fn test_oauth2_openid_public_flow_impl(
|
|||
.await
|
||||
.expect("Failed to send request.");
|
||||
|
||||
assert_eq!(response.status(), reqwest::StatusCode::OK);
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_no_cache!(response);
|
||||
|
||||
let consent_req: AuthorisationResponse = response
|
||||
|
@ -764,7 +766,7 @@ async fn test_oauth2_openid_public_flow_impl(
|
|||
.expect("Failed to send request.");
|
||||
|
||||
// This should yield a 302 redirect with some query params.
|
||||
assert_eq!(response.status(), reqwest::StatusCode::FOUND);
|
||||
assert_eq!(response.status(), StatusCode::FOUND);
|
||||
assert_no_cache!(response);
|
||||
|
||||
// And we should have a URL in the location header.
|
||||
|
@ -812,7 +814,7 @@ async fn test_oauth2_openid_public_flow_impl(
|
|||
.await
|
||||
.expect("Failed to send code exchange request.");
|
||||
|
||||
assert_eq!(response.status(), reqwest::StatusCode::OK);
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
assert_no_cache!(response);
|
||||
|
||||
// The body is a json AccessTokenResponse
|
||||
|
@ -857,10 +859,10 @@ async fn test_oauth2_openid_public_flow_impl(
|
|||
.await
|
||||
.expect("Failed to send userinfo preflight request.");
|
||||
|
||||
assert_eq!(response.status(), reqwest::StatusCode::OK);
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
let cors_header: &str = response
|
||||
.headers()
|
||||
.get(http::header::ACCESS_CONTROL_ALLOW_ORIGIN)
|
||||
.get(header::ACCESS_CONTROL_ALLOW_ORIGIN)
|
||||
.expect("missing access-control-allow-origin header")
|
||||
.to_str()
|
||||
.expect("invalid access-control-allow-origin header");
|
||||
|
@ -899,7 +901,7 @@ async fn test_oauth2_openid_public_flow_impl(
|
|||
///
|
||||
/// The response should be returned as a query parameter.
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_oauth2_openid_public_flow_mode_unset(rsclient: KanidmClient) {
|
||||
async fn test_oauth2_openid_public_flow_mode_unset(rsclient: &KanidmClient) {
|
||||
test_oauth2_openid_public_flow_impl(rsclient, None, false).await;
|
||||
}
|
||||
|
||||
|
@ -908,7 +910,7 @@ async fn test_oauth2_openid_public_flow_mode_unset(rsclient: KanidmClient) {
|
|||
///
|
||||
/// The response should be returned as a query parameter.
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_oauth2_openid_public_flow_mode_query(rsclient: KanidmClient) {
|
||||
async fn test_oauth2_openid_public_flow_mode_query(rsclient: &KanidmClient) {
|
||||
test_oauth2_openid_public_flow_impl(rsclient, Some("query"), false).await;
|
||||
}
|
||||
|
||||
|
@ -917,12 +919,12 @@ async fn test_oauth2_openid_public_flow_mode_query(rsclient: KanidmClient) {
|
|||
///
|
||||
/// The response should be returned in the URI's fragment.
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_oauth2_openid_public_flow_mode_fragment(rsclient: KanidmClient) {
|
||||
async fn test_oauth2_openid_public_flow_mode_fragment(rsclient: &KanidmClient) {
|
||||
test_oauth2_openid_public_flow_impl(rsclient, Some("fragment"), true).await;
|
||||
}
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_oauth2_token_post_bad_bodies(rsclient: KanidmClient) {
|
||||
async fn test_oauth2_token_post_bad_bodies(rsclient: &KanidmClient) {
|
||||
let res = rsclient
|
||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
||||
.await;
|
||||
|
@ -930,6 +932,7 @@ async fn test_oauth2_token_post_bad_bodies(rsclient: KanidmClient) {
|
|||
|
||||
let client = reqwest::Client::builder()
|
||||
.redirect(reqwest::redirect::Policy::none())
|
||||
.tls_built_in_native_certs(false)
|
||||
.no_proxy()
|
||||
.build()
|
||||
.expect("Failed to create client.");
|
||||
|
@ -957,7 +960,7 @@ async fn test_oauth2_token_post_bad_bodies(rsclient: KanidmClient) {
|
|||
}
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_oauth2_token_revoke_post(rsclient: KanidmClient) {
|
||||
async fn test_oauth2_token_revoke_post(rsclient: &KanidmClient) {
|
||||
let res = rsclient
|
||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
||||
.await;
|
||||
|
@ -965,6 +968,7 @@ async fn test_oauth2_token_revoke_post(rsclient: KanidmClient) {
|
|||
|
||||
let client = reqwest::Client::builder()
|
||||
.redirect(reqwest::redirect::Policy::none())
|
||||
.tls_built_in_native_certs(false)
|
||||
.no_proxy()
|
||||
.build()
|
||||
.expect("Failed to create client.");
|
|
@ -1,10 +1,10 @@
|
|||
use kanidm_client::{ClientError, KanidmClient};
|
||||
use kanidm_client::{ClientError, KanidmClient, StatusCode};
|
||||
use kanidm_proto::constants::ATTR_MAIL;
|
||||
use kanidmd_testkit::{create_user, ADMIN_TEST_PASSWORD, ADMIN_TEST_USER};
|
||||
use serde_json::Value;
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_v1_person_id_patch(rsclient: KanidmClient) {
|
||||
async fn test_v1_person_id_patch(rsclient: &KanidmClient) {
|
||||
let res = rsclient
|
||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
||||
.await;
|
||||
|
@ -25,7 +25,7 @@ async fn test_v1_person_id_patch(rsclient: KanidmClient) {
|
|||
}
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_v1_person_id_ssh_pubkeys_post(rsclient: KanidmClient) {
|
||||
async fn test_v1_person_id_ssh_pubkeys_post(rsclient: &KanidmClient) {
|
||||
let res = rsclient
|
||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
||||
.await;
|
||||
|
@ -48,6 +48,6 @@ async fn test_v1_person_id_ssh_pubkeys_post(rsclient: KanidmClient) {
|
|||
eprintln!("response: {:#?}", response);
|
||||
assert!(matches!(
|
||||
response,
|
||||
ClientError::Http(reqwest::StatusCode::BAD_REQUEST, _, _)
|
||||
ClientError::Http(StatusCode::BAD_REQUEST, _, _)
|
||||
));
|
||||
}
|
|
@ -3,6 +3,7 @@ use std::path::Path;
|
|||
use std::time::SystemTime;
|
||||
|
||||
use kanidm_proto::constants::{ATTR_GIDNUMBER, KSESSIONID};
|
||||
|
||||
use kanidm_proto::internal::{
|
||||
ApiToken, CURegState, Filter, ImageValue, Modify, ModifyList, UatPurpose, UserAuthToken,
|
||||
};
|
||||
|
@ -10,10 +11,10 @@ use kanidm_proto::v1::{
|
|||
AuthCredential, AuthIssueSession, AuthMech, AuthRequest, AuthResponse, AuthState, AuthStep,
|
||||
Entry,
|
||||
};
|
||||
use kanidmd_lib::constants::{NAME_IDM_ADMINS, NAME_SYSTEM_ADMINS};
|
||||
use kanidmd_lib::credential::totp::Totp;
|
||||
use kanidmd_lib::prelude::{
|
||||
Attribute, BUILTIN_GROUP_IDM_ADMINS_V1, BUILTIN_GROUP_SYSTEM_ADMINS_V1,
|
||||
};
|
||||
|
||||
use kanidmd_lib::prelude::Attribute;
|
||||
use tracing::{debug, trace};
|
||||
|
||||
use std::str::FromStr;
|
||||
|
@ -28,7 +29,7 @@ use kanidmd_testkit::{ADMIN_TEST_PASSWORD, ADMIN_TEST_USER};
|
|||
const UNIX_TEST_PASSWORD: &str = "unix test user password";
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_server_create(rsclient: KanidmClient) {
|
||||
async fn test_server_create(rsclient: &KanidmClient) {
|
||||
let e: Entry = serde_json::from_str(
|
||||
r#"{
|
||||
"attrs": {
|
||||
|
@ -54,7 +55,7 @@ async fn test_server_create(rsclient: KanidmClient) {
|
|||
}
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_server_whoami_anonymous(rsclient: KanidmClient) {
|
||||
async fn test_server_whoami_anonymous(rsclient: &KanidmClient) {
|
||||
// First show we are un-authenticated.
|
||||
let pre_res = rsclient.whoami().await;
|
||||
// This means it was okay whoami, but no uat attached.
|
||||
|
@ -83,7 +84,7 @@ async fn test_server_whoami_anonymous(rsclient: KanidmClient) {
|
|||
}
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_server_whoami_admin_simple_password(rsclient: KanidmClient) {
|
||||
async fn test_server_whoami_admin_simple_password(rsclient: &KanidmClient) {
|
||||
// First show we are un-authenticated.
|
||||
let pre_res = rsclient.whoami().await;
|
||||
// This means it was okay whoami, but no uat attached.
|
||||
|
@ -108,7 +109,7 @@ async fn test_server_whoami_admin_simple_password(rsclient: KanidmClient) {
|
|||
}
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_server_search(rsclient: KanidmClient) {
|
||||
async fn test_server_search(rsclient: &KanidmClient) {
|
||||
// First show we are un-authenticated.
|
||||
let pre_res = rsclient.whoami().await;
|
||||
// This means it was okay whoami, but no uat attached.
|
||||
|
@ -134,7 +135,7 @@ async fn test_server_search(rsclient: KanidmClient) {
|
|||
|
||||
// test the rest group endpoint.
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_server_rest_group_read(rsclient: KanidmClient) {
|
||||
async fn test_server_rest_group_read(rsclient: &KanidmClient) {
|
||||
let res = rsclient
|
||||
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||
.await;
|
||||
|
@ -144,16 +145,13 @@ async fn test_server_rest_group_read(rsclient: KanidmClient) {
|
|||
let g_list = rsclient.idm_group_list().await.unwrap();
|
||||
assert!(!g_list.is_empty());
|
||||
|
||||
let g = rsclient
|
||||
.idm_group_get(BUILTIN_GROUP_IDM_ADMINS_V1.name)
|
||||
.await
|
||||
.unwrap();
|
||||
let g = rsclient.idm_group_get(NAME_IDM_ADMINS).await.unwrap();
|
||||
assert!(g.is_some());
|
||||
println!("{:?}", g);
|
||||
}
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_server_rest_group_lifecycle(rsclient: KanidmClient) {
|
||||
async fn test_server_rest_group_lifecycle(rsclient: &KanidmClient) {
|
||||
let res = rsclient
|
||||
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||
.await;
|
||||
|
@ -165,7 +163,7 @@ async fn test_server_rest_group_lifecycle(rsclient: KanidmClient) {
|
|||
|
||||
// Create a new group
|
||||
rsclient
|
||||
.idm_group_create("demo_group", Some(BUILTIN_GROUP_IDM_ADMINS_V1.name))
|
||||
.idm_group_create("demo_group", Some(NAME_IDM_ADMINS))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -245,16 +243,13 @@ async fn test_server_rest_group_lifecycle(rsclient: KanidmClient) {
|
|||
assert_eq!(g_list_3.len(), g_list.len());
|
||||
|
||||
// Check we can get an exact group
|
||||
let g = rsclient
|
||||
.idm_group_get(BUILTIN_GROUP_IDM_ADMINS_V1.name)
|
||||
.await
|
||||
.unwrap();
|
||||
let g = rsclient.idm_group_get(NAME_IDM_ADMINS).await.unwrap();
|
||||
assert!(g.is_some());
|
||||
println!("{:?}", g);
|
||||
|
||||
// They should have members
|
||||
let members = rsclient
|
||||
.idm_group_get_members(BUILTIN_GROUP_IDM_ADMINS_V1.name)
|
||||
.idm_group_get_members(NAME_IDM_ADMINS)
|
||||
.await
|
||||
.unwrap();
|
||||
println!("{:?}", members);
|
||||
|
@ -268,7 +263,7 @@ async fn test_server_rest_group_lifecycle(rsclient: KanidmClient) {
|
|||
}
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_server_rest_account_read(rsclient: KanidmClient) {
|
||||
async fn test_server_rest_account_read(rsclient: &KanidmClient) {
|
||||
let res = rsclient
|
||||
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||
.await;
|
||||
|
@ -284,7 +279,7 @@ async fn test_server_rest_account_read(rsclient: KanidmClient) {
|
|||
}
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_server_rest_schema_read(rsclient: KanidmClient) {
|
||||
async fn test_server_rest_schema_read(rsclient: &KanidmClient) {
|
||||
let res = rsclient
|
||||
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||
.await;
|
||||
|
@ -318,7 +313,7 @@ async fn test_server_rest_schema_read(rsclient: KanidmClient) {
|
|||
|
||||
// Test resetting a radius cred, and then checking/viewing it.
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_server_radius_credential_lifecycle(rsclient: KanidmClient) {
|
||||
async fn test_server_radius_credential_lifecycle(rsclient: &KanidmClient) {
|
||||
let res = rsclient
|
||||
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||
.await;
|
||||
|
@ -326,7 +321,7 @@ async fn test_server_radius_credential_lifecycle(rsclient: KanidmClient) {
|
|||
|
||||
// All admin to create persons.
|
||||
rsclient
|
||||
.idm_group_add_members(BUILTIN_GROUP_IDM_ADMINS_V1.name, &["admin"])
|
||||
.idm_group_add_members(NAME_IDM_ADMINS, &["admin"])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -389,7 +384,7 @@ async fn test_server_radius_credential_lifecycle(rsclient: KanidmClient) {
|
|||
}
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_server_rest_person_account_lifecycle(rsclient: KanidmClient) {
|
||||
async fn test_server_rest_person_account_lifecycle(rsclient: &KanidmClient) {
|
||||
let res = rsclient
|
||||
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||
.await;
|
||||
|
@ -397,7 +392,7 @@ async fn test_server_rest_person_account_lifecycle(rsclient: KanidmClient) {
|
|||
// To enable the admin to actually make some of these changes, we have
|
||||
// to make them a people admin. NOT recommended in production!
|
||||
rsclient
|
||||
.idm_group_add_members(BUILTIN_GROUP_IDM_ADMINS_V1.name, &["admin"])
|
||||
.idm_group_add_members(NAME_IDM_ADMINS, &["admin"])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -444,7 +439,7 @@ async fn test_server_rest_person_account_lifecycle(rsclient: KanidmClient) {
|
|||
}
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_server_rest_sshkey_lifecycle(rsclient: KanidmClient) {
|
||||
async fn test_server_rest_sshkey_lifecycle(rsclient: &KanidmClient) {
|
||||
let res = rsclient
|
||||
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||
.await;
|
||||
|
@ -514,7 +509,7 @@ async fn test_server_rest_sshkey_lifecycle(rsclient: KanidmClient) {
|
|||
}
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_server_rest_domain_lifecycle(rsclient: KanidmClient) {
|
||||
async fn test_server_rest_domain_lifecycle(rsclient: &KanidmClient) {
|
||||
let res = rsclient
|
||||
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||
.await;
|
||||
|
@ -544,14 +539,14 @@ async fn test_server_rest_domain_lifecycle(rsclient: KanidmClient) {
|
|||
}
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_server_rest_posix_lifecycle(rsclient: KanidmClient) {
|
||||
async fn test_server_rest_posix_lifecycle(rsclient: &KanidmClient) {
|
||||
let res = rsclient
|
||||
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||
.await;
|
||||
assert!(res.is_ok());
|
||||
// Not recommended in production!
|
||||
rsclient
|
||||
.idm_group_add_members(BUILTIN_GROUP_IDM_ADMINS_V1.name, &["admin"])
|
||||
.idm_group_add_members(NAME_IDM_ADMINS, &["admin"])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -571,7 +566,7 @@ async fn test_server_rest_posix_lifecycle(rsclient: KanidmClient) {
|
|||
|
||||
// Extend the group with posix attrs
|
||||
rsclient
|
||||
.idm_group_create("posix_group", Some(BUILTIN_GROUP_IDM_ADMINS_V1.name))
|
||||
.idm_group_create("posix_group", Some(NAME_IDM_ADMINS))
|
||||
.await
|
||||
.unwrap();
|
||||
rsclient
|
||||
|
@ -665,7 +660,7 @@ async fn test_server_rest_posix_lifecycle(rsclient: KanidmClient) {
|
|||
}
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_server_rest_posix_auth_lifecycle(rsclient: KanidmClient) {
|
||||
async fn test_server_rest_posix_auth_lifecycle(rsclient: &KanidmClient) {
|
||||
let res = rsclient
|
||||
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||
.await;
|
||||
|
@ -676,7 +671,7 @@ async fn test_server_rest_posix_auth_lifecycle(rsclient: KanidmClient) {
|
|||
|
||||
// Not recommended in production!
|
||||
rsclient
|
||||
.idm_group_add_members(BUILTIN_GROUP_IDM_ADMINS_V1.name, &["admin"])
|
||||
.idm_group_add_members(NAME_IDM_ADMINS, &["admin"])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -765,7 +760,7 @@ async fn test_server_rest_posix_auth_lifecycle(rsclient: KanidmClient) {
|
|||
}
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_server_rest_recycle_lifecycle(rsclient: KanidmClient) {
|
||||
async fn test_server_rest_recycle_lifecycle(rsclient: &KanidmClient) {
|
||||
let res = rsclient
|
||||
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||
.await;
|
||||
|
@ -773,7 +768,7 @@ async fn test_server_rest_recycle_lifecycle(rsclient: KanidmClient) {
|
|||
|
||||
// Not recommended in production!
|
||||
rsclient
|
||||
.idm_group_add_members(BUILTIN_GROUP_IDM_ADMINS_V1.name, &["admin"])
|
||||
.idm_group_add_members(NAME_IDM_ADMINS, &["admin"])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -819,14 +814,14 @@ async fn test_server_rest_recycle_lifecycle(rsclient: KanidmClient) {
|
|||
}
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_server_rest_oauth2_basic_lifecycle(rsclient: KanidmClient) {
|
||||
async fn test_server_rest_oauth2_basic_lifecycle(rsclient: &KanidmClient) {
|
||||
let res = rsclient
|
||||
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||
.await;
|
||||
assert!(res.is_ok());
|
||||
|
||||
rsclient
|
||||
.idm_group_add_members(BUILTIN_GROUP_IDM_ADMINS_V1.name, &["admin"])
|
||||
.idm_group_add_members(NAME_IDM_ADMINS, &["admin"])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -902,11 +897,7 @@ async fn test_server_rest_oauth2_basic_lifecycle(rsclient: KanidmClient) {
|
|||
|
||||
// Check that we can add scope maps and delete them.
|
||||
rsclient
|
||||
.idm_oauth2_rs_update_scope_map(
|
||||
"test_integration",
|
||||
BUILTIN_GROUP_SYSTEM_ADMINS_V1.name,
|
||||
vec!["a", "b"],
|
||||
)
|
||||
.idm_oauth2_rs_update_scope_map("test_integration", NAME_SYSTEM_ADMINS, vec!["a", "b"])
|
||||
.await
|
||||
.expect("Failed to create scope map");
|
||||
|
||||
|
@ -921,11 +912,7 @@ async fn test_server_rest_oauth2_basic_lifecycle(rsclient: KanidmClient) {
|
|||
|
||||
// Check we can update a scope map
|
||||
rsclient
|
||||
.idm_oauth2_rs_update_scope_map(
|
||||
"test_integration",
|
||||
BUILTIN_GROUP_SYSTEM_ADMINS_V1.name,
|
||||
vec!["a", "b", "c"],
|
||||
)
|
||||
.idm_oauth2_rs_update_scope_map("test_integration", NAME_SYSTEM_ADMINS, vec!["a", "b", "c"])
|
||||
.await
|
||||
.expect("Failed to create scope map");
|
||||
|
||||
|
@ -955,9 +942,8 @@ async fn test_server_rest_oauth2_basic_lifecycle(rsclient: KanidmClient) {
|
|||
assert!(res.is_ok());
|
||||
|
||||
//test getting the image
|
||||
let client = reqwest::Client::new();
|
||||
|
||||
let response = client
|
||||
let response = rsclient
|
||||
.client()
|
||||
.get(rsclient.make_url("/ui/images/oauth2/test_integration"))
|
||||
.bearer_auth(rsclient.get_token().await.unwrap());
|
||||
|
||||
|
@ -1009,7 +995,7 @@ async fn test_server_rest_oauth2_basic_lifecycle(rsclient: KanidmClient) {
|
|||
// Check we can delete a scope map.
|
||||
|
||||
rsclient
|
||||
.idm_oauth2_rs_delete_scope_map("test_integration", BUILTIN_GROUP_SYSTEM_ADMINS_V1.name)
|
||||
.idm_oauth2_rs_delete_scope_map("test_integration", NAME_SYSTEM_ADMINS)
|
||||
.await
|
||||
.expect("Failed to delete scope map");
|
||||
|
||||
|
@ -1041,7 +1027,7 @@ async fn test_server_rest_oauth2_basic_lifecycle(rsclient: KanidmClient) {
|
|||
}
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_server_credential_update_session_pw(rsclient: KanidmClient) {
|
||||
async fn test_server_credential_update_session_pw(rsclient: &KanidmClient) {
|
||||
let res = rsclient
|
||||
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||
.await;
|
||||
|
@ -1049,7 +1035,7 @@ async fn test_server_credential_update_session_pw(rsclient: KanidmClient) {
|
|||
|
||||
// Not recommended in production!
|
||||
rsclient
|
||||
.idm_group_add_members(BUILTIN_GROUP_IDM_ADMINS_V1.name, &["admin"])
|
||||
.idm_group_add_members(NAME_IDM_ADMINS, &["admin"])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -1116,7 +1102,7 @@ async fn test_server_credential_update_session_pw(rsclient: KanidmClient) {
|
|||
}
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_server_credential_update_session_totp_pw(rsclient: KanidmClient) {
|
||||
async fn test_server_credential_update_session_totp_pw(rsclient: &KanidmClient) {
|
||||
let res = rsclient
|
||||
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||
.await;
|
||||
|
@ -1124,7 +1110,7 @@ async fn test_server_credential_update_session_totp_pw(rsclient: KanidmClient) {
|
|||
|
||||
// Not recommended in production!
|
||||
rsclient
|
||||
.idm_group_add_members(BUILTIN_GROUP_IDM_ADMINS_V1.name, &["admin"])
|
||||
.idm_group_add_members(NAME_IDM_ADMINS, &["admin"])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -1253,7 +1239,7 @@ async fn setup_demo_account_passkey(rsclient: &KanidmClient) -> WebauthnAuthenti
|
|||
|
||||
// Not recommended in production!
|
||||
rsclient
|
||||
.idm_group_add_members(BUILTIN_GROUP_IDM_ADMINS_V1.name, &["admin"])
|
||||
.idm_group_add_members(NAME_IDM_ADMINS, &["admin"])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -1379,7 +1365,7 @@ async fn setup_demo_account_password(
|
|||
}
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_server_credential_update_session_passkey(rsclient: KanidmClient) {
|
||||
async fn test_server_credential_update_session_passkey(rsclient: &KanidmClient) {
|
||||
let mut wa = setup_demo_account_passkey(&rsclient).await;
|
||||
|
||||
let res = rsclient
|
||||
|
@ -1397,7 +1383,7 @@ async fn test_server_credential_update_session_passkey(rsclient: KanidmClient) {
|
|||
}
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_server_api_token_lifecycle(rsclient: KanidmClient) {
|
||||
async fn test_server_api_token_lifecycle(rsclient: &KanidmClient) {
|
||||
let res = rsclient
|
||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
||||
.await;
|
||||
|
@ -1409,7 +1395,7 @@ async fn test_server_api_token_lifecycle(rsclient: KanidmClient) {
|
|||
.idm_service_account_create(
|
||||
test_service_account_username,
|
||||
"Test Service",
|
||||
BUILTIN_GROUP_IDM_ADMINS_V1.name,
|
||||
NAME_IDM_ADMINS,
|
||||
)
|
||||
.await
|
||||
.expect("Failed to create service account");
|
||||
|
@ -1580,7 +1566,7 @@ async fn test_server_api_token_lifecycle(rsclient: KanidmClient) {
|
|||
}
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_server_user_auth_token_lifecycle(rsclient: KanidmClient) {
|
||||
async fn test_server_user_auth_token_lifecycle(rsclient: &KanidmClient) {
|
||||
let res = rsclient
|
||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
||||
.await;
|
||||
|
@ -1703,7 +1689,7 @@ async fn test_server_user_auth_token_lifecycle(rsclient: KanidmClient) {
|
|||
}
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_server_user_auth_reauthentication(rsclient: KanidmClient) {
|
||||
async fn test_server_user_auth_reauthentication(rsclient: &KanidmClient) {
|
||||
let mut wa = setup_demo_account_passkey(&rsclient).await;
|
||||
|
||||
let res = rsclient
|
||||
|
@ -1793,7 +1779,8 @@ async fn start_password_session(
|
|||
password: &str,
|
||||
privileged: bool,
|
||||
) -> Result<UserAuthToken, ()> {
|
||||
let client = reqwest::Client::new();
|
||||
let fresh_rsclient = rsclient.new_session().unwrap();
|
||||
let client = fresh_rsclient.client();
|
||||
|
||||
let authreq = AuthRequest {
|
||||
step: AuthStep::Init2 {
|
||||
|
@ -1881,7 +1868,7 @@ async fn start_password_session(
|
|||
}
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_server_user_auth_unprivileged(rsclient: KanidmClient) {
|
||||
async fn test_server_user_auth_unprivileged(rsclient: &KanidmClient) {
|
||||
let (account_name, account_pass) = setup_demo_account_password(&rsclient)
|
||||
.await
|
||||
.expect("Failed to setup demo_account");
|
||||
|
@ -1904,7 +1891,7 @@ async fn test_server_user_auth_unprivileged(rsclient: KanidmClient) {
|
|||
}
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_server_user_auth_privileged_shortcut(rsclient: KanidmClient) {
|
||||
async fn test_server_user_auth_privileged_shortcut(rsclient: &KanidmClient) {
|
||||
let (account_name, account_pass) = setup_demo_account_password(&rsclient)
|
||||
.await
|
||||
.expect("Failed to setup demo_account");
|
|
@ -2,14 +2,14 @@ use compact_jwt::{traits::JwsVerifiable, JwsCompact, JwsEs256Verifier, JwsVerifi
|
|||
use kanidm_client::KanidmClient;
|
||||
use kanidm_proto::internal::ScimSyncToken;
|
||||
use kanidm_proto::scim_v1::ScimEntryGetQuery;
|
||||
use kanidmd_lib::prelude::{Attribute, BUILTIN_GROUP_IDM_ADMINS_V1};
|
||||
use kanidmd_lib::constants::NAME_IDM_ADMINS;
|
||||
use kanidmd_lib::prelude::Attribute;
|
||||
use kanidmd_testkit::{ADMIN_TEST_PASSWORD, ADMIN_TEST_USER};
|
||||
use reqwest::header::HeaderValue;
|
||||
use std::str::FromStr;
|
||||
use url::Url;
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_sync_account_lifecycle(rsclient: KanidmClient) {
|
||||
async fn test_sync_account_lifecycle(rsclient: &KanidmClient) {
|
||||
let a_res = rsclient
|
||||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
||||
.await;
|
||||
|
@ -104,65 +104,7 @@ async fn test_sync_account_lifecycle(rsclient: KanidmClient) {
|
|||
}
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_scim_sync_get(rsclient: KanidmClient) {
|
||||
// We need to do manual reqwests here.
|
||||
|
||||
let mut headers = reqwest::header::HeaderMap::new();
|
||||
headers.insert(
|
||||
reqwest::header::AUTHORIZATION,
|
||||
HeaderValue::from_str(&format!("Bearer {:?}", rsclient.get_token().await)).unwrap(),
|
||||
);
|
||||
|
||||
let client = reqwest::Client::builder()
|
||||
.danger_accept_invalid_certs(true)
|
||||
.default_headers(headers)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
// here we test the /ui/ endpoint which should have the headers
|
||||
let response = match client.get(rsclient.make_url("/scim/v1/Sync")).send().await {
|
||||
Ok(value) => value,
|
||||
Err(error) => {
|
||||
panic!(
|
||||
"Failed to query {:?} : {:#?}",
|
||||
rsclient.make_url("/scim/v1/Sync"),
|
||||
error
|
||||
);
|
||||
}
|
||||
};
|
||||
eprintln!("response: {:#?}", response);
|
||||
assert!(response.status().is_client_error());
|
||||
|
||||
// check that the CSP headers are coming back
|
||||
eprintln!(
|
||||
"csp headers: {:#?}",
|
||||
response
|
||||
.headers()
|
||||
.get(http::header::CONTENT_SECURITY_POLICY)
|
||||
);
|
||||
assert_ne!(
|
||||
response
|
||||
.headers()
|
||||
.get(http::header::CONTENT_SECURITY_POLICY),
|
||||
None
|
||||
);
|
||||
|
||||
// test that the proper content type comes back
|
||||
let url = rsclient.make_url("/scim/v1/Sink");
|
||||
let response = match client.get(url.clone()).send().await {
|
||||
Ok(value) => value,
|
||||
Err(error) => {
|
||||
panic!("Failed to query {:?} : {:#?}", url, error);
|
||||
}
|
||||
};
|
||||
assert!(response.status().is_success());
|
||||
let content_type = response.headers().get(http::header::CONTENT_TYPE).unwrap();
|
||||
assert!(content_type.to_str().unwrap().contains("text/html"));
|
||||
assert!(response.text().await.unwrap().contains("Sink"));
|
||||
}
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_scim_sync_entry_get(rsclient: KanidmClient) {
|
||||
async fn test_scim_sync_entry_get(rsclient: &KanidmClient) {
|
||||
let res = rsclient
|
||||
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||
.await;
|
||||
|
@ -170,7 +112,7 @@ async fn test_scim_sync_entry_get(rsclient: KanidmClient) {
|
|||
|
||||
// All admin to create persons.
|
||||
rsclient
|
||||
.idm_group_add_members(BUILTIN_GROUP_IDM_ADMINS_V1.name, &["admin"])
|
||||
.idm_group_add_members(NAME_IDM_ADMINS, &["admin"])
|
||||
.await
|
||||
.unwrap();
|
||||
|
|
@ -2,12 +2,9 @@ use kanidm_client::KanidmClient;
|
|||
|
||||
/// This literally tests that the thing exists and responds in a way we expect, probably worth testing it better...
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_v1_service_account_id_attr_attr_delete(rsclient: KanidmClient) {
|
||||
async fn test_v1_service_account_id_attr_attr_delete(rsclient: &KanidmClient) {
|
||||
// We need to do manual reqwests here.
|
||||
let client = reqwest::ClientBuilder::new()
|
||||
.danger_accept_invalid_certs(true)
|
||||
.build()
|
||||
.unwrap();
|
||||
let client = rsclient.client();
|
||||
|
||||
// let post_body = serde_json::json!({"filter": "self"}).to_string();
|
||||
|
|
@ -2,11 +2,8 @@ use kanidm_client::KanidmClient;
|
|||
|
||||
/// This literally tests that the thing exists and responds in a way we expect, probably worth testing it better...
|
||||
#[kanidmd_testkit::test]
|
||||
async fn test_v1_system_post_attr(rsclient: KanidmClient) {
|
||||
let client = reqwest::ClientBuilder::new()
|
||||
.danger_accept_invalid_certs(true)
|
||||
.build()
|
||||
.unwrap();
|
||||
async fn test_v1_system_post_attr(rsclient: &KanidmClient) {
|
||||
let client = rsclient.client();
|
||||
|
||||
let response = match client
|
||||
.post(rsclient.make_url("/v1/system/_attr/domain_name"))
|
|
@ -1,19 +1,14 @@
|
|||
use kanidm_client::KanidmClient;
|
||||
use kanidmd_lib::prelude::BUILTIN_GROUP_IDM_ADMINS_V1;
|
||||
use kanidmd_lib::constants::NAME_IDM_ADMINS;
|
||||
use kanidmd_testkit::*;
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
async fn account_id_unix_token(rsclient: KanidmClient) {
|
||||
login_put_admin_idm_admins(&rsclient).await;
|
||||
async fn account_id_unix_token(rsclient: &KanidmClient) {
|
||||
login_put_admin_idm_admins(rsclient).await;
|
||||
|
||||
create_user(&rsclient, "group_manager", "idm_group_manage_priv").await;
|
||||
// create test user without creating new groups
|
||||
create_user(
|
||||
&rsclient,
|
||||
NOT_ADMIN_TEST_USERNAME,
|
||||
BUILTIN_GROUP_IDM_ADMINS_V1.name,
|
||||
)
|
||||
.await;
|
||||
create_user(&rsclient, NOT_ADMIN_TEST_USERNAME, NAME_IDM_ADMINS).await;
|
||||
login_account(&rsclient, "group_manager").await;
|
||||
|
||||
let response = rsclient
|
|
@ -22,14 +22,14 @@ dev-oauth2-device-flow = []
|
|||
[lib]
|
||||
name = "kanidm_cli"
|
||||
path = "src/cli/lib.rs"
|
||||
test = true
|
||||
test = false
|
||||
doctest = false
|
||||
|
||||
[[bin]]
|
||||
name = "kanidm"
|
||||
path = "src/cli/main.rs"
|
||||
doc = false
|
||||
test = true
|
||||
test = false
|
||||
doctest = false
|
||||
|
||||
[[bin]]
|
||||
|
|
|
@ -12,8 +12,8 @@ repository = { workspace = true }
|
|||
|
||||
|
||||
[lib]
|
||||
test = false
|
||||
doctest = false
|
||||
test = false
|
||||
|
||||
[features]
|
||||
|
||||
|
|
|
@ -11,6 +11,11 @@ license = { workspace = true }
|
|||
homepage = { workspace = true }
|
||||
repository = { workspace = true }
|
||||
|
||||
[[bin]]
|
||||
name = "kanidm-ipa-sync"
|
||||
test = false
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
clap = { workspace = true, features = ["derive", "env"] }
|
||||
chrono = { workspace = true }
|
||||
|
|
|
@ -11,6 +11,11 @@ license = { workspace = true }
|
|||
homepage = { workspace = true }
|
||||
repository = { workspace = true }
|
||||
|
||||
[[bin]]
|
||||
name = "kanidm-ldap-sync"
|
||||
test = false
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
clap = { workspace = true, features = ["derive", "env"] }
|
||||
chrono = { workspace = true }
|
||||
|
|
|
@ -14,7 +14,7 @@ repository = { workspace = true }
|
|||
[[bin]]
|
||||
name = "orca"
|
||||
path = "src/main.rs"
|
||||
test = true
|
||||
test = false
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
|
|
|
@ -21,28 +21,28 @@ tpm = ["kanidm-hsm-crypto/tpm"]
|
|||
name = "kanidm_unixd"
|
||||
path = "src/bin/kanidm_unixd.rs"
|
||||
required-features = ["unix"]
|
||||
test = true
|
||||
test = false
|
||||
doctest = false
|
||||
|
||||
[[bin]]
|
||||
name = "kanidm_unixd_tasks"
|
||||
path = "src/bin/kanidm_unixd_tasks.rs"
|
||||
required-features = ["unix"]
|
||||
test = true
|
||||
test = false
|
||||
doctest = false
|
||||
|
||||
[[bin]]
|
||||
name = "kanidm_ssh_authorizedkeys"
|
||||
path = "src/bin/kanidm_ssh_authorizedkeys.rs"
|
||||
required-features = ["unix"]
|
||||
test = true
|
||||
test = false
|
||||
doctest = false
|
||||
|
||||
[[bin]]
|
||||
name = "kanidm-unix"
|
||||
path = "src/bin/kanidm-unix.rs"
|
||||
required-features = ["unix"]
|
||||
test = true
|
||||
test = false
|
||||
doctest = false
|
||||
|
||||
[lib]
|
||||
|
|
|
@ -88,6 +88,7 @@ async fn setup_test(fix_fn: Fixture) -> (Resolver, KanidmClient) {
|
|||
// Run fixtures
|
||||
let adminclient = KanidmClientBuilder::new()
|
||||
.address(addr.clone())
|
||||
.enable_native_ca_roots(false)
|
||||
.no_proxy()
|
||||
.build()
|
||||
.expect("Failed to build sync client");
|
||||
|
@ -96,12 +97,14 @@ async fn setup_test(fix_fn: Fixture) -> (Resolver, KanidmClient) {
|
|||
|
||||
let client = KanidmClientBuilder::new()
|
||||
.address(addr.clone())
|
||||
.enable_native_ca_roots(false)
|
||||
.no_proxy()
|
||||
.build()
|
||||
.expect("Failed to build async admin client");
|
||||
|
||||
let rsclient = KanidmClientBuilder::new()
|
||||
.address(addr)
|
||||
.enable_native_ca_roots(false)
|
||||
.no_proxy()
|
||||
.build()
|
||||
.expect("Failed to build client");
|
||||
|
|
Loading…
Reference in a new issue