Allow opt-in of easter eggs ()

So that we can start to add some more easter eggs to the server,
we also need to respect user preferences that may not want them.

This adds a configuration setting to the domain allowing a release
build to opt-in to easter eggs, and development builds to opt-out
of them.
This commit is contained in:
Firstyear 2024-12-19 13:30:35 +10:00 committed by GitHub
parent 1fbbf323fa
commit 50a7d9d700
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 143 additions and 0 deletions
libs/client/src
proto/src
server/lib/src
tools/cli/src
cli/domain
opt

View file

@ -1,4 +1,5 @@
use crate::{ClientError, KanidmClient};
use kanidm_proto::constants::ATTR_DOMAIN_ALLOW_EASTER_EGGS;
use kanidm_proto::internal::ImageValue;
use reqwest::multipart;
@ -8,6 +9,14 @@ impl KanidmClient {
self.perform_delete_request("/v1/domain/_image").await
}
pub async fn idm_set_domain_allow_easter_eggs(&self, enable: bool) -> Result<(), ClientError> {
self.perform_put_request(
&format!("{}{}", "/v1/domain/_attr/", ATTR_DOMAIN_ALLOW_EASTER_EGGS),
vec![enable.to_string()],
)
.await
}
/// Add or update the domain logo/image
pub async fn idm_domain_update_image(&self, image: ImageValue) -> Result<(), ClientError> {
let file_content_type = image.filetype.as_content_type_str();

View file

@ -53,6 +53,7 @@ pub enum Attribute {
DisplayName,
Dn,
Domain,
DomainAllowEasterEggs,
DomainDevelopmentTaint,
DomainDisplayName,
DomainLdapBasedn,
@ -282,6 +283,7 @@ impl Attribute {
Attribute::DisplayName => ATTR_DISPLAYNAME,
Attribute::Dn => ATTR_DN,
Attribute::Domain => ATTR_DOMAIN,
Attribute::DomainAllowEasterEggs => ATTR_DOMAIN_ALLOW_EASTER_EGGS,
Attribute::DomainDevelopmentTaint => ATTR_DOMAIN_DEVELOPMENT_TAINT,
Attribute::DomainDisplayName => ATTR_DOMAIN_DISPLAY_NAME,
Attribute::DomainLdapBasedn => ATTR_DOMAIN_LDAP_BASEDN,
@ -464,6 +466,7 @@ impl Attribute {
ATTR_DISPLAYNAME => Attribute::DisplayName,
ATTR_DN => Attribute::Dn,
ATTR_DOMAIN => Attribute::Domain,
ATTR_DOMAIN_ALLOW_EASTER_EGGS => Attribute::DomainAllowEasterEggs,
ATTR_DOMAIN_DISPLAY_NAME => Attribute::DomainDisplayName,
ATTR_DOMAIN_DEVELOPMENT_TAINT => Attribute::DomainDevelopmentTaint,
ATTR_DOMAIN_LDAP_BASEDN => Attribute::DomainLdapBasedn,

View file

@ -89,6 +89,7 @@ pub const ATTR_DESCRIPTION: &str = "description";
pub const ATTR_DIRECTMEMBEROF: &str = "directmemberof";
pub const ATTR_DISPLAYNAME: &str = "displayname";
pub const ATTR_DN: &str = "dn";
pub const ATTR_DOMAIN_ALLOW_EASTER_EGGS: &str = "domain_allow_easter_eggs";
pub const ATTR_DOMAIN_DEVELOPMENT_TAINT: &str = "domain_development_taint";
pub const ATTR_DOMAIN_DISPLAY_NAME: &str = "domain_display_name";
pub const ATTR_DOMAIN_LDAP_BASEDN: &str = "domain_ldap_basedn";

View file

@ -1129,6 +1129,64 @@ lazy_static! {
};
}
lazy_static! {
pub static ref IDM_ACP_DOMAIN_ADMIN_DL9: 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::DomainAllowEasterEggs,
Attribute::DomainDisplayName,
Attribute::DomainName,
Attribute::DomainLdapBasedn,
Attribute::DomainSsid,
Attribute::DomainUuid,
Attribute::KeyInternalData,
Attribute::LdapAllowUnixPwBind,
Attribute::Version,
Attribute::Image,
],
modify_removed_attrs: vec![
Attribute::DomainDisplayName,
Attribute::DomainSsid,
Attribute::DomainLdapBasedn,
Attribute::DomainAllowEasterEggs,
Attribute::LdapAllowUnixPwBind,
Attribute::KeyActionRevoke,
Attribute::KeyActionRotate,
Attribute::Image,
],
modify_present_attrs: vec![
Attribute::DomainDisplayName,
Attribute::DomainLdapBasedn,
Attribute::DomainSsid,
Attribute::DomainAllowEasterEggs,
Attribute::LdapAllowUnixPwBind,
Attribute::KeyActionRevoke,
Attribute::KeyActionRotate,
Attribute::Image,
],
..Default::default()
};
}
lazy_static! {
pub static ref IDM_ACP_SYNC_ACCOUNT_MANAGE_V1: BuiltinAcp = BuiltinAcp {
classes: vec![

View file

@ -770,6 +770,14 @@ pub static ref SCHEMA_ATTR_DOMAIN_DEVELOPMENT_TAINT_DL7: SchemaAttribute = Schem
..Default::default()
};
pub static ref SCHEMA_ATTR_DOMAIN_ALLOW_EASTER_EGGS_DL9: SchemaAttribute = SchemaAttribute {
uuid: UUID_SCHEMA_ATTR_DOMAIN_ALLOW_EASTER_EGGS,
name: Attribute::DomainAllowEasterEggs,
description: "A flag to enable easter eggs in the server that may not always be wanted by all users/deployments.".to_string(),
syntax: SyntaxType::Boolean,
..Default::default()
};
pub static ref SCHEMA_ATTR_REFERS_DL7: SchemaAttribute = SchemaAttribute {
uuid: UUID_SCHEMA_ATTR_REFERS,
name: Attribute::Refers,
@ -1177,6 +1185,30 @@ pub static ref SCHEMA_CLASS_DOMAIN_INFO_DL8: SchemaClass = SchemaClass {
..Default::default()
};
pub static ref SCHEMA_CLASS_DOMAIN_INFO_DL9: SchemaClass = SchemaClass {
uuid: UUID_SCHEMA_CLASS_DOMAIN_INFO,
name: EntryClass::DomainInfo.into(),
description: "Local domain information and configuration".to_string(),
systemmay: vec![
Attribute::DomainSsid,
Attribute::DomainLdapBasedn,
Attribute::LdapAllowUnixPwBind,
Attribute::Image,
Attribute::PatchLevel,
Attribute::DomainDevelopmentTaint,
Attribute::DomainAllowEasterEggs,
],
systemmust: vec![
Attribute::Name,
Attribute::DomainUuid,
Attribute::DomainName,
Attribute::DomainDisplayName,
Attribute::Version,
],
..Default::default()
};
pub static ref SCHEMA_CLASS_POSIXGROUP: SchemaClass = SchemaClass {
uuid: UUID_SCHEMA_CLASS_POSIXGROUP,
name: EntryClass::PosixGroup.into(),

View file

@ -321,6 +321,8 @@ pub const UUID_SCHEMA_ATTR_APPLICATION_PASSWORD: Uuid =
pub const UUID_SCHEMA_ATTR_CREATED_AT_CID: Uuid = uuid!("00000000-0000-0000-0000-ffff00000184");
pub const UUID_SCHEMA_ATTR_ALLOW_PRIMARY_CRED_FALLBACK: Uuid =
uuid!("00000000-0000-0000-0000-ffff00000185");
pub const UUID_SCHEMA_ATTR_DOMAIN_ALLOW_EASTER_EGGS: Uuid =
uuid!("00000000-0000-0000-0000-ffff00000186");
// System and domain infos
// I'd like to strongly criticise william of the past for making poor choices about these allocations.

View file

@ -647,7 +647,9 @@ impl QueryServerWriteTransaction<'_> {
// 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
@ -663,6 +665,7 @@ impl QueryServerWriteTransaction<'_> {
let idm_data = [
IDM_ACP_OAUTH2_MANAGE_DL9.clone().into(),
IDM_ACP_GROUP_MANAGE_DL9.clone().into(),
IDM_ACP_DOMAIN_ADMIN_DL9.clone().into(),
];
idm_data

View file

@ -79,6 +79,7 @@ pub struct DomainInfo {
pub(crate) d_patch_level: u32,
pub(crate) d_devel_taint: bool,
pub(crate) d_ldap_allow_unix_pw_bind: bool,
pub(crate) d_allow_easter_eggs: bool,
// In future this should be image reference instead of the image itself.
d_image: Option<ImageValue>,
}
@ -103,6 +104,10 @@ impl DomainInfo {
pub fn has_custom_image(&self) -> bool {
self.d_image.is_some()
}
pub fn allow_easter_eggs(&self) -> bool {
self.d_allow_easter_eggs
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
@ -1657,6 +1662,7 @@ impl QueryServer {
// Automatically derive our current taint mode based on the PRERELEASE setting.
d_devel_taint: option_env!("KANIDM_PRE_RELEASE").is_some(),
d_ldap_allow_unix_pw_bind: false,
d_allow_easter_eggs: false,
d_image: None,
}));
@ -2284,6 +2290,11 @@ impl<'a> QueryServerWriteTransaction<'a> {
.get_ava_single_bool(Attribute::DomainDevelopmentTaint)
.unwrap_or_default();
let domain_allow_easter_eggs = domain_info
.get_ava_single_bool(Attribute::DomainAllowEasterEggs)
// This defaults to false for release versions, and true in development
.unwrap_or(option_env!("KANIDM_PRE_RELEASE").is_some());
// We have to set the domain version here so that features which check for it
// will now see it's been increased. This also prevents recursion during reloads
// inside of a domain migration.
@ -2293,6 +2304,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
mut_d_info.d_vers = domain_info_version;
mut_d_info.d_patch_level = domain_info_patch_level;
mut_d_info.d_devel_taint = domain_info_devel_taint;
mut_d_info.d_allow_easter_eggs = domain_allow_easter_eggs;
// We must both be at the correct domain version *and* the correct patch level. If we are
// not, then we only proceed to migrate *if* our server boot phase is correct.

View file

@ -12,6 +12,7 @@ impl DomainOpt {
| DomainOpt::SetImage { copt, .. }
| DomainOpt::RemoveImage { copt }
| DomainOpt::SetLdapAllowUnixPasswordBind { copt, .. }
| DomainOpt::SetAllowEasterEggs { copt, .. }
| DomainOpt::RevokeKey { copt, .. }
| DomainOpt::Show(copt) => copt.debug,
}
@ -51,6 +52,19 @@ impl DomainOpt {
Err(e) => handle_client_error(e, copt.output_mode),
}
}
DomainOpt::SetAllowEasterEggs { copt, enable } => {
let client = copt.to_client(OpType::Write).await;
match client.idm_set_domain_allow_easter_eggs(*enable).await {
Ok(_) => {
if *enable {
println!("Success 🎉 🥚 🎉")
} else {
println!("Success")
}
}
Err(e) => handle_client_error(e, copt.output_mode),
}
}
DomainOpt::Show(copt) => {
let client = copt.to_client(OpType::Read).await;
match client.idm_domain_get().await {

View file

@ -1332,6 +1332,15 @@ pub enum DomainOpt {
#[clap(name = "allow", action = clap::ArgAction::Set)]
enable: bool,
},
/// Enable or disable easter eggs in the server. This includes seasonal icons, kanidm
/// birthday surprises and other fun components. Defaults to false for production releases
/// and true in development builds.
SetAllowEasterEggs {
#[clap(flatten)]
copt: CommonOpt,
#[clap(name = "allow", action = clap::ArgAction::Set)]
enable: bool,
},
#[clap(name = "show")]
/// Show information about this system's domain
Show(CommonOpt),