mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 04:27:02 +01:00
Cinco de yakko (#2108)
* there are always more yaks * see? ldap yaks. * fixing stupid radius container build thing
This commit is contained in:
parent
77da40d528
commit
d5ed335b52
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -3108,6 +3108,7 @@ dependencies = [
|
|||
"kanidm_proto",
|
||||
"kanidmd_core",
|
||||
"kanidmd_lib",
|
||||
"lazy_static",
|
||||
"oauth2",
|
||||
"openssl",
|
||||
"reqwest",
|
||||
|
|
|
@ -64,6 +64,7 @@ pub const ATTR_GROUP: &str = "group";
|
|||
pub const ATTR_ID_VERIFICATION_ECKEY: &str = "id_verification_eckey";
|
||||
pub const ATTR_INDEX: &str = "index";
|
||||
pub const ATTR_IPANTHASH: &str = "ipanthash";
|
||||
pub const ATTR_IPASSHPUBKEY: &str = "ipasshpubkey";
|
||||
pub const ATTR_JWS_ES256_PRIVATE_KEY: &str = "jws_es256_private_key";
|
||||
pub const ATTR_LAST_MODIFIED_CID: &str = "last_modified_cid";
|
||||
pub const ATTR_LEGALNAME: &str = "legalname";
|
||||
|
@ -111,8 +112,8 @@ pub const ATTR_SELF: &str = "self";
|
|||
pub const ATTR_SOURCE_UUID: &str = "source_uuid";
|
||||
pub const ATTR_SPN: &str = "spn";
|
||||
pub const ATTR_SUPPLEMENTS: &str = "supplements";
|
||||
pub const ATTR_LDAP_SSH_PUBLICKEY: &str = "ssh_publickey";
|
||||
pub const ATTR_SSHPUBLICKEY: &str = "sshpublickey";
|
||||
pub const ATTR_LDAP_SSHPUBLICKEY: &str = "sshpublickey";
|
||||
pub const ATTR_SSH_PUBLICKEY: &str = "ssh_publickey";
|
||||
pub const ATTR_SYNC_ALLOWED: &str = "sync_allowed";
|
||||
pub const ATTR_SYNC_CLASS: &str = "sync_class";
|
||||
pub const ATTR_SYNC_COOKIE: &str = "sync_cookie";
|
||||
|
|
|
@ -8,8 +8,8 @@ pub use scim_proto::user::MultiValueAttr;
|
|||
use scim_proto::*;
|
||||
|
||||
use crate::constants::{
|
||||
ATTR_DESCRIPTION, ATTR_DISPLAYNAME, ATTR_GIDNUMBER, ATTR_LDAP_SSH_PUBLICKEY, ATTR_LOGINSHELL,
|
||||
ATTR_MAIL, ATTR_MEMBER, ATTR_NAME,
|
||||
ATTR_DESCRIPTION, ATTR_DISPLAYNAME, ATTR_GIDNUMBER, ATTR_LOGINSHELL, ATTR_MAIL, ATTR_MEMBER,
|
||||
ATTR_NAME, ATTR_SSH_PUBLICKEY,
|
||||
};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
|
||||
|
@ -183,7 +183,7 @@ impl Into<ScimEntry> for ScimSyncPerson {
|
|||
set_multi_complex!(attrs, "totp_import", totp_import);
|
||||
set_option_string!(attrs, ATTR_LOGINSHELL, login_shell);
|
||||
set_multi_complex!(attrs, ATTR_MAIL, mail);
|
||||
set_multi_complex!(attrs, ATTR_LDAP_SSH_PUBLICKEY, ssh_publickey);
|
||||
set_multi_complex!(attrs, ATTR_SSH_PUBLICKEY, ssh_publickey); // with the underscore
|
||||
|
||||
ScimEntry {
|
||||
schemas,
|
||||
|
|
|
@ -15,7 +15,7 @@ use webauthn_rs_proto::{
|
|||
RequestChallengeResponse,
|
||||
};
|
||||
|
||||
use crate::constants::{ATTR_GROUP, ATTR_LDAP_SSH_PUBLICKEY};
|
||||
use crate::constants::{ATTR_GROUP, ATTR_LDAP_SSHPUBLICKEY};
|
||||
|
||||
// These proto implementations are here because they have public definitions
|
||||
|
||||
|
@ -613,7 +613,7 @@ impl fmt::Display for UnixUserToken {
|
|||
}
|
||||
self.sshkeys
|
||||
.iter()
|
||||
.try_for_each(|s| writeln!(f, "{}: {}", ATTR_LDAP_SSH_PUBLICKEY, s))?;
|
||||
.try_for_each(|s| writeln!(f, "{}: {}", ATTR_LDAP_SSHPUBLICKEY, s))?;
|
||||
self.groups
|
||||
.iter()
|
||||
.try_for_each(|g| writeln!(f, "{}: {}", ATTR_GROUP, g))
|
||||
|
|
|
@ -3,7 +3,7 @@ ARG BASE_IMAGE=opensuse/tumbleweed:latest
|
|||
FROM ${BASE_IMAGE}
|
||||
|
||||
ADD ../scripts/zypper_fixing.sh /zypper_fixing.sh
|
||||
RUN --mount=type=cache,id=zypp,target=/var/cache/zypp /zypper_fixing.sh
|
||||
RUN /zypper_fixing.sh
|
||||
|
||||
# ======================
|
||||
# FROM repos
|
||||
|
@ -11,7 +11,7 @@ ARG RADIUS_USER=radiusd
|
|||
EXPOSE 1812 1813
|
||||
ENV KANIDM_CONFIG_FILE="/data/kanidm"
|
||||
|
||||
RUN --mount=type=cache,id=zypp,target=/var/cache/zypp zypper install -y \
|
||||
RUN zypper install -y \
|
||||
freeradius-client \
|
||||
freeradius-server \
|
||||
freeradius-server-python3 \
|
||||
|
|
|
@ -540,7 +540,7 @@ impl QueryServerReadV1 {
|
|||
// From the entry, turn it into the value
|
||||
.and_then(|entry| {
|
||||
entry
|
||||
.get_ava_single(Attribute::RadiusSecret.as_ref())
|
||||
.get_ava_single(Attribute::RadiusSecret)
|
||||
.and_then(|v| v.get_secret_str().map(str::to_string))
|
||||
});
|
||||
Ok(r)
|
||||
|
@ -744,7 +744,7 @@ impl QueryServerReadV1 {
|
|||
// get the first entry
|
||||
.and_then(|e| {
|
||||
// From the entry, turn it into the value
|
||||
e.get_ava_iter_sshpubkeys(Attribute::SshPublicKey.into())
|
||||
e.get_ava_iter_sshpubkeys(Attribute::SshPublicKey)
|
||||
.map(|i| i.map(|s| s.to_string()).collect())
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
|
@ -807,11 +807,10 @@ impl QueryServerReadV1 {
|
|||
// get the first entry
|
||||
.map(|e| {
|
||||
// From the entry, turn it into the value
|
||||
e.get_ava_set(Attribute::SshPublicKey.into())
|
||||
.and_then(|vs| {
|
||||
// Get the one tagged value
|
||||
vs.get_ssh_tag(&tag).map(str::to_string)
|
||||
})
|
||||
e.get_ava_set(Attribute::SshPublicKey).and_then(|vs| {
|
||||
// Get the one tagged value
|
||||
vs.get_ssh_tag(&tag).map(str::to_string)
|
||||
})
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
// No matching entry? Return none.
|
||||
|
@ -1274,7 +1273,7 @@ impl QueryServerReadV1 {
|
|||
// From the entry, turn it into the value
|
||||
.and_then(|entry| {
|
||||
entry
|
||||
.get_ava_single(Attribute::OAuth2RsBasicSecret.into())
|
||||
.get_ava_single(Attribute::OAuth2RsBasicSecret)
|
||||
.and_then(|v| v.get_secret_str().map(str::to_string))
|
||||
});
|
||||
Ok(r)
|
||||
|
|
|
@ -882,10 +882,11 @@ impl QueryServerWriteV1 {
|
|||
e
|
||||
})?;
|
||||
|
||||
let target_attr = Attribute::try_from(attr)?;
|
||||
let mdf = match ModifyEvent::from_target_uuid_attr_purge(
|
||||
ident,
|
||||
target_uuid,
|
||||
&attr,
|
||||
target_attr,
|
||||
filter,
|
||||
&idms_prox_write.qs_write,
|
||||
) {
|
||||
|
@ -1037,7 +1038,7 @@ impl QueryServerWriteV1 {
|
|||
) -> Result<(), OperationError> {
|
||||
// Because this is from internal, we can generate a real modlist, rather
|
||||
// than relying on the proto ones.
|
||||
let ml = ModifyList::new_append(ATTR_LDAP_SSH_PUBLICKEY, Value::new_sshkey(tag, key));
|
||||
let ml = ModifyList::new_append(Attribute::SshPublicKey, Value::new_sshkey(tag, key));
|
||||
|
||||
self.modify_from_internal_parts(uat, &uuid_or_name, &ml, filter)
|
||||
.await
|
||||
|
@ -1066,16 +1067,18 @@ impl QueryServerWriteV1 {
|
|||
.chain(iter::once(
|
||||
gidnumber
|
||||
.as_ref()
|
||||
.map(|_| Modify::Purged("gidnumber".into())),
|
||||
.map(|_| Modify::Purged(Attribute::GidNumber.into())),
|
||||
))
|
||||
.chain(iter::once(gidnumber.map(|n| {
|
||||
Modify::Present("gidnumber".into(), Value::new_uint32(n))
|
||||
Modify::Present(Attribute::GidNumber.into(), Value::new_uint32(n))
|
||||
})))
|
||||
.chain(iter::once(
|
||||
shell.as_ref().map(|_| Modify::Purged("loginshell".into())),
|
||||
shell
|
||||
.as_ref()
|
||||
.map(|_| Modify::Purged(Attribute::LoginShell.into())),
|
||||
))
|
||||
.chain(iter::once(shell.map(|s| {
|
||||
Modify::Present("loginshell".into(), Value::new_iutf8(s.as_str()))
|
||||
Modify::Present(Attribute::LoginShell.into(), Value::new_iutf8(s.as_str()))
|
||||
})))
|
||||
.flatten()
|
||||
.collect();
|
||||
|
@ -1106,8 +1109,11 @@ impl QueryServerWriteV1 {
|
|||
|
||||
let gidnumber_mods = if let Some(gid) = gx.gidnumber {
|
||||
[
|
||||
Some(Modify::Purged("gidnumber".into())),
|
||||
Some(Modify::Present("gidnumber".into(), Value::new_uint32(gid))),
|
||||
Some(Modify::Purged(Attribute::GidNumber.into())),
|
||||
Some(Modify::Present(
|
||||
Attribute::GidNumber.into(),
|
||||
Value::new_uint32(gid),
|
||||
)),
|
||||
]
|
||||
} else {
|
||||
[None, None]
|
||||
|
@ -1209,7 +1215,7 @@ impl QueryServerWriteV1 {
|
|||
})?;
|
||||
|
||||
let ml = ModifyList::new_append(
|
||||
"oauth2_rs_scope_map",
|
||||
Attribute::OAuth2RsScopeMap,
|
||||
Value::new_oauthscopemap(group_uuid, scopes.into_iter().collect()).ok_or_else(
|
||||
|| OperationError::InvalidAttribute("Invalid Oauth Scope Map syntax".to_string()),
|
||||
)?,
|
||||
|
@ -1266,7 +1272,8 @@ impl QueryServerWriteV1 {
|
|||
e
|
||||
})?;
|
||||
|
||||
let ml = ModifyList::new_remove("oauth2_rs_scope_map", PartialValue::Refer(group_uuid));
|
||||
let ml =
|
||||
ModifyList::new_remove(Attribute::OAuth2RsScopeMap, PartialValue::Refer(group_uuid));
|
||||
|
||||
let mdf = match ModifyEvent::from_internal_parts(
|
||||
ident,
|
||||
|
@ -1323,7 +1330,7 @@ impl QueryServerWriteV1 {
|
|||
})?;
|
||||
|
||||
let ml = ModifyList::new_append(
|
||||
"oauth2_rs_sup_scope_map",
|
||||
Attribute::OAuth2RsSupScopeMap,
|
||||
Value::new_oauthscopemap(group_uuid, scopes.into_iter().collect()).ok_or_else(
|
||||
|| OperationError::InvalidAttribute("Invalid Oauth Scope Map syntax".to_string()),
|
||||
)?,
|
||||
|
@ -1380,7 +1387,10 @@ impl QueryServerWriteV1 {
|
|||
e
|
||||
})?;
|
||||
|
||||
let ml = ModifyList::new_remove("oauth2_rs_sup_scope_map", PartialValue::Refer(group_uuid));
|
||||
let ml = ModifyList::new_remove(
|
||||
Attribute::OAuth2RsSupScopeMap,
|
||||
PartialValue::Refer(group_uuid),
|
||||
);
|
||||
|
||||
let mdf = match ModifyEvent::from_internal_parts(
|
||||
ident,
|
||||
|
|
|
@ -84,9 +84,9 @@ pub async fn oauth2_basic_post(
|
|||
Json(obj): Json<ProtoEntry>,
|
||||
) -> impl IntoResponse {
|
||||
let classes = vec![
|
||||
"oauth2_resource_server".to_string(),
|
||||
"oauth2_resource_server_basic".to_string(),
|
||||
"object".to_string(),
|
||||
EntryClass::OAuth2ResourceServer.to_string(),
|
||||
EntryClass::OAuth2ResourceServerBasic.to_string(),
|
||||
EntryClass::Object.to_string(),
|
||||
];
|
||||
json_rest_event_post(state, classes, obj, kopid).await
|
||||
}
|
||||
|
@ -97,9 +97,9 @@ pub async fn oauth2_public_post(
|
|||
Json(obj): Json<ProtoEntry>,
|
||||
) -> impl IntoResponse {
|
||||
let classes = vec![
|
||||
"oauth2_resource_server".to_string(),
|
||||
"oauth2_resource_server_public".to_string(),
|
||||
"object".to_string(),
|
||||
EntryClass::OAuth2ResourceServer.to_string(),
|
||||
EntryClass::OAuth2ResourceServerPublic.to_string(),
|
||||
EntryClass::Object.to_string(),
|
||||
];
|
||||
json_rest_event_post(state, classes, obj, kopid).await
|
||||
}
|
||||
|
|
|
@ -457,7 +457,7 @@ impl std::fmt::Display for DbEntry {
|
|||
write!(f, "{name:?}, ")?;
|
||||
}
|
||||
}
|
||||
if let Some(names) = dbe_v1.attrs.get("classname") {
|
||||
if let Some(names) = dbe_v1.attrs.get(Attribute::ClassName.as_ref()) {
|
||||
for name in names {
|
||||
write!(f, "{name:?}, ")?;
|
||||
}
|
||||
|
|
|
@ -1095,7 +1095,7 @@ impl<'a> IdlArcSqliteWriteTransaction<'a> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn create_idx(&self, attr: &str, itype: IndexType) -> Result<(), OperationError> {
|
||||
pub fn create_idx(&self, attr: Attribute, itype: IndexType) -> Result<(), OperationError> {
|
||||
// We don't need to affect this, so pass it down.
|
||||
self.db.create_idx(attr, itype)
|
||||
}
|
||||
|
|
|
@ -259,10 +259,7 @@ pub trait IdlSqliteTransaction {
|
|||
itype.as_idx_str(),
|
||||
attr
|
||||
);
|
||||
let mut stmt = self
|
||||
.get_conn()?
|
||||
.prepare(query.as_str())
|
||||
.map_err(sqlite_error)?;
|
||||
let mut stmt = self.get_conn()?.prepare(&query).map_err(sqlite_error)?;
|
||||
let idl_raw: Option<Vec<u8>> = stmt
|
||||
.query_row(&[(":idx_key", &idx_key)], |row| row.get(0))
|
||||
// We don't mind if it doesn't exist
|
||||
|
@ -1082,7 +1079,7 @@ impl IdlSqliteWriteTransaction {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn create_idx(&self, attr: &str, itype: IndexType) -> Result<(), OperationError> {
|
||||
pub fn create_idx(&self, attr: Attribute, itype: IndexType) -> Result<(), OperationError> {
|
||||
// Is there a better way than formatting this? I can't seem
|
||||
// to template into the str.
|
||||
//
|
||||
|
|
|
@ -4,6 +4,7 @@ use std::hash::{Hash, Hasher};
|
|||
|
||||
use smartstring::alias::String as AttrString;
|
||||
|
||||
use crate::prelude::entries::Attribute;
|
||||
use crate::value::IndexType;
|
||||
|
||||
pub type IdxSlope = u8;
|
||||
|
@ -17,7 +18,7 @@ pub struct IdxKey {
|
|||
}
|
||||
|
||||
impl IdxKey {
|
||||
pub fn new(attr: &str, itype: IndexType) -> Self {
|
||||
pub fn new(attr: Attribute, itype: IndexType) -> Self {
|
||||
IdxKey {
|
||||
attr: attr.into(),
|
||||
itype,
|
||||
|
|
|
@ -1578,10 +1578,10 @@ impl<'a> BackendWriteTransaction<'a> {
|
|||
trace!("Creating index -> uuid2rdn");
|
||||
self.idlayer.create_uuid2rdn()?;
|
||||
|
||||
self.idxmeta_wr
|
||||
.idxkeys
|
||||
.keys()
|
||||
.try_for_each(|ikey| self.idlayer.create_idx(&ikey.attr, ikey.itype))
|
||||
self.idxmeta_wr.idxkeys.keys().try_for_each(|ikey| {
|
||||
let attr: Attribute = (&ikey.attr).try_into()?;
|
||||
self.idlayer.create_idx(attr, ikey.itype)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn upgrade_reindex(&mut self, v: i64) -> Result<(), OperationError> {
|
||||
|
@ -2249,8 +2249,8 @@ mod tests {
|
|||
// Modify single
|
||||
assert!(be.modify(&CID_ZERO, &[pre1], &[vr1.clone()]).is_ok());
|
||||
// Assert no other changes
|
||||
assert!(entry_attr_pres!(be, vr1, Attribute::TestAttr.as_ref()));
|
||||
assert!(!entry_attr_pres!(be, vr2, Attribute::TestAttr.as_ref()));
|
||||
assert!(entry_attr_pres!(be, vr1, Attribute::TestAttr));
|
||||
assert!(!entry_attr_pres!(be, vr2, Attribute::TestAttr));
|
||||
|
||||
// Modify both
|
||||
assert!(be
|
||||
|
@ -2261,8 +2261,8 @@ mod tests {
|
|||
)
|
||||
.is_ok());
|
||||
|
||||
assert!(entry_attr_pres!(be, vr1, Attribute::TestAttr.as_ref()));
|
||||
assert!(entry_attr_pres!(be, vr2, Attribute::TestAttr.as_ref()));
|
||||
assert!(entry_attr_pres!(be, vr1, Attribute::TestAttr));
|
||||
assert!(entry_attr_pres!(be, vr2, Attribute::TestAttr));
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -2884,9 +2884,9 @@ mod tests {
|
|||
// add something.
|
||||
ce1.add_ava(Attribute::TestNumber, Value::from("test"));
|
||||
// remove something.
|
||||
ce1.purge_ava(Attribute::TestAttr.as_ref());
|
||||
ce1.purge_ava(Attribute::TestAttr);
|
||||
// mod something.
|
||||
ce1.purge_ava(Attribute::Name.as_ref());
|
||||
ce1.purge_ava(Attribute::Name);
|
||||
ce1.add_ava(Attribute::Name, Value::new_iname("claire"));
|
||||
|
||||
let ce1 = ce1.into_sealed_committed();
|
||||
|
@ -2953,8 +2953,8 @@ mod tests {
|
|||
let rset: Vec<_> = rset.into_iter().map(Arc::new).collect();
|
||||
// Now, alter the new entry.
|
||||
let mut ce1 = rset[0].as_ref().clone().into_invalid();
|
||||
ce1.purge_ava(Attribute::Name.as_ref());
|
||||
ce1.purge_ava(Attribute::Uuid.as_ref());
|
||||
ce1.purge_ava(Attribute::Name);
|
||||
ce1.purge_ava(Attribute::Uuid);
|
||||
ce1.add_ava(Attribute::Name, Value::new_iname("claire"));
|
||||
ce1.add_ava(
|
||||
Attribute::Uuid,
|
||||
|
@ -3357,36 +3357,30 @@ mod tests {
|
|||
assert!(!be.is_idx_slopeyness_generated().unwrap());
|
||||
|
||||
let ta_eq_slope = be
|
||||
.get_idx_slope(&IdxKey::new(
|
||||
Attribute::TestAttr.as_ref(),
|
||||
IndexType::Equality,
|
||||
))
|
||||
.get_idx_slope(&IdxKey::new(Attribute::TestAttr, IndexType::Equality))
|
||||
.unwrap();
|
||||
assert_eq!(ta_eq_slope, 45);
|
||||
|
||||
let tb_eq_slope = be
|
||||
.get_idx_slope(&IdxKey::new(
|
||||
Attribute::TestNumber.as_ref(),
|
||||
IndexType::Equality,
|
||||
))
|
||||
.get_idx_slope(&IdxKey::new(Attribute::TestNumber, IndexType::Equality))
|
||||
.unwrap();
|
||||
assert_eq!(tb_eq_slope, 45);
|
||||
|
||||
let name_eq_slope = be
|
||||
.get_idx_slope(&IdxKey::new(Attribute::Name.as_ref(), IndexType::Equality))
|
||||
.get_idx_slope(&IdxKey::new(Attribute::Name, IndexType::Equality))
|
||||
.unwrap();
|
||||
assert_eq!(name_eq_slope, 1);
|
||||
let uuid_eq_slope = be
|
||||
.get_idx_slope(&IdxKey::new(Attribute::Uuid.as_ref(), IndexType::Equality))
|
||||
.get_idx_slope(&IdxKey::new(Attribute::Uuid, IndexType::Equality))
|
||||
.unwrap();
|
||||
assert_eq!(uuid_eq_slope, 1);
|
||||
|
||||
let name_pres_slope = be
|
||||
.get_idx_slope(&IdxKey::new(Attribute::Name.as_ref(), IndexType::Presence))
|
||||
.get_idx_slope(&IdxKey::new(Attribute::Name, IndexType::Presence))
|
||||
.unwrap();
|
||||
assert_eq!(name_pres_slope, 90);
|
||||
let uuid_pres_slope = be
|
||||
.get_idx_slope(&IdxKey::new(Attribute::Uuid.as_ref(), IndexType::Presence))
|
||||
.get_idx_slope(&IdxKey::new(Attribute::Uuid, IndexType::Presence))
|
||||
.unwrap();
|
||||
assert_eq!(uuid_pres_slope, 90);
|
||||
// Check the slopes are what we expect for hardcoded values.
|
||||
|
@ -3397,36 +3391,30 @@ mod tests {
|
|||
assert!(be.is_idx_slopeyness_generated().unwrap());
|
||||
|
||||
let ta_eq_slope = be
|
||||
.get_idx_slope(&IdxKey::new(
|
||||
Attribute::TestAttr.as_ref(),
|
||||
IndexType::Equality,
|
||||
))
|
||||
.get_idx_slope(&IdxKey::new(Attribute::TestAttr, IndexType::Equality))
|
||||
.unwrap();
|
||||
assert_eq!(ta_eq_slope, 200);
|
||||
|
||||
let tb_eq_slope = be
|
||||
.get_idx_slope(&IdxKey::new(
|
||||
Attribute::TestNumber.as_ref(),
|
||||
IndexType::Equality,
|
||||
))
|
||||
.get_idx_slope(&IdxKey::new(Attribute::TestNumber, IndexType::Equality))
|
||||
.unwrap();
|
||||
assert_eq!(tb_eq_slope, 133);
|
||||
|
||||
let name_eq_slope = be
|
||||
.get_idx_slope(&IdxKey::new(Attribute::Name.as_ref(), IndexType::Equality))
|
||||
.get_idx_slope(&IdxKey::new(Attribute::Name, IndexType::Equality))
|
||||
.unwrap();
|
||||
assert_eq!(name_eq_slope, 51);
|
||||
let uuid_eq_slope = be
|
||||
.get_idx_slope(&IdxKey::new(Attribute::Uuid.as_ref(), IndexType::Equality))
|
||||
.get_idx_slope(&IdxKey::new(Attribute::Uuid, IndexType::Equality))
|
||||
.unwrap();
|
||||
assert_eq!(uuid_eq_slope, 51);
|
||||
|
||||
let name_pres_slope = be
|
||||
.get_idx_slope(&IdxKey::new(Attribute::Name.as_ref(), IndexType::Presence))
|
||||
.get_idx_slope(&IdxKey::new(Attribute::Name, IndexType::Presence))
|
||||
.unwrap();
|
||||
assert_eq!(name_pres_slope, 200);
|
||||
let uuid_pres_slope = be
|
||||
.get_idx_slope(&IdxKey::new(Attribute::Uuid.as_ref(), IndexType::Presence))
|
||||
.get_idx_slope(&IdxKey::new(Attribute::Uuid, IndexType::Presence))
|
||||
.unwrap();
|
||||
assert_eq!(uuid_pres_slope, 200);
|
||||
})
|
||||
|
|
|
@ -40,7 +40,7 @@ lazy_static! {
|
|||
/// Built-in Access Control Profile definitions
|
||||
pub struct BuiltinAcp {
|
||||
classes: Vec<EntryClass>,
|
||||
name: &'static str,
|
||||
pub name: &'static str,
|
||||
uuid: Uuid,
|
||||
description: &'static str,
|
||||
receiver_group: Uuid,
|
||||
|
@ -227,7 +227,7 @@ lazy_static! {
|
|||
ProtoFilter::And(
|
||||
vec![
|
||||
match_class_filter!(EntryClass::Person),
|
||||
ProtoFilter::Eq("class".to_string(), "account".to_string()),
|
||||
ProtoFilter::Eq(EntryClass::Class.to_string(), EntryClass::Account.to_string()),
|
||||
match_class_filter!(EntryClass::Account),
|
||||
ProtoFilter::SelfUuid,
|
||||
]
|
||||
|
@ -268,7 +268,7 @@ lazy_static! {
|
|||
EntryClass::AccessControlModify
|
||||
],
|
||||
receiver_group: UUID_IDM_ALL_ACCOUNTS,
|
||||
target_scope: ProtoFilter::And(vec![ProtoFilter::Eq("class".to_string(), "account".to_string()), ProtoFilter::SelfUuid]),
|
||||
target_scope: ProtoFilter::And(vec![ProtoFilter::Eq(Attribute::Class.to_string(), Attribute::Account.to_string()), ProtoFilter::SelfUuid]),
|
||||
modify_removed_attrs: vec![
|
||||
Attribute::UserAuthTokenSession
|
||||
],
|
||||
|
@ -1763,10 +1763,19 @@ lazy_static! {
|
|||
description: "Builtin IDM Control for managing IDM synchronisation accounts / connections",
|
||||
receiver_group: UUID_IDM_HP_SYNC_ACCOUNT_MANAGE_PRIV,
|
||||
target_scope: ProtoFilter::And(vec![
|
||||
ProtoFilter::Eq("class".to_string(), "sync_account".to_string()),
|
||||
ProtoFilter::Eq(
|
||||
Attribute::Class.to_string(),
|
||||
EntryClass::SyncAccount.to_string()
|
||||
),
|
||||
ProtoFilter::AndNot(Box::new(ProtoFilter::Or(vec![
|
||||
ProtoFilter::Eq("class".to_string(), "tombstone".to_string()),
|
||||
ProtoFilter::Eq("class".to_string(), "recycled".to_string()),
|
||||
ProtoFilter::Eq(
|
||||
Attribute::Class.to_string(),
|
||||
EntryClass::Tombstone.to_string()
|
||||
),
|
||||
ProtoFilter::Eq(
|
||||
Attribute::Class.to_string(),
|
||||
EntryClass::Tombstone.to_string()
|
||||
),
|
||||
]))),
|
||||
]),
|
||||
search_attrs: vec![
|
||||
|
|
|
@ -34,7 +34,7 @@ fn test_valueattribute_round_trip() {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Sequence, Hash)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Sequence, Hash)]
|
||||
pub enum Attribute {
|
||||
Account,
|
||||
AccountExpire,
|
||||
|
@ -90,6 +90,7 @@ pub enum Attribute {
|
|||
IdVerificationEcKey,
|
||||
Index,
|
||||
IpaNtHash,
|
||||
IpaSshPubKey,
|
||||
JwsEs256PrivateKey,
|
||||
LastModifiedCid,
|
||||
/// An LDAP Compatible emailAddress
|
||||
|
@ -263,11 +264,12 @@ impl TryFrom<String> for Attribute {
|
|||
ATTR_ID_VERIFICATION_ECKEY => Attribute::IdVerificationEcKey,
|
||||
ATTR_INDEX => Attribute::Index,
|
||||
ATTR_IPANTHASH => Attribute::IpaNtHash,
|
||||
ATTR_IPASSHPUBKEY => Attribute::IpaSshPubKey,
|
||||
ATTR_JWS_ES256_PRIVATE_KEY => Attribute::JwsEs256PrivateKey,
|
||||
ATTR_LAST_MODIFIED_CID => Attribute::LastModifiedCid,
|
||||
ATTR_LDAP_EMAIL_ADDRESS => Attribute::LdapEmailAddress,
|
||||
ATTR_LDAP_KEYS => Attribute::LdapKeys,
|
||||
ATTR_LDAP_SSH_PUBLICKEY => Attribute::SshPublicKey,
|
||||
ATTR_SSH_PUBLICKEY => Attribute::SshPublicKey,
|
||||
ATTR_LEGALNAME => Attribute::LegalName,
|
||||
ATTR_LOGINSHELL => Attribute::LoginShell,
|
||||
ATTR_MAIL => Attribute::Mail,
|
||||
|
@ -309,7 +311,7 @@ impl TryFrom<String> for Attribute {
|
|||
ATTR_SCOPE => Attribute::Scope,
|
||||
ATTR_SOURCE_UUID => Attribute::SourceUuid,
|
||||
ATTR_SPN => Attribute::Spn,
|
||||
ATTR_SSHPUBLICKEY => Attribute::LdapSshPublicKey,
|
||||
ATTR_LDAP_SSHPUBLICKEY => Attribute::LdapSshPublicKey,
|
||||
ATTR_SUPPLEMENTS => Attribute::Supplements,
|
||||
ATTR_SYNC_ALLOWED => Attribute::SyncAllowed,
|
||||
ATTR_SYNC_CLASS => Attribute::SyncClass,
|
||||
|
@ -346,7 +348,10 @@ impl TryFrom<String> for Attribute {
|
|||
TEST_ATTR_NUMBER => Attribute::TestNumber,
|
||||
#[cfg(any(debug_assertions, test))]
|
||||
TEST_ATTR_NOTALLOWED => Attribute::TestNotAllowed,
|
||||
_ => return Err(OperationError::InvalidAttributeName(val)),
|
||||
_ => {
|
||||
trace!("Failed to convert {} to Attribute", val);
|
||||
return Err(OperationError::InvalidAttributeName(val));
|
||||
}
|
||||
};
|
||||
Ok(res)
|
||||
}
|
||||
|
@ -409,11 +414,12 @@ impl From<Attribute> for &'static str {
|
|||
Attribute::IdVerificationEcKey => ATTR_ID_VERIFICATION_ECKEY,
|
||||
Attribute::Index => ATTR_INDEX,
|
||||
Attribute::IpaNtHash => ATTR_IPANTHASH,
|
||||
Attribute::IpaSshPubKey => ATTR_IPASSHPUBKEY,
|
||||
Attribute::JwsEs256PrivateKey => ATTR_JWS_ES256_PRIVATE_KEY,
|
||||
Attribute::LastModifiedCid => ATTR_LAST_MODIFIED_CID,
|
||||
Attribute::LdapEmailAddress => ATTR_LDAP_EMAIL_ADDRESS,
|
||||
Attribute::LdapKeys => ATTR_LDAP_KEYS,
|
||||
Attribute::LdapSshPublicKey => ATTR_SSHPUBLICKEY,
|
||||
Attribute::LdapSshPublicKey => ATTR_LDAP_SSHPUBLICKEY,
|
||||
Attribute::LegalName => ATTR_LEGALNAME,
|
||||
Attribute::LoginShell => ATTR_LOGINSHELL,
|
||||
Attribute::Mail => ATTR_MAIL,
|
||||
|
@ -455,7 +461,7 @@ impl From<Attribute> for &'static str {
|
|||
Attribute::Scope => ATTR_SCOPE,
|
||||
Attribute::SourceUuid => ATTR_SOURCE_UUID,
|
||||
Attribute::Spn => ATTR_SPN,
|
||||
Attribute::SshPublicKey => ATTR_LDAP_SSH_PUBLICKEY,
|
||||
Attribute::SshPublicKey => ATTR_SSH_PUBLICKEY,
|
||||
Attribute::Supplements => ATTR_SUPPLEMENTS,
|
||||
Attribute::SyncAllowed => ATTR_SYNC_ALLOWED,
|
||||
Attribute::SyncClass => ATTR_SYNC_CLASS,
|
||||
|
@ -521,6 +527,16 @@ impl Attribute {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> serde::Deserialize<'a> for Attribute {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'a>,
|
||||
{
|
||||
let s = String::deserialize(deserializer)?;
|
||||
Attribute::try_from(s).map_err(|e| serde::de::Error::custom(format!("{:?}", e)))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum EntryClass {
|
||||
AccessControlCreate,
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -614,7 +614,7 @@ impl ModifyEvent {
|
|||
pub fn from_target_uuid_attr_purge(
|
||||
ident: Identity,
|
||||
target_uuid: Uuid,
|
||||
attr: &str,
|
||||
attr: Attribute,
|
||||
filter: Filter<FilterInvalid>,
|
||||
qs: &QueryServerWriteTransaction,
|
||||
) -> Result<Self, OperationError> {
|
||||
|
|
|
@ -41,53 +41,44 @@ pub fn f_eq<'a>(a: Attribute, v: PartialValue) -> FC<'a> {
|
|||
FC::Eq(a.into(), v)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn f_sub(a: &str, v: PartialValue) -> FC {
|
||||
FC::Sub(a, v)
|
||||
pub fn f_sub<'a>(a: Attribute, v: PartialValue) -> FC<'a> {
|
||||
FC::Sub(a.into(), v)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn f_pres<'a>(a: Attribute) -> FC<'a> {
|
||||
FC::Pres(a.into())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn f_lt(a: &str, v: PartialValue) -> FC {
|
||||
FC::LessThan(a, v)
|
||||
pub fn f_lt<'a>(a: Attribute, v: PartialValue) -> FC<'a> {
|
||||
FC::LessThan(a.into(), v)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn f_or(vs: Vec<FC>) -> FC {
|
||||
FC::Or(vs)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn f_and(vs: Vec<FC>) -> FC {
|
||||
FC::And(vs)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn f_inc(vs: Vec<FC>) -> FC {
|
||||
FC::Inclusion(vs)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn f_andnot(fc: FC) -> FC {
|
||||
FC::AndNot(Box::new(fc))
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn f_self<'a>() -> FC<'a> {
|
||||
FC::SelfUuid
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn f_id(id: &str) -> FC<'static> {
|
||||
let uf = Uuid::parse_str(id)
|
||||
pub fn f_id(uuid: &str) -> FC<'static> {
|
||||
let uf = Uuid::parse_str(uuid)
|
||||
.ok()
|
||||
.map(|u| FC::Eq(Attribute::Uuid.as_ref(), PartialValue::Uuid(u)));
|
||||
let spnf = PartialValue::new_spn_s(id).map(|spn| FC::Eq("spn", spn));
|
||||
let nf = FC::Eq(Attribute::Name.as_ref(), PartialValue::new_iname(id));
|
||||
let spnf = PartialValue::new_spn_s(uuid).map(|spn| FC::Eq(Attribute::Spn.as_ref(), spn));
|
||||
let nf = FC::Eq(Attribute::Name.as_ref(), PartialValue::new_iname(uuid));
|
||||
let f: Vec<_> = iter::once(uf)
|
||||
.chain(iter::once(spnf))
|
||||
.flatten()
|
||||
|
@ -96,7 +87,6 @@ pub fn f_id(id: &str) -> FC<'static> {
|
|||
FC::Or(f)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn f_spn_name(id: &str) -> FC<'static> {
|
||||
let spnf = PartialValue::new_spn_s(id).map(|spn| FC::Eq(Attribute::Spn.as_ref(), spn));
|
||||
let nf = FC::Eq(Attribute::Name.as_ref(), PartialValue::new_iname(id));
|
||||
|
@ -1361,7 +1351,7 @@ mod tests {
|
|||
f_eq(Attribute::UserId, PartialValue::new_iutf8("test_a")),
|
||||
f_eq(Attribute::UserId, PartialValue::new_iutf8("test_b")),
|
||||
]),
|
||||
f_sub(Attribute::Class.as_ref(), EntryClass::User.into()),
|
||||
f_sub(Attribute::Class, EntryClass::User.into()),
|
||||
]));
|
||||
}
|
||||
|
||||
|
@ -1422,14 +1412,14 @@ mod tests {
|
|||
Attribute::Class,
|
||||
EntryClass::TestClass.to_partialvalue()
|
||||
)]),
|
||||
f_sub(Attribute::Class.as_ref(), PartialValue::new_class("te")),
|
||||
f_sub(Attribute::Class, PartialValue::new_class("te")),
|
||||
f_pres(Attribute::Class),
|
||||
f_eq(Attribute::Class, EntryClass::TestClass.to_partialvalue())
|
||||
]),
|
||||
f_and(vec![
|
||||
f_eq(Attribute::Class, EntryClass::TestClass.to_partialvalue()),
|
||||
f_pres(Attribute::Class),
|
||||
f_sub(Attribute::Class.as_ref(), PartialValue::new_class("te")),
|
||||
f_sub(Attribute::Class, PartialValue::new_class("te")),
|
||||
])
|
||||
);
|
||||
|
||||
|
@ -1441,7 +1431,7 @@ mod tests {
|
|||
f_eq(Attribute::Class, EntryClass::TestClass.to_partialvalue()),
|
||||
f_eq(Attribute::Uid, PartialValue::new_class("bar")),
|
||||
]),
|
||||
f_sub(Attribute::Class.as_ref(), PartialValue::new_class("te")),
|
||||
f_sub(Attribute::Class, PartialValue::new_class("te")),
|
||||
f_pres(Attribute::Class),
|
||||
f_eq(Attribute::Class, EntryClass::TestClass.to_partialvalue())
|
||||
]),
|
||||
|
@ -1450,7 +1440,7 @@ mod tests {
|
|||
f_eq(Attribute::Class, EntryClass::TestClass.to_partialvalue()),
|
||||
f_pres(Attribute::Class),
|
||||
f_eq(Attribute::Uid, PartialValue::new_class("bar")),
|
||||
f_sub(Attribute::Class.as_ref(), PartialValue::new_class("te")),
|
||||
f_sub(Attribute::Class, PartialValue::new_class("te")),
|
||||
])
|
||||
);
|
||||
|
||||
|
@ -1458,14 +1448,14 @@ mod tests {
|
|||
f_or(vec![
|
||||
f_eq(Attribute::Class, EntryClass::TestClass.to_partialvalue()),
|
||||
f_pres(Attribute::Class),
|
||||
f_sub(Attribute::Class.as_ref(), PartialValue::new_class("te")),
|
||||
f_sub(Attribute::Class, PartialValue::new_class("te")),
|
||||
f_or(vec![f_eq(
|
||||
Attribute::Class,
|
||||
EntryClass::TestClass.to_partialvalue()
|
||||
)]),
|
||||
]),
|
||||
f_or(vec![
|
||||
f_sub(Attribute::Class.as_ref(), PartialValue::new_class("te")),
|
||||
f_sub(Attribute::Class, PartialValue::new_class("te")),
|
||||
f_pres(Attribute::Class),
|
||||
f_eq(Attribute::Class, EntryClass::TestClass.to_partialvalue())
|
||||
])
|
||||
|
@ -1538,7 +1528,7 @@ mod tests {
|
|||
assert_eq!(f_t3b.partial_cmp(&f_t1a), Some(Ordering::Less));
|
||||
|
||||
// transitivity: a < b and b < c implies a < c. The same must hold for both == and >.
|
||||
let f_t4b = filter_resolved!(f_sub("userid", PartialValue::new_iutf8("")));
|
||||
let f_t4b = filter_resolved!(f_sub(Attribute::UserId, PartialValue::new_iutf8("")));
|
||||
assert_eq!(f_t1a.partial_cmp(&f_t4b), Some(Ordering::Less));
|
||||
assert_eq!(f_t3b.partial_cmp(&f_t4b), Some(Ordering::Less));
|
||||
|
||||
|
@ -1577,22 +1567,13 @@ mod tests {
|
|||
)
|
||||
.into_sealed_new();
|
||||
|
||||
let f_t1a = filter_resolved!(f_lt(
|
||||
Attribute::GidNumber.as_ref(),
|
||||
PartialValue::new_uint32(500)
|
||||
));
|
||||
let f_t1a = filter_resolved!(f_lt(Attribute::GidNumber, PartialValue::new_uint32(500)));
|
||||
assert!(!e.entry_match_no_index(&f_t1a));
|
||||
|
||||
let f_t1b = filter_resolved!(f_lt(
|
||||
Attribute::GidNumber.as_ref(),
|
||||
PartialValue::new_uint32(1000)
|
||||
));
|
||||
let f_t1b = filter_resolved!(f_lt(Attribute::GidNumber, PartialValue::new_uint32(1000)));
|
||||
assert!(!e.entry_match_no_index(&f_t1b));
|
||||
|
||||
let f_t1c = filter_resolved!(f_lt(
|
||||
Attribute::GidNumber.as_ref(),
|
||||
PartialValue::new_uint32(1001)
|
||||
));
|
||||
let f_t1c = filter_resolved!(f_lt(Attribute::GidNumber, PartialValue::new_uint32(1001)));
|
||||
assert!(e.entry_match_no_index(&f_t1c));
|
||||
}
|
||||
|
||||
|
|
|
@ -27,65 +27,65 @@ use kanidm_lib_crypto::CryptoPolicy;
|
|||
macro_rules! try_from_entry {
|
||||
($value:expr, $groups:expr) => {{
|
||||
// Check the classes
|
||||
if !$value.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
&EntryClass::Account.to_partialvalue(),
|
||||
) {
|
||||
return Err(OperationError::InvalidAccountState(
|
||||
"Missing class: account".to_string(),
|
||||
));
|
||||
if !$value.attribute_equality(Attribute::Class, &EntryClass::Account.to_partialvalue()) {
|
||||
return Err(OperationError::InvalidAccountState(format!(
|
||||
"Missing class: {}",
|
||||
EntryClass::Account
|
||||
)));
|
||||
}
|
||||
|
||||
// Now extract our needed attributes
|
||||
let name = $value
|
||||
.get_ava_single_iname(Attribute::Name.as_ref())
|
||||
.get_ava_single_iname(Attribute::Name)
|
||||
.map(|s| s.to_string())
|
||||
.ok_or(OperationError::InvalidAccountState(
|
||||
"Missing attribute: name".to_string(),
|
||||
))?;
|
||||
.ok_or(OperationError::InvalidAccountState(format!(
|
||||
"Missing attribute: {}",
|
||||
Attribute::Name
|
||||
)))?;
|
||||
|
||||
let displayname = $value
|
||||
.get_ava_single_utf8(Attribute::DisplayName.as_ref())
|
||||
.get_ava_single_utf8(Attribute::DisplayName)
|
||||
.map(|s| s.to_string())
|
||||
.ok_or(OperationError::InvalidAccountState(
|
||||
"Missing attribute: displayname".to_string(),
|
||||
))?;
|
||||
.ok_or(OperationError::InvalidAccountState(format!(
|
||||
"Missing attribute: {}",
|
||||
Attribute::DisplayName
|
||||
)))?;
|
||||
|
||||
let sync_parent_uuid = $value.get_ava_single_refer("sync_parent_uuid");
|
||||
let sync_parent_uuid = $value.get_ava_single_refer(Attribute::SyncParentUuid);
|
||||
|
||||
let primary = $value
|
||||
.get_ava_single_credential("primary_credential")
|
||||
.get_ava_single_credential(Attribute::PrimaryCredential)
|
||||
.map(|v| v.clone());
|
||||
|
||||
let passkeys = $value
|
||||
.get_ava_passkeys("passkeys")
|
||||
.get_ava_passkeys(Attribute::PassKeys)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
let devicekeys = $value
|
||||
.get_ava_devicekeys("devicekeys")
|
||||
.get_ava_devicekeys(Attribute::DeviceKeys)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
let spn = $value
|
||||
.get_ava_single_proto_string(Attribute::Spn.as_ref())
|
||||
.ok_or(OperationError::InvalidAccountState(
|
||||
"Missing attribute: spn".to_string(),
|
||||
))?;
|
||||
let spn = $value.get_ava_single_proto_string(Attribute::Spn).ok_or(
|
||||
OperationError::InvalidAccountState(format!("Missing attribute: {}", Attribute::Spn)),
|
||||
)?;
|
||||
|
||||
let mail_primary = $value.get_ava_mail_primary("mail").map(str::to_string);
|
||||
let mail_primary = $value
|
||||
.get_ava_mail_primary(Attribute::Mail)
|
||||
.map(str::to_string);
|
||||
|
||||
let mail = $value
|
||||
.get_ava_iter_mail("mail")
|
||||
.get_ava_iter_mail(Attribute::Mail)
|
||||
.map(|i| i.map(str::to_string).collect())
|
||||
.unwrap_or_else(Vec::new);
|
||||
|
||||
let valid_from = $value.get_ava_single_datetime("account_valid_from");
|
||||
let valid_from = $value.get_ava_single_datetime(Attribute::AccountValidFrom);
|
||||
|
||||
let expire = $value.get_ava_single_datetime("account_expire");
|
||||
let expire = $value.get_ava_single_datetime(Attribute::AccountExpire);
|
||||
|
||||
let radius_secret = $value
|
||||
.get_ava_single_secret("radius_secret")
|
||||
.get_ava_single_secret(Attribute::RadiusSecret)
|
||||
.map(str::to_string);
|
||||
|
||||
// Resolved by the caller
|
||||
|
@ -94,7 +94,7 @@ macro_rules! try_from_entry {
|
|||
let uuid = $value.get_uuid().clone();
|
||||
|
||||
let credential_update_intent_tokens = $value
|
||||
.get_ava_as_intenttokens("credential_update_intent_token")
|
||||
.get_ava_as_intenttokens(Attribute::CredentialUpdateIntentToken)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
|
@ -107,22 +107,16 @@ macro_rules! try_from_entry {
|
|||
.collect();
|
||||
|
||||
// For now disable cred updates on sync accounts too.
|
||||
if $value.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
&EntryClass::Person.to_partialvalue(),
|
||||
) {
|
||||
if $value.attribute_equality(Attribute::Class, &EntryClass::Person.to_partialvalue()) {
|
||||
ui_hints.insert(UiHint::CredentialUpdate);
|
||||
}
|
||||
|
||||
if $value.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
&EntryClass::SyncObject.to_partialvalue(),
|
||||
) {
|
||||
if $value.attribute_equality(Attribute::Class, &EntryClass::SyncObject.to_partialvalue()) {
|
||||
ui_hints.insert(UiHint::SynchronisedAccount);
|
||||
}
|
||||
|
||||
if $value.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
Attribute::Class,
|
||||
&EntryClass::PosixAccount.to_partialvalue(),
|
||||
) {
|
||||
ui_hints.insert(UiHint::PosixAccount);
|
||||
|
@ -408,13 +402,19 @@ impl Account {
|
|||
Some(primary) => {
|
||||
let ncred = primary.set_password(crypto_policy, cleartext)?;
|
||||
let vcred = Value::new_credential("primary", ncred);
|
||||
Ok(ModifyList::new_purge_and_set("primary_credential", vcred))
|
||||
Ok(ModifyList::new_purge_and_set(
|
||||
Attribute::PrimaryCredential,
|
||||
vcred,
|
||||
))
|
||||
}
|
||||
// Make a new credential instead
|
||||
None => {
|
||||
let ncred = Credential::new_password_only(crypto_policy, cleartext)?;
|
||||
let vcred = Value::new_credential("primary", ncred);
|
||||
Ok(ModifyList::new_purge_and_set("primary_credential", vcred))
|
||||
Ok(ModifyList::new_purge_and_set(
|
||||
Attribute::PrimaryCredential,
|
||||
vcred,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -430,7 +430,7 @@ impl Account {
|
|||
if let Some(ncred) = primary.upgrade_password(crypto_policy, cleartext)? {
|
||||
let vcred = Value::new_credential("primary", ncred);
|
||||
Ok(Some(ModifyList::new_purge_and_set(
|
||||
"primary_credential",
|
||||
Attribute::PrimaryCredential,
|
||||
vcred,
|
||||
)))
|
||||
} else {
|
||||
|
@ -456,20 +456,20 @@ impl Account {
|
|||
|
||||
if let Some(ncred) = opt_ncred {
|
||||
let vcred = Value::new_credential("primary", ncred);
|
||||
ml.push(Modify::Purged("primary_credential".into()));
|
||||
ml.push(Modify::Present("primary_credential".into(), vcred));
|
||||
ml.push(Modify::Purged(Attribute::PrimaryCredential.into()));
|
||||
ml.push(Modify::Present(Attribute::PrimaryCredential.into(), vcred));
|
||||
}
|
||||
|
||||
// Is it a passkey?
|
||||
self.passkeys.iter_mut().for_each(|(u, (t, k))| {
|
||||
if let Some(true) = k.update_credential(auth_result) {
|
||||
ml.push(Modify::Removed(
|
||||
"passkeys".into(),
|
||||
Attribute::PassKeys.into(),
|
||||
PartialValue::Passkey(*u),
|
||||
));
|
||||
|
||||
ml.push(Modify::Present(
|
||||
"passkeys".into(),
|
||||
Attribute::PassKeys.into(),
|
||||
Value::Passkey(*u, t.clone(), k.clone()),
|
||||
));
|
||||
}
|
||||
|
@ -493,7 +493,10 @@ impl Account {
|
|||
match r_ncred {
|
||||
Ok(ncred) => {
|
||||
let vcred = Value::new_credential("primary", ncred);
|
||||
Ok(ModifyList::new_purge_and_set("primary_credential", vcred))
|
||||
Ok(ModifyList::new_purge_and_set(
|
||||
Attribute::PrimaryCredential,
|
||||
vcred,
|
||||
))
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
|
@ -510,7 +513,10 @@ impl Account {
|
|||
cleartext: &str,
|
||||
) -> Result<ModifyList<ModifyInvalid>, OperationError> {
|
||||
let vcred = Value::new_secret_str(cleartext);
|
||||
Ok(ModifyList::new_purge_and_set("radius_secret", vcred))
|
||||
Ok(ModifyList::new_purge_and_set(
|
||||
Attribute::RadiusSecret,
|
||||
vcred,
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) fn to_credentialstatus(&self) -> Result<CredentialStatus, OperationError> {
|
||||
|
@ -548,8 +554,12 @@ impl Account {
|
|||
|
||||
let within_valid_window = Account::check_within_valid_time(
|
||||
ct,
|
||||
entry.get_ava_single_datetime("account_valid_from").as_ref(),
|
||||
entry.get_ava_single_datetime("account_expire").as_ref(),
|
||||
entry
|
||||
.get_ava_single_datetime(Attribute::AccountValidFrom)
|
||||
.as_ref(),
|
||||
entry
|
||||
.get_ava_single_datetime(Attribute::AccountExpire)
|
||||
.as_ref(),
|
||||
);
|
||||
|
||||
if !within_valid_window {
|
||||
|
@ -567,7 +577,7 @@ impl Account {
|
|||
} else {
|
||||
// Get the sessions.
|
||||
let session_present = entry
|
||||
.get_ava_as_session_map("user_auth_token_session")
|
||||
.get_ava_as_session_map(Attribute::UserAuthTokenSession)
|
||||
.and_then(|session_map| session_map.get(&uat.session_id));
|
||||
|
||||
// Important - we don't have to check the expiry time against ct here since it was
|
||||
|
@ -694,13 +704,16 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
|
||||
// Copy the current classes
|
||||
let prev_classes: BTreeSet<_> = account_entry
|
||||
.get_ava_as_iutf8_iter(Attribute::Class.as_ref())
|
||||
.get_ava_as_iutf8_iter(Attribute::Class)
|
||||
.ok_or_else(|| {
|
||||
admin_error!(
|
||||
"Invalid entry, {} attribute is not present or not iutf8",
|
||||
Attribute::Class.as_ref()
|
||||
);
|
||||
OperationError::InvalidAccountState("Missing attribute: class".to_string())
|
||||
OperationError::InvalidAccountState(format!(
|
||||
"Missing attribute: {}",
|
||||
Attribute::Class
|
||||
))
|
||||
})?
|
||||
.collect();
|
||||
|
||||
|
@ -723,7 +736,7 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
// Now construct the modlist which:
|
||||
// removes service_account
|
||||
let mut modlist = ModifyList::new_remove(
|
||||
Attribute::Class.as_ref(),
|
||||
Attribute::Class,
|
||||
EntryClass::ServiceAccount.to_partialvalue(),
|
||||
);
|
||||
// add person
|
||||
|
@ -788,7 +801,7 @@ impl<'a> IdmServerProxyReadTransaction<'a> {
|
|||
.and_then(|e| {
|
||||
let account_id = e.get_uuid();
|
||||
// From the entry, turn it into the value
|
||||
e.get_ava_as_session_map("user_auth_token_session")
|
||||
e.get_ava_as_session_map(Attribute::UserAuthTokenSession)
|
||||
.map(|smap| {
|
||||
smap.iter()
|
||||
.map(|(u, s)| {
|
||||
|
|
|
@ -40,16 +40,16 @@ impl<'a> IdmServerProxyReadTransaction<'a> {
|
|||
.iter()
|
||||
.filter_map(|entry| {
|
||||
let display_name = entry
|
||||
.get_ava_single_utf8(Attribute::DisplayName.as_ref())
|
||||
.get_ava_single_utf8(Attribute::DisplayName)
|
||||
.map(str::to_string)?;
|
||||
|
||||
let redirect_url = entry
|
||||
.get_ava_single_url("oauth2_rs_origin_landing")
|
||||
.or_else(|| entry.get_ava_single_url(Attribute::OAuth2RsOrigin.as_ref()))
|
||||
.get_ava_single_url(Attribute::OAuth2RsOriginLanding)
|
||||
.or_else(|| entry.get_ava_single_url(Attribute::OAuth2RsOrigin))
|
||||
.cloned()?;
|
||||
|
||||
let name = entry
|
||||
.get_ava_single_iname("oauth2_rs_name")
|
||||
.get_ava_single_iname(Attribute::OAuth2RsName)
|
||||
.map(str::to_string)?;
|
||||
|
||||
Some(AppLink::Oauth2 {
|
||||
|
@ -160,7 +160,7 @@ mod tests {
|
|||
let mut idms_prox_write = idms.proxy_write(ct).await;
|
||||
let me_inv_m = ModifyEvent::new_internal_invalid(
|
||||
filter!(f_eq(Attribute::Uuid, PartialValue::Refer(grp_uuid))),
|
||||
ModifyList::new_append("member", Value::Refer(usr_uuid)),
|
||||
ModifyList::new_append(Attribute::Member, Value::Refer(usr_uuid)),
|
||||
);
|
||||
assert!(idms_prox_write.qs_write.modify(&me_inv_m).is_ok());
|
||||
assert!(idms_prox_write.commit().is_ok());
|
||||
|
|
|
@ -372,19 +372,19 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
let eperm_search_primary_cred = match &eperm.search {
|
||||
Access::Denied => false,
|
||||
Access::Grant => true,
|
||||
Access::Allow(attrs) => attrs.contains("primary_credential"),
|
||||
Access::Allow(attrs) => attrs.contains(Attribute::PrimaryCredential.as_ref()),
|
||||
};
|
||||
|
||||
let eperm_mod_primary_cred = match &eperm.modify_pres {
|
||||
Access::Denied => false,
|
||||
Access::Grant => true,
|
||||
Access::Allow(attrs) => attrs.contains("primary_credential"),
|
||||
Access::Allow(attrs) => attrs.contains(Attribute::PrimaryCredential.as_ref()),
|
||||
};
|
||||
|
||||
let eperm_rem_primary_cred = match &eperm.modify_rem {
|
||||
Access::Denied => false,
|
||||
Access::Grant => true,
|
||||
Access::Allow(attrs) => attrs.contains("primary_credential"),
|
||||
Access::Allow(attrs) => attrs.contains(Attribute::PrimaryCredential.as_ref()),
|
||||
};
|
||||
|
||||
let primary_can_edit =
|
||||
|
@ -393,19 +393,19 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
let eperm_search_passkeys = match &eperm.search {
|
||||
Access::Denied => false,
|
||||
Access::Grant => true,
|
||||
Access::Allow(attrs) => attrs.contains("passkeys"),
|
||||
Access::Allow(attrs) => attrs.contains(Attribute::PassKeys.as_ref()),
|
||||
};
|
||||
|
||||
let eperm_mod_passkeys = match &eperm.modify_pres {
|
||||
Access::Denied => false,
|
||||
Access::Grant => true,
|
||||
Access::Allow(attrs) => attrs.contains("passkeys"),
|
||||
Access::Allow(attrs) => attrs.contains(Attribute::PassKeys.as_ref()),
|
||||
};
|
||||
|
||||
let eperm_rem_passkeys = match &eperm.modify_rem {
|
||||
Access::Denied => false,
|
||||
Access::Grant => true,
|
||||
Access::Allow(attrs) => attrs.contains("passkeys"),
|
||||
Access::Allow(attrs) => attrs.contains(Attribute::PassKeys.as_ref()),
|
||||
};
|
||||
|
||||
let passkeys_can_edit = eperm_search_passkeys && eperm_mod_passkeys && eperm_rem_passkeys;
|
||||
|
@ -431,7 +431,7 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
match &eperm.search {
|
||||
Access::Denied => false,
|
||||
Access::Grant => true,
|
||||
Access::Allow(attrs) => attrs.contains("sync_credential_portal"),
|
||||
Access::Allow(attrs) => attrs.contains(Attribute::SyncCredentialPortal.as_ref()),
|
||||
}
|
||||
} else {
|
||||
false
|
||||
|
@ -487,7 +487,7 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
(Some(sync_parent_uuid), true) => {
|
||||
let sync_entry = self.qs_write.internal_search_uuid(sync_parent_uuid)?;
|
||||
sync_entry
|
||||
.get_ava_single_url("sync_credential_portal")
|
||||
.get_ava_single_url(Attribute::SyncCredentialPortal)
|
||||
.cloned()
|
||||
.map(CUExtPortal::Some)
|
||||
.unwrap_or(CUExtPortal::Hidden)
|
||||
|
@ -582,7 +582,7 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
// write anyway, and instead on the intent access path we invalidate IF the collision
|
||||
// occurs.
|
||||
let mut modlist = ModifyList::new_append(
|
||||
"credential_update_intent_token",
|
||||
Attribute::CredentialUpdateIntentToken,
|
||||
Value::IntentToken(
|
||||
intent_id.clone(),
|
||||
IntentTokenState::Valid { max_ttl, perms },
|
||||
|
|
|
@ -20,7 +20,7 @@ macro_rules! try_from_account_e {
|
|||
($value:expr, $qs:expr) => {{
|
||||
/*
|
||||
let name = $value
|
||||
.get_ava_single_iname(Attribute::Name.as_ref())
|
||||
.get_ava_single_iname(Attribute::Name)
|
||||
.map(str::to_string)
|
||||
.ok_or_else(|| {
|
||||
OperationError::InvalidAccountState("Missing attribute: name".to_string())
|
||||
|
@ -28,11 +28,9 @@ macro_rules! try_from_account_e {
|
|||
*/
|
||||
|
||||
// Setup the user private group
|
||||
let spn = $value
|
||||
.get_ava_single_proto_string(Attribute::Spn.as_ref())
|
||||
.ok_or(OperationError::InvalidAccountState(
|
||||
"Missing attribute: spn".to_string(),
|
||||
))?;
|
||||
let spn = $value.get_ava_single_proto_string(Attribute::Spn).ok_or(
|
||||
OperationError::InvalidAccountState(format!("Missing attribute: {}", Attribute::Spn)),
|
||||
)?;
|
||||
|
||||
let uuid = $value.get_uuid();
|
||||
|
||||
|
@ -45,7 +43,7 @@ macro_rules! try_from_account_e {
|
|||
ui_hints,
|
||||
};
|
||||
|
||||
let mut groups: Vec<Group> = match $value.get_ava_as_refuuid("memberof") {
|
||||
let mut groups: Vec<Group> = match $value.get_ava_as_refuuid(Attribute::MemberOf) {
|
||||
Some(riter) => {
|
||||
// given a list of uuid, make a filter: even if this is empty, the be will
|
||||
// just give and empty result set.
|
||||
|
@ -104,7 +102,7 @@ impl Group {
|
|||
pub fn try_from_entry(
|
||||
value: &Entry<EntrySealed, EntryCommitted>,
|
||||
) -> Result<Self, OperationError> {
|
||||
if !value.attribute_equality(Attribute::Class.as_ref(), &EntryClass::Group.into()) {
|
||||
if !value.attribute_equality(Attribute::Class, &EntryClass::Group.into()) {
|
||||
return Err(OperationError::InvalidAccountState(
|
||||
"Missing class: group".to_string(),
|
||||
));
|
||||
|
@ -113,14 +111,14 @@ impl Group {
|
|||
// Now extract our needed attributes
|
||||
/*
|
||||
let name = value
|
||||
.get_ava_single_iname(Attribute::Name.as_ref())
|
||||
.get_ava_single_iname(Attribute::Name)
|
||||
.map(|s| s.to_string())
|
||||
.ok_or_else(|| {
|
||||
OperationError::InvalidAccountState("Missing attribute: name".to_string())
|
||||
})?;
|
||||
*/
|
||||
let spn = value
|
||||
.get_ava_single_proto_string(Attribute::Spn.as_ref())
|
||||
.get_ava_single_proto_string(Attribute::Spn)
|
||||
.ok_or_else(|| {
|
||||
OperationError::InvalidAccountState("Missing attribute: spn".to_string())
|
||||
})?;
|
||||
|
@ -128,7 +126,7 @@ impl Group {
|
|||
let uuid = value.get_uuid();
|
||||
|
||||
let ui_hints = value
|
||||
.get_ava_uihint("grant_ui_hint")
|
||||
.get_ava_uihint(Attribute::GrantUiHint)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use std::time::SystemTime;
|
||||
|
||||
use kanidm_proto::constants::ATTR_ID_VERIFICATION_ECKEY;
|
||||
use kanidm_proto::{internal::IdentifyUserResponse, v1::OperationError};
|
||||
use openssl::ec::EcKey;
|
||||
use openssl::pkey::{PKey, Private, Public};
|
||||
|
@ -9,7 +8,7 @@ use sketching::admin_error;
|
|||
use uuid::Uuid;
|
||||
|
||||
use crate::credential::totp::{Totp, TotpAlgo, TotpDigits};
|
||||
use crate::prelude::{tagged_event, EventTag};
|
||||
use crate::prelude::{tagged_event, Attribute, EventTag};
|
||||
use crate::server::QueryServerTransaction;
|
||||
use crate::{event::SearchEvent, server::identity::Identity};
|
||||
|
||||
|
@ -161,7 +160,7 @@ impl<'a> IdmServerProxyReadTransaction<'a> {
|
|||
.search(&search)
|
||||
.and_then(|mut entries| entries.pop().ok_or(OperationError::NoMatchingEntries))
|
||||
.map(
|
||||
|entry| match entry.get_ava_single_eckey_private(ATTR_ID_VERIFICATION_ECKEY) {
|
||||
|entry| match entry.get_ava_single_eckey_private(Attribute::IdVerificationEcKey) {
|
||||
Some(key) => key.check_key().is_ok(),
|
||||
None => false,
|
||||
},
|
||||
|
@ -187,7 +186,7 @@ impl<'a> IdmServerProxyReadTransaction<'a> {
|
|||
.search(&search)
|
||||
.and_then(|mut entries| entries.pop().ok_or(OperationError::NoMatchingEntries))?;
|
||||
|
||||
match user_entry.get_ava_single_eckey_public(ATTR_ID_VERIFICATION_ECKEY) {
|
||||
match user_entry.get_ava_single_eckey_public(Attribute::IdVerificationEcKey) {
|
||||
Some(key) => Ok(key.check_key().is_ok()),
|
||||
None => Ok(false),
|
||||
}
|
||||
|
@ -210,7 +209,7 @@ impl<'a> IdmServerProxyReadTransaction<'a> {
|
|||
.search(&search)
|
||||
.and_then(|mut entries| entries.pop().ok_or(OperationError::NoMatchingEntries))
|
||||
.and_then(|entry| {
|
||||
match entry.get_ava_single_eckey_private(ATTR_ID_VERIFICATION_ECKEY) {
|
||||
match entry.get_ava_single_eckey_private(Attribute::IdVerificationEcKey) {
|
||||
Some(key) => Ok(key.clone()),
|
||||
None => Err(OperationError::InvalidAccountState(format!(
|
||||
"{}'s private key is missing!",
|
||||
|
@ -240,14 +239,14 @@ impl<'a> IdmServerProxyReadTransaction<'a> {
|
|||
self.qs_read
|
||||
.search(&search)
|
||||
.and_then(|mut entries| entries.pop().ok_or(OperationError::NoMatchingEntries))
|
||||
.and_then(
|
||||
|entry| match entry.get_ava_single_eckey_public(ATTR_ID_VERIFICATION_ECKEY) {
|
||||
.and_then(|entry| {
|
||||
match entry.get_ava_single_eckey_public(Attribute::IdVerificationEcKey) {
|
||||
Some(key) => Ok(key.clone()),
|
||||
None => Err(OperationError::InvalidAccountState(format!(
|
||||
"{target}'s public key is missing!",
|
||||
))),
|
||||
},
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn compute_totp(&mut self, totp_secret: Vec<u8>) -> Result<u32, OperationError> {
|
||||
|
|
|
@ -69,11 +69,11 @@ impl LdapServer {
|
|||
.internal_search_uuid(UUID_DOMAIN_INFO)?;
|
||||
|
||||
let basedn = domain_entry
|
||||
.get_ava_single_iutf8("domain_ldap_basedn")
|
||||
.get_ava_single_iutf8(Attribute::DomainLdapBasedn)
|
||||
.map(|s| s.to_string())
|
||||
.or_else(|| {
|
||||
domain_entry
|
||||
.get_ava_single_iname(Attribute::DomainName.as_ref())
|
||||
.get_ava_single_iname(Attribute::DomainName)
|
||||
.map(ldap_domain_to_dc)
|
||||
})
|
||||
.ok_or(OperationError::InvalidEntryState)?;
|
||||
|
@ -88,7 +88,7 @@ impl LdapServer {
|
|||
dn: "".to_string(),
|
||||
attributes: vec![
|
||||
LdapPartialAttribute {
|
||||
atype: "objectclass".to_string(),
|
||||
atype: ATTR_OBJECTCLASS.to_string(),
|
||||
vals: vec!["top".as_bytes().to_vec()],
|
||||
},
|
||||
LdapPartialAttribute {
|
||||
|
@ -135,7 +135,7 @@ impl LdapServer {
|
|||
// eventid: &Uuid,
|
||||
) -> Result<Vec<LdapMsg>, OperationError> {
|
||||
admin_info!("Attempt LDAP Search for {}", uat.spn);
|
||||
// If the request is "", Base, Present("objectclass"), [], then we want the rootdse.
|
||||
// If the request is "", Base, Present(Attribute::ObjectClass.into()), [], then we want the rootdse.
|
||||
if sr.base.is_empty() && sr.scope == LdapSearchScope::Base {
|
||||
admin_info!("LDAP Search success - RootDSE");
|
||||
Ok(vec![
|
||||
|
@ -563,16 +563,16 @@ pub(crate) fn ldap_all_vattrs() -> Vec<String> {
|
|||
vec![
|
||||
ATTR_CN.to_string(),
|
||||
ATTR_EMAIL.to_string(),
|
||||
"emailaddress".to_string(),
|
||||
"emailalternative".to_string(),
|
||||
"emailprimary".to_string(),
|
||||
ATTR_LDAP_EMAIL_ADDRESS.to_string(),
|
||||
LDAP_ATTR_EMAIL_ALTERNATIVE.to_string(),
|
||||
LDAP_ATTR_EMAIL_PRIMARY.to_string(),
|
||||
"entrydn".to_string(),
|
||||
"entryuuid".to_string(),
|
||||
"keys".to_string(),
|
||||
"mail;alternative".to_string(),
|
||||
"mail;primary".to_string(),
|
||||
"objectclass".to_string(),
|
||||
ATTR_LDAP_SSH_PUBLICKEY.to_string(),
|
||||
LDAP_ATTR_ENTRYUUID.to_string(),
|
||||
LDAP_ATTR_KEYS.to_string(),
|
||||
LDAP_ATTR_MAIL_ALTERNATIVE.to_string(),
|
||||
LDAP_ATTR_MAIL_PRIMARY.to_string(),
|
||||
ATTR_OBJECTCLASS.to_string(),
|
||||
ATTR_LDAP_SSHPUBLICKEY.to_string(),
|
||||
ATTR_UIDNUMBER.to_string(),
|
||||
]
|
||||
}
|
||||
|
@ -586,18 +586,18 @@ pub(crate) fn ldap_vattr_map(input: &str) -> Option<&str> {
|
|||
//
|
||||
// LDAP NAME KANI ATTR SOURCE NAME
|
||||
match input {
|
||||
"cn" => Some(ATTR_NAME),
|
||||
ATTR_CN => Some(ATTR_NAME),
|
||||
ATTR_EMAIL => Some(ATTR_MAIL),
|
||||
"emailaddress" => Some(ATTR_MAIL),
|
||||
"emailalternative" => Some(ATTR_MAIL),
|
||||
"emailprimary" => Some(ATTR_MAIL),
|
||||
"entryuuid" => Some(ATTR_UUID),
|
||||
"keys" => Some(ATTR_LDAP_SSH_PUBLICKEY),
|
||||
"mail;alternative" => Some(ATTR_MAIL),
|
||||
"mail;primary" => Some(ATTR_MAIL),
|
||||
"objectclass" => Some(ATTR_CLASS),
|
||||
ATTR_LDAP_SSH_PUBLICKEY => Some(ATTR_LDAP_SSH_PUBLICKEY), // no-underscore -> underscore
|
||||
"uidnumber" => Some(ATTR_GIDNUMBER), // yes this is intentional
|
||||
ATTR_LDAP_EMAIL_ADDRESS => Some(ATTR_MAIL),
|
||||
LDAP_ATTR_EMAIL_ALTERNATIVE => Some(ATTR_MAIL),
|
||||
LDAP_ATTR_EMAIL_PRIMARY => Some(ATTR_MAIL),
|
||||
LDAP_ATTR_ENTRYUUID => Some(ATTR_UUID),
|
||||
LDAP_ATTR_KEYS => Some(ATTR_SSH_PUBLICKEY),
|
||||
LDAP_ATTR_MAIL_ALTERNATIVE => Some(ATTR_MAIL),
|
||||
LDAP_ATTR_MAIL_PRIMARY => Some(ATTR_MAIL),
|
||||
ATTR_OBJECTCLASS => Some(ATTR_CLASS),
|
||||
ATTR_LDAP_SSHPUBLICKEY => Some(ATTR_SSH_PUBLICKEY), // no-underscore -> underscore
|
||||
ATTR_UIDNUMBER => Some(ATTR_GIDNUMBER), // yes this is intentional
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -853,7 +853,7 @@ mod tests {
|
|||
msgid: 1,
|
||||
base: "dc=example,dc=com".to_string(),
|
||||
scope: LdapSearchScope::Subtree,
|
||||
filter: LdapFilter::Equality("name".to_string(), "testperson1".to_string()),
|
||||
filter: LdapFilter::Equality(Attribute::Name.to_string(), "testperson1".to_string()),
|
||||
attrs: vec!["*".to_string()],
|
||||
};
|
||||
let r1 = ldaps.do_search(idms, &sr, &anon_t).await.unwrap();
|
||||
|
@ -885,7 +885,7 @@ mod tests {
|
|||
msgid: 1,
|
||||
base: "dc=example,dc=com".to_string(),
|
||||
scope: LdapSearchScope::Subtree,
|
||||
filter: LdapFilter::Equality("name".to_string(), "testperson1".to_string()),
|
||||
filter: LdapFilter::Equality(Attribute::Name.to_string(), "testperson1".to_string()),
|
||||
attrs: vec!["+".to_string()],
|
||||
};
|
||||
let r1 = ldaps.do_search(idms, &sr, &anon_t).await.unwrap();
|
||||
|
@ -924,12 +924,12 @@ mod tests {
|
|||
msgid: 1,
|
||||
base: "dc=example,dc=com".to_string(),
|
||||
scope: LdapSearchScope::Subtree,
|
||||
filter: LdapFilter::Equality("name".to_string(), "testperson1".to_string()),
|
||||
filter: LdapFilter::Equality(Attribute::Name.to_string(), "testperson1".to_string()),
|
||||
attrs: vec![
|
||||
"name".to_string(),
|
||||
"entrydn".to_string(),
|
||||
"keys".to_string(),
|
||||
"uidnumber".to_string(),
|
||||
LDAP_ATTR_NAME.to_string(),
|
||||
Attribute::EntryDn.to_string(),
|
||||
ATTR_LDAP_KEYS.to_string(),
|
||||
Attribute::UidNumber.to_string(),
|
||||
],
|
||||
};
|
||||
let r1 = ldaps.do_search(idms, &sr, &anon_t).await.unwrap();
|
||||
|
@ -967,15 +967,18 @@ mod tests {
|
|||
msgid: 1,
|
||||
base: "dc=example,dc=com".to_string(),
|
||||
scope: LdapSearchScope::Subtree,
|
||||
filter: LdapFilter::Equality("name".to_string(), "testperson1".to_string()),
|
||||
filter: LdapFilter::Equality(Attribute::Name.to_string(), "testperson1".to_string()),
|
||||
attrs: vec![
|
||||
"name".to_string(),
|
||||
"mail".to_string(),
|
||||
"mail;primary".to_string(),
|
||||
"mail;alternative".to_string(),
|
||||
"emailprimary".to_string(),
|
||||
"emailalternative".to_string(),
|
||||
],
|
||||
LDAP_ATTR_NAME,
|
||||
LDAP_ATTR_MAIL,
|
||||
LDAP_ATTR_MAIL_PRIMARY,
|
||||
LDAP_ATTR_MAIL_ALTERNATIVE,
|
||||
LDAP_ATTR_EMAIL_PRIMARY,
|
||||
LDAP_ATTR_EMAIL_ALTERNATIVE,
|
||||
]
|
||||
.into_iter()
|
||||
.map(|s| s.to_string())
|
||||
.collect(),
|
||||
};
|
||||
|
||||
let sa_uuid = uuid::uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930");
|
||||
|
@ -1030,7 +1033,7 @@ mod tests {
|
|||
let me = ModifyEvent::new_internal_invalid(
|
||||
filter!(f_eq(
|
||||
Attribute::Name,
|
||||
PartialValue::new_iname("idm_people_read_priv")
|
||||
PartialValue::new_iname(IDM_PEOPLE_READ_PRIV_V1.name)
|
||||
)),
|
||||
ModifyList::new_list(vec![Modify::Present(
|
||||
Attribute::Member.into(),
|
||||
|
@ -1109,10 +1112,16 @@ mod tests {
|
|||
Attribute::Mail.as_ref(),
|
||||
"testperson1.alternative@example.com"
|
||||
),
|
||||
("mail;primary", "testperson1@example.com"),
|
||||
("mail;alternative", "testperson1.alternative@example.com"),
|
||||
("emailprimary", "testperson1@example.com"),
|
||||
("emailalternative", "testperson1.alternative@example.com")
|
||||
(LDAP_ATTR_MAIL_PRIMARY, "testperson1@example.com"),
|
||||
(
|
||||
LDAP_ATTR_MAIL_ALTERNATIVE,
|
||||
"testperson1.alternative@example.com"
|
||||
),
|
||||
(LDAP_ATTR_MAIL_PRIMARY, "testperson1@example.com"),
|
||||
(
|
||||
LDAP_ATTR_MAIL_ALTERNATIVE,
|
||||
"testperson1.alternative@example.com"
|
||||
)
|
||||
);
|
||||
}
|
||||
_ => assert!(false),
|
||||
|
@ -1156,13 +1165,13 @@ mod tests {
|
|||
msgid: 1,
|
||||
base: "dc=example,dc=com".to_string(),
|
||||
scope: LdapSearchScope::Subtree,
|
||||
filter: LdapFilter::Equality("name".to_string(), "testperson1".to_string()),
|
||||
filter: LdapFilter::Equality(Attribute::Name.to_string(), "testperson1".to_string()),
|
||||
attrs: vec![
|
||||
"*".to_string(),
|
||||
// Already being returned
|
||||
"name".to_string(),
|
||||
LDAP_ATTR_NAME.to_string(),
|
||||
// This is a virtual attribute
|
||||
"entryuuid".to_string(),
|
||||
Attribute::EntryUuid.to_string(),
|
||||
],
|
||||
};
|
||||
let r1 = ldaps.do_search(idms, &sr, &anon_t).await.unwrap();
|
||||
|
@ -1177,7 +1186,10 @@ mod tests {
|
|||
(Attribute::Name, "testperson1"),
|
||||
(Attribute::DisplayName, "testperson1"),
|
||||
(Attribute::Uuid, "cc8e95b4-c24f-4d68-ba54-8bed76f63930"),
|
||||
("entryuuid", "cc8e95b4-c24f-4d68-ba54-8bed76f63930")
|
||||
(
|
||||
Attribute::EntryUuid.as_ref(),
|
||||
"cc8e95b4-c24f-4d68-ba54-8bed76f63930"
|
||||
)
|
||||
);
|
||||
}
|
||||
_ => assert!(false),
|
||||
|
@ -1195,7 +1207,7 @@ mod tests {
|
|||
msgid: 1,
|
||||
base: "".to_string(),
|
||||
scope: LdapSearchScope::Base,
|
||||
filter: LdapFilter::Present("objectclass".to_string()),
|
||||
filter: LdapFilter::Present(Attribute::ObjectClass.to_string()),
|
||||
attrs: vec!["*".to_string()],
|
||||
};
|
||||
let r1 = ldaps.do_search(idms, &sr, &anon_t).await.unwrap();
|
||||
|
@ -1227,7 +1239,7 @@ mod tests {
|
|||
let me_posix = ModifyEvent::new_internal_invalid(
|
||||
filter!(f_eq(Attribute::Uuid, PartialValue::Uuid(UUID_DOMAIN_INFO))),
|
||||
ModifyList::new_purge_and_set(
|
||||
"domain_ldap_basedn",
|
||||
Attribute::DomainLdapBasedn,
|
||||
Value::new_iutf8("o=kanidmproject"),
|
||||
),
|
||||
);
|
||||
|
@ -1245,7 +1257,7 @@ mod tests {
|
|||
msgid: 1,
|
||||
base: "".to_string(),
|
||||
scope: LdapSearchScope::Base,
|
||||
filter: LdapFilter::Present("objectclass".to_string()),
|
||||
filter: LdapFilter::Present(Attribute::ObjectClass.to_string()),
|
||||
attrs: vec!["*".to_string()],
|
||||
};
|
||||
let r1 = ldaps.do_search(idms, &sr, &anon_t).await.unwrap();
|
||||
|
|
|
@ -317,20 +317,20 @@ impl<'a> Oauth2ResourceServersWriteTransaction<'a> {
|
|||
let uuid = ent.get_uuid();
|
||||
admin_info!(?uuid, "Checking oauth2 configuration");
|
||||
// From each entry, attempt to make an oauth2 configuration.
|
||||
if !ent.attribute_equality(Attribute::Class.as_ref(), &EntryClass::OAuth2ResourceServer.into()) {
|
||||
if !ent.attribute_equality(Attribute::Class, &EntryClass::OAuth2ResourceServer.into()) {
|
||||
admin_error!("Missing class oauth2_resource_server");
|
||||
// Check we have oauth2_resource_server class
|
||||
return Err(OperationError::InvalidEntryState);
|
||||
}
|
||||
|
||||
let type_ = if ent.attribute_equality(Attribute::Class.as_ref(), &EntryClass::OAuth2ResourceServerBasic.into()) {
|
||||
let type_ = if ent.attribute_equality(Attribute::Class, &EntryClass::OAuth2ResourceServerBasic.into()) {
|
||||
let authz_secret = ent
|
||||
.get_ava_single_secret(ATTR_OAUTH2_RS_BASIC_SECRET)
|
||||
.get_ava_single_secret(Attribute::OAuth2RsBasicSecret)
|
||||
.map(str::to_string)
|
||||
.ok_or(OperationError::InvalidValueState)?;
|
||||
|
||||
let enable_pkce = ent
|
||||
.get_ava_single_bool(Attribute::OAuth2AllowInsecureClientDisablePkce.as_ref())
|
||||
.get_ava_single_bool(Attribute::OAuth2AllowInsecureClientDisablePkce)
|
||||
.map(|e| !e)
|
||||
.unwrap_or(true);
|
||||
|
||||
|
@ -338,7 +338,7 @@ impl<'a> Oauth2ResourceServersWriteTransaction<'a> {
|
|||
authz_secret,
|
||||
enable_pkce,
|
||||
}
|
||||
} else if ent.attribute_equality(Attribute::Class.as_ref(), &EntryClass::OAuth2ResourceServerPublic.into()) {
|
||||
} else if ent.attribute_equality(Attribute::Class, &EntryClass::OAuth2ResourceServerPublic.into()) {
|
||||
OauthRSType::Public
|
||||
} else {
|
||||
error!("Missing class determining oauth2 rs type");
|
||||
|
@ -347,22 +347,22 @@ impl<'a> Oauth2ResourceServersWriteTransaction<'a> {
|
|||
|
||||
// Now we know we can load the shared attrs.
|
||||
let name = ent
|
||||
.get_ava_single_iname("oauth2_rs_name")
|
||||
.get_ava_single_iname(Attribute::OAuth2RsName)
|
||||
.map(str::to_string)
|
||||
.ok_or(OperationError::InvalidValueState)?;
|
||||
|
||||
let displayname = ent
|
||||
.get_ava_single_utf8("displayname")
|
||||
.get_ava_single_utf8(Attribute::DisplayName)
|
||||
.map(str::to_string)
|
||||
.ok_or(OperationError::InvalidValueState)?;
|
||||
|
||||
let (origin, origin_https) = ent
|
||||
.get_ava_single_url(Attribute::OAuth2RsOrigin.as_ref())
|
||||
.get_ava_single_url(Attribute::OAuth2RsOrigin)
|
||||
.map(|url| (url.origin(), url.scheme() == "https"))
|
||||
.ok_or(OperationError::InvalidValueState)?;
|
||||
|
||||
let landing_valid = ent
|
||||
.get_ava_single_url("oauth2_rs_origin_landing")
|
||||
.get_ava_single_url(Attribute::OAuth2RsOriginLanding)
|
||||
.map(|url| url.origin() == origin).
|
||||
unwrap_or(true);
|
||||
|
||||
|
@ -371,27 +371,27 @@ impl<'a> Oauth2ResourceServersWriteTransaction<'a> {
|
|||
}
|
||||
|
||||
let token_fernet = ent
|
||||
.get_ava_single_secret("oauth2_rs_token_key")
|
||||
.get_ava_single_secret(Attribute::OAuth2RsTokenKey)
|
||||
.ok_or(OperationError::InvalidValueState)
|
||||
.and_then(|key| {
|
||||
Fernet::new(key).ok_or(OperationError::CryptographyError)
|
||||
})?;
|
||||
|
||||
let scope_maps = ent
|
||||
.get_ava_as_oauthscopemaps(Attribute::OAuth2RsScopeMap.as_ref())
|
||||
.get_ava_as_oauthscopemaps(Attribute::OAuth2RsScopeMap)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
let sup_scope_maps = ent
|
||||
.get_ava_as_oauthscopemaps(Attribute::OAuth2RsSupScopeMap.as_ref())
|
||||
.get_ava_as_oauthscopemaps(Attribute::OAuth2RsSupScopeMap)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
trace!("{}", Attribute::OAuth2JwtLegacyCryptoEnable.as_ref());
|
||||
let jws_signer = if ent.get_ava_single_bool(Attribute::OAuth2JwtLegacyCryptoEnable.as_ref()).unwrap_or(false) {
|
||||
trace!("rs256_private_key_der");
|
||||
let jws_signer = if ent.get_ava_single_bool(Attribute::OAuth2JwtLegacyCryptoEnable).unwrap_or(false) {
|
||||
trace!("{}", Attribute::Rs256PrivateKeyDer);
|
||||
ent
|
||||
.get_ava_single_private_binary("rs256_private_key_der")
|
||||
.get_ava_single_private_binary(Attribute::Rs256PrivateKeyDer)
|
||||
.ok_or(OperationError::InvalidValueState)
|
||||
.and_then(|key_der| {
|
||||
JwsSigner::from_rs256_der(key_der).map_err(|e| {
|
||||
|
@ -400,9 +400,9 @@ impl<'a> Oauth2ResourceServersWriteTransaction<'a> {
|
|||
})
|
||||
})?
|
||||
} else {
|
||||
trace!("es256_private_key_der");
|
||||
trace!("{}", Attribute::Es256PrivateKeyDer);
|
||||
ent
|
||||
.get_ava_single_private_binary("es256_private_key_der")
|
||||
.get_ava_single_private_binary(Attribute::Es256PrivateKeyDer)
|
||||
.ok_or(OperationError::InvalidValueState)
|
||||
.and_then(|key_der| {
|
||||
JwsSigner::from_es256_der(key_der).map_err(|e| {
|
||||
|
@ -413,7 +413,7 @@ impl<'a> Oauth2ResourceServersWriteTransaction<'a> {
|
|||
};
|
||||
|
||||
let prefer_short_username = ent
|
||||
.get_ava_single_bool(Attribute::OAuth2PreferShortUsername.as_ref())
|
||||
.get_ava_single_bool(Attribute::OAuth2PreferShortUsername)
|
||||
.unwrap_or(false);
|
||||
|
||||
let mut authorization_endpoint = self.inner.origin.clone();
|
||||
|
@ -914,7 +914,7 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
|
||||
// Check the not issued before of the session relative to this refresh iat
|
||||
let oauth2_session = entry
|
||||
.get_ava_as_oauth2session_map("oauth2_session")
|
||||
.get_ava_as_oauth2session_map(Attribute::OAuth2Session)
|
||||
.and_then(|map| map.get(&session_id))
|
||||
.ok_or_else(|| {
|
||||
security_info!(
|
||||
|
@ -1147,7 +1147,7 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
// NOTE: Oauth2_session has special handling that allows update in place without
|
||||
// the remove step needing to be carried out.
|
||||
// Modify::Removed("oauth2_session".into(), PartialValue::Refer(session_id)),
|
||||
Modify::Present("oauth2_session".into(), session),
|
||||
Modify::Present(Attribute::OAuth2Session.into(), session),
|
||||
]);
|
||||
|
||||
self.qs_write
|
||||
|
@ -2119,7 +2119,7 @@ mod tests {
|
|||
.internal_search_uuid(uuid)
|
||||
.expect("Failed to retrieve oauth2 resource entry ");
|
||||
let secret = entry
|
||||
.get_ava_single_secret("oauth2_rs_basic_secret")
|
||||
.get_ava_single_secret(Attribute::OAuth2RsBasicSecret)
|
||||
.map(str::to_string)
|
||||
.expect("No oauth2_rs_basic_secret found");
|
||||
|
||||
|
@ -2164,9 +2164,9 @@ mod tests {
|
|||
|
||||
// Mod the user
|
||||
let modlist = ModifyList::new_list(vec![
|
||||
Modify::Present("user_auth_token_session".into(), session),
|
||||
Modify::Present(Attribute::UserAuthTokenSession.into(), session),
|
||||
Modify::Present(
|
||||
"primary_credential".into(),
|
||||
Attribute::PrimaryCredential.into(),
|
||||
Value::Cred("primary".to_string(), cred),
|
||||
),
|
||||
]);
|
||||
|
@ -2286,9 +2286,9 @@ mod tests {
|
|||
|
||||
// Mod the user
|
||||
let modlist = ModifyList::new_list(vec![
|
||||
Modify::Present("user_auth_token_session".into(), session),
|
||||
Modify::Present(Attribute::UserAuthTokenSession.into(), session),
|
||||
Modify::Present(
|
||||
"primary_credential".into(),
|
||||
Attribute::PrimaryCredential.into(),
|
||||
Value::Cred("primary".to_string(), cred),
|
||||
),
|
||||
]);
|
||||
|
@ -3228,7 +3228,7 @@ mod tests {
|
|||
.internal_search_uuid(UUID_ADMIN)
|
||||
.expect("failed");
|
||||
let valid = entry
|
||||
.get_ava_as_oauth2session_map("oauth2_session")
|
||||
.get_ava_as_oauth2session_map(Attribute::OAuth2Session)
|
||||
.map(|map| map.get(&session_id).is_some())
|
||||
.unwrap_or(false);
|
||||
assert!(valid);
|
||||
|
@ -3250,7 +3250,7 @@ mod tests {
|
|||
.internal_search_uuid(UUID_ADMIN)
|
||||
.expect("failed");
|
||||
let revoked = entry
|
||||
.get_ava_as_oauth2session_map("oauth2_session")
|
||||
.get_ava_as_oauth2session_map(Attribute::OAuth2Session)
|
||||
.and_then(|sessions| sessions.get(&session_id))
|
||||
.map(|session| matches!(session.state, SessionState::RevokedAt(_)))
|
||||
.unwrap_or(false);
|
||||
|
@ -4805,7 +4805,7 @@ mod tests {
|
|||
.internal_search_uuid(UUID_ADMIN)
|
||||
.expect("failed");
|
||||
let valid = entry
|
||||
.get_ava_as_oauth2session_map("oauth2_session")
|
||||
.get_ava_as_oauth2session_map(Attribute::OAuth2Session)
|
||||
.and_then(|sessions| sessions.first_key_value())
|
||||
.map(|(_, session)| !matches!(session.state, SessionState::RevokedAt(_)))
|
||||
// If there is no map, then something is wrong.
|
||||
|
|
|
@ -24,40 +24,49 @@ impl RadiusAccount {
|
|||
value: &Entry<EntryReduced, EntryCommitted>,
|
||||
qs: &mut QueryServerReadTransaction,
|
||||
) -> Result<Self, OperationError> {
|
||||
if !value.attribute_equality(Attribute::Class.as_ref(), &EntryClass::Account.into()) {
|
||||
if !value.attribute_equality(Attribute::Class, &EntryClass::Account.into()) {
|
||||
return Err(OperationError::InvalidAccountState(
|
||||
"Missing class: account".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let radius_secret = value
|
||||
.get_ava_single_secret("radius_secret")
|
||||
.get_ava_single_secret(Attribute::RadiusSecret)
|
||||
.ok_or_else(|| {
|
||||
OperationError::InvalidAccountState("Missing attribute: radius_secret".to_string())
|
||||
OperationError::InvalidAccountState(format!(
|
||||
"Missing attribute: {}",
|
||||
Attribute::RadiusSecret
|
||||
))
|
||||
})?
|
||||
.to_string();
|
||||
|
||||
let name = value
|
||||
.get_ava_single_iname(Attribute::Name.as_ref())
|
||||
.get_ava_single_iname(Attribute::Name)
|
||||
.map(|s| s.to_string())
|
||||
.ok_or_else(|| {
|
||||
OperationError::InvalidAccountState("Missing attribute: name".to_string())
|
||||
OperationError::InvalidAccountState(format!(
|
||||
"Missing attribute: {}",
|
||||
Attribute::Name
|
||||
))
|
||||
})?;
|
||||
|
||||
let uuid = value.get_uuid();
|
||||
|
||||
let displayname = value
|
||||
.get_ava_single_utf8("displayname")
|
||||
.get_ava_single_utf8(Attribute::DisplayName)
|
||||
.map(|s| s.to_string())
|
||||
.ok_or_else(|| {
|
||||
OperationError::InvalidAccountState("Missing attribute: displayname".to_string())
|
||||
OperationError::InvalidAccountState(format!(
|
||||
"Missing attribute: {}",
|
||||
Attribute::DisplayName
|
||||
))
|
||||
})?;
|
||||
|
||||
let groups = Group::try_from_account_entry_red_ro(value, qs)?;
|
||||
|
||||
let valid_from = value.get_ava_single_datetime("account_valid_from");
|
||||
let valid_from = value.get_ava_single_datetime(Attribute::AccountValidFrom);
|
||||
|
||||
let expire = value.get_ava_single_datetime("account_expire");
|
||||
let expire = value.get_ava_single_datetime(Attribute::AccountExpire);
|
||||
|
||||
Ok(RadiusAccount {
|
||||
name,
|
||||
|
|
|
@ -44,7 +44,7 @@ impl<'a> IdmServerAuthTransaction<'a> {
|
|||
|
||||
// Check that the entry/session can be re-authed.
|
||||
let session = entry
|
||||
.get_ava_as_session_map("user_auth_token_session")
|
||||
.get_ava_as_session_map(Attribute::UserAuthTokenSession)
|
||||
.and_then(|sessions| sessions.get(&ident.session_id))
|
||||
.ok_or_else(|| {
|
||||
error!("Ident session is not present in entry. Perhaps replication is delayed?");
|
||||
|
|
|
@ -28,28 +28,29 @@ pub(crate) struct SyncAccount {
|
|||
macro_rules! try_from_entry {
|
||||
($value:expr) => {{
|
||||
// Check the classes
|
||||
if !$value.attribute_equality(Attribute::Class.into(), &EntryClass::SyncAccount.into()) {
|
||||
if !$value.attribute_equality(Attribute::Class, &EntryClass::SyncAccount.into()) {
|
||||
return Err(OperationError::InvalidAccountState(
|
||||
"Missing class: sync account".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let name = $value
|
||||
.get_ava_single_iname(Attribute::Name.as_ref())
|
||||
.get_ava_single_iname(Attribute::Name)
|
||||
.map(|s| s.to_string())
|
||||
.ok_or(OperationError::InvalidAccountState(
|
||||
"Missing attribute: name".to_string(),
|
||||
))?;
|
||||
|
||||
let jws_key = $value
|
||||
.get_ava_single_jws_key_es256("jws_es256_private_key")
|
||||
.get_ava_single_jws_key_es256(Attribute::JwsEs256PrivateKey)
|
||||
.cloned()
|
||||
.ok_or(OperationError::InvalidAccountState(
|
||||
"Missing attribute: jws_es256_private_key".to_string(),
|
||||
))?;
|
||||
.ok_or(OperationError::InvalidAccountState(format!(
|
||||
"Missing attribute: {}",
|
||||
Attribute::JwsEs256PrivateKey
|
||||
)))?;
|
||||
|
||||
let sync_tokens = $value
|
||||
.get_ava_as_apitoken_map("sync_token_session")
|
||||
.get_ava_as_apitoken_map(Attribute::SyncTokenSession)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
|
@ -83,7 +84,7 @@ impl SyncAccount {
|
|||
|
||||
// Get the sessions. There are no gracewindows on sync, we are much stricter.
|
||||
let session_present = entry
|
||||
.get_ava_as_apitoken_map("sync_token_session")
|
||||
.get_ava_as_apitoken_map(Attribute::SyncTokenSession)
|
||||
.map(|session_map| session_map.get(&sst.token_id).is_some())
|
||||
.unwrap_or(false);
|
||||
|
||||
|
@ -310,13 +311,19 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
if existing_entries {
|
||||
// Now modify these to remove their sync related attributes.
|
||||
let schema = self.qs_write.get_schema();
|
||||
let sync_class = schema.get_classes().get("sync_object").ok_or_else(|| {
|
||||
error!("Failed to access sync_object class, schema corrupt");
|
||||
OperationError::InvalidState
|
||||
})?;
|
||||
let sync_class = schema
|
||||
.get_classes()
|
||||
.get(EntryClass::SyncObject.into())
|
||||
.ok_or_else(|| {
|
||||
error!(
|
||||
"Failed to access {} class, schema corrupt",
|
||||
EntryClass::SyncObject
|
||||
);
|
||||
OperationError::InvalidState
|
||||
})?;
|
||||
|
||||
let modlist = std::iter::once(Modify::Removed(
|
||||
"class".into(),
|
||||
Attribute::Class.into(),
|
||||
EntryClass::SyncObject.into(),
|
||||
))
|
||||
.chain(
|
||||
|
@ -433,13 +440,19 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
|
||||
// Now modify these to remove their sync related attributes.
|
||||
let schema = self.qs_write.get_schema();
|
||||
let sync_class = schema.get_classes().get("sync_object").ok_or_else(|| {
|
||||
error!("Failed to access sync_object class, schema corrupt");
|
||||
OperationError::InvalidState
|
||||
})?;
|
||||
let sync_class = schema
|
||||
.get_classes()
|
||||
.get(EntryClass::SyncObject.into())
|
||||
.ok_or_else(|| {
|
||||
error!(
|
||||
"Failed to access {} class, schema corrupt",
|
||||
EntryClass::SyncObject
|
||||
);
|
||||
OperationError::InvalidState
|
||||
})?;
|
||||
|
||||
let modlist = std::iter::once(Modify::Removed(
|
||||
"class".into(),
|
||||
Attribute::Class.into(),
|
||||
EntryClass::SyncObject.into(),
|
||||
))
|
||||
.chain(
|
||||
|
@ -552,7 +565,7 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
|
||||
match (
|
||||
&changes.from_state,
|
||||
sync_entry.get_ava_single_private_binary("sync_cookie"),
|
||||
sync_entry.get_ava_single_private_binary(Attribute::SyncCookie),
|
||||
) {
|
||||
(ScimSyncState::Refresh, _) => {
|
||||
// valid
|
||||
|
@ -581,7 +594,7 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
|
||||
// Get the sync authority set from the entry.
|
||||
let sync_authority_set = sync_entry
|
||||
.get_ava_as_iutf8("sync_yield_authority")
|
||||
.get_ava_as_iutf8(Attribute::SyncYieldAuthority)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
|
@ -696,11 +709,14 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
*u,
|
||||
ModifyList::new_list(vec![
|
||||
Modify::Assert(
|
||||
"sync_parent_uuid".into(),
|
||||
Attribute::SyncParentUuid,
|
||||
PartialValue::Refer(sync_uuid),
|
||||
),
|
||||
Modify::Purged("sync_external_id".into()),
|
||||
Modify::Present("sync_external_id".into(), Value::new_iutf8(ext_id)),
|
||||
Modify::Purged(Attribute::SyncExternalId.into()),
|
||||
Modify::Present(
|
||||
Attribute::SyncExternalId.into(),
|
||||
Value::new_iutf8(ext_id),
|
||||
),
|
||||
]),
|
||||
)
|
||||
})
|
||||
|
@ -1133,17 +1149,17 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
let mut mods = Vec::new();
|
||||
|
||||
mods.push(Modify::Assert(
|
||||
"sync_parent_uuid".into(),
|
||||
Attribute::SyncParentUuid,
|
||||
PartialValue::Refer(sync_uuid),
|
||||
));
|
||||
|
||||
for req_class in requested_classes.keys() {
|
||||
mods.push(Modify::Present(
|
||||
"sync_class".into(),
|
||||
Attribute::SyncClass.into(),
|
||||
Value::new_iutf8(req_class),
|
||||
));
|
||||
mods.push(Modify::Present(
|
||||
EntryClass::Class.into(),
|
||||
Attribute::Class.into(),
|
||||
Value::new_iutf8(req_class),
|
||||
));
|
||||
}
|
||||
|
@ -1376,7 +1392,9 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
if ent.mask_recycled_ts().is_none() {
|
||||
debug!("Skipping already deleted entry {}", ent.get_uuid());
|
||||
None
|
||||
} else if ent.get_ava_single_refer("sync_parent_uuid") != Some(sync_uuid) {
|
||||
} else if ent.get_ava_single_refer(Attribute::SyncParentUuid)
|
||||
!= Some(sync_uuid)
|
||||
{
|
||||
warn!(
|
||||
"Skipping entry that is not within sync control {}",
|
||||
ent.get_uuid()
|
||||
|
@ -1429,10 +1447,11 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
// to reflect the new sync state.
|
||||
|
||||
let modlist = match to_state {
|
||||
ScimSyncState::Active { cookie } => {
|
||||
ModifyList::new_purge_and_set("sync_cookie", Value::PrivateBinary(cookie.0.clone()))
|
||||
}
|
||||
ScimSyncState::Refresh => ModifyList::new_purge("sync_cookie"),
|
||||
ScimSyncState::Active { cookie } => ModifyList::new_purge_and_set(
|
||||
Attribute::SyncCookie,
|
||||
Value::PrivateBinary(cookie.0.clone()),
|
||||
),
|
||||
ScimSyncState::Refresh => ModifyList::new_purge(Attribute::SyncCookie),
|
||||
};
|
||||
|
||||
self.qs_write
|
||||
|
@ -1478,7 +1497,7 @@ impl<'a> IdmServerProxyReadTransaction<'a> {
|
|||
let sync_entry = self.qs_read.internal_search_uuid(sync_uuid)?;
|
||||
|
||||
Ok(
|
||||
match sync_entry.get_ava_single_private_binary("sync_cookie") {
|
||||
match sync_entry.get_ava_single_private_binary(Attribute::SyncCookie) {
|
||||
Some(b) => ScimSyncState::Active {
|
||||
cookie: Base64UrlSafeData(b.to_vec()),
|
||||
},
|
||||
|
@ -1646,9 +1665,7 @@ mod tests {
|
|||
Attribute::Name,
|
||||
PartialValue::new_iname("test_scim_sync")
|
||||
)),
|
||||
ModifyList::new_list(vec![Modify::Purged(AttrString::from(
|
||||
"jws_es256_private_key",
|
||||
))]),
|
||||
ModifyList::new_list(vec![Modify::Purged(Attribute::JwsEs256PrivateKey.into())]),
|
||||
);
|
||||
assert!(idms_prox_write.qs_write.modify(&me_inv_m).is_ok());
|
||||
assert!(idms_prox_write.commit().is_ok());
|
||||
|
@ -1666,12 +1683,12 @@ mod tests {
|
|||
.expect("Unable to access sync entry");
|
||||
|
||||
let jws_key = sync_entry
|
||||
.get_ava_single_jws_key_es256("jws_es256_private_key")
|
||||
.get_ava_single_jws_key_es256(Attribute::JwsEs256PrivateKey)
|
||||
.cloned()
|
||||
.expect("Missing attribute: jws_es256_private_key");
|
||||
|
||||
let sync_tokens = sync_entry
|
||||
.get_ava_as_apitoken_map("sync_token_session")
|
||||
.get_ava_as_apitoken_map(Attribute::SyncTokenSession)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
|
@ -1804,7 +1821,7 @@ mod tests {
|
|||
.expect("Failed to access sync stub entry");
|
||||
|
||||
assert!(
|
||||
synced_entry.get_ava_single_iutf8("sync_external_id")
|
||||
synced_entry.get_ava_single_iutf8(Attribute::SyncExternalId)
|
||||
== Some("dn=william,ou=people,dc=test")
|
||||
);
|
||||
assert!(synced_entry.get_uuid() == user_sync_uuid);
|
||||
|
@ -1933,9 +1950,10 @@ mod tests {
|
|||
.internal_search_uuid(user_sync_uuid)
|
||||
.expect("Unable to access entry");
|
||||
|
||||
assert!(ent.get_ava_single_iname(Attribute::Name.as_ref()) == Some("testgroup"));
|
||||
assert!(ent.get_ava_single_iname(Attribute::Name) == Some("testgroup"));
|
||||
assert!(
|
||||
ent.get_ava_single_iutf8("sync_external_id") == Some("cn=testgroup,ou=people,dc=test")
|
||||
ent.get_ava_single_iutf8(Attribute::SyncExternalId)
|
||||
== Some("cn=testgroup,ou=people,dc=test")
|
||||
);
|
||||
|
||||
assert!(idms_prox_write.commit().is_ok());
|
||||
|
@ -2028,7 +2046,7 @@ mod tests {
|
|||
ScimAttr::SingleSimple(ScimSimpleAttr::String("testgroup".to_string()))
|
||||
),
|
||||
(
|
||||
"class".to_string(),
|
||||
Attribute::Class.to_string(),
|
||||
ScimAttr::SingleSimple(ScimSimpleAttr::String("posixgroup".to_string()))
|
||||
)
|
||||
),
|
||||
|
@ -2491,7 +2509,7 @@ mod tests {
|
|||
.internal_search_uuid(sync_uuid_a)
|
||||
.expect("Unable to access entry");
|
||||
|
||||
assert!(ent.get_ava_single_iname(Attribute::Name.as_ref()) == Some("testgroup"));
|
||||
assert!(ent.get_ava_single_iname(Attribute::Name) == Some("testgroup"));
|
||||
|
||||
assert!(idms_prox_write.commit().is_ok());
|
||||
}
|
||||
|
@ -2588,48 +2606,58 @@ mod tests {
|
|||
|
||||
let testgroup = get_single_entry("testgroup", &mut idms_prox_write);
|
||||
assert!(
|
||||
testgroup.get_ava_single_iutf8("sync_external_id")
|
||||
testgroup.get_ava_single_iutf8(Attribute::SyncExternalId)
|
||||
== Some("cn=testgroup,cn=groups,cn=accounts,dc=dev,dc=blackhats,dc=net,dc=au")
|
||||
);
|
||||
assert!(testgroup.get_ava_single_uint32("gidnumber").is_none());
|
||||
assert!(testgroup
|
||||
.get_ava_single_uint32(Attribute::GidNumber)
|
||||
.is_none());
|
||||
|
||||
let testposix = get_single_entry("testposix", &mut idms_prox_write);
|
||||
assert!(
|
||||
testposix.get_ava_single_iutf8("sync_external_id")
|
||||
testposix.get_ava_single_iutf8(Attribute::SyncExternalId)
|
||||
== Some("cn=testposix,cn=groups,cn=accounts,dc=dev,dc=blackhats,dc=net,dc=au")
|
||||
);
|
||||
assert!(testposix.get_ava_single_uint32("gidnumber") == Some(1234567));
|
||||
assert!(testposix.get_ava_single_uint32(Attribute::GidNumber) == Some(1234567));
|
||||
|
||||
let testexternal = get_single_entry("testexternal", &mut idms_prox_write);
|
||||
assert!(
|
||||
testexternal.get_ava_single_iutf8("sync_external_id")
|
||||
testexternal.get_ava_single_iutf8(Attribute::SyncExternalId)
|
||||
== Some("cn=testexternal,cn=groups,cn=accounts,dc=dev,dc=blackhats,dc=net,dc=au")
|
||||
);
|
||||
assert!(testexternal.get_ava_single_uint32("gidnumber").is_none());
|
||||
assert!(testexternal
|
||||
.get_ava_single_uint32(Attribute::GidNumber)
|
||||
.is_none());
|
||||
|
||||
let testuser = get_single_entry("testuser", &mut idms_prox_write);
|
||||
assert!(
|
||||
testuser.get_ava_single_iutf8("sync_external_id")
|
||||
testuser.get_ava_single_iutf8(Attribute::SyncExternalId)
|
||||
== Some("uid=testuser,cn=users,cn=accounts,dc=dev,dc=blackhats,dc=net,dc=au")
|
||||
);
|
||||
assert!(testuser.get_ava_single_uint32("gidnumber") == Some(12345));
|
||||
assert!(testuser.get_ava_single_utf8("displayname") == Some("Test User"));
|
||||
assert!(testuser.get_ava_single_iutf8("loginshell") == Some("/bin/sh"));
|
||||
assert!(testuser.get_ava_single_uint32(Attribute::GidNumber) == Some(12345));
|
||||
assert!(testuser.get_ava_single_utf8(Attribute::DisplayName) == Some("Test User"));
|
||||
assert!(testuser.get_ava_single_iutf8(Attribute::LoginShell) == Some("/bin/sh"));
|
||||
|
||||
let mut ssh_keyiter = testuser
|
||||
.get_ava_iter_sshpubkeys(Attribute::SshPublicKey.into())
|
||||
.get_ava_iter_sshpubkeys(Attribute::SshPublicKey)
|
||||
.expect("Failed to access ssh pubkeys");
|
||||
assert_eq!(ssh_keyiter.next(), Some("sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBENubZikrb8hu+HeVRdZ0pp/VAk2qv4JDbuJhvD0yNdWDL2e3cBbERiDeNPkWx58Q4rVnxkbV1fa8E2waRtT91wAAAAEc3NoOg== testuser@fidokey"));
|
||||
assert_eq!(ssh_keyiter.next(), None);
|
||||
|
||||
// Check memberof works.
|
||||
let testgroup_mb = testgroup.get_ava_refer("member").expect("No members!");
|
||||
let testgroup_mb = testgroup
|
||||
.get_ava_refer(Attribute::Member)
|
||||
.expect("No members!");
|
||||
assert!(testgroup_mb.contains(&testuser.get_uuid()));
|
||||
|
||||
let testposix_mb = testposix.get_ava_refer("member").expect("No members!");
|
||||
let testposix_mb = testposix
|
||||
.get_ava_refer(Attribute::Member)
|
||||
.expect("No members!");
|
||||
assert!(testposix_mb.contains(&testuser.get_uuid()));
|
||||
|
||||
let testuser_mo = testuser.get_ava_refer("memberof").expect("No memberof!");
|
||||
let testuser_mo = testuser
|
||||
.get_ava_refer(Attribute::MemberOf)
|
||||
.expect("No memberof!");
|
||||
assert!(testuser_mo.contains(&testposix.get_uuid()));
|
||||
assert!(testuser_mo.contains(&testgroup.get_uuid()));
|
||||
|
||||
|
@ -2659,28 +2687,34 @@ mod tests {
|
|||
let testposix = get_single_entry("testposix", &mut idms_prox_write);
|
||||
info!("{:?}", testposix);
|
||||
assert!(
|
||||
testposix.get_ava_single_iutf8("sync_external_id")
|
||||
testposix.get_ava_single_iutf8(Attribute::SyncExternalId)
|
||||
== Some("cn=testposix,cn=groups,cn=accounts,dc=dev,dc=blackhats,dc=net,dc=au")
|
||||
);
|
||||
assert!(testposix.get_ava_single_uint32("gidnumber") == Some(1234567));
|
||||
assert!(testposix.get_ava_single_uint32(Attribute::GidNumber) == Some(1234567));
|
||||
|
||||
let testexternal = get_single_entry("testexternal2", &mut idms_prox_write);
|
||||
info!("{:?}", testexternal);
|
||||
assert!(
|
||||
testexternal.get_ava_single_iutf8("sync_external_id")
|
||||
testexternal.get_ava_single_iutf8(Attribute::SyncExternalId)
|
||||
== Some("cn=testexternal2,cn=groups,cn=accounts,dc=dev,dc=blackhats,dc=net,dc=au")
|
||||
);
|
||||
assert!(testexternal.get_ava_single_uint32("gidnumber").is_none());
|
||||
assert!(testexternal
|
||||
.get_ava_single_uint32(Attribute::GidNumber)
|
||||
.is_none());
|
||||
|
||||
let testuser = get_single_entry("testuser", &mut idms_prox_write);
|
||||
|
||||
// Check memberof works.
|
||||
let testexternal_mb = testexternal.get_ava_refer("member").expect("No members!");
|
||||
let testexternal_mb = testexternal
|
||||
.get_ava_refer(Attribute::Member)
|
||||
.expect("No members!");
|
||||
assert!(testexternal_mb.contains(&testuser.get_uuid()));
|
||||
|
||||
assert!(testposix.get_ava_refer("member").is_none());
|
||||
assert!(testposix.get_ava_refer(Attribute::Member).is_none());
|
||||
|
||||
let testuser_mo = testuser.get_ava_refer("memberof").expect("No memberof!");
|
||||
let testuser_mo = testuser
|
||||
.get_ava_refer(Attribute::MemberOf)
|
||||
.expect("No memberof!");
|
||||
assert!(testuser_mo.contains(&testexternal.get_uuid()));
|
||||
|
||||
assert!(idms_prox_write.commit().is_ok());
|
||||
|
@ -2712,42 +2746,52 @@ mod tests {
|
|||
// Check entries still remain as expected.
|
||||
let testgroup = get_single_entry("testgroup", &mut idms_prox_write);
|
||||
assert!(
|
||||
testgroup.get_ava_single_iutf8("sync_external_id")
|
||||
testgroup.get_ava_single_iutf8(Attribute::SyncExternalId)
|
||||
== Some("cn=testgroup,cn=groups,cn=accounts,dc=dev,dc=blackhats,dc=net,dc=au")
|
||||
);
|
||||
assert!(testgroup.get_ava_single_uint32("gidnumber").is_none());
|
||||
assert!(testgroup
|
||||
.get_ava_single_uint32(Attribute::GidNumber)
|
||||
.is_none());
|
||||
|
||||
let testposix = get_single_entry("testposix", &mut idms_prox_write);
|
||||
assert!(
|
||||
testposix.get_ava_single_iutf8("sync_external_id")
|
||||
testposix.get_ava_single_iutf8(Attribute::SyncExternalId)
|
||||
== Some("cn=testposix,cn=groups,cn=accounts,dc=dev,dc=blackhats,dc=net,dc=au")
|
||||
);
|
||||
assert!(testposix.get_ava_single_uint32("gidnumber") == Some(1234567));
|
||||
assert!(testposix.get_ava_single_uint32(Attribute::GidNumber) == Some(1234567));
|
||||
|
||||
let testexternal = get_single_entry("testexternal", &mut idms_prox_write);
|
||||
assert!(
|
||||
testexternal.get_ava_single_iutf8("sync_external_id")
|
||||
testexternal.get_ava_single_iutf8(Attribute::SyncExternalId)
|
||||
== Some("cn=testexternal,cn=groups,cn=accounts,dc=dev,dc=blackhats,dc=net,dc=au")
|
||||
);
|
||||
assert!(testexternal.get_ava_single_uint32("gidnumber").is_none());
|
||||
assert!(testexternal
|
||||
.get_ava_single_uint32(Attribute::GidNumber)
|
||||
.is_none());
|
||||
|
||||
let testuser = get_single_entry("testuser", &mut idms_prox_write);
|
||||
assert!(
|
||||
testuser.get_ava_single_iutf8("sync_external_id")
|
||||
testuser.get_ava_single_iutf8(Attribute::SyncExternalId)
|
||||
== Some("uid=testuser,cn=users,cn=accounts,dc=dev,dc=blackhats,dc=net,dc=au")
|
||||
);
|
||||
assert!(testuser.get_ava_single_uint32("gidnumber") == Some(12345));
|
||||
assert!(testuser.get_ava_single_utf8("displayname") == Some("Test User"));
|
||||
assert!(testuser.get_ava_single_iutf8("loginshell") == Some("/bin/sh"));
|
||||
assert!(testuser.get_ava_single_uint32(Attribute::GidNumber) == Some(12345));
|
||||
assert!(testuser.get_ava_single_utf8(Attribute::DisplayName) == Some("Test User"));
|
||||
assert!(testuser.get_ava_single_iutf8(Attribute::LoginShell) == Some("/bin/sh"));
|
||||
|
||||
// Check memberof works.
|
||||
let testgroup_mb = testgroup.get_ava_refer("member").expect("No members!");
|
||||
let testgroup_mb = testgroup
|
||||
.get_ava_refer(Attribute::Member)
|
||||
.expect("No members!");
|
||||
assert!(testgroup_mb.contains(&testuser.get_uuid()));
|
||||
|
||||
let testposix_mb = testposix.get_ava_refer("member").expect("No members!");
|
||||
let testposix_mb = testposix
|
||||
.get_ava_refer(Attribute::Member)
|
||||
.expect("No members!");
|
||||
assert!(testposix_mb.contains(&testuser.get_uuid()));
|
||||
|
||||
let testuser_mo = testuser.get_ava_refer("memberof").expect("No memberof!");
|
||||
let testuser_mo = testuser
|
||||
.get_ava_refer(Attribute::MemberOf)
|
||||
.expect("No memberof!");
|
||||
assert!(testuser_mo.contains(&testposix.get_uuid()));
|
||||
assert!(testuser_mo.contains(&testgroup.get_uuid()));
|
||||
|
||||
|
@ -2778,25 +2822,31 @@ mod tests {
|
|||
|
||||
let testgroup = get_single_entry("testgroup", &mut idms_prox_write);
|
||||
assert!(
|
||||
testgroup.get_ava_single_iutf8("sync_external_id")
|
||||
testgroup.get_ava_single_iutf8(Attribute::SyncExternalId)
|
||||
== Some("cn=testgroup,cn=groups,cn=accounts,dc=dev,dc=blackhats,dc=net,dc=au")
|
||||
);
|
||||
assert!(testgroup.get_ava_single_uint32("gidnumber").is_none());
|
||||
assert!(testgroup
|
||||
.get_ava_single_uint32(Attribute::GidNumber)
|
||||
.is_none());
|
||||
|
||||
let testuser = get_single_entry("testuser", &mut idms_prox_write);
|
||||
assert!(
|
||||
testuser.get_ava_single_iutf8("sync_external_id")
|
||||
testuser.get_ava_single_iutf8(Attribute::SyncExternalId)
|
||||
== Some("uid=testuser,cn=users,cn=accounts,dc=dev,dc=blackhats,dc=net,dc=au")
|
||||
);
|
||||
assert!(testuser.get_ava_single_uint32("gidnumber") == Some(12345));
|
||||
assert!(testuser.get_ava_single_utf8("displayname") == Some("Test User"));
|
||||
assert!(testuser.get_ava_single_iutf8("loginshell") == Some("/bin/sh"));
|
||||
assert!(testuser.get_ava_single_uint32(Attribute::GidNumber) == Some(12345));
|
||||
assert!(testuser.get_ava_single_utf8(Attribute::DisplayName) == Some("Test User"));
|
||||
assert!(testuser.get_ava_single_iutf8(Attribute::LoginShell) == Some("/bin/sh"));
|
||||
|
||||
// Check memberof works.
|
||||
let testgroup_mb = testgroup.get_ava_refer("member").expect("No members!");
|
||||
let testgroup_mb = testgroup
|
||||
.get_ava_refer(Attribute::Member)
|
||||
.expect("No members!");
|
||||
assert!(testgroup_mb.contains(&testuser.get_uuid()));
|
||||
|
||||
let testuser_mo = testuser.get_ava_refer("memberof").expect("No memberof!");
|
||||
let testuser_mo = testuser
|
||||
.get_ava_refer(Attribute::MemberOf)
|
||||
.expect("No memberof!");
|
||||
assert!(testuser_mo.contains(&testgroup.get_uuid()));
|
||||
|
||||
assert!(idms_prox_write.commit().is_ok());
|
||||
|
@ -2823,8 +2873,8 @@ mod tests {
|
|||
.internal_modify_uuid(
|
||||
sync_uuid,
|
||||
&ModifyList::new_purge_and_set(
|
||||
"sync_yield_authority",
|
||||
Value::new_iutf8("legalname")
|
||||
Attribute::SyncYieldAuthority,
|
||||
Value::new_iutf8(Attribute::LegalName.as_ref())
|
||||
)
|
||||
)
|
||||
.is_ok());
|
||||
|
@ -2837,7 +2887,7 @@ mod tests {
|
|||
.internal_modify(
|
||||
&testuser_filter,
|
||||
&ModifyList::new_purge_and_set(
|
||||
"legalname",
|
||||
Attribute::LegalName,
|
||||
Value::Utf8("Test Userington the First".to_string())
|
||||
)
|
||||
)
|
||||
|
@ -2855,7 +2905,9 @@ mod tests {
|
|||
.map(|mut results| results.pop().expect("Empty result set"))
|
||||
.expect("Failed to access testuser");
|
||||
|
||||
assert!(testuser.get_ava_single_utf8("legalname") == Some("Test Userington the First"));
|
||||
assert!(
|
||||
testuser.get_ava_single_utf8(Attribute::LegalName) == Some("Test Userington the First")
|
||||
);
|
||||
|
||||
assert!(idms_prox_write.commit().is_ok());
|
||||
}
|
||||
|
@ -2894,21 +2946,16 @@ mod tests {
|
|||
|
||||
// Check that the entries still exists but now have no sync_object attached.
|
||||
let testgroup = get_single_entry("testgroup", &mut idms_prox_write);
|
||||
assert!(!testgroup
|
||||
.attribute_equality(Attribute::Class.as_ref(), &EntryClass::SyncObject.into()));
|
||||
assert!(!testgroup.attribute_equality(Attribute::Class, &EntryClass::SyncObject.into()));
|
||||
|
||||
let testposix = get_single_entry("testposix", &mut idms_prox_write);
|
||||
assert!(!testposix
|
||||
.attribute_equality(Attribute::Class.as_ref(), &EntryClass::SyncObject.into()));
|
||||
assert!(!testposix.attribute_equality(Attribute::Class, &EntryClass::SyncObject.into()));
|
||||
|
||||
let testexternal = get_single_entry("testexternal", &mut idms_prox_write);
|
||||
assert!(!testexternal
|
||||
.attribute_equality(Attribute::Class.as_ref(), &EntryClass::SyncObject.into()));
|
||||
assert!(!testexternal.attribute_equality(Attribute::Class, &EntryClass::SyncObject.into()));
|
||||
|
||||
let testuser = get_single_entry("testuser", &mut idms_prox_write);
|
||||
assert!(
|
||||
!testuser.attribute_equality(Attribute::Class.as_ref(), &EntryClass::SyncObject.into())
|
||||
);
|
||||
assert!(!testuser.attribute_equality(Attribute::Class, &EntryClass::SyncObject.into()));
|
||||
|
||||
assert!(idms_prox_write.commit().is_ok());
|
||||
}
|
||||
|
@ -2954,13 +3001,10 @@ mod tests {
|
|||
|
||||
// Check that the entries still exists but now have no sync_object attached.
|
||||
let testgroup = get_single_entry("testgroup", &mut idms_prox_write);
|
||||
assert!(!testgroup
|
||||
.attribute_equality(Attribute::Class.as_ref(), &EntryClass::SyncObject.into()));
|
||||
assert!(!testgroup.attribute_equality(Attribute::Class, &EntryClass::SyncObject.into()));
|
||||
|
||||
let testuser = get_single_entry("testuser", &mut idms_prox_write);
|
||||
assert!(
|
||||
!testuser.attribute_equality(Attribute::Class.as_ref(), &EntryClass::SyncObject.into())
|
||||
);
|
||||
assert!(!testuser.attribute_equality(Attribute::Class, &EntryClass::SyncObject.into()));
|
||||
|
||||
for iname in ["testposix", "testexternal"] {
|
||||
trace!(%iname);
|
||||
|
|
|
@ -568,7 +568,7 @@ pub trait IdmServerTransaction<'a> {
|
|||
})?;
|
||||
|
||||
let user_signer = entry
|
||||
.get_ava_single_jws_key_es256("jws_es256_private_key")
|
||||
.get_ava_single_jws_key_es256(Attribute::JwsEs256PrivateKey)
|
||||
.ok_or_else(|| {
|
||||
admin_error!(
|
||||
?kid,
|
||||
|
@ -657,10 +657,10 @@ pub trait IdmServerTransaction<'a> {
|
|||
let within_valid_window = Account::check_within_valid_time(
|
||||
ct,
|
||||
entry
|
||||
.get_ava_single_datetime(Attribute::AccountValidFrom.as_ref())
|
||||
.get_ava_single_datetime(Attribute::AccountValidFrom)
|
||||
.as_ref(),
|
||||
entry
|
||||
.get_ava_single_datetime(Attribute::AccountExpire.as_ref())
|
||||
.get_ava_single_datetime(Attribute::AccountExpire)
|
||||
.as_ref(),
|
||||
);
|
||||
|
||||
|
@ -676,10 +676,10 @@ pub trait IdmServerTransaction<'a> {
|
|||
let grace_valid = ct < (Duration::from_secs(iat as u64) + GRACE_WINDOW);
|
||||
|
||||
let oauth2_session = entry
|
||||
.get_ava_as_oauth2session_map(Attribute::OAuth2Session.as_ref())
|
||||
.get_ava_as_oauth2session_map(Attribute::OAuth2Session)
|
||||
.and_then(|sessions| sessions.get(&session_id));
|
||||
let uat_session = entry
|
||||
.get_ava_as_session_map(Attribute::UserAuthTokenSession.as_ref())
|
||||
.get_ava_as_session_map(Attribute::UserAuthTokenSession)
|
||||
.and_then(|sessions| sessions.get(&parent_session_id));
|
||||
|
||||
if let Some(oauth2_session) = oauth2_session {
|
||||
|
@ -848,8 +848,12 @@ pub trait IdmServerTransaction<'a> {
|
|||
|
||||
if Account::check_within_valid_time(
|
||||
ct,
|
||||
entry.get_ava_single_datetime("account_valid_from").as_ref(),
|
||||
entry.get_ava_single_datetime("account_expire").as_ref(),
|
||||
entry
|
||||
.get_ava_single_datetime(Attribute::AccountValidFrom)
|
||||
.as_ref(),
|
||||
entry
|
||||
.get_ava_single_datetime(Attribute::AccountExpire)
|
||||
.as_ref(),
|
||||
) {
|
||||
// Good to go
|
||||
let limits = Limits::default();
|
||||
|
@ -922,7 +926,7 @@ pub trait IdmServerTransaction<'a> {
|
|||
})?;
|
||||
|
||||
let user_signer = entry
|
||||
.get_ava_single_jws_key_es256("jws_es256_private_key")
|
||||
.get_ava_single_jws_key_es256(Attribute::JwsEs256PrivateKey)
|
||||
.ok_or_else(|| {
|
||||
admin_error!(
|
||||
?kid,
|
||||
|
@ -1334,7 +1338,7 @@ impl<'a> IdmServerAuthTransaction<'a> {
|
|||
}
|
||||
Token::ApiToken(apit, entry) => {
|
||||
let spn = entry
|
||||
.get_ava_single_proto_string(Attribute::Spn.as_ref())
|
||||
.get_ava_single_proto_string(Attribute::Spn)
|
||||
.ok_or_else(|| {
|
||||
OperationError::InvalidAccountState("Missing attribute: spn".to_string())
|
||||
})?;
|
||||
|
@ -1844,9 +1848,9 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
let vcred = Value::new_credential("primary", ncred);
|
||||
// We need to remove other credentials too.
|
||||
let modlist = ModifyList::new_list(vec![
|
||||
m_purge("passkeys"),
|
||||
m_purge("primary_credential"),
|
||||
Modify::Present("primary_credential".into(), vcred),
|
||||
m_purge(Attribute::PassKeys),
|
||||
m_purge(Attribute::PrimaryCredential),
|
||||
Modify::Present(Attribute::PrimaryCredential.into(), vcred),
|
||||
]);
|
||||
|
||||
trace!(?modlist, "processing change");
|
||||
|
@ -2041,7 +2045,7 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
info!(session_id = %asr.session_id, "Persisting auth session");
|
||||
|
||||
// modify the account to put the session onto it.
|
||||
let modlist = ModifyList::new_append("user_auth_token_session", session);
|
||||
let modlist = ModifyList::new_append(Attribute::UserAuthTokenSession, session);
|
||||
|
||||
self.qs_write
|
||||
.internal_modify(
|
||||
|
@ -2880,7 +2884,7 @@ mod tests {
|
|||
.internal_search_uuid(UUID_ADMIN)
|
||||
.expect("Can't access admin entry.");
|
||||
let cred_before = admin_entry
|
||||
.get_ava_single_credential("primary_credential")
|
||||
.get_ava_single_credential(Attribute::PrimaryCredential)
|
||||
.expect("No credential present")
|
||||
.clone();
|
||||
drop(idms_prox_read);
|
||||
|
@ -2911,7 +2915,7 @@ mod tests {
|
|||
.internal_search_uuid(UUID_ADMIN)
|
||||
.expect("Can't access admin entry.");
|
||||
let cred_after = admin_entry
|
||||
.get_ava_single_credential("primary_credential")
|
||||
.get_ava_single_credential(Attribute::PrimaryCredential)
|
||||
.expect("No credential present")
|
||||
.clone();
|
||||
drop(idms_prox_read);
|
||||
|
@ -3549,7 +3553,7 @@ mod tests {
|
|||
.qs_read
|
||||
.internal_search_uuid(UUID_ADMIN)
|
||||
.expect("failed");
|
||||
let sessions = admin.get_ava_as_session_map("user_auth_token_session");
|
||||
let sessions = admin.get_ava_as_session_map(Attribute::UserAuthTokenSession);
|
||||
assert!(sessions.is_none());
|
||||
drop(idms_prox_read);
|
||||
|
||||
|
@ -3574,7 +3578,7 @@ mod tests {
|
|||
.internal_search_uuid(UUID_ADMIN)
|
||||
.expect("failed");
|
||||
let sessions = admin
|
||||
.get_ava_as_session_map("user_auth_token_session")
|
||||
.get_ava_as_session_map(Attribute::UserAuthTokenSession)
|
||||
.expect("Sessions must be present!");
|
||||
assert!(sessions.len() == 1);
|
||||
let session_data_a = sessions.get(&session_a).expect("Session A is missing!");
|
||||
|
@ -3604,7 +3608,7 @@ mod tests {
|
|||
.internal_search_uuid(UUID_ADMIN)
|
||||
.expect("failed");
|
||||
let sessions = admin
|
||||
.get_ava_as_session_map("user_auth_token_session")
|
||||
.get_ava_as_session_map(Attribute::UserAuthTokenSession)
|
||||
.expect("Sessions must be present!");
|
||||
trace!(?sessions);
|
||||
assert!(sessions.len() == 2);
|
||||
|
|
|
@ -32,36 +32,32 @@ use crate::value::ApiToken;
|
|||
macro_rules! try_from_entry {
|
||||
($value:expr) => {{
|
||||
// Check the classes
|
||||
if !$value.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
&EntryClass::ServiceAccount.into(),
|
||||
) {
|
||||
if !$value.attribute_equality(Attribute::Class, &EntryClass::ServiceAccount.into()) {
|
||||
return Err(OperationError::InvalidAccountState(
|
||||
"Missing class: service account".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let spn = $value
|
||||
.get_ava_single_proto_string(Attribute::Spn.as_ref())
|
||||
.ok_or(OperationError::InvalidAccountState(
|
||||
"Missing attribute: spn".to_string(),
|
||||
))?;
|
||||
let spn = $value.get_ava_single_proto_string(Attribute::Spn).ok_or(
|
||||
OperationError::InvalidAccountState(format!("Missing attribute: {}", Attribute::Spn)),
|
||||
)?;
|
||||
|
||||
let jws_key = $value
|
||||
.get_ava_single_jws_key_es256("jws_es256_private_key")
|
||||
.get_ava_single_jws_key_es256(Attribute::JwsEs256PrivateKey)
|
||||
.cloned()
|
||||
.ok_or(OperationError::InvalidAccountState(
|
||||
"Missing attribute: jws_es256_private_key".to_string(),
|
||||
))?;
|
||||
.ok_or(OperationError::InvalidAccountState(format!(
|
||||
"Missing attribute: {}",
|
||||
Attribute::JwsEs256PrivateKey
|
||||
)))?;
|
||||
|
||||
let api_tokens = $value
|
||||
.get_ava_as_apitoken_map("api_token_session")
|
||||
.get_ava_as_apitoken_map(Attribute::ApiTokenSession)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
let valid_from = $value.get_ava_single_datetime("account_valid_from");
|
||||
let valid_from = $value.get_ava_single_datetime(Attribute::AccountValidFrom);
|
||||
|
||||
let expire = $value.get_ava_single_datetime("account_expire");
|
||||
let expire = $value.get_ava_single_datetime(Attribute::AccountExpire);
|
||||
|
||||
let uuid = $value.get_uuid().clone();
|
||||
|
||||
|
@ -105,8 +101,12 @@ impl ServiceAccount {
|
|||
) -> bool {
|
||||
let within_valid_window = Account::check_within_valid_time(
|
||||
ct,
|
||||
entry.get_ava_single_datetime("account_valid_from").as_ref(),
|
||||
entry.get_ava_single_datetime("account_expire").as_ref(),
|
||||
entry
|
||||
.get_ava_single_datetime(Attribute::AccountValidFrom)
|
||||
.as_ref(),
|
||||
entry
|
||||
.get_ava_single_datetime(Attribute::AccountExpire)
|
||||
.as_ref(),
|
||||
);
|
||||
|
||||
if !within_valid_window {
|
||||
|
@ -116,7 +116,7 @@ impl ServiceAccount {
|
|||
|
||||
// Get the sessions.
|
||||
let session_present = entry
|
||||
.get_ava_as_apitoken_map("api_token_session")
|
||||
.get_ava_as_apitoken_map(Attribute::ApiTokenSession)
|
||||
.map(|session_map| session_map.get(&apit.token_id).is_some())
|
||||
.unwrap_or(false);
|
||||
|
||||
|
@ -335,9 +335,9 @@ impl<'a> IdmServerProxyWriteTransaction<'a> {
|
|||
let vcred = Value::new_credential("primary", ncred);
|
||||
// We need to remove other credentials too.
|
||||
let modlist = ModifyList::new_list(vec![
|
||||
m_purge("passkeys"),
|
||||
m_purge("primary_credential"),
|
||||
Modify::Present("primary_credential".into(), vcred),
|
||||
m_purge(Attribute::PassKeys),
|
||||
m_purge(Attribute::PrimaryCredential),
|
||||
Modify::Present(Attribute::PrimaryCredential.into(), vcred),
|
||||
]);
|
||||
|
||||
trace!(?modlist, "processing change");
|
||||
|
@ -387,26 +387,27 @@ impl<'a> IdmServerProxyReadTransaction<'a> {
|
|||
.and_then(|e| {
|
||||
let account_id = e.get_uuid();
|
||||
// From the entry, turn it into the value
|
||||
e.get_ava_as_apitoken_map("api_token_session").map(|smap| {
|
||||
smap.iter()
|
||||
.map(|(u, s)| {
|
||||
s.scope
|
||||
.try_into()
|
||||
.map(|purpose| ProtoApiToken {
|
||||
account_id,
|
||||
token_id: *u,
|
||||
label: s.label.clone(),
|
||||
expiry: s.expiry,
|
||||
issued_at: s.issued_at,
|
||||
purpose,
|
||||
})
|
||||
.map_err(|e| {
|
||||
admin_error!("Invalid api_token {}", u);
|
||||
e
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
})
|
||||
e.get_ava_as_apitoken_map(Attribute::ApiTokenSession)
|
||||
.map(|smap| {
|
||||
smap.iter()
|
||||
.map(|(u, s)| {
|
||||
s.scope
|
||||
.try_into()
|
||||
.map(|purpose| ProtoApiToken {
|
||||
account_id,
|
||||
token_id: *u,
|
||||
label: s.label.clone(),
|
||||
expiry: s.expiry,
|
||||
issued_at: s.issued_at,
|
||||
purpose,
|
||||
})
|
||||
.map_err(|e| {
|
||||
admin_error!("Invalid api_token {}", u);
|
||||
e
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
})
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
// No matching entry? Return none.
|
||||
|
|
|
@ -34,17 +34,14 @@ pub(crate) struct UnixUserAccount {
|
|||
|
||||
macro_rules! try_from_entry {
|
||||
($value:expr, $groups:expr) => {{
|
||||
if !$value.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
&EntryClass::Account.to_partialvalue(),
|
||||
) {
|
||||
if !$value.attribute_equality(Attribute::Class, &EntryClass::Account.to_partialvalue()) {
|
||||
return Err(OperationError::InvalidAccountState(
|
||||
"Missing class: account".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
if !$value.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
Attribute::Class,
|
||||
&EntryClass::PosixAccount.to_partialvalue(),
|
||||
) {
|
||||
return Err(OperationError::InvalidAccountState(
|
||||
|
@ -53,56 +50,70 @@ macro_rules! try_from_entry {
|
|||
}
|
||||
|
||||
let name = $value
|
||||
.get_ava_single_iname(Attribute::Name.as_ref())
|
||||
.get_ava_single_iname(Attribute::Name)
|
||||
.map(|s| s.to_string())
|
||||
.ok_or_else(|| {
|
||||
OperationError::InvalidAccountState("Missing attribute: name".to_string())
|
||||
OperationError::InvalidAccountState(format!(
|
||||
"Missing attribute: {}",
|
||||
Attribute::Name
|
||||
))
|
||||
})?;
|
||||
|
||||
let spn = $value
|
||||
.get_ava_single_proto_string(Attribute::Spn.as_ref())
|
||||
.get_ava_single_proto_string(Attribute::Spn)
|
||||
.ok_or_else(|| {
|
||||
OperationError::InvalidAccountState("Missing attribute: spn".to_string())
|
||||
OperationError::InvalidAccountState(format!(
|
||||
"Missing attribute: {}",
|
||||
Attribute::Spn
|
||||
))
|
||||
})?;
|
||||
|
||||
let uuid = $value.get_uuid();
|
||||
|
||||
let displayname = $value
|
||||
.get_ava_single_utf8("displayname")
|
||||
.get_ava_single_utf8(Attribute::DisplayName)
|
||||
.map(|s| s.to_string())
|
||||
.ok_or_else(|| {
|
||||
OperationError::InvalidAccountState("Missing attribute: displayname".to_string())
|
||||
OperationError::InvalidAccountState(format!(
|
||||
"Missing attribute: {}",
|
||||
Attribute::DisplayName
|
||||
))
|
||||
})?;
|
||||
|
||||
let gidnumber = $value.get_ava_single_uint32("gidnumber").ok_or_else(|| {
|
||||
OperationError::InvalidAccountState("Missing attribute: gidnumber".to_string())
|
||||
})?;
|
||||
let gidnumber = $value
|
||||
.get_ava_single_uint32(Attribute::GidNumber)
|
||||
.ok_or_else(|| {
|
||||
OperationError::InvalidAccountState(format!(
|
||||
"Missing attribute: {}",
|
||||
Attribute::GidNumber
|
||||
))
|
||||
})?;
|
||||
|
||||
let shell = $value
|
||||
.get_ava_single_iutf8(ATTR_LOGINSHELL)
|
||||
.get_ava_single_iutf8(Attribute::LoginShell)
|
||||
.map(|s| s.to_string());
|
||||
|
||||
let sshkeys = $value
|
||||
.get_ava_iter_sshpubkeys(ATTR_LDAP_SSH_PUBLICKEY)
|
||||
.get_ava_iter_sshpubkeys(Attribute::SshPublicKey)
|
||||
.map(|i| i.map(|s| s.to_string()).collect())
|
||||
.unwrap_or_else(Vec::new);
|
||||
|
||||
let cred = $value
|
||||
.get_ava_single_credential("unix_password")
|
||||
.get_ava_single_credential(Attribute::UnixPassword)
|
||||
.map(|v| v.clone());
|
||||
|
||||
let radius_secret = $value
|
||||
.get_ava_single_secret("radius_secret")
|
||||
.get_ava_single_secret(Attribute::RadiusSecret)
|
||||
.map(str::to_string);
|
||||
|
||||
let mail = $value
|
||||
.get_ava_iter_mail("mail")
|
||||
.get_ava_iter_mail(Attribute::Mail)
|
||||
.map(|i| i.map(str::to_string).collect())
|
||||
.unwrap_or_else(Vec::new);
|
||||
|
||||
let valid_from = $value.get_ava_single_datetime("account_valid_from");
|
||||
let valid_from = $value.get_ava_single_datetime(Attribute::AccountValidFrom);
|
||||
|
||||
let expire = $value.get_ava_single_datetime("account_expire");
|
||||
let expire = $value.get_ava_single_datetime(Attribute::AccountExpire);
|
||||
|
||||
Ok(UnixUserAccount {
|
||||
name,
|
||||
|
@ -183,7 +194,10 @@ impl UnixUserAccount {
|
|||
) -> Result<ModifyList<ModifyInvalid>, OperationError> {
|
||||
let ncred = Credential::new_password_only(crypto_policy, cleartext)?;
|
||||
let vcred = Value::new_credential("unix", ncred);
|
||||
Ok(ModifyList::new_purge_and_set("unix_password", vcred))
|
||||
Ok(ModifyList::new_purge_and_set(
|
||||
Attribute::UnixPassword,
|
||||
vcred,
|
||||
))
|
||||
}
|
||||
|
||||
pub(crate) fn gen_password_upgrade_mod(
|
||||
|
@ -196,7 +210,10 @@ impl UnixUserAccount {
|
|||
Some(ucred) => {
|
||||
if let Some(ncred) = ucred.upgrade_password(crypto_policy, cleartext)? {
|
||||
let vcred = Value::new_credential("primary", ncred);
|
||||
Ok(Some(ModifyList::new_purge_and_set("unix_password", vcred)))
|
||||
Ok(Some(ModifyList::new_purge_and_set(
|
||||
Attribute::UnixPassword,
|
||||
vcred,
|
||||
)))
|
||||
} else {
|
||||
// No action, not the same pw
|
||||
Ok(None)
|
||||
|
@ -314,42 +331,56 @@ macro_rules! try_from_group_e {
|
|||
($value:expr) => {{
|
||||
// We could be looking at a user for their UPG, OR a true group.
|
||||
|
||||
if !(($value.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
&EntryClass::Account.to_partialvalue(),
|
||||
) && $value.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
&EntryClass::PosixAccount.to_partialvalue(),
|
||||
)) || ($value.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
&EntryClass::Group.to_partialvalue(),
|
||||
) && $value.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
&EntryClass::PosixGroup.to_partialvalue(),
|
||||
))) {
|
||||
return Err(OperationError::InvalidAccountState(
|
||||
"Missing class: account && posixaccount OR group && posixgroup".to_string(),
|
||||
));
|
||||
if !(($value.attribute_equality(Attribute::Class, &EntryClass::Account.to_partialvalue())
|
||||
&& $value.attribute_equality(
|
||||
Attribute::Class,
|
||||
&EntryClass::PosixAccount.to_partialvalue(),
|
||||
))
|
||||
|| ($value.attribute_equality(Attribute::Class, &EntryClass::Group.to_partialvalue())
|
||||
&& $value.attribute_equality(
|
||||
Attribute::Class,
|
||||
&EntryClass::PosixGroup.to_partialvalue(),
|
||||
)))
|
||||
{
|
||||
return Err(OperationError::InvalidAccountState(format!(
|
||||
"Missing {}: {} && {} OR {} && {}",
|
||||
Attribute::Class,
|
||||
Attribute::Account,
|
||||
EntryClass::PosixAccount,
|
||||
Attribute::Group,
|
||||
EntryClass::PosixGroup,
|
||||
)));
|
||||
}
|
||||
|
||||
let name = $value
|
||||
.get_ava_single_iname(Attribute::Name.as_ref())
|
||||
.get_ava_single_iname(Attribute::Name)
|
||||
.map(|s| s.to_string())
|
||||
.ok_or_else(|| {
|
||||
OperationError::InvalidAccountState("Missing attribute: name".to_string())
|
||||
OperationError::InvalidAccountState(format!(
|
||||
"Missing attribute: {}",
|
||||
Attribute::Name
|
||||
))
|
||||
})?;
|
||||
|
||||
let spn = $value
|
||||
.get_ava_single_proto_string(Attribute::Spn.as_ref())
|
||||
.get_ava_single_proto_string(Attribute::Spn)
|
||||
.ok_or_else(|| {
|
||||
OperationError::InvalidAccountState("Missing attribute: spn".to_string())
|
||||
OperationError::InvalidAccountState(format!(
|
||||
"Missing attribute: {}",
|
||||
Attribute::Spn
|
||||
))
|
||||
})?;
|
||||
|
||||
let uuid = $value.get_uuid();
|
||||
|
||||
let gidnumber = $value.get_ava_single_uint32("gidnumber").ok_or_else(|| {
|
||||
OperationError::InvalidAccountState("Missing attribute: gidnumber".to_string())
|
||||
})?;
|
||||
let gidnumber = $value
|
||||
.get_ava_single_uint32(Attribute::GidNumber)
|
||||
.ok_or_else(|| {
|
||||
OperationError::InvalidAccountState(format!(
|
||||
"Missing attribute: {}",
|
||||
Attribute::GidNumber
|
||||
))
|
||||
})?;
|
||||
|
||||
Ok(UnixGroup {
|
||||
name,
|
||||
|
@ -365,42 +396,52 @@ macro_rules! try_from_account_group_e {
|
|||
// First synthesise the self-group from the account.
|
||||
// We have already checked these, but paranoia is better than
|
||||
// complacency.
|
||||
if !$value.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
&EntryClass::Account.to_partialvalue(),
|
||||
) {
|
||||
return Err(OperationError::InvalidAccountState(
|
||||
"Missing class: account".to_string(),
|
||||
));
|
||||
if !$value.attribute_equality(Attribute::Class, &EntryClass::Account.to_partialvalue()) {
|
||||
return Err(OperationError::InvalidAccountState(format!(
|
||||
"Missing class: {}",
|
||||
EntryClass::Account
|
||||
)));
|
||||
}
|
||||
|
||||
if !$value.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
Attribute::Class,
|
||||
&EntryClass::PosixAccount.to_partialvalue(),
|
||||
) {
|
||||
return Err(OperationError::InvalidAccountState(
|
||||
"Missing class: posixaccount".to_string(),
|
||||
));
|
||||
return Err(OperationError::InvalidAccountState(format!(
|
||||
"Missing class: {}",
|
||||
EntryClass::PosixAccount
|
||||
)));
|
||||
}
|
||||
|
||||
let name = $value
|
||||
.get_ava_single_iname(Attribute::Name.as_ref())
|
||||
.get_ava_single_iname(Attribute::Name)
|
||||
.map(|s| s.to_string())
|
||||
.ok_or_else(|| {
|
||||
OperationError::InvalidAccountState("Missing attribute: name".to_string())
|
||||
OperationError::InvalidAccountState(format!(
|
||||
"Missing attribute: {}",
|
||||
Attribute::Name
|
||||
))
|
||||
})?;
|
||||
|
||||
let spn = $value
|
||||
.get_ava_single_proto_string(Attribute::Spn.as_ref())
|
||||
.get_ava_single_proto_string(Attribute::Spn)
|
||||
.ok_or_else(|| {
|
||||
OperationError::InvalidAccountState("Missing attribute: spn".to_string())
|
||||
OperationError::InvalidAccountState(format!(
|
||||
"Missing attribute: {}",
|
||||
Attribute::Spn
|
||||
))
|
||||
})?;
|
||||
|
||||
let uuid = $value.get_uuid();
|
||||
|
||||
let gidnumber = $value.get_ava_single_uint32("gidnumber").ok_or_else(|| {
|
||||
OperationError::InvalidAccountState("Missing attribute: gidnumber".to_string())
|
||||
})?;
|
||||
let gidnumber = $value
|
||||
.get_ava_single_uint32(Attribute::GidNumber)
|
||||
.ok_or_else(|| {
|
||||
OperationError::InvalidAccountState(format!(
|
||||
"Missing attribute: {}",
|
||||
Attribute::GidNumber
|
||||
))
|
||||
})?;
|
||||
|
||||
// This is the user private group.
|
||||
let upg = UnixGroup {
|
||||
|
@ -410,7 +451,7 @@ macro_rules! try_from_account_group_e {
|
|||
uuid,
|
||||
};
|
||||
|
||||
match $value.get_ava_as_refuuid("memberof") {
|
||||
match $value.get_ava_as_refuuid(Attribute::MemberOf) {
|
||||
Some(riter) => {
|
||||
let f = filter!(f_and!([
|
||||
f_eq(Attribute::Class, EntryClass::PosixGroup.into()),
|
||||
|
|
|
@ -514,6 +514,7 @@ macro_rules! vs_utf8 {
|
|||
|
||||
#[allow(unused_macros)]
|
||||
#[macro_export]
|
||||
/// Takes EntryClass objects and makes a VaueSetIutf8
|
||||
macro_rules! vs_iutf8 {
|
||||
() => (
|
||||
compile_error!("ValueSetIutf8 needs at least 1 element")
|
||||
|
|
|
@ -33,27 +33,23 @@ pub enum Modify {
|
|||
// This attr *should not* exist.
|
||||
Purged(AttrString),
|
||||
// This attr and value must exist *in this state* for this change to proceed.
|
||||
Assert(AttrString, PartialValue),
|
||||
Assert(Attribute, PartialValue),
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn m_pres(a: &str, v: &Value) -> Modify {
|
||||
Modify::Present(a.into(), v.clone())
|
||||
pub fn m_pres(attr: Attribute, v: &Value) -> Modify {
|
||||
Modify::Present(attr.into(), v.clone())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn m_remove(a: &str, v: &PartialValue) -> Modify {
|
||||
Modify::Removed(a.into(), v.clone())
|
||||
pub fn m_remove(attr: Attribute, v: &PartialValue) -> Modify {
|
||||
Modify::Removed(attr.into(), v.clone())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn m_purge(a: &str) -> Modify {
|
||||
Modify::Purged(AttrString::from(a))
|
||||
pub fn m_purge(attr: Attribute) -> Modify {
|
||||
Modify::Purged(attr.into())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn m_assert(a: &str, v: &PartialValue) -> Modify {
|
||||
Modify::Assert(a.into(), v.clone())
|
||||
pub fn m_assert(attr: Attribute, v: &PartialValue) -> Modify {
|
||||
Modify::Assert(attr, v.clone())
|
||||
}
|
||||
|
||||
impl Modify {
|
||||
|
@ -102,22 +98,19 @@ impl ModifyList<ModifyInvalid> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn new_purge_and_set(attr: &str, v: Value) -> Self {
|
||||
Self::new_list(vec![
|
||||
m_purge(attr),
|
||||
Modify::Present(AttrString::from(attr), v),
|
||||
])
|
||||
pub fn new_purge_and_set(attr: Attribute, v: Value) -> Self {
|
||||
Self::new_list(vec![m_purge(attr), Modify::Present(attr.into(), v)])
|
||||
}
|
||||
|
||||
pub fn new_append(attr: &str, v: Value) -> Self {
|
||||
Self::new_list(vec![Modify::Present(AttrString::from(attr), v)])
|
||||
pub fn new_append(attr: Attribute, v: Value) -> Self {
|
||||
Self::new_list(vec![Modify::Present(attr.into(), v)])
|
||||
}
|
||||
|
||||
pub fn new_remove(attr: &str, pv: PartialValue) -> Self {
|
||||
Self::new_list(vec![Modify::Removed(AttrString::from(attr), pv)])
|
||||
pub fn new_remove(attr: Attribute, pv: PartialValue) -> Self {
|
||||
Self::new_list(vec![Modify::Removed(attr.into(), pv)])
|
||||
}
|
||||
|
||||
pub fn new_purge(attr: &str) -> Self {
|
||||
pub fn new_purge(attr: Attribute) -> Self {
|
||||
Self::new_list(vec![m_purge(attr)])
|
||||
}
|
||||
|
||||
|
@ -148,12 +141,13 @@ impl ModifyList<ModifyInvalid> {
|
|||
|
||||
pe.attrs.iter().try_for_each(|(attr, vals)| {
|
||||
// Issue a purge to the attr.
|
||||
let attr: Attribute = (attr.clone()).try_into()?;
|
||||
mods.push(m_purge(attr));
|
||||
// Now if there are vals, push those too.
|
||||
// For each value we want to now be present.
|
||||
vals.iter().try_for_each(|val| {
|
||||
qs.clone_value(attr, val).map(|resolved_v| {
|
||||
mods.push(Modify::Present(attr.as_str().into(), resolved_v));
|
||||
qs.clone_value(attr.as_ref(), val).map(|resolved_v| {
|
||||
mods.push(Modify::Present(attr.into(), resolved_v));
|
||||
})
|
||||
})
|
||||
})?;
|
||||
|
@ -196,15 +190,13 @@ impl ModifyList<ModifyInvalid> {
|
|||
None => Err(SchemaError::InvalidAttribute(attr_norm.to_string())),
|
||||
}
|
||||
}
|
||||
Modify::Assert(attr, value) => {
|
||||
let attr_norm = schema.normalise_attr_name(attr);
|
||||
match schema_attributes.get(&attr_norm) {
|
||||
Some(schema_a) => schema_a
|
||||
.validate_partialvalue(attr_norm.as_str(), value)
|
||||
.map(|_| Modify::Assert(attr_norm, value.clone())),
|
||||
None => Err(SchemaError::InvalidAttribute(attr_norm.to_string())),
|
||||
}
|
||||
}
|
||||
Modify::Assert(attr, value) => match schema_attributes.get(attr.as_ref()) {
|
||||
// TODO: given attr is an enum... you can't get this wrong anymore?
|
||||
Some(schema_a) => schema_a
|
||||
.validate_partialvalue(attr.as_ref(), value)
|
||||
.map(|_| Modify::Assert(attr.to_owned(), value.clone())),
|
||||
None => Err(SchemaError::InvalidAttribute(attr.to_string())),
|
||||
},
|
||||
Modify::Purged(attr) => {
|
||||
let attr_norm = schema.normalise_attr_name(attr);
|
||||
match schema_attributes.get(&attr_norm) {
|
||||
|
|
|
@ -33,23 +33,23 @@ where
|
|||
.filter_map(|e| e.mask_recycled_ts())
|
||||
.try_for_each(|e| {
|
||||
let uuid = e
|
||||
.get_ava_single_uuid("uuid")
|
||||
.get_ava_single_uuid(Attribute::Uuid)
|
||||
.ok_or_else(|| {
|
||||
error!("An entry is missing its uuid. This should be impossible!");
|
||||
OperationError::InvalidEntryState
|
||||
})?;
|
||||
|
||||
// Faster to iterate over the attr vec inside this loop.
|
||||
for attr in uniqueattrs.iter() {
|
||||
if let Some(vs) = e.get_ava_set(attr) {
|
||||
for attrstr in uniqueattrs.iter() {
|
||||
if let Some(vs) = e.get_ava_set(attrstr.try_into()?) {
|
||||
for pv in vs.to_partialvalue_iter() {
|
||||
let key = (attr.clone(), pv);
|
||||
let key = (attrstr.clone(), pv);
|
||||
cand_attr.entry(key)
|
||||
// Must have conflicted, lets append.
|
||||
.and_modify(|v| {
|
||||
warn!(
|
||||
"ava already exists -> {:?} on entry {:?} has conflicts within change set",
|
||||
attr,
|
||||
attrstr,
|
||||
e.get_display_id()
|
||||
);
|
||||
v.push(uuid)
|
||||
|
@ -116,17 +116,15 @@ fn enforce_unique<VALID, STATE>(
|
|||
}
|
||||
|
||||
// Now do an internal search on name and !uuid for each
|
||||
let cand_filters: Vec<_> = cand_attr
|
||||
.iter()
|
||||
.map(|((attr, v), uuid)| {
|
||||
// and[ attr eq k, andnot [ uuid eq v ]]
|
||||
// Basically this says where name but also not self.
|
||||
f_and(vec![
|
||||
FC::Eq(attr, v.clone()),
|
||||
f_andnot(FC::Eq(ATTR_UUID, PartialValue::Uuid(*uuid))),
|
||||
])
|
||||
})
|
||||
.collect();
|
||||
let mut cand_filters = Vec::new();
|
||||
for ((attr, v), uuid) in cand_attr.iter() {
|
||||
// and[ attr eq k, andnot [ uuid eq v ]]
|
||||
// Basically this says where name but also not self.
|
||||
cand_filters.push(f_and(vec![
|
||||
FC::Eq(attr, v.clone()),
|
||||
f_andnot(FC::Eq(Attribute::Uuid.as_ref(), PartialValue::Uuid(*uuid))),
|
||||
]));
|
||||
}
|
||||
|
||||
// Or
|
||||
let filt_in = filter!(f_or(cand_filters.clone()));
|
||||
|
@ -312,7 +310,7 @@ impl Plugin for AttrUnique {
|
|||
// Basically this says where name but also not self.
|
||||
f_and(vec![
|
||||
FC::Eq(attr, v.clone()),
|
||||
f_andnot(FC::Eq(ATTR_UUID, PartialValue::Uuid(*uuid))),
|
||||
f_andnot(FC::Eq(Attribute::Uuid.as_ref(), PartialValue::Uuid(*uuid))),
|
||||
])
|
||||
})
|
||||
})
|
||||
|
@ -368,7 +366,7 @@ impl Plugin for AttrUnique {
|
|||
.map(|(attr, pv)| {
|
||||
f_and(vec![
|
||||
FC::Eq(attr, pv.clone()),
|
||||
f_andnot(FC::Eq(ATTR_UUID, PartialValue::Uuid(uuid))),
|
||||
f_andnot(FC::Eq(Attribute::Uuid.as_ref(), PartialValue::Uuid(uuid))),
|
||||
])
|
||||
})
|
||||
.collect();
|
||||
|
|
|
@ -45,19 +45,23 @@ impl Plugin for Base {
|
|||
entry.add_ava(Attribute::Class, EntryClass::Object.to_value());
|
||||
|
||||
// if they don't have uuid, create it.
|
||||
match entry.get_ava_set("uuid").map(|s| s.len()) {
|
||||
match entry.get_ava_set(Attribute::Uuid).map(|s| s.len()) {
|
||||
None => {
|
||||
// Generate
|
||||
let ava_uuid = Value::Uuid(Uuid::new_v4());
|
||||
trace!("Setting temporary UUID {:?} to entry", ava_uuid);
|
||||
entry.set_ava("uuid", once(ava_uuid));
|
||||
entry.set_ava(Attribute::Uuid, once(ava_uuid));
|
||||
}
|
||||
Some(1) => {
|
||||
// Do nothing
|
||||
}
|
||||
Some(x) => {
|
||||
// If we get some it MUST be 2 +
|
||||
admin_error!("Entry defines uuid attr, but has multiple ({}) values.", x);
|
||||
admin_error!(
|
||||
"Entry defines {} attr, but has multiple ({}) values.",
|
||||
Attribute::Uuid,
|
||||
x
|
||||
);
|
||||
return Err(OperationError::Plugin(PluginError::Base(
|
||||
"Uuid has multiple values".to_string(),
|
||||
)));
|
||||
|
@ -75,7 +79,7 @@ impl Plugin for Base {
|
|||
// we may not have filled in the uuid field yet.
|
||||
for entry in cand.iter() {
|
||||
let uuid_ref: Uuid = entry
|
||||
.get_ava_single_uuid("uuid")
|
||||
.get_ava_single_uuid(Attribute::Uuid)
|
||||
.ok_or_else(|| OperationError::InvalidAttribute(Attribute::Uuid.to_string()))?;
|
||||
if !cand_uuid.insert(uuid_ref) {
|
||||
trace!("uuid duplicate found in create set! {:?}", uuid_ref);
|
||||
|
@ -121,7 +125,7 @@ impl Plugin for Base {
|
|||
let filt_in = filter_all!(FC::Or(
|
||||
cand_uuid
|
||||
.into_iter()
|
||||
.map(|u| FC::Eq("uuid", PartialValue::Uuid(u)))
|
||||
.map(|u| FC::Eq(Attribute::Uuid.as_ref(), PartialValue::Uuid(u)))
|
||||
.collect(),
|
||||
));
|
||||
|
||||
|
@ -166,7 +170,7 @@ impl Plugin for Base {
|
|||
Modify::Purged(a) => Some(a),
|
||||
Modify::Assert(_, _) => None,
|
||||
};
|
||||
if attr.map(|s| s.as_str()) == Some("uuid") {
|
||||
if attr == Some(&AttrString::from(Attribute::Uuid)) {
|
||||
debug!(?modify, "Modify in violation");
|
||||
request_error!("Modifications to UUID's are NOT ALLOWED");
|
||||
Err(OperationError::SystemProtectedAttribute)
|
||||
|
@ -193,7 +197,7 @@ impl Plugin for Base {
|
|||
Modify::Purged(a) => Some(a),
|
||||
Modify::Assert(_, _) => None,
|
||||
};
|
||||
if attr.map(|s| s.as_str()) == Some("uuid") {
|
||||
if attr == Some(&AttrString::from(Attribute::Uuid)) {
|
||||
debug!(?modify, "Modify in violation");
|
||||
request_error!("Modifications to UUID's are NOT ALLOWED");
|
||||
Err(OperationError::SystemProtectedAttribute)
|
||||
|
@ -348,7 +352,7 @@ mod tests {
|
|||
)))
|
||||
.expect("Internal search failure");
|
||||
let ue = cands.first().expect("No cand");
|
||||
assert!(ue.attribute_pres("uuid"));
|
||||
assert!(ue.attribute_pres(Attribute::Uuid));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -400,7 +404,7 @@ mod tests {
|
|||
}"#,
|
||||
);
|
||||
|
||||
let vs = e.get_ava_mut("uuid").unwrap();
|
||||
let vs = e.get_ava_mut(Attribute::Uuid).unwrap();
|
||||
vs.clear();
|
||||
|
||||
let create = vec![e.clone()];
|
||||
|
|
|
@ -59,12 +59,11 @@ impl CredImport {
|
|||
fn modify_inner<T: Clone>(cand: &mut [Entry<EntryInvalid, T>]) -> Result<(), OperationError> {
|
||||
cand.iter_mut().try_for_each(|e| {
|
||||
// PASSWORD IMPORT
|
||||
if let Some(vs) = e.pop_ava("password_import") {
|
||||
if let Some(vs) = e.pop_ava(Attribute::PasswordImport) {
|
||||
// if there are multiple, fail.
|
||||
let im_pw = vs.to_utf8_single().ok_or_else(|| {
|
||||
OperationError::Plugin(PluginError::CredImport(
|
||||
"password_import has incorrect value type - should be a single utf8 string"
|
||||
.to_string(),
|
||||
format!("{} has incorrect value type - should be a single utf8 string", Attribute::PasswordImport),
|
||||
))
|
||||
})?;
|
||||
|
||||
|
@ -86,11 +85,11 @@ impl CredImport {
|
|||
})?;
|
||||
|
||||
// does the entry have a primary cred?
|
||||
match e.get_ava_single_credential(Attribute::PrimaryCredential.into()) {
|
||||
match e.get_ava_single_credential(Attribute::PrimaryCredential) {
|
||||
Some(c) => {
|
||||
let c = c.update_password(pw);
|
||||
e.set_ava(
|
||||
Attribute::PrimaryCredential.into(),
|
||||
Attribute::PrimaryCredential,
|
||||
once(Value::new_credential("primary", c)),
|
||||
);
|
||||
}
|
||||
|
@ -98,7 +97,7 @@ impl CredImport {
|
|||
// just set it then!
|
||||
let c = Credential::new_from_password(pw);
|
||||
e.set_ava(
|
||||
Attribute::PrimaryCredential.into(),
|
||||
Attribute::PrimaryCredential,
|
||||
once(Value::new_credential("primary", c)),
|
||||
);
|
||||
}
|
||||
|
@ -107,7 +106,7 @@ impl CredImport {
|
|||
|
||||
// TOTP IMPORT - Must be subsequent to password import to allow primary cred to
|
||||
// be created.
|
||||
if let Some(vs) = e.pop_ava(Attribute::TotpImport.as_ref()) {
|
||||
if let Some(vs) = e.pop_ava(Attribute::TotpImport) {
|
||||
// Get the map.
|
||||
let totps = vs.as_totp_map().ok_or_else(|| {
|
||||
OperationError::Plugin(PluginError::CredImport(
|
||||
|
@ -115,12 +114,12 @@ impl CredImport {
|
|||
))
|
||||
})?;
|
||||
|
||||
if let Some(c) = e.get_ava_single_credential(Attribute::PrimaryCredential.as_ref()) {
|
||||
if let Some(c) = e.get_ava_single_credential(Attribute::PrimaryCredential) {
|
||||
let c = totps.iter().fold(c.clone(), |acc, (label, totp)| {
|
||||
acc.append_totp(label.clone(), totp.clone())
|
||||
});
|
||||
e.set_ava(
|
||||
Attribute::PrimaryCredential.as_ref(),
|
||||
Attribute::PrimaryCredential,
|
||||
once(Value::new_credential("primary", c)),
|
||||
);
|
||||
} else {
|
||||
|
@ -281,7 +280,7 @@ mod tests {
|
|||
.internal_search_uuid(uuid!("d2b496bd-8493-47b7-8142-f568b5cf47ee"))
|
||||
.expect("failed to get entry");
|
||||
let c = e
|
||||
.get_ava_single_credential(Attribute::PrimaryCredential.as_ref())
|
||||
.get_ava_single_credential(Attribute::PrimaryCredential)
|
||||
.expect("failed to get primary cred.");
|
||||
match &c.type_ {
|
||||
CredentialType::PasswordMfa(_pw, totp, webauthn, backup_code) => {
|
||||
|
@ -342,7 +341,7 @@ mod tests {
|
|||
|qs: &mut QueryServerWriteTransaction| {
|
||||
let e = qs.internal_search_uuid(euuid).expect("failed to get entry");
|
||||
let c = e
|
||||
.get_ava_single_credential(Attribute::PrimaryCredential.as_ref())
|
||||
.get_ava_single_credential(Attribute::PrimaryCredential)
|
||||
.expect("failed to get primary cred.");
|
||||
match &c.type_ {
|
||||
CredentialType::PasswordMfa(_pw, totp, webauthn, backup_code) => {
|
||||
|
|
|
@ -68,54 +68,54 @@ impl Domain {
|
|||
cand: &mut [Entry<EntryInvalid, T>],
|
||||
) -> Result<(), OperationError> {
|
||||
cand.iter_mut().try_for_each(|e| {
|
||||
if e.attribute_equality(Attribute::Class.as_ref(), &EntryClass::DomainInfo.into())
|
||||
&& e.attribute_equality(Attribute::Uuid.as_ref(), &PVUUID_DOMAIN_INFO)
|
||||
if e.attribute_equality(Attribute::Class, &EntryClass::DomainInfo.into())
|
||||
&& e.attribute_equality(Attribute::Uuid, &PVUUID_DOMAIN_INFO)
|
||||
{
|
||||
// Validate the domain ldap basedn syntax.
|
||||
if let Some(basedn) = e
|
||||
.get_ava_single_iutf8("domain_ldap_basedn") {
|
||||
.get_ava_single_iutf8(Attribute::DomainLdapBasedn) {
|
||||
|
||||
if !DOMAIN_LDAP_BASEDN_RE.is_match(basedn) {
|
||||
error!("Invalid domain_ldap_basedn. Must pass regex \"{}\"", *DOMAIN_LDAP_BASEDN_RE);
|
||||
error!("Invalid {}. Must pass regex \"{}\"", Attribute::DomainLdapBasedn, *DOMAIN_LDAP_BASEDN_RE);
|
||||
return Err(OperationError::InvalidState);
|
||||
}
|
||||
}
|
||||
|
||||
// We always set this, because the DB uuid is authoritative.
|
||||
let u = Value::Uuid(qs.get_domain_uuid());
|
||||
e.set_ava("domain_uuid", once(u));
|
||||
e.set_ava(Attribute::DomainUuid, once(u));
|
||||
trace!("plugin_domain: Applying uuid transform");
|
||||
|
||||
// We only apply this if one isn't provided.
|
||||
if !e.attribute_pres("domain_name") {
|
||||
if !e.attribute_pres(Attribute::DomainName) {
|
||||
let n = Value::new_iname(qs.get_domain_name());
|
||||
e.set_ava("domain_name", once(n));
|
||||
e.set_ava(Attribute::DomainName, once(n));
|
||||
trace!("plugin_domain: Applying domain_name transform");
|
||||
}
|
||||
|
||||
// Setup the minimum functional level if one is not set already.
|
||||
if !e.attribute_pres(Attribute::Version.as_ref()) {
|
||||
if !e.attribute_pres(Attribute::Version) {
|
||||
let n = Value::Uint32(DOMAIN_MIN_LEVEL);
|
||||
e.set_ava(Attribute::Version.as_ref(), once(n));
|
||||
e.set_ava(Attribute::Version, once(n));
|
||||
trace!("plugin_domain: Applying domain version transform");
|
||||
}
|
||||
|
||||
// create the domain_display_name if it's missing
|
||||
if !e.attribute_pres(Attribute::DomainDisplayName.as_ref()) {
|
||||
if !e.attribute_pres(Attribute::DomainDisplayName) {
|
||||
let domain_display_name = Value::new_utf8(format!("Kanidm {}", qs.get_domain_name()));
|
||||
security_info!("plugin_domain: setting default domain_display_name to {:?}", domain_display_name);
|
||||
|
||||
e.set_ava(Attribute::DomainDisplayName.into(), once(domain_display_name));
|
||||
e.set_ava(Attribute::DomainDisplayName, once(domain_display_name));
|
||||
}
|
||||
|
||||
if !e.attribute_pres(Attribute::FernetPrivateKeyStr.as_ref()) {
|
||||
if !e.attribute_pres(Attribute::FernetPrivateKeyStr) {
|
||||
security_info!("regenerating domain token encryption key");
|
||||
let k = fernet::Fernet::generate_key();
|
||||
let v = Value::new_secret_str(&k);
|
||||
e.add_ava(Attribute::FernetPrivateKeyStr, v);
|
||||
}
|
||||
|
||||
if !e.attribute_pres(Attribute::Es256PrivateKeyDer.as_ref()) {
|
||||
if !e.attribute_pres(Attribute::Es256PrivateKeyDer) {
|
||||
security_info!("regenerating domain es256 private key");
|
||||
let der = JwsSigner::generate_es256()
|
||||
.and_then(|jws| jws.private_key_to_der())
|
||||
|
@ -127,7 +127,7 @@ impl Domain {
|
|||
e.add_ava(Attribute::Es256PrivateKeyDer, v);
|
||||
}
|
||||
|
||||
if !e.attribute_pres(Attribute::PrivateCookieKey.as_ref()) {
|
||||
if !e.attribute_pres(Attribute::PrivateCookieKey) {
|
||||
security_info!("regenerating domain cookie key");
|
||||
let mut key = [0; 64];
|
||||
let mut rng = StdRng::from_entropy();
|
||||
|
@ -159,8 +159,6 @@ mod tests {
|
|||
|
||||
let u_dom = server_txn.get_domain_uuid();
|
||||
|
||||
assert!(
|
||||
e_dom.attribute_equality(Attribute::DomainUuid.as_ref(), &PartialValue::Uuid(u_dom))
|
||||
);
|
||||
assert!(e_dom.attribute_equality(Attribute::DomainUuid, &PartialValue::Uuid(u_dom)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,15 +43,15 @@ impl DynGroup {
|
|||
// Go through them all and update the new groups.
|
||||
for (pre, mut nd_group) in work_set.into_iter() {
|
||||
let scope_f: ProtoFilter = nd_group
|
||||
.get_ava_single_protofilter("dyngroup_filter")
|
||||
.get_ava_single_protofilter(Attribute::DynGroupFilter)
|
||||
.cloned()
|
||||
.ok_or_else(|| {
|
||||
admin_error!("Missing dyngroup_filter");
|
||||
admin_error!("Missing {}", Attribute::DynGroupFilter);
|
||||
OperationError::InvalidEntryState
|
||||
})?;
|
||||
|
||||
let scope_i = Filter::from_rw(ident_internal, &scope_f, qs).map_err(|e| {
|
||||
admin_error!("dyngroup_filter validation failed {:?}", e);
|
||||
admin_error!("{} validation failed {:?}", Attribute::DynGroupFilter, e);
|
||||
e
|
||||
})?;
|
||||
|
||||
|
@ -72,23 +72,23 @@ impl DynGroup {
|
|||
}
|
||||
|
||||
// Mark the former members as being affected also.
|
||||
if let Some(uuid_iter) = pre.get_ava_as_refuuid("dynmember") {
|
||||
if let Some(uuid_iter) = pre.get_ava_as_refuuid(Attribute::DynMember) {
|
||||
affected_uuids.extend(uuid_iter);
|
||||
}
|
||||
|
||||
if let Some(members) = members {
|
||||
// Only set something if there is actually something to do!
|
||||
nd_group.set_ava_set("dynmember", members);
|
||||
nd_group.set_ava_set(Attribute::DynMember, members);
|
||||
// push the entries to pre/cand
|
||||
} else {
|
||||
nd_group.purge_ava("dynmember");
|
||||
nd_group.purge_ava(Attribute::DynMember);
|
||||
}
|
||||
|
||||
candidate_tuples.push((pre, nd_group));
|
||||
|
||||
// Insert to our new instances
|
||||
if dyn_groups.insts.insert(uuid, scope_i).is_none() == expect {
|
||||
admin_error!("dyngroup cache uuid conflict {}", uuid);
|
||||
admin_error!("{} cache uuid conflict {}", Attribute::DynGroup, uuid);
|
||||
return Err(OperationError::InvalidState);
|
||||
}
|
||||
}
|
||||
|
@ -109,10 +109,10 @@ impl DynGroup {
|
|||
|
||||
for nd_group in entries.into_iter() {
|
||||
let scope_f: ProtoFilter = nd_group
|
||||
.get_ava_single_protofilter("dyngroup_filter")
|
||||
.get_ava_single_protofilter(Attribute::DynGroupFilter)
|
||||
.cloned()
|
||||
.ok_or_else(|| {
|
||||
admin_error!("Missing dyngroup_filter");
|
||||
admin_error!("Missing {}", Attribute::DynGroupFilter);
|
||||
OperationError::InvalidEntryState
|
||||
})?;
|
||||
|
||||
|
@ -146,7 +146,7 @@ impl DynGroup {
|
|||
let ident_internal = Identity::from_internal();
|
||||
|
||||
let (n_dyn_groups, entries): (Vec<&Entry<_, _>>, Vec<_>) = cand.iter().partition(|entry| {
|
||||
entry.attribute_equality(Attribute::Class.as_ref(), &EntryClass::DynGroup.into())
|
||||
entry.attribute_equality(Attribute::Class, &EntryClass::DynGroup.into())
|
||||
});
|
||||
|
||||
// DANGER: Why do we have to do this? During the use of qs for internal search
|
||||
|
@ -249,12 +249,12 @@ impl DynGroup {
|
|||
// Probably should be filter here instead.
|
||||
let (_, pre_entries): (Vec<&Arc<Entry<_, _>>>, Vec<_>) =
|
||||
pre_cand.iter().partition(|entry| {
|
||||
entry.attribute_equality(Attribute::Class.as_ref(), &EntryClass::DynGroup.into())
|
||||
entry.attribute_equality(Attribute::Class, &EntryClass::DynGroup.into())
|
||||
});
|
||||
|
||||
let (n_dyn_groups, post_entries): (Vec<&Entry<_, _>>, Vec<_>) =
|
||||
cand.iter().partition(|entry| {
|
||||
entry.attribute_equality(Attribute::Class.as_ref(), &EntryClass::DynGroup.into())
|
||||
entry.attribute_equality(Attribute::Class, &EntryClass::DynGroup.into())
|
||||
});
|
||||
|
||||
// DANGER: Why do we have to do this? During the use of qs for internal search
|
||||
|
@ -323,8 +323,7 @@ impl DynGroup {
|
|||
if let Some((pre, mut d_group)) = work_set.pop() {
|
||||
matches.iter().copied().for_each(|choice| match choice {
|
||||
Ok(u) => d_group.add_ava(Attribute::DynMember, Value::Refer(u)),
|
||||
Err(u) => d_group
|
||||
.remove_ava(Attribute::DynMember.as_ref(), &PartialValue::Refer(u)),
|
||||
Err(u) => d_group.remove_ava(Attribute::DynMember, &PartialValue::Refer(u)),
|
||||
});
|
||||
|
||||
affected_uuids.extend(matches.into_iter().map(|choice| match choice {
|
||||
|
@ -406,7 +405,7 @@ mod tests {
|
|||
|
||||
let d_group = cands.get(0).expect("Unable to access group.");
|
||||
let members = d_group
|
||||
.get_ava_set("dynmember")
|
||||
.get_ava_set(Attribute::DynMember)
|
||||
.expect("No members on dyn group");
|
||||
|
||||
assert!(members.to_refer_single() == Some(UUID_TEST_GROUP));
|
||||
|
@ -455,7 +454,7 @@ mod tests {
|
|||
|
||||
let d_group = cands.get(0).expect("Unable to access group.");
|
||||
let members = d_group
|
||||
.get_ava_set("dynmember")
|
||||
.get_ava_set(Attribute::DynMember)
|
||||
.expect("No members on dyn group");
|
||||
|
||||
assert!(members.to_refer_single() == Some(UUID_TEST_GROUP));
|
||||
|
@ -503,7 +502,7 @@ mod tests {
|
|||
.expect("Internal search failure");
|
||||
|
||||
let d_group = cands.get(0).expect("Unable to access group.");
|
||||
assert!(d_group.get_ava_set("dynmember").is_none());
|
||||
assert!(d_group.get_ava_set(Attribute::DynMember).is_none());
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -549,11 +548,11 @@ mod tests {
|
|||
|
||||
let d_group = cands.get(0).expect("Unable to access group.");
|
||||
let members = d_group
|
||||
.get_ava_set("dynmember")
|
||||
.get_ava_set(Attribute::DynMember)
|
||||
.expect("No members on dyn group");
|
||||
|
||||
assert!(members.to_refer_single() == Some(UUID_TEST_GROUP));
|
||||
assert!(d_group.get_ava_set("member").is_none());
|
||||
assert!(d_group.get_ava_set(Attribute::Member).is_none());
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -611,7 +610,7 @@ mod tests {
|
|||
|
||||
let d_group = cands.get(0).expect("Unable to access group.");
|
||||
let members = d_group
|
||||
.get_ava_set("dynmember")
|
||||
.get_ava_set(Attribute::DynMember)
|
||||
.expect("No members on dyn group");
|
||||
|
||||
assert!(members.to_refer_single() == Some(UUID_TEST_GROUP));
|
||||
|
@ -671,7 +670,7 @@ mod tests {
|
|||
.expect("Internal search failure");
|
||||
|
||||
let d_group = cands.get(0).expect("Unable to access group.");
|
||||
assert!(d_group.get_ava_set("dynmember").is_none());
|
||||
assert!(d_group.get_ava_set(Attribute::DynMember).is_none());
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -723,7 +722,7 @@ mod tests {
|
|||
|
||||
let d_group = cands.get(0).expect("Unable to access group.");
|
||||
let members = d_group
|
||||
.get_ava_set("dynmember")
|
||||
.get_ava_set(Attribute::DynMember)
|
||||
.expect("No members on dyn group");
|
||||
// We assert to refer single here because we should have "removed" uuid_admin being added
|
||||
// at all.
|
||||
|
@ -776,7 +775,7 @@ mod tests {
|
|||
|
||||
let d_group = cands.get(0).expect("Unable to access group.");
|
||||
let members = d_group
|
||||
.get_ava_set("dynmember")
|
||||
.get_ava_set(Attribute::DynMember)
|
||||
.expect("No members on dyn group");
|
||||
// We assert to refer single here because we should have re-added the members
|
||||
assert!(members.to_refer_single() == Some(UUID_TEST_GROUP));
|
||||
|
@ -831,7 +830,7 @@ mod tests {
|
|||
|
||||
let d_group = cands.get(0).expect("Unable to access group.");
|
||||
let members = d_group
|
||||
.get_ava_set("dynmember")
|
||||
.get_ava_set(Attribute::DynMember)
|
||||
.expect("No members on dyn group");
|
||||
|
||||
assert!(members.to_refer_single() == Some(UUID_TEST_GROUP));
|
||||
|
@ -882,7 +881,7 @@ mod tests {
|
|||
.expect("Internal search failure");
|
||||
|
||||
let d_group = cands.get(0).expect("Unable to access group.");
|
||||
assert!(d_group.get_ava_set("dynmember").is_none());
|
||||
assert!(d_group.get_ava_set(Attribute::DynMember).is_none());
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -925,7 +924,7 @@ mod tests {
|
|||
.expect("Internal search failure");
|
||||
|
||||
let d_group = cands.get(0).expect("Unable to access group.");
|
||||
assert!(d_group.get_ava_set("dynmember").is_none());
|
||||
assert!(d_group.get_ava_set(Attribute::DynMember).is_none());
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -972,7 +971,7 @@ mod tests {
|
|||
.expect("Internal search failure");
|
||||
|
||||
let d_group = cands.get(0).expect("Unable to access group.");
|
||||
assert!(d_group.get_ava_set("memberof").is_none());
|
||||
assert!(d_group.get_ava_set(Attribute::MemberOf).is_none());
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -23,19 +23,21 @@ impl EcdhKeyGen {
|
|||
cands: &mut [Entry<EntryInvalid, STATE>],
|
||||
) -> Result<(), OperationError> {
|
||||
for cand in cands.iter_mut() {
|
||||
if cand.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
&EntryClass::Person.to_partialvalue(),
|
||||
) && !cand.attribute_pres(Attribute::IdVerificationEcKey.into())
|
||||
if cand.attribute_equality(Attribute::Class, &EntryClass::Person.to_partialvalue())
|
||||
&& !cand.attribute_pres(Attribute::IdVerificationEcKey)
|
||||
{
|
||||
debug!("Generating idv_eckey for {}", cand.get_display_id());
|
||||
debug!(
|
||||
"Generating {} for {}",
|
||||
Attribute::IdVerificationEcKey,
|
||||
cand.get_display_id()
|
||||
);
|
||||
|
||||
let new_private_key = EcKey::generate(&DEFAULT_KEY_GROUP).map_err(|e| {
|
||||
error!(err = ?e, "Unable to generate id verification ECDH private key");
|
||||
OperationError::CryptographyError
|
||||
})?;
|
||||
cand.add_ava_if_not_exist(
|
||||
Attribute::IdVerificationEcKey.into(),
|
||||
Attribute::IdVerificationEcKey,
|
||||
crate::value::Value::EcKeyPrivate(new_private_key),
|
||||
)
|
||||
}
|
||||
|
@ -113,7 +115,7 @@ mod tests {
|
|||
let e = qs.internal_search_uuid(uuid).expect("failed to get entry");
|
||||
|
||||
let key = e
|
||||
.get_ava_single_eckey_private(Attribute::IdVerificationEcKey.into())
|
||||
.get_ava_single_eckey_private(Attribute::IdVerificationEcKey)
|
||||
.expect("unable to retrieve the ecdh key");
|
||||
|
||||
assert!(key.check_key().is_ok())
|
||||
|
@ -178,15 +180,15 @@ mod tests {
|
|||
Ok(()),
|
||||
preload,
|
||||
filter!(f_eq(Attribute::Name, PartialValue::new_iname("test_name"))),
|
||||
modlist!([m_purge(Attribute::IdVerificationEcKey.into())]),
|
||||
modlist!([m_purge(Attribute::IdVerificationEcKey)]),
|
||||
None,
|
||||
|_| {},
|
||||
|qs: &mut QueryServerWriteTransaction| {
|
||||
let e = qs.internal_search_uuid(uuid).expect("failed to get entry");
|
||||
|
||||
assert!(
|
||||
!e.attribute_equality(Attribute::IdVerificationEcKey.into(), &key_partialvalue)
|
||||
&& e.attribute_pres(Attribute::IdVerificationEcKey.into())
|
||||
!e.attribute_equality(Attribute::IdVerificationEcKey, &key_partialvalue)
|
||||
&& e.attribute_pres(Attribute::IdVerificationEcKey)
|
||||
)
|
||||
}
|
||||
);
|
||||
|
@ -218,18 +220,15 @@ mod tests {
|
|||
Ok(()),
|
||||
preload,
|
||||
filter!(f_eq(Attribute::Name, PartialValue::new_iname("test_name"))),
|
||||
modlist!([m_remove(
|
||||
Attribute::IdVerificationEcKey.into(),
|
||||
&key_partialvalue
|
||||
)]),
|
||||
modlist!([m_remove(Attribute::IdVerificationEcKey, &key_partialvalue)]),
|
||||
None,
|
||||
|_| {},
|
||||
|qs: &mut QueryServerWriteTransaction| {
|
||||
let e = qs.internal_search_uuid(uuid).expect("failed to get entry");
|
||||
|
||||
assert!(
|
||||
!e.attribute_equality(Attribute::IdVerificationEcKey.into(), &key_partialvalue)
|
||||
&& e.attribute_pres(Attribute::IdVerificationEcKey.into())
|
||||
!e.attribute_equality(Attribute::IdVerificationEcKey, &key_partialvalue)
|
||||
&& e.attribute_pres(Attribute::IdVerificationEcKey)
|
||||
)
|
||||
}
|
||||
);
|
||||
|
|
|
@ -20,9 +20,9 @@ const GID_SAFETY_NUMBER_MIN: u32 = 1000;
|
|||
pub struct GidNumber {}
|
||||
|
||||
fn apply_gidnumber<T: Clone>(e: &mut Entry<EntryInvalid, T>) -> Result<(), OperationError> {
|
||||
if (e.attribute_equality(Attribute::Class.as_ref(), &EntryClass::PosixGroup.into())
|
||||
|| e.attribute_equality(Attribute::Class.into(), &EntryClass::PosixAccount.into()))
|
||||
&& !e.attribute_pres(Attribute::GidNumber.as_ref())
|
||||
if (e.attribute_equality(Attribute::Class, &EntryClass::PosixGroup.into())
|
||||
|| e.attribute_equality(Attribute::Class, &EntryClass::PosixAccount.into()))
|
||||
&& !e.attribute_pres(Attribute::GidNumber)
|
||||
{
|
||||
let u_ref = e
|
||||
.get_uuid()
|
||||
|
@ -36,19 +36,25 @@ fn apply_gidnumber<T: Clone>(e: &mut Entry<EntryInvalid, T>) -> Result<(), Opera
|
|||
// assert the value is greater than the system range.
|
||||
if gid < GID_SYSTEM_NUMBER_MIN {
|
||||
return Err(OperationError::InvalidAttribute(format!(
|
||||
"gidnumber {gid} may overlap with system range {GID_SYSTEM_NUMBER_MIN}"
|
||||
"{} {} may overlap with system range {}",
|
||||
Attribute::GidNumber,
|
||||
gid,
|
||||
GID_SYSTEM_NUMBER_MIN
|
||||
)));
|
||||
}
|
||||
|
||||
let gid_v = Value::new_uint32(gid);
|
||||
admin_info!("Generated {} for {:?}", gid, u_ref);
|
||||
e.set_ava(Attribute::GidNumber.as_ref(), once(gid_v));
|
||||
e.set_ava(Attribute::GidNumber, once(gid_v));
|
||||
Ok(())
|
||||
} else if let Some(gid) = e.get_ava_single_uint32(Attribute::GidNumber.as_ref()) {
|
||||
} else if let Some(gid) = e.get_ava_single_uint32(Attribute::GidNumber) {
|
||||
// If they provided us with a gid number, ensure it's in a safe range.
|
||||
if gid <= GID_SAFETY_NUMBER_MIN {
|
||||
Err(OperationError::InvalidAttribute(format!(
|
||||
"gidnumber {gid} overlaps into system secure range {GID_SAFETY_NUMBER_MIN}"
|
||||
"{} {} overlaps into system secure range {}",
|
||||
Attribute::GidNumber,
|
||||
gid,
|
||||
GID_SAFETY_NUMBER_MIN
|
||||
)))
|
||||
} else {
|
||||
Ok(())
|
||||
|
@ -100,7 +106,7 @@ mod tests {
|
|||
fn check_gid(qs_write: &mut QueryServerWriteTransaction, uuid: &str, gid: u32) {
|
||||
let u = Uuid::parse_str(uuid).unwrap();
|
||||
let e = qs_write.internal_search_uuid(u).unwrap();
|
||||
let gidnumber = e.get_ava_single(Attribute::GidNumber.as_ref()).unwrap();
|
||||
let gidnumber = e.get_ava_single(Attribute::GidNumber).unwrap();
|
||||
let ex_gid = Value::new_uint32(gid);
|
||||
assert!(ex_gid == gidnumber);
|
||||
}
|
||||
|
@ -188,7 +194,7 @@ mod tests {
|
|||
Ok(()),
|
||||
preload,
|
||||
filter!(f_eq(Attribute::Name, PartialValue::new_iname("testperson"))),
|
||||
modlist!([m_pres("class", &EntryClass::PosixGroup.into())]),
|
||||
modlist!([m_pres(Attribute::Class, &EntryClass::PosixGroup.into())]),
|
||||
None,
|
||||
|_| {},
|
||||
|qs_write: &mut QueryServerWriteTransaction| check_gid(
|
||||
|
@ -220,7 +226,7 @@ mod tests {
|
|||
Ok(()),
|
||||
preload,
|
||||
filter!(f_eq(Attribute::Name, PartialValue::new_iname("testperson"))),
|
||||
modlist!([m_purge(Attribute::GidNumber.as_ref())]),
|
||||
modlist!([m_purge(Attribute::GidNumber)]),
|
||||
None,
|
||||
|_| {},
|
||||
|qs_write: &mut QueryServerWriteTransaction| check_gid(
|
||||
|
@ -253,8 +259,8 @@ mod tests {
|
|||
preload,
|
||||
filter!(f_eq(Attribute::Name, PartialValue::new_iname("testperson"))),
|
||||
modlist!([
|
||||
m_purge(Attribute::GidNumber.as_ref()),
|
||||
m_pres(Attribute::GidNumber.as_ref(), &Value::new_uint32(2000))
|
||||
m_purge(Attribute::GidNumber),
|
||||
m_pres(Attribute::GidNumber, &Value::new_uint32(2000))
|
||||
]),
|
||||
None,
|
||||
|_| {},
|
||||
|
|
|
@ -46,21 +46,21 @@ impl Plugin for JwsKeygen {
|
|||
impl JwsKeygen {
|
||||
fn modify_inner<T: Clone>(cand: &mut [Entry<EntryInvalid, T>]) -> Result<(), OperationError> {
|
||||
cand.iter_mut().try_for_each(|e| {
|
||||
if e.attribute_equality(Attribute::Class.as_ref(), &EntryClass::OAuth2ResourceServerBasic.into()) &&
|
||||
!e.attribute_pres(Attribute::OAuth2RsBasicSecret.into()) {
|
||||
if e.attribute_equality(Attribute::Class, &EntryClass::OAuth2ResourceServerBasic.into()) &&
|
||||
!e.attribute_pres(Attribute::OAuth2RsBasicSecret) {
|
||||
security_info!("regenerating oauth2 basic secret");
|
||||
let v = Value::SecretValue(password_from_random());
|
||||
e.add_ava(Attribute::OAuth2RsBasicSecret, v);
|
||||
}
|
||||
|
||||
if e.attribute_equality(Attribute::Class.as_ref(), &EntryClass::OAuth2ResourceServer.into()) {
|
||||
if !e.attribute_pres(Attribute::OAuth2RsTokenKey.as_ref()) {
|
||||
if e.attribute_equality(Attribute::Class, &EntryClass::OAuth2ResourceServer.into()) {
|
||||
if !e.attribute_pres(Attribute::OAuth2RsTokenKey) {
|
||||
security_info!("regenerating oauth2 token key");
|
||||
let k = fernet::Fernet::generate_key();
|
||||
let v = Value::new_secret_str(&k);
|
||||
e.add_ava(Attribute::OAuth2RsTokenKey, v);
|
||||
}
|
||||
if !e.attribute_pres(Attribute::Es256PrivateKeyDer.as_ref()) {
|
||||
if !e.attribute_pres(Attribute::Es256PrivateKeyDer) {
|
||||
security_info!("regenerating oauth2 es256 private key");
|
||||
let der = JwsSigner::generate_es256()
|
||||
.and_then(|jws| jws.private_key_to_der())
|
||||
|
@ -71,8 +71,8 @@ impl JwsKeygen {
|
|||
let v = Value::new_privatebinary(&der);
|
||||
e.add_ava(Attribute::Es256PrivateKeyDer, v);
|
||||
}
|
||||
if e.get_ava_single_bool(Attribute::OAuth2JwtLegacyCryptoEnable.as_ref()).unwrap_or(false)
|
||||
&& !e.attribute_pres(Attribute::Rs256PrivateKeyDer.into()) {
|
||||
if e.get_ava_single_bool(Attribute::OAuth2JwtLegacyCryptoEnable).unwrap_or(false)
|
||||
&& !e.attribute_pres(Attribute::Rs256PrivateKeyDer) {
|
||||
security_info!("regenerating oauth2 legacy rs256 private key");
|
||||
let der = JwsSigner::generate_legacy_rs256()
|
||||
.and_then(|jws| jws.private_key_to_der())
|
||||
|
@ -85,9 +85,9 @@ impl JwsKeygen {
|
|||
}
|
||||
}
|
||||
|
||||
if (e.attribute_equality(Attribute::Class.as_ref(), &EntryClass::ServiceAccount.into()) ||
|
||||
e.attribute_equality(Attribute::Class.as_ref(), &EntryClass::SyncAccount.into())) &&
|
||||
!e.attribute_pres(Attribute::JwsEs256PrivateKey.as_ref()) {
|
||||
if (e.attribute_equality(Attribute::Class, &EntryClass::ServiceAccount.into()) ||
|
||||
e.attribute_equality(Attribute::Class, &EntryClass::SyncAccount.into())) &&
|
||||
!e.attribute_pres(Attribute::JwsEs256PrivateKey) {
|
||||
security_info!("regenerating jws es256 private key");
|
||||
let jwssigner = JwsSigner::generate_es256()
|
||||
.map_err(|e| {
|
||||
|
@ -156,8 +156,8 @@ mod tests {
|
|||
let e = qs
|
||||
.internal_search_uuid(uuid)
|
||||
.expect("failed to get oauth2 config");
|
||||
assert!(e.attribute_pres("oauth2_rs_basic_secret"));
|
||||
assert!(e.attribute_pres("oauth2_rs_token_key"));
|
||||
assert!(e.attribute_pres(Attribute::OAuth2RsBasicSecret));
|
||||
assert!(e.attribute_pres(Attribute::OAuth2RsTokenKey));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -220,11 +220,11 @@ mod tests {
|
|||
let e = qs
|
||||
.internal_search_uuid(uuid)
|
||||
.expect("failed to get oauth2 config");
|
||||
assert!(e.attribute_pres("oauth2_rs_basic_secret"));
|
||||
assert!(e.attribute_pres("oauth2_rs_token_key"));
|
||||
assert!(e.attribute_pres(Attribute::OAuth2RsBasicSecret));
|
||||
assert!(e.attribute_pres(Attribute::OAuth2RsTokenKey));
|
||||
// Check the values are different.
|
||||
assert!(e.get_ava_single_secret("oauth2_rs_basic_secret") != Some("12345"));
|
||||
assert!(e.get_ava_single_secret("oauth2_rs_token_key") != Some("12345"));
|
||||
assert!(e.get_ava_single_secret(Attribute::OAuth2RsBasicSecret) != Some("12345"));
|
||||
assert!(e.get_ava_single_secret(Attribute::OAuth2RsTokenKey) != Some("12345"));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
|
@ -44,11 +44,11 @@ fn do_memberof(
|
|||
})?;
|
||||
|
||||
// Ensure we are MO capable. We only add this if it's not already present.
|
||||
tgte.add_ava_if_not_exist(Attribute::Class.as_ref(), EntryClass::MemberOf.into());
|
||||
tgte.add_ava_if_not_exist(Attribute::Class, EntryClass::MemberOf.into());
|
||||
// Clear the dmo + mos, we will recreate them now.
|
||||
// This is how we handle deletes/etc.
|
||||
tgte.purge_ava("memberof");
|
||||
tgte.purge_ava("directmemberof");
|
||||
tgte.purge_ava(Attribute::MemberOf);
|
||||
tgte.purge_ava(Attribute::DirectMemberOf);
|
||||
|
||||
// What are our direct and indirect mos?
|
||||
let dmo = ValueSetRefer::from_iter(groups.iter().map(|g| g.get_uuid()));
|
||||
|
@ -57,7 +57,7 @@ fn do_memberof(
|
|||
groups
|
||||
.iter()
|
||||
.filter_map(|g| {
|
||||
g.get_ava_set("memberof")
|
||||
g.get_ava_set(Attribute::MemberOf)
|
||||
.and_then(|s| s.as_refer_set())
|
||||
.map(|s| s.iter())
|
||||
})
|
||||
|
@ -68,7 +68,7 @@ fn do_memberof(
|
|||
// Add all the direct mo's and mos.
|
||||
if let Some(dmo) = dmo {
|
||||
// We need to clone this else type checker gets real sad.
|
||||
tgte.set_ava_set("directmemberof", dmo.clone());
|
||||
tgte.set_ava_set(Attribute::DirectMemberOf, dmo.clone());
|
||||
|
||||
if let Some(mo) = &mut mo {
|
||||
let dmo = dmo as ValueSet;
|
||||
|
@ -81,18 +81,18 @@ fn do_memberof(
|
|||
};
|
||||
|
||||
if let Some(mo) = mo {
|
||||
tgte.set_ava_set("memberof", mo);
|
||||
tgte.set_ava_set(Attribute::MemberOf, mo);
|
||||
}
|
||||
|
||||
trace!(
|
||||
"Updating {:?} to be dir mo {:?}",
|
||||
uuid,
|
||||
tgte.get_ava_set("directmemberof")
|
||||
tgte.get_ava_set(Attribute::DirectMemberOf)
|
||||
);
|
||||
trace!(
|
||||
"Updating {:?} to be mo {:?}",
|
||||
uuid,
|
||||
tgte.get_ava_set("memberof")
|
||||
tgte.get_ava_set(Attribute::MemberOf)
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ fn apply_memberof(
|
|||
for (pre, mut tgte) in work_set.into_iter() {
|
||||
let guuid = pre.get_uuid();
|
||||
// load the entry from the db.
|
||||
if !tgte.attribute_equality(Attribute::Class.as_ref(), &EntryClass::Group.into()) {
|
||||
if !tgte.attribute_equality(Attribute::Class, &EntryClass::Group.into()) {
|
||||
// It's not a group, we'll deal with you later. We should NOT
|
||||
// have seen this UUID before, as either we are on the first
|
||||
// iteration OR the checks belowe should have filtered it out.
|
||||
|
@ -147,8 +147,9 @@ fn apply_memberof(
|
|||
do_memberof(qs, guuid, &mut tgte)?;
|
||||
|
||||
// Did we change? Note we don't check if the class changed, only if mo changed.
|
||||
if pre.get_ava_set("memberof") != tgte.get_ava_set("memberof")
|
||||
|| pre.get_ava_set("directmemberof") != tgte.get_ava_set("directmemberof")
|
||||
if pre.get_ava_set(Attribute::MemberOf) != tgte.get_ava_set(Attribute::MemberOf)
|
||||
|| pre.get_ava_set(Attribute::DirectMemberOf)
|
||||
!= tgte.get_ava_set(Attribute::DirectMemberOf)
|
||||
{
|
||||
// Yes we changed - we now must process all our members, as they need to
|
||||
// inherit changes. Some of these members COULD be non groups, but we
|
||||
|
@ -157,10 +158,10 @@ fn apply_memberof(
|
|||
"{:?} changed, flagging members as groups to change. ",
|
||||
guuid
|
||||
);
|
||||
if let Some(miter) = tgte.get_ava_as_refuuid("member") {
|
||||
if let Some(miter) = tgte.get_ava_as_refuuid(Attribute::Member) {
|
||||
group_affect.extend(miter.filter(|m| !other_cache.contains_key(m)));
|
||||
};
|
||||
if let Some(miter) = tgte.get_ava_as_refuuid("dynmember") {
|
||||
if let Some(miter) = tgte.get_ava_as_refuuid(Attribute::DynMember) {
|
||||
group_affect.extend(miter.filter(|m| !other_cache.contains_key(m)));
|
||||
};
|
||||
|
||||
|
@ -188,13 +189,12 @@ fn apply_memberof(
|
|||
.into_iter()
|
||||
.try_for_each(|(auuid, (pre, mut tgte))| {
|
||||
trace!("=> processing affected uuid {:?}", auuid);
|
||||
debug_assert!(
|
||||
!tgte.attribute_equality(Attribute::Class.as_ref(), &EntryClass::Group.into())
|
||||
);
|
||||
debug_assert!(!tgte.attribute_equality(Attribute::Class, &EntryClass::Group.into()));
|
||||
do_memberof(qs, auuid, &mut tgte)?;
|
||||
// Only write if a change occurred.
|
||||
if pre.get_ava_set("memberof") != tgte.get_ava_set("memberof")
|
||||
|| pre.get_ava_set("directmemberof") != tgte.get_ava_set("directmemberof")
|
||||
if pre.get_ava_set(Attribute::MemberOf) != tgte.get_ava_set(Attribute::MemberOf)
|
||||
|| pre.get_ava_set(Attribute::DirectMemberOf)
|
||||
!= tgte.get_ava_set(Attribute::DirectMemberOf)
|
||||
{
|
||||
changes.push((pre, tgte));
|
||||
}
|
||||
|
@ -209,7 +209,7 @@ fn apply_memberof(
|
|||
|
||||
impl Plugin for MemberOf {
|
||||
fn id() -> &'static str {
|
||||
"memberof"
|
||||
Attribute::MemberOf.as_ref()
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", name = "memberof_post_create", skip(qs, cand, ce))]
|
||||
|
@ -275,8 +275,8 @@ impl Plugin for MemberOf {
|
|||
.iter()
|
||||
.filter_map(|e| {
|
||||
// Is it a group?
|
||||
if e.attribute_equality(Attribute::Class.as_ref(), &EntryClass::Group.into()) {
|
||||
e.get_ava_as_refuuid("member")
|
||||
if e.attribute_equality(Attribute::Class, &EntryClass::Group.into()) {
|
||||
e.get_ava_as_refuuid(Attribute::Member)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -286,11 +286,8 @@ impl Plugin for MemberOf {
|
|||
// Or a dyn group?
|
||||
cand.iter()
|
||||
.filter_map(|post| {
|
||||
if post.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
&EntryClass::DynGroup.into(),
|
||||
) {
|
||||
post.get_ava_as_refuuid("dynmember")
|
||||
if post.attribute_equality(Attribute::Class, &EntryClass::DynGroup.into()) {
|
||||
post.get_ava_as_refuuid(Attribute::DynMember)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -347,7 +344,7 @@ impl Plugin for MemberOf {
|
|||
|
||||
trace!("DMO search groups {:?} -> {:?}", e.get_uuid(), d_groups_set);
|
||||
|
||||
match (e.get_ava_set("directmemberof"), d_groups_set) {
|
||||
match (e.get_ava_set(Attribute::DirectMemberOf), d_groups_set) {
|
||||
(Some(edmos), Some(b)) => {
|
||||
// Can they both be reference sets?
|
||||
match edmos.as_refer_set() {
|
||||
|
@ -414,11 +411,8 @@ impl MemberOf {
|
|||
cand.iter()
|
||||
.filter_map(|e| {
|
||||
// Is it a group?
|
||||
if e.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
&EntryClass::Group.into(),
|
||||
) {
|
||||
e.get_ava_as_refuuid("member")
|
||||
if e.attribute_equality(Attribute::Class, &EntryClass::Group.into()) {
|
||||
e.get_ava_as_refuuid(Attribute::Member)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -447,11 +441,8 @@ impl MemberOf {
|
|||
pre_cand
|
||||
.iter()
|
||||
.filter_map(|pre| {
|
||||
if pre.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
&EntryClass::Group.into(),
|
||||
) {
|
||||
pre.get_ava_as_refuuid("member")
|
||||
if pre.attribute_equality(Attribute::Class, &EntryClass::Group.into()) {
|
||||
pre.get_ava_as_refuuid(Attribute::Member)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -461,11 +452,8 @@ impl MemberOf {
|
|||
.chain(
|
||||
cand.iter()
|
||||
.filter_map(|post| {
|
||||
if post.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
&EntryClass::Group.into(),
|
||||
) {
|
||||
post.get_ava_as_refuuid("member")
|
||||
if post.attribute_equality(Attribute::Class, &EntryClass::Group.into()) {
|
||||
post.get_ava_as_refuuid(Attribute::Member)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
|
@ -16,17 +16,23 @@ lazy_static! {
|
|||
// it contains all the partialvalues used to match against an Entry's class,
|
||||
// we just need a partialvalue to match in order to target the entry
|
||||
static ref CLASSES_TO_UPDATE: [PartialValue; 1] = [PartialValue::new_iutf8(EntryClass::Account.into())];
|
||||
static ref HISTORY_ATTRIBUTES: [&'static str;1] = [Attribute::Name.as_ref()];
|
||||
}
|
||||
|
||||
const HISTORY_ATTRIBUTES: [Attribute; 1] = [Attribute::Name];
|
||||
|
||||
#[test]
|
||||
fn test_history_attribute() {
|
||||
assert_eq!(NameHistory::get_ava_name(Attribute::Name), "name_history");
|
||||
}
|
||||
|
||||
impl NameHistory {
|
||||
fn is_entry_to_update<VALUE, STATE>(entry: &mut Entry<VALUE, STATE>) -> bool {
|
||||
CLASSES_TO_UPDATE
|
||||
.iter()
|
||||
.any(|pv| entry.attribute_equality(Attribute::Class.as_ref(), pv))
|
||||
.any(|pv| entry.attribute_equality(Attribute::Class, pv))
|
||||
}
|
||||
|
||||
fn get_ava_name(history_attr: &str) -> String {
|
||||
fn get_ava_name(history_attr: Attribute) -> String {
|
||||
format!("{}_history", history_attr)
|
||||
}
|
||||
|
||||
|
@ -38,7 +44,7 @@ impl NameHistory {
|
|||
for (pre, post) in pre_cand.iter().zip(cand) {
|
||||
// here we check if the current entry has at least one of the classes we intend to target
|
||||
if Self::is_entry_to_update(post) {
|
||||
for history_attr in HISTORY_ATTRIBUTES.iter() {
|
||||
for history_attr in HISTORY_ATTRIBUTES.into_iter() {
|
||||
let pre_name_option = pre.get_ava_single(history_attr);
|
||||
let post_name_option = post.get_ava_single(history_attr);
|
||||
if let (Some(pre_name), Some(post_name)) = (pre_name_option, post_name_option) {
|
||||
|
@ -49,7 +55,7 @@ impl NameHistory {
|
|||
// as of now we're interested just in the name so we use Iname
|
||||
match post_name {
|
||||
Value::Iname(n) => post.add_ava_if_not_exist(
|
||||
&ava_name,
|
||||
ava_name.try_into()?,
|
||||
Value::AuditLogString(cid.clone(), n),
|
||||
),
|
||||
_ => return Err(OperationError::InvalidValueState),
|
||||
|
@ -68,12 +74,12 @@ impl NameHistory {
|
|||
) -> Result<(), OperationError> {
|
||||
for cand in cands.iter_mut() {
|
||||
if Self::is_entry_to_update(cand) {
|
||||
for history_attr in HISTORY_ATTRIBUTES.iter() {
|
||||
for history_attr in HISTORY_ATTRIBUTES.into_iter() {
|
||||
if let Some(name) = cand.get_ava_single(history_attr) {
|
||||
let ava_name = Self::get_ava_name(history_attr);
|
||||
match name {
|
||||
Value::Iname(n) => cand.add_ava_if_not_exist(
|
||||
&ava_name,
|
||||
ava_name.try_into()?,
|
||||
Value::AuditLogString(cid.clone(), n),
|
||||
),
|
||||
_ => return Err(OperationError::InvalidValueState),
|
||||
|
@ -158,8 +164,8 @@ mod tests {
|
|||
preload,
|
||||
filter!(f_eq(Attribute::Name, PartialValue::new_iname("old_name"))),
|
||||
modlist!([
|
||||
m_purge(Attribute::Name.as_ref()),
|
||||
m_pres(Attribute::Name.as_ref(), &Value::new_iname("new_name_1"))
|
||||
m_purge(Attribute::Name),
|
||||
m_pres(Attribute::Name, &Value::new_iname("new_name_1"))
|
||||
]),
|
||||
None,
|
||||
|_| {},
|
||||
|
@ -168,7 +174,7 @@ mod tests {
|
|||
.internal_search_uuid(uuid!("d2b496bd-8493-47b7-8142-f568b5cf47ee"))
|
||||
.expect("failed to get entry");
|
||||
let c = e
|
||||
.get_ava_set(Attribute::NameHistory.as_ref())
|
||||
.get_ava_set(Attribute::NameHistory)
|
||||
.expect("failed to get primary cred.");
|
||||
trace!("{:?}", c.clone());
|
||||
assert!(
|
||||
|
@ -206,7 +212,7 @@ mod tests {
|
|||
.expect("failed to get entry");
|
||||
trace!("{:?}", e.get_ava());
|
||||
let name_history = e
|
||||
.get_ava_set(Attribute::NameHistory.as_ref())
|
||||
.get_ava_set(Attribute::NameHistory)
|
||||
.expect("failed to get name_history ava");
|
||||
|
||||
assert!(name_history.contains(&PartialValue::new_utf8s("old_name")))
|
||||
|
@ -249,8 +255,8 @@ mod tests {
|
|||
preload,
|
||||
filter!(f_eq(Attribute::Name, PartialValue::new_iname("old_name8"))),
|
||||
modlist!([
|
||||
m_purge(Attribute::Name.as_ref()),
|
||||
m_pres(Attribute::Name.as_ref(), &Value::new_iname("new_name"))
|
||||
m_purge(Attribute::Name),
|
||||
m_pres(Attribute::Name, &Value::new_iname("new_name"))
|
||||
]),
|
||||
None,
|
||||
|_| {},
|
||||
|
@ -259,7 +265,7 @@ mod tests {
|
|||
.internal_search_uuid(uuid!("d2b496bd-8493-47b7-8142-f568b5cf47ee"))
|
||||
.expect("failed to get entry");
|
||||
let c = e
|
||||
.get_ava_set(Attribute::NameHistory.as_ref())
|
||||
.get_ava_set(Attribute::NameHistory)
|
||||
.expect("failed to get name_history ava :/");
|
||||
trace!(?c);
|
||||
assert!(
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
// may only have certain modifications performed.
|
||||
|
||||
use hashbrown::HashSet;
|
||||
use kanidm_proto::constants::{ATTR_MAY, ATTR_MUST};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::event::{CreateEvent, DeleteEvent, ModifyEvent};
|
||||
|
@ -17,23 +16,23 @@ pub struct Protected {}
|
|||
// schema will have checked this, and we don't allow class changes!
|
||||
|
||||
lazy_static! {
|
||||
static ref ALLOWED_ATTRS: HashSet<&'static str> = {
|
||||
static ref ALLOWED_ATTRS: HashSet<Attribute> = {
|
||||
let mut m = HashSet::with_capacity(16);
|
||||
// Allow modification of some schema class types to allow local extension
|
||||
// of schema types.
|
||||
//
|
||||
m.insert(ATTR_MUST);
|
||||
m.insert(ATTR_MAY);
|
||||
m.insert(Attribute::Must);
|
||||
m.insert(Attribute::May);
|
||||
// Allow modification of some domain info types for local configuration.
|
||||
m.insert("domain_ssid");
|
||||
m.insert("domain_ldap_basedn");
|
||||
m.insert("fernet_private_key_str");
|
||||
m.insert("es256_private_key_der");
|
||||
m.insert("id_verification_eckey");
|
||||
m.insert("badlist_password");
|
||||
m.insert("domain_display_name");
|
||||
m.insert("authsession_expiry");
|
||||
m.insert("privilege_expiry");
|
||||
m.insert(Attribute::DomainSsid);
|
||||
m.insert(Attribute::DomainLdapBasedn);
|
||||
m.insert(Attribute::FernetPrivateKeyStr);
|
||||
m.insert(Attribute::Es256PrivateKeyDer);
|
||||
m.insert(Attribute::IdVerificationEcKey);
|
||||
m.insert(Attribute::BadlistPassword);
|
||||
m.insert(Attribute::DomainDisplayName);
|
||||
m.insert(Attribute::AuthSessionExpiry);
|
||||
m.insert(Attribute::PrivilegeExpiry);
|
||||
m
|
||||
};
|
||||
}
|
||||
|
@ -56,14 +55,13 @@ impl Plugin for Protected {
|
|||
}
|
||||
|
||||
cand.iter().try_fold((), |(), cand| {
|
||||
if cand.attribute_equality(Attribute::Class.as_ref(), &EntryClass::System.into())
|
||||
|| cand.attribute_equality(Attribute::Class.into(), &EntryClass::DomainInfo.into())
|
||||
|| cand.attribute_equality(Attribute::Class.into(), &EntryClass::SystemInfo.into())
|
||||
|| cand
|
||||
.attribute_equality(Attribute::Class.into(), &EntryClass::SystemConfig.into())
|
||||
|| cand.attribute_equality(Attribute::Class.as_ref(), &EntryClass::Tombstone.into())
|
||||
|| cand.attribute_equality(Attribute::Class.as_ref(), &EntryClass::Recycled.into())
|
||||
|| cand.attribute_equality(Attribute::Class.as_ref(), &EntryClass::DynGroup.into())
|
||||
if cand.attribute_equality(Attribute::Class, &EntryClass::System.into())
|
||||
|| cand.attribute_equality(Attribute::Class, &EntryClass::DomainInfo.into())
|
||||
|| cand.attribute_equality(Attribute::Class, &EntryClass::SystemInfo.into())
|
||||
|| 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())
|
||||
{
|
||||
Err(OperationError::SystemProtectedObject)
|
||||
} else {
|
||||
|
@ -86,7 +84,7 @@ impl Plugin for Protected {
|
|||
// Prevent adding class: system, domain_info, tombstone, or recycled.
|
||||
me.modlist.iter().try_fold((), |(), m| match m {
|
||||
Modify::Present(a, v) => {
|
||||
if a == "class"
|
||||
if a == Attribute::Class.as_ref()
|
||||
&& (v == &EntryClass::System.to_value()
|
||||
|| v == &EntryClass::DomainInfo.to_value()
|
||||
|| v == &EntryClass::SystemInfo.into()
|
||||
|
@ -107,9 +105,9 @@ impl Plugin for Protected {
|
|||
// HARD block mods on tombstone or recycle. We soft block on the rest as they may
|
||||
// have some allowed attrs.
|
||||
cand.iter().try_fold((), |(), cand| {
|
||||
if cand.attribute_equality(Attribute::Class.as_ref(), &EntryClass::Tombstone.into())
|
||||
|| cand.attribute_equality(Attribute::Class.as_ref(), &EntryClass::Recycled.into())
|
||||
|| cand.attribute_equality(Attribute::Class.as_ref(), &EntryClass::DynGroup.into())
|
||||
if cand.attribute_equality(Attribute::Class, &EntryClass::Tombstone.into())
|
||||
|| cand.attribute_equality(Attribute::Class, &EntryClass::Recycled.into())
|
||||
|| cand.attribute_equality(Attribute::Class, &EntryClass::DynGroup.into())
|
||||
{
|
||||
Err(OperationError::SystemProtectedObject)
|
||||
} else {
|
||||
|
@ -121,7 +119,7 @@ impl Plugin for Protected {
|
|||
let system_pres = cand.iter().any(|c| {
|
||||
// We don't need to check for domain info here because domain_info has a class
|
||||
// system also. We just need to block it from being created.
|
||||
c.attribute_equality(Attribute::Class.as_ref(), &EntryClass::System.into())
|
||||
c.attribute_equality(Attribute::Class, &EntryClass::System.into())
|
||||
});
|
||||
|
||||
trace!("class: system -> {}", system_pres);
|
||||
|
@ -131,16 +129,17 @@ impl Plugin for Protected {
|
|||
}
|
||||
|
||||
// Something altered is system, check if it's allowed.
|
||||
me.modlist.iter().try_fold((), |(), m| {
|
||||
me.modlist.into_iter().try_fold((), |(), m| {
|
||||
// Already hit an error, move on.
|
||||
let a = match m {
|
||||
Modify::Present(a, _) | Modify::Removed(a, _) | Modify::Purged(a) => Some(a),
|
||||
Modify::Assert(_, _) => None,
|
||||
};
|
||||
if let Some(a) = a {
|
||||
match ALLOWED_ATTRS.get(a.as_str()) {
|
||||
Some(_) => Ok(()),
|
||||
None => Err(OperationError::SystemProtectedObject),
|
||||
let attr: Attribute = a.try_into()?;
|
||||
match ALLOWED_ATTRS.contains(&attr) {
|
||||
true => Ok(()),
|
||||
false => Err(OperationError::SystemProtectedObject),
|
||||
}
|
||||
} else {
|
||||
// Was not a mod needing checking
|
||||
|
@ -165,10 +164,10 @@ impl Plugin for Protected {
|
|||
.flat_map(|ml| ml.iter())
|
||||
.try_fold((), |(), m| match m {
|
||||
Modify::Present(a, v) => {
|
||||
if a == "class"
|
||||
if a == Attribute::Class.as_ref()
|
||||
&& (v == &EntryClass::System.to_value()
|
||||
|| v == &EntryClass::DomainInfo.to_value()
|
||||
|| v == &(EntryClass::SystemInfo.to_value())
|
||||
|| v == &EntryClass::SystemInfo.to_value()
|
||||
|| v == &EntryClass::SystemConfig.to_value()
|
||||
|| v == &EntryClass::DynGroup.to_value()
|
||||
|| v == &EntryClass::SyncObject.to_value()
|
||||
|
@ -186,9 +185,9 @@ impl Plugin for Protected {
|
|||
// HARD block mods on tombstone or recycle. We soft block on the rest as they may
|
||||
// have some allowed attrs.
|
||||
cand.iter().try_fold((), |(), cand| {
|
||||
if cand.attribute_equality(Attribute::Class.as_ref(), &EntryClass::Tombstone.into())
|
||||
|| cand.attribute_equality(Attribute::Class.as_ref(), &EntryClass::Recycled.into())
|
||||
|| cand.attribute_equality(Attribute::Class.as_ref(), &EntryClass::DynGroup.into())
|
||||
if cand.attribute_equality(Attribute::Class, &EntryClass::Tombstone.into())
|
||||
|| cand.attribute_equality(Attribute::Class, &EntryClass::Recycled.into())
|
||||
|| cand.attribute_equality(Attribute::Class, &EntryClass::DynGroup.into())
|
||||
{
|
||||
Err(OperationError::SystemProtectedObject)
|
||||
} else {
|
||||
|
@ -200,10 +199,10 @@ impl Plugin for Protected {
|
|||
let system_pres = cand.iter().any(|c| {
|
||||
// We don't need to check for domain info here because domain_info has a class
|
||||
// system also. We just need to block it from being created.
|
||||
c.attribute_equality(Attribute::Class.as_ref(), &EntryClass::System.into())
|
||||
c.attribute_equality(Attribute::Class, &EntryClass::System.into())
|
||||
});
|
||||
|
||||
trace!("class: system -> {}", system_pres);
|
||||
trace!("{}: system -> {}", Attribute::Class, system_pres);
|
||||
// No system types being altered, return.
|
||||
if !system_pres {
|
||||
return Ok(());
|
||||
|
@ -220,9 +219,10 @@ impl Plugin for Protected {
|
|||
Modify::Assert(_, _) => None,
|
||||
};
|
||||
if let Some(a) = a {
|
||||
match ALLOWED_ATTRS.get(a.as_str()) {
|
||||
Some(_) => Ok(()),
|
||||
None => Err(OperationError::SystemProtectedObject),
|
||||
let attr: Attribute = a.try_into()?;
|
||||
match ALLOWED_ATTRS.contains(&attr) {
|
||||
true => Ok(()),
|
||||
false => Err(OperationError::SystemProtectedObject),
|
||||
}
|
||||
} else {
|
||||
// Was not a mod needing checking
|
||||
|
@ -244,14 +244,13 @@ impl Plugin for Protected {
|
|||
}
|
||||
|
||||
cand.iter().try_fold((), |(), cand| {
|
||||
if cand.attribute_equality(Attribute::Class.as_ref(), &EntryClass::System.into())
|
||||
|| cand.attribute_equality(Attribute::Class.into(), &EntryClass::DomainInfo.into())
|
||||
|| cand.attribute_equality(Attribute::Class.into(), &EntryClass::SystemInfo.into())
|
||||
|| cand
|
||||
.attribute_equality(Attribute::Class.into(), &EntryClass::SystemConfig.into())
|
||||
|| cand.attribute_equality(Attribute::Class.as_ref(), &EntryClass::Tombstone.into())
|
||||
|| cand.attribute_equality(Attribute::Class.as_ref(), &EntryClass::Recycled.into())
|
||||
|| cand.attribute_equality(Attribute::Class.as_ref(), &EntryClass::DynGroup.into())
|
||||
if cand.attribute_equality(Attribute::Class, &EntryClass::System.into())
|
||||
|| cand.attribute_equality(Attribute::Class, &EntryClass::DomainInfo.into())
|
||||
|| cand.attribute_equality(Attribute::Class, &EntryClass::SystemInfo.into())
|
||||
|| 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())
|
||||
{
|
||||
Err(OperationError::SystemProtectedObject)
|
||||
} else {
|
||||
|
@ -329,19 +328,19 @@ mod tests {
|
|||
),
|
||||
(
|
||||
Attribute::AcpModifyRemovedAttr,
|
||||
Value::new_iutf8("domain_display_name")
|
||||
Attribute::DomainDisplayName.to_value()
|
||||
),
|
||||
(
|
||||
Attribute::AcpModifyRemovedAttr,
|
||||
Value::new_iutf8("domain_uuid")
|
||||
Attribute::DomainUuid.to_value()
|
||||
),
|
||||
(
|
||||
Attribute::AcpModifyRemovedAttr,
|
||||
Value::new_iutf8("domain_ssid")
|
||||
Attribute::DomainSsid.to_value()
|
||||
),
|
||||
(
|
||||
Attribute::AcpModifyRemovedAttr,
|
||||
Value::new_iutf8("fernet_private_key_str")
|
||||
Attribute::FernetPrivateKeyStr.to_value()
|
||||
),
|
||||
(
|
||||
Attribute::AcpModifyRemovedAttr,
|
||||
|
@ -360,23 +359,23 @@ mod tests {
|
|||
(Attribute::AcpModifyPresentAttr, Attribute::Must.to_value()),
|
||||
(
|
||||
Attribute::AcpModifyPresentAttr,
|
||||
Value::new_iutf8("domain_name")
|
||||
Attribute::DomainName.to_value()
|
||||
),
|
||||
(
|
||||
Attribute::AcpModifyPresentAttr,
|
||||
Value::new_iutf8("domain_display_name")
|
||||
Attribute::DomainDisplayName.to_value()
|
||||
),
|
||||
(
|
||||
Attribute::AcpModifyPresentAttr,
|
||||
Value::new_iutf8("domain_uuid")
|
||||
Attribute::DomainUuid.to_value()
|
||||
),
|
||||
(
|
||||
Attribute::AcpModifyPresentAttr,
|
||||
Value::new_iutf8("domain_ssid")
|
||||
Attribute::DomainSsid.to_value()
|
||||
),
|
||||
(
|
||||
Attribute::AcpModifyPresentAttr,
|
||||
Value::new_iutf8("fernet_private_key_str")
|
||||
Attribute::FernetPrivateKeyStr.to_value()
|
||||
),
|
||||
(
|
||||
Attribute::AcpModifyPresentAttr,
|
||||
|
@ -397,14 +396,14 @@ mod tests {
|
|||
(Attribute::AcpCreateAttr, Attribute::DomainName.to_value(),),
|
||||
(
|
||||
Attribute::AcpCreateAttr,
|
||||
Value::new_iutf8("domain_display_name")
|
||||
Attribute::DomainDisplayName.to_value()
|
||||
),
|
||||
(Attribute::AcpCreateAttr, Value::new_iutf8("domain_uuid")),
|
||||
(Attribute::AcpCreateAttr, Value::new_iutf8("domain_ssid")),
|
||||
(Attribute::AcpCreateAttr, Attribute::DomainUuid.to_value()),
|
||||
(Attribute::AcpCreateAttr, Attribute::DomainSsid.to_value()),
|
||||
(Attribute::AcpCreateAttr, Attribute::Uuid.to_value()),
|
||||
(
|
||||
Attribute::AcpCreateAttr,
|
||||
Value::new_iutf8("fernet_private_key_str")
|
||||
Attribute::FernetPrivateKeyStr.to_value()
|
||||
),
|
||||
(
|
||||
Attribute::AcpCreateAttr,
|
||||
|
@ -414,7 +413,7 @@ mod tests {
|
|||
Attribute::AcpCreateAttr,
|
||||
Attribute::PrivateCookieKey.to_value()
|
||||
),
|
||||
(Attribute::AcpCreateAttr, Value::new_iutf8("version"))
|
||||
(Attribute::AcpCreateAttr, Attribute::Version.to_value())
|
||||
);
|
||||
pub static ref PRELOAD: Vec<EntryInitNew> =
|
||||
vec![TEST_ACCOUNT.clone(), TEST_GROUP.clone(), ALLOW_ALL.clone()];
|
||||
|
@ -470,8 +469,8 @@ mod tests {
|
|||
preload,
|
||||
filter!(f_eq(Attribute::Name, PartialValue::new_iname("testperson"))),
|
||||
modlist!([
|
||||
m_purge("displayname"),
|
||||
m_pres("displayname", &Value::new_utf8s("system test")),
|
||||
m_purge(Attribute::DisplayName),
|
||||
m_pres(Attribute::DisplayName, &Value::new_utf8s("system test")),
|
||||
]),
|
||||
Some(E_TEST_ACCOUNT.clone()),
|
||||
|_| {},
|
||||
|
@ -501,8 +500,8 @@ mod tests {
|
|||
preload,
|
||||
filter!(f_eq(Attribute::ClassName, EntryClass::TestClass.into())),
|
||||
modlist!([
|
||||
m_pres(Attribute::May.as_ref(), &Attribute::Name.to_value()),
|
||||
m_pres(Attribute::Must.as_ref(), &Attribute::Name.to_value()),
|
||||
m_pres(Attribute::May, &Attribute::Name.to_value()),
|
||||
m_pres(Attribute::Must, &Attribute::Name.to_value()),
|
||||
]),
|
||||
Some(E_TEST_ACCOUNT.clone()),
|
||||
|_| {},
|
||||
|
@ -570,8 +569,8 @@ mod tests {
|
|||
PartialValue::new_iname("domain_example.net.au")
|
||||
)),
|
||||
modlist!([
|
||||
m_purge("domain_ssid"),
|
||||
m_pres("domain_ssid", &Value::new_utf8s("NewExampleWifi")),
|
||||
m_purge(Attribute::DomainSsid),
|
||||
m_pres(Attribute::DomainSsid, &Value::new_utf8s("NewExampleWifi")),
|
||||
]),
|
||||
Some(E_TEST_ACCOUNT.clone()),
|
||||
|_| {},
|
||||
|
|
|
@ -127,11 +127,12 @@ impl ReferentialIntegrity {
|
|||
|
||||
let mut work_set = qs.internal_search_writeable(&filt)?;
|
||||
|
||||
work_set.iter_mut().for_each(|(_, post)| {
|
||||
ref_types
|
||||
.values()
|
||||
.for_each(|attr| post.remove_avas(attr.name.as_str(), &removed_ids));
|
||||
});
|
||||
for (_, post) in work_set.iter_mut() {
|
||||
for schema_attribute in ref_types.values() {
|
||||
let attribute = (&schema_attribute.name).try_into()?;
|
||||
post.remove_avas(attribute, &removed_ids);
|
||||
}
|
||||
}
|
||||
|
||||
qs.internal_apply_writable(work_set)
|
||||
}
|
||||
|
@ -322,8 +323,19 @@ impl Plugin for ReferentialIntegrity {
|
|||
for c in &all_cand {
|
||||
// For all reference in each cand.
|
||||
for rtype in ref_types.values() {
|
||||
let attr: Attribute = match (&rtype.name).try_into() {
|
||||
Ok(val) => val,
|
||||
Err(err) => {
|
||||
// we shouldn't be able to get here...
|
||||
admin_error!("verify referential integrity invalid attribute {} specified - please log this as a bug! {:?}", &rtype.name, err);
|
||||
res.push(Err(ConsistencyError::InvalidAttributeType(
|
||||
rtype.name.to_string(),
|
||||
)));
|
||||
continue;
|
||||
}
|
||||
};
|
||||
// If the attribute is present
|
||||
if let Some(vs) = c.get_ava_set(&rtype.name) {
|
||||
if let Some(vs) = c.get_ava_set(attr) {
|
||||
// For each value in the set.
|
||||
match vs.as_ref_uuid_iter() {
|
||||
Some(uuid_iter) => {
|
||||
|
@ -356,20 +368,27 @@ impl ReferentialIntegrity {
|
|||
// Fast Path
|
||||
let mut vsiter = cand.iter().flat_map(|c| {
|
||||
// If it's dyngroup, skip member since this will be reset in the next step.
|
||||
let dyn_group =
|
||||
c.attribute_equality(Attribute::Class.as_ref(), &EntryClass::DynGroup.into());
|
||||
let dyn_group = c.attribute_equality(Attribute::Class, &EntryClass::DynGroup.into());
|
||||
|
||||
ref_types.values().filter_map(move |rtype| {
|
||||
// Skip dynamic members, these are recalculated by the
|
||||
// memberof plugin.
|
||||
let skip_mb = dyn_group && rtype.name == "dynmember";
|
||||
let skip_mb = dyn_group && rtype.name == Attribute::DynMember.as_ref();
|
||||
// Skip memberOf, also recalculated.
|
||||
let skip_mo = rtype.name == "memberof";
|
||||
let skip_mo = rtype.name == Attribute::MemberOf.as_ref();
|
||||
if skip_mb || skip_mo {
|
||||
None
|
||||
} else {
|
||||
trace!(rtype_name = ?rtype.name, "examining");
|
||||
c.get_ava_set(&rtype.name)
|
||||
c.get_ava_set(
|
||||
(&rtype.name)
|
||||
.try_into()
|
||||
.map_err(|e| {
|
||||
admin_error!(?e, "invalid attribute type {}", &rtype.name);
|
||||
None::<Attribute>
|
||||
})
|
||||
.ok()?,
|
||||
)
|
||||
}
|
||||
})
|
||||
});
|
||||
|
@ -978,7 +997,7 @@ mod tests {
|
|||
.expect("Internal search failure");
|
||||
let ue = cands.first().expect("No entry");
|
||||
assert!(ue
|
||||
.get_ava_as_oauthscopemaps("oauth2_rs_scope_map")
|
||||
.get_ava_as_oauthscopemaps(Attribute::OAuth2RsScopeMap)
|
||||
.is_none())
|
||||
}
|
||||
);
|
||||
|
@ -1104,8 +1123,8 @@ mod tests {
|
|||
// Still there
|
||||
|
||||
let entry = server_txn.internal_search_uuid(tuuid).expect("failed");
|
||||
assert!(entry.attribute_equality(Attribute::UserAuthTokenSession.as_ref(), &pv_parent_id));
|
||||
assert!(entry.attribute_equality(Attribute::OAuth2Session.as_ref(), &pv_session_id));
|
||||
assert!(entry.attribute_equality(Attribute::UserAuthTokenSession, &pv_parent_id));
|
||||
assert!(entry.attribute_equality(Attribute::OAuth2Session, &pv_session_id));
|
||||
|
||||
// Delete the oauth2 resource server.
|
||||
assert!(server_txn.internal_delete_uuid(rs_uuid).is_ok());
|
||||
|
@ -1115,14 +1134,14 @@ mod tests {
|
|||
|
||||
// Note the uat is present still.
|
||||
let session = entry
|
||||
.get_ava_as_session_map(Attribute::UserAuthTokenSession.into())
|
||||
.get_ava_as_session_map(Attribute::UserAuthTokenSession)
|
||||
.and_then(|sessions| sessions.get(&parent_id))
|
||||
.expect("No session map found");
|
||||
assert!(matches!(session.state, SessionState::NeverExpires));
|
||||
|
||||
// The oauth2 session is revoked.
|
||||
let session = entry
|
||||
.get_ava_as_oauth2session_map(Attribute::OAuth2Session.into())
|
||||
.get_ava_as_oauth2session_map(Attribute::OAuth2Session)
|
||||
.and_then(|sessions| sessions.get(&session_id))
|
||||
.expect("No session map found");
|
||||
assert!(matches!(session.state, SessionState::RevokedAt(_)));
|
||||
|
@ -1177,8 +1196,8 @@ mod tests {
|
|||
.expect("Failed to access dyn group");
|
||||
|
||||
let dyn_member = dyna
|
||||
.get_ava_refer("dynmember")
|
||||
.expect("Failed to get member attribute");
|
||||
.get_ava_refer(Attribute::DynMember)
|
||||
.expect("Failed to get dyn member attribute");
|
||||
assert!(dyn_member.len() == 1);
|
||||
assert!(dyn_member.contains(&tgroup_uuid));
|
||||
|
||||
|
@ -1187,7 +1206,7 @@ mod tests {
|
|||
.expect("Failed to access mo group");
|
||||
|
||||
let grp_member = group
|
||||
.get_ava_refer("memberof")
|
||||
.get_ava_refer(Attribute::MemberOf)
|
||||
.expect("Failed to get memberof attribute");
|
||||
assert!(grp_member.len() == 1);
|
||||
assert!(grp_member.contains(&dyn_uuid));
|
||||
|
|
|
@ -57,18 +57,18 @@ impl SessionConsistency {
|
|||
// * If the session's credential is no longer on the account, we remove the session.
|
||||
let cred_ids: BTreeSet<Uuid> =
|
||||
entry
|
||||
.get_ava_single_credential(Attribute::PrimaryCredential.into())
|
||||
.get_ava_single_credential(Attribute::PrimaryCredential)
|
||||
.iter()
|
||||
.map(|c| c.uuid)
|
||||
|
||||
.chain(
|
||||
entry.get_ava_passkeys(Attribute::PassKeys.into())
|
||||
entry.get_ava_passkeys(Attribute::PassKeys)
|
||||
.iter()
|
||||
.flat_map(|pks| pks.keys().copied() )
|
||||
)
|
||||
.collect();
|
||||
|
||||
let invalidate: Option<BTreeSet<_>> = entry.get_ava_as_session_map(Attribute::UserAuthTokenSession.into())
|
||||
let invalidate: Option<BTreeSet<_>> = entry.get_ava_as_session_map(Attribute::UserAuthTokenSession)
|
||||
.map(|sessions| {
|
||||
sessions.iter().filter_map(|(session_id, session)| {
|
||||
if !cred_ids.contains(&session.cred_id) {
|
||||
|
@ -82,11 +82,11 @@ impl SessionConsistency {
|
|||
});
|
||||
|
||||
if let Some(invalidate) = invalidate.as_ref() {
|
||||
entry.remove_avas(Attribute::UserAuthTokenSession.into(), invalidate);
|
||||
entry.remove_avas(Attribute::UserAuthTokenSession, invalidate);
|
||||
}
|
||||
|
||||
// * If a UAT is past its expiry, remove it.
|
||||
let expired: Option<BTreeSet<_>> = entry.get_ava_as_session_map(Attribute::UserAuthTokenSession.into())
|
||||
let expired: Option<BTreeSet<_>> = entry.get_ava_as_session_map(Attribute::UserAuthTokenSession)
|
||||
.map(|sessions| {
|
||||
sessions.iter().filter_map(|(session_id, session)| {
|
||||
trace!(?session_id, ?session);
|
||||
|
@ -102,14 +102,14 @@ impl SessionConsistency {
|
|||
});
|
||||
|
||||
if let Some(expired) = expired.as_ref() {
|
||||
entry.remove_avas(Attribute::UserAuthTokenSession.into(), expired);
|
||||
entry.remove_avas(Attribute::UserAuthTokenSession, expired);
|
||||
}
|
||||
|
||||
// * If an oauth2 session is past it's expiry, remove it.
|
||||
// * If an oauth2 session is past the grace window, and no parent session exists, remove it.
|
||||
let oauth2_remove: Option<BTreeSet<_>> = entry.get_ava_as_oauth2session_map("oauth2_session").map(|oauth2_sessions| {
|
||||
let oauth2_remove: Option<BTreeSet<_>> = entry.get_ava_as_oauth2session_map(Attribute::OAuth2Session).map(|oauth2_sessions| {
|
||||
// If we have oauth2 sessions, we need to be able to lookup if sessions exist in the uat.
|
||||
let sessions = entry.get_ava_as_session_map(Attribute::UserAuthTokenSession.into());
|
||||
let sessions = entry.get_ava_as_session_map(Attribute::UserAuthTokenSession);
|
||||
|
||||
oauth2_sessions.iter().filter_map(|(o2_session_id, session)| {
|
||||
trace!(?o2_session_id, ?session);
|
||||
|
@ -157,7 +157,7 @@ impl SessionConsistency {
|
|||
});
|
||||
|
||||
if let Some(oauth2_remove) = oauth2_remove.as_ref() {
|
||||
entry.remove_avas(Attribute::OAuth2Session.as_ref(), oauth2_remove);
|
||||
entry.remove_avas(Attribute::OAuth2Session, oauth2_remove);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -255,7 +255,7 @@ mod tests {
|
|||
let entry = server_txn.internal_search_uuid(tuuid).expect("failed");
|
||||
|
||||
let session = entry
|
||||
.get_ava_as_session_map(Attribute::UserAuthTokenSession.into())
|
||||
.get_ava_as_session_map(Attribute::UserAuthTokenSession)
|
||||
.and_then(|sessions| sessions.get(&session_id))
|
||||
.expect("No session map found");
|
||||
assert!(matches!(session.state, SessionState::ExpiresAt(_)));
|
||||
|
@ -265,7 +265,7 @@ mod tests {
|
|||
|
||||
// Mod again - anything will do.
|
||||
let modlist = ModifyList::new_purge_and_set(
|
||||
Attribute::Description.as_ref(),
|
||||
Attribute::Description,
|
||||
Value::new_utf8s("test person 1 change"),
|
||||
);
|
||||
|
||||
|
@ -281,7 +281,7 @@ mod tests {
|
|||
|
||||
// We get the attribute and have to check it's now in a revoked state.
|
||||
let session = entry
|
||||
.get_ava_as_session_map(Attribute::UserAuthTokenSession.into())
|
||||
.get_ava_as_session_map(Attribute::UserAuthTokenSession)
|
||||
.and_then(|sessions| sessions.get(&session_id))
|
||||
.expect("No session map found");
|
||||
assert!(matches!(session.state, SessionState::RevokedAt(_)));
|
||||
|
@ -418,13 +418,13 @@ mod tests {
|
|||
let entry = server_txn.internal_search_uuid(tuuid).expect("failed");
|
||||
|
||||
let session = entry
|
||||
.get_ava_as_session_map(Attribute::UserAuthTokenSession.into())
|
||||
.get_ava_as_session_map(Attribute::UserAuthTokenSession)
|
||||
.and_then(|sessions| sessions.get(&parent_id))
|
||||
.expect("No session map found");
|
||||
assert!(matches!(session.state, SessionState::NeverExpires));
|
||||
|
||||
let session = entry
|
||||
.get_ava_as_oauth2session_map(Attribute::OAuth2Session.into())
|
||||
.get_ava_as_oauth2session_map(Attribute::OAuth2Session)
|
||||
.and_then(|sessions| sessions.get(&session_id))
|
||||
.expect("No session map found");
|
||||
assert!(matches!(session.state, SessionState::ExpiresAt(_)));
|
||||
|
@ -437,7 +437,7 @@ mod tests {
|
|||
|
||||
// Mod again - anything will do.
|
||||
let modlist = ModifyList::new_purge_and_set(
|
||||
Attribute::Description.as_ref(),
|
||||
Attribute::Description,
|
||||
Value::new_utf8s("test person 1 change"),
|
||||
);
|
||||
|
||||
|
@ -453,13 +453,13 @@ mod tests {
|
|||
|
||||
// Note the uat is still present
|
||||
let session = entry
|
||||
.get_ava_as_session_map(Attribute::UserAuthTokenSession.into())
|
||||
.get_ava_as_session_map(Attribute::UserAuthTokenSession)
|
||||
.and_then(|sessions| sessions.get(&parent_id))
|
||||
.expect("No session map found");
|
||||
assert!(matches!(session.state, SessionState::NeverExpires));
|
||||
|
||||
let session = entry
|
||||
.get_ava_as_oauth2session_map(Attribute::OAuth2Session.into())
|
||||
.get_ava_as_oauth2session_map(Attribute::OAuth2Session)
|
||||
.and_then(|sessions| sessions.get(&session_id))
|
||||
.expect("No session map found");
|
||||
assert!(matches!(session.state, SessionState::RevokedAt(_)));
|
||||
|
@ -592,13 +592,13 @@ mod tests {
|
|||
let entry = server_txn.internal_search_uuid(tuuid).expect("failed");
|
||||
|
||||
let session = entry
|
||||
.get_ava_as_session_map(Attribute::UserAuthTokenSession.into())
|
||||
.get_ava_as_session_map(Attribute::UserAuthTokenSession)
|
||||
.and_then(|sessions| sessions.get(&parent_id))
|
||||
.expect("No session map found");
|
||||
assert!(matches!(session.state, SessionState::NeverExpires));
|
||||
|
||||
let session = entry
|
||||
.get_ava_as_oauth2session_map(Attribute::OAuth2Session.into())
|
||||
.get_ava_as_oauth2session_map(Attribute::OAuth2Session)
|
||||
.and_then(|sessions| sessions.get(&session_id))
|
||||
.expect("No session map found");
|
||||
assert!(matches!(session.state, SessionState::NeverExpires));
|
||||
|
@ -625,7 +625,7 @@ mod tests {
|
|||
|
||||
// Note the uat is removed
|
||||
let session = entry
|
||||
.get_ava_as_session_map(Attribute::UserAuthTokenSession.into())
|
||||
.get_ava_as_session_map(Attribute::UserAuthTokenSession)
|
||||
.and_then(|sessions| sessions.get(&parent_id))
|
||||
.expect("No session map found");
|
||||
assert!(matches!(session.state, SessionState::RevokedAt(_)));
|
||||
|
@ -748,7 +748,7 @@ mod tests {
|
|||
|
||||
// Mod again - anything will do.
|
||||
let modlist = ModifyList::new_purge_and_set(
|
||||
Attribute::Description.as_ref(),
|
||||
Attribute::Description,
|
||||
Value::new_utf8s("test person 1 change"),
|
||||
);
|
||||
|
||||
|
@ -842,7 +842,7 @@ mod tests {
|
|||
let entry = server_txn.internal_search_uuid(tuuid).expect("failed");
|
||||
|
||||
let session = entry
|
||||
.get_ava_as_session_map(Attribute::UserAuthTokenSession.into())
|
||||
.get_ava_as_session_map(Attribute::UserAuthTokenSession)
|
||||
.and_then(|sessions| sessions.get(&session_id))
|
||||
.expect("No session map found");
|
||||
assert!(matches!(session.state, SessionState::NeverExpires));
|
||||
|
@ -853,7 +853,7 @@ mod tests {
|
|||
let mut server_txn = server.write(curtime).await;
|
||||
|
||||
// Remove the primary credential
|
||||
let modlist = ModifyList::new_purge(Attribute::PrimaryCredential.into());
|
||||
let modlist = ModifyList::new_purge(Attribute::PrimaryCredential);
|
||||
|
||||
server_txn
|
||||
.internal_modify(
|
||||
|
@ -867,7 +867,7 @@ mod tests {
|
|||
|
||||
// Note it's a not condition now.
|
||||
let session = entry
|
||||
.get_ava_as_session_map(Attribute::UserAuthTokenSession.into())
|
||||
.get_ava_as_session_map(Attribute::UserAuthTokenSession)
|
||||
.and_then(|sessions| sessions.get(&session_id))
|
||||
.expect("No session map found");
|
||||
assert!(matches!(session.state, SessionState::RevokedAt(_)));
|
||||
|
|
|
@ -117,7 +117,7 @@ impl Plugin for Spn {
|
|||
r.push(Err(ConsistencyError::InvalidSpn(e.get_id())));
|
||||
continue;
|
||||
};
|
||||
match e.get_ava_single("spn") {
|
||||
match e.get_ava_single(Attribute::Spn) {
|
||||
Some(r_spn) => {
|
||||
trace!("verify spn: s {:?} == ex {:?} ?", r_spn, g_spn);
|
||||
if r_spn != g_spn {
|
||||
|
@ -149,8 +149,8 @@ impl Spn {
|
|||
let domain_name = qs.get_domain_name();
|
||||
|
||||
for ent in cand.iter_mut() {
|
||||
if ent.attribute_equality(Attribute::Class.as_ref(), &EntryClass::Group.into())
|
||||
|| ent.attribute_equality(Attribute::Class.as_ref(), &EntryClass::Account.into())
|
||||
if ent.attribute_equality(Attribute::Class, &EntryClass::Group.into())
|
||||
|| ent.attribute_equality(Attribute::Class, &EntryClass::Account.into())
|
||||
{
|
||||
let spn = ent
|
||||
.generate_spn(domain_name)
|
||||
|
@ -162,8 +162,13 @@ impl Spn {
|
|||
);
|
||||
e
|
||||
})?;
|
||||
trace!("plugin_spn: set spn to {:?}", spn);
|
||||
ent.set_ava("spn", once(spn));
|
||||
trace!(
|
||||
"plugin_{}: set {} to {:?}",
|
||||
Attribute::Spn,
|
||||
Attribute::Spn,
|
||||
spn
|
||||
);
|
||||
ent.set_ava(Attribute::Spn, once(spn));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
@ -177,9 +182,9 @@ impl Spn {
|
|||
// On modify, if changing domain_name on UUID_DOMAIN_INFO trigger the spn regen
|
||||
|
||||
let domain_name_changed = cand.iter().zip(pre_cand.iter()).find_map(|(post, pre)| {
|
||||
let domain_name = post.get_ava_single("domain_name");
|
||||
if post.attribute_equality(Attribute::Uuid.as_ref(), &PVUUID_DOMAIN_INFO)
|
||||
&& domain_name != pre.get_ava_single("domain_name")
|
||||
let domain_name = post.get_ava_single(Attribute::DomainName);
|
||||
if post.attribute_equality(Attribute::Uuid, &PVUUID_DOMAIN_INFO)
|
||||
&& domain_name != pre.get_ava_single(Attribute::DomainName)
|
||||
{
|
||||
domain_name
|
||||
} else {
|
||||
|
@ -209,7 +214,7 @@ impl Spn {
|
|||
f_eq(Attribute::Class, EntryClass::Group.into()),
|
||||
f_eq(Attribute::Class, EntryClass::Account.into()),
|
||||
])),
|
||||
&modlist!([m_purge("spn")]),
|
||||
&modlist!([m_purge(Attribute::Spn)]),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -265,7 +270,7 @@ mod tests {
|
|||
Ok(()),
|
||||
preload,
|
||||
filter!(f_eq(Attribute::Name, PartialValue::new_iname("testperson"))),
|
||||
modlist!([m_purge("spn")]),
|
||||
modlist!([m_purge(Attribute::Spn)]),
|
||||
None,
|
||||
|_| {},
|
||||
|_| {}
|
||||
|
@ -320,8 +325,11 @@ mod tests {
|
|||
preload,
|
||||
filter!(f_eq(Attribute::Name, PartialValue::new_iname("testperson"))),
|
||||
modlist!([
|
||||
m_purge("spn"),
|
||||
m_pres("spn", &Value::new_spn_str("invalid", "spn"))
|
||||
m_purge(Attribute::Spn),
|
||||
m_pres(
|
||||
Attribute::Spn,
|
||||
&Value::new_spn_str("invalid", Attribute::Spn.as_ref())
|
||||
)
|
||||
]),
|
||||
None,
|
||||
|_| {},
|
||||
|
@ -341,7 +349,7 @@ mod tests {
|
|||
.internal_search_uuid(UUID_ADMIN)
|
||||
.expect("must not fail");
|
||||
|
||||
let e_pre_spn = e_pre.get_ava_single("spn").expect("must not fail");
|
||||
let e_pre_spn = e_pre.get_ava_single(Attribute::Spn).expect("must not fail");
|
||||
assert!(e_pre_spn == ex1);
|
||||
|
||||
// trigger the domain_name change (this will be a cli option to the server
|
||||
|
@ -356,7 +364,9 @@ mod tests {
|
|||
.internal_search_uuid(UUID_ADMIN)
|
||||
.expect("must not fail");
|
||||
|
||||
let e_post_spn = e_post.get_ava_single("spn").expect("must not fail");
|
||||
let e_post_spn = e_post
|
||||
.get_ava_single(Attribute::Spn)
|
||||
.expect("must not fail");
|
||||
debug!("{:?}", e_post_spn);
|
||||
debug!("{:?}", ex2);
|
||||
assert!(e_post_spn == ex2);
|
||||
|
|
|
@ -221,10 +221,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
.iter()
|
||||
.chain(pre_cand.iter().map(|e| e.as_ref()))
|
||||
.any(|e| {
|
||||
e.attribute_equality(
|
||||
Attribute::Class.into(),
|
||||
&EntryClass::AccessControlProfile.into(),
|
||||
)
|
||||
e.attribute_equality(Attribute::Class, &EntryClass::AccessControlProfile.into())
|
||||
})
|
||||
}
|
||||
if !self.changed_oauth2 {
|
||||
|
@ -232,19 +229,14 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
.iter()
|
||||
.chain(pre_cand.iter().map(|e| e.as_ref()))
|
||||
.any(|e| {
|
||||
e.attribute_equality(
|
||||
Attribute::Class.into(),
|
||||
&EntryClass::OAuth2ResourceServer.into(),
|
||||
)
|
||||
e.attribute_equality(Attribute::Class, &EntryClass::OAuth2ResourceServer.into())
|
||||
});
|
||||
}
|
||||
if !self.changed_sync_agreement {
|
||||
self.changed_sync_agreement = cand
|
||||
.iter()
|
||||
.chain(pre_cand.iter().map(|e| e.as_ref()))
|
||||
.any(|e| {
|
||||
e.attribute_equality(Attribute::Class.as_ref(), &EntryClass::SyncAccount.into())
|
||||
});
|
||||
.any(|e| e.attribute_equality(Attribute::Class, &EntryClass::SyncAccount.into()));
|
||||
}
|
||||
|
||||
trace!(
|
||||
|
|
|
@ -251,25 +251,25 @@ impl EntryChangelog {
|
|||
EntryChangelog { anchors, changes }
|
||||
}
|
||||
|
||||
pub fn add_ava_iter<T>(&mut self, cid: &Cid, attr: &str, viter: T)
|
||||
where
|
||||
T: IntoIterator<Item = Value>,
|
||||
{
|
||||
if !self.changes.contains_key(cid) {
|
||||
self.changes.insert(cid.clone(), Change { s: Vec::new() });
|
||||
}
|
||||
// fn add_ava_iter<T>(&mut self, cid: &Cid, attr: Attribute, viter: T)
|
||||
// where
|
||||
// T: IntoIterator<Item = Value>,
|
||||
// {
|
||||
// if !self.changes.contains_key(cid) {
|
||||
// self.changes.insert(cid.clone(), Change { s: Vec::new() });
|
||||
// }
|
||||
|
||||
#[allow(clippy::expect_used)]
|
||||
let change = self
|
||||
.changes
|
||||
.get_mut(cid)
|
||||
.expect("Memory corruption, change must exist");
|
||||
// #[allow(clippy::expect_used)]
|
||||
// let change = self
|
||||
// .changes
|
||||
// .get_mut(cid)
|
||||
// .expect("Memory corruption, change must exist");
|
||||
|
||||
viter
|
||||
.into_iter()
|
||||
.map(|v| Transition::ModifyPresent(AttrString::from(attr), Box::new(v)))
|
||||
.for_each(|t| change.s.push(t));
|
||||
}
|
||||
// viter
|
||||
// .into_iter()
|
||||
// .map(|v| Transition::ModifyPresent(attr.into(), Box::new(v)))
|
||||
// .for_each(|t| change.s.push(t));
|
||||
// }
|
||||
|
||||
pub fn remove_ava_iter<T>(&mut self, cid: &Cid, attr: &str, viter: T)
|
||||
where
|
||||
|
@ -291,7 +291,7 @@ impl EntryChangelog {
|
|||
.for_each(|t| change.s.push(t));
|
||||
}
|
||||
|
||||
pub fn assert_ava(&mut self, cid: &Cid, attr: &str, value: PartialValue) {
|
||||
pub fn assert_ava(&mut self, cid: &Cid, attr: Attribute, value: PartialValue) {
|
||||
if !self.changes.contains_key(cid) {
|
||||
self.changes.insert(cid.clone(), Change { s: Vec::new() });
|
||||
}
|
||||
|
@ -302,25 +302,22 @@ impl EntryChangelog {
|
|||
.get_mut(cid)
|
||||
.expect("Memory corruption, change must exist");
|
||||
|
||||
change.s.push(Transition::ModifyAssert(
|
||||
AttrString::from(attr),
|
||||
Box::new(value),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn purge_ava(&mut self, cid: &Cid, attr: &str) {
|
||||
if !self.changes.contains_key(cid) {
|
||||
self.changes.insert(cid.clone(), Change { s: Vec::new() });
|
||||
}
|
||||
|
||||
#[allow(clippy::expect_used)]
|
||||
let change = self
|
||||
.changes
|
||||
.get_mut(cid)
|
||||
.expect("Memory corruption, change must exist");
|
||||
change
|
||||
.s
|
||||
.push(Transition::ModifyPurge(AttrString::from(attr)));
|
||||
.push(Transition::ModifyAssert(attr.into(), Box::new(value)))
|
||||
}
|
||||
|
||||
pub fn purge_ava(&mut self, cid: &Cid, attr: Attribute) {
|
||||
if !self.changes.contains_key(cid) {
|
||||
self.changes.insert(cid.clone(), Change { s: Vec::new() });
|
||||
}
|
||||
|
||||
#[allow(clippy::expect_used)]
|
||||
let change = self
|
||||
.changes
|
||||
.get_mut(cid)
|
||||
.expect("Memory corruption, change must exist");
|
||||
change.s.push(Transition::ModifyPurge(attr.info()));
|
||||
}
|
||||
|
||||
pub fn recycled(&mut self, cid: &Cid) {
|
||||
|
|
|
@ -87,13 +87,13 @@ impl EntryChangeState {
|
|||
EntryChangeState { st }
|
||||
}
|
||||
|
||||
pub fn change_ava(&mut self, cid: &Cid, attr: &str) {
|
||||
pub fn change_ava(&mut self, cid: &Cid, attr: Attribute) {
|
||||
match &mut self.st {
|
||||
State::Live {
|
||||
at: _,
|
||||
ref mut changes,
|
||||
} => {
|
||||
if let Some(change) = changes.get_mut(attr) {
|
||||
if let Some(change) = changes.get_mut(attr.as_ref()) {
|
||||
// Update the cid.
|
||||
if change != cid {
|
||||
*change = cid.clone()
|
||||
|
|
|
@ -528,7 +528,7 @@ impl ReplEntryV1 {
|
|||
|
||||
let mut eattrs = Eattrs::default();
|
||||
|
||||
let class_ava = vs_iutf8!["object", "tombstone"];
|
||||
let class_ava = vs_iutf8![EntryClass::Object.into(), EntryClass::Tombstone.into()];
|
||||
let last_mod_ava = vs_cid![at.clone()];
|
||||
|
||||
eattrs.insert(Attribute::Uuid.into(), vs_uuid![self.uuid]);
|
||||
|
|
|
@ -78,7 +78,7 @@ impl<'a> QueryServerReadTransaction<'a> {
|
|||
|
||||
// Separate the entries into schema, meta and remaining.
|
||||
let (schema_entries, rem_entries): (Vec<_>, Vec<_>) = entries.into_iter().partition(|e| {
|
||||
e.get_ava_set(Attribute::Class.as_ref())
|
||||
e.get_ava_set(Attribute::Class)
|
||||
.map(|cls| {
|
||||
cls.contains(&EntryClass::AttributeType.into() as &PartialValue)
|
||||
|| cls.contains(&EntryClass::ClassType.into() as &PartialValue)
|
||||
|
@ -87,7 +87,7 @@ impl<'a> QueryServerReadTransaction<'a> {
|
|||
});
|
||||
|
||||
let (meta_entries, entries): (Vec<_>, Vec<_>) = rem_entries.into_iter().partition(|e| {
|
||||
e.get_ava_set("uuid")
|
||||
e.get_ava_set(Attribute::Uuid)
|
||||
.map(|uset| {
|
||||
uset.contains(&PVUUID_DOMAIN_INFO as &PartialValue)
|
||||
|| uset.contains(&PVUUID_SYSTEM_INFO as &PartialValue)
|
||||
|
|
|
@ -446,7 +446,7 @@ async fn test_repl_increment_basic_entry_tombstone(server_a: &QueryServer, serve
|
|||
.internal_search_all_uuid(t_uuid)
|
||||
.expect("Unable to access entry.");
|
||||
|
||||
assert!(e1.attribute_equality(Attribute::Class.as_ref(), &EntryClass::Tombstone.into()));
|
||||
assert!(e1.attribute_equality(Attribute::Class, &EntryClass::Tombstone.into()));
|
||||
|
||||
assert!(e1 == e2);
|
||||
|
||||
|
@ -612,10 +612,7 @@ async fn test_repl_increment_basic_bidirectional_write(
|
|||
|
||||
// Now perform a write on A
|
||||
assert!(server_a_txn
|
||||
.internal_modify_uuid(
|
||||
t_uuid,
|
||||
&ModifyList::new_purge(Attribute::Description.as_ref())
|
||||
)
|
||||
.internal_modify_uuid(t_uuid, &ModifyList::new_purge(Attribute::Description))
|
||||
.is_ok());
|
||||
|
||||
server_a_txn.commit().expect("Failed to commit");
|
||||
|
@ -637,7 +634,7 @@ async fn test_repl_increment_basic_bidirectional_write(
|
|||
|
||||
// They are consistent again.
|
||||
assert!(e1 == e2);
|
||||
assert!(e1.get_ava_set(Attribute::Description.as_ref()).is_none());
|
||||
assert!(e1.get_ava_set(Attribute::Description).is_none());
|
||||
|
||||
server_b_txn.commit().expect("Failed to commit");
|
||||
drop(server_a_txn);
|
||||
|
@ -676,10 +673,7 @@ async fn test_repl_increment_basic_deleted_attr(server_a: &QueryServer, server_b
|
|||
// presence
|
||||
let mut server_a_txn = server_a.write(duration_from_epoch_now()).await;
|
||||
assert!(server_a_txn
|
||||
.internal_modify_uuid(
|
||||
t_uuid,
|
||||
&ModifyList::new_purge(Attribute::Description.as_ref())
|
||||
)
|
||||
.internal_modify_uuid(t_uuid, &ModifyList::new_purge(Attribute::Description))
|
||||
.is_ok());
|
||||
server_a_txn.commit().expect("Failed to commit");
|
||||
|
||||
|
@ -698,7 +692,7 @@ async fn test_repl_increment_basic_deleted_attr(server_a: &QueryServer, server_b
|
|||
.expect("Unable to access entry.");
|
||||
|
||||
// They are consistent again.
|
||||
assert!(e1.get_ava_set(Attribute::Description.as_ref()).is_none());
|
||||
assert!(e1.get_ava_set(Attribute::Description).is_none());
|
||||
assert!(e1 == e2);
|
||||
|
||||
let e1_cs = e1.get_changestate();
|
||||
|
@ -767,10 +761,7 @@ async fn test_repl_increment_simultaneous_bidirectional_write(
|
|||
assert!(server_a_txn
|
||||
.internal_modify_uuid(
|
||||
t_uuid,
|
||||
&ModifyList::new_purge_and_set(
|
||||
Attribute::Description.as_ref(),
|
||||
Value::new_utf8s("repl_test")
|
||||
)
|
||||
&ModifyList::new_purge_and_set(Attribute::Description, Value::new_utf8s("repl_test"))
|
||||
)
|
||||
.is_ok());
|
||||
|
||||
|
@ -782,7 +773,7 @@ async fn test_repl_increment_simultaneous_bidirectional_write(
|
|||
assert!(server_b_txn
|
||||
.internal_modify_uuid(
|
||||
t_uuid,
|
||||
&ModifyList::new_purge_and_set("displayname", Value::new_utf8s("repl_test"))
|
||||
&ModifyList::new_purge_and_set(Attribute::DisplayName, Value::new_utf8s("repl_test"))
|
||||
)
|
||||
.is_ok());
|
||||
|
||||
|
@ -816,8 +807,8 @@ async fn test_repl_increment_simultaneous_bidirectional_write(
|
|||
|
||||
// They are consistent again.
|
||||
assert!(e1 == e2);
|
||||
assert!(e1.get_ava_single_utf8(Attribute::Description.as_ref()) == Some("repl_test"));
|
||||
assert!(e1.get_ava_single_utf8("displayname") == Some("repl_test"));
|
||||
assert!(e1.get_ava_single_utf8(Attribute::Description) == Some("repl_test"));
|
||||
assert!(e1.get_ava_single_utf8(Attribute::DisplayName) == Some("repl_test"));
|
||||
}
|
||||
|
||||
// Create entry on A -> B
|
||||
|
@ -900,7 +891,7 @@ async fn test_repl_increment_basic_bidirectional_lifecycle(
|
|||
|
||||
// They are consistent again.
|
||||
assert!(e1 == e2);
|
||||
assert!(e1.attribute_equality(Attribute::Class.as_ref(), &EntryClass::Recycled.into()));
|
||||
assert!(e1.attribute_equality(Attribute::Class, &EntryClass::Recycled.into()));
|
||||
|
||||
server_b_txn.commit().expect("Failed to commit");
|
||||
drop(server_a_txn);
|
||||
|
@ -934,9 +925,9 @@ async fn test_repl_increment_basic_bidirectional_lifecycle(
|
|||
// They are NOT consistent.
|
||||
assert!(e1 != e2);
|
||||
// E1 from A is NOT a tombstone ... yet.
|
||||
assert!(!e1.attribute_equality(Attribute::Class.as_ref(), &EntryClass::Tombstone.into()));
|
||||
assert!(!e1.attribute_equality(Attribute::Class, &EntryClass::Tombstone.into()));
|
||||
// E2 from B is a tombstone!
|
||||
assert!(e2.attribute_equality(Attribute::Class.as_ref(), &EntryClass::Tombstone.into()));
|
||||
assert!(e2.attribute_equality(Attribute::Class, &EntryClass::Tombstone.into()));
|
||||
|
||||
server_b_txn.commit().expect("Failed to commit");
|
||||
drop(server_a_txn);
|
||||
|
@ -955,7 +946,7 @@ async fn test_repl_increment_basic_bidirectional_lifecycle(
|
|||
.expect("Unable to access entry.");
|
||||
|
||||
// Ts on both.
|
||||
assert!(e1.attribute_equality(Attribute::Class.as_ref(), &EntryClass::Tombstone.into()));
|
||||
assert!(e1.attribute_equality(Attribute::Class, &EntryClass::Tombstone.into()));
|
||||
assert!(e1 == e2);
|
||||
|
||||
server_a_txn.commit().expect("Failed to commit");
|
||||
|
@ -1046,7 +1037,7 @@ async fn test_repl_increment_basic_bidirectional_recycle(
|
|||
// They are equal, but their CL states are not. e2 should have been
|
||||
// retained due to being the latest!
|
||||
assert!(e1 == e2);
|
||||
assert!(e1.attribute_equality(Attribute::Class.as_ref(), &EntryClass::Recycled.into()));
|
||||
assert!(e1.attribute_equality(Attribute::Class, &EntryClass::Recycled.into()));
|
||||
|
||||
// Remember entry comparison doesn't compare last_mod_cid.
|
||||
assert!(e1.get_last_changed() < e2.get_last_changed());
|
||||
|
@ -1167,8 +1158,8 @@ async fn test_repl_increment_basic_bidirectional_tombstone(
|
|||
.internal_search_all_uuid(t_uuid)
|
||||
.expect("Unable to access entry.");
|
||||
|
||||
assert!(e1.attribute_equality(Attribute::Class.as_ref(), &EntryClass::Tombstone.into()));
|
||||
assert!(e2.attribute_equality(Attribute::Class.as_ref(), &EntryClass::Tombstone.into()));
|
||||
assert!(e1.attribute_equality(Attribute::Class, &EntryClass::Tombstone.into()));
|
||||
assert!(e2.attribute_equality(Attribute::Class, &EntryClass::Tombstone.into()));
|
||||
trace!("{:?}", e1.get_last_changed());
|
||||
trace!("{:?}", e2.get_last_changed());
|
||||
assert!(e1.get_last_changed() < e2.get_last_changed());
|
||||
|
@ -1189,8 +1180,8 @@ async fn test_repl_increment_basic_bidirectional_tombstone(
|
|||
.internal_search_all_uuid(t_uuid)
|
||||
.expect("Unable to access entry.");
|
||||
|
||||
assert!(e1.attribute_equality(Attribute::Class.as_ref(), &EntryClass::Tombstone.into()));
|
||||
assert!(e2.attribute_equality(Attribute::Class.as_ref(), &EntryClass::Tombstone.into()));
|
||||
assert!(e1.attribute_equality(Attribute::Class, &EntryClass::Tombstone.into()));
|
||||
assert!(e2.attribute_equality(Attribute::Class, &EntryClass::Tombstone.into()));
|
||||
assert!(e1.get_last_changed() == e2.get_last_changed());
|
||||
|
||||
server_b_txn.commit().expect("Failed to commit");
|
||||
|
@ -1290,7 +1281,7 @@ async fn test_repl_increment_creation_uuid_conflict(
|
|||
// Should be a vec.
|
||||
.pop()
|
||||
.expect("No conflict entries present");
|
||||
assert!(cnf_a.get_ava_single_iname(Attribute::Name.as_ref()) == Some("testperson1"));
|
||||
assert!(cnf_a.get_ava_single_iname(Attribute::Name) == Some("testperson1"));
|
||||
|
||||
let cnf_b = server_b_txn
|
||||
.internal_search_conflict_uuid(t_uuid)
|
||||
|
@ -1395,9 +1386,9 @@ async fn test_repl_increment_create_tombstone_uuid_conflict(
|
|||
.expect("Unable to access entry.");
|
||||
assert!(e1 != e2);
|
||||
// E1 from A is a ts
|
||||
assert!(e1.attribute_equality(Attribute::Class.as_ref(), &EntryClass::Tombstone.into()));
|
||||
assert!(e1.attribute_equality(Attribute::Class, &EntryClass::Tombstone.into()));
|
||||
// E2 from B is not a TS
|
||||
assert!(!e2.attribute_equality(Attribute::Class.as_ref(), &EntryClass::Tombstone.into()));
|
||||
assert!(!e2.attribute_equality(Attribute::Class, &EntryClass::Tombstone.into()));
|
||||
|
||||
server_a_txn.commit().expect("Failed to commit");
|
||||
drop(server_b_txn);
|
||||
|
@ -1417,7 +1408,7 @@ async fn test_repl_increment_create_tombstone_uuid_conflict(
|
|||
.internal_search_all_uuid(t_uuid)
|
||||
.expect("Unable to access entry.");
|
||||
assert!(e1 == e2);
|
||||
assert!(e1.attribute_equality(Attribute::Class.as_ref(), &EntryClass::Tombstone.into()));
|
||||
assert!(e1.attribute_equality(Attribute::Class, &EntryClass::Tombstone.into()));
|
||||
|
||||
server_b_txn.commit().expect("Failed to commit");
|
||||
drop(server_a_txn);
|
||||
|
@ -1492,8 +1483,8 @@ async fn test_repl_increment_create_tombstone_conflict(
|
|||
|
||||
assert!(e1.get_last_changed() > e2.get_last_changed());
|
||||
// Yet, they are both TS. Curious.
|
||||
assert!(e1.attribute_equality(Attribute::Class.as_ref(), &EntryClass::Tombstone.into()));
|
||||
assert!(e2.attribute_equality(Attribute::Class.as_ref(), &EntryClass::Tombstone.into()));
|
||||
assert!(e1.attribute_equality(Attribute::Class, &EntryClass::Tombstone.into()));
|
||||
assert!(e2.attribute_equality(Attribute::Class, &EntryClass::Tombstone.into()));
|
||||
|
||||
server_b_txn.commit().expect("Failed to commit");
|
||||
drop(server_a_txn);
|
||||
|
@ -1513,7 +1504,7 @@ async fn test_repl_increment_create_tombstone_conflict(
|
|||
.expect("Unable to access entry.");
|
||||
|
||||
assert!(e1 == e2);
|
||||
assert!(e1.attribute_equality(Attribute::Class.as_ref(), &EntryClass::Tombstone.into()));
|
||||
assert!(e1.attribute_equality(Attribute::Class, &EntryClass::Tombstone.into()));
|
||||
|
||||
server_a_txn.commit().expect("Failed to commit");
|
||||
drop(server_b_txn);
|
||||
|
@ -1574,10 +1565,10 @@ async fn test_repl_increment_schema_conflict(server_a: &QueryServer, server_b: &
|
|||
let ct = ct + Duration::from_secs(1);
|
||||
let mut server_b_txn = server_b.write(ct).await;
|
||||
let modlist = ModifyList::new_list(vec![
|
||||
Modify::Removed("class".into(), EntryClass::Person.into()),
|
||||
Modify::Present("class".into(), EntryClass::Group.into()),
|
||||
Modify::Purged("id_verification_eckey".into()),
|
||||
Modify::Purged("displayname".into()),
|
||||
Modify::Removed(Attribute::Class.into(), EntryClass::Person.into()),
|
||||
Modify::Present(Attribute::Class.into(), EntryClass::Group.into()),
|
||||
Modify::Purged(Attribute::IdVerificationEcKey.into()),
|
||||
Modify::Purged(Attribute::DisplayName.into()),
|
||||
]);
|
||||
assert!(server_b_txn.internal_modify_uuid(t_uuid, &modlist).is_ok());
|
||||
server_b_txn.commit().expect("Failed to commit");
|
||||
|
@ -1589,7 +1580,7 @@ async fn test_repl_increment_schema_conflict(server_a: &QueryServer, server_b: &
|
|||
.internal_modify_uuid(
|
||||
t_uuid,
|
||||
&ModifyList::new_purge_and_set(
|
||||
"displayname",
|
||||
Attribute::DisplayName,
|
||||
Value::Utf8("Updated displayname".to_string())
|
||||
)
|
||||
)
|
||||
|
@ -1610,7 +1601,7 @@ async fn test_repl_increment_schema_conflict(server_a: &QueryServer, server_b: &
|
|||
.internal_search_all_uuid(t_uuid)
|
||||
.expect("Unable to access new entry.");
|
||||
|
||||
assert!(e1.attribute_equality(Attribute::Class.as_ref(), &EntryClass::Conflict.into()));
|
||||
assert!(e1.attribute_equality(Attribute::Class, &EntryClass::Conflict.into()));
|
||||
|
||||
server_a_txn.commit().expect("Failed to commit");
|
||||
drop(server_b_txn);
|
||||
|
@ -1626,7 +1617,7 @@ async fn test_repl_increment_schema_conflict(server_a: &QueryServer, server_b: &
|
|||
.internal_search_all_uuid(t_uuid)
|
||||
.expect("Unable to access entry.");
|
||||
|
||||
assert!(e2.attribute_equality(Attribute::Class.as_ref(), &EntryClass::Conflict.into()));
|
||||
assert!(e2.attribute_equality(Attribute::Class, &EntryClass::Conflict.into()));
|
||||
|
||||
server_b_txn.commit().expect("Failed to commit");
|
||||
drop(server_a_txn);
|
||||
|
@ -1693,7 +1684,7 @@ async fn test_repl_increment_consumer_lagging_attributes(
|
|||
.internal_modify_uuid(
|
||||
t_uuid,
|
||||
&ModifyList::new_purge_and_set(
|
||||
"displayname",
|
||||
Attribute::DisplayName,
|
||||
Value::Utf8("Updated displayname".to_string())
|
||||
)
|
||||
)
|
||||
|
@ -1956,7 +1947,7 @@ async fn test_repl_increment_domain_rename(server_a: &QueryServer, server_b: &Qu
|
|||
.internal_search_all_uuid(UUID_ADMIN)
|
||||
.expect("Unable to access entry.");
|
||||
|
||||
let vx1 = e1.get_ava_single("spn").expect("spn not present");
|
||||
let vx1 = e1.get_ava_single(Attribute::Spn).expect("spn not present");
|
||||
let ex1 = Value::new_spn_str("admin", "new.example.com");
|
||||
assert!(vx1 == ex1);
|
||||
|
||||
|
@ -1983,7 +1974,7 @@ async fn test_repl_increment_domain_rename(server_a: &QueryServer, server_b: &Qu
|
|||
.internal_search_all_uuid(t_uuid)
|
||||
.expect("Unable to access entry.");
|
||||
|
||||
let vx2 = e2.get_ava_single("spn").expect("spn not present");
|
||||
let vx2 = e2.get_ava_single(Attribute::Spn).expect("spn not present");
|
||||
let ex2 = Value::new_spn_str("testperson1", "new.example.com");
|
||||
assert!(vx2 == ex2);
|
||||
|
||||
|
@ -2009,9 +2000,9 @@ async fn test_repl_increment_domain_rename(server_a: &QueryServer, server_b: &Qu
|
|||
.expect("Unable to access new entry.");
|
||||
let e2 = server_b_txn
|
||||
.internal_search_all_uuid(UUID_ADMIN)
|
||||
.expect("Unable to access entry.");
|
||||
.expect("get_ava_single(spn).");
|
||||
|
||||
let vx1 = e1.get_ava_single("spn").expect("spn not present");
|
||||
let vx1 = e1.get_ava_single(Attribute::Spn).expect("spn not present");
|
||||
let ex1 = Value::new_spn_str("admin", "new.example.com");
|
||||
assert!(vx1 == ex1);
|
||||
assert!(e1 == e2);
|
||||
|
@ -2028,7 +2019,7 @@ async fn test_repl_increment_domain_rename(server_a: &QueryServer, server_b: &Qu
|
|||
.internal_search_all_uuid(t_uuid)
|
||||
.expect("Unable to access entry.");
|
||||
|
||||
let vx2 = e2.get_ava_single("spn").expect("spn not present");
|
||||
let vx2 = e2.get_ava_single(Attribute::Spn).expect("spn not present");
|
||||
let ex2 = Value::new_spn_str("testperson1", "new.example.com");
|
||||
assert!(vx2 == ex2);
|
||||
assert!(e1 == e2);
|
||||
|
@ -2182,10 +2173,10 @@ async fn test_repl_increment_memberof_basic(server_a: &QueryServer, server_b: &Q
|
|||
.expect("Unable to access entry.");
|
||||
|
||||
assert!(e1 == e2);
|
||||
assert!(e1.attribute_equality(Attribute::MemberOf.as_ref(), &PartialValue::Refer(g_uuid)));
|
||||
assert!(e1.attribute_equality(Attribute::MemberOf, &PartialValue::Refer(g_uuid)));
|
||||
// We should also check dyngroups too here :)
|
||||
assert!(e1.attribute_equality(
|
||||
Attribute::MemberOf.as_ref(),
|
||||
Attribute::MemberOf,
|
||||
&PartialValue::Refer(UUID_IDM_ALL_ACCOUNTS)
|
||||
));
|
||||
|
||||
|
@ -2261,16 +2252,16 @@ async fn test_repl_increment_memberof_conflict(server_a: &QueryServer, server_b:
|
|||
let e = server_b_txn
|
||||
.internal_search_all_uuid(g_uuid)
|
||||
.expect("Unable to access entry.");
|
||||
assert!(!e.attribute_equality(Attribute::Member.as_ref(), &PartialValue::Refer(t_uuid)));
|
||||
assert!(!e.attribute_equality(Attribute::Member, &PartialValue::Refer(t_uuid)));
|
||||
assert!(e.attribute_equality(
|
||||
Attribute::Name.as_ref(),
|
||||
Attribute::Name,
|
||||
&PartialValue::new_iname("testgroup_conflict")
|
||||
));
|
||||
|
||||
let e = server_b_txn
|
||||
.internal_search_all_uuid(t_uuid)
|
||||
.expect("Unable to access entry.");
|
||||
assert!(!e.attribute_equality(Attribute::MemberOf.as_ref(), &PartialValue::Refer(g_uuid)));
|
||||
assert!(!e.attribute_equality(Attribute::MemberOf, &PartialValue::Refer(g_uuid)));
|
||||
|
||||
server_b_txn.commit().expect("Failed to commit");
|
||||
drop(server_a_txn);
|
||||
|
@ -2290,9 +2281,9 @@ async fn test_repl_increment_memberof_conflict(server_a: &QueryServer, server_b:
|
|||
.expect("Unable to access entry.");
|
||||
|
||||
assert!(e1 == e2);
|
||||
assert!(!e1.attribute_equality(Attribute::Member.as_ref(), &PartialValue::Refer(t_uuid)));
|
||||
assert!(!e1.attribute_equality(Attribute::Member, &PartialValue::Refer(t_uuid)));
|
||||
assert!(e1.attribute_equality(
|
||||
Attribute::Name.as_ref(),
|
||||
Attribute::Name,
|
||||
&PartialValue::new_iname("testgroup_conflict")
|
||||
));
|
||||
|
||||
|
@ -2304,7 +2295,7 @@ async fn test_repl_increment_memberof_conflict(server_a: &QueryServer, server_b:
|
|||
.expect("Unable to access entry.");
|
||||
|
||||
assert!(e1 == e2);
|
||||
assert!(!e1.attribute_equality(Attribute::MemberOf.as_ref(), &PartialValue::Refer(g_uuid)));
|
||||
assert!(!e1.attribute_equality(Attribute::MemberOf, &PartialValue::Refer(g_uuid)));
|
||||
|
||||
server_a_txn.commit().expect("Failed to commit");
|
||||
drop(server_b_txn);
|
||||
|
@ -2375,7 +2366,7 @@ async fn test_repl_increment_refint_tombstone(server_a: &QueryServer, server_b:
|
|||
assert!(server_a_txn
|
||||
.internal_modify_uuid(
|
||||
g_uuid,
|
||||
&ModifyList::new_purge_and_set(Attribute::Member.as_ref(), Value::Refer(t_uuid))
|
||||
&ModifyList::new_purge_and_set(Attribute::Member, Value::Refer(t_uuid))
|
||||
)
|
||||
.is_ok());
|
||||
server_a_txn.commit().expect("Failed to commit");
|
||||
|
@ -2392,7 +2383,7 @@ async fn test_repl_increment_refint_tombstone(server_a: &QueryServer, server_b:
|
|||
let e = server_b_txn
|
||||
.internal_search_all_uuid(g_uuid)
|
||||
.expect("Unable to access entry.");
|
||||
assert!(!e.attribute_equality(Attribute::Member.as_ref(), &PartialValue::Refer(t_uuid)));
|
||||
assert!(!e.attribute_equality(Attribute::Member, &PartialValue::Refer(t_uuid)));
|
||||
|
||||
server_b_txn.commit().expect("Failed to commit");
|
||||
drop(server_a_txn);
|
||||
|
@ -2418,7 +2409,7 @@ async fn test_repl_increment_refint_tombstone(server_a: &QueryServer, server_b:
|
|||
assert!(e1_cs == e2_cs);
|
||||
|
||||
assert!(e1 == e2);
|
||||
assert!(!e1.attribute_equality(Attribute::Member.as_ref(), &PartialValue::Refer(t_uuid)));
|
||||
assert!(!e1.attribute_equality(Attribute::Member, &PartialValue::Refer(t_uuid)));
|
||||
|
||||
server_a_txn.commit().expect("Failed to commit");
|
||||
drop(server_b_txn);
|
||||
|
@ -2495,7 +2486,7 @@ async fn test_repl_increment_refint_conflict(server_a: &QueryServer, server_b: &
|
|||
let e = server_b_txn
|
||||
.internal_search_all_uuid(g_uuid)
|
||||
.expect("Unable to access entry.");
|
||||
assert!(!e.attribute_equality(Attribute::Member.as_ref(), &PartialValue::Refer(t_uuid)));
|
||||
assert!(!e.attribute_equality(Attribute::Member, &PartialValue::Refer(t_uuid)));
|
||||
|
||||
server_b_txn.commit().expect("Failed to commit");
|
||||
drop(server_a_txn);
|
||||
|
@ -2519,7 +2510,7 @@ async fn test_repl_increment_refint_conflict(server_a: &QueryServer, server_b: &
|
|||
assert!(e1_cs == e2_cs);
|
||||
|
||||
assert!(e1 == e2);
|
||||
assert!(!e1.attribute_equality(Attribute::Member.as_ref(), &PartialValue::Refer(t_uuid)));
|
||||
assert!(!e1.attribute_equality(Attribute::Member, &PartialValue::Refer(t_uuid)));
|
||||
|
||||
server_a_txn.commit().expect("Failed to commit");
|
||||
drop(server_b_txn);
|
||||
|
@ -2587,7 +2578,7 @@ async fn test_repl_increment_refint_delete_to_member_holder(
|
|||
assert!(server_a_txn
|
||||
.internal_modify_uuid(
|
||||
g_uuid,
|
||||
&ModifyList::new_purge_and_set(Attribute::Member.as_ref(), Value::Refer(t_uuid))
|
||||
&ModifyList::new_purge_and_set(Attribute::Member, Value::Refer(t_uuid))
|
||||
)
|
||||
.is_ok());
|
||||
server_a_txn.commit().expect("Failed to commit");
|
||||
|
@ -2609,7 +2600,7 @@ async fn test_repl_increment_refint_delete_to_member_holder(
|
|||
let e = server_a_txn
|
||||
.internal_search_all_uuid(g_uuid)
|
||||
.expect("Unable to access entry.");
|
||||
assert!(!e.attribute_equality(Attribute::Member.as_ref(), &PartialValue::Refer(t_uuid)));
|
||||
assert!(!e.attribute_equality(Attribute::Member, &PartialValue::Refer(t_uuid)));
|
||||
|
||||
server_a_txn.commit().expect("Failed to commit");
|
||||
drop(server_b_txn);
|
||||
|
@ -2634,7 +2625,7 @@ async fn test_repl_increment_refint_delete_to_member_holder(
|
|||
|
||||
assert!(e1_cs == e2_cs);
|
||||
assert!(e1 == e2);
|
||||
assert!(!e1.attribute_equality(Attribute::Member.as_ref(), &PartialValue::Refer(t_uuid)));
|
||||
assert!(!e1.attribute_equality(Attribute::Member, &PartialValue::Refer(t_uuid)));
|
||||
|
||||
server_b_txn.commit().expect("Failed to commit");
|
||||
drop(server_a_txn);
|
||||
|
@ -2746,10 +2737,7 @@ async fn test_repl_increment_attrunique_conflict_basic(
|
|||
assert!(server_a_txn
|
||||
.internal_modify_uuid(
|
||||
g_a_uuid,
|
||||
&ModifyList::new_purge_and_set(
|
||||
Attribute::Name.as_ref(),
|
||||
Value::new_iname("name_conflict")
|
||||
)
|
||||
&ModifyList::new_purge_and_set(Attribute::Name, Value::new_iname("name_conflict"))
|
||||
)
|
||||
.is_ok());
|
||||
server_a_txn.commit().expect("Failed to commit");
|
||||
|
@ -2758,10 +2746,7 @@ async fn test_repl_increment_attrunique_conflict_basic(
|
|||
assert!(server_b_txn
|
||||
.internal_modify_uuid(
|
||||
g_b_uuid,
|
||||
&ModifyList::new_purge_and_set(
|
||||
Attribute::Name.as_ref(),
|
||||
Value::new_iname("name_conflict")
|
||||
)
|
||||
&ModifyList::new_purge_and_set(Attribute::Name, Value::new_iname("name_conflict"))
|
||||
)
|
||||
.is_ok());
|
||||
server_b_txn.commit().expect("Failed to commit");
|
||||
|
@ -2785,28 +2770,28 @@ async fn test_repl_increment_attrunique_conflict_basic(
|
|||
.expect("Unable to search conflict entries.")
|
||||
.pop()
|
||||
.expect("No conflict entries present");
|
||||
assert!(cnf_a.get_ava_single_iname("name") == Some("name_conflict"));
|
||||
assert!(cnf_a.get_ava_single_iname(Attribute::Name) == Some("name_conflict"));
|
||||
|
||||
let cnf_b = server_a_txn
|
||||
.internal_search_conflict_uuid(g_b_uuid)
|
||||
.expect("Unable to search conflict entries.")
|
||||
.pop()
|
||||
.expect("No conflict entries present");
|
||||
assert!(cnf_b.get_ava_single_iname("name") == Some("name_conflict"));
|
||||
assert!(cnf_b.get_ava_single_iname(Attribute::Name) == Some("name_conflict"));
|
||||
|
||||
// Check the person has MO A/B removed.
|
||||
let e = server_a_txn
|
||||
.internal_search_all_uuid(t_uuid)
|
||||
.expect("Unable to access entry.");
|
||||
assert!(!e.attribute_equality(Attribute::MemberOf.as_ref(), &PartialValue::Refer(g_a_uuid)));
|
||||
assert!(!e.attribute_equality(Attribute::MemberOf.as_ref(), &PartialValue::Refer(g_b_uuid)));
|
||||
assert!(!e.attribute_equality(Attribute::MemberOf, &PartialValue::Refer(g_a_uuid)));
|
||||
assert!(!e.attribute_equality(Attribute::MemberOf, &PartialValue::Refer(g_b_uuid)));
|
||||
|
||||
// Check the group has M A/B removed.
|
||||
let e = server_a_txn
|
||||
.internal_search_all_uuid(g_c_uuid)
|
||||
.expect("Unable to access entry.");
|
||||
assert!(!e.attribute_equality(Attribute::Member.as_ref(), &PartialValue::Refer(g_a_uuid)));
|
||||
assert!(!e.attribute_equality(Attribute::Member.as_ref(), &PartialValue::Refer(g_b_uuid)));
|
||||
assert!(!e.attribute_equality(Attribute::Member, &PartialValue::Refer(g_a_uuid)));
|
||||
assert!(!e.attribute_equality(Attribute::Member, &PartialValue::Refer(g_b_uuid)));
|
||||
|
||||
server_a_txn.commit().expect("Failed to commit");
|
||||
drop(server_b_txn);
|
||||
|
@ -2824,14 +2809,14 @@ async fn test_repl_increment_attrunique_conflict_basic(
|
|||
.expect("Unable to search conflict entries.")
|
||||
.pop()
|
||||
.expect("No conflict entries present");
|
||||
assert!(cnf_a.get_ava_single_iname("name") == Some("name_conflict"));
|
||||
assert!(cnf_a.get_ava_single_iname(Attribute::Name) == Some("name_conflict"));
|
||||
|
||||
let cnf_b = server_b_txn
|
||||
.internal_search_conflict_uuid(g_b_uuid)
|
||||
.expect("Unable to search conflict entries.")
|
||||
.pop()
|
||||
.expect("No conflict entries present");
|
||||
assert!(cnf_b.get_ava_single_iname("name") == Some("name_conflict"));
|
||||
assert!(cnf_b.get_ava_single_iname(Attribute::Name) == Some("name_conflict"));
|
||||
|
||||
server_b_txn.commit().expect("Failed to commit");
|
||||
drop(server_a_txn);
|
||||
|
@ -2912,18 +2897,12 @@ async fn test_repl_increment_attrunique_conflict_complex(
|
|||
let e = server_a_txn
|
||||
.internal_search_all_uuid(g_a_uuid)
|
||||
.expect("Unable to access entry.");
|
||||
assert!(e.attribute_equality(
|
||||
Attribute::Name.as_ref(),
|
||||
&PartialValue::new_iname("name_conflict")
|
||||
));
|
||||
assert!(e.attribute_equality(Attribute::Name, &PartialValue::new_iname("name_conflict")));
|
||||
|
||||
let e = server_a_txn
|
||||
.internal_search_all_uuid(g_b_uuid)
|
||||
.expect("Unable to access entry.");
|
||||
assert!(e.attribute_equality(
|
||||
Attribute::Name.as_ref(),
|
||||
&PartialValue::new_iname("uuid_conflict")
|
||||
));
|
||||
assert!(e.attribute_equality(Attribute::Name, &PartialValue::new_iname("uuid_conflict")));
|
||||
|
||||
// The other entry will not be conflicted here, since A is not the origin node.
|
||||
|
||||
|
@ -2943,18 +2922,12 @@ async fn test_repl_increment_attrunique_conflict_complex(
|
|||
let e = server_b_txn
|
||||
.internal_search_all_uuid(g_a_uuid)
|
||||
.expect("Unable to access entry.");
|
||||
assert!(e.attribute_equality(
|
||||
Attribute::Name.as_ref(),
|
||||
&PartialValue::new_iname("name_conflict")
|
||||
));
|
||||
assert!(e.attribute_equality(Attribute::Name, &PartialValue::new_iname("name_conflict")));
|
||||
|
||||
let e = server_b_txn
|
||||
.internal_search_all_uuid(g_b_uuid)
|
||||
.expect("Unable to access entry.");
|
||||
assert!(e.attribute_equality(
|
||||
Attribute::Name.as_ref(),
|
||||
&PartialValue::new_iname("uuid_conflict")
|
||||
));
|
||||
assert!(e.attribute_equality(Attribute::Name, &PartialValue::new_iname("uuid_conflict")));
|
||||
|
||||
// Check the conflict was also now created.
|
||||
let cnf_a = server_b_txn
|
||||
|
@ -2962,7 +2935,7 @@ async fn test_repl_increment_attrunique_conflict_complex(
|
|||
.expect("Unable to search conflict entries.")
|
||||
.pop()
|
||||
.expect("No conflict entries present");
|
||||
assert!(cnf_a.get_ava_single_iname("name") == Some("name_conflict"));
|
||||
assert!(cnf_a.get_ava_single_iname(Attribute::Name) == Some("name_conflict"));
|
||||
|
||||
server_b_txn.commit().expect("Failed to commit");
|
||||
drop(server_a_txn);
|
||||
|
|
|
@ -101,16 +101,21 @@ impl SchemaAttribute {
|
|||
let uuid = value.get_uuid();
|
||||
|
||||
// class
|
||||
if !value.attribute_equality(Attribute::Class.as_ref(), &EntryClass::AttributeType.into()) {
|
||||
admin_error!("class attribute type not present - {:?}", uuid);
|
||||
return Err(OperationError::InvalidSchemaState(
|
||||
"missing attributetype".to_string(),
|
||||
));
|
||||
if !value.attribute_equality(Attribute::Class, &EntryClass::AttributeType.into()) {
|
||||
admin_error!(
|
||||
"class {} not present - {:?}",
|
||||
EntryClass::AttributeType,
|
||||
uuid
|
||||
);
|
||||
return Err(OperationError::InvalidSchemaState(format!(
|
||||
"missing {}",
|
||||
EntryClass::AttributeType
|
||||
)));
|
||||
}
|
||||
|
||||
// name
|
||||
let name = value
|
||||
.get_ava_single_iutf8(Attribute::AttributeName.into())
|
||||
.get_ava_single_iutf8(Attribute::AttributeName)
|
||||
.map(|s| s.into())
|
||||
.ok_or_else(|| {
|
||||
admin_error!("missing {} - {:?}", Attribute::AttributeName.as_ref(), uuid);
|
||||
|
@ -118,7 +123,7 @@ impl SchemaAttribute {
|
|||
})?;
|
||||
// description
|
||||
let description = value
|
||||
.get_ava_single_utf8(Attribute::Description.as_ref())
|
||||
.get_ava_single_utf8(Attribute::Description)
|
||||
.map(|s| s.to_string())
|
||||
.ok_or_else(|| {
|
||||
admin_error!("missing {} - {}", Attribute::Description, name);
|
||||
|
@ -127,44 +132,46 @@ impl SchemaAttribute {
|
|||
|
||||
// multivalue
|
||||
let multivalue = value
|
||||
.get_ava_single_bool(Attribute::MultiValue.as_ref())
|
||||
.get_ava_single_bool(Attribute::MultiValue)
|
||||
.ok_or_else(|| {
|
||||
admin_error!("missing {} - {}", Attribute::MultiValue, name);
|
||||
OperationError::InvalidSchemaState("missing multivalue".to_string())
|
||||
})?;
|
||||
let unique = value
|
||||
.get_ava_single_bool(Attribute::Unique.as_ref())
|
||||
.get_ava_single_bool(Attribute::Unique)
|
||||
.ok_or_else(|| {
|
||||
admin_error!("missing {} - {}", Attribute::Unique, name);
|
||||
OperationError::InvalidSchemaState("missing unique".to_string())
|
||||
})?;
|
||||
|
||||
let phantom = value
|
||||
.get_ava_single_bool(Attribute::Phantom.as_ref())
|
||||
.get_ava_single_bool(Attribute::Phantom)
|
||||
.unwrap_or(false);
|
||||
|
||||
let sync_allowed = value
|
||||
.get_ava_single_bool(Attribute::SyncAllowed.as_ref())
|
||||
.get_ava_single_bool(Attribute::SyncAllowed)
|
||||
.unwrap_or(false);
|
||||
|
||||
// Default, all attributes are replicated unless you opt in for them to NOT be.
|
||||
// Generally this is internal to the server only, so we don't advertise it.
|
||||
let replicated = value
|
||||
.get_ava_single_bool(Attribute::Replicated.as_ref())
|
||||
.get_ava_single_bool(Attribute::Replicated)
|
||||
.unwrap_or(true);
|
||||
|
||||
// index vec
|
||||
// even if empty, it SHOULD be present ... (is that valid to put an empty set?)
|
||||
// The get_ava_opt_index handles the optional case for us :)
|
||||
let index = value.get_ava_opt_index(ATTR_INDEX).ok_or_else(|| {
|
||||
let index = value.get_ava_opt_index(Attribute::Index).ok_or_else(|| {
|
||||
admin_error!("invalid {} - {}", Attribute::Index, name);
|
||||
OperationError::InvalidSchemaState(format!("invalid {}", ATTR_INDEX))
|
||||
OperationError::InvalidSchemaState(format!("invalid {}", Attribute::Index))
|
||||
})?;
|
||||
// syntax type
|
||||
let syntax = value.get_ava_single_syntax(ATTR_SYNTAX).ok_or_else(|| {
|
||||
admin_error!("missing {} - {}", Attribute::Syntax, name);
|
||||
OperationError::InvalidSchemaState(format!("missing {}", ATTR_SYNTAX))
|
||||
})?;
|
||||
let syntax = value
|
||||
.get_ava_single_syntax(Attribute::Syntax)
|
||||
.ok_or_else(|| {
|
||||
admin_error!("missing {} - {}", Attribute::Syntax, name);
|
||||
OperationError::InvalidSchemaState(format!("missing {}", Attribute::Syntax))
|
||||
})?;
|
||||
|
||||
Ok(SchemaAttribute {
|
||||
name,
|
||||
|
@ -403,7 +410,7 @@ impl SchemaClass {
|
|||
// uuid
|
||||
let uuid = value.get_uuid();
|
||||
// Convert entry to a schema class.
|
||||
if !value.attribute_equality(Attribute::Class.as_ref(), &EntryClass::ClassType.into()) {
|
||||
if !value.attribute_equality(Attribute::Class, &EntryClass::ClassType.into()) {
|
||||
admin_error!("class classtype not present - {:?}", uuid);
|
||||
return Err(OperationError::InvalidSchemaState(
|
||||
"missing classtype".to_string(),
|
||||
|
@ -412,57 +419,57 @@ impl SchemaClass {
|
|||
|
||||
// name
|
||||
let name = value
|
||||
.get_ava_single_iutf8(Attribute::ClassName.as_ref())
|
||||
.get_ava_single_iutf8(Attribute::ClassName)
|
||||
.map(AttrString::from)
|
||||
.ok_or_else(|| {
|
||||
admin_error!("missing classname - {:?}", uuid);
|
||||
OperationError::InvalidSchemaState("missing classname".to_string())
|
||||
admin_error!("missing {} - {:?}", Attribute::ClassName, uuid);
|
||||
OperationError::InvalidSchemaState(format!("missing {}", Attribute::ClassName))
|
||||
})?;
|
||||
// description
|
||||
let description = value
|
||||
.get_ava_single_utf8(Attribute::Description.as_ref())
|
||||
.get_ava_single_utf8(Attribute::Description)
|
||||
.map(String::from)
|
||||
.ok_or_else(|| {
|
||||
admin_error!("missing description - {}", name);
|
||||
OperationError::InvalidSchemaState("missing description".to_string())
|
||||
admin_error!("missing {} - {}", Attribute::Description, name);
|
||||
OperationError::InvalidSchemaState(format!("missing {}", Attribute::Description))
|
||||
})?;
|
||||
|
||||
let sync_allowed = value
|
||||
.get_ava_single_bool(Attribute::SyncAllowed.as_ref())
|
||||
.get_ava_single_bool(Attribute::SyncAllowed)
|
||||
.unwrap_or(false);
|
||||
|
||||
// These are all "optional" lists of strings.
|
||||
let systemmay = value
|
||||
.get_ava_iter_iutf8(Attribute::SystemMay.as_ref())
|
||||
.get_ava_iter_iutf8(Attribute::SystemMay)
|
||||
.map(|i| i.map(|v| v.into()).collect())
|
||||
.unwrap_or_else(Vec::new);
|
||||
let systemmust = value
|
||||
.get_ava_iter_iutf8(Attribute::SystemMust.as_ref())
|
||||
.get_ava_iter_iutf8(Attribute::SystemMust)
|
||||
.map(|i| i.map(|v| v.into()).collect())
|
||||
.unwrap_or_else(Vec::new);
|
||||
let may = value
|
||||
.get_ava_iter_iutf8(Attribute::May.as_ref())
|
||||
.get_ava_iter_iutf8(Attribute::May)
|
||||
.map(|i| i.map(|v| v.into()).collect())
|
||||
.unwrap_or_else(Vec::new);
|
||||
let must = value
|
||||
.get_ava_iter_iutf8(Attribute::Must.as_ref())
|
||||
.get_ava_iter_iutf8(Attribute::Must)
|
||||
.map(|i| i.map(|v| v.into()).collect())
|
||||
.unwrap_or_else(Vec::new);
|
||||
|
||||
let systemsupplements = value
|
||||
.get_ava_iter_iutf8(Attribute::SystemSupplements.as_ref())
|
||||
.get_ava_iter_iutf8(Attribute::SystemSupplements)
|
||||
.map(|i| i.map(|v| v.into()).collect())
|
||||
.unwrap_or_else(Vec::new);
|
||||
let supplements = value
|
||||
.get_ava_iter_iutf8(Attribute::Supplements.as_ref())
|
||||
.get_ava_iter_iutf8(Attribute::Supplements)
|
||||
.map(|i| i.map(|v| v.into()).collect())
|
||||
.unwrap_or_else(Vec::new);
|
||||
let systemexcludes = value
|
||||
.get_ava_iter_iutf8(Attribute::SystemExcludes.as_ref())
|
||||
.get_ava_iter_iutf8(Attribute::SystemExcludes)
|
||||
.map(|i| i.map(|v| v.into()).collect())
|
||||
.unwrap_or_else(Vec::new);
|
||||
let excludes = value
|
||||
.get_ava_iter_iutf8(Attribute::Excludes.as_ref())
|
||||
.get_ava_iter_iutf8(Attribute::Excludes)
|
||||
.map(|i| i.map(|v| v.into()).collect())
|
||||
.unwrap_or_else(Vec::new);
|
||||
|
||||
|
@ -2355,7 +2362,6 @@ mod tests {
|
|||
|
||||
// Test single value string
|
||||
let single_value_string = SchemaAttribute {
|
||||
// class: vec![String::from("attributetype")],
|
||||
name: AttrString::from("single_value"),
|
||||
uuid: Uuid::new_v4(),
|
||||
description: String::from(""),
|
||||
|
@ -2379,7 +2385,6 @@ mod tests {
|
|||
// test multivalue string, boolean
|
||||
|
||||
let multi_value_string = SchemaAttribute {
|
||||
// class: vec![String::from("attributetype")],
|
||||
name: AttrString::from("mv_string"),
|
||||
uuid: Uuid::new_v4(),
|
||||
description: String::from(""),
|
||||
|
@ -2394,7 +2399,6 @@ mod tests {
|
|||
assert_eq!(r5, Ok(()));
|
||||
|
||||
let multi_value_boolean = SchemaAttribute {
|
||||
// class: vec![String::from("attributetype")],
|
||||
name: AttrString::from("mv_bool"),
|
||||
uuid: Uuid::new_v4(),
|
||||
description: String::from(""),
|
||||
|
@ -2426,7 +2430,6 @@ mod tests {
|
|||
|
||||
// syntax_id and index_type values
|
||||
let single_value_syntax = SchemaAttribute {
|
||||
// class: vec![String::from("attributetype")],
|
||||
name: AttrString::from("sv_syntax"),
|
||||
uuid: Uuid::new_v4(),
|
||||
description: String::from(""),
|
||||
|
@ -2447,7 +2450,6 @@ mod tests {
|
|||
);
|
||||
|
||||
let single_value_index = SchemaAttribute {
|
||||
// class: vec![String::from("attributetype")],
|
||||
name: AttrString::from("sv_index"),
|
||||
uuid: Uuid::new_v4(),
|
||||
description: String::from(""),
|
||||
|
@ -2690,7 +2692,9 @@ mod tests {
|
|||
|
||||
assert_eq!(
|
||||
e_extensible_phantom.validate(&schema),
|
||||
Err(SchemaError::PhantomAttribute("password_import".to_string()))
|
||||
Err(SchemaError::PhantomAttribute(
|
||||
Attribute::PasswordImport.to_string()
|
||||
))
|
||||
);
|
||||
|
||||
let e_extensible: Entry<EntryInvalid, EntryNew> = Entry::unsafe_from_entry_str(
|
||||
|
@ -2756,14 +2760,14 @@ mod tests {
|
|||
// Test mixed case attr name - this is a pass, due to normalisation
|
||||
let f_or_ok = filter_all!(f_andnot(f_and!([
|
||||
f_eq(Attribute::Class, EntryClass::AttributeType.into()),
|
||||
f_sub(Attribute::Class.as_ref(), EntryClass::ClassType.into()),
|
||||
f_sub(Attribute::Class, EntryClass::ClassType.into()),
|
||||
f_pres(Attribute::Class)
|
||||
])));
|
||||
assert_eq!(
|
||||
f_or_ok.validate(&schema),
|
||||
Ok(filter_valid!(f_andnot(f_and!([
|
||||
f_eq(Attribute::Class, EntryClass::AttributeType.into()),
|
||||
f_sub(Attribute::Class.as_ref(), EntryClass::ClassType.into()),
|
||||
f_sub(Attribute::Class, EntryClass::ClassType.into()),
|
||||
f_pres(Attribute::Class)
|
||||
]))))
|
||||
);
|
||||
|
@ -2868,7 +2872,7 @@ mod tests {
|
|||
|
||||
assert_eq!(
|
||||
e_service.validate(&schema),
|
||||
Err(SchemaError::RequiresNotSatisfied(vec!["account".to_string()]))
|
||||
Err(SchemaError::RequiresNotSatisfied(vec![Attribute::Account.to_string()]))
|
||||
);
|
||||
*/
|
||||
|
||||
|
|
|
@ -91,7 +91,7 @@ fn create_filter_entry<'a>(
|
|||
// I still think if this is None, we should just fail here ...
|
||||
// because it shouldn't be possible to match.
|
||||
|
||||
let create_classes: BTreeSet<&str> = match entry.get_ava_iter_iutf8(Attribute::Class.as_ref()) {
|
||||
let create_classes: BTreeSet<&str> = match entry.get_ava_iter_iutf8(Attribute::Class) {
|
||||
Some(s) => s.collect(),
|
||||
None => {
|
||||
admin_error!("Class set failed to build - corrupted entry?");
|
||||
|
@ -161,7 +161,7 @@ fn protected_filter_entry(ident: &Identity, entry: &Entry<EntryInit, EntryNew>)
|
|||
// Now check things ...
|
||||
|
||||
// For now we just block create on sync object
|
||||
if let Some(classes) = entry.get_ava_set(Attribute::Class.as_ref()) {
|
||||
if let Some(classes) = entry.get_ava_set(Attribute::Class) {
|
||||
if classes.contains(&EntryClass::SyncObject.into()) {
|
||||
// Block the mod
|
||||
security_access!("attempt to create with protected class type");
|
||||
|
|
|
@ -115,7 +115,7 @@ fn protected_filter_entry(ident: &Identity, entry: &Arc<EntrySealedCommitted>) -
|
|||
// Now check things ...
|
||||
|
||||
// For now we just block create on sync object
|
||||
if let Some(classes) = entry.get_ava_set(Attribute::Class.as_ref()) {
|
||||
if let Some(classes) = entry.get_ava_set(Attribute::Class) {
|
||||
if classes.contains(&EntryClass::SyncObject.into()) {
|
||||
// Block the mod
|
||||
security_access!("attempt to delete with protected class type");
|
||||
|
|
|
@ -378,10 +378,10 @@ pub trait AccessControlsTransaction<'a> {
|
|||
let disallow = me
|
||||
.modlist
|
||||
.iter()
|
||||
.any(|m| matches!(m, Modify::Purged(a) if a == "class"));
|
||||
.any(|m| matches!(m, Modify::Purged(a) if a == Attribute::Class.as_ref()));
|
||||
|
||||
if disallow {
|
||||
security_access!("Disallowing purge class in modification");
|
||||
security_access!("Disallowing purge {} in modification", Attribute::Class);
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
|
@ -417,7 +417,7 @@ pub trait AccessControlsTransaction<'a> {
|
|||
.iter()
|
||||
.filter_map(|m| match m {
|
||||
Modify::Present(a, v) => {
|
||||
if a.as_str() == "class" {
|
||||
if a == Attribute::Class.as_ref() {
|
||||
// Here we have an option<&str> which could mean there is a risk of
|
||||
// a malicious entity attempting to trick us by masking class mods
|
||||
// in non-iutf8 types. However, the server first won't respect their
|
||||
|
@ -430,7 +430,7 @@ pub trait AccessControlsTransaction<'a> {
|
|||
}
|
||||
}
|
||||
Modify::Removed(a, v) => {
|
||||
if a.as_str() == "class" {
|
||||
if a == Attribute::Class.as_ref() {
|
||||
v.to_str()
|
||||
} else {
|
||||
None
|
||||
|
@ -519,10 +519,10 @@ pub trait AccessControlsTransaction<'a> {
|
|||
|
||||
let disallow = modlist
|
||||
.iter()
|
||||
.any(|m| matches!(m, Modify::Purged(a) if a == "class"));
|
||||
.any(|m| matches!(m, Modify::Purged(a) if a == Attribute::Class.as_ref()));
|
||||
|
||||
if disallow {
|
||||
security_access!("Disallowing purge class in modification");
|
||||
security_access!("Disallowing purge in modification");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -551,7 +551,7 @@ pub trait AccessControlsTransaction<'a> {
|
|||
.iter()
|
||||
.filter_map(|m| match m {
|
||||
Modify::Present(a, v) => {
|
||||
if a.as_str() == "class" {
|
||||
if a == Attribute::Class.as_ref() {
|
||||
// Here we have an option<&str> which could mean there is a risk of
|
||||
// a malicious entity attempting to trick us by masking class mods
|
||||
// in non-iutf8 types. However, the server first won't respect their
|
||||
|
@ -564,7 +564,7 @@ pub trait AccessControlsTransaction<'a> {
|
|||
}
|
||||
}
|
||||
Modify::Removed(a, v) => {
|
||||
if a.as_str() == "class" {
|
||||
if a == Attribute::Class.as_ref() {
|
||||
v.to_str()
|
||||
} else {
|
||||
None
|
||||
|
@ -1882,7 +1882,7 @@ mod tests {
|
|||
Attribute::Name,
|
||||
PartialValue::new_iname("testperson1")
|
||||
)),
|
||||
modlist!([m_pres(Attribute::Name.as_ref(), &Value::new_iname("value"))]),
|
||||
modlist!([m_pres(Attribute::Name, &Value::new_iname("value"))]),
|
||||
);
|
||||
// Name rem
|
||||
let me_rem = ModifyEvent::new_impersonate_entry(
|
||||
|
@ -1891,10 +1891,7 @@ mod tests {
|
|||
Attribute::Name,
|
||||
PartialValue::new_iname("testperson1")
|
||||
)),
|
||||
modlist!([m_remove(
|
||||
Attribute::Name.as_ref(),
|
||||
&PartialValue::new_iname("value")
|
||||
)]),
|
||||
modlist!([m_remove(Attribute::Name, &PartialValue::new_iname("value"))]),
|
||||
);
|
||||
// Name purge
|
||||
let me_purge = ModifyEvent::new_impersonate_entry(
|
||||
|
@ -1903,7 +1900,7 @@ mod tests {
|
|||
Attribute::Name,
|
||||
PartialValue::new_iname("testperson1")
|
||||
)),
|
||||
modlist!([m_purge("name")]),
|
||||
modlist!([m_purge(Attribute::Name)]),
|
||||
);
|
||||
|
||||
// Class account pres
|
||||
|
@ -1913,7 +1910,7 @@ mod tests {
|
|||
Attribute::Name,
|
||||
PartialValue::new_iname("testperson1")
|
||||
)),
|
||||
modlist!([m_pres("class", &EntryClass::Account.to_value())]),
|
||||
modlist!([m_pres(Attribute::Class, &EntryClass::Account.to_value())]),
|
||||
);
|
||||
// Class account rem
|
||||
let me_rem_class = ModifyEvent::new_impersonate_entry(
|
||||
|
@ -1922,7 +1919,10 @@ mod tests {
|
|||
Attribute::Name,
|
||||
PartialValue::new_iname("testperson1")
|
||||
)),
|
||||
modlist!([m_remove("class", &EntryClass::Account.to_partialvalue())]),
|
||||
modlist!([m_remove(
|
||||
Attribute::Class,
|
||||
&EntryClass::Account.to_partialvalue()
|
||||
)]),
|
||||
);
|
||||
// Class purge
|
||||
let me_purge_class = ModifyEvent::new_impersonate_entry(
|
||||
|
@ -1931,7 +1931,7 @@ mod tests {
|
|||
Attribute::Name,
|
||||
PartialValue::new_iname("testperson1")
|
||||
)),
|
||||
modlist!([m_purge("class")]),
|
||||
modlist!([m_purge(Attribute::Class)]),
|
||||
);
|
||||
|
||||
// Allow name and class, class is account
|
||||
|
@ -2032,7 +2032,7 @@ mod tests {
|
|||
Attribute::Name,
|
||||
PartialValue::new_iname("testperson1")
|
||||
)),
|
||||
modlist!([m_pres(Attribute::Name.as_ref(), &Value::new_iname("value"))]),
|
||||
modlist!([m_pres(Attribute::Name, &Value::new_iname("value"))]),
|
||||
);
|
||||
|
||||
// Name present
|
||||
|
@ -2042,7 +2042,7 @@ mod tests {
|
|||
Attribute::Name,
|
||||
PartialValue::new_iname("testperson1")
|
||||
)),
|
||||
modlist!([m_pres(Attribute::Name.as_ref(), &Value::new_iname("value"))]),
|
||||
modlist!([m_pres(Attribute::Name, &Value::new_iname("value"))]),
|
||||
);
|
||||
|
||||
let acp_allow = AccessControlModify::from_raw(
|
||||
|
@ -2564,9 +2564,9 @@ mod tests {
|
|||
PartialValue::new_iname("testperson1")
|
||||
)),
|
||||
// Allow pres user_auth_token_session
|
||||
"user_auth_token_session name",
|
||||
&format!("{} {}", Attribute::UserAuthTokenSession, Attribute::Name),
|
||||
// Allow user_auth_token_session
|
||||
"user_auth_token_session name",
|
||||
&format!("{} {}", Attribute::UserAuthTokenSession, Attribute::Name),
|
||||
// And the class allowed is account, we don't use it though.
|
||||
EntryClass::Account.into(),
|
||||
);
|
||||
|
@ -2581,7 +2581,7 @@ mod tests {
|
|||
PartialValue::new_iname("testperson1")
|
||||
)),
|
||||
modlist!([m_pres(
|
||||
"user_auth_token_session",
|
||||
Attribute::UserAuthTokenSession,
|
||||
&Value::new_iname("value")
|
||||
)]),
|
||||
);
|
||||
|
@ -2593,7 +2593,7 @@ mod tests {
|
|||
PartialValue::new_iname("testperson1")
|
||||
)),
|
||||
modlist!([m_remove(
|
||||
"user_auth_token_session",
|
||||
Attribute::UserAuthTokenSession,
|
||||
&PartialValue::new_iname("value")
|
||||
)]),
|
||||
);
|
||||
|
@ -2604,7 +2604,7 @@ mod tests {
|
|||
Attribute::Name,
|
||||
PartialValue::new_iname("testperson1")
|
||||
)),
|
||||
modlist!([m_purge("user_auth_token_session")]),
|
||||
modlist!([m_purge(Attribute::UserAuthTokenSession)]),
|
||||
);
|
||||
|
||||
// Test allowed pres
|
||||
|
@ -2628,7 +2628,7 @@ mod tests {
|
|||
Attribute::Name,
|
||||
PartialValue::new_iname("testperson1")
|
||||
)),
|
||||
modlist!([m_pres(Attribute::Name.as_ref(), &Value::new_iname("value"))]),
|
||||
modlist!([m_pres(Attribute::Name, &Value::new_iname("value"))]),
|
||||
);
|
||||
// Name rem
|
||||
let me_rem = ModifyEvent::new_impersonate_entry(
|
||||
|
@ -2637,10 +2637,7 @@ mod tests {
|
|||
Attribute::Name,
|
||||
PartialValue::new_iname("testperson1")
|
||||
)),
|
||||
modlist!([m_remove(
|
||||
Attribute::Name.as_ref(),
|
||||
&PartialValue::new_iname("value")
|
||||
)]),
|
||||
modlist!([m_remove(Attribute::Name, &PartialValue::new_iname("value"))]),
|
||||
);
|
||||
// Name purge
|
||||
let me_purge = ModifyEvent::new_impersonate_entry(
|
||||
|
@ -2649,7 +2646,7 @@ mod tests {
|
|||
Attribute::Name,
|
||||
PartialValue::new_iname("testperson1")
|
||||
)),
|
||||
modlist!([m_purge("name")]),
|
||||
modlist!([m_purge(Attribute::Name)]),
|
||||
);
|
||||
|
||||
// Test reject pres
|
||||
|
|
|
@ -203,7 +203,7 @@ fn modify_sync_constrain<'a>(
|
|||
// * We are a sync object
|
||||
// * We have a sync_parent_uuid
|
||||
let is_sync = entry
|
||||
.get_ava_set(Attribute::Class.as_ref())
|
||||
.get_ava_set(Attribute::Class)
|
||||
.map(|classes| classes.contains(&EntryClass::SyncObject.into()))
|
||||
.unwrap_or(false);
|
||||
|
||||
|
@ -211,12 +211,12 @@ fn modify_sync_constrain<'a>(
|
|||
return AccessResult::Ignore;
|
||||
}
|
||||
|
||||
if let Some(sync_uuid) = entry.get_ava_single_refer("sync_parent_uuid") {
|
||||
if let Some(sync_uuid) = entry.get_ava_single_refer(Attribute::SyncParentUuid) {
|
||||
let mut set = btreeset![
|
||||
"user_auth_token_session",
|
||||
"oauth2_session",
|
||||
"oauth2_consent_scope_map",
|
||||
"credential_update_intent_token"
|
||||
Attribute::UserAuthTokenSession.as_ref(),
|
||||
Attribute::OAuth2Session.as_ref(),
|
||||
Attribute::OAuth2ConsentScopeMap.as_ref(),
|
||||
Attribute::CredentialUpdateIntentToken.as_ref()
|
||||
];
|
||||
|
||||
if let Some(sync_yield_authority) = sync_agreements.get(&sync_uuid) {
|
||||
|
|
|
@ -20,21 +20,19 @@ impl AccessControlSearch {
|
|||
qs: &mut QueryServerWriteTransaction,
|
||||
value: &Entry<EntrySealed, EntryCommitted>,
|
||||
) -> Result<Self, OperationError> {
|
||||
if !value.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
&EntryClass::AccessControlSearch.into(),
|
||||
) {
|
||||
admin_error!("class access_control_search not present.");
|
||||
return Err(OperationError::InvalidAcpState(
|
||||
"Missing access_control_search".to_string(),
|
||||
));
|
||||
if !value.attribute_equality(Attribute::Class, &EntryClass::AccessControlSearch.into()) {
|
||||
admin_error!("class {} not present.", EntryClass::AccessControlSearch);
|
||||
return Err(OperationError::InvalidAcpState(format!(
|
||||
"Missing {}",
|
||||
EntryClass::AccessControlSearch
|
||||
)));
|
||||
}
|
||||
|
||||
let attrs = value
|
||||
.get_ava_iter_iutf8("acp_search_attr")
|
||||
.get_ava_iter_iutf8(Attribute::AcpSearchAttr)
|
||||
.ok_or_else(|| {
|
||||
admin_error!("Missing acp_search_attr");
|
||||
OperationError::InvalidAcpState("Missing acp_search_attr".to_string())
|
||||
admin_error!("Missing {}", Attribute::AcpSearchAttr);
|
||||
OperationError::InvalidAcpState(format!("Missing {}", Attribute::AcpSearchAttr))
|
||||
})?
|
||||
.map(AttrString::from)
|
||||
.collect();
|
||||
|
@ -76,10 +74,7 @@ impl AccessControlDelete {
|
|||
qs: &mut QueryServerWriteTransaction,
|
||||
value: &Entry<EntrySealed, EntryCommitted>,
|
||||
) -> Result<Self, OperationError> {
|
||||
if !value.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
&EntryClass::AccessControlDelete.into(),
|
||||
) {
|
||||
if !value.attribute_equality(Attribute::Class, &EntryClass::AccessControlDelete.into()) {
|
||||
admin_error!("class access_control_delete not present.");
|
||||
return Err(OperationError::InvalidAcpState(
|
||||
"Missing access_control_delete".to_string(),
|
||||
|
@ -123,23 +118,21 @@ impl AccessControlCreate {
|
|||
qs: &mut QueryServerWriteTransaction,
|
||||
value: &Entry<EntrySealed, EntryCommitted>,
|
||||
) -> Result<Self, OperationError> {
|
||||
if !value.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
&EntryClass::AccessControlCreate.into(),
|
||||
) {
|
||||
admin_error!("class access_control_create not present.");
|
||||
return Err(OperationError::InvalidAcpState(
|
||||
"Missing access_control_create".to_string(),
|
||||
));
|
||||
if !value.attribute_equality(Attribute::Class, &EntryClass::AccessControlCreate.into()) {
|
||||
admin_error!("class {} not present.", EntryClass::AccessControlCreate);
|
||||
return Err(OperationError::InvalidAcpState(format!(
|
||||
"Missing {}",
|
||||
EntryClass::AccessControlCreate
|
||||
)));
|
||||
}
|
||||
|
||||
let attrs = value
|
||||
.get_ava_iter_iutf8("acp_create_attr")
|
||||
.get_ava_iter_iutf8(Attribute::AcpCreateAttr)
|
||||
.map(|i| i.map(AttrString::from).collect())
|
||||
.unwrap_or_else(Vec::new);
|
||||
|
||||
let classes = value
|
||||
.get_ava_iter_iutf8("acp_create_class")
|
||||
.get_ava_iter_iutf8(Attribute::AcpCreateClass)
|
||||
.map(|i| i.map(AttrString::from).collect())
|
||||
.unwrap_or_else(Vec::new);
|
||||
|
||||
|
@ -187,10 +180,7 @@ impl AccessControlModify {
|
|||
qs: &mut QueryServerWriteTransaction,
|
||||
value: &Entry<EntrySealed, EntryCommitted>,
|
||||
) -> Result<Self, OperationError> {
|
||||
if !value.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
&EntryClass::AccessControlModify.into(),
|
||||
) {
|
||||
if !value.attribute_equality(Attribute::Class, &EntryClass::AccessControlModify.into()) {
|
||||
admin_error!("class access_control_modify not present.");
|
||||
return Err(OperationError::InvalidAcpState(
|
||||
"Missing access_control_modify".to_string(),
|
||||
|
@ -198,17 +188,17 @@ impl AccessControlModify {
|
|||
}
|
||||
|
||||
let presattrs = value
|
||||
.get_ava_iter_iutf8("acp_modify_presentattr")
|
||||
.get_ava_iter_iutf8(Attribute::AcpModifyPresentAttr)
|
||||
.map(|i| i.map(AttrString::from).collect())
|
||||
.unwrap_or_else(Vec::new);
|
||||
|
||||
let remattrs = value
|
||||
.get_ava_iter_iutf8("acp_modify_removedattr")
|
||||
.get_ava_iter_iutf8(Attribute::AcpModifyRemovedAttr)
|
||||
.map(|i| i.map(AttrString::from).collect())
|
||||
.unwrap_or_else(Vec::new);
|
||||
|
||||
let classes = value
|
||||
.get_ava_iter_iutf8("acp_modify_class")
|
||||
.get_ava_iter_iutf8(Attribute::AcpModifyClass)
|
||||
.map(|i| i.map(AttrString::from).collect())
|
||||
.unwrap_or_else(Vec::new);
|
||||
|
||||
|
@ -278,10 +268,7 @@ impl AccessControlProfile {
|
|||
value: &Entry<EntrySealed, EntryCommitted>,
|
||||
) -> Result<Self, OperationError> {
|
||||
// Assert we have class access_control_profile
|
||||
if !value.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
&EntryClass::AccessControlProfile.into(),
|
||||
) {
|
||||
if !value.attribute_equality(Attribute::Class, &EntryClass::AccessControlProfile.into()) {
|
||||
admin_error!("class access_control_profile not present.");
|
||||
return Err(OperationError::InvalidAcpState(
|
||||
"Missing access_control_profile".to_string(),
|
||||
|
@ -290,10 +277,10 @@ impl AccessControlProfile {
|
|||
|
||||
// copy name
|
||||
let name = value
|
||||
.get_ava_single_iname("name")
|
||||
.get_ava_single_iname(Attribute::Name)
|
||||
.ok_or_else(|| {
|
||||
admin_error!("Missing name");
|
||||
OperationError::InvalidAcpState("Missing name".to_string())
|
||||
admin_error!("Missing {}", Attribute::Name);
|
||||
OperationError::InvalidAcpState(format!("Missing {}", Attribute::Name))
|
||||
})?
|
||||
.to_string();
|
||||
// copy uuid
|
||||
|
@ -302,7 +289,7 @@ impl AccessControlProfile {
|
|||
|
||||
// === ⚠️ WARNING!!! ⚠️ ===
|
||||
// See struct ACP for details.
|
||||
let receiver = value.get_ava_single_refer(ATTR_ACP_RECEIVER_GROUP);
|
||||
let receiver = value.get_ava_single_refer(Attribute::AcpReceiverGroup);
|
||||
/*
|
||||
.ok_or_else(|| {
|
||||
admin_error!("Missing acp_receiver_group");
|
||||
|
@ -312,23 +299,23 @@ impl AccessControlProfile {
|
|||
|
||||
// targetscope, and turn to real filter
|
||||
let targetscope_f: ProtoFilter = value
|
||||
.get_ava_single_protofilter("acp_targetscope")
|
||||
.get_ava_single_protofilter(Attribute::AcpTargetScope)
|
||||
// .map(|pf| pf.clone())
|
||||
.cloned()
|
||||
.ok_or_else(|| {
|
||||
admin_error!("Missing acp_targetscope");
|
||||
OperationError::InvalidAcpState("Missing acp_targetscope".to_string())
|
||||
admin_error!("Missing {}", Attribute::AcpTargetScope);
|
||||
OperationError::InvalidAcpState(format!("Missing {}", Attribute::AcpTargetScope))
|
||||
})?;
|
||||
|
||||
let ident = Identity::from_internal();
|
||||
|
||||
let targetscope_i = Filter::from_rw(&ident, &targetscope_f, qs).map_err(|e| {
|
||||
admin_error!("Targetscope validation failed {:?}", e);
|
||||
admin_error!("{} validation failed {:?}", Attribute::AcpTargetScope, e);
|
||||
e
|
||||
})?;
|
||||
|
||||
let targetscope = targetscope_i.validate(qs.get_schema()).map_err(|e| {
|
||||
admin_error!("acp_targetscope Schema Violation {:?}", e);
|
||||
admin_error!("{} Schema Violation {:?}", Attribute::AcpTargetScope, e);
|
||||
OperationError::SchemaViolation(e)
|
||||
})?;
|
||||
|
||||
|
|
|
@ -127,15 +127,15 @@ fn search_oauth2_filter_entry<'a>(
|
|||
IdentType::Internal | IdentType::Synch(_) => AccessResult::Ignore,
|
||||
IdentType::User(iuser) => {
|
||||
let contains_o2_rs = entry
|
||||
.get_ava_as_iutf8(Attribute::Class.as_ref())
|
||||
.get_ava_as_iutf8(Attribute::Class)
|
||||
.map(|set| {
|
||||
trace!(?set);
|
||||
set.contains("oauth2_resource_server")
|
||||
set.contains(&EntryClass::OAuth2ResourceServer.to_string())
|
||||
})
|
||||
.unwrap_or(false);
|
||||
|
||||
let contains_o2_scope_member = entry
|
||||
.get_ava_as_oauthscopemaps("oauth2_rs_scope_map")
|
||||
.get_ava_as_oauthscopemaps(Attribute::OAuth2RsScopeMap)
|
||||
.and_then(|maps| ident.get_memberof().map(|mo| (maps, mo)))
|
||||
.map(|(maps, mo)| maps.keys().any(|k| mo.contains(k)))
|
||||
.unwrap_or(false);
|
||||
|
@ -167,19 +167,20 @@ fn search_sync_account_filter_entry<'a>(
|
|||
// Is the user a synced object?
|
||||
let is_user_sync_account = iuser
|
||||
.entry
|
||||
.get_ava_as_iutf8(Attribute::Class.as_ref())
|
||||
.get_ava_as_iutf8(Attribute::Class)
|
||||
.map(|set| {
|
||||
trace!(?set);
|
||||
set.contains("sync_object") && set.contains("account")
|
||||
set.contains(&EntryClass::SyncObject.to_string())
|
||||
&& set.contains(EntryClass::Account.into())
|
||||
})
|
||||
.unwrap_or(false);
|
||||
|
||||
if is_user_sync_account {
|
||||
let is_target_sync_account = entry
|
||||
.get_ava_as_iutf8(Attribute::Class.as_ref())
|
||||
.get_ava_as_iutf8(Attribute::Class)
|
||||
.map(|set| {
|
||||
trace!(?set);
|
||||
set.contains("sync_account")
|
||||
set.contains(&EntryClass::SyncAccount.to_string())
|
||||
})
|
||||
.unwrap_or(false);
|
||||
|
||||
|
@ -188,7 +189,7 @@ fn search_sync_account_filter_entry<'a>(
|
|||
let sync_uuid = entry.get_uuid();
|
||||
let sync_source_match = iuser
|
||||
.entry
|
||||
.get_ava_single_refer("sync_parent_uuid")
|
||||
.get_ava_single_refer(Attribute::SyncParentUuid)
|
||||
.map(|sync_parent_uuid| sync_parent_uuid == sync_uuid)
|
||||
.unwrap_or(false);
|
||||
|
||||
|
@ -197,9 +198,9 @@ fn search_sync_account_filter_entry<'a>(
|
|||
security_access!(entry = ?entry.get_uuid(), ident = ?iuser.entry.get_uuid2rdn(), "ident is a synchronsied account from this sync account");
|
||||
|
||||
return AccessResult::Allow(btreeset!(
|
||||
"class",
|
||||
"uuid",
|
||||
"sync_credential_portal"
|
||||
Attribute::Class.as_ref(),
|
||||
Attribute::Uuid.as_ref(),
|
||||
Attribute::SyncCredentialPortal.as_ref()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -189,11 +189,8 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
.iter()
|
||||
.chain(pre_candidates.iter().map(|e| e.as_ref()))
|
||||
.any(|e| {
|
||||
e.attribute_equality(Attribute::Class.as_ref(), &EntryClass::ClassType.into())
|
||||
|| e.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
&EntryClass::AttributeType.into(),
|
||||
)
|
||||
e.attribute_equality(Attribute::Class, &EntryClass::ClassType.into())
|
||||
|| e.attribute_equality(Attribute::Class, &EntryClass::AttributeType.into())
|
||||
});
|
||||
}
|
||||
if !self.changed_acp {
|
||||
|
@ -201,10 +198,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
.iter()
|
||||
.chain(pre_candidates.iter().map(|e| e.as_ref()))
|
||||
.any(|e| {
|
||||
e.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
&EntryClass::AccessControlProfile.into(),
|
||||
)
|
||||
e.attribute_equality(Attribute::Class, &EntryClass::AccessControlProfile.into())
|
||||
});
|
||||
}
|
||||
if !self.changed_oauth2 {
|
||||
|
@ -212,17 +206,14 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
.iter()
|
||||
.chain(pre_candidates.iter().map(|e| e.as_ref()))
|
||||
.any(|e| {
|
||||
e.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
&EntryClass::OAuth2ResourceServer.into(),
|
||||
)
|
||||
e.attribute_equality(Attribute::Class, &EntryClass::OAuth2ResourceServer.into())
|
||||
});
|
||||
}
|
||||
if !self.changed_domain {
|
||||
self.changed_domain = norm_cand
|
||||
.iter()
|
||||
.chain(pre_candidates.iter().map(|e| e.as_ref()))
|
||||
.any(|e| e.attribute_equality(Attribute::Uuid.as_ref(), &PVUUID_DOMAIN_INFO));
|
||||
.any(|e| e.attribute_equality(Attribute::Uuid, &PVUUID_DOMAIN_INFO));
|
||||
}
|
||||
|
||||
self.changed_uuid.extend(
|
||||
|
@ -296,17 +287,11 @@ mod tests {
|
|||
[
|
||||
(
|
||||
uuid_a,
|
||||
ModifyList::new_append(
|
||||
Attribute::Description.as_ref(),
|
||||
Value::Utf8("a".into())
|
||||
)
|
||||
ModifyList::new_append(Attribute::Description, Value::Utf8("a".into()))
|
||||
),
|
||||
(
|
||||
uuid_b,
|
||||
ModifyList::new_append(
|
||||
Attribute::Description.as_ref(),
|
||||
Value::Utf8("b".into())
|
||||
)
|
||||
ModifyList::new_append(Attribute::Description, Value::Utf8("b".into()))
|
||||
),
|
||||
]
|
||||
.into_iter()
|
||||
|
@ -321,7 +306,7 @@ mod tests {
|
|||
.internal_search_uuid(uuid_b)
|
||||
.expect("Failed to get entry.");
|
||||
|
||||
assert!(ent_a.get_ava_single_utf8(Attribute::Description.as_ref()) == Some("a"));
|
||||
assert!(ent_b.get_ava_single_utf8(Attribute::Description.as_ref()) == Some("b"));
|
||||
assert!(ent_a.get_ava_single_utf8(Attribute::Description) == Some("a"));
|
||||
assert!(ent_b.get_ava_single_utf8(Attribute::Description) == Some("b"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -107,38 +107,29 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
// schema or acp requires reload.
|
||||
if !self.changed_schema {
|
||||
self.changed_schema = commit_cand.iter().any(|e| {
|
||||
e.attribute_equality(Attribute::Class.as_ref(), &EntryClass::ClassType.into())
|
||||
|| e.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
&EntryClass::AttributeType.into(),
|
||||
)
|
||||
e.attribute_equality(Attribute::Class, &EntryClass::ClassType.into())
|
||||
|| e.attribute_equality(Attribute::Class, &EntryClass::AttributeType.into())
|
||||
});
|
||||
}
|
||||
if !self.changed_acp {
|
||||
self.changed_acp = commit_cand.iter().any(|e| {
|
||||
e.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
&EntryClass::AccessControlProfile.into(),
|
||||
)
|
||||
e.attribute_equality(Attribute::Class, &EntryClass::AccessControlProfile.into())
|
||||
});
|
||||
}
|
||||
if !self.changed_oauth2 {
|
||||
self.changed_oauth2 = commit_cand.iter().any(|e| {
|
||||
e.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
&EntryClass::OAuth2ResourceServer.into(),
|
||||
)
|
||||
e.attribute_equality(Attribute::Class, &EntryClass::OAuth2ResourceServer.into())
|
||||
});
|
||||
}
|
||||
if !self.changed_domain {
|
||||
self.changed_domain = commit_cand
|
||||
.iter()
|
||||
.any(|e| e.attribute_equality(Attribute::Uuid.as_ref(), &PVUUID_DOMAIN_INFO));
|
||||
.any(|e| e.attribute_equality(Attribute::Uuid, &PVUUID_DOMAIN_INFO));
|
||||
}
|
||||
if !self.changed_sync_agreement {
|
||||
self.changed_sync_agreement = commit_cand.iter().any(|e| {
|
||||
e.attribute_equality(Attribute::Class.as_ref(), &EntryClass::SyncAccount.into())
|
||||
});
|
||||
self.changed_sync_agreement = commit_cand
|
||||
.iter()
|
||||
.any(|e| e.attribute_equality(Attribute::Class, &EntryClass::SyncAccount.into()));
|
||||
}
|
||||
|
||||
self.changed_uuid
|
||||
|
@ -233,7 +224,7 @@ mod tests {
|
|||
let key = r2
|
||||
.first()
|
||||
.unwrap()
|
||||
.get_ava_single_eckey_private(Attribute::IdVerificationEcKey.as_ref())
|
||||
.get_ava_single_eckey_private(Attribute::IdVerificationEcKey)
|
||||
.unwrap();
|
||||
|
||||
e.add_ava(
|
||||
|
|
|
@ -102,38 +102,29 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
// schema or acp requires reload.
|
||||
if !self.changed_schema {
|
||||
self.changed_schema = del_cand.iter().any(|e| {
|
||||
e.attribute_equality(Attribute::Class.as_ref(), &EntryClass::ClassType.into())
|
||||
|| e.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
&EntryClass::AttributeType.into(),
|
||||
)
|
||||
e.attribute_equality(Attribute::Class, &EntryClass::ClassType.into())
|
||||
|| e.attribute_equality(Attribute::Class, &EntryClass::AttributeType.into())
|
||||
});
|
||||
}
|
||||
if !self.changed_acp {
|
||||
self.changed_acp = del_cand.iter().any(|e| {
|
||||
e.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
&EntryClass::AccessControlProfile.into(),
|
||||
)
|
||||
e.attribute_equality(Attribute::Class, &EntryClass::AccessControlProfile.into())
|
||||
});
|
||||
}
|
||||
if !self.changed_oauth2 {
|
||||
self.changed_oauth2 = del_cand.iter().any(|e| {
|
||||
e.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
&EntryClass::OAuth2ResourceServer.into(),
|
||||
)
|
||||
e.attribute_equality(Attribute::Class, &EntryClass::OAuth2ResourceServer.into())
|
||||
});
|
||||
}
|
||||
if !self.changed_domain {
|
||||
self.changed_domain = del_cand
|
||||
.iter()
|
||||
.any(|e| e.attribute_equality(Attribute::Uuid.as_ref(), &PVUUID_DOMAIN_INFO));
|
||||
.any(|e| e.attribute_equality(Attribute::Uuid, &PVUUID_DOMAIN_INFO));
|
||||
}
|
||||
if !self.changed_sync_agreement {
|
||||
self.changed_sync_agreement = del_cand.iter().any(|e| {
|
||||
e.attribute_equality(Attribute::Uuid.as_ref(), &EntryClass::SyncAccount.into())
|
||||
});
|
||||
self.changed_sync_agreement = del_cand
|
||||
.iter()
|
||||
.any(|e| e.attribute_equality(Attribute::Uuid, &EntryClass::SyncAccount.into()));
|
||||
}
|
||||
|
||||
self.changed_uuid
|
||||
|
|
|
@ -181,7 +181,7 @@ impl Identity {
|
|||
IdentType::Internal | IdentType::Synch(_) => None,
|
||||
IdentType::User(u) => u
|
||||
.entry
|
||||
.get_ava_as_session_map("user_auth_token_session")
|
||||
.get_ava_as_session_map(Attribute::UserAuthTokenSession)
|
||||
.and_then(|sessions| sessions.get(&self.session_id)),
|
||||
}
|
||||
}
|
||||
|
@ -223,7 +223,7 @@ impl Identity {
|
|||
IdentType::Internal | IdentType::Synch(_) => false,
|
||||
IdentType::User(u) => u
|
||||
.entry
|
||||
.attribute_equality(Attribute::Claim.as_ref(), &PartialValue::new_iutf8(claim)),
|
||||
.attribute_equality(Attribute::Claim, &PartialValue::new_iutf8(claim)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -232,14 +232,14 @@ impl Identity {
|
|||
IdentType::Internal | IdentType::Synch(_) => false,
|
||||
IdentType::User(u) => u
|
||||
.entry
|
||||
.attribute_equality(Attribute::MemberOf.as_ref(), &PartialValue::Refer(group)),
|
||||
.attribute_equality(Attribute::MemberOf, &PartialValue::Refer(group)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_memberof(&self) -> Option<&BTreeSet<Uuid>> {
|
||||
match &self.origin {
|
||||
IdentType::Internal | IdentType::Synch(_) => None,
|
||||
IdentType::User(u) => u.entry.get_ava_refer("memberof"),
|
||||
IdentType::User(u) => u.entry.get_ava_refer(Attribute::MemberOf),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -248,7 +248,7 @@ impl Identity {
|
|||
IdentType::Internal | IdentType::Synch(_) => None,
|
||||
IdentType::User(u) => u
|
||||
.entry
|
||||
.get_ava_as_oauthscopemaps("oauth2_consent_scope_map")
|
||||
.get_ava_as_oauthscopemaps(Attribute::OAuth2ConsentScopeMap)
|
||||
.and_then(|scope_map| scope_map.get(&oauth2_rs)),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ impl QueryServer {
|
|||
// effect as ... there is nothing to migrate! It allows reset of the version to 0 to force
|
||||
// db migrations to take place.
|
||||
let system_info_version = match write_txn.internal_search_uuid(UUID_SYSTEM_INFO) {
|
||||
Ok(e) => Ok(e.get_ava_single_uint32("version").unwrap_or(0)),
|
||||
Ok(e) => Ok(e.get_ava_single_uint32(Attribute::Version).unwrap_or(0)),
|
||||
Err(OperationError::NoMatchingEntries) => Ok(0),
|
||||
Err(r) => Err(r),
|
||||
}?;
|
||||
|
@ -233,17 +233,17 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
candidates.iter_mut().try_for_each(|er| {
|
||||
// Migrate basic secrets if they exist.
|
||||
let nvs = er
|
||||
.get_ava_set("oauth2_rs_basic_secret")
|
||||
.get_ava_set(Attribute::OAuth2RsBasicSecret)
|
||||
.and_then(|vs| vs.as_utf8_iter())
|
||||
.and_then(|vs_iter| {
|
||||
ValueSetSecret::from_iter(vs_iter.map(|s: &str| s.to_string()))
|
||||
});
|
||||
if let Some(nvs) = nvs {
|
||||
er.set_ava_set("oauth2_rs_basic_secret", nvs)
|
||||
er.set_ava_set(Attribute::OAuth2RsBasicSecret, nvs)
|
||||
}
|
||||
|
||||
// Migrate implicit scopes if they exist.
|
||||
let nv = if let Some(vs) = er.get_ava_set(Attribute::OAuth2RsImplicitScopes.as_ref()) {
|
||||
let nv = if let Some(vs) = er.get_ava_set(Attribute::OAuth2RsImplicitScopes) {
|
||||
vs.as_oauthscope_set()
|
||||
.map(|v| Value::OauthScopeMap(UUID_IDM_ALL_PERSONS, v.clone()))
|
||||
} else {
|
||||
|
@ -253,7 +253,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
if let Some(nv) = nv {
|
||||
er.add_ava(Attribute::OAuth2RsScopeMap, nv)
|
||||
}
|
||||
er.purge_ava(Attribute::OAuth2RsImplicitScopes.as_ref());
|
||||
er.purge_ava(Attribute::OAuth2RsImplicitScopes);
|
||||
|
||||
Ok(())
|
||||
})?;
|
||||
|
@ -298,8 +298,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
]));
|
||||
// This "does nothing" since everything has object anyway, but it forces the entry to be
|
||||
// loaded and rewritten.
|
||||
let modlist =
|
||||
ModifyList::new_append(Attribute::Class.as_ref(), EntryClass::Object.to_value());
|
||||
let modlist = ModifyList::new_append(Attribute::Class, EntryClass::Object.to_value());
|
||||
self.internal_modify(&filter, &modlist)
|
||||
// Complete
|
||||
}
|
||||
|
@ -325,7 +324,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
let modset: Vec<_> = pre_candidates
|
||||
.into_iter()
|
||||
.filter_map(|ent| {
|
||||
ent.get_ava_single_credential("primary_credential")
|
||||
ent.get_ava_single_credential(Attribute::PrimaryCredential)
|
||||
.and_then(|cred| cred.passkey_ref().ok())
|
||||
.map(|pk_map| {
|
||||
let modlist = pk_map
|
||||
|
@ -336,7 +335,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
Value::Passkey(Uuid::new_v4(), t.clone(), k.clone()),
|
||||
)
|
||||
})
|
||||
.chain(std::iter::once(m_purge("primary_credential")))
|
||||
.chain(std::iter::once(m_purge(Attribute::PrimaryCredential)))
|
||||
.collect();
|
||||
(ent.get_uuid(), ModifyList::new_list(modlist))
|
||||
})
|
||||
|
@ -381,19 +380,22 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
// webauthn type.
|
||||
|
||||
for (_, ent) in mod_candidates.iter_mut() {
|
||||
if let Some(api_token_session) = ent.pop_ava("api_token_session") {
|
||||
if let Some(api_token_session) = ent.pop_ava(Attribute::ApiTokenSession) {
|
||||
let api_token_session =
|
||||
api_token_session
|
||||
.migrate_session_to_apitoken()
|
||||
.map_err(|e| {
|
||||
error!("Failed to convert api_token_session from session -> apitoken");
|
||||
error!(
|
||||
"Failed to convert {} from session -> apitoken",
|
||||
Attribute::ApiTokenSession
|
||||
);
|
||||
e
|
||||
})?;
|
||||
|
||||
ent.set_ava_set("api_token_session", api_token_session);
|
||||
ent.set_ava_set(Attribute::ApiTokenSession, api_token_session);
|
||||
}
|
||||
|
||||
if let Some(sync_token_session) = ent.pop_ava("sync_token_session") {
|
||||
if let Some(sync_token_session) = ent.pop_ava(Attribute::SyncTokenSession) {
|
||||
let sync_token_session =
|
||||
sync_token_session
|
||||
.migrate_session_to_apitoken()
|
||||
|
@ -402,7 +404,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
e
|
||||
})?;
|
||||
|
||||
ent.set_ava_set("sync_token_session", sync_token_session);
|
||||
ent.set_ava_set(Attribute::SyncTokenSession, sync_token_session);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -418,7 +420,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
f_eq(Attribute::Uuid, PVUUID_DOMAIN_INFO.clone()),
|
||||
]));
|
||||
// Delete the existing cookie key to trigger a regeneration.
|
||||
let modlist = ModifyList::new_purge(ATTR_PRIVATE_COOKIE_KEY);
|
||||
let modlist = ModifyList::new_purge(Attribute::PrivateCookieKey);
|
||||
self.internal_modify(&filter, &modlist)
|
||||
// Complete
|
||||
}
|
||||
|
@ -431,7 +433,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
EntryClass::DynGroup.to_partialvalue()
|
||||
));
|
||||
// Delete the incorrectly added "member" attr.
|
||||
let modlist = ModifyList::new_purge("member");
|
||||
let modlist = ModifyList::new_purge(Attribute::Member);
|
||||
self.internal_modify(&filter, &modlist)
|
||||
// Complete
|
||||
}
|
||||
|
@ -442,7 +444,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
let filter = filter!(f_eq(Attribute::Class, EntryClass::Person.into()));
|
||||
// Delete the non-existing attr for idv private key which triggers
|
||||
// it to regen.
|
||||
let modlist = ModifyList::new_purge("id_verification_eckey");
|
||||
let modlist = ModifyList::new_purge(Attribute::IdVerificationEcKey);
|
||||
self.internal_modify(&filter, &modlist)
|
||||
// Complete
|
||||
}
|
||||
|
@ -821,7 +823,7 @@ mod tests {
|
|||
ModifyEvent::new_internal_invalid(
|
||||
filter!(f_or!([
|
||||
f_eq(Attribute::AttributeName, Attribute::Name.to_partialvalue()),
|
||||
f_eq(Attribute::AttributeName, PartialValue::new_iutf8("domain_name")),
|
||||
f_eq(Attribute::AttributeName, Attribute::DomainName.into()),
|
||||
])),
|
||||
ModifyList::new_purge_and_set(
|
||||
"syntax",
|
||||
|
@ -864,7 +866,7 @@ mod tests {
|
|||
ModifyEvent::new_internal_invalid(
|
||||
filter!(f_or!([
|
||||
f_eq(Attribute::AttributeName, Attribute::Name.to_partialvalue()),
|
||||
f_eq(Attribute::AttributeName, PartialValue::new_iutf8("domain_name")),
|
||||
f_eq(Attribute::AttributeName, Attribute::DomainName.into()),
|
||||
])),
|
||||
ModifyList::new_purge_and_set(
|
||||
"syntax",
|
||||
|
@ -891,12 +893,12 @@ mod tests {
|
|||
.expect("failed");
|
||||
// ++ assert all names are iname
|
||||
assert!(
|
||||
domain.get_ava_set(Attribute::Name.as_ref()).expect("no name?").syntax() == SyntaxType::Utf8StringIname
|
||||
domain.get_ava_set(Attribute::Name).expect("no name?").syntax() == SyntaxType::Utf8StringIname
|
||||
);
|
||||
// ++ assert all domain/domain_name are iname
|
||||
assert!(
|
||||
domain
|
||||
.get_ava_set("domain_name")
|
||||
.get_ava_set(Attribute::DomainName.as_ref())
|
||||
.expect("no domain_name?")
|
||||
.syntax()
|
||||
== SyntaxType::Utf8StringIname
|
||||
|
|
|
@ -757,7 +757,7 @@ pub trait QueryServerTransaction<'a> {
|
|||
self.internal_search_uuid(UUID_DOMAIN_INFO)
|
||||
.and_then(|e| {
|
||||
trace!(?e);
|
||||
e.get_ava_single_iname("domain_name")
|
||||
e.get_ava_single_iname(Attribute::DomainName)
|
||||
.map(str::to_string)
|
||||
.ok_or(OperationError::InvalidEntryState)
|
||||
})
|
||||
|
@ -770,7 +770,7 @@ pub trait QueryServerTransaction<'a> {
|
|||
fn get_domain_fernet_private_key(&mut self) -> Result<String, OperationError> {
|
||||
self.internal_search_uuid(UUID_DOMAIN_INFO)
|
||||
.and_then(|e| {
|
||||
e.get_ava_single_secret("fernet_private_key_str")
|
||||
e.get_ava_single_secret(Attribute::FernetPrivateKeyStr)
|
||||
.map(str::to_string)
|
||||
.ok_or(OperationError::InvalidEntryState)
|
||||
})
|
||||
|
@ -783,7 +783,7 @@ pub trait QueryServerTransaction<'a> {
|
|||
fn get_domain_es256_private_key(&mut self) -> Result<Vec<u8>, OperationError> {
|
||||
self.internal_search_uuid(UUID_DOMAIN_INFO)
|
||||
.and_then(|e| {
|
||||
e.get_ava_single_private_binary("es256_private_key_der")
|
||||
e.get_ava_single_private_binary(Attribute::Es256PrivateKeyDer)
|
||||
.map(|s| s.to_vec())
|
||||
.ok_or(OperationError::InvalidEntryState)
|
||||
})
|
||||
|
@ -796,7 +796,7 @@ pub trait QueryServerTransaction<'a> {
|
|||
fn get_domain_cookie_key(&mut self) -> Result<[u8; 64], OperationError> {
|
||||
self.internal_search_uuid(UUID_DOMAIN_INFO)
|
||||
.and_then(|e| {
|
||||
e.get_ava_single_private_binary(ATTR_PRIVATE_COOKIE_KEY)
|
||||
e.get_ava_single_private_binary(Attribute::PrivateCookieKey)
|
||||
.and_then(|s| {
|
||||
let mut x = [0; 64];
|
||||
if s.len() == x.len() {
|
||||
|
@ -817,7 +817,7 @@ pub trait QueryServerTransaction<'a> {
|
|||
// This is a helper to get password badlist.
|
||||
fn get_password_badlist(&mut self) -> Result<HashSet<String>, OperationError> {
|
||||
self.internal_search_uuid(UUID_SYSTEM_CONFIG)
|
||||
.map(|e| match e.get_ava_iter_iutf8("badlist_password") {
|
||||
.map(|e| match e.get_ava_iter_iutf8(Attribute::BadlistPassword) {
|
||||
Some(vs_str_iter) => vs_str_iter.map(str::to_string).collect::<HashSet<_>>(),
|
||||
None => HashSet::default(),
|
||||
})
|
||||
|
@ -830,7 +830,7 @@ pub trait QueryServerTransaction<'a> {
|
|||
fn get_authsession_expiry(&mut self) -> Result<u32, OperationError> {
|
||||
self.internal_search_uuid(UUID_SYSTEM_CONFIG)
|
||||
.and_then(|e| {
|
||||
if let Some(expiry_time) = e.get_ava_single_uint32("authsession_expiry") {
|
||||
if let Some(expiry_time) = e.get_ava_single_uint32(Attribute::AuthSessionExpiry) {
|
||||
Ok(expiry_time)
|
||||
} else {
|
||||
Err(OperationError::NoMatchingAttributes)
|
||||
|
@ -848,7 +848,7 @@ pub trait QueryServerTransaction<'a> {
|
|||
fn get_privilege_expiry(&mut self) -> Result<u32, OperationError> {
|
||||
self.internal_search_uuid(UUID_SYSTEM_CONFIG)
|
||||
.and_then(|e| {
|
||||
if let Some(expiry_time) = e.get_ava_single_uint32("privilege_expiry") {
|
||||
if let Some(expiry_time) = e.get_ava_single_uint32(Attribute::PrivilegeExpiry) {
|
||||
Ok(expiry_time)
|
||||
} else {
|
||||
Err(OperationError::NoMatchingAttributes)
|
||||
|
@ -1358,7 +1358,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
let sync_agreement_map: HashMap<Uuid, BTreeSet<String>> = res
|
||||
.iter()
|
||||
.filter_map(|e| {
|
||||
e.get_ava_as_iutf8("sync_yield_authority")
|
||||
e.get_ava_as_iutf8(Attribute::SyncYieldAuthority)
|
||||
.cloned()
|
||||
.map(|set| (e.get_uuid(), set))
|
||||
})
|
||||
|
@ -1485,7 +1485,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
self.internal_search_uuid(UUID_DOMAIN_INFO)
|
||||
.and_then(|e| {
|
||||
trace!(?e);
|
||||
e.get_ava_single_utf8("domain_display_name")
|
||||
e.get_ava_single_utf8(Attribute::DomainDisplayName)
|
||||
.map(str::to_string)
|
||||
.ok_or(OperationError::InvalidEntryState)
|
||||
})
|
||||
|
@ -1530,7 +1530,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
/// activities (yet)
|
||||
pub fn set_domain_display_name(&mut self, new_domain_name: &str) -> Result<(), OperationError> {
|
||||
let modl = ModifyList::new_purge_and_set(
|
||||
"domain_display_name",
|
||||
Attribute::DomainDisplayName,
|
||||
Value::new_utf8(new_domain_name.to_string()),
|
||||
);
|
||||
let udi = PVUUID_DOMAIN_INFO.clone();
|
||||
|
@ -1551,7 +1551,8 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
/// approached, especially if we have a domain re-name replicated to us. It could
|
||||
/// be that we end up needing to have this as a cow cell or similar?
|
||||
pub fn danger_domain_rename(&mut self, new_domain_name: &str) -> Result<(), OperationError> {
|
||||
let modl = ModifyList::new_purge_and_set("domain_name", Value::new_iname(new_domain_name));
|
||||
let modl =
|
||||
ModifyList::new_purge_and_set(Attribute::DomainName, Value::new_iname(new_domain_name));
|
||||
let udi = PVUUID_DOMAIN_INFO.clone();
|
||||
let filt = filter_all!(f_eq(Attribute::Uuid, udi));
|
||||
self.internal_modify(&filt, &modl)
|
||||
|
@ -1908,7 +1909,7 @@ mod tests {
|
|||
let testobj1 = server_txn
|
||||
.internal_search_uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
|
||||
.expect("failed");
|
||||
assert!(testobj1.attribute_equality("class", &EntryClass::TestClass.into()));
|
||||
assert!(testobj1.attribute_equality(Attribute::Class, &EntryClass::TestClass.into()));
|
||||
|
||||
// Should still be good
|
||||
server_txn.commit().expect("should not fail");
|
||||
|
@ -1936,10 +1937,7 @@ mod tests {
|
|||
Attribute::Uuid,
|
||||
Value::Uuid(uuid!("cfcae205-31c3-484b-8ced-667d1709c5e3"))
|
||||
),
|
||||
(
|
||||
Attribute::AttributeName,
|
||||
Value::new_iutf8(Attribute::TestAttr.as_ref())
|
||||
),
|
||||
(Attribute::AttributeName, Attribute::TestAttr.to_value()),
|
||||
(Attribute::Description, Value::new_utf8s("Test Attribute")),
|
||||
(Attribute::MultiValue, Value::new_bool(false)),
|
||||
(Attribute::Unique, Value::new_bool(false)),
|
||||
|
@ -1994,10 +1992,7 @@ mod tests {
|
|||
let testobj1 = server_txn
|
||||
.internal_search_uuid(uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930"))
|
||||
.expect("failed");
|
||||
assert!(testobj1.attribute_equality(
|
||||
Attribute::TestAttr.as_ref(),
|
||||
&PartialValue::new_utf8s("test")
|
||||
));
|
||||
assert!(testobj1.attribute_equality(Attribute::TestAttr, &PartialValue::new_utf8s("test")));
|
||||
|
||||
server_txn.commit().expect("should not fail");
|
||||
// Commit.
|
||||
|
|
|
@ -194,11 +194,8 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
.iter()
|
||||
.chain(pre_candidates.iter().map(|e| e.as_ref()))
|
||||
.any(|e| {
|
||||
e.attribute_equality(Attribute::Class.as_ref(), &EntryClass::ClassType.into())
|
||||
|| e.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
&EntryClass::AttributeType.into(),
|
||||
)
|
||||
e.attribute_equality(Attribute::Class, &EntryClass::ClassType.into())
|
||||
|| e.attribute_equality(Attribute::Class, &EntryClass::AttributeType.into())
|
||||
});
|
||||
}
|
||||
if !self.changed_acp {
|
||||
|
@ -206,10 +203,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
.iter()
|
||||
.chain(pre_candidates.iter().map(|e| e.as_ref()))
|
||||
.any(|e| {
|
||||
e.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
&EntryClass::AccessControlProfile.into(),
|
||||
)
|
||||
e.attribute_equality(Attribute::Class, &EntryClass::AccessControlProfile.into())
|
||||
})
|
||||
}
|
||||
if !self.changed_oauth2 {
|
||||
|
@ -217,25 +211,20 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
.iter()
|
||||
.chain(pre_candidates.iter().map(|e| e.as_ref()))
|
||||
.any(|e| {
|
||||
e.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
&EntryClass::OAuth2ResourceServer.into(),
|
||||
)
|
||||
e.attribute_equality(Attribute::Class, &EntryClass::OAuth2ResourceServer.into())
|
||||
});
|
||||
}
|
||||
if !self.changed_domain {
|
||||
self.changed_domain = norm_cand
|
||||
.iter()
|
||||
.chain(pre_candidates.iter().map(|e| e.as_ref()))
|
||||
.any(|e| e.attribute_equality(Attribute::Uuid.as_ref(), &PVUUID_DOMAIN_INFO));
|
||||
.any(|e| e.attribute_equality(Attribute::Uuid, &PVUUID_DOMAIN_INFO));
|
||||
}
|
||||
if !self.changed_sync_agreement {
|
||||
self.changed_sync_agreement = norm_cand
|
||||
.iter()
|
||||
.chain(pre_candidates.iter().map(|e| e.as_ref()))
|
||||
.any(|e| {
|
||||
e.attribute_equality(Attribute::Class.as_ref(), &EntryClass::SyncAccount.into())
|
||||
});
|
||||
.any(|e| e.attribute_equality(Attribute::Class, &EntryClass::SyncAccount.into()));
|
||||
}
|
||||
|
||||
self.changed_uuid.extend(
|
||||
|
@ -364,11 +353,8 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
.iter()
|
||||
.chain(pre_candidates.iter().map(|e| e.as_ref()))
|
||||
.any(|e| {
|
||||
e.attribute_equality(Attribute::Class.as_ref(), &EntryClass::ClassType.into())
|
||||
|| e.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
&EntryClass::AttributeType.into(),
|
||||
)
|
||||
e.attribute_equality(Attribute::Class, &EntryClass::ClassType.into())
|
||||
|| e.attribute_equality(Attribute::Class, &EntryClass::AttributeType.into())
|
||||
});
|
||||
}
|
||||
if !self.changed_acp {
|
||||
|
@ -376,24 +362,18 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
.iter()
|
||||
.chain(pre_candidates.iter().map(|e| e.as_ref()))
|
||||
.any(|e| {
|
||||
e.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
&EntryClass::AccessControlProfile.into(),
|
||||
)
|
||||
e.attribute_equality(Attribute::Class, &EntryClass::AccessControlProfile.into())
|
||||
});
|
||||
}
|
||||
if !self.changed_oauth2 {
|
||||
self.changed_oauth2 = norm_cand.iter().any(|e| {
|
||||
e.attribute_equality(
|
||||
Attribute::Class.as_ref(),
|
||||
&EntryClass::OAuth2ResourceServer.into(),
|
||||
)
|
||||
e.attribute_equality(Attribute::Class, &EntryClass::OAuth2ResourceServer.into())
|
||||
});
|
||||
}
|
||||
if !self.changed_domain {
|
||||
self.changed_domain = norm_cand
|
||||
.iter()
|
||||
.any(|e| e.attribute_equality(Attribute::Uuid.into(), &PVUUID_DOMAIN_INFO));
|
||||
.any(|e| e.attribute_equality(Attribute::Uuid, &PVUUID_DOMAIN_INFO));
|
||||
}
|
||||
self.changed_uuid.extend(
|
||||
norm_cand
|
||||
|
@ -553,7 +533,6 @@ mod tests {
|
|||
assert!(server_txn.modify(&me_emp) == Err(OperationError::EmptyRequest));
|
||||
|
||||
// Mod changes no objects
|
||||
// TODO: @yaleman fix this because we don't have a way to do this anymore
|
||||
let me_nochg = ModifyEvent::new_impersonate_entry_ser(
|
||||
BUILTIN_ACCOUNT_IDM_ADMIN.clone(),
|
||||
filter!(f_eq(
|
||||
|
@ -652,8 +631,8 @@ mod tests {
|
|||
server_txn.internal_modify_uuid(
|
||||
t_uuid,
|
||||
&ModifyList::new_list(vec![
|
||||
m_assert(Attribute::Uuid.as_ref(), &PartialValue::Uuid(r_uuid)),
|
||||
m_pres(Attribute::Description.into(), &Value::Utf8("test".into()))
|
||||
m_assert(Attribute::Uuid, &PartialValue::Uuid(r_uuid)),
|
||||
m_pres(Attribute::Description, &Value::Utf8("test".into()))
|
||||
])
|
||||
),
|
||||
Err(OperationError::ModifyAssertionFailed)
|
||||
|
@ -664,8 +643,8 @@ mod tests {
|
|||
.internal_modify_uuid(
|
||||
t_uuid,
|
||||
&ModifyList::new_list(vec![
|
||||
m_assert("uuid", &PartialValue::Uuid(t_uuid)),
|
||||
m_pres(Attribute::Description.into(), &Value::Utf8("test".into()))
|
||||
m_assert(Attribute::Uuid, &PartialValue::Uuid(t_uuid)),
|
||||
m_pres(Attribute::Description, &Value::Utf8("test".into()))
|
||||
])
|
||||
)
|
||||
.is_ok());
|
||||
|
@ -728,7 +707,7 @@ mod tests {
|
|||
)),
|
||||
ModifyList::new_list(vec![
|
||||
Modify::Present(Attribute::Class.into(), EntryClass::SystemInfo.to_value()),
|
||||
// Modify::Present("domain".to_string(), Value::new_iutf8("domain.name")),
|
||||
// Modify::Present(Attribute::Domain.into(), Value::new_iutf8("domain.name")),
|
||||
Modify::Present(Attribute::Version.into(), Value::new_uint32(1)),
|
||||
]),
|
||||
);
|
||||
|
@ -795,7 +774,7 @@ mod tests {
|
|||
.expect("failed");
|
||||
// get the primary ava
|
||||
let cred_ref = test_ent
|
||||
.get_ava_single_credential("primary_credential")
|
||||
.get_ava_single_credential(Attribute::PrimaryCredential)
|
||||
.expect("Failed");
|
||||
// do a pw check.
|
||||
assert!(cred_ref.verify_password("test_password").unwrap());
|
||||
|
|
|
@ -32,7 +32,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
})?;
|
||||
let rc = self.internal_search(filter_all!(f_and!([
|
||||
f_eq(Attribute::Class, EntryClass::Recycled.into()),
|
||||
f_lt("last_modified_cid", PartialValue::new_cid(cid)),
|
||||
f_lt(Attribute::LastModifiedCid, PartialValue::new_cid(cid)),
|
||||
])))?;
|
||||
|
||||
if rc.is_empty() {
|
||||
|
@ -142,7 +142,7 @@ impl<'a> QueryServerWriteTransaction<'a> {
|
|||
// Get this entries uuid.
|
||||
let u: Uuid = e.get_uuid();
|
||||
|
||||
if let Some(riter) = e.get_ava_as_refuuid("directmemberof") {
|
||||
if let Some(riter) = e.get_ava_as_refuuid(Attribute::DirectMemberOf) {
|
||||
for g_uuid in riter {
|
||||
dm_mods
|
||||
.entry(g_uuid)
|
||||
|
@ -642,10 +642,7 @@ mod tests {
|
|||
.pop()
|
||||
.unwrap();
|
||||
|
||||
e.attribute_equality(
|
||||
Attribute::MemberOf.as_ref(),
|
||||
&PartialValue::new_refer_s(mo).unwrap(),
|
||||
)
|
||||
e.attribute_equality(Attribute::MemberOf, &PartialValue::new_refer_s(mo).unwrap())
|
||||
}
|
||||
|
||||
#[qs_test]
|
||||
|
|
|
@ -30,15 +30,14 @@ hyper-tls = { workspace = true }
|
|||
# used for webdriver testing
|
||||
fantoccini = { version = "0.19.3", optional = true }
|
||||
serde = { workspace = true }
|
||||
|
||||
|
||||
url = { workspace = true, features = ["serde"] }
|
||||
reqwest = { workspace = true, default-features = false, features = ["cookies"] }
|
||||
sketching = { workspace = true }
|
||||
testkit-macros = { workspace = true }
|
||||
tracing = { workspace = true, features = ["attributes"] }
|
||||
tokio = { workspace = true, features = ["net", "sync", "io-util", "macros"] }
|
||||
openssl.workspace = true
|
||||
openssl = { workspace = true }
|
||||
lazy_static = { workspace = true }
|
||||
|
||||
|
||||
[build-dependencies]
|
||||
|
|
|
@ -14,11 +14,14 @@ use std::net::TcpStream;
|
|||
use std::sync::atomic::{AtomicU16, Ordering};
|
||||
|
||||
use kanidm_client::{KanidmClient, KanidmClientBuilder};
|
||||
use kanidm_proto::constants::{ATTR_DESCRIPTION, ATTR_LDAP_SSH_PUBLICKEY, ATTR_MAIL, ATTR_NAME};
|
||||
|
||||
use kanidm_proto::v1::{Filter, Modify, ModifyList};
|
||||
use kanidmd_core::config::{Configuration, IntegrationTestConfig};
|
||||
use kanidmd_core::{create_server_core, CoreHandle};
|
||||
use kanidmd_lib::prelude::Attribute;
|
||||
use kanidmd_lib::prelude::{
|
||||
Attribute, BUILTIN_GROUP_IDM_ADMINS_V1, IDM_PEOPLE_ACCOUNT_PASSWORD_IMPORT_PRIV_V1,
|
||||
IDM_PEOPLE_EXTEND_PRIV_V1,
|
||||
};
|
||||
use tokio::task;
|
||||
|
||||
pub const ADMIN_TEST_USER: &str = "admin";
|
||||
|
@ -151,18 +154,18 @@ pub async fn add_all_attrs(
|
|||
.await
|
||||
.expect("Failed to extend user group");
|
||||
|
||||
for attr in [ATTR_LDAP_SSH_PUBLICKEY, ATTR_MAIL].iter() {
|
||||
for attr in [Attribute::SshPublicKey, Attribute::Mail].into_iter() {
|
||||
println!("Checking writable for {}", attr);
|
||||
#[allow(clippy::expect_used)]
|
||||
let res = is_attr_writable(rsclient, id, attr)
|
||||
.await
|
||||
.expect("Failed to get wriable status for attribute");
|
||||
.expect("Failed to get writable status for attribute");
|
||||
assert!(res);
|
||||
}
|
||||
|
||||
if let Some(legalname) = legalname {
|
||||
#[allow(clippy::expect_used)]
|
||||
let res = is_attr_writable(rsclient, legalname, "legalname")
|
||||
let res = is_attr_writable(rsclient, legalname, Attribute::LegalName)
|
||||
.await
|
||||
.expect("Failed to get writable status for legalname field");
|
||||
assert!(res);
|
||||
|
@ -185,22 +188,22 @@ pub async fn add_all_attrs(
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn is_attr_writable(rsclient: &KanidmClient, id: &str, attr: &str) -> Option<bool> {
|
||||
pub async fn is_attr_writable(rsclient: &KanidmClient, id: &str, attr: Attribute) -> Option<bool> {
|
||||
println!("writing to attribute: {}", attr);
|
||||
match attr {
|
||||
"radius_secret" => Some(
|
||||
Attribute::RadiusSecret => Some(
|
||||
rsclient
|
||||
.idm_account_radius_credential_regenerate(id)
|
||||
.await
|
||||
.is_ok(),
|
||||
),
|
||||
"primary_credential" => Some(
|
||||
Attribute::PrimaryCredential => Some(
|
||||
rsclient
|
||||
.idm_person_account_primary_credential_set_password(id, "dsadjasiodqwjk12asdl")
|
||||
.await
|
||||
.is_ok(),
|
||||
),
|
||||
kanidm_proto::constants::ATTR_LDAP_SSH_PUBLICKEY => Some(
|
||||
Attribute::SshPublicKey => Some(
|
||||
rsclient
|
||||
.idm_person_account_post_ssh_pubkey(
|
||||
id,
|
||||
|
@ -211,28 +214,36 @@ pub async fn is_attr_writable(rsclient: &KanidmClient, id: &str, attr: &str) ->
|
|||
.await
|
||||
.is_ok(),
|
||||
),
|
||||
"unix_password" => Some(
|
||||
Attribute::UnixPassword => Some(
|
||||
rsclient
|
||||
.idm_person_account_unix_cred_put(id, "dsadjasiodqwjk12asdl")
|
||||
.await
|
||||
.is_ok(),
|
||||
),
|
||||
"legalname" => Some(
|
||||
Attribute::LegalName => Some(
|
||||
rsclient
|
||||
.idm_person_account_set_attr(id, "legalname", &["test legal name"])
|
||||
.idm_person_account_set_attr(
|
||||
id,
|
||||
Attribute::LegalName.as_ref(),
|
||||
&["test legal name"],
|
||||
)
|
||||
.await
|
||||
.is_ok(),
|
||||
),
|
||||
"mail" => Some(
|
||||
Attribute::Mail => Some(
|
||||
rsclient
|
||||
.idm_person_account_set_attr(id, "mail", &[&format!("{}@example.com", id)])
|
||||
.idm_person_account_set_attr(
|
||||
id,
|
||||
Attribute::Mail.as_ref(),
|
||||
&[&format!("{}@example.com", id)],
|
||||
)
|
||||
.await
|
||||
.is_ok(),
|
||||
),
|
||||
entry => {
|
||||
let new_value = match entry {
|
||||
"acp_receiver_group" => "00000000-0000-0000-0000-000000000011".to_string(),
|
||||
"acp_targetscope" => "{\"and\": [{\"eq\": [\"class\",\"access_control_profile\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}".to_string(),
|
||||
Attribute::AcpReceiverGroup => "00000000-0000-0000-0000-000000000011".to_string(),
|
||||
Attribute::AcpTargetScope => "{\"and\": [{\"eq\": [\"class\",\"access_control_profile\"]}, {\"andnot\": {\"or\": [{\"eq\": [\"class\", \"tombstone\"]}, {\"eq\": [\"class\", \"recycled\"]}]}}]}".to_string(),
|
||||
_ => id.to_string(),
|
||||
};
|
||||
let m = ModifyList::new_list(vec![
|
||||
|
@ -249,7 +260,7 @@ pub async fn login_account(rsclient: &KanidmClient, id: &str) {
|
|||
#[allow(clippy::expect_used)]
|
||||
rsclient
|
||||
.idm_group_add_members(
|
||||
"idm_people_account_password_import_priv",
|
||||
IDM_PEOPLE_ACCOUNT_PASSWORD_IMPORT_PRIV_V1.name,
|
||||
&[ADMIN_TEST_USER],
|
||||
)
|
||||
.await
|
||||
|
@ -257,7 +268,7 @@ pub async fn login_account(rsclient: &KanidmClient, id: &str) {
|
|||
|
||||
#[allow(clippy::expect_used)]
|
||||
rsclient
|
||||
.idm_group_add_members("idm_people_extend_priv", &[ADMIN_TEST_USER])
|
||||
.idm_group_add_members(IDM_PEOPLE_EXTEND_PRIV_V1.name, &[ADMIN_TEST_USER])
|
||||
.await
|
||||
.expect("Failed to add user to idm_people_extend_priv");
|
||||
|
||||
|
@ -297,7 +308,12 @@ pub async fn login_account_via_admin(rsclient: &KanidmClient, id: &str) {
|
|||
login_account(rsclient, id).await
|
||||
}
|
||||
|
||||
pub async fn test_read_attrs(rsclient: &KanidmClient, id: &str, attrs: &[&str], is_readable: bool) {
|
||||
pub async fn test_read_attrs(
|
||||
rsclient: &KanidmClient,
|
||||
id: &str,
|
||||
attrs: &[Attribute],
|
||||
is_readable: bool,
|
||||
) {
|
||||
println!("Test read to {}, is readable: {}", id, is_readable);
|
||||
#[allow(clippy::expect_used)]
|
||||
let rset = rsclient
|
||||
|
@ -312,14 +328,14 @@ pub async fn test_read_attrs(rsclient: &KanidmClient, id: &str, attrs: &[&str],
|
|||
println!("Reading {}", attr);
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let is_ok = match *attr {
|
||||
"radius_secret" => rsclient
|
||||
Attribute::RadiusSecret => rsclient
|
||||
.idm_account_radius_credential_get(id)
|
||||
.await
|
||||
.unwrap()
|
||||
.is_some(),
|
||||
_ => e.attrs.get(*attr).is_some(),
|
||||
_ => e.attrs.get(attr.as_ref()).is_some(),
|
||||
};
|
||||
|
||||
dbg!(is_ok, is_readable);
|
||||
assert!(is_ok == is_readable)
|
||||
}
|
||||
}
|
||||
|
@ -327,14 +343,14 @@ pub async fn test_read_attrs(rsclient: &KanidmClient, id: &str, attrs: &[&str],
|
|||
pub async fn test_write_attrs(
|
||||
rsclient: &KanidmClient,
|
||||
id: &str,
|
||||
attrs: &[&str],
|
||||
attrs: &[Attribute],
|
||||
is_writeable: bool,
|
||||
) {
|
||||
println!("Test write to {}, is writeable: {}", id, is_writeable);
|
||||
for attr in attrs.iter() {
|
||||
println!("Writing to {} - ex {}", attr, is_writeable);
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let is_ok = is_attr_writable(rsclient, id, attr).await.unwrap();
|
||||
let is_ok = is_attr_writable(rsclient, id, *attr).await.unwrap();
|
||||
assert!(is_ok == is_writeable)
|
||||
}
|
||||
}
|
||||
|
@ -347,7 +363,7 @@ pub async fn test_modify_group(
|
|||
// need user test created to be added as test part
|
||||
for group in group_names.iter() {
|
||||
println!("Testing group: {}", group);
|
||||
for attr in [ATTR_DESCRIPTION, ATTR_NAME].iter() {
|
||||
for attr in [Attribute::Description, Attribute::Name].into_iter() {
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let is_writable = is_attr_writable(rsclient, group, attr).await.unwrap();
|
||||
dbg!(group, attr, is_writable, can_be_modified);
|
||||
|
@ -373,7 +389,7 @@ pub async fn login_put_admin_idm_admins(rsclient: &KanidmClient) {
|
|||
|
||||
#[allow(clippy::expect_used)]
|
||||
rsclient
|
||||
.idm_group_add_members("idm_admins", &[ADMIN_TEST_USER])
|
||||
.idm_group_add_members(BUILTIN_GROUP_IDM_ADMINS_V1.name, &[ADMIN_TEST_USER])
|
||||
.await
|
||||
.expect("Failed to add admin user to idm_admins")
|
||||
}
|
||||
|
|
|
@ -1,66 +1,67 @@
|
|||
#![deny(warnings)]
|
||||
use lazy_static::lazy_static;
|
||||
use std::collections::HashSet;
|
||||
|
||||
use kanidm_client::KanidmClient;
|
||||
use kanidm_proto::constants::{
|
||||
APPLICATION_JSON, ATTR_ACP_RECEIVER_GROUP, ATTR_ACP_TARGET_SCOPE, ATTR_DESCRIPTION,
|
||||
ATTR_LDAP_SSH_PUBLICKEY, ATTR_NAME,
|
||||
};
|
||||
use kanidmd_lib::prelude::{Attribute, EntryClass};
|
||||
use kanidm_proto::constants::APPLICATION_JSON;
|
||||
use kanidmd_lib::prelude::*;
|
||||
use kanidmd_testkit::*;
|
||||
use reqwest::header::CONTENT_TYPE;
|
||||
|
||||
// TODO: feed this off attrs
|
||||
static USER_READABLE_ATTRS: [&str; 9] = [
|
||||
"name",
|
||||
"spn",
|
||||
"displayname",
|
||||
"class",
|
||||
"memberof",
|
||||
"uuid",
|
||||
"gidnumber",
|
||||
"loginshell",
|
||||
ATTR_LDAP_SSH_PUBLICKEY,
|
||||
static USER_READABLE_ATTRS: [Attribute; 9] = [
|
||||
Attribute::Name,
|
||||
Attribute::Spn,
|
||||
Attribute::DisplayName,
|
||||
Attribute::Class,
|
||||
Attribute::MemberOf,
|
||||
Attribute::Uuid,
|
||||
Attribute::GidNumber,
|
||||
Attribute::LoginShell,
|
||||
Attribute::SshPublicKey,
|
||||
];
|
||||
// TODO: feed this off attrs
|
||||
static SELF_WRITEABLE_ATTRS: [&str; 7] = [
|
||||
"name",
|
||||
"displayname",
|
||||
"legalname",
|
||||
"radius_secret",
|
||||
ATTR_LDAP_SSH_PUBLICKEY,
|
||||
"unix_password",
|
||||
// Must be last - changing credential invalidates auth sessions!
|
||||
"primary_credential",
|
||||
static SELF_WRITEABLE_ATTRS: [Attribute; 7] = [
|
||||
Attribute::Name,
|
||||
Attribute::DisplayName,
|
||||
Attribute::LegalName,
|
||||
Attribute::RadiusSecret,
|
||||
Attribute::SshPublicKey,
|
||||
Attribute::UnixPassword,
|
||||
// needs to be last
|
||||
Attribute::PrimaryCredential,
|
||||
];
|
||||
static DEFAULT_HP_GROUP_NAMES: [&str; 24] = [
|
||||
"idm_admins",
|
||||
"system_admins",
|
||||
"idm_people_manage_priv",
|
||||
"idm_people_account_password_import_priv",
|
||||
"idm_people_extend_priv",
|
||||
"idm_people_write_priv",
|
||||
"idm_people_read_priv",
|
||||
"idm_group_manage_priv",
|
||||
"idm_group_write_priv",
|
||||
"idm_account_manage_priv",
|
||||
"idm_account_write_priv",
|
||||
"idm_account_read_priv",
|
||||
"idm_radius_servers",
|
||||
"idm_hp_account_manage_priv",
|
||||
"idm_hp_account_write_priv",
|
||||
"idm_hp_account_read_priv",
|
||||
"idm_hp_account_unix_extend_priv",
|
||||
"idm_schema_manage_priv",
|
||||
"idm_hp_group_manage_priv",
|
||||
"idm_hp_group_write_priv",
|
||||
"idm_hp_group_unix_extend_priv",
|
||||
"idm_acp_manage_priv",
|
||||
"domain_admins",
|
||||
"idm_high_privilege",
|
||||
];
|
||||
static DEFAULT_NOT_HP_GROUP_NAMES: [&str; 2] =
|
||||
["idm_account_unix_extend_priv", "idm_group_unix_extend_priv"];
|
||||
|
||||
lazy_static! {
|
||||
static ref DEFAULT_HP_GROUP_NAMES: [&'static str; 24] = [
|
||||
BUILTIN_GROUP_IDM_ADMINS_V1.name,
|
||||
BUILTIN_GROUP_SYSTEM_ADMINS_V1.name,
|
||||
IDM_PEOPLE_MANAGE_PRIV_V1.name,
|
||||
IDM_PEOPLE_ACCOUNT_PASSWORD_IMPORT_PRIV_V1.name,
|
||||
IDM_PEOPLE_EXTEND_PRIV_V1.name,
|
||||
IDM_PEOPLE_WRITE_PRIV_V1.name,
|
||||
IDM_PEOPLE_READ_PRIV_V1.name,
|
||||
IDM_GROUP_MANAGE_PRIV_V1.name,
|
||||
IDM_GROUP_WRITE_PRIV_V1.name,
|
||||
IDM_ACCOUNT_MANAGE_PRIV_V1.name,
|
||||
IDM_ACCOUNT_WRITE_PRIV_V1.name,
|
||||
IDM_ACCOUNT_READ_PRIV_V1.name,
|
||||
IDM_RADIUS_SERVERS_V1.name,
|
||||
IDM_HP_ACCOUNT_MANAGE_PRIV_V1.name,
|
||||
IDM_HP_ACCOUNT_WRITE_PRIV_V1.name,
|
||||
IDM_HP_ACCOUNT_READ_PRIV_V1.name,
|
||||
IDM_HP_ACCOUNT_UNIX_EXTEND_PRIV_V1.name,
|
||||
IDM_SCHEMA_MANAGE_PRIV_V1.name,
|
||||
IDM_HP_GROUP_MANAGE_PRIV_V1.name,
|
||||
IDM_HP_GROUP_WRITE_PRIV_V1.name,
|
||||
IDM_HP_GROUP_UNIX_EXTEND_PRIV_V1.name,
|
||||
IDM_ACP_MANAGE_PRIV_V1.name,
|
||||
DOMAIN_ADMINS.name,
|
||||
IDM_HIGH_PRIVILEGE_V1.name,
|
||||
];
|
||||
static ref DEFAULT_NOT_HP_GROUP_NAMES: [&'static str; 2] = [
|
||||
IDM_ACCOUNT_UNIX_EXTEND_PRIV_V1.name,
|
||||
IDM_GROUP_UNIX_EXTEND_PRIV_V1.name,
|
||||
];
|
||||
}
|
||||
|
||||
// Users
|
||||
// - Read to all self attributes (within security constraints).
|
||||
|
@ -78,21 +79,33 @@ async fn test_default_entries_rbac_users(rsclient: KanidmClient) {
|
|||
test_read_attrs(&rsclient, "self_account", &USER_READABLE_ATTRS, true).await;
|
||||
test_read_attrs(&rsclient, "other_account", &USER_READABLE_ATTRS, true).await;
|
||||
|
||||
static GROUP_READABLE_ATTRS: [&str; 5] = ["class", "name", "spn", "uuid", "member"];
|
||||
static GROUP_READABLE_ATTRS: [Attribute; 5] = [
|
||||
Attribute::Class,
|
||||
Attribute::Name,
|
||||
Attribute::Spn,
|
||||
Attribute::Uuid,
|
||||
Attribute::Member,
|
||||
];
|
||||
test_read_attrs(&rsclient, "self_group", &GROUP_READABLE_ATTRS, true).await;
|
||||
test_read_attrs(&rsclient, "other_group", &GROUP_READABLE_ATTRS, true).await;
|
||||
|
||||
static USER_SENSITIVE_ATTRS: [&str; 2] = ["legalname", "mail"];
|
||||
static USER_SENSITIVE_ATTRS: [Attribute; 2] = [Attribute::LegalName, Attribute::Mail];
|
||||
test_read_attrs(&rsclient, "other_account", &USER_SENSITIVE_ATTRS, false).await;
|
||||
|
||||
static SELF_READABLE_ATTRS: [&str; 1] = ["radius_secret"];
|
||||
static SELF_READABLE_ATTRS: [Attribute; 1] = [Attribute::RadiusSecret];
|
||||
test_read_attrs(&rsclient, "self_account", &SELF_READABLE_ATTRS, true).await;
|
||||
test_read_attrs(&rsclient, "other_account", &SELF_READABLE_ATTRS, false).await;
|
||||
|
||||
test_write_attrs(&rsclient, "self_account", &SELF_WRITEABLE_ATTRS, true).await;
|
||||
test_write_attrs(&rsclient, "other_account", &SELF_WRITEABLE_ATTRS, false).await;
|
||||
|
||||
static NON_SELF_WRITEABLE_ATTRS: [&str; 5] = ["spn", "class", "memberof", "gidnumber", "uuid"];
|
||||
static NON_SELF_WRITEABLE_ATTRS: [Attribute; 5] = [
|
||||
Attribute::Spn,
|
||||
Attribute::Class,
|
||||
Attribute::MemberOf,
|
||||
Attribute::GidNumber,
|
||||
Attribute::Uuid,
|
||||
];
|
||||
test_write_attrs(&rsclient, "self_account", &NON_SELF_WRITEABLE_ATTRS, false).await;
|
||||
}
|
||||
|
||||
|
@ -103,7 +116,12 @@ async fn test_default_entries_rbac_users(rsclient: KanidmClient) {
|
|||
async fn test_default_entries_rbac_account_managers(rsclient: KanidmClient) {
|
||||
login_put_admin_idm_admins(&rsclient).await;
|
||||
|
||||
create_user(&rsclient, "account_manager", "idm_account_manage_priv").await;
|
||||
create_user(
|
||||
&rsclient,
|
||||
"account_manager",
|
||||
IDM_ACCOUNT_MANAGE_PRIV_V1.name,
|
||||
)
|
||||
.await;
|
||||
create_user_with_all_attrs(&rsclient, NOT_ADMIN_TEST_USERNAME, Some("test_group")).await;
|
||||
|
||||
login_account(&rsclient, "account_manager").await;
|
||||
|
@ -115,13 +133,12 @@ async fn test_default_entries_rbac_account_managers(rsclient: KanidmClient) {
|
|||
true,
|
||||
)
|
||||
.await;
|
||||
static ACCOUNT_MANAGER_ATTRS: [&str; 5] = [
|
||||
"name",
|
||||
"displayname",
|
||||
ATTR_LDAP_SSH_PUBLICKEY,
|
||||
"mail",
|
||||
// Must be last, writing to this invalidates sessions.
|
||||
"primary_credential",
|
||||
static ACCOUNT_MANAGER_ATTRS: [Attribute; 5] = [
|
||||
Attribute::Name,
|
||||
Attribute::DisplayName,
|
||||
Attribute::PrimaryCredential,
|
||||
Attribute::SshPublicKey,
|
||||
Attribute::Mail,
|
||||
];
|
||||
test_write_attrs(
|
||||
&rsclient,
|
||||
|
@ -131,7 +148,7 @@ async fn test_default_entries_rbac_account_managers(rsclient: KanidmClient) {
|
|||
)
|
||||
.await;
|
||||
|
||||
static PRIVATE_DATA_ATTRS: [&str; 1] = ["legalname"];
|
||||
static PRIVATE_DATA_ATTRS: [Attribute; 1] = [Attribute::LegalName];
|
||||
test_read_attrs(
|
||||
&rsclient,
|
||||
NOT_ADMIN_TEST_USERNAME,
|
||||
|
@ -156,9 +173,14 @@ async fn test_default_entries_rbac_account_managers(rsclient: KanidmClient) {
|
|||
async fn test_default_entries_rbac_group_managers(rsclient: KanidmClient) {
|
||||
login_put_admin_idm_admins(&rsclient).await;
|
||||
|
||||
create_user(&rsclient, "group_manager", "idm_group_manage_priv").await;
|
||||
create_user(&rsclient, "group_manager", IDM_GROUP_MANAGE_PRIV_V1.name).await;
|
||||
// create test user without creating new groups
|
||||
create_user(&rsclient, NOT_ADMIN_TEST_USERNAME, "idm_admins").await;
|
||||
create_user(
|
||||
&rsclient,
|
||||
NOT_ADMIN_TEST_USERNAME,
|
||||
BUILTIN_GROUP_IDM_ADMINS_V1.name,
|
||||
)
|
||||
.await;
|
||||
|
||||
login_account(&rsclient, "group_manager").await;
|
||||
|
||||
|
@ -172,22 +194,31 @@ async fn test_default_entries_rbac_group_managers(rsclient: KanidmClient) {
|
|||
let groups = rsclient.idm_group_list().await.unwrap();
|
||||
let group_names: HashSet<String> = groups
|
||||
.iter()
|
||||
.map(|entry| entry.attrs.get("name").unwrap().first().unwrap())
|
||||
.map(|entry| {
|
||||
entry
|
||||
.attrs
|
||||
.get(Attribute::Name.as_ref())
|
||||
.unwrap()
|
||||
.first()
|
||||
.unwrap()
|
||||
})
|
||||
.cloned()
|
||||
.collect();
|
||||
assert!(default_group_names.is_subset(&group_names));
|
||||
|
||||
test_modify_group(&rsclient, &DEFAULT_HP_GROUP_NAMES, false).await;
|
||||
test_modify_group(&rsclient, &DEFAULT_NOT_HP_GROUP_NAMES, true).await;
|
||||
test_modify_group(&rsclient, &(*DEFAULT_HP_GROUP_NAMES), false).await;
|
||||
test_modify_group(&rsclient, &(*DEFAULT_NOT_HP_GROUP_NAMES), true).await;
|
||||
|
||||
rsclient.idm_group_create("test_group").await.unwrap();
|
||||
rsclient
|
||||
.idm_group_add_members("test_group", &[NOT_ADMIN_TEST_USERNAME])
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(is_attr_writable(&rsclient, "test_group", ATTR_DESCRIPTION)
|
||||
.await
|
||||
.unwrap());
|
||||
assert!(
|
||||
is_attr_writable(&rsclient, "test_group", Attribute::Description)
|
||||
.await
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
// Admins
|
||||
|
@ -196,46 +227,46 @@ async fn test_default_entries_rbac_group_managers(rsclient: KanidmClient) {
|
|||
async fn test_default_entries_rbac_admins_access_control_entries(rsclient: KanidmClient) {
|
||||
login_put_admin_idm_admins(&rsclient).await;
|
||||
|
||||
static ACP_COMMON_ATTRS: [&str; 4] = [
|
||||
ATTR_NAME,
|
||||
ATTR_DESCRIPTION,
|
||||
ATTR_ACP_RECEIVER_GROUP,
|
||||
ATTR_ACP_TARGET_SCOPE,
|
||||
static ACP_COMMON_ATTRS: [Attribute; 4] = [
|
||||
Attribute::Name,
|
||||
Attribute::Description,
|
||||
Attribute::AcpReceiverGroup,
|
||||
Attribute::AcpTargetScope,
|
||||
];
|
||||
static ACP_ENTRIES: [&str; 28] = [
|
||||
"idm_admins_acp_recycle_search",
|
||||
"idm_admins_acp_revive",
|
||||
"idm_self_acp_read",
|
||||
"idm_self_acp_write",
|
||||
"idm_all_acp_read",
|
||||
"idm_acp_people_read_priv",
|
||||
"idm_acp_people_write_priv",
|
||||
"idm_acp_people_manage",
|
||||
"idm_acp_people_account_password_import_priv",
|
||||
"idm_acp_people_extend_priv",
|
||||
"idm_acp_group_write_priv",
|
||||
"idm_acp_account_read_priv",
|
||||
"idm_acp_account_write_priv",
|
||||
"idm_acp_account_manage",
|
||||
"idm_acp_radius_servers",
|
||||
"idm_acp_hp_account_read_priv",
|
||||
"idm_acp_hp_account_write_priv",
|
||||
"idm_acp_hp_group_write_priv",
|
||||
"idm_acp_schema_write_attrs_priv",
|
||||
"idm_acp_acp_manage_priv",
|
||||
"idm_acp_schema_write_classes_priv",
|
||||
"idm_acp_group_manage",
|
||||
"idm_acp_hp_account_manage",
|
||||
"idm_acp_hp_group_manage",
|
||||
"idm_acp_domain_admin_priv",
|
||||
"idm_acp_system_config_priv",
|
||||
"idm_acp_account_unix_extend_priv",
|
||||
"idm_acp_group_unix_extend_priv",
|
||||
let acp_entries = vec![
|
||||
IDM_ADMINS_ACP_RECYCLE_SEARCH_V1.clone(),
|
||||
IDM_ADMINS_ACP_REVIVE_V1.clone(),
|
||||
IDM_SELF_ACP_READ_V1.clone(),
|
||||
IDM_SELF_ACP_WRITE_V1.clone(),
|
||||
IDM_ALL_ACP_READ_V1.clone(),
|
||||
IDM_ACP_PEOPLE_READ_PRIV_V1.clone(),
|
||||
IDM_ACP_PEOPLE_WRITE_PRIV_V1.clone(),
|
||||
IDM_ACP_PEOPLE_MANAGE_PRIV_V1.clone(),
|
||||
IDM_ACP_PEOPLE_ACCOUNT_PASSWORD_IMPORT_PRIV_V1.clone(),
|
||||
IDM_ACP_PEOPLE_EXTEND_PRIV_V1.clone(),
|
||||
IDM_ACP_GROUP_WRITE_PRIV_V1.clone(),
|
||||
IDM_ACP_ACCOUNT_READ_PRIV_V1.clone(),
|
||||
IDM_ACP_ACCOUNT_WRITE_PRIV_V1.clone(),
|
||||
IDM_ACP_ACCOUNT_MANAGE_PRIV_V1.clone(),
|
||||
IDM_ACP_RADIUS_SERVERS_V1.clone(),
|
||||
IDM_ACP_HP_ACCOUNT_READ_PRIV_V1.clone(),
|
||||
IDM_ACP_HP_ACCOUNT_WRITE_PRIV_V1.clone(),
|
||||
IDM_ACP_HP_GROUP_WRITE_PRIV_V1.clone(),
|
||||
IDM_ACP_SCHEMA_WRITE_ATTRS_PRIV_V1.clone(),
|
||||
IDM_ACP_ACP_MANAGE_PRIV_V1.clone(),
|
||||
IDM_ACP_SCHEMA_WRITE_CLASSES_PRIV_V1.clone(),
|
||||
IDM_ACP_GROUP_MANAGE_PRIV_V1.clone(),
|
||||
IDM_ACP_HP_ACCOUNT_MANAGE_PRIV_V1.clone(),
|
||||
IDM_ACP_HP_GROUP_MANAGE_PRIV_V1.clone(),
|
||||
IDM_ACP_DOMAIN_ADMIN_PRIV_V1.clone(),
|
||||
IDM_ACP_SYSTEM_CONFIG_PRIV_V1.clone(),
|
||||
IDM_ACP_ACCOUNT_UNIX_EXTEND_PRIV_V1.clone(),
|
||||
IDM_ACP_GROUP_UNIX_EXTEND_PRIV_V1.clone(),
|
||||
];
|
||||
|
||||
for entry in ACP_ENTRIES.iter() {
|
||||
test_read_attrs(&rsclient, entry, &ACP_COMMON_ATTRS, true).await;
|
||||
test_write_attrs(&rsclient, entry, &ACP_COMMON_ATTRS, true).await;
|
||||
for entry in acp_entries.iter() {
|
||||
test_read_attrs(&rsclient, entry.name, &ACP_COMMON_ATTRS, true).await;
|
||||
test_write_attrs(&rsclient, entry.name, &ACP_COMMON_ATTRS, true).await;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -327,7 +358,7 @@ async fn test_default_entries_rbac_admins_schema_entries(rsclient: KanidmClient)
|
|||
Attribute::DisplayName,
|
||||
Attribute::LegalName,
|
||||
Attribute::Mail,
|
||||
Attribute::LdapSshPublicKey,
|
||||
Attribute::SshPublicKey,
|
||||
Attribute::PrimaryCredential,
|
||||
Attribute::RadiusSecret,
|
||||
Attribute::DomainName,
|
||||
|
@ -383,8 +414,12 @@ async fn test_default_entries_rbac_admins_group_entries(rsclient: KanidmClient)
|
|||
async fn test_default_entries_rbac_admins_ha_accounts(rsclient: KanidmClient) {
|
||||
login_put_admin_idm_admins(&rsclient).await;
|
||||
|
||||
static MAIN_ATTRS: [&str; 3] = ["name", "displayname", "primary_credential"];
|
||||
test_write_attrs(&rsclient, "idm_admin", &MAIN_ATTRS, true).await;
|
||||
static MAIN_ATTRS: [Attribute; 3] = [
|
||||
Attribute::Name,
|
||||
Attribute::DisplayName,
|
||||
Attribute::PrimaryCredential,
|
||||
];
|
||||
test_write_attrs(&rsclient, BUILTIN_ACCOUNT_IDM_ADMIN.name, &MAIN_ATTRS, true).await;
|
||||
}
|
||||
|
||||
// recover from the recycle bin
|
||||
|
@ -417,12 +452,21 @@ async fn test_default_entries_rbac_admins_recycle_accounts(rsclient: KanidmClien
|
|||
async fn test_default_entries_rbac_people_managers(rsclient: KanidmClient) {
|
||||
login_put_admin_idm_admins(&rsclient).await;
|
||||
|
||||
create_user(&rsclient, "read_people_manager", "idm_people_read_priv").await;
|
||||
create_user(
|
||||
&rsclient,
|
||||
"read_people_manager",
|
||||
IDM_PEOPLE_READ_PRIV_V1.name,
|
||||
)
|
||||
.await;
|
||||
create_user_with_all_attrs(&rsclient, NOT_ADMIN_TEST_USERNAME, Some("test_group")).await;
|
||||
|
||||
static PEOPLE_MANAGER_ATTRS: [&str; 2] = ["legalname", "mail"];
|
||||
static PEOPLE_MANAGER_ATTRS: [Attribute; 2] = [Attribute::LegalName, Attribute::Mail];
|
||||
|
||||
static TECHNICAL_ATTRS: [&str; 3] = ["radius_secret", "unix_password", "primary_credential"];
|
||||
static TECHNICAL_ATTRS: [Attribute; 3] = [
|
||||
Attribute::PrimaryCredential,
|
||||
Attribute::RadiusSecret,
|
||||
Attribute::UnixPassword,
|
||||
];
|
||||
test_read_attrs(
|
||||
&rsclient,
|
||||
NOT_ADMIN_TEST_USERNAME,
|
||||
|
@ -455,7 +499,12 @@ async fn test_default_entries_rbac_people_managers(rsclient: KanidmClient) {
|
|||
.auth_simple_password(ADMIN_TEST_USER, ADMIN_TEST_PASSWORD)
|
||||
.await
|
||||
.unwrap();
|
||||
create_user(&rsclient, "write_people_manager", "idm_people_write_priv").await;
|
||||
create_user(
|
||||
&rsclient,
|
||||
"write_people_manager",
|
||||
IDM_PEOPLE_WRITE_PRIV_V1.name,
|
||||
)
|
||||
.await;
|
||||
login_account(&rsclient, "write_people_manager").await;
|
||||
|
||||
test_read_attrs(
|
||||
|
@ -517,11 +566,16 @@ async fn test_default_entries_rbac_anonymous_entry(rsclient: KanidmClient) {
|
|||
async fn test_default_entries_rbac_radius_servers(rsclient: KanidmClient) {
|
||||
login_put_admin_idm_admins(&rsclient).await;
|
||||
|
||||
create_user(&rsclient, "radius_server", "idm_radius_servers").await;
|
||||
create_user(&rsclient, "radius_server", IDM_RADIUS_SERVERS_V1.name).await;
|
||||
create_user_with_all_attrs(&rsclient, NOT_ADMIN_TEST_USERNAME, Some("test_group")).await;
|
||||
|
||||
login_account(&rsclient, "radius_server").await;
|
||||
static RADIUS_NECESSARY_ATTRS: [&str; 4] = ["name", "spn", "uuid", "radius_secret"];
|
||||
static RADIUS_NECESSARY_ATTRS: [Attribute; 4] = [
|
||||
Attribute::Name,
|
||||
Attribute::Spn,
|
||||
Attribute::Uuid,
|
||||
Attribute::RadiusSecret,
|
||||
];
|
||||
|
||||
test_read_attrs(
|
||||
&rsclient,
|
||||
|
@ -555,7 +609,7 @@ async fn test_self_write_mail_priv_people(rsclient: KanidmClient) {
|
|||
create_user_with_all_attrs(&rsclient, "other", None).await;
|
||||
rsclient
|
||||
.idm_group_add_members(
|
||||
"idm_people_self_write_mail_priv",
|
||||
IDM_PEOPLE_SELF_WRITE_MAIL_PRIV_V1.name,
|
||||
&["other", NOT_ADMIN_TEST_USERNAME],
|
||||
)
|
||||
.await
|
||||
|
@ -565,15 +619,15 @@ async fn test_self_write_mail_priv_people(rsclient: KanidmClient) {
|
|||
|
||||
login_account(&rsclient, NOT_ADMIN_TEST_USERNAME).await;
|
||||
// can write to own mail
|
||||
test_write_attrs(&rsclient, NOT_ADMIN_TEST_USERNAME, &["mail"], true).await;
|
||||
test_write_attrs(&rsclient, NOT_ADMIN_TEST_USERNAME, &[Attribute::Mail], true).await;
|
||||
// not someone elses
|
||||
test_write_attrs(&rsclient, "other", &["mail"], false).await;
|
||||
test_write_attrs(&rsclient, "other", &[Attribute::Mail], false).await;
|
||||
|
||||
// but they can write to theirs
|
||||
login_account_via_admin(&rsclient, "other").await;
|
||||
test_write_attrs(&rsclient, "other", &["mail"], true).await;
|
||||
test_write_attrs(&rsclient, "other", &[Attribute::Mail], true).await;
|
||||
login_account_via_admin(&rsclient, "nonperson").await;
|
||||
test_write_attrs(&rsclient, "nonperson", &["mail"], false).await;
|
||||
test_write_attrs(&rsclient, "nonperson", &[Attribute::Mail], false).await;
|
||||
}
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
|
|
|
@ -5,7 +5,10 @@ use kanidm_proto::{
|
|||
v1::Entry,
|
||||
};
|
||||
|
||||
use kanidmd_lib::prelude::Attribute;
|
||||
use kanidmd_lib::prelude::{
|
||||
Attribute, BUILTIN_GROUP_IDM_ADMINS_V1, IDM_PEOPLE_ACCOUNT_PASSWORD_IMPORT_PRIV_V1,
|
||||
IDM_PEOPLE_MANAGE_PRIV_V1,
|
||||
};
|
||||
use kanidmd_testkit::ADMIN_TEST_PASSWORD;
|
||||
use reqwest::StatusCode;
|
||||
|
||||
|
@ -273,17 +276,17 @@ async fn setup_server(rsclient: &KanidmClient) {
|
|||
// To enable the admin to actually make some of these changes, we have
|
||||
// to make them a people admin. NOT recommended in production!
|
||||
rsclient
|
||||
.idm_group_add_members("idm_people_account_password_import_priv", &["admin"])
|
||||
.idm_group_add_members(IDM_PEOPLE_ACCOUNT_PASSWORD_IMPORT_PRIV_V1.name, &["admin"])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
rsclient
|
||||
.idm_group_add_members("idm_people_manage_priv", &["admin"])
|
||||
.idm_group_add_members(IDM_PEOPLE_MANAGE_PRIV_V1.name, &["admin"])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
rsclient
|
||||
.idm_group_add_members("idm_admins", &["admin"])
|
||||
.idm_group_add_members(BUILTIN_GROUP_IDM_ADMINS_V1.name, &["admin"])
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ use kanidm_proto::oauth2::{
|
|||
AccessTokenIntrospectRequest, AccessTokenIntrospectResponse, AccessTokenRequest,
|
||||
AccessTokenResponse, AuthorisationResponse, GrantTypeReq, OidcDiscoveryResponse,
|
||||
};
|
||||
use kanidmd_lib::prelude::{Attribute, BUILTIN_GROUP_IDM_ADMINS_V1, IDM_ALL_ACCOUNTS};
|
||||
use oauth2_ext::PkceCodeChallenge;
|
||||
use reqwest::header::{HeaderValue, CONTENT_TYPE};
|
||||
use reqwest::StatusCode;
|
||||
|
@ -65,7 +66,7 @@ async fn test_oauth2_openid_basic_flow(rsclient: KanidmClient) {
|
|||
|
||||
// Extend the admin account with extended details for openid claims.
|
||||
rsclient
|
||||
.idm_group_add_members("idm_admins", &["admin"])
|
||||
.idm_group_add_members(BUILTIN_GROUP_IDM_ADMINS_V1.name, &["admin"])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -75,7 +76,11 @@ async fn test_oauth2_openid_basic_flow(rsclient: KanidmClient) {
|
|||
.expect("Failed to create account details");
|
||||
|
||||
rsclient
|
||||
.idm_person_account_set_attr("oauth_test", "mail", &["oauth_test@localhost"])
|
||||
.idm_person_account_set_attr(
|
||||
"oauth_test",
|
||||
Attribute::Mail.as_ref(),
|
||||
&["oauth_test@localhost"],
|
||||
)
|
||||
.await
|
||||
.expect("Failed to create account mail");
|
||||
|
||||
|
@ -92,14 +97,18 @@ async fn test_oauth2_openid_basic_flow(rsclient: KanidmClient) {
|
|||
rsclient
|
||||
.idm_oauth2_rs_update_scope_map(
|
||||
"test_integration",
|
||||
"idm_all_accounts",
|
||||
IDM_ALL_ACCOUNTS.name,
|
||||
vec![OAUTH2_SCOPE_READ, OAUTH2_SCOPE_EMAIL, OAUTH2_SCOPE_OPENID],
|
||||
)
|
||||
.await
|
||||
.expect("Failed to update oauth2 scopes");
|
||||
|
||||
rsclient
|
||||
.idm_oauth2_rs_update_sup_scope_map("test_integration", "idm_all_accounts", vec!["admin"])
|
||||
.idm_oauth2_rs_update_sup_scope_map(
|
||||
"test_integration",
|
||||
IDM_ALL_ACCOUNTS.name,
|
||||
vec!["admin"],
|
||||
)
|
||||
.await
|
||||
.expect("Failed to update oauth2 scopes");
|
||||
|
||||
|
@ -417,7 +426,7 @@ async fn test_oauth2_openid_public_flow(rsclient: KanidmClient) {
|
|||
|
||||
// Extend the admin account with extended details for openid claims.
|
||||
rsclient
|
||||
.idm_group_add_members("idm_admins", &["admin"])
|
||||
.idm_group_add_members(BUILTIN_GROUP_IDM_ADMINS_V1.name, &["admin"])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -427,7 +436,11 @@ async fn test_oauth2_openid_public_flow(rsclient: KanidmClient) {
|
|||
.expect("Failed to create account details");
|
||||
|
||||
rsclient
|
||||
.idm_person_account_set_attr("oauth_test", "mail", &["oauth_test@localhost"])
|
||||
.idm_person_account_set_attr(
|
||||
"oauth_test",
|
||||
Attribute::Mail.as_ref(),
|
||||
&["oauth_test@localhost"],
|
||||
)
|
||||
.await
|
||||
.expect("Failed to create account mail");
|
||||
|
||||
|
@ -444,14 +457,18 @@ async fn test_oauth2_openid_public_flow(rsclient: KanidmClient) {
|
|||
rsclient
|
||||
.idm_oauth2_rs_update_scope_map(
|
||||
"test_integration",
|
||||
"idm_all_accounts",
|
||||
IDM_ALL_ACCOUNTS.name,
|
||||
vec![OAUTH2_SCOPE_READ, OAUTH2_SCOPE_EMAIL, OAUTH2_SCOPE_OPENID],
|
||||
)
|
||||
.await
|
||||
.expect("Failed to update oauth2 scopes");
|
||||
|
||||
rsclient
|
||||
.idm_oauth2_rs_update_sup_scope_map("test_integration", "idm_all_accounts", vec!["admin"])
|
||||
.idm_oauth2_rs_update_sup_scope_map(
|
||||
"test_integration",
|
||||
IDM_ALL_ACCOUNTS.name,
|
||||
vec!["admin"],
|
||||
)
|
||||
.await
|
||||
.expect("Failed to update oauth2 scopes");
|
||||
|
||||
|
|
|
@ -7,7 +7,10 @@ use kanidm_proto::v1::{
|
|||
UserAuthToken,
|
||||
};
|
||||
use kanidmd_lib::credential::totp::Totp;
|
||||
use kanidmd_lib::prelude::Attribute;
|
||||
use kanidmd_lib::prelude::{
|
||||
Attribute, BUILTIN_GROUP_IDM_ADMINS_V1, BUILTIN_GROUP_SYSTEM_ADMINS_V1,
|
||||
IDM_PEOPLE_ACCOUNT_PASSWORD_IMPORT_PRIV_V1,
|
||||
};
|
||||
use tracing::debug;
|
||||
|
||||
use std::str::FromStr;
|
||||
|
@ -52,8 +55,8 @@ async fn test_server_modify(rsclient: KanidmClient) {
|
|||
// Build a self mod.
|
||||
let f = Filter::SelfUuid;
|
||||
let m = ModifyList::new_list(vec![
|
||||
Modify::Purged("displayname".to_string()),
|
||||
Modify::Present("displayname".to_string(), "test".to_string()),
|
||||
Modify::Purged(Attribute::DisplayName.to_string()),
|
||||
Modify::Present(Attribute::DisplayName.to_string(), "test".to_string()),
|
||||
]);
|
||||
|
||||
// Not logged in - should fail!
|
||||
|
@ -155,7 +158,10 @@ async fn test_server_rest_group_read(rsclient: KanidmClient) {
|
|||
let g_list = rsclient.idm_group_list().await.unwrap();
|
||||
assert!(!g_list.is_empty());
|
||||
|
||||
let g = rsclient.idm_group_get("idm_admins").await.unwrap();
|
||||
let g = rsclient
|
||||
.idm_group_get(BUILTIN_GROUP_IDM_ADMINS_V1.name)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(g.is_some());
|
||||
println!("{:?}", g);
|
||||
}
|
||||
|
@ -224,12 +230,18 @@ async fn test_server_rest_group_lifecycle(rsclient: KanidmClient) {
|
|||
assert!(g_list_3.len() == g_list.len());
|
||||
|
||||
// Check we can get an exact group
|
||||
let g = rsclient.idm_group_get("idm_admins").await.unwrap();
|
||||
let g = rsclient
|
||||
.idm_group_get(BUILTIN_GROUP_IDM_ADMINS_V1.name)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(g.is_some());
|
||||
println!("{:?}", g);
|
||||
|
||||
// They should have members
|
||||
let members = rsclient.idm_group_get_members("idm_admins").await.unwrap();
|
||||
let members = rsclient
|
||||
.idm_group_get_members(BUILTIN_GROUP_IDM_ADMINS_V1.name)
|
||||
.await
|
||||
.unwrap();
|
||||
println!("{:?}", members);
|
||||
assert!(members == Some(vec!["idm_admin@localhost".to_string()]));
|
||||
}
|
||||
|
@ -268,11 +280,17 @@ async fn test_server_rest_schema_read(rsclient: KanidmClient) {
|
|||
assert!(!c_list.is_empty());
|
||||
|
||||
// Get an attr/class
|
||||
let a = rsclient.idm_schema_attributetype_get("name").await.unwrap();
|
||||
let a = rsclient
|
||||
.idm_schema_attributetype_get(Attribute::Name.as_ref())
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(a.is_some());
|
||||
println!("{:?}", a);
|
||||
|
||||
let c = rsclient.idm_schema_classtype_get("account").await.unwrap();
|
||||
let c = rsclient
|
||||
.idm_schema_classtype_get(Attribute::Account.as_ref())
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(c.is_some());
|
||||
println!("{:?}", c);
|
||||
}
|
||||
|
@ -287,7 +305,7 @@ async fn test_server_radius_credential_lifecycle(rsclient: KanidmClient) {
|
|||
|
||||
// All admin to create persons.
|
||||
rsclient
|
||||
.idm_group_add_members("idm_admins", &["admin"])
|
||||
.idm_group_add_members(BUILTIN_GROUP_IDM_ADMINS_V1.name, &["admin"])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -358,7 +376,7 @@ async fn test_server_rest_person_account_lifecycle(rsclient: KanidmClient) {
|
|||
// To enable the admin to actually make some of these changes, we have
|
||||
// to make them a people admin. NOT recommended in production!
|
||||
rsclient
|
||||
.idm_group_add_members("idm_admins", &["admin"])
|
||||
.idm_group_add_members(BUILTIN_GROUP_IDM_ADMINS_V1.name, &["admin"])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -382,12 +400,16 @@ async fn test_server_rest_person_account_lifecycle(rsclient: KanidmClient) {
|
|||
|
||||
// Test adding some mail addrs
|
||||
rsclient
|
||||
.idm_person_account_add_attr("demo_account", "mail", &["demo@idm.example.com"])
|
||||
.idm_person_account_add_attr(
|
||||
"demo_account",
|
||||
Attribute::Mail.as_ref(),
|
||||
&["demo@idm.example.com"],
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let r = rsclient
|
||||
.idm_person_account_get_attr("demo_account", "mail")
|
||||
.idm_person_account_get_attr("demo_account", Attribute::Mail.as_ref())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -494,7 +516,7 @@ async fn test_server_rest_domain_lifecycle(rsclient: KanidmClient) {
|
|||
assert!(
|
||||
dlocal
|
||||
.attrs
|
||||
.get("domain_display_name")
|
||||
.get(Attribute::DomainDisplayName.as_ref())
|
||||
.and_then(|v| v.get(0))
|
||||
== Some(&"Super Cool Crabz".to_string())
|
||||
);
|
||||
|
@ -508,7 +530,7 @@ async fn test_server_rest_posix_lifecycle(rsclient: KanidmClient) {
|
|||
assert!(res.is_ok());
|
||||
// Not recommended in production!
|
||||
rsclient
|
||||
.idm_group_add_members("idm_admins", &["admin"])
|
||||
.idm_group_add_members(BUILTIN_GROUP_IDM_ADMINS_V1.name, &["admin"])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -617,7 +639,7 @@ async fn test_server_rest_posix_auth_lifecycle(rsclient: KanidmClient) {
|
|||
|
||||
// Not recommended in production!
|
||||
rsclient
|
||||
.idm_group_add_members("idm_admins", &["admin"])
|
||||
.idm_group_add_members(BUILTIN_GROUP_IDM_ADMINS_V1.name, &["admin"])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -714,7 +736,7 @@ async fn test_server_rest_recycle_lifecycle(rsclient: KanidmClient) {
|
|||
|
||||
// Not recommended in production!
|
||||
rsclient
|
||||
.idm_group_add_members("idm_admins", &["admin"])
|
||||
.idm_group_add_members(BUILTIN_GROUP_IDM_ADMINS_V1.name, &["admin"])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -768,11 +790,11 @@ async fn test_server_rest_account_import_password(rsclient: KanidmClient) {
|
|||
// To enable the admin to actually make some of these changes, we have
|
||||
// to make them a password import admin. NOT recommended in production!
|
||||
rsclient
|
||||
.idm_group_add_members("idm_people_account_password_import_priv", &["admin"])
|
||||
.idm_group_add_members(IDM_PEOPLE_ACCOUNT_PASSWORD_IMPORT_PRIV_V1.name, &["admin"])
|
||||
.await
|
||||
.unwrap();
|
||||
rsclient
|
||||
.idm_group_add_members("idm_admins", &["admin"])
|
||||
.idm_group_add_members(BUILTIN_GROUP_IDM_ADMINS_V1.name, &["admin"])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -865,9 +887,13 @@ async fn test_server_rest_oauth2_basic_lifecycle(rsclient: KanidmClient) {
|
|||
eprintln!("{:?}", oauth2_config);
|
||||
|
||||
// What can we see?
|
||||
assert!(oauth2_config.attrs.contains_key("oauth2_rs_basic_secret"));
|
||||
assert!(oauth2_config
|
||||
.attrs
|
||||
.contains_key(Attribute::OAuth2RsBasicSecret.as_ref()));
|
||||
// This is present, but redacted.
|
||||
assert!(oauth2_config.attrs.contains_key("oauth2_rs_token_key"));
|
||||
assert!(oauth2_config
|
||||
.attrs
|
||||
.contains_key(Attribute::OAuth2RsTokenKey.as_ref()));
|
||||
|
||||
// Mod delete the secret/key and check them again.
|
||||
// Check we can patch the oauth2_rs_name / oauth2_rs_origin
|
||||
|
@ -896,7 +922,11 @@ async fn test_server_rest_oauth2_basic_lifecycle(rsclient: KanidmClient) {
|
|||
|
||||
// Check that we can add scope maps and delete them.
|
||||
rsclient
|
||||
.idm_oauth2_rs_update_scope_map("test_integration", "system_admins", vec!["a", "b"])
|
||||
.idm_oauth2_rs_update_scope_map(
|
||||
"test_integration",
|
||||
BUILTIN_GROUP_SYSTEM_ADMINS_V1.name,
|
||||
vec!["a", "b"],
|
||||
)
|
||||
.await
|
||||
.expect("Failed to create scope map");
|
||||
|
||||
|
@ -911,7 +941,11 @@ async fn test_server_rest_oauth2_basic_lifecycle(rsclient: KanidmClient) {
|
|||
|
||||
// Check we can update a scope map
|
||||
rsclient
|
||||
.idm_oauth2_rs_update_scope_map("test_integration", "system_admins", vec!["a", "b", "c"])
|
||||
.idm_oauth2_rs_update_scope_map(
|
||||
"test_integration",
|
||||
BUILTIN_GROUP_SYSTEM_ADMINS_V1.name,
|
||||
vec!["a", "b", "c"],
|
||||
)
|
||||
.await
|
||||
.expect("Failed to create scope map");
|
||||
|
||||
|
@ -927,7 +961,7 @@ async fn test_server_rest_oauth2_basic_lifecycle(rsclient: KanidmClient) {
|
|||
// Check we can delete a scope map.
|
||||
|
||||
rsclient
|
||||
.idm_oauth2_rs_delete_scope_map("test_integration", "system_admins")
|
||||
.idm_oauth2_rs_delete_scope_map("test_integration", BUILTIN_GROUP_SYSTEM_ADMINS_V1.name)
|
||||
.await
|
||||
.expect("Failed to delete scope map");
|
||||
|
||||
|
@ -967,7 +1001,7 @@ async fn test_server_credential_update_session_pw(rsclient: KanidmClient) {
|
|||
|
||||
// Not recommended in production!
|
||||
rsclient
|
||||
.idm_group_add_members("idm_admins", &["admin"])
|
||||
.idm_group_add_members(BUILTIN_GROUP_IDM_ADMINS_V1.name, &["admin"])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -1025,7 +1059,7 @@ async fn test_server_credential_update_session_totp_pw(rsclient: KanidmClient) {
|
|||
|
||||
// Not recommended in production!
|
||||
rsclient
|
||||
.idm_group_add_members("idm_admins", &["admin"])
|
||||
.idm_group_add_members(BUILTIN_GROUP_IDM_ADMINS_V1.name, &["admin"])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -1154,7 +1188,7 @@ async fn setup_demo_account_passkey(rsclient: &KanidmClient) -> WebauthnAuthenti
|
|||
|
||||
// Not recommended in production!
|
||||
rsclient
|
||||
.idm_group_add_members("idm_admins", &["admin"])
|
||||
.idm_group_add_members(BUILTIN_GROUP_IDM_ADMINS_V1.name, &["admin"])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -1238,7 +1272,7 @@ async fn setup_demo_account_password(
|
|||
|
||||
// Not recommended in production!
|
||||
rsclient
|
||||
.idm_group_add_members("idm_admins", &["admin"])
|
||||
.idm_group_add_members(BUILTIN_GROUP_IDM_ADMINS_V1.name, &["admin"])
|
||||
.await
|
||||
.expect("Failed to add admin to idm_admins");
|
||||
|
||||
|
@ -1312,7 +1346,7 @@ async fn test_server_api_token_lifecycle(rsclient: KanidmClient) {
|
|||
|
||||
// Not recommended in production!
|
||||
rsclient
|
||||
.idm_group_add_members("idm_admins", &["admin"])
|
||||
.idm_group_add_members(BUILTIN_GROUP_IDM_ADMINS_V1.name, &["admin"])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -1370,7 +1404,7 @@ async fn test_server_user_auth_token_lifecycle(rsclient: KanidmClient) {
|
|||
|
||||
// Not recommended in production!
|
||||
rsclient
|
||||
.idm_group_add_members("idm_admins", &["admin"])
|
||||
.idm_group_add_members(BUILTIN_GROUP_IDM_ADMINS_V1.name, &["admin"])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use kanidm_client::KanidmClient;
|
||||
use kanidmd_lib::prelude::BUILTIN_GROUP_IDM_ADMINS_V1;
|
||||
use kanidmd_testkit::*;
|
||||
|
||||
#[kanidmd_testkit::test]
|
||||
|
@ -7,7 +8,12 @@ async fn account_id_unix_token(rsclient: KanidmClient) {
|
|||
|
||||
create_user(&rsclient, "group_manager", "idm_group_manage_priv").await;
|
||||
// create test user without creating new groups
|
||||
create_user(&rsclient, NOT_ADMIN_TEST_USERNAME, "idm_admins").await;
|
||||
create_user(
|
||||
&rsclient,
|
||||
NOT_ADMIN_TEST_USERNAME,
|
||||
BUILTIN_GROUP_IDM_ADMINS_V1.name,
|
||||
)
|
||||
.await;
|
||||
login_account(&rsclient, "group_manager").await;
|
||||
|
||||
let response = rsclient
|
||||
|
|
|
@ -368,7 +368,6 @@ impl Component for AdminViewPerson {
|
|||
Some(value) => value.to_string(),
|
||||
None => String::from("Display Name Unset"),
|
||||
};
|
||||
// let user_groups = userinfo.youare.attrs.get("memberof");
|
||||
|
||||
// let mail_primary = match userinfo.uat.mail_primary.as_ref() {
|
||||
// Some(email_address) => {
|
||||
|
|
|
@ -6,6 +6,7 @@ use dialoguer::theme::ColorfulTheme;
|
|||
use dialoguer::{Confirm, Input, Password, Select};
|
||||
use kanidm_client::ClientError::Http as ClientErrorHttp;
|
||||
use kanidm_client::KanidmClient;
|
||||
use kanidm_proto::constants::{ATTR_ACCOUNT_EXPIRE, ATTR_ACCOUNT_VALID_FROM};
|
||||
use kanidm_proto::messages::{AccountChangeMessage, ConsoleOutputMode, MessageStatus};
|
||||
use kanidm_proto::v1::OperationError::PasswordQuality;
|
||||
use kanidm_proto::v1::{
|
||||
|
@ -355,7 +356,7 @@ impl PersonOpt {
|
|||
let ex = match client
|
||||
.idm_person_account_get_attr(
|
||||
ano.aopts.account_id.as_str(),
|
||||
"account_expire",
|
||||
ATTR_ACCOUNT_EXPIRE,
|
||||
)
|
||||
.await
|
||||
{
|
||||
|
@ -366,7 +367,7 @@ impl PersonOpt {
|
|||
let vf = match client
|
||||
.idm_person_account_get_attr(
|
||||
ano.aopts.account_id.as_str(),
|
||||
"account_valid_from",
|
||||
ATTR_ACCOUNT_VALID_FROM,
|
||||
)
|
||||
.await
|
||||
{
|
||||
|
@ -415,7 +416,7 @@ impl PersonOpt {
|
|||
match client
|
||||
.idm_person_account_purge_attr(
|
||||
ano.aopts.account_id.as_str(),
|
||||
"account_expire",
|
||||
ATTR_ACCOUNT_EXPIRE,
|
||||
)
|
||||
.await
|
||||
{
|
||||
|
@ -435,7 +436,7 @@ impl PersonOpt {
|
|||
match client
|
||||
.idm_person_account_set_attr(
|
||||
ano.aopts.account_id.as_str(),
|
||||
"account_expire",
|
||||
ATTR_ACCOUNT_EXPIRE,
|
||||
&[&now],
|
||||
)
|
||||
.await
|
||||
|
@ -456,7 +457,7 @@ impl PersonOpt {
|
|||
match client
|
||||
.idm_person_account_set_attr(
|
||||
ano.aopts.account_id.as_str(),
|
||||
"account_expire",
|
||||
ATTR_ACCOUNT_EXPIRE,
|
||||
&[&epoch_str],
|
||||
)
|
||||
.await
|
||||
|
@ -473,7 +474,7 @@ impl PersonOpt {
|
|||
match client
|
||||
.idm_person_account_set_attr(
|
||||
ano.aopts.account_id.as_str(),
|
||||
"account_expire",
|
||||
ATTR_ACCOUNT_EXPIRE,
|
||||
&[ano.datetime.as_str()],
|
||||
)
|
||||
.await
|
||||
|
@ -490,7 +491,7 @@ impl PersonOpt {
|
|||
match client
|
||||
.idm_person_account_purge_attr(
|
||||
ano.aopts.account_id.as_str(),
|
||||
"account_valid_from",
|
||||
ATTR_ACCOUNT_VALID_FROM,
|
||||
)
|
||||
.await
|
||||
{
|
||||
|
@ -511,7 +512,7 @@ impl PersonOpt {
|
|||
match client
|
||||
.idm_person_account_set_attr(
|
||||
ano.aopts.account_id.as_str(),
|
||||
"account_valid_from",
|
||||
ATTR_ACCOUNT_VALID_FROM,
|
||||
&[ano.datetime.as_str()],
|
||||
)
|
||||
.await
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::common::OpType;
|
||||
use kanidm_proto::constants::{ATTR_ACCOUNT_EXPIRE, ATTR_ACCOUNT_VALID_FROM};
|
||||
use kanidm_proto::messages::{AccountChangeMessage, ConsoleOutputMode, MessageStatus};
|
||||
use time::OffsetDateTime;
|
||||
|
||||
|
@ -368,7 +369,7 @@ impl ServiceAccountOpt {
|
|||
let ex = match client
|
||||
.idm_service_account_get_attr(
|
||||
ano.aopts.account_id.as_str(),
|
||||
"account_expire",
|
||||
ATTR_ACCOUNT_EXPIRE,
|
||||
)
|
||||
.await
|
||||
{
|
||||
|
@ -382,7 +383,7 @@ impl ServiceAccountOpt {
|
|||
let vf = match client
|
||||
.idm_service_account_get_attr(
|
||||
ano.aopts.account_id.as_str(),
|
||||
"account_valid_from",
|
||||
ATTR_ACCOUNT_VALID_FROM,
|
||||
)
|
||||
.await
|
||||
{
|
||||
|
@ -434,7 +435,7 @@ impl ServiceAccountOpt {
|
|||
match client
|
||||
.idm_service_account_purge_attr(
|
||||
ano.aopts.account_id.as_str(),
|
||||
"account_expire",
|
||||
ATTR_ACCOUNT_EXPIRE,
|
||||
)
|
||||
.await
|
||||
{
|
||||
|
@ -450,7 +451,7 @@ impl ServiceAccountOpt {
|
|||
match client
|
||||
.idm_service_account_set_attr(
|
||||
ano.aopts.account_id.as_str(),
|
||||
"account_expire",
|
||||
ATTR_ACCOUNT_EXPIRE,
|
||||
&[ano.datetime.as_str()],
|
||||
)
|
||||
.await
|
||||
|
@ -467,7 +468,7 @@ impl ServiceAccountOpt {
|
|||
match client
|
||||
.idm_service_account_purge_attr(
|
||||
ano.aopts.account_id.as_str(),
|
||||
"account_valid_from",
|
||||
ATTR_ACCOUNT_VALID_FROM,
|
||||
)
|
||||
.await
|
||||
{
|
||||
|
@ -484,7 +485,7 @@ impl ServiceAccountOpt {
|
|||
match client
|
||||
.idm_service_account_set_attr(
|
||||
ano.aopts.account_id.as_str(),
|
||||
"account_valid_from",
|
||||
ATTR_ACCOUNT_VALID_FROM,
|
||||
&[ano.datetime.as_str()],
|
||||
)
|
||||
.await
|
||||
|
|
|
@ -23,6 +23,7 @@ use base64urlsafedata::Base64UrlSafeData;
|
|||
use chrono::Utc;
|
||||
use clap::Parser;
|
||||
use cron::Schedule;
|
||||
use kanidm_proto::constants::{LDAP_ATTR_CN, LDAP_ATTR_OBJECTCLASS};
|
||||
use kanidmd_lib::prelude::Attribute;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fs::metadata;
|
||||
|
@ -354,35 +355,35 @@ async fn run_sync(
|
|||
let is_initialise = cookie.is_none();
|
||||
|
||||
let filter = LdapFilter::Or(vec![
|
||||
// LdapFilter::Equality("objectclass".to_string(), "domain".to_string()),
|
||||
// LdapFilter::Equality(LDAP_ATTR_OBJECTCLASS.into(), "domain".to_string()),
|
||||
LdapFilter::And(vec![
|
||||
LdapFilter::Equality("objectclass".to_string(), "person".to_string()),
|
||||
LdapFilter::Equality("objectclass".to_string(), "ipantuserattrs".to_string()),
|
||||
LdapFilter::Equality("objectclass".to_string(), "posixaccount".to_string()),
|
||||
LdapFilter::Equality(LDAP_ATTR_OBJECTCLASS.into(), "person".to_string()),
|
||||
LdapFilter::Equality(LDAP_ATTR_OBJECTCLASS.into(), "ipantuserattrs".to_string()),
|
||||
LdapFilter::Equality(LDAP_ATTR_OBJECTCLASS.into(), "posixaccount".to_string()),
|
||||
]),
|
||||
LdapFilter::And(vec![
|
||||
LdapFilter::Equality("objectclass".to_string(), "groupofnames".to_string()),
|
||||
LdapFilter::Equality("objectclass".to_string(), "ipausergroup".to_string()),
|
||||
LdapFilter::Equality(LDAP_ATTR_OBJECTCLASS.into(), "groupofnames".to_string()),
|
||||
LdapFilter::Equality(LDAP_ATTR_OBJECTCLASS.into(), "ipausergroup".to_string()),
|
||||
// Ignore user private groups, kani generates these internally.
|
||||
LdapFilter::Not(Box::new(LdapFilter::Equality(
|
||||
"objectclass".to_string(),
|
||||
LDAP_ATTR_OBJECTCLASS.into(),
|
||||
"mepmanagedentry".to_string(),
|
||||
))),
|
||||
// Need to exclude the admins group as it gid conflicts to admin.
|
||||
LdapFilter::Not(Box::new(LdapFilter::Equality(
|
||||
"cn".to_string(),
|
||||
LDAP_ATTR_CN.into(),
|
||||
"admins".to_string(),
|
||||
))),
|
||||
// Kani internally has an all persons group.
|
||||
LdapFilter::Not(Box::new(LdapFilter::Equality(
|
||||
"cn".to_string(),
|
||||
LDAP_ATTR_CN.into(),
|
||||
"ipausers".to_string(),
|
||||
))),
|
||||
]),
|
||||
// Fetch TOTP's so we know when/if they change.
|
||||
LdapFilter::And(vec![
|
||||
LdapFilter::Equality("objectclass".to_string(), "ipatoken".to_string()),
|
||||
LdapFilter::Equality("objectclass".to_string(), "ipatokentotp".to_string()),
|
||||
LdapFilter::Equality(LDAP_ATTR_OBJECTCLASS.into(), "ipatoken".to_string()),
|
||||
LdapFilter::Equality(LDAP_ATTR_OBJECTCLASS.into(), "ipatokentotp".to_string()),
|
||||
]),
|
||||
]);
|
||||
|
||||
|
@ -549,7 +550,7 @@ async fn process_ipa_sync_result(
|
|||
if lentry
|
||||
.entry
|
||||
.attrs
|
||||
.get("objectclass")
|
||||
.get(LDAP_ATTR_OBJECTCLASS)
|
||||
.map(|oc| oc.contains("ipatokentotp"))
|
||||
.unwrap_or_default()
|
||||
{
|
||||
|
@ -580,7 +581,7 @@ async fn process_ipa_sync_result(
|
|||
if lentry
|
||||
.entry
|
||||
.attrs
|
||||
.get("objectclass")
|
||||
.get(LDAP_ATTR_OBJECTCLASS)
|
||||
.map(|oc| oc.contains("person"))
|
||||
.unwrap_or_default()
|
||||
{
|
||||
|
@ -624,8 +625,8 @@ async fn process_ipa_sync_result(
|
|||
|
||||
if !totp_conditions.is_empty() {
|
||||
or_filter.push(LdapFilter::And(vec![
|
||||
LdapFilter::Equality("objectclass".to_string(), "ipatoken".to_string()),
|
||||
LdapFilter::Equality("objectclass".to_string(), "ipatokentotp".to_string()),
|
||||
LdapFilter::Equality(LDAP_ATTR_OBJECTCLASS.into(), "ipatoken".to_string()),
|
||||
LdapFilter::Equality(LDAP_ATTR_OBJECTCLASS.into(), "ipatokentotp".to_string()),
|
||||
LdapFilter::Or(totp_conditions),
|
||||
]));
|
||||
}
|
||||
|
@ -642,9 +643,9 @@ async fn process_ipa_sync_result(
|
|||
|
||||
if !user_conditions.is_empty() {
|
||||
or_filter.push(LdapFilter::And(vec![
|
||||
LdapFilter::Equality("objectclass".to_string(), "person".to_string()),
|
||||
LdapFilter::Equality("objectclass".to_string(), "ipantuserattrs".to_string()),
|
||||
LdapFilter::Equality("objectclass".to_string(), "posixaccount".to_string()),
|
||||
LdapFilter::Equality(LDAP_ATTR_OBJECTCLASS.into(), "person".to_string()),
|
||||
LdapFilter::Equality(LDAP_ATTR_OBJECTCLASS.into(), "ipantuserattrs".to_string()),
|
||||
LdapFilter::Equality(LDAP_ATTR_OBJECTCLASS.into(), "posixaccount".to_string()),
|
||||
LdapFilter::Or(user_conditions),
|
||||
]));
|
||||
}
|
||||
|
@ -681,7 +682,7 @@ async fn process_ipa_sync_result(
|
|||
if lentry
|
||||
.entry
|
||||
.attrs
|
||||
.get("objectclass")
|
||||
.get(LDAP_ATTR_OBJECTCLASS)
|
||||
.map(|oc| oc.contains("ipatokentotp"))
|
||||
.unwrap_or_default()
|
||||
{
|
||||
|
@ -776,9 +777,13 @@ fn ipa_to_scim_entry(
|
|||
return Ok(None);
|
||||
}
|
||||
|
||||
let oc = sync_entry.entry.attrs.get("objectclass").ok_or_else(|| {
|
||||
error!("Invalid entry - no object class {}", dn);
|
||||
})?;
|
||||
let oc = sync_entry
|
||||
.entry
|
||||
.attrs
|
||||
.get(LDAP_ATTR_OBJECTCLASS)
|
||||
.ok_or_else(|| {
|
||||
error!("Invalid entry - no object class {}", dn);
|
||||
})?;
|
||||
|
||||
if oc.contains("person") {
|
||||
let LdapSyncReplEntry {
|
||||
|
@ -837,7 +842,7 @@ fn ipa_to_scim_entry(
|
|||
.or_else(|| entry.remove_ava_single(Attribute::UserPassword.as_ref()));
|
||||
|
||||
let mail: Vec<_> = entry
|
||||
.remove_ava("mail")
|
||||
.remove_ava(Attribute::Mail.as_ref())
|
||||
.map(|set| {
|
||||
set.into_iter()
|
||||
.map(|addr| MultiValueAttr {
|
||||
|
@ -867,12 +872,12 @@ fn ipa_to_scim_entry(
|
|||
};
|
||||
|
||||
let ssh_publickey = entry
|
||||
.remove_ava("ipasshpubkey")
|
||||
.remove_ava(Attribute::IpaSshPubKey.as_ref())
|
||||
.map(|set| {
|
||||
set.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, value)| ScimSshPubKey {
|
||||
label: format!("ipasshpubkey-{}", i),
|
||||
label: format!("{}-{}", Attribute::IpaSshPubKey, i),
|
||||
value,
|
||||
})
|
||||
.collect()
|
||||
|
@ -930,7 +935,7 @@ fn ipa_to_scim_entry(
|
|||
.transpose()?;
|
||||
|
||||
let members: Vec<_> = entry
|
||||
.remove_ava("member")
|
||||
.remove_ava(Attribute::Member.as_ref())
|
||||
.map(|set| {
|
||||
set.into_iter()
|
||||
.map(|external_id| ScimExternalMember { external_id })
|
||||
|
|
|
@ -19,6 +19,7 @@ use crate::error::SyncError;
|
|||
use chrono::Utc;
|
||||
use clap::Parser;
|
||||
use cron::Schedule;
|
||||
use kanidm_proto::constants::ATTR_OBJECTCLASS;
|
||||
use std::fs::metadata;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
|
@ -472,9 +473,13 @@ fn ldap_to_scim_entry(
|
|||
return Ok(None);
|
||||
}
|
||||
|
||||
let oc = sync_entry.entry.attrs.get("objectclass").ok_or_else(|| {
|
||||
error!("Invalid entry - no object class {}", dn);
|
||||
})?;
|
||||
let oc = sync_entry
|
||||
.entry
|
||||
.attrs
|
||||
.get(ATTR_OBJECTCLASS)
|
||||
.ok_or_else(|| {
|
||||
error!("Invalid entry - no object class {}", dn);
|
||||
})?;
|
||||
|
||||
if oc.contains(&sync_config.person_objectclass) {
|
||||
let LdapSyncReplEntry {
|
||||
|
|
|
@ -6,6 +6,7 @@ use std::sync::atomic::{AtomicU16, Ordering};
|
|||
use std::time::Duration;
|
||||
|
||||
use kanidm_client::{KanidmClient, KanidmClientBuilder};
|
||||
use kanidm_proto::constants::ATTR_ACCOUNT_EXPIRE;
|
||||
use kanidm_unix_common::constants::{
|
||||
DEFAULT_GID_ATTR_MAP, DEFAULT_HOME_ALIAS, DEFAULT_HOME_ATTR, DEFAULT_HOME_PREFIX,
|
||||
DEFAULT_SHELL, DEFAULT_UID_ATTR_MAP,
|
||||
|
@ -132,6 +133,15 @@ async fn setup_test(fix_fn: Fixture) -> (Resolver<KanidmProvider>, KanidmClient)
|
|||
// let the tables hit the floor
|
||||
}
|
||||
|
||||
/// This is the test fixture. It sets up the following:
|
||||
/// - adds admin to idm_admins
|
||||
/// - creates a test account (testaccount1)
|
||||
/// - extends the test account with posix attrs
|
||||
/// - adds a ssh public key to the test account
|
||||
/// - sets a posix password for the test account
|
||||
/// - creates a test group (testgroup1) and adds the test account to the test group
|
||||
/// - extends testgroup1 with posix attrs
|
||||
/// - creates two more groups with unix perms (allowed_group, masked_group)
|
||||
async fn test_fixture(rsclient: KanidmClient) {
|
||||
let res = rsclient
|
||||
.auth_simple_password("admin", ADMIN_TEST_PASSWORD)
|
||||
|
@ -585,7 +595,7 @@ async fn test_cache_account_expiry() {
|
|||
.await
|
||||
.expect("failed to auth as admin");
|
||||
adminclient
|
||||
.idm_person_account_set_attr("testaccount1", "account_expire", &[ACCOUNT_EXPIRE])
|
||||
.idm_person_account_set_attr("testaccount1", ATTR_ACCOUNT_EXPIRE, &[ACCOUNT_EXPIRE])
|
||||
.await
|
||||
.unwrap();
|
||||
// auth will fail
|
||||
|
|
Loading…
Reference in a new issue