Allow opt-in of easter eggs (#3308)

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

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),