mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 12:37:00 +01:00
WIP: serialization and domain info setting wonkiness (#2791)
This commit is contained in:
parent
5bbca0fb2c
commit
1d0a606e69
|
@ -25,9 +25,12 @@ use std::path::Path;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use compact_jwt::Jwk;
|
use compact_jwt::Jwk;
|
||||||
|
|
||||||
use kanidm_proto::constants::uri::V1_AUTH_VALID;
|
use kanidm_proto::constants::uri::V1_AUTH_VALID;
|
||||||
use kanidm_proto::constants::{
|
use kanidm_proto::constants::{
|
||||||
APPLICATION_JSON, ATTR_ENTRY_MANAGED_BY, ATTR_NAME, CLIENT_TOKEN_CACHE, KOPID, KVERSION,
|
APPLICATION_JSON, ATTR_DOMAIN_DISPLAY_NAME, ATTR_DOMAIN_LDAP_BASEDN, ATTR_DOMAIN_SSID,
|
||||||
|
ATTR_ENTRY_MANAGED_BY, ATTR_KEY_ACTION_REVOKE, ATTR_LDAP_ALLOW_UNIX_PW_BIND, ATTR_NAME,
|
||||||
|
CLIENT_TOKEN_CACHE, KOPID, KVERSION,
|
||||||
};
|
};
|
||||||
use kanidm_proto::internal::*;
|
use kanidm_proto::internal::*;
|
||||||
use kanidm_proto::v1::*;
|
use kanidm_proto::v1::*;
|
||||||
|
@ -71,6 +74,7 @@ pub enum ClientError {
|
||||||
ConfigParseIssue(String),
|
ConfigParseIssue(String),
|
||||||
CertParseIssue(String),
|
CertParseIssue(String),
|
||||||
UntrustedCertificate(String),
|
UntrustedCertificate(String),
|
||||||
|
InvalidRequest(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Settings describing a single instance.
|
/// Settings describing a single instance.
|
||||||
|
@ -864,6 +868,10 @@ impl KanidmClient {
|
||||||
|
|
||||||
match response.status() {
|
match response.status() {
|
||||||
reqwest::StatusCode::OK => {}
|
reqwest::StatusCode::OK => {}
|
||||||
|
reqwest::StatusCode::UNPROCESSABLE_ENTITY => {
|
||||||
|
return Err(ClientError::InvalidRequest(format!("Something about the request content was invalid, check the server logs for further information. Operation ID: {} Error: {:?}",opid, response.text().await.ok() )))
|
||||||
|
}
|
||||||
|
|
||||||
unexpect => {
|
unexpect => {
|
||||||
return Err(ClientError::Http(
|
return Err(ClientError::Http(
|
||||||
unexpect,
|
unexpect,
|
||||||
|
@ -1927,16 +1935,16 @@ impl KanidmClient {
|
||||||
new_display_name: &str,
|
new_display_name: &str,
|
||||||
) -> Result<(), ClientError> {
|
) -> Result<(), ClientError> {
|
||||||
self.perform_put_request(
|
self.perform_put_request(
|
||||||
"/v1/domain/_attr/domain_display_name",
|
&format!("/v1/domain/_attr/{}", ATTR_DOMAIN_DISPLAY_NAME),
|
||||||
vec![new_display_name.to_string()],
|
vec![new_display_name],
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn idm_domain_set_ldap_basedn(&self, new_basedn: &str) -> Result<(), ClientError> {
|
pub async fn idm_domain_set_ldap_basedn(&self, new_basedn: &str) -> Result<(), ClientError> {
|
||||||
self.perform_put_request(
|
self.perform_put_request(
|
||||||
"/v1/domain/_attr/domain_ldap_basedn",
|
&format!("/v1/domain/_attr/{}", ATTR_DOMAIN_LDAP_BASEDN),
|
||||||
vec![new_basedn.to_string()],
|
vec![new_basedn],
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
@ -1945,12 +1953,15 @@ impl KanidmClient {
|
||||||
&self,
|
&self,
|
||||||
enable: bool,
|
enable: bool,
|
||||||
) -> Result<(), ClientError> {
|
) -> Result<(), ClientError> {
|
||||||
self.perform_put_request("/v1/domain/_attr/ldap_allow_unix_pw_bind", vec![enable])
|
self.perform_put_request(
|
||||||
.await
|
&format!("{}{}", "/v1/domain/_attr/", ATTR_LDAP_ALLOW_UNIX_PW_BIND),
|
||||||
|
vec![enable.to_string()],
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn idm_domain_get_ssid(&self) -> Result<String, ClientError> {
|
pub async fn idm_domain_get_ssid(&self) -> Result<String, ClientError> {
|
||||||
self.perform_get_request("/v1/domain/_attr/domain_ssid")
|
self.perform_get_request(&format!("/v1/domain/_attr/{}", ATTR_DOMAIN_SSID))
|
||||||
.await
|
.await
|
||||||
.and_then(|mut r: Vec<String>|
|
.and_then(|mut r: Vec<String>|
|
||||||
// Get the first result
|
// Get the first result
|
||||||
|
@ -1961,13 +1972,16 @@ impl KanidmClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn idm_domain_set_ssid(&self, ssid: &str) -> Result<(), ClientError> {
|
pub async fn idm_domain_set_ssid(&self, ssid: &str) -> Result<(), ClientError> {
|
||||||
self.perform_put_request("/v1/domain/_attr/domain_ssid", vec![ssid.to_string()])
|
self.perform_put_request(
|
||||||
.await
|
&format!("/v1/domain/_attr/{}", ATTR_DOMAIN_SSID),
|
||||||
|
vec![ssid.to_string()],
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn idm_domain_revoke_key(&self, key_id: &str) -> Result<(), ClientError> {
|
pub async fn idm_domain_revoke_key(&self, key_id: &str) -> Result<(), ClientError> {
|
||||||
self.perform_put_request(
|
self.perform_put_request(
|
||||||
"/v1/domain/_attr/key_action_revoke",
|
&format!("/v1/domain/_attr/{}", ATTR_KEY_ACTION_REVOKE),
|
||||||
vec![key_id.to_string()],
|
vec![key_id.to_string()],
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|
|
@ -454,6 +454,8 @@ impl FromStr for ServerRole {
|
||||||
pub struct IntegrationTestConfig {
|
pub struct IntegrationTestConfig {
|
||||||
pub admin_user: String,
|
pub admin_user: String,
|
||||||
pub admin_password: String,
|
pub admin_password: String,
|
||||||
|
pub idm_admin_user: String,
|
||||||
|
pub idm_admin_password: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
|
@ -344,6 +344,15 @@ pub async fn json_rest_event_post_id_attr(
|
||||||
.map_err(WebError::from)
|
.map_err(WebError::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Okay, so a put normally needs
|
||||||
|
/// * filter of what we are working on (id + class)
|
||||||
|
/// * a `Map<String, Vec<String>>` that we turn into a modlist.
|
||||||
|
///
|
||||||
|
/// OR
|
||||||
|
/// * filter of what we are working on (id + class)
|
||||||
|
/// * a `Vec<String>` that we are changing
|
||||||
|
/// * the attr name (as a param to this in path)
|
||||||
|
///
|
||||||
pub async fn json_rest_event_put_attr(
|
pub async fn json_rest_event_put_attr(
|
||||||
state: ServerState,
|
state: ServerState,
|
||||||
id: String,
|
id: String,
|
||||||
|
@ -378,27 +387,6 @@ pub async fn json_rest_event_post_attr(
|
||||||
.map_err(WebError::from)
|
.map_err(WebError::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Okay, so a put normally needs
|
|
||||||
/// * filter of what we are working on (id + class)
|
|
||||||
/// * a `Map<String, Vec<String>>` that we turn into a modlist.
|
|
||||||
///
|
|
||||||
/// OR
|
|
||||||
/// * filter of what we are working on (id + class)
|
|
||||||
/// * a `Vec<String>` that we are changing
|
|
||||||
/// * the attr name (as a param to this in path)
|
|
||||||
///
|
|
||||||
pub async fn json_rest_event_put_id_attr(
|
|
||||||
state: ServerState,
|
|
||||||
id: String,
|
|
||||||
attr: String,
|
|
||||||
filter: Filter<FilterInvalid>,
|
|
||||||
values: Vec<String>,
|
|
||||||
kopid: KOpId,
|
|
||||||
client_auth_info: ClientAuthInfo,
|
|
||||||
) -> Result<Json<()>, WebError> {
|
|
||||||
json_rest_event_put_attr(state, id, attr, filter, values, kopid, client_auth_info).await
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn json_rest_event_delete_id_attr(
|
pub async fn json_rest_event_delete_id_attr(
|
||||||
state: ServerState,
|
state: ServerState,
|
||||||
id: String,
|
id: String,
|
||||||
|
@ -2304,7 +2292,7 @@ pub async fn group_id_attr_put(
|
||||||
Json(values): Json<Vec<String>>,
|
Json(values): Json<Vec<String>>,
|
||||||
) -> Result<Json<()>, WebError> {
|
) -> Result<Json<()>, WebError> {
|
||||||
let filter = filter_all!(f_eq(Attribute::Class, EntryClass::Group.into()));
|
let filter = filter_all!(f_eq(Attribute::Class, EntryClass::Group.into()));
|
||||||
json_rest_event_put_id_attr(state, id, attr, filter, values, kopid, client_auth_info).await
|
json_rest_event_put_attr(state, id, attr, filter, values, kopid, client_auth_info).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[utoipa::path(
|
#[utoipa::path(
|
||||||
|
@ -2426,6 +2414,7 @@ pub async fn domain_attr_put(
|
||||||
Json(values): Json<Vec<String>>,
|
Json(values): Json<Vec<String>>,
|
||||||
) -> Result<Json<()>, WebError> {
|
) -> Result<Json<()>, WebError> {
|
||||||
let filter = filter_all!(f_eq(Attribute::Class, EntryClass::DomainInfo.into()));
|
let filter = filter_all!(f_eq(Attribute::Class, EntryClass::DomainInfo.into()));
|
||||||
|
|
||||||
json_rest_event_put_attr(
|
json_rest_event_put_attr(
|
||||||
state,
|
state,
|
||||||
STR_UUID_DOMAIN_INFO.to_string(),
|
STR_UUID_DOMAIN_INFO.to_string(),
|
||||||
|
|
|
@ -3,7 +3,7 @@ use super::errors::WebError;
|
||||||
use super::middleware::KOpId;
|
use super::middleware::KOpId;
|
||||||
use super::v1::{
|
use super::v1::{
|
||||||
json_rest_event_get, json_rest_event_get_id, json_rest_event_get_id_attr, json_rest_event_post,
|
json_rest_event_get, json_rest_event_get_id, json_rest_event_get_id_attr, json_rest_event_post,
|
||||||
json_rest_event_put_id_attr,
|
json_rest_event_put_attr,
|
||||||
};
|
};
|
||||||
use super::ServerState;
|
use super::ServerState;
|
||||||
use crate::https::extractors::VerifiedClientInformation;
|
use crate::https::extractors::VerifiedClientInformation;
|
||||||
|
@ -298,7 +298,7 @@ pub async fn sync_account_id_attr_put(
|
||||||
Json(values): Json<Vec<String>>,
|
Json(values): Json<Vec<String>>,
|
||||||
) -> Result<Json<()>, WebError> {
|
) -> Result<Json<()>, WebError> {
|
||||||
let filter = filter_all!(f_eq(Attribute::Class, EntryClass::SyncAccount.into()));
|
let filter = filter_all!(f_eq(Attribute::Class, EntryClass::SyncAccount.into()));
|
||||||
json_rest_event_put_id_attr(state, id, attr, filter, values, kopid, client_auth_info).await
|
json_rest_event_put_attr(state, id, attr, filter, values, kopid, client_auth_info).await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// When you want the kitchen Sink
|
/// When you want the kitchen Sink
|
||||||
|
|
|
@ -864,13 +864,26 @@ pub async fn create_server_core(
|
||||||
match &config.integration_test_config {
|
match &config.integration_test_config {
|
||||||
Some(itc) => {
|
Some(itc) => {
|
||||||
let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await;
|
let mut idms_prox_write = idms.proxy_write(duration_from_epoch_now()).await;
|
||||||
// We need to get the admin pw.
|
// We need to set the admin pw.
|
||||||
match idms_prox_write.recover_account("admin", Some(&itc.admin_password)) {
|
match idms_prox_write.recover_account(&itc.admin_user, Some(&itc.admin_password)) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!(
|
error!(
|
||||||
"Unable to configure INTEGRATION TEST admin account -> {:?}",
|
"Unable to configure INTEGRATION TEST {} account -> {:?}",
|
||||||
e
|
&itc.admin_user, e
|
||||||
|
);
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// set the idm_admin account password
|
||||||
|
match idms_prox_write
|
||||||
|
.recover_account(&itc.idm_admin_user, Some(&itc.idm_admin_password))
|
||||||
|
{
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(e) => {
|
||||||
|
error!(
|
||||||
|
"Unable to configure INTEGRATION TEST {} account -> {:?}",
|
||||||
|
&itc.idm_admin_user, e
|
||||||
);
|
);
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1604,6 +1604,7 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
||||||
|
|
||||||
// Deny the change if the account is anonymous!
|
// Deny the change if the account is anonymous!
|
||||||
if account.is_anonymous() {
|
if account.is_anonymous() {
|
||||||
|
trace!("Unable to use anonymous to change UNIX account password");
|
||||||
return Err(OperationError::SystemProtectedObject);
|
return Err(OperationError::SystemProtectedObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -84,7 +84,7 @@ impl Domain {
|
||||||
.get_ava_single_iutf8(Attribute::DomainLdapBasedn) {
|
.get_ava_single_iutf8(Attribute::DomainLdapBasedn) {
|
||||||
|
|
||||||
if !DOMAIN_LDAP_BASEDN_RE.is_match(basedn) {
|
if !DOMAIN_LDAP_BASEDN_RE.is_match(basedn) {
|
||||||
error!("Invalid {}. Must pass regex \"{}\"", Attribute::DomainLdapBasedn, *DOMAIN_LDAP_BASEDN_RE);
|
error!("Invalid {} '{}'. Must pass regex \"{}\"", Attribute::DomainLdapBasedn,basedn, *DOMAIN_LDAP_BASEDN_RE);
|
||||||
return Err(OperationError::InvalidState);
|
return Err(OperationError::InvalidState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ use crate::utils::uuid_to_gid_u32;
|
||||||
/// system uids from 0 - 1000, and many others give user ids between 1000 to
|
/// system uids from 0 - 1000, and many others give user ids between 1000 to
|
||||||
/// 2000. This whole numberspace is cursed, lets assume it's not ours. :(
|
/// 2000. This whole numberspace is cursed, lets assume it's not ours. :(
|
||||||
///
|
///
|
||||||
/// Per https://systemd.io/UIDS-GIDS/, systemd claims a huge chunk of this
|
/// Per <https://systemd.io/UIDS-GIDS/>, systemd claims a huge chunk of this
|
||||||
/// space to itself. As a result we can't allocate between 65536 and u32 max
|
/// space to itself. As a result we can't allocate between 65536 and u32 max
|
||||||
/// because systemd takes most of the usable range for its own containers,
|
/// because systemd takes most of the usable range for its own containers,
|
||||||
/// and half the range is probably going to trigger linux kernel issues.
|
/// and half the range is probably going to trigger linux kernel issues.
|
||||||
|
|
|
@ -17,30 +17,47 @@ pub struct Protected {}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref ALLOWED_ATTRS: HashSet<Attribute> = {
|
static ref ALLOWED_ATTRS: HashSet<Attribute> = {
|
||||||
let mut m = HashSet::with_capacity(32);
|
let attrs = vec![
|
||||||
// Allow modification of some schema class types to allow local extension
|
// Allow modification of some schema class types to allow local extension
|
||||||
// of schema types.
|
// of schema types.
|
||||||
//
|
Attribute::Must,
|
||||||
m.insert(Attribute::Must);
|
Attribute::May,
|
||||||
m.insert(Attribute::May);
|
// modification of some domain info types for local configuratiomn.
|
||||||
// Allow modification of some domain info types for local configuration.
|
Attribute::DomainSsid,
|
||||||
m.insert(Attribute::DomainSsid);
|
Attribute::DomainLdapBasedn,
|
||||||
m.insert(Attribute::DomainLdapBasedn);
|
Attribute::LdapAllowUnixPwBind,
|
||||||
m.insert(Attribute::FernetPrivateKeyStr);
|
Attribute::FernetPrivateKeyStr,
|
||||||
m.insert(Attribute::Es256PrivateKeyDer);
|
Attribute::Es256PrivateKeyDer,
|
||||||
m.insert(Attribute::KeyActionRevoke);
|
Attribute::KeyActionRevoke,
|
||||||
m.insert(Attribute::KeyActionRotate);
|
Attribute::KeyActionRotate,
|
||||||
m.insert(Attribute::IdVerificationEcKey);
|
Attribute::IdVerificationEcKey,
|
||||||
m.insert(Attribute::BadlistPassword);
|
Attribute::BadlistPassword,
|
||||||
m.insert(Attribute::DeniedName);
|
Attribute::DeniedName,
|
||||||
m.insert(Attribute::DomainDisplayName);
|
Attribute::DomainDisplayName,
|
||||||
// Allow modification of account policy values for dyngroups
|
// modification of account policy values for dyngroup.
|
||||||
m.insert(Attribute::AuthSessionExpiry);
|
Attribute::AuthSessionExpiry,
|
||||||
m.insert(Attribute::PrivilegeExpiry);
|
Attribute::PrivilegeExpiry,
|
||||||
m.insert(Attribute::CredentialTypeMinimum);
|
Attribute::CredentialTypeMinimum,
|
||||||
m.insert(Attribute::WebauthnAttestationCaList);
|
Attribute::WebauthnAttestationCaList,
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut m = HashSet::with_capacity(attrs.len());
|
||||||
|
m.extend(attrs);
|
||||||
|
|
||||||
m
|
m
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static ref PROTECTED_ENTRYCLASSES: Vec<EntryClass> =
|
||||||
|
vec![
|
||||||
|
EntryClass::System,
|
||||||
|
EntryClass::DomainInfo,
|
||||||
|
EntryClass::SystemInfo,
|
||||||
|
EntryClass::SystemConfig,
|
||||||
|
EntryClass::DynGroup,
|
||||||
|
EntryClass::SyncObject,
|
||||||
|
EntryClass::Tombstone,
|
||||||
|
EntryClass::Recycled,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Plugin for Protected {
|
impl Plugin for Protected {
|
||||||
|
@ -61,14 +78,11 @@ impl Plugin for Protected {
|
||||||
}
|
}
|
||||||
|
|
||||||
cand.iter().try_fold((), |(), cand| {
|
cand.iter().try_fold((), |(), cand| {
|
||||||
if cand.attribute_equality(Attribute::Class, &EntryClass::System.into())
|
if PROTECTED_ENTRYCLASSES
|
||||||
|| cand.attribute_equality(Attribute::Class, &EntryClass::DomainInfo.into())
|
.iter()
|
||||||
|| cand.attribute_equality(Attribute::Class, &EntryClass::SystemInfo.into())
|
.any(|c| cand.attribute_equality(Attribute::Class, &c.to_partialvalue()))
|
||||||
|| cand.attribute_equality(Attribute::Class, &EntryClass::SystemConfig.into())
|
|
||||||
|| cand.attribute_equality(Attribute::Class, &EntryClass::Tombstone.into())
|
|
||||||
|| cand.attribute_equality(Attribute::Class, &EntryClass::Recycled.into())
|
|
||||||
|| cand.attribute_equality(Attribute::Class, &EntryClass::DynGroup.into())
|
|
||||||
{
|
{
|
||||||
|
trace!("Rejecting operation during pre_create check");
|
||||||
Err(OperationError::SystemProtectedObject)
|
Err(OperationError::SystemProtectedObject)
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -91,15 +105,9 @@ impl Plugin for Protected {
|
||||||
me.modlist.iter().try_fold((), |(), m| match m {
|
me.modlist.iter().try_fold((), |(), m| match m {
|
||||||
Modify::Present(a, v) => {
|
Modify::Present(a, v) => {
|
||||||
if a == Attribute::Class.as_ref()
|
if a == Attribute::Class.as_ref()
|
||||||
&& (v == &EntryClass::System.to_value()
|
&& PROTECTED_ENTRYCLASSES.iter().any(|c| v == &c.to_value())
|
||||||
|| v == &EntryClass::DomainInfo.to_value()
|
|
||||||
|| v == &EntryClass::SystemInfo.into()
|
|
||||||
|| v == &EntryClass::SystemConfig.to_value()
|
|
||||||
|| v == &EntryClass::DynGroup.to_value()
|
|
||||||
|| v == &EntryClass::SyncObject.to_value()
|
|
||||||
|| v == &EntryClass::Tombstone.to_value()
|
|
||||||
|| v == &EntryClass::Recycled.to_value())
|
|
||||||
{
|
{
|
||||||
|
trace!("Rejecting operation during pre_modify check");
|
||||||
Err(OperationError::SystemProtectedObject)
|
Err(OperationError::SystemProtectedObject)
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -144,7 +152,10 @@ impl Plugin for Protected {
|
||||||
let attr: Attribute = a.try_into()?;
|
let attr: Attribute = a.try_into()?;
|
||||||
match ALLOWED_ATTRS.contains(&attr) {
|
match ALLOWED_ATTRS.contains(&attr) {
|
||||||
true => Ok(()),
|
true => Ok(()),
|
||||||
false => Err(OperationError::SystemProtectedObject),
|
false => {
|
||||||
|
trace!("If you're getting this, you need to modify the ALLOWED_ATTRS list");
|
||||||
|
Err(OperationError::SystemProtectedObject)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Was not a mod needing checking
|
// Was not a mod needing checking
|
||||||
|
@ -171,15 +182,9 @@ impl Plugin for Protected {
|
||||||
.try_fold((), |(), m| match m {
|
.try_fold((), |(), m| match m {
|
||||||
Modify::Present(a, v) => {
|
Modify::Present(a, v) => {
|
||||||
if a == Attribute::Class.as_ref()
|
if a == Attribute::Class.as_ref()
|
||||||
&& (v == &EntryClass::System.to_value()
|
&& PROTECTED_ENTRYCLASSES.iter().any(|c| v == &c.to_value())
|
||||||
|| v == &EntryClass::DomainInfo.to_value()
|
|
||||||
|| v == &EntryClass::SystemInfo.to_value()
|
|
||||||
|| v == &EntryClass::SystemConfig.to_value()
|
|
||||||
|| v == &EntryClass::DynGroup.to_value()
|
|
||||||
|| v == &EntryClass::SyncObject.to_value()
|
|
||||||
|| v == &EntryClass::Tombstone.to_value()
|
|
||||||
|| v == &EntryClass::Recycled.to_value())
|
|
||||||
{
|
{
|
||||||
|
trace!("Rejecting operation during pre_batch_modify check");
|
||||||
Err(OperationError::SystemProtectedObject)
|
Err(OperationError::SystemProtectedObject)
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -227,7 +232,11 @@ impl Plugin for Protected {
|
||||||
let attr: Attribute = a.try_into()?;
|
let attr: Attribute = a.try_into()?;
|
||||||
match ALLOWED_ATTRS.contains(&attr) {
|
match ALLOWED_ATTRS.contains(&attr) {
|
||||||
true => Ok(()),
|
true => Ok(()),
|
||||||
false => Err(OperationError::SystemProtectedObject),
|
false => {
|
||||||
|
|
||||||
|
trace!("Rejecting operation during pre_batch_modify check, if you're getting this check ALLOWED_ATTRS");
|
||||||
|
Err(OperationError::SystemProtectedObject)
|
||||||
|
},
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Was not a mod needing checking
|
// Was not a mod needing checking
|
||||||
|
@ -249,14 +258,11 @@ impl Plugin for Protected {
|
||||||
}
|
}
|
||||||
|
|
||||||
cand.iter().try_fold((), |(), cand| {
|
cand.iter().try_fold((), |(), cand| {
|
||||||
if cand.attribute_equality(Attribute::Class, &EntryClass::System.into())
|
if PROTECTED_ENTRYCLASSES
|
||||||
|| cand.attribute_equality(Attribute::Class, &EntryClass::DomainInfo.into())
|
.iter()
|
||||||
|| cand.attribute_equality(Attribute::Class, &EntryClass::SystemInfo.into())
|
.any(|c| cand.attribute_equality(Attribute::Class, &c.to_partialvalue()))
|
||||||
|| cand.attribute_equality(Attribute::Class, &EntryClass::SystemConfig.into())
|
|
||||||
|| cand.attribute_equality(Attribute::Class, &EntryClass::Tombstone.into())
|
|
||||||
|| cand.attribute_equality(Attribute::Class, &EntryClass::Recycled.into())
|
|
||||||
|| cand.attribute_equality(Attribute::Class, &EntryClass::DynGroup.into())
|
|
||||||
{
|
{
|
||||||
|
trace!("Rejecting operation during pre_delete check");
|
||||||
Err(OperationError::SystemProtectedObject)
|
Err(OperationError::SystemProtectedObject)
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -193,7 +193,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
||||||
self.internal_migrate_or_create_ignore_attrs(e, &[])
|
self.internal_migrate_or_create_ignore_attrs(e, &[])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is the same as [internal_migrate_or_create] but it will ignore the specified
|
/// 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
|
/// list of attributes, so that if an admin has modified those values then we don't
|
||||||
/// stomp them.
|
/// stomp them.
|
||||||
#[instrument(level = "trace", skip_all)]
|
#[instrument(level = "trace", skip_all)]
|
||||||
|
|
|
@ -62,6 +62,8 @@ pub async fn setup_async_test(mut config: Configuration) -> (KanidmClient, CoreH
|
||||||
let int_config = Box::new(IntegrationTestConfig {
|
let int_config = Box::new(IntegrationTestConfig {
|
||||||
admin_user: ADMIN_TEST_USER.to_string(),
|
admin_user: ADMIN_TEST_USER.to_string(),
|
||||||
admin_password: ADMIN_TEST_PASSWORD.to_string(),
|
admin_password: ADMIN_TEST_PASSWORD.to_string(),
|
||||||
|
idm_admin_user: IDM_ADMIN_TEST_USER.to_string(),
|
||||||
|
idm_admin_password: IDM_ADMIN_TEST_PASSWORD.to_string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
let addr = format!("http://localhost:{}", port);
|
let addr = format!("http://localhost:{}", port);
|
||||||
|
|
52
server/testkit/tests/domain.rs
Normal file
52
server/testkit/tests/domain.rs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
use kanidm_client::KanidmClient;
|
||||||
|
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) {
|
||||||
|
rsclient
|
||||||
|
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
||||||
|
.await
|
||||||
|
.expect("Failed to login as admin");
|
||||||
|
rsclient
|
||||||
|
.idm_set_ldap_allow_unix_password_bind(true)
|
||||||
|
.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) {
|
||||||
|
rsclient
|
||||||
|
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
||||||
|
.await
|
||||||
|
.expect("Failed to login as admin");
|
||||||
|
|
||||||
|
rsclient
|
||||||
|
.idm_domain_set_ldap_basedn("dc=example,dc=com")
|
||||||
|
.await
|
||||||
|
.expect("Failed to set idm_domain_set_ldap_basedn");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[kanidmd_testkit::test]
|
||||||
|
async fn test_idm_domain_set_display_name(rsclient: KanidmClient) {
|
||||||
|
rsclient
|
||||||
|
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
||||||
|
.await
|
||||||
|
.expect("Failed to login as admin");
|
||||||
|
|
||||||
|
let new_domain_display_name = "hello kanidm 12345667";
|
||||||
|
|
||||||
|
rsclient
|
||||||
|
.idm_domain_set_display_name(new_domain_display_name)
|
||||||
|
.await
|
||||||
|
.expect("Failed to set idm_domain_set_display_name");
|
||||||
|
|
||||||
|
let domain_after = rsclient
|
||||||
|
.idm_domain_get()
|
||||||
|
.await
|
||||||
|
.expect("Failed to idm_domain_get");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
domain_after.attrs.get(ATTR_DOMAIN_DISPLAY_NAME),
|
||||||
|
Some(&vec![new_domain_display_name.to_string()])
|
||||||
|
);
|
||||||
|
}
|
|
@ -24,6 +24,8 @@ use kanidm_hsm_crypto::{soft::SoftTpm, AuthValue, BoxedDynTpm, Tpm};
|
||||||
|
|
||||||
const ADMIN_TEST_USER: &str = "admin";
|
const ADMIN_TEST_USER: &str = "admin";
|
||||||
const ADMIN_TEST_PASSWORD: &str = "integration test admin password";
|
const ADMIN_TEST_PASSWORD: &str = "integration test admin password";
|
||||||
|
const IDM_ADMIN_TEST_USER: &str = "idm_admin";
|
||||||
|
const IDM_ADMIN_TEST_PASSWORD: &str = "integration test idm_admin password";
|
||||||
const TESTACCOUNT1_PASSWORD_A: &str = "password a for account1 test";
|
const TESTACCOUNT1_PASSWORD_A: &str = "password a for account1 test";
|
||||||
const TESTACCOUNT1_PASSWORD_B: &str = "password b for account1 test";
|
const TESTACCOUNT1_PASSWORD_B: &str = "password b for account1 test";
|
||||||
const TESTACCOUNT1_PASSWORD_INC: &str = "never going to work";
|
const TESTACCOUNT1_PASSWORD_INC: &str = "never going to work";
|
||||||
|
@ -58,6 +60,8 @@ async fn setup_test(fix_fn: Fixture) -> (Resolver<KanidmProvider>, KanidmClient)
|
||||||
let int_config = Box::new(IntegrationTestConfig {
|
let int_config = Box::new(IntegrationTestConfig {
|
||||||
admin_user: ADMIN_TEST_USER.to_string(),
|
admin_user: ADMIN_TEST_USER.to_string(),
|
||||||
admin_password: ADMIN_TEST_PASSWORD.to_string(),
|
admin_password: ADMIN_TEST_PASSWORD.to_string(),
|
||||||
|
idm_admin_user: IDM_ADMIN_TEST_USER.to_string(),
|
||||||
|
idm_admin_password: IDM_ADMIN_TEST_PASSWORD.to_string(),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Setup the config ...
|
// Setup the config ...
|
||||||
|
|
Loading…
Reference in a new issue