From 329750981ee608d23425bc87b3d150a126574706 Mon Sep 17 00:00:00 2001 From: Firstyear Date: Thu, 1 Aug 2024 00:02:11 +1000 Subject: [PATCH] Update to 1.4.0-dev (#2943) --- Cargo.lock | 50 +-- Cargo.toml | 28 +- proto/src/internal/error.rs | 2 + server/lib/src/constants/acp.rs | 300 +---------------- server/lib/src/constants/entries.rs | 30 +- server/lib/src/constants/mod.rs | 25 +- server/lib/src/constants/schema.rs | 154 --------- server/lib/src/idm/account.rs | 2 +- server/lib/src/idm/authsession.rs | 2 +- server/lib/src/idm/scim.rs | 113 ------- server/lib/src/idm/serviceaccount.rs | 111 ------ server/lib/src/server/access/mod.rs | 2 +- server/lib/src/server/keys/provider.rs | 83 +---- server/lib/src/server/migrations.rs | 450 +++++-------------------- server/lib/src/server/mod.rs | 25 +- server/testkit/tests/oauth2_test.rs | 25 +- 16 files changed, 181 insertions(+), 1221 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c617e9bdb..19b4241b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1213,7 +1213,7 @@ dependencies = [ [[package]] name = "daemon" -version = "1.3.0-dev" +version = "1.4.0-dev" dependencies = [ "clap", "clap_complete", @@ -3188,7 +3188,7 @@ dependencies = [ [[package]] name = "kanidm-ipa-sync" -version = "1.3.0-dev" +version = "1.4.0-dev" dependencies = [ "chrono", "clap", @@ -3212,7 +3212,7 @@ dependencies = [ [[package]] name = "kanidm-ldap-sync" -version = "1.3.0-dev" +version = "1.4.0-dev" dependencies = [ "base64urlsafedata 0.5.0", "chrono", @@ -3238,7 +3238,7 @@ dependencies = [ [[package]] name = "kanidm_build_profiles" -version = "1.3.0-dev" +version = "1.4.0-dev" dependencies = [ "base64 0.22.1", "gix", @@ -3248,7 +3248,7 @@ dependencies = [ [[package]] name = "kanidm_client" -version = "1.3.0-dev" +version = "1.4.0-dev" dependencies = [ "compact_jwt 0.4.2", "http 1.1.0", @@ -3269,7 +3269,7 @@ dependencies = [ [[package]] name = "kanidm_lib_crypto" -version = "1.3.0-dev" +version = "1.4.0-dev" dependencies = [ "argon2", "base64 0.22.1", @@ -3290,7 +3290,7 @@ dependencies = [ [[package]] name = "kanidm_lib_file_permissions" -version = "1.3.0-dev" +version = "1.4.0-dev" dependencies = [ "kanidm_utils_users", "whoami", @@ -3298,7 +3298,7 @@ dependencies = [ [[package]] name = "kanidm_proto" -version = "1.3.0-dev" +version = "1.4.0-dev" dependencies = [ "base32", "base64urlsafedata 0.5.0", @@ -3318,7 +3318,7 @@ dependencies = [ [[package]] name = "kanidm_tools" -version = "1.3.0-dev" +version = "1.4.0-dev" dependencies = [ "anyhow", "async-recursion", @@ -3351,7 +3351,7 @@ dependencies = [ [[package]] name = "kanidm_unix_common" -version = "1.3.0-dev" +version = "1.4.0-dev" dependencies = [ "bytes", "csv", @@ -3367,7 +3367,7 @@ dependencies = [ [[package]] name = "kanidm_unix_int" -version = "1.3.0-dev" +version = "1.4.0-dev" dependencies = [ "async-trait", "bytes", @@ -3407,14 +3407,14 @@ dependencies = [ [[package]] name = "kanidm_utils_users" -version = "1.3.0-dev" +version = "1.4.0-dev" dependencies = [ "libc", ] [[package]] name = "kanidmd_core" -version = "1.3.0-dev" +version = "1.4.0-dev" dependencies = [ "askama", "async-trait", @@ -3471,7 +3471,7 @@ dependencies = [ [[package]] name = "kanidmd_lib" -version = "1.3.0-dev" +version = "1.4.0-dev" dependencies = [ "base64 0.22.1", "base64urlsafedata 0.5.0", @@ -3532,7 +3532,7 @@ dependencies = [ [[package]] name = "kanidmd_lib_macros" -version = "1.3.0-dev" +version = "1.4.0-dev" dependencies = [ "proc-macro2", "quote", @@ -3541,7 +3541,7 @@ dependencies = [ [[package]] name = "kanidmd_testkit" -version = "1.3.0-dev" +version = "1.4.0-dev" dependencies = [ "assert_cmd", "compact_jwt 0.4.2", @@ -3579,7 +3579,7 @@ dependencies = [ [[package]] name = "kanidmd_web_ui_admin" -version = "1.3.0-dev" +version = "1.4.0-dev" dependencies = [ "enum-iterator", "gloo", @@ -3601,7 +3601,7 @@ dependencies = [ [[package]] name = "kanidmd_web_ui_login_flows" -version = "1.3.0-dev" +version = "1.4.0-dev" dependencies = [ "gloo", "gloo-utils 0.2.0", @@ -3622,7 +3622,7 @@ dependencies = [ [[package]] name = "kanidmd_web_ui_shared" -version = "1.3.0-dev" +version = "1.4.0-dev" dependencies = [ "gloo", "js-sys", @@ -3641,7 +3641,7 @@ dependencies = [ [[package]] name = "kanidmd_web_ui_user" -version = "1.3.0-dev" +version = "1.4.0-dev" dependencies = [ "enum-iterator", "gloo", @@ -4097,7 +4097,7 @@ dependencies = [ [[package]] name = "nss_kanidm" -version = "1.3.0-dev" +version = "1.4.0-dev" dependencies = [ "kanidm_unix_common", "lazy_static", @@ -4470,7 +4470,7 @@ dependencies = [ [[package]] name = "orca" -version = "1.3.0-dev" +version = "1.4.0-dev" dependencies = [ "async-trait", "chrono", @@ -4513,7 +4513,7 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "pam_kanidm" -version = "1.3.0-dev" +version = "1.4.0-dev" dependencies = [ "kanidm_unix_common", "libc", @@ -5367,7 +5367,7 @@ dependencies = [ [[package]] name = "scim_proto" -version = "1.3.0-dev" +version = "1.4.0-dev" dependencies = [ "base64urlsafedata 0.5.0", "peg", @@ -5647,7 +5647,7 @@ dependencies = [ [[package]] name = "sketching" -version = "1.3.0-dev" +version = "1.4.0-dev" dependencies = [ "gethostname", "num_enum", diff --git a/Cargo.toml b/Cargo.toml index de1a53d47..caf9b7ab4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace.package] -version = "1.3.0-dev" +version = "1.4.0-dev" authors = [ "William Brown ", "James Hodgkinson ", @@ -124,20 +124,20 @@ codegen-units = 256 # kanidm-hsm-crypto = { path = "../hsm-crypto" } [workspace.dependencies] -kanidmd_core = { path = "./server/core", version = "=1.3.0-dev" } -kanidmd_lib = { path = "./server/lib", version = "=1.3.0-dev" } -kanidmd_lib_macros = { path = "./server/lib-macros", version = "=1.3.0-dev" } -kanidmd_testkit = { path = "./server/testkit", version = "=1.3.0-dev" } -kanidm_build_profiles = { path = "./libs/profiles", version = "=1.3.0-dev" } -kanidm_client = { path = "./libs/client", version = "=1.3.0-dev" } +kanidmd_core = { path = "./server/core", version = "=1.4.0-dev" } +kanidmd_lib = { path = "./server/lib", version = "=1.4.0-dev" } +kanidmd_lib_macros = { path = "./server/lib-macros", version = "=1.4.0-dev" } +kanidmd_testkit = { path = "./server/testkit", version = "=1.4.0-dev" } +kanidm_build_profiles = { path = "./libs/profiles", version = "=1.4.0-dev" } +kanidm_client = { path = "./libs/client", version = "=1.4.0-dev" } kanidm-hsm-crypto = "^0.2.0" -kanidm_lib_crypto = { path = "./libs/crypto", version = "=1.3.0-dev" } -kanidm_lib_file_permissions = { path = "./libs/file_permissions", version = "=1.3.0-dev" } -kanidm_proto = { path = "./proto", version = "=1.3.0-dev" } -kanidm_unix_common = { path = "./unix_integration/common", version = "=1.3.0-dev" } -kanidm_utils_users = { path = "./libs/users", version = "=1.3.0-dev" } -scim_proto = { path = "./libs/scim_proto", version = "=1.3.0-dev" } -sketching = { path = "./libs/sketching", version = "=1.3.0-dev" } +kanidm_lib_crypto = { path = "./libs/crypto", version = "=1.4.0-dev" } +kanidm_lib_file_permissions = { path = "./libs/file_permissions", version = "=1.4.0-dev" } +kanidm_proto = { path = "./proto", version = "=1.4.0-dev" } +kanidm_unix_common = { path = "./unix_integration/common", version = "=1.4.0-dev" } +kanidm_utils_users = { path = "./libs/users", version = "=1.4.0-dev" } +scim_proto = { path = "./libs/scim_proto", version = "=1.4.0-dev" } +sketching = { path = "./libs/sketching", version = "=1.4.0-dev" } anyhow = { version = "1.0.86" } argon2 = { version = "0.5.3", features = ["alloc"] } diff --git a/proto/src/internal/error.rs b/proto/src/internal/error.rs index 35b11b01f..b5ebe868d 100644 --- a/proto/src/internal/error.rs +++ b/proto/src/internal/error.rs @@ -153,6 +153,7 @@ pub enum OperationError { MG0005GidConstraintsNotMet, MG0006SKConstraintsNotMet, MG0007Oauth2StrictConstraintsNotMet, + MG0008SkipUpgradeAttempted, // KP0001KeyProviderNotLoaded, KP0002KeyProviderInvalidClass, @@ -306,6 +307,7 @@ impl OperationError { Self::MG0005GidConstraintsNotMet => None, Self::MG0006SKConstraintsNotMet => Some("Migration Constraints Not Met - Security Keys should not be present."), Self::MG0007Oauth2StrictConstraintsNotMet => Some("Migration Constraints Not Met - All OAuth2 clients must have strict-redirect-uri mode enabled."), + Self::MG0008SkipUpgradeAttempted => Some("Skip Upgrade Attempted."), Self::KP0001KeyProviderNotLoaded => None, Self::KP0002KeyProviderInvalidClass => None, Self::KP0003KeyProviderInvalidType => None, diff --git a/server/lib/src/constants/acp.rs b/server/lib/src/constants/acp.rs index f3409649d..283ae5a0c 100644 --- a/server/lib/src/constants/acp.rs +++ b/server/lib/src/constants/acp.rs @@ -491,53 +491,6 @@ lazy_static! { }; } -lazy_static! { - pub static ref IDM_ACP_GROUP_ACCOUNT_POLICY_MANAGE_V1: BuiltinAcp = BuiltinAcp { - classes: vec![ - EntryClass::Object, - EntryClass::AccessControlProfile, - EntryClass::AccessControlModify, - EntryClass::AccessControlSearch - ], - name: "idm_acp_group_account_policy_manage_", - uuid: UUID_IDM_ACP_GROUP_ACCOUNT_POLICY_MANAGE, - description: "Builtin IDM Control for management of account policy on groups", - receiver: BuiltinAcpReceiver::Group(vec![UUID_IDM_ACCOUNT_POLICY_ADMINS]), - target: BuiltinAcpTarget::Filter(ProtoFilter::And(vec![ - match_class_filter!(EntryClass::Group), - ProtoFilter::AndNot(Box::new(FILTER_HP_OR_RECYCLED_OR_TOMBSTONE.clone())), - ])), - search_attrs: vec![ - Attribute::Class, - Attribute::Name, - Attribute::Uuid, - Attribute::AuthSessionExpiry, - Attribute::AuthPasswordMinimumLength, - Attribute::CredentialTypeMinimum, - Attribute::PrivilegeExpiry, - Attribute::WebauthnAttestationCaList, - ], - modify_removed_attrs: vec![ - Attribute::Class, - Attribute::AuthSessionExpiry, - Attribute::AuthPasswordMinimumLength, - Attribute::CredentialTypeMinimum, - Attribute::PrivilegeExpiry, - Attribute::WebauthnAttestationCaList, - ], - modify_present_attrs: vec![ - Attribute::Class, - Attribute::AuthSessionExpiry, - Attribute::AuthPasswordMinimumLength, - Attribute::CredentialTypeMinimum, - Attribute::PrivilegeExpiry, - Attribute::WebauthnAttestationCaList, - ], - modify_classes: vec![EntryClass::AccountPolicy,], - ..Default::default() - }; -} - lazy_static! { pub static ref IDM_ACP_GROUP_ACCOUNT_POLICY_MANAGE_DL6: BuiltinAcp = BuiltinAcp { classes: vec![ @@ -591,96 +544,6 @@ lazy_static! { }; } -lazy_static! { - pub static ref IDM_ACP_OAUTH2_MANAGE_V1: BuiltinAcp = BuiltinAcp { - classes: vec![ - EntryClass::Object, - EntryClass::AccessControlProfile, - EntryClass::AccessControlCreate, - EntryClass::AccessControlDelete, - EntryClass::AccessControlModify, - EntryClass::AccessControlSearch - ], - name: "idm_acp_hp_oauth2_manage_priv", - uuid: UUID_IDM_ACP_OAUTH2_MANAGE_V1, - description: "Builtin IDM Control for managing oauth2 resource server integrations.", - receiver: BuiltinAcpReceiver::Group(vec![UUID_IDM_OAUTH2_ADMINS]), - target: BuiltinAcpTarget::Filter(ProtoFilter::And(vec![ - match_class_filter!(EntryClass::OAuth2ResourceServer), - FILTER_ANDNOT_TOMBSTONE_OR_RECYCLED.clone(), - ])), - search_attrs: vec![ - Attribute::Class, - Attribute::Description, - Attribute::DisplayName, - Attribute::OAuth2RsName, - Attribute::OAuth2RsOrigin, - Attribute::OAuth2RsOriginLanding, - Attribute::OAuth2RsScopeMap, - Attribute::OAuth2RsSupScopeMap, - Attribute::OAuth2RsBasicSecret, - Attribute::OAuth2RsTokenKey, - Attribute::Es256PrivateKeyDer, - Attribute::OAuth2AllowInsecureClientDisablePkce, - Attribute::Rs256PrivateKeyDer, - Attribute::OAuth2JwtLegacyCryptoEnable, - Attribute::OAuth2PreferShortUsername, - Attribute::Image, - ], - modify_removed_attrs: vec![ - Attribute::Description, - Attribute::DisplayName, - Attribute::OAuth2RsName, - Attribute::OAuth2RsOrigin, - Attribute::OAuth2RsOriginLanding, - Attribute::OAuth2RsScopeMap, - Attribute::OAuth2RsSupScopeMap, - Attribute::OAuth2RsBasicSecret, - Attribute::OAuth2RsTokenKey, - Attribute::Es256PrivateKeyDer, - Attribute::OAuth2AllowInsecureClientDisablePkce, - Attribute::Rs256PrivateKeyDer, - Attribute::OAuth2JwtLegacyCryptoEnable, - Attribute::OAuth2PreferShortUsername, - Attribute::Image, - ], - modify_present_attrs: vec![ - Attribute::Description, - Attribute::DisplayName, - Attribute::OAuth2RsName, - Attribute::OAuth2RsOrigin, - Attribute::OAuth2RsOriginLanding, - Attribute::OAuth2RsSupScopeMap, - Attribute::OAuth2RsScopeMap, - Attribute::OAuth2AllowInsecureClientDisablePkce, - Attribute::OAuth2JwtLegacyCryptoEnable, - Attribute::OAuth2PreferShortUsername, - Attribute::Image, - ], - create_attrs: vec![ - Attribute::Class, - Attribute::Description, - Attribute::DisplayName, - Attribute::OAuth2RsName, - Attribute::OAuth2RsOrigin, - Attribute::OAuth2RsOriginLanding, - Attribute::OAuth2RsSupScopeMap, - Attribute::OAuth2RsScopeMap, - Attribute::OAuth2AllowInsecureClientDisablePkce, - Attribute::OAuth2JwtLegacyCryptoEnable, - Attribute::OAuth2PreferShortUsername, - Attribute::Image, - ], - create_classes: vec![ - EntryClass::Object, - EntryClass::OAuth2ResourceServer, - EntryClass::OAuth2ResourceServerBasic, - EntryClass::OAuth2ResourceServerPublic, - ], - ..Default::default() - }; -} - lazy_static! { pub static ref IDM_ACP_OAUTH2_MANAGE_DL4: BuiltinAcp = BuiltinAcp { classes: vec![ @@ -858,6 +721,7 @@ lazy_static! { Attribute::Class, Attribute::Description, Attribute::Name, + Attribute::DisplayName, Attribute::OAuth2RsName, Attribute::OAuth2RsOrigin, Attribute::OAuth2RsOriginLanding, @@ -963,6 +827,7 @@ lazy_static! { Attribute::Class, Attribute::Description, Attribute::Name, + Attribute::DisplayName, Attribute::OAuth2RsName, Attribute::OAuth2RsOrigin, Attribute::OAuth2RsOriginLanding, @@ -987,59 +852,6 @@ lazy_static! { }; } -lazy_static! { - pub static ref IDM_ACP_DOMAIN_ADMIN_V1: BuiltinAcp = BuiltinAcp { - classes: vec![ - EntryClass::Object, - EntryClass::AccessControlProfile, - EntryClass::AccessControlModify, - EntryClass::AccessControlSearch - ], - name: "idm_acp_domain_admin", - uuid: UUID_IDM_ACP_DOMAIN_ADMIN_V1, - description: "Builtin IDM Control for granting domain info administration locally", - receiver: BuiltinAcpReceiver::Group(vec![UUID_DOMAIN_ADMINS]), - target: BuiltinAcpTarget::Filter(ProtoFilter::And(vec![ - ProtoFilter::Eq( - Attribute::Uuid.to_string(), - STR_UUID_DOMAIN_INFO.to_string() - ), - FILTER_ANDNOT_TOMBSTONE_OR_RECYCLED.clone() - ])), - search_attrs: vec![ - Attribute::Class, - Attribute::Name, - Attribute::Uuid, - Attribute::DomainDisplayName, - Attribute::DomainName, - Attribute::DomainLdapBasedn, - Attribute::DomainSsid, - Attribute::DomainUuid, - Attribute::Es256PrivateKeyDer, - Attribute::FernetPrivateKeyStr, - Attribute::CookiePrivateKey, - Attribute::LdapAllowUnixPwBind, - Attribute::Version, - ], - modify_removed_attrs: vec![ - Attribute::DomainDisplayName, - Attribute::DomainSsid, - Attribute::DomainLdapBasedn, - Attribute::Es256PrivateKeyDer, - Attribute::CookiePrivateKey, - Attribute::FernetPrivateKeyStr, - Attribute::LdapAllowUnixPwBind, - ], - modify_present_attrs: vec![ - Attribute::DomainDisplayName, - Attribute::DomainLdapBasedn, - Attribute::DomainSsid, - Attribute::LdapAllowUnixPwBind, - ], - ..Default::default() - }; -} - lazy_static! { pub static ref IDM_ACP_DOMAIN_ADMIN_DL6: BuiltinAcp = BuiltinAcp { classes: vec![ @@ -1479,26 +1291,6 @@ lazy_static! { }; } -lazy_static! { - pub static ref IDM_ACP_ACCOUNT_MAIL_READ_V1: BuiltinAcp = BuiltinAcp { - classes: vec![ - EntryClass::Object, - EntryClass::AccessControlProfile, - EntryClass::AccessControlSearch - ], - name: "idm_acp_account_mail_read", - uuid: UUID_IDM_ACP_ACCOUNT_MAIL_READ_V1, - description: "Builtin IDM Control for reading account mail attributes.", - receiver: BuiltinAcpReceiver::Group(vec![UUID_IDM_ACCOUNT_MAIL_READ]), - target: BuiltinAcpTarget::Filter(ProtoFilter::And(vec![ - match_class_filter!(EntryClass::Account), - FILTER_ANDNOT_TOMBSTONE_OR_RECYCLED.clone(), - ])), - search_attrs: vec![Attribute::Mail], - ..Default::default() - }; -} - lazy_static! { pub static ref IDM_ACP_ACCOUNT_MAIL_READ_DL6: BuiltinAcp = BuiltinAcp { classes: vec![ @@ -1610,61 +1402,6 @@ lazy_static! { }; } -lazy_static! { - pub static ref IDM_ACP_GROUP_MANAGE_V1: BuiltinAcp = BuiltinAcp{ - classes: vec![ - EntryClass::Object, - EntryClass::AccessControlProfile, - EntryClass::AccessControlCreate, - EntryClass::AccessControlDelete, - EntryClass::AccessControlModify, - EntryClass::AccessControlSearch - ], - name: "idm_acp_group_manage", - uuid: UUID_IDM_ACP_GROUP_MANAGE_V1, - description: "Builtin IDM Control for creating and deleting groups in the directory", - receiver: BuiltinAcpReceiver::Group ( vec![UUID_IDM_GROUP_ADMINS] ), - // group which is not in HP, Recycled, Tombstone - target: BuiltinAcpTarget::Filter( ProtoFilter::And(vec![ - match_class_filter!(EntryClass::Group), - FILTER_ANDNOT_HP_OR_RECYCLED_OR_TOMBSTONE.clone(), - ])), - search_attrs: vec![ - Attribute::Class, - Attribute::Name, - Attribute::Uuid, - Attribute::Spn, - Attribute::Uuid, - Attribute::Description, - Attribute::Member, - Attribute::DynMember, - Attribute::EntryManagedBy, - ], - create_attrs: vec![ - Attribute::Class, - Attribute::Name, - Attribute::Description, - Attribute::Member, - Attribute::EntryManagedBy, - ], - create_classes: vec![ - EntryClass::Object, - EntryClass::Group, - ], - modify_present_attrs: vec![ - Attribute::Name, - Attribute::Description, - Attribute::Member, - ], - modify_removed_attrs: vec![ - Attribute::Name, - Attribute::Description, - Attribute::Member, - ], - ..Default::default() - }; -} - lazy_static! { pub static ref IDM_ACP_GROUP_MANAGE_DL6: BuiltinAcp = BuiltinAcp{ classes: vec![ @@ -1862,39 +1599,6 @@ lazy_static! { }; } -// Person Account Create -lazy_static! { - pub static ref IDM_ACP_PEOPLE_CREATE_V1: BuiltinAcp = BuiltinAcp { - classes: vec![ - EntryClass::Object, - EntryClass::AccessControlProfile, - EntryClass::AccessControlCreate, - ], - name: "idm_acp_people_create", - uuid: UUID_IDM_ACP_PEOPLE_CREATE_V1, - description: "Builtin IDM Control for creating new persons.", - receiver: BuiltinAcpReceiver::Group(vec![ - UUID_IDM_PEOPLE_ADMINS, - UUID_IDM_PEOPLE_ON_BOARDING - ]), - target: BuiltinAcpTarget::Filter(ProtoFilter::And(vec![ - match_class_filter!(EntryClass::Person).clone(), - match_class_filter!(EntryClass::Account).clone(), - FILTER_ANDNOT_TOMBSTONE_OR_RECYCLED.clone(), - ])), - create_attrs: vec![ - Attribute::Class, - Attribute::Name, - Attribute::DisplayName, - Attribute::Mail, - Attribute::AccountExpire, - Attribute::AccountValidFrom, - ], - create_classes: vec![EntryClass::Object, EntryClass::Account, EntryClass::Person,], - ..Default::default() - }; -} - lazy_static! { pub static ref IDM_ACP_PEOPLE_CREATE_DL6: BuiltinAcp = BuiltinAcp { classes: vec![ diff --git a/server/lib/src/constants/entries.rs b/server/lib/src/constants/entries.rs index fcee1e427..096ce4ad2 100644 --- a/server/lib/src/constants/entries.rs +++ b/server/lib/src/constants/entries.rs @@ -800,18 +800,6 @@ lazy_static! { (Attribute::Version, Value::Uint32(20)) ); - pub static ref E_DOMAIN_INFO_V1: EntryInitNew = entry_init!( - (Attribute::Class, EntryClass::Object.to_value()), - (Attribute::Class, EntryClass::DomainInfo.to_value()), - (Attribute::Class, EntryClass::System.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_DOMAIN_INFO_DL6: EntryInitNew = entry_init!( (Attribute::Class, EntryClass::Object.to_value()), (Attribute::Class, EntryClass::DomainInfo.to_value()), @@ -916,17 +904,6 @@ lazy_static! { }; } -lazy_static! { - pub static ref BUILTIN_ACCOUNT_ANONYMOUS_V1: BuiltinAccount = BuiltinAccount { - account_type: AccountType::ServiceAccount, - entry_managed_by: None, - name: "anonymous", - uuid: UUID_ANONYMOUS, - description: "Anonymous access account.", - displayname: "Anonymous", - }; -} - lazy_static! { pub static ref BUILTIN_ACCOUNT_ANONYMOUS_DL6: BuiltinAccount = BuiltinAccount { account_type: AccountType::ServiceAccount, @@ -940,9 +917,9 @@ lazy_static! { pub fn builtin_accounts() -> Vec<&'static BuiltinAccount> { vec![ - &BUILTIN_ACCOUNT_ANONYMOUS_V1, &BUILTIN_ACCOUNT_ADMIN, &BUILTIN_ACCOUNT_IDM_ADMIN, + &BUILTIN_ACCOUNT_ANONYMOUS_DL6, ] } @@ -981,7 +958,6 @@ lazy_static! { /// Build a list of internal admin entries pub fn idm_builtin_admin_entries() -> Result, OperationError> { let mut res: Vec = vec![ - BUILTIN_ACCOUNT_ANONYMOUS_V1.clone().into(), BUILTIN_ACCOUNT_ADMIN.clone().into(), BUILTIN_ACCOUNT_IDM_ADMIN.clone().into(), ]; @@ -989,5 +965,9 @@ pub fn idm_builtin_admin_entries() -> Result, OperationError> 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) } diff --git a/server/lib/src/constants/mod.rs b/server/lib/src/constants/mod.rs index b0fdeab66..0d44ce7b6 100644 --- a/server/lib/src/constants/mod.rs +++ b/server/lib/src/constants/mod.rs @@ -52,15 +52,6 @@ pub type DomainVersion = u32; /// previously. pub const DOMAIN_LEVEL_0: DomainVersion = 0; -/// Deprcated as of 1.2.0 -pub const DOMAIN_LEVEL_1: DomainVersion = 1; -/// Deprcated as of 1.2.0 -pub const DOMAIN_LEVEL_2: DomainVersion = 2; -/// Deprcated as of 1.2.0 -pub const DOMAIN_LEVEL_3: DomainVersion = 3; -/// Deprcated as of 1.2.0 -pub const DOMAIN_LEVEL_4: DomainVersion = 4; - /// Deprcated as of 1.3.0 pub const DOMAIN_LEVEL_5: DomainVersion = 5; @@ -77,22 +68,26 @@ pub const DOMAIN_LEVEL_7: DomainVersion = 7; /// Deprcated as of 1.6.0 pub const DOMAIN_LEVEL_8: DomainVersion = 8; +/// Domain Level introduced with 1.5.0. +/// Deprcated as of 1.7.0 +pub const DOMAIN_LEVEL_9: DomainVersion = 9; + // The minimum level that we can re-migrate from. // This should be DOMAIN_TGT_LEVEL minus 2 -pub const DOMAIN_MIN_REMIGRATION_LEVEL: DomainVersion = DOMAIN_LEVEL_5; -// The minimum supported domain functional level +pub const DOMAIN_MIN_REMIGRATION_LEVEL: DomainVersion = DOMAIN_LEVEL_6; +// The minimum supported domain functional level (for replication) pub const DOMAIN_MIN_LEVEL: DomainVersion = DOMAIN_TGT_LEVEL; // The previous releases domain functional level -pub const DOMAIN_PREVIOUS_TGT_LEVEL: DomainVersion = DOMAIN_LEVEL_6; +pub const DOMAIN_PREVIOUS_TGT_LEVEL: DomainVersion = DOMAIN_LEVEL_7; // The target supported domain functional level. During development this is // the NEXT level that users will upgrade too. -pub const DOMAIN_TGT_LEVEL: DomainVersion = DOMAIN_LEVEL_7; +pub const DOMAIN_TGT_LEVEL: DomainVersion = DOMAIN_LEVEL_8; // The current patch level if any out of band fixes are required. pub const DOMAIN_TGT_PATCH_LEVEL: u32 = PATCH_LEVEL_1; // The target domain functional level for the SUBSEQUENT release/dev cycle. -pub const DOMAIN_TGT_NEXT_LEVEL: DomainVersion = DOMAIN_LEVEL_8; +pub const DOMAIN_TGT_NEXT_LEVEL: DomainVersion = DOMAIN_LEVEL_9; // The maximum supported domain functional level -pub const DOMAIN_MAX_LEVEL: DomainVersion = DOMAIN_LEVEL_8; +pub const DOMAIN_MAX_LEVEL: DomainVersion = DOMAIN_LEVEL_9; // On test builds define to 60 seconds #[cfg(test)] diff --git a/server/lib/src/constants/schema.rs b/server/lib/src/constants/schema.rs index 032535446..1c33aab11 100644 --- a/server/lib/src/constants/schema.rs +++ b/server/lib/src/constants/schema.rs @@ -839,23 +839,6 @@ pub static ref SCHEMA_CLASS_ORGPERSON: SchemaClass = SchemaClass { ..Default::default() }; -pub static ref SCHEMA_CLASS_GROUP: SchemaClass = SchemaClass { - uuid: UUID_SCHEMA_CLASS_GROUP, - name: EntryClass::Group.into(), - description: "Object representation of a group".to_string(), - - sync_allowed: true, - systemmay: vec![ - Attribute::Member.into(), - Attribute::GrantUiHint.into(), - Attribute::Description.into() - ], - systemmust: vec![ - Attribute::Name.into(), - Attribute::Spn.into()], - ..Default::default() -}; - pub static ref SCHEMA_CLASS_GROUP_DL6: SchemaClass = SchemaClass { uuid: UUID_SCHEMA_CLASS_GROUP, name: EntryClass::Group.into(), @@ -885,22 +868,6 @@ pub static ref SCHEMA_CLASS_DYNGROUP: SchemaClass = SchemaClass { ..Default::default() }; -pub static ref SCHEMA_CLASS_ACCOUNT_POLICY: SchemaClass = SchemaClass { - uuid: UUID_SCHEMA_CLASS_ACCOUNT_POLICY, - name: EntryClass::AccountPolicy.into(), - description: "Policies applied to accounts that are members of a group".to_string(), - - systemmay: vec![ - Attribute::AuthSessionExpiry.into(), - Attribute::PrivilegeExpiry.into(), - Attribute::AuthPasswordMinimumLength.into(), - Attribute::CredentialTypeMinimum.into(), - Attribute::WebauthnAttestationCaList.into(), - ], - systemsupplements: vec![Attribute::Group.into()], - ..Default::default() -}; - pub static ref SCHEMA_CLASS_ACCOUNT_POLICY_DL6: SchemaClass = SchemaClass { uuid: UUID_SCHEMA_CLASS_ACCOUNT_POLICY, name: EntryClass::AccountPolicy.into(), @@ -977,43 +944,6 @@ pub static ref SCHEMA_CLASS_ACCOUNT_DL5: SchemaClass = SchemaClass { ..Default::default() }; -pub static ref SCHEMA_CLASS_SERVICE_ACCOUNT: SchemaClass = SchemaClass { - uuid: UUID_SCHEMA_CLASS_SERVICE_ACCOUNT, - name: EntryClass::ServiceAccount.into(), - description: "Object representation of service account".to_string(), - - sync_allowed: true, - systemmay: vec![ - Attribute::Mail.into(), - Attribute::PrimaryCredential.into(), - Attribute::JwsEs256PrivateKey.into(), - Attribute::ApiTokenSession.into(), - ], - systemexcludes: vec![EntryClass::Person.into()], - ..Default::default() -}; - -pub static ref SCHEMA_CLASS_SERVICE_ACCOUNT_DL5: SchemaClass = SchemaClass { - uuid: UUID_SCHEMA_CLASS_SERVICE_ACCOUNT, - name: EntryClass::ServiceAccount.into(), - description: "Object representation of service account".to_string(), - - sync_allowed: true, - systemmay: vec![ - Attribute::SshPublicKey.into(), - Attribute::UserAuthTokenSession.into(), - Attribute::OAuth2Session.into(), - Attribute::Description.into(), - - Attribute::Mail.into(), - Attribute::PrimaryCredential.into(), - Attribute::JwsEs256PrivateKey.into(), - Attribute::ApiTokenSession.into(), - ], - systemexcludes: vec![EntryClass::Person.into()], - ..Default::default() -}; - pub static ref SCHEMA_CLASS_SERVICE_ACCOUNT_DL6: SchemaClass = SchemaClass { uuid: UUID_SCHEMA_CLASS_SERVICE_ACCOUNT, name: EntryClass::ServiceAccount.into(), @@ -1058,22 +988,6 @@ pub static ref SCHEMA_CLASS_SERVICE_ACCOUNT_DL7: SchemaClass = SchemaClass { ..Default::default() }; -pub static ref SCHEMA_CLASS_SYNC_ACCOUNT: SchemaClass = SchemaClass { - uuid: UUID_SCHEMA_CLASS_SYNC_ACCOUNT, - name: EntryClass::SyncAccount.into(), - description: "Object representation of sync account".to_string(), - - systemmust: vec![Attribute::Name.into(), Attribute::JwsEs256PrivateKey.into()], - systemmay: vec![ - Attribute::SyncTokenSession.into(), - Attribute::SyncCookie.into(), - Attribute::SyncCredentialPortal.into(), - Attribute::SyncYieldAuthority.into(), - ], - systemexcludes: vec![EntryClass::Account.into()], - ..Default::default() -}; - pub static ref SCHEMA_CLASS_SYNC_ACCOUNT_DL6: SchemaClass = SchemaClass { uuid: UUID_SCHEMA_CLASS_SYNC_ACCOUNT, name: EntryClass::SyncAccount.into(), @@ -1107,29 +1021,6 @@ pub static ref SCHEMA_CLASS_SYNC_ACCOUNT_DL7: SchemaClass = SchemaClass { ..Default::default() }; -pub static ref SCHEMA_CLASS_DOMAIN_INFO: SchemaClass = SchemaClass { - uuid: UUID_SCHEMA_CLASS_DOMAIN_INFO, - name: EntryClass::DomainInfo.into(), - description: "Local domain information and partial configuration".to_string(), - - systemmay: vec![ - Attribute::DomainSsid.into(), - Attribute::DomainLdapBasedn.into(), - Attribute::LdapAllowUnixPwBind.into() - ], - systemmust: vec![ - Attribute::Name.into(), - Attribute::DomainUuid.into(), - Attribute::DomainName.into(), - Attribute::DomainDisplayName.into(), - Attribute::FernetPrivateKeyStr.into(), - Attribute::Es256PrivateKeyDer.into(), - Attribute::PrivateCookieKey.into(), - Attribute::Version.into(), - ], - ..Default::default() -}; - pub static ref SCHEMA_CLASS_DOMAIN_INFO_DL6: SchemaClass = SchemaClass { uuid: UUID_SCHEMA_CLASS_DOMAIN_INFO, name: EntryClass::DomainInfo.into(), @@ -1213,31 +1104,6 @@ pub static ref SCHEMA_CLASS_SYSTEM_CONFIG: SchemaClass = SchemaClass { ..Default::default() }; -pub static ref SCHEMA_CLASS_OAUTH2_RS: SchemaClass = SchemaClass { - uuid: UUID_SCHEMA_CLASS_OAUTH2_RS, - name: EntryClass::OAuth2ResourceServer.into(), - description: "The class representing a configured Oauth2 Resource Server".to_string(), - - systemmay: vec![ - Attribute::Description.into(), - Attribute::OAuth2RsScopeMap.into(), - Attribute::OAuth2RsSupScopeMap.into(), - Attribute::Rs256PrivateKeyDer.into(), - Attribute::OAuth2JwtLegacyCryptoEnable.into(), - Attribute::OAuth2PreferShortUsername.into(), - Attribute::OAuth2RsOriginLanding.into(), - Attribute::Image.into(), - ], - systemmust: vec![ - Attribute::OAuth2RsName.into(), - Attribute::DisplayName.into(), - Attribute::OAuth2RsOrigin.into(), - Attribute::OAuth2RsTokenKey.into(), - Attribute::Es256PrivateKeyDer.into(), - ], - ..Default::default() -}; - pub static ref SCHEMA_CLASS_OAUTH2_RS_DL4: SchemaClass = SchemaClass { uuid: UUID_SCHEMA_CLASS_OAUTH2_RS, name: EntryClass::OAuth2ResourceServer.into(), @@ -1315,17 +1181,6 @@ pub static ref SCHEMA_CLASS_OAUTH2_RS_DL7: SchemaClass = SchemaClass { ..Default::default() }; -pub static ref SCHEMA_CLASS_OAUTH2_RS_BASIC: SchemaClass = SchemaClass { - uuid: UUID_SCHEMA_CLASS_OAUTH2_RS_BASIC, - name: EntryClass::OAuth2ResourceServerBasic.into(), - description: "The class representing a configured Oauth2 Resource Server authenticated with http basic authentication".to_string(), - - systemmay: vec![ Attribute::OAuth2AllowInsecureClientDisablePkce.into()], - systemmust: vec![ Attribute::OAuth2RsBasicSecret.into()], - systemexcludes: vec![ EntryClass::OAuth2ResourceServerPublic.into()], - ..Default::default() -}; - pub static ref SCHEMA_CLASS_OAUTH2_RS_BASIC_DL5: SchemaClass = SchemaClass { uuid: UUID_SCHEMA_CLASS_OAUTH2_RS_BASIC, name: EntryClass::OAuth2ResourceServerBasic.into(), @@ -1339,15 +1194,6 @@ pub static ref SCHEMA_CLASS_OAUTH2_RS_BASIC_DL5: SchemaClass = SchemaClass { ..Default::default() }; -pub static ref SCHEMA_CLASS_OAUTH2_RS_PUBLIC: SchemaClass = SchemaClass { - uuid: UUID_SCHEMA_CLASS_OAUTH2_RS_PUBLIC, - name: EntryClass::OAuth2ResourceServerPublic.into(), - description: "The class representing a configured Oauth2 Resource Server with public clients and pkce verification".to_string(), - - systemexcludes: vec![EntryClass::OAuth2ResourceServerBasic.into()], - ..Default::default() -}; - // Introduced in DomainLevel4 pub static ref SCHEMA_CLASS_OAUTH2_RS_PUBLIC_DL4: SchemaClass = SchemaClass { uuid: UUID_SCHEMA_CLASS_OAUTH2_RS_PUBLIC, diff --git a/server/lib/src/idm/account.rs b/server/lib/src/idm/account.rs index 49beecb52..4c7eb644a 100644 --- a/server/lib/src/idm/account.rs +++ b/server/lib/src/idm/account.rs @@ -970,7 +970,7 @@ mod tests { #[test] fn test_idm_account_from_anonymous() { - let account: Account = BUILTIN_ACCOUNT_ANONYMOUS_V1.clone().into(); + let account: Account = BUILTIN_ACCOUNT_ANONYMOUS_DL6.clone().into(); debug!("{:?}", account); // I think that's it? we may want to check anonymous mech ... } diff --git a/server/lib/src/idm/authsession.rs b/server/lib/src/idm/authsession.rs index 37c69fdfe..6651dfe71 100644 --- a/server/lib/src/idm/authsession.rs +++ b/server/lib/src/idm/authsession.rs @@ -1732,7 +1732,7 @@ mod tests { let webauthn = create_webauthn(); - let anon_account: Account = BUILTIN_ACCOUNT_ANONYMOUS_V1.clone().into(); + let anon_account: Account = BUILTIN_ACCOUNT_ANONYMOUS_DL6.clone().into(); let asd = AuthSessionData { account: anon_account, diff --git a/server/lib/src/idm/scim.rs b/server/lib/src/idm/scim.rs index 67e436c13..5aa98f6ce 100644 --- a/server/lib/src/idm/scim.rs +++ b/server/lib/src/idm/scim.rs @@ -1521,8 +1521,6 @@ impl<'a> IdmServerProxyReadTransaction<'a> { mod tests { use crate::idm::server::{IdmServerProxyWriteTransaction, IdmServerTransaction}; use crate::prelude::*; - use crate::server::keys::KeyProvidersTransaction; - use crate::value::KeyStatus; use compact_jwt::traits::JwsVerifiable; use compact_jwt::{Jws, JwsCompact, JwsEs256Signer, JwsSigner}; use kanidm_proto::internal::ApiTokenPurpose; @@ -1730,117 +1728,6 @@ mod tests { assert!(matches!(fail, Err(OperationError::NotAuthenticated))); } - #[idm_test(domain_level=DOMAIN_LEVEL_5)] - async fn test_idm_scim_sync_token_dl5_dl6_token( - idms: &IdmServer, - _idms_delayed: &mut IdmServerDelayed, - ) { - let ct = Duration::from_secs(TEST_CURRENT_TIME); - let mut idms_prox_write = idms.proxy_write(ct).await; - - assert_eq!( - idms_prox_write.qs_write.get_domain_version(), - DOMAIN_LEVEL_5 - ); - - let sync_uuid = Uuid::new_v4(); - - let e1 = entry_init!( - (Attribute::Class, EntryClass::Object.to_value()), - (Attribute::Class, EntryClass::SyncAccount.to_value()), - (Attribute::Name, Value::new_iname("test_scim_sync")), - (Attribute::Uuid, Value::Uuid(sync_uuid)), - ( - Attribute::Description, - Value::new_utf8s("A test sync agreement") - ) - ); - - idms_prox_write - .qs_write - .internal_create(vec![e1]) - .expect("Failed to create sync account"); - - let gte = GenerateScimSyncTokenEvent::new_internal(sync_uuid, "Sync Connector"); - - let old_sync_token = idms_prox_write - .scim_sync_generate_token(>e, ct) - .expect("failed to generate new scim sync token"); - - assert!(idms_prox_write.commit().is_ok()); - - // Now trigger 5 -> 6 - let mut idms_prox_write = idms.proxy_write(ct).await; - idms_prox_write - .qs_write - .internal_apply_domain_migration(DOMAIN_LEVEL_6) - .expect("Unable to set domain level to version 6"); - assert!(idms_prox_write.commit().is_ok()); - - // Check existing token still validates. - let mut idms_prox_write = idms.proxy_write(ct).await; - - let _ident = idms_prox_write - .validate_sync_client_auth_info_to_ident(old_sync_token.clone().into(), ct) - .expect("Failed to process old sync token to ident"); - - // Delete the old session else we get a schema violation - let modlist = ModifyList::new_purge(Attribute::SyncTokenSession.into()); - - idms_prox_write - .qs_write - .internal_modify_uuid(sync_uuid, &modlist) - .expect("Unable to delete previous sync token session"); - - let gte = GenerateScimSyncTokenEvent::new_internal(sync_uuid, "Sync Connector"); - - let new_sync_token = idms_prox_write - .scim_sync_generate_token(>e, ct) - .expect("failed to generate new scim sync token"); - - assert_ne!(old_sync_token.kid(), new_sync_token.kid()); - - let _ident = idms_prox_write - .validate_sync_client_auth_info_to_ident(new_sync_token.into(), ct) - .expect("Failed to process new sync token to ident"); - - // The former key is now on the domain object. - let key_object = idms_prox_write - .qs_write - .get_key_providers() - .get_key_object(UUID_DOMAIN_INFO) - .expect("Unable to retrieve key object by uuid"); - - // Assert the former key is now in the domain key object, and now is "retained". - let former_kid = old_sync_token.kid().unwrap().to_string(); - let status = key_object - .kid_status(&former_kid) - .expect("Failed to access kid status"); - assert_eq!(status, Some(KeyStatus::Retained)); - - assert!(idms_prox_write.commit().is_ok()); - - // Now trigger 6 -> 7 - let mut idms_prox_write = idms.proxy_write(ct).await; - idms_prox_write - .qs_write - .internal_apply_domain_migration(DOMAIN_LEVEL_7) - .expect("Unable to set domain level to version 7"); - assert!(idms_prox_write.commit().is_ok()); - - // The key on the service account is removed. - let mut idms_prox_write = idms.proxy_write(ct).await; - - let sync_entry = idms_prox_write - .qs_write - .internal_search_uuid(sync_uuid) - .expect("Unable to access service account"); - - assert!(!sync_entry.attribute_pres(Attribute::JwsEs256PrivateKey)); - - assert!(idms_prox_write.commit().is_ok()); - } - fn test_scim_sync_apply_setup_ident( idms_prox_write: &mut IdmServerProxyWriteTransaction, ct: Duration, diff --git a/server/lib/src/idm/serviceaccount.rs b/server/lib/src/idm/serviceaccount.rs index 7841ecccf..193e24433 100644 --- a/server/lib/src/idm/serviceaccount.rs +++ b/server/lib/src/idm/serviceaccount.rs @@ -423,15 +423,12 @@ impl<'a> IdmServerProxyReadTransaction<'a> { mod tests { use std::time::Duration; - use compact_jwt::traits::JwsVerifiable; use compact_jwt::{dangernoverify::JwsDangerReleaseWithoutVerify, JwsVerifier}; use kanidm_proto::internal::ApiToken; use super::{DestroyApiTokenEvent, GenerateApiTokenEvent}; use crate::idm::server::IdmServerTransaction; use crate::prelude::*; - use crate::server::keys::KeyProvidersTransaction; - use crate::value::KeyStatus; const TEST_CURRENT_TIME: u64 = 6000; @@ -520,112 +517,4 @@ mod tests { assert!(idms_prox_write.commit().is_ok()); } - - #[idm_test(domain_level=DOMAIN_LEVEL_5)] - async fn test_idm_service_account_dl5_dl6_api_token( - idms: &IdmServer, - _idms_delayed: &mut IdmServerDelayed, - ) { - let ct = Duration::from_secs(TEST_CURRENT_TIME); - let exp = Duration::from_secs(TEST_CURRENT_TIME + 6000); - - let mut idms_prox_write = idms.proxy_write(ct).await; - - assert_eq!( - idms_prox_write.qs_write.get_domain_version(), - DOMAIN_LEVEL_5 - ); - - let testaccount_uuid = Uuid::new_v4(); - - let e1 = entry_init!( - (Attribute::Class, EntryClass::Object.to_value()), - (Attribute::Class, EntryClass::Account.to_value()), - (Attribute::Class, EntryClass::ServiceAccount.to_value()), - (Attribute::Name, Value::new_iname("test_account_only")), - (Attribute::Uuid, Value::Uuid(testaccount_uuid)), - (Attribute::Description, Value::new_utf8s("testaccount")), - (Attribute::DisplayName, Value::new_utf8s("testaccount")) - ); - - idms_prox_write - .qs_write - .internal_create(vec![e1]) - .expect("Failed to create service account"); - - let gte = GenerateApiTokenEvent::new_internal(testaccount_uuid, "TestToken", Some(exp)); - - let api_token = idms_prox_write - .service_account_generate_api_token(>e, ct) - .expect("failed to generate new api token"); - - trace!(?api_token); - - assert!(idms_prox_write.commit().is_ok()); - - // Now trigger 5 -> 6 - let mut idms_prox_write = idms.proxy_write(ct).await; - idms_prox_write - .qs_write - .internal_apply_domain_migration(DOMAIN_LEVEL_6) - .expect("Unable to set domain level to version 6"); - assert!(idms_prox_write.commit().is_ok()); - - // Now check our api token still validates. - let mut idms_prox_write = idms.proxy_write(ct).await; - - // Check a new token is domain key signed. - let gte = GenerateApiTokenEvent::new_internal(testaccount_uuid, "TestToken", Some(exp)); - - let new_api_token = idms_prox_write - .service_account_generate_api_token(>e, ct) - .expect("failed to generate new api token"); - - assert_ne!(api_token.kid(), new_api_token.kid()); - - // Check that both tokens verify and work. - let _ident = idms_prox_write - .validate_client_auth_info_to_ident(api_token.clone().into(), ct) - .expect("Unable to verify old api token."); - - let _ident = idms_prox_write - .validate_client_auth_info_to_ident(new_api_token.clone().into(), ct) - .expect("Unable to verify new api token."); - - // The former key is now on the domain object. - let key_object = idms_prox_write - .qs_write - .get_key_providers() - .get_key_object(UUID_DOMAIN_INFO) - .expect("Unable to retrieve key object by uuid"); - - // Assert the former key is now in the domain key object, and now is "retained". - let former_kid = api_token.kid().unwrap().to_string(); - let status = key_object - .kid_status(&former_kid) - .expect("Failed to access kid status"); - assert_eq!(status, Some(KeyStatus::Retained)); - - assert!(idms_prox_write.commit().is_ok()); - - // Now trigger 6 -> 7 - let mut idms_prox_write = idms.proxy_write(ct).await; - idms_prox_write - .qs_write - .internal_apply_domain_migration(DOMAIN_LEVEL_7) - .expect("Unable to set domain level to version 7"); - assert!(idms_prox_write.commit().is_ok()); - - // The key on the service account is removed. - let mut idms_prox_write = idms.proxy_write(ct).await; - - let service_entry = idms_prox_write - .qs_write - .internal_search_uuid(testaccount_uuid) - .expect("Unable to access service account"); - - assert!(!service_entry.attribute_pres(Attribute::JwsEs256PrivateKey)); - - assert!(idms_prox_write.commit().is_ok()); - } } diff --git a/server/lib/src/server/access/mod.rs b/server/lib/src/server/access/mod.rs index 4ce0846b9..3427e1de6 100644 --- a/server/lib/src/server/access/mod.rs +++ b/server/lib/src/server/access/mod.rs @@ -3099,7 +3099,7 @@ mod tests { #[test] fn test_access_delete_protect_system_ranges() { - let ev1: EntryInitNew = BUILTIN_ACCOUNT_ANONYMOUS_V1.clone().into(); + let ev1: EntryInitNew = BUILTIN_ACCOUNT_ANONYMOUS_DL6.clone().into(); let ev1 = ev1.into_sealed_committed(); let r_set = vec![Arc::new(ev1)]; diff --git a/server/lib/src/server/keys/provider.rs b/server/lib/src/server/keys/provider.rs index a87aba1e2..dff3eeddc 100644 --- a/server/lib/src/server/keys/provider.rs +++ b/server/lib/src/server/keys/provider.rs @@ -292,6 +292,7 @@ impl<'a> KeyProvidersWriteTransaction<'a> { } } +/* #[cfg(test)] mod tests { use super::{KeyProvider, KeyProvidersTransaction}; @@ -299,85 +300,5 @@ mod tests { use crate::value::KeyStatus; use compact_jwt::{JwsEs256Signer, JwsSigner}; - #[qs_test(domain_level=DOMAIN_LEVEL_5)] - async fn test_key_provider_internal_migration(server: &QueryServer) { - let mut write_txn = server.write(duration_from_epoch_now()).await; - - // Read the initial state of the domain object, including it's - // private key. - let domain_object_initial = write_txn - .internal_search_uuid(UUID_DOMAIN_INFO) - .expect("unable to access domain object"); - - let initial_private_es256_key = domain_object_initial - .get_ava_single_private_binary(Attribute::Es256PrivateKeyDer) - .map(|s| s.to_vec()) - .expect("No private key found"); - - let initial_jwt_signer = - JwsEs256Signer::from_es256_der(&initial_private_es256_key).unwrap(); - - let former_kid = initial_jwt_signer.get_legacy_kid().to_string(); - - // Set the version to 6. - write_txn - .internal_apply_domain_migration(DOMAIN_LEVEL_6) - .expect("Unable to set domain level to version 6"); - - // The internal key provider is created from dl 5 to 6 - let key_provider_object = write_txn - .internal_search_uuid(UUID_KEY_PROVIDER_INTERNAL) - .expect("Unable to find key provider entry."); - - assert!(key_provider_object.attribute_equality( - Attribute::Name, - &PartialValue::new_iname("key_provider_internal") - )); - - // Check that is loaded in the qs. - let key_provider = write_txn - .get_key_providers() - .get_uuid(UUID_KEY_PROVIDER_INTERNAL) - .expect("Unable to access key provider object."); - - // Because there is only one variant today ... - #[allow(irrefutable_let_patterns)] - let KeyProvider::Internal(key_provider_internal) = key_provider - else { - unreachable!() - }; - - // Run the providers internal test - assert!(key_provider_internal.test().is_ok()); - - // Now at this point, the domain object should now be a key object, and have it's - // keys migrated. - let key_object = write_txn - .get_key_providers() - .get_key_object(UUID_DOMAIN_INFO) - .expect("Unable to retrieve key object by uuid"); - - // Assert the former key is now in the domain key object, and now is "retained". - let status = key_object - .kid_status(&former_kid) - .expect("Failed to access kid status"); - assert_eq!(status, Some(KeyStatus::Retained)); - - // Now from DL6 -> 7 the keys are actually removed. - write_txn - .internal_apply_domain_migration(DOMAIN_LEVEL_7) - .expect("Unable to set domain level to version 7"); - - let domain_object_migrated = write_txn - .internal_search_uuid(UUID_DOMAIN_INFO) - .expect("unable to access domain object"); - - assert!(!domain_object_migrated.attribute_pres(Attribute::Es256PrivateKeyDer)); - - assert!(!domain_object_migrated.attribute_pres(Attribute::FernetPrivateKeyStr)); - - assert!(!domain_object_migrated.attribute_pres(Attribute::PrivateCookieKey)); - - write_txn.commit().expect("Failed to commit"); - } } +*/ diff --git a/server/lib/src/server/migrations.rs b/server/lib/src/server/migrations.rs index 396e57e6d..05a73ad1b 100644 --- a/server/lib/src/server/migrations.rs +++ b/server/lib/src/server/migrations.rs @@ -1,4 +1,3 @@ -use crate::value::CredentialType; use std::time::Duration; use crate::prelude::*; @@ -80,13 +79,13 @@ impl QueryServer { // are created in the next phase of migrations. write_txn.set_phase(ServerPhase::SchemaReady); - // Init idm will now set the system config version and minimum domain - // level if none was present - write_txn.initialise_domain_info()?; - // 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()?; @@ -294,273 +293,6 @@ impl<'a> QueryServerWriteTransaction<'a> { } } - #[instrument(level = "info", skip_all)] - /// This migration will - /// * Trigger a "once off" mfa account policy rule on all persons. - pub(crate) fn migrate_domain_2_to_3(&mut self) -> Result<(), OperationError> { - let idm_all_persons = match self.internal_search_uuid(UUID_IDM_ALL_PERSONS) { - Ok(entry) => entry, - Err(OperationError::NoMatchingEntries) => return Ok(()), - Err(e) => return Err(e), - }; - - let credential_policy = - idm_all_persons.get_ava_single_credential_type(Attribute::CredentialTypeMinimum); - - if credential_policy.is_some() { - debug!("Credential policy already present, not applying change."); - return Ok(()); - } - - self.internal_modify_uuid( - UUID_IDM_ALL_PERSONS, - &ModifyList::new_purge_and_set( - Attribute::CredentialTypeMinimum, - CredentialType::Mfa.into(), - ), - ) - .map(|()| { - info!("Upgraded default account policy to enforce MFA"); - }) - } - - #[instrument(level = "info", skip_all)] - /// Migrations for Oauth to support multiple origins, and custom claims. - pub(crate) fn migrate_domain_3_to_4(&mut self) -> Result<(), OperationError> { - let idm_schema_attrs = [ - SCHEMA_ATTR_OAUTH2_RS_CLAIM_MAP_DL4.clone().into(), - SCHEMA_ATTR_OAUTH2_ALLOW_LOCALHOST_REDIRECT_DL4 - .clone() - .into(), - ]; - - idm_schema_attrs - .into_iter() - .try_for_each(|entry| self.internal_migrate_or_create(entry)) - .map_err(|err| { - error!(?err, "migrate_domain_3_to_4 -> Error"); - err - })?; - - let idm_schema_classes = [ - SCHEMA_CLASS_OAUTH2_RS_DL4.clone().into(), - SCHEMA_CLASS_OAUTH2_RS_PUBLIC_DL4.clone().into(), - IDM_ACP_OAUTH2_MANAGE_DL4.clone().into(), - ]; - - idm_schema_classes - .into_iter() - .try_for_each(|entry| self.internal_migrate_or_create(entry)) - .map_err(|err| { - error!(?err, "migrate_domain_3_to_4 -> Error"); - err - }) - } - - #[instrument(level = "info", skip_all)] - /// Migrations for Oauth to move rs name from a dedicated type to name - /// and to allow oauth2 sessions on resource servers for client credentials - /// grants. Accounts, persons and service accounts have some attributes - /// relocated to allow oauth2 rs to become accounts. - pub(crate) fn migrate_domain_4_to_5(&mut self) -> Result<(), OperationError> { - let idm_schema_classes = [ - SCHEMA_CLASS_PERSON_DL5.clone().into(), - SCHEMA_CLASS_ACCOUNT_DL5.clone().into(), - SCHEMA_CLASS_SERVICE_ACCOUNT_DL5.clone().into(), - SCHEMA_CLASS_OAUTH2_RS_DL5.clone().into(), - SCHEMA_CLASS_OAUTH2_RS_BASIC_DL5.clone().into(), - IDM_ACP_OAUTH2_MANAGE_DL5.clone().into(), - ]; - - idm_schema_classes - .into_iter() - .try_for_each(|entry| self.internal_migrate_or_create(entry)) - .map_err(|err| { - error!(?err, "migrate_domain_4_to_5 -> Error"); - err - })?; - - // Reload mid txn so that the next modification works. - self.force_schema_reload(); - self.reload()?; - - // Now we remove attributes from service accounts that have been unable to be set - // via a user interface for more than a year. - let filter = filter!(f_and!([ - f_eq(Attribute::Class, EntryClass::Account.into()), - f_eq(Attribute::Class, EntryClass::ServiceAccount.into()), - ])); - let modlist = ModifyList::new_list(vec![ - Modify::Purged(Attribute::PassKeys.into()), - Modify::Purged(Attribute::AttestedPasskeys.into()), - Modify::Purged(Attribute::CredentialUpdateIntentToken.into()), - Modify::Purged(Attribute::RadiusSecret.into()), - ]); - self.internal_modify(&filter, &modlist)?; - - // Now move all oauth2 rs name. - let filter = filter!(f_and!([ - f_eq(Attribute::Class, EntryClass::OAuth2ResourceServer.into()), - f_pres(Attribute::OAuth2RsName), - ])); - - let pre_candidates = self.internal_search(filter).map_err(|err| { - admin_error!(?err, "migrate_domain_4_to_5 internal search failure"); - err - })?; - - let modset: Vec<_> = pre_candidates - .into_iter() - .filter_map(|ent| { - ent.get_ava_single_iname(Attribute::OAuth2RsName) - .map(|rs_name| { - let modlist = vec![ - Modify::Present(Attribute::Class.into(), EntryClass::Account.into()), - Modify::Present(Attribute::Name.into(), Value::new_iname(rs_name)), - m_purge(Attribute::OAuth2RsName), - ]; - - (ent.get_uuid(), ModifyList::new_list(modlist)) - }) - }) - .collect(); - - // If there is nothing, we don't need to do anything. - if modset.is_empty() { - admin_info!("migrate_domain_4_to_5 no entries to migrate, complete"); - return Ok(()); - } - - // Apply the batch mod. - self.internal_batch_modify(modset.into_iter()) - } - - /// Migration domain level 5 to 6 - support query limits in account policy. - #[instrument(level = "info", skip_all)] - pub(crate) fn migrate_domain_5_to_6(&mut self) -> Result<(), OperationError> { - let idm_schema_classes = [ - 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(), - SCHEMA_CLASS_ACCOUNT_POLICY_DL6.clone().into(), - SCHEMA_CLASS_DOMAIN_INFO_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(), - ]; - - idm_schema_classes - .into_iter() - .try_for_each(|entry| self.internal_migrate_or_create(entry)) - .map_err(|err| { - error!(?err, "migrate_domain_5_to_6 -> Error"); - err - })?; - - self.reload()?; - - let idm_data = [ - // Update access controls. - IDM_ACP_GROUP_ACCOUNT_POLICY_MANAGE_DL6.clone().into(), - IDM_ACP_PEOPLE_CREATE_DL6.clone().into(), - IDM_ACP_GROUP_MANAGE_DL6.clone().into(), - IDM_ACP_ACCOUNT_MAIL_READ_DL6.clone().into(), - // Update anonymous with the correct entry manager, - BUILTIN_ACCOUNT_ANONYMOUS_DL6.clone().into(), - // Add the internal key provider. - E_KEY_PROVIDER_INTERNAL_DL6.clone(), - ]; - - idm_data - .into_iter() - .try_for_each(|entry| self.internal_migrate_or_create(entry)) - .map_err(|err| { - error!(?err, "migrate_domain_5_to_6 -> Error"); - err - })?; - - // all existing built-in objects get a builtin class - let filter = f_lt( - Attribute::Uuid, - PartialValue::Uuid(DYNAMIC_RANGE_MINIMUM_UUID), - ); - let modlist = modlist!([m_pres(Attribute::Class, &EntryClass::Builtin.into())]); - - self.internal_modify(&filter!(filter), &modlist)?; - - // Reload such that the new default key provider is loaded. - self.reload()?; - - // Update the domain entry to contain its key object, which can now be generated. - let idm_data = [ - IDM_ACP_DOMAIN_ADMIN_DL6.clone().into(), - E_DOMAIN_INFO_DL6.clone(), - ]; - idm_data - .into_iter() - .try_for_each(|entry| self.internal_migrate_or_create(entry)) - .map_err(|err| { - error!(?err, "migrate_domain_5_to_6 -> Error"); - err - })?; - - // At this point we reload to show the new key objects on the domain. - self.reload()?; - - // Migrate the domain key to a retained key on the key object. - let domain_es256_private_key = self.get_domain_es256_private_key().map_err(|err| { - error!(?err, "migrate_domain_5_to_6 -> Error"); - err - })?; - - // Migrate all service/scim account keys to the domain key for verification. - let filter = filter!(f_or!([ - f_eq(Attribute::Class, EntryClass::ServiceAccount.into()), - f_eq(Attribute::Class, EntryClass::SyncAccount.into()) - ])); - let entry_keys_to_migrate = self.internal_search(filter)?; - - let mut modlist = Vec::with_capacity(1 + entry_keys_to_migrate.len()); - - modlist.push(Modify::Present( - Attribute::KeyActionImportJwsEs256.into(), - Value::PrivateBinary(domain_es256_private_key), - )); - - for entry in entry_keys_to_migrate { - // In these entries, the keys are in JwsEs256PrivateKey. - if let Some(jws_signer) = - entry.get_ava_single_jws_key_es256(Attribute::JwsEs256PrivateKey) - { - let es256_private_key = jws_signer.private_key_to_der().map_err(|err| { - error!(?err, uuid = ?entry.get_display_id(), "unable to convert signer to der"); - OperationError::InvalidValueState - })?; - - modlist.push(Modify::Present( - Attribute::KeyActionImportJwsEs256.into(), - Value::PrivateBinary(es256_private_key), - )); - } - } - - let modlist = ModifyList::new_list(modlist); - - self.internal_modify_uuid(UUID_DOMAIN_INFO, &modlist)?; - - Ok(()) - } - /// Migration domain level 6 to 7 #[instrument(level = "info", skip_all)] pub(crate) fn migrate_domain_6_to_7(&mut self) -> Result<(), OperationError> { @@ -859,6 +591,19 @@ impl<'a> QueryServerWriteTransaction<'a> { Ok(()) } + /// Migration domain level 8 to 9 + #[instrument(level = "info", skip_all)] + pub(crate) fn migrate_domain_8_to_9(&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 ============== + + Ok(()) + } + #[instrument(level = "info", skip_all)] pub fn initialise_schema_core(&mut self) -> Result<(), OperationError> { admin_debug!("initialise_schema_core -> start ..."); @@ -966,6 +711,20 @@ impl<'a> QueryServerWriteTransaction<'a> { 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(), ]; let r = idm_schema @@ -984,21 +743,30 @@ impl<'a> QueryServerWriteTransaction<'a> { // // DO NOT MODIFY THIS DEFINITION let idm_schema_classes_dl1: Vec = vec![ - SCHEMA_CLASS_ACCOUNT.clone().into(), - SCHEMA_CLASS_ACCOUNT_POLICY.clone().into(), - SCHEMA_CLASS_DOMAIN_INFO.clone().into(), SCHEMA_CLASS_DYNGROUP.clone().into(), - SCHEMA_CLASS_GROUP.clone().into(), - SCHEMA_CLASS_OAUTH2_RS.clone().into(), SCHEMA_CLASS_ORGPERSON.clone().into(), - SCHEMA_CLASS_PERSON.clone().into(), SCHEMA_CLASS_POSIXACCOUNT.clone().into(), SCHEMA_CLASS_POSIXGROUP.clone().into(), - SCHEMA_CLASS_SERVICE_ACCOUNT.clone().into(), - SCHEMA_CLASS_SYNC_ACCOUNT.clone().into(), SCHEMA_CLASS_SYSTEM_CONFIG.clone().into(), - SCHEMA_CLASS_OAUTH2_RS_BASIC.clone().into(), - SCHEMA_CLASS_OAUTH2_RS_PUBLIC.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_DOMAIN_INFO_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(), ]; let r: Result<(), _> = idm_schema_classes_dl1 @@ -1018,18 +786,24 @@ impl<'a> QueryServerWriteTransaction<'a> { #[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> { - // First, check the system_info object. This stores some server information - // and details. It's a pretty const thing. Also check anonymous, important to many - // concepts. - let res = self - .internal_migrate_or_create(E_SYSTEM_INFO_V1.clone()) - .and_then(|_| self.internal_migrate_or_create(E_DOMAIN_INFO_V1.clone())) - .and_then(|_| self.internal_migrate_or_create(E_SYSTEM_CONFIG_V1.clone())); - if res.is_err() { - admin_error!("initialise_domain_info -> result {:?}", res); - } - debug_assert!(res.is_ok()); - res + // 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)] @@ -1048,9 +822,9 @@ impl<'a> QueryServerWriteTransaction<'a> { // Each item individually logs it's result .try_for_each(|ent| self.internal_migrate_or_create(ent)); if res.is_ok() { - admin_debug!("initialise_idm p1 -> result Ok!"); + debug!("initialise_idm p1 -> result Ok!"); } else { - admin_error!(?res, "initialise_idm p1 -> result"); + error!(?res, "initialise_idm p1 -> result"); } debug_assert!(res.is_ok()); res?; @@ -1059,9 +833,9 @@ impl<'a> QueryServerWriteTransaction<'a> { .into_iter() .try_for_each(|e| self.internal_migrate_or_create(e.clone().try_into()?)); if res.is_ok() { - admin_debug!("initialise_idm p2 -> result Ok!"); + debug!("initialise_idm p2 -> result Ok!"); } else { - admin_error!(?res, "initialise_idm p2 -> result"); + error!(?res, "initialise_idm p2 -> result"); } debug_assert!(res.is_ok()); res?; @@ -1079,9 +853,6 @@ impl<'a> QueryServerWriteTransaction<'a> { IDM_ACP_ACP_MANAGE_V1.clone(), IDM_ACP_GROUP_ENTRY_MANAGED_BY_MODIFY_V1.clone(), IDM_ACP_GROUP_ENTRY_MANAGER_V1.clone(), - IDM_ACP_GROUP_ACCOUNT_POLICY_MANAGE_V1.clone(), - IDM_ACP_OAUTH2_MANAGE_V1.clone(), - IDM_ACP_DOMAIN_ADMIN_V1.clone(), IDM_ACP_SYNC_ACCOUNT_MANAGE_V1.clone(), IDM_ACP_RADIUS_SERVERS_V1.clone(), IDM_ACP_RADIUS_SECRET_MANAGE_V1.clone(), @@ -1091,16 +862,13 @@ impl<'a> QueryServerWriteTransaction<'a> { IDM_ACP_ACCOUNT_SELF_WRITE_V1.clone(), IDM_ACP_SELF_NAME_WRITE_V1.clone(), IDM_ACP_ALL_ACCOUNTS_POSIX_READ_V1.clone(), - IDM_ACP_ACCOUNT_MAIL_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_GROUP_MANAGE_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_CREATE_V1.clone(), IDM_ACP_PEOPLE_READ_V1.clone(), IDM_ACP_PEOPLE_MANAGE_V1.clone(), IDM_ACP_PEOPLE_DELETE_V1.clone(), @@ -1112,6 +880,15 @@ impl<'a> QueryServerWriteTransaction<'a> { 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().into(), + // DL6 + IDM_ACP_GROUP_ACCOUNT_POLICY_MANAGE_DL6.clone().into(), + 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(), ]; let res: Result<(), _> = idm_entries @@ -1375,46 +1152,6 @@ mod tests { } } - #[qs_test(domain_level=DOMAIN_LEVEL_5)] - async fn test_migrations_dl5_dl6(server: &QueryServer) { - // Assert our instance was setup to version 5 - let mut write_txn = server.write(duration_from_epoch_now()).await; - - let db_domain_version = write_txn - .internal_search_uuid(UUID_DOMAIN_INFO) - .expect("unable to access domain entry") - .get_ava_single_uint32(Attribute::Version) - .expect("Attribute Version not present"); - - assert_eq!(db_domain_version, DOMAIN_LEVEL_5); - - // Entry doesn't exist yet. - let _entry_not_found = write_txn - .internal_search_uuid(UUID_SCHEMA_ATTR_LIMIT_SEARCH_MAX_RESULTS) - .expect_err("unable to newly migrated schema entry"); - - // Set the version to 6. - write_txn - .internal_modify_uuid( - UUID_DOMAIN_INFO, - &ModifyList::new_purge_and_set( - Attribute::Version, - Value::new_uint32(DOMAIN_LEVEL_6), - ), - ) - .expect("Unable to set domain level to version 6"); - - // Re-load - this applies the migrations. - write_txn.reload().expect("Unable to reload transaction"); - - // It now exists as the migrations were run. - let _entry = write_txn - .internal_search_uuid(UUID_SCHEMA_ATTR_LIMIT_SEARCH_MAX_RESULTS) - .expect("unable to newly migrated schema entry"); - - write_txn.commit().expect("Unable to commit"); - } - #[qs_test(domain_level=DOMAIN_LEVEL_6)] async fn test_migrations_dl6_dl7(server: &QueryServer) { // Assert our instance was setup to version 6 @@ -1460,18 +1197,9 @@ mod tests { // Set the version to 7. write_txn - .internal_modify_uuid( - UUID_DOMAIN_INFO, - &ModifyList::new_purge_and_set( - Attribute::Version, - Value::new_uint32(DOMAIN_LEVEL_7), - ), - ) + .internal_apply_domain_migration(DOMAIN_LEVEL_7) .expect("Unable to set domain level to version 7"); - // Re-load - this applies the migrations. - write_txn.reload().expect("Unable to reload transaction"); - // post migration verification. let domain_entry = write_txn .internal_search_uuid(UUID_DOMAIN_INFO) @@ -1582,20 +1310,14 @@ mod tests { // Set the version to 8. write_txn - .internal_modify_uuid( - UUID_DOMAIN_INFO, - &ModifyList::new_purge_and_set( - Attribute::Version, - Value::new_uint32(DOMAIN_LEVEL_8), - ), - ) + .internal_apply_domain_migration(DOMAIN_LEVEL_8) .expect("Unable to set domain level to version 8"); - // Re-load - this applies the migrations. - write_txn.reload().expect("Unable to reload transaction"); - // post migration verification. write_txn.commit().expect("Unable to commit"); } + + #[qs_test(domain_level=DOMAIN_LEVEL_8)] + async fn test_migrations_dl8_dl9(_server: &QueryServer) {} } diff --git a/server/lib/src/server/mod.rs b/server/lib/src/server/mod.rs index 996cd6103..1b3bf68e3 100644 --- a/server/lib/src/server/mod.rs +++ b/server/lib/src/server/mod.rs @@ -1890,20 +1890,13 @@ 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); - if previous_version <= DOMAIN_LEVEL_2 && domain_info_version >= DOMAIN_LEVEL_3 { - self.migrate_domain_2_to_3()?; - } - - if previous_version <= DOMAIN_LEVEL_3 && domain_info_version >= DOMAIN_LEVEL_4 { - self.migrate_domain_3_to_4()?; - } - - if previous_version <= DOMAIN_LEVEL_4 && domain_info_version >= DOMAIN_LEVEL_5 { - self.migrate_domain_4_to_5()?; - } - - if previous_version <= DOMAIN_LEVEL_5 && domain_info_version >= DOMAIN_LEVEL_6 { - self.migrate_domain_5_to_6()?; + // We have to check for DL0 since that's the initialisation level. + if previous_version <= DOMAIN_LEVEL_5 && previous_version != DOMAIN_LEVEL_0 { + 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); + error!(domain_previous_patch_level = ?previous_patch_level, domain_target_patch_level = ?domain_info_patch_level); + return Err(OperationError::MG0008SkipUpgradeAttempted); } if previous_version <= DOMAIN_LEVEL_6 && domain_info_version >= DOMAIN_LEVEL_7 { @@ -1920,6 +1913,10 @@ impl<'a> QueryServerWriteTransaction<'a> { self.migrate_domain_7_to_8()?; } + if previous_version <= DOMAIN_LEVEL_8 && domain_info_version >= DOMAIN_LEVEL_9 { + self.migrate_domain_8_to_9()?; + } + // 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. diff --git a/server/testkit/tests/oauth2_test.rs b/server/testkit/tests/oauth2_test.rs index ad09744b0..48c86af64 100644 --- a/server/testkit/tests/oauth2_test.rs +++ b/server/testkit/tests/oauth2_test.rs @@ -48,6 +48,7 @@ const TEST_INTEGRATION_RS_ID: &str = "test_integration"; const TEST_INTEGRATION_RS_GROUP_ALL: &str = "idm_all_accounts"; const TEST_INTEGRATION_RS_DISPLAY: &str = "Test Integration"; const TEST_INTEGRATION_RS_URL: &str = "https://demo.example.com"; +const TEST_INTEGRATION_REDIRECT_URL: &str = "https://demo.example.com/oauth2/flow"; #[kanidmd_testkit::test] async fn test_oauth2_openid_basic_flow(rsclient: KanidmClient) { @@ -66,6 +67,14 @@ async fn test_oauth2_openid_basic_flow(rsclient: KanidmClient) { .await .expect("Failed to create oauth2 config"); + rsclient + .idm_oauth2_client_add_origin( + TEST_INTEGRATION_RS_ID, + &Url::parse(TEST_INTEGRATION_REDIRECT_URL).expect("Invalid URL"), + ) + .await + .expect("Failed to update oauth2 config"); + // Extend the admin account with extended details for openid claims. rsclient .idm_person_account_create("oauth_test", "oauth_test") @@ -236,7 +245,7 @@ async fn test_oauth2_openid_basic_flow(rsclient: KanidmClient) { ("state", "YWJjZGVm"), ("code_challenge", pkce_code_challenge.as_str()), ("code_challenge_method", "S256"), - ("redirect_uri", "https://demo.example.com/oauth2/flow"), + ("redirect_uri", TEST_INTEGRATION_REDIRECT_URL), ("scope", "email read openid"), ]) .send() @@ -303,7 +312,7 @@ async fn test_oauth2_openid_basic_flow(rsclient: KanidmClient) { let form_req: AccessTokenRequest = GrantTypeReq::AuthorizationCode { code: code.to_string(), - redirect_uri: Url::parse("https://demo.example.com/oauth2/flow").expect("Invalid URL"), + redirect_uri: Url::parse(TEST_INTEGRATION_REDIRECT_URL).expect("Invalid URL"), code_verifier: Some(pkce_code_verifier.secret().clone()), } .into(); @@ -496,6 +505,14 @@ async fn test_oauth2_openid_public_flow(rsclient: KanidmClient) { .await .expect("Failed to create oauth2 config"); + rsclient + .idm_oauth2_client_add_origin( + TEST_INTEGRATION_RS_ID, + &Url::parse(TEST_INTEGRATION_REDIRECT_URL).expect("Invalid URL"), + ) + .await + .expect("Failed to update oauth2 config"); + // Extend the admin account with extended details for openid claims. rsclient .idm_person_account_create("oauth_test", "oauth_test") @@ -616,7 +633,7 @@ async fn test_oauth2_openid_public_flow(rsclient: KanidmClient) { ("state", "YWJjZGVm"), ("code_challenge", pkce_code_challenge.as_str()), ("code_challenge_method", "S256"), - ("redirect_uri", "https://demo.example.com/oauth2/flow"), + ("redirect_uri", TEST_INTEGRATION_REDIRECT_URL), ("scope", "email read openid"), ]) .send() @@ -683,7 +700,7 @@ async fn test_oauth2_openid_public_flow(rsclient: KanidmClient) { let form_req = AccessTokenRequest { grant_type: GrantTypeReq::AuthorizationCode { code: code.to_string(), - redirect_uri: Url::parse("https://demo.example.com/oauth2/flow").expect("Invalid URL"), + redirect_uri: Url::parse(TEST_INTEGRATION_REDIRECT_URL).expect("Invalid URL"), code_verifier: Some(pkce_code_verifier.secret().clone()), }, client_id: Some("test_integration".to_string()),