20231120 2320 sssd compat ()

This commit is contained in:
Firstyear 2023-11-22 10:18:03 +10:00 committed by GitHub
parent 6dc8f1db3a
commit bb8914c70d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
42 changed files with 866 additions and 53 deletions

View file

@ -36,6 +36,7 @@
- [SUSE / OpenSUSE](integrations/pam_and_nsswitch/suse.md)
- [Fedora](integrations/pam_and_nsswitch/fedora.md)
- [Troubleshooting](integrations/pam_and_nsswitch/troubleshooting.md)
- [SSSD](integrations/sssd.md)
- [SSH Key Distribution](integrations/ssh_key_dist.md)
- [Oauth2](integrations/oauth2.md)
- [LDAP](integrations/ldap.md)

View file

@ -0,0 +1,132 @@
# SSSD
[SSSD](https://sssd.io/) is an alternative [PAM and nsswitch](./pam_and_nsswitch) provider that is
commonly available on Linux.
<!-- deno-fmt-ignore-start -->
{{#template ../templates/kani-warning.md
imagepath=../images
title=WARNING
text=SSSD should be considered a "last resort". If possible, always use the native Kanidm pam and nsswitch tools instead.
}}
<!-- deno-fmt-ignore-end -->
## Limitations
SSSD has many significant limitations compared to Kanidm's native
[PAM and nsswitch](./pam_and_nsswitch) provider.
### Performance
Kanidm's native provider outperforms SSSD significantly for both online and offline user resolving
and operations. Because of this, SSSD can cause higher load on the Kanidm server due to its design
limitations.
### Features
SSSD is not able to access all of the features of Kanidm, limiting the integration options available
to you.
### Security
By default Kanidm uses state of the art cryptographic methods with configurable TPM binding of
cached local credentials. SSSD uses significantly weaker methods to cache passwords. This means that
you should not be caching credentials with SSSD, limiting deployment flexibility.
In addition, Kanidm's providers are written in Rust rather than C, meaning they have less surface
area for attack and compromise. These providers have been through multiple security audits performed
by the SUSE product security teams.
## Support
If you choose to use the SSSD provider the Kanidm project will only provide "best effort" for
compatibility and issue resolution.
## Configuration
An example configuration for SSSD is provided.
```
# Example configuration for SSSD to resolve accounts via Kanidm
#
# This should always be a "last resort". If possible you should always use the
# kanidm pam and nsswitch resolver as these will give you a better and more
# reliable setup.
#
# Changing the values of this config is not recommended.
#
# Support for environments using SSSD is "best effort".
# Setup for ssh keys
# Inside /etc/ssh/sshd_config add the lines:
# AuthorizedKeysCommand /usr/bin/sss_ssh_authorizedkeys
# AuthorizedKeysCommandUser nobody
# You can test with the command: sss_ssh_authorizedkeys <username>
[sssd]
services = nss, pam, ssh
config_file_version = 2
domains = ldap
[nss]
homedir_substring = /home
[domain/ldap]
# Uncomment this for more verbose logging.
# debug_level=3
id_provider = ldap
auth_provider = ldap
access_provider = ldap
chpass_provider = ldap
ldap_schema = rfc2307bis
ldap_search_base = o=idm
# Your URI must be LDAPS. Kanidm does not support StartTLS.
ldap_uri = ldaps://idm.example.com
# These allow SSSD to resolve user primary groups, which in Kanidm are implied by
# the existence of the user. Ensure you change the search base to your ldap_search_base.
ldap_group_object_class = object
ldap_group_search_base = o=idm?subtree?(|(objectClass=posixAccount)(objectClass=posixGroup))
# To use cacert dir, place *.crt files in this path then run:
# /usr/bin/openssl rehash /etc/openldap/certs
# or (for older versions of openssl)
# /usr/bin/c_rehash /etc/openldap/certs
# ldap_tls_cacertdir = /etc/openldap/certs
# Path to the cacert
# ldap_tls_cacert = /etc/openldap/certs/ca.crt
# Only users who match this filter can login and authorise to this machine. Note
# that users who do NOT match, will still have their uid/gid resolve, but they
# can't login.
#
# Note that because of how Kanidm presents group names, this value SHOULD be an SPN
ldap_access_filter = (memberof=idm_all_accounts@idm.example.com)
# Set the home dir override. Kanidm does not support configuration of homedirs as an
# attribute, and will use the uid number of the account. This is because users can
# change their uid at anytime, so you must have home directories configured in a stable
# way that does not change.
#
# Beware, than SSSD will incorrectly treat this value as a signed integer rather than unsigned
# so some users will have a -uidnumber instead.
override_homedir = /home/%U
# This prevents an issue where SSSD incorrectly attempts to recursively walk all
# entries in Kanidm.
#
# ⚠️ NEVER CHANGE THIS VALUE ⚠️
ignore_group_members = False
# Disable caching of credentials by SSSD. SSSD uses less secure local password storage
# mechanisims, and is a risk for credential disclosure.
#
# ⚠️ NEVER CHANGE THIS VALUE ⚠️
cache_credentials = False
```

View file

@ -88,6 +88,7 @@ pub const ATTR_LDAP_KEYS: &str = "keys";
pub const ATTR_EXCLUDES: &str = "excludes";
pub const ATTR_ES256_PRIVATE_KEY_DER: &str = "es256_private_key_der";
pub const ATTR_FERNET_PRIVATE_KEY_STR: &str = "fernet_private_key_str";
pub const ATTR_GECOS: &str = "gecos";
pub const ATTR_GIDNUMBER: &str = "gidnumber";
pub const ATTR_GRANT_UI_HINT: &str = "grant_ui_hint";
pub const ATTR_GROUP: &str = "group";
@ -143,6 +144,7 @@ pub const ATTR_SCOPE: &str = "scope";
pub const ATTR_SELF: &str = "self";
pub const ATTR_SOURCE_UUID: &str = "source_uuid";
pub const ATTR_SPN: &str = "spn";
pub const ATTR_SUDOHOST: &str = "sudohost";
pub const ATTR_SUPPLEMENTS: &str = "supplements";
pub const ATTR_LDAP_SSHPUBLICKEY: &str = "sshpublickey";
pub const ATTR_SSH_PUBLICKEY: &str = "ssh_publickey";
@ -192,6 +194,7 @@ pub const LDAP_ATTR_MEMBER: &str = "member";
pub const LDAP_ATTR_NAME: &str = "name";
pub const LDAP_ATTR_OBJECTCLASS: &str = "objectClass";
pub const LDAP_ATTR_OU: &str = "ou";
pub const LDAP_ATTR_UID: &str = "uid";
pub const LDAP_CLASS_GROUPOFNAMES: &str = "groupofnames";
// Rust can't deal with this being compiled out, don't try and #[cfg()] them

View file

@ -798,8 +798,8 @@ pub enum Filter {
// This is attr - value
#[serde(alias = "Eq")]
Eq(String, String),
#[serde(alias = "Sub")]
Sub(String, String),
#[serde(alias = "Cnt")]
Cnt(String, String),
#[serde(alias = "Pres")]
Pres(String),
#[serde(alias = "Or")]

View file

@ -234,7 +234,7 @@ pub trait BackendTransaction {
(IdList::AllIds, FilterPlan::EqUnindexed(attr.clone()))
}
}
FilterResolved::Sub(attr, subvalue, idx) => {
FilterResolved::Cnt(attr, subvalue, idx) => {
if idx.is_some() {
// Get the idx_key
let idx_key = subvalue.get_idx_sub_key();
@ -254,6 +254,14 @@ pub trait BackendTransaction {
(IdList::AllIds, FilterPlan::SubUnindexed(attr.clone()))
}
}
FilterResolved::Stw(attr, _subvalue, _idx) => {
// Schema believes this is not indexed
(IdList::AllIds, FilterPlan::SubUnindexed(attr.clone()))
}
FilterResolved::Enw(attr, _subvalue, _idx) => {
// Schema believes this is not indexed
(IdList::AllIds, FilterPlan::SubUnindexed(attr.clone()))
}
FilterResolved::Pres(attr, idx) => {
if idx.is_some() {
// Get the idl for this

View file

@ -87,6 +87,7 @@ pub enum Attribute {
Es256PrivateKeyDer,
Excludes,
FernetPrivateKeyStr,
Gecos,
GidNumber,
GrantUiHint,
Group,
@ -145,6 +146,7 @@ pub enum Attribute {
LdapSshPublicKey,
/// The Kanidm-local ssh_publickey
SshPublicKey,
SudoHost,
Supplements,
SystemSupplements,
SyncAllowed,
@ -266,6 +268,7 @@ impl TryFrom<String> for Attribute {
ATTR_ES256_PRIVATE_KEY_DER => Attribute::Es256PrivateKeyDer,
ATTR_EXCLUDES => Attribute::Excludes,
ATTR_FERNET_PRIVATE_KEY_STR => Attribute::FernetPrivateKeyStr,
ATTR_GECOS => Attribute::Gecos,
ATTR_GIDNUMBER => Attribute::GidNumber,
ATTR_GRANT_UI_HINT => Attribute::GrantUiHint,
ATTR_GROUP => Attribute::Group,
@ -322,6 +325,7 @@ impl TryFrom<String> for Attribute {
ATTR_SOURCE_UUID => Attribute::SourceUuid,
ATTR_SPN => Attribute::Spn,
ATTR_LDAP_SSHPUBLICKEY => Attribute::LdapSshPublicKey,
ATTR_SUDOHOST => Attribute::SudoHost,
ATTR_SUPPLEMENTS => Attribute::Supplements,
ATTR_SYNC_ALLOWED => Attribute::SyncAllowed,
ATTR_SYNC_CLASS => Attribute::SyncClass,
@ -421,6 +425,7 @@ impl From<Attribute> for &'static str {
Attribute::Es256PrivateKeyDer => ATTR_ES256_PRIVATE_KEY_DER,
Attribute::Excludes => ATTR_EXCLUDES,
Attribute::FernetPrivateKeyStr => ATTR_FERNET_PRIVATE_KEY_STR,
Attribute::Gecos => ATTR_GECOS,
Attribute::GidNumber => ATTR_GIDNUMBER,
Attribute::GrantUiHint => ATTR_GRANT_UI_HINT,
Attribute::Group => ATTR_GROUP,
@ -477,6 +482,7 @@ impl From<Attribute> for &'static str {
Attribute::SourceUuid => ATTR_SOURCE_UUID,
Attribute::Spn => ATTR_SPN,
Attribute::SshPublicKey => ATTR_SSH_PUBLICKEY,
Attribute::SudoHost => ATTR_SUDOHOST,
Attribute::Supplements => ATTR_SUPPLEMENTS,
Attribute::SyncAllowed => ATTR_SYNC_ALLOWED,
Attribute::SyncClass => ATTR_SYNC_CLASS,

View file

@ -249,6 +249,9 @@ pub const UUID_SCHEMA_ATTR_AUTH_PASSWORD_MINIMUM_LENGTH: Uuid =
uuid!("00000000-0000-0000-0000-ffff00000147");
pub const UUID_SCHEMA_ATTR_CREDENTIAL_TYPE_MINIMUM: Uuid =
uuid!("00000000-0000-0000-0000-ffff00000148");
pub const UUID_SCHEMA_ATTR_SUDOHOST: Uuid = uuid!("00000000-0000-0000-0000-ffff00000149");
pub const UUID_SCHEMA_ATTR_UID: Uuid = uuid!("00000000-0000-0000-0000-ffff00000150");
pub const UUID_SCHEMA_ATTR_GECOS: Uuid = uuid!("00000000-0000-0000-0000-ffff00000151");
// System and domain infos
// I'd like to strongly criticise william of the past for making poor choices about these allocations.

View file

@ -2889,7 +2889,27 @@ impl<VALID, STATE> Entry<VALID, STATE> {
}
#[inline(always)]
/// Assert if an attribute of this name is present, and one of it's values is less than
/// Assert if an attribute of this name is present, and one of its values startswith
/// the following string, if possible to perform the comparison.
pub fn attribute_startswith(&self, attr: Attribute, subvalue: &PartialValue) -> bool {
self.attrs
.get(attr.as_ref())
.map(|vset| vset.startswith(subvalue))
.unwrap_or(false)
}
#[inline(always)]
/// Assert if an attribute of this name is present, and one of its values startswith
/// the following string, if possible to perform the comparison.
pub fn attribute_endswith(&self, attr: Attribute, subvalue: &PartialValue) -> bool {
self.attrs
.get(attr.as_ref())
.map(|vset| vset.endswith(subvalue))
.unwrap_or(false)
}
#[inline(always)]
/// Assert if an attribute of this name is present, and one of its values is less than
/// the following partial value
pub fn attribute_lessthan(&self, attr: Attribute, subvalue: &PartialValue) -> bool {
self.attrs
@ -2923,13 +2943,27 @@ impl<VALID, STATE> Entry<VALID, STATE> {
false
}
},
FilterResolved::Sub(attr, subvalue, _) => match attr.try_into() {
FilterResolved::Cnt(attr, subvalue, _) => match attr.try_into() {
Ok(a) => self.attribute_substring(a, subvalue),
Err(_) => {
admin_error!("Failed to convert {} to attribute!", attr);
false
}
},
FilterResolved::Stw(attr, subvalue, _) => match attr.try_into() {
Ok(a) => self.attribute_startswith(a, subvalue),
Err(_) => {
admin_error!("Failed to convert {} to attribute!", attr);
false
}
},
FilterResolved::Enw(attr, subvalue, _) => match attr.try_into() {
Ok(a) => self.attribute_endswith(a, subvalue),
Err(_) => {
admin_error!("Failed to convert {} to attribute!", attr);
false
}
},
FilterResolved::Pres(attr, _) => match attr.try_into() {
Ok(a) => self.attribute_pres(a),
Err(_) => {
@ -3447,6 +3481,20 @@ mod tests {
assert!(!e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("llim")));
assert!(!e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("bob")));
assert!(!e.attribute_substring(Attribute::UserId, &PartialValue::new_utf8s("wl")));
assert!(e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("will")));
assert!(!e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("liam")));
assert!(!e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("lli")));
assert!(!e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("llim")));
assert!(!e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("bob")));
assert!(!e.attribute_startswith(Attribute::UserId, &PartialValue::new_utf8s("wl")));
assert!(e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("liam")));
assert!(!e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("will")));
assert!(!e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("lli")));
assert!(!e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("llim")));
assert!(!e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("bob")));
assert!(!e.attribute_endswith(Attribute::UserId, &PartialValue::new_utf8s("wl")));
}
#[test]

View file

@ -43,7 +43,7 @@ pub fn f_eq<'a>(a: Attribute, v: PartialValue) -> FC<'a> {
}
pub fn f_sub<'a>(a: Attribute, v: PartialValue) -> FC<'a> {
FC::Sub(a.into(), v)
FC::Cnt(a.into(), v)
}
pub fn f_pres<'a>(a: Attribute) -> FC<'a> {
@ -100,7 +100,7 @@ pub fn f_spn_name(id: &str) -> FC<'static> {
#[derive(Clone, Debug, Deserialize)]
pub enum FC<'a> {
Eq(&'a str, PartialValue),
Sub(&'a str, PartialValue),
Cnt(&'a str, PartialValue),
Pres(&'a str),
LessThan(&'a str, PartialValue),
Or(Vec<FC<'a>>),
@ -116,7 +116,9 @@ pub enum FC<'a> {
enum FilterComp {
// This is attr - value
Eq(AttrString, PartialValue),
Sub(AttrString, PartialValue),
Cnt(AttrString, PartialValue),
Stw(AttrString, PartialValue),
Enw(AttrString, PartialValue),
Pres(AttrString),
LessThan(AttrString, PartialValue),
Or(Vec<FilterComp>),
@ -134,8 +136,14 @@ impl fmt::Debug for FilterComp {
FilterComp::Eq(attr, pv) => {
write!(f, "{} eq {:?}", attr, pv)
}
FilterComp::Sub(attr, pv) => {
write!(f, "{} sub {:?}", attr, pv)
FilterComp::Cnt(attr, pv) => {
write!(f, "{} cnt {:?}", attr, pv)
}
FilterComp::Stw(attr, pv) => {
write!(f, "{} stw {:?}", attr, pv)
}
FilterComp::Enw(attr, pv) => {
write!(f, "{} enw {:?}", attr, pv)
}
FilterComp::Pres(attr) => {
write!(f, "{} pres", attr)
@ -197,7 +205,9 @@ impl fmt::Debug for FilterComp {
pub enum FilterResolved {
// This is attr - value - indexed slope factor
Eq(AttrString, PartialValue, Option<NonZeroU8>),
Sub(AttrString, PartialValue, Option<NonZeroU8>),
Cnt(AttrString, PartialValue, Option<NonZeroU8>),
Stw(AttrString, PartialValue, Option<NonZeroU8>),
Enw(AttrString, PartialValue, Option<NonZeroU8>),
Pres(AttrString, Option<NonZeroU8>),
LessThan(AttrString, PartialValue, Option<NonZeroU8>),
Or(Vec<FilterResolved>, Option<NonZeroU8>),
@ -219,10 +229,28 @@ impl fmt::Debug for FilterResolved {
pv
)
}
FilterResolved::Sub(attr, pv, idx) => {
FilterResolved::Cnt(attr, pv, idx) => {
write!(
f,
"(s{} {} sub {:?})",
"(s{} {} cnt {:?})",
idx.unwrap_or(NonZeroU8::MAX),
attr,
pv
)
}
FilterResolved::Stw(attr, pv, idx) => {
write!(
f,
"(s{} {} stw {:?})",
idx.unwrap_or(NonZeroU8::MAX),
attr,
pv
)
}
FilterResolved::Enw(attr, pv, idx) => {
write!(
f,
"(s{} {} enw {:?})",
idx.unwrap_or(NonZeroU8::MAX),
attr,
pv
@ -682,7 +710,7 @@ impl FilterComp {
fn new(fc: FC) -> Self {
match fc {
FC::Eq(a, v) => FilterComp::Eq(AttrString::from(a), v),
FC::Sub(a, v) => FilterComp::Sub(AttrString::from(a), v),
FC::Cnt(a, v) => FilterComp::Cnt(AttrString::from(a), v),
FC::Pres(a) => FilterComp::Pres(AttrString::from(a)),
FC::LessThan(a, v) => FilterComp::LessThan(AttrString::from(a), v),
FC::Or(v) => FilterComp::Or(v.into_iter().map(FilterComp::new).collect()),
@ -712,16 +740,12 @@ impl FilterComp {
fn get_attr_set<'a>(&'a self, r_set: &mut BTreeSet<&'a str>) {
match self {
FilterComp::Eq(attr, _) => {
r_set.insert(attr.as_str());
}
FilterComp::Sub(attr, _) => {
r_set.insert(attr.as_str());
}
FilterComp::Pres(attr) => {
r_set.insert(attr.as_str());
}
FilterComp::LessThan(attr, _) => {
FilterComp::Eq(attr, _)
| FilterComp::Cnt(attr, _)
| FilterComp::Stw(attr, _)
| FilterComp::Enw(attr, _)
| FilterComp::Pres(attr)
| FilterComp::LessThan(attr, _) => {
r_set.insert(attr.as_str());
}
FilterComp::Or(vs) => vs.iter().for_each(|f| f.get_attr_set(r_set)),
@ -762,7 +786,7 @@ impl FilterComp {
None => Err(SchemaError::InvalidAttribute(attr_norm.to_string())),
}
}
FilterComp::Sub(attr, value) => {
FilterComp::Cnt(attr, value) => {
// Validate/normalise the attr name.
let attr_norm = schema.normalise_attr_name(attr);
// Now check it exists
@ -771,7 +795,37 @@ impl FilterComp {
schema_a
.validate_partialvalue(attr_norm.as_str(), value)
// Okay, it worked, transform to a filter component
.map(|_| FilterComp::Sub(attr_norm, value.clone()))
.map(|_| FilterComp::Cnt(attr_norm, value.clone()))
// On error, pass the error back out.
}
None => Err(SchemaError::InvalidAttribute(attr_norm.to_string())),
}
}
FilterComp::Stw(attr, value) => {
// Validate/normalise the attr name.
let attr_norm = schema.normalise_attr_name(attr);
// Now check it exists
match schema_attributes.get(&attr_norm) {
Some(schema_a) => {
schema_a
.validate_partialvalue(attr_norm.as_str(), value)
// Okay, it worked, transform to a filter component
.map(|_| FilterComp::Stw(attr_norm, value.clone()))
// On error, pass the error back out.
}
None => Err(SchemaError::InvalidAttribute(attr_norm.to_string())),
}
}
FilterComp::Enw(attr, value) => {
// Validate/normalise the attr name.
let attr_norm = schema.normalise_attr_name(attr);
// Now check it exists
match schema_attributes.get(&attr_norm) {
Some(schema_a) => {
schema_a
.validate_partialvalue(attr_norm.as_str(), value)
// Okay, it worked, transform to a filter component
.map(|_| FilterComp::Enw(attr_norm, value.clone()))
// On error, pass the error back out.
}
None => Err(SchemaError::InvalidAttribute(attr_norm.to_string())),
@ -870,10 +924,10 @@ impl FilterComp {
let v = qs.clone_partialvalue(nk.as_str(), v)?;
FilterComp::Eq(nk, v)
}
ProtoFilter::Sub(a, v) => {
ProtoFilter::Cnt(a, v) => {
let nk = qs.get_schema().normalise_attr_name(a);
let v = qs.clone_partialvalue(nk.as_str(), v)?;
FilterComp::Sub(nk, v)
FilterComp::Cnt(nk, v)
}
ProtoFilter::Pres(a) => {
let nk = qs.get_schema().normalise_attr_name(a);
@ -922,10 +976,10 @@ impl FilterComp {
let v = qs.clone_partialvalue(nk.as_str(), v)?;
FilterComp::Eq(nk, v)
}
ProtoFilter::Sub(a, v) => {
ProtoFilter::Cnt(a, v) => {
let nk = qs.get_schema().normalise_attr_name(a);
let v = qs.clone_partialvalue(nk.as_str(), v)?;
FilterComp::Sub(nk, v)
FilterComp::Cnt(nk, v)
}
ProtoFilter::Pres(a) => {
let nk = qs.get_schema().normalise_attr_name(a);
@ -1004,16 +1058,32 @@ impl FilterComp {
}
LdapFilter::Present(a) => FilterComp::Pres(ldap_attr_filter_map(a)),
LdapFilter::Substring(
_a,
a,
LdapSubstringFilter {
initial: _,
any: _,
final_: _,
initial,
any,
final_,
},
) => {
// let a = ldap_attr_filter_map(a);
admin_error!("Unable to convert ldapsubstringfilter to sub filter");
return Err(OperationError::FilterGeneration);
let a = ldap_attr_filter_map(a);
let mut terms = Vec::with_capacity(any.len() + 2);
if let Some(ini) = initial {
let v = qs.clone_partialvalue(a.as_str(), ini)?;
terms.push(FilterComp::Stw(a.clone(), v));
}
for term in any.into_iter() {
let v = qs.clone_partialvalue(a.as_str(), &term)?;
terms.push(FilterComp::Cnt(a.clone(), v));
}
if let Some(fin) = final_ {
let v = qs.clone_partialvalue(a.as_str(), fin)?;
terms.push(FilterComp::Enw(a.clone(), v));
}
FilterComp::And(terms)
}
LdapFilter::GreaterOrEqual(_, _) => {
admin_error!("Unsupported filter operation - greater or equal");
@ -1071,7 +1141,7 @@ impl PartialEq for FilterResolved {
fn eq(&self, rhs: &FilterResolved) -> bool {
match (self, rhs) {
(FilterResolved::Eq(a1, v1, _), FilterResolved::Eq(a2, v2, _)) => a1 == a2 && v1 == v2,
(FilterResolved::Sub(a1, v1, _), FilterResolved::Sub(a2, v2, _)) => {
(FilterResolved::Cnt(a1, v1, _), FilterResolved::Cnt(a2, v2, _)) => {
a1 == a2 && v1 == v2
}
(FilterResolved::Pres(a1, _), FilterResolved::Pres(a2, _)) => a1 == a2,
@ -1115,7 +1185,7 @@ impl Ord for FilterResolved {
if r == Ordering::Equal {
match (self, rhs) {
(FilterResolved::Eq(a1, v1, _), FilterResolved::Eq(a2, v2, _))
| (FilterResolved::Sub(a1, v1, _), FilterResolved::Sub(a2, v2, _))
| (FilterResolved::Cnt(a1, v1, _), FilterResolved::Cnt(a2, v2, _))
| (FilterResolved::LessThan(a1, v1, _), FilterResolved::LessThan(a2, v2, _)) => {
match a1.cmp(a2) {
Ordering::Equal => v1.cmp(v2),
@ -1130,8 +1200,8 @@ impl Ord for FilterResolved {
(_, FilterResolved::Pres(_, _)) => Ordering::Greater,
(FilterResolved::LessThan(_, _, _), _) => Ordering::Less,
(_, FilterResolved::LessThan(_, _, _)) => Ordering::Greater,
(FilterResolved::Sub(_, _, _), _) => Ordering::Less,
(_, FilterResolved::Sub(_, _, _)) => Ordering::Greater,
(FilterResolved::Cnt(_, _, _), _) => Ordering::Less,
(_, FilterResolved::Cnt(_, _, _)) => Ordering::Greater,
// They can't be re-arranged, they don't move!
(_, _) => Ordering::Equal,
}
@ -1153,12 +1223,14 @@ impl FilterResolved {
FilterResolved::Eq(a, v, idx)
}
FilterComp::SelfUuid => panic!("Not possible to resolve SelfUuid in from_invalid!"),
FilterComp::Sub(a, v) => {
FilterComp::Cnt(a, v) => {
// TODO: For now, don't emit substring indexes.
// let idx = idxmeta.contains(&(&a, &IndexType::SubString));
// let idx = NonZeroU8::new(idx as u8);
FilterResolved::Sub(a, v, None)
FilterResolved::Cnt(a, v, None)
}
FilterComp::Stw(a, v) => FilterResolved::Stw(a, v, None),
FilterComp::Enw(a, v) => FilterResolved::Enw(a, v, None),
FilterComp::Pres(a) => {
let idx = idxmeta.contains(&(&a, &IndexType::Presence));
FilterResolved::Pres(a, NonZeroU8::new(idx as u8))
@ -1222,13 +1294,35 @@ impl FilterResolved {
.and_then(NonZeroU8::new);
FilterResolved::Eq(Attribute::Uuid.into(), PartialValue::Uuid(uuid), idx)
}),
FilterComp::Sub(a, v) => {
FilterComp::Cnt(a, v) => {
let idxkref = IdxKeyRef::new(&a, &IndexType::SubString);
let idx = idxmeta
.get(&idxkref as &dyn IdxKeyToRef)
.copied()
.and_then(NonZeroU8::new);
Some(FilterResolved::Sub(a, v, idx))
Some(FilterResolved::Cnt(a, v, idx))
}
FilterComp::Stw(a, v) => {
/*
let idxkref = IdxKeyRef::new(&a, &IndexType::SubString);
let idx = idxmeta
.get(&idxkref as &dyn IdxKeyToRef)
.copied()
.and_then(NonZeroU8::new);
Some(FilterResolved::Stw(a, v, idx))
*/
Some(FilterResolved::Stw(a, v, None))
}
FilterComp::Enw(a, v) => {
/*
let idxkref = IdxKeyRef::new(&a, &IndexType::SubString);
let idx = idxmeta
.get(&idxkref as &dyn IdxKeyToRef)
.copied()
.and_then(NonZeroU8::new);
Some(FilterResolved::Enw(a, v, idx))
*/
Some(FilterResolved::Enw(a, v, None))
}
FilterComp::Pres(a) => {
let idxkref = IdxKeyRef::new(&a, &IndexType::Presence);
@ -1298,7 +1392,9 @@ impl FilterResolved {
NonZeroU8::new(true as u8),
)
}),
FilterComp::Sub(a, v) => Some(FilterResolved::Sub(a, v, None)),
FilterComp::Cnt(a, v) => Some(FilterResolved::Cnt(a, v, None)),
FilterComp::Stw(a, v) => Some(FilterResolved::Stw(a, v, None)),
FilterComp::Enw(a, v) => Some(FilterResolved::Enw(a, v, None)),
FilterComp::Pres(a) => Some(FilterResolved::Pres(a, None)),
FilterComp::LessThan(a, v) => Some(FilterResolved::LessThan(a, v, None)),
FilterComp::Or(vs) => {
@ -1461,7 +1557,9 @@ impl FilterResolved {
fn get_slopeyness_factor(&self) -> Option<NonZeroU8> {
match self {
FilterResolved::Eq(_, _, sf)
| FilterResolved::Sub(_, _, sf)
| FilterResolved::Cnt(_, _, sf)
| FilterResolved::Stw(_, _, sf)
| FilterResolved::Enw(_, _, sf)
| FilterResolved::Pres(_, sf)
| FilterResolved::LessThan(_, _, sf)
| FilterResolved::Or(_, sf)

View file

@ -574,6 +574,8 @@ pub(crate) fn ldap_all_vattrs() -> Vec<String> {
ATTR_OBJECTCLASS.to_string(),
ATTR_LDAP_SSHPUBLICKEY.to_string(),
ATTR_UIDNUMBER.to_string(),
ATTR_UID.to_string(),
ATTR_GECOS.to_string(),
]
}
@ -586,7 +588,8 @@ pub(crate) fn ldap_vattr_map(input: &str) -> Option<&str> {
//
// LDAP NAME KANI ATTR SOURCE NAME
match input {
ATTR_CN => Some(ATTR_NAME),
ATTR_CN | ATTR_UID => Some(ATTR_NAME),
ATTR_GECOS => Some(ATTR_DISPLAYNAME),
ATTR_EMAIL => Some(ATTR_MAIL),
ATTR_LDAP_EMAIL_ADDRESS => Some(ATTR_MAIL),
LDAP_ATTR_EMAIL_ALTERNATIVE => Some(ATTR_MAIL),
@ -616,7 +619,7 @@ mod tests {
use compact_jwt::{Jws, JwsUnverified};
use hashbrown::HashSet;
use kanidm_proto::v1::ApiToken;
use ldap3_proto::proto::{LdapFilter, LdapOp, LdapSearchScope};
use ldap3_proto::proto::{LdapFilter, LdapOp, LdapSearchScope, LdapSubstringFilter};
use ldap3_proto::simple::*;
use super::{LdapServer, LdapSession};
@ -1318,4 +1321,106 @@ mod tests {
_ => assert!(false),
};
}
#[idm_test]
async fn test_ldap_sssd_compat(idms: &IdmServer, _idms_delayed: &IdmServerDelayed) {
let ldaps = LdapServer::new(idms).await.expect("failed to start ldap");
let acct_uuid = uuid!("cc8e95b4-c24f-4d68-ba54-8bed76f63930");
// Setup a user we want to check.
{
let e1 = entry_init!(
(Attribute::Class, EntryClass::Person.to_value()),
(Attribute::Class, EntryClass::Account.to_value()),
(Attribute::Class, EntryClass::PosixAccount.to_value()),
(Attribute::Name, Value::new_iname("testperson1")),
(Attribute::Uuid, Value::Uuid(acct_uuid)),
(Attribute::GidNumber, Value::Uint32(123456)),
(Attribute::Description, Value::new_utf8s("testperson1")),
(Attribute::DisplayName, Value::new_utf8s("testperson1"))
);
let mut server_txn = idms.proxy_write(duration_from_epoch_now()).await;
assert!(server_txn
.qs_write
.internal_create(vec![e1])
.and_then(|_| server_txn.commit())
.is_ok());
}
// Setup the anonymous login.
let anon_t = ldaps.do_bind(idms, "", "").await.unwrap().unwrap();
assert!(anon_t.effective_session == LdapSession::UnixBind(UUID_ANONYMOUS));
// SSSD tries to just search for silly attrs all the time. We ignore them.
let sr = SearchRequest {
msgid: 1,
base: "dc=example,dc=com".to_string(),
scope: LdapSearchScope::Subtree,
filter: LdapFilter::And(vec![
LdapFilter::Equality(Attribute::Class.to_string(), "sudohost".to_string()),
LdapFilter::Substring(
Attribute::SudoHost.to_string(),
LdapSubstringFilter {
initial: Some("a".to_string()),
any: vec!["x".to_string()],
final_: Some("z".to_string()),
},
),
]),
attrs: vec![
"*".to_string(),
// Already being returned
LDAP_ATTR_NAME.to_string(),
// This is a virtual attribute
Attribute::EntryUuid.to_string(),
],
};
let r1 = ldaps.do_search(idms, &sr, &anon_t).await.unwrap();
// Empty results and ldap proto success msg.
assert!(r1.len() == 1);
// Second search
let sr = SearchRequest {
msgid: 1,
base: "dc=example,dc=com".to_string(),
scope: LdapSearchScope::Subtree,
filter: LdapFilter::Equality(Attribute::Name.to_string(), "testperson1".to_string()),
attrs: vec![
"uid".to_string(),
"uidNumber".to_string(),
"gidNumber".to_string(),
"gecos".to_string(),
"cn".to_string(),
"entryuuid".to_string(),
],
};
let r1 = ldaps.do_search(idms, &sr, &anon_t).await.unwrap();
trace!(?r1);
// The result, and the ldap proto success msg.
assert!(r1.len() == 2);
match &r1[0].op {
LdapOp::SearchResultEntry(lsre) => {
assert_entry_contains!(
lsre,
"spn=testperson1@example.com,dc=example,dc=com",
(Attribute::Uid, "testperson1"),
(Attribute::Cn, "testperson1"),
(Attribute::Gecos, "testperson1"),
(Attribute::UidNumber, "123456"),
(Attribute::GidNumber, "123456"),
(
Attribute::EntryUuid.as_ref(),
"cc8e95b4-c24f-4d68-ba54-8bed76f63930"
)
);
}
_ => assert!(false),
};
}
}

View file

@ -1750,6 +1750,36 @@ impl<'a> SchemaWriteTransaction<'a> {
syntax: SyntaxType::EmailAddress,
},
);
self.attributes.insert(
Attribute::Gecos.into(),
SchemaAttribute {
name: Attribute::Gecos.into(),
uuid: UUID_SCHEMA_ATTR_GECOS,
description: String::from("An LDAP Compatible gecos."),
multivalue: false,
unique: false,
phantom: true,
sync_allowed: false,
replicated: false,
index: vec![],
syntax: SyntaxType::Utf8String,
},
);
self.attributes.insert(
Attribute::Uid.into(),
SchemaAttribute {
name: Attribute::Uid.into(),
uuid: UUID_SCHEMA_ATTR_UID,
description: String::from("An LDAP Compatible uid."),
multivalue: false,
unique: false,
phantom: true,
sync_allowed: false,
replicated: false,
index: vec![],
syntax: SyntaxType::Utf8String,
},
);
self.attributes.insert(
Attribute::UidNumber.into(),
SchemaAttribute {
@ -1765,6 +1795,22 @@ impl<'a> SchemaWriteTransaction<'a> {
syntax: SyntaxType::Uint32,
},
);
self.attributes.insert(
Attribute::SudoHost.into(),
SchemaAttribute {
name: Attribute::SudoHost.into(),
uuid: UUID_SCHEMA_ATTR_SUDOHOST,
description: String::from("An LDAP Compatible sudohost."),
multivalue: false,
unique: false,
phantom: true,
sync_allowed: false,
replicated: false,
index: vec![],
syntax: SyntaxType::Utf8String,
},
);
// end LDAP masking phantoms
self.attributes.insert(
Attribute::Image.into(),
SchemaAttribute {
@ -1780,7 +1826,6 @@ impl<'a> SchemaWriteTransaction<'a> {
syntax: SyntaxType::Image,
},
);
// end LDAP masking phantoms
self.classes.insert(
EntryClass::AttributeType.into(),

View file

@ -129,6 +129,14 @@ impl ValueSetT for ValueSetAddress {
false
}
fn startswith(&self, _pv: &PartialValue) -> bool {
false
}
fn endswith(&self, _pv: &PartialValue) -> bool {
false
}
fn lessthan(&self, _pv: &PartialValue) -> bool {
false
}
@ -352,6 +360,14 @@ impl ValueSetT for ValueSetEmailAddress {
false
}
fn startswith(&self, _pv: &PartialValue) -> bool {
false
}
fn endswith(&self, _pv: &PartialValue) -> bool {
false
}
fn lessthan(&self, _pv: &PartialValue) -> bool {
false
}

View file

@ -84,6 +84,14 @@ impl ValueSetT for ValueSetAuditLogString {
false
}
fn startswith(&self, _pv: &PartialValue) -> bool {
false
}
fn endswith(&self, _pv: &PartialValue) -> bool {
false
}
fn lessthan(&self, _pv: &PartialValue) -> bool {
false
}

View file

@ -74,6 +74,14 @@ impl ValueSetT for ValueSetPrivateBinary {
false
}
fn startswith(&self, _pv: &PartialValue) -> bool {
false
}
fn endswith(&self, _pv: &PartialValue) -> bool {
false
}
fn lessthan(&self, _pv: &PartialValue) -> bool {
false
}
@ -227,6 +235,14 @@ impl ValueSetT for ValueSetPublicBinary {
false
}
fn startswith(&self, _pv: &PartialValue) -> bool {
false
}
fn endswith(&self, _pv: &PartialValue) -> bool {
false
}
fn lessthan(&self, _pv: &PartialValue) -> bool {
false
}

View file

@ -79,6 +79,14 @@ impl ValueSetT for ValueSetBool {
false
}
fn startswith(&self, _pv: &PartialValue) -> bool {
false
}
fn endswith(&self, _pv: &PartialValue) -> bool {
false
}
fn lessthan(&self, _pv: &PartialValue) -> bool {
false
}

View file

@ -86,6 +86,14 @@ impl ValueSetT for ValueSetCid {
false
}
fn startswith(&self, _pv: &PartialValue) -> bool {
false
}
fn endswith(&self, _pv: &PartialValue) -> bool {
false
}
fn lessthan(&self, pv: &PartialValue) -> bool {
match pv {
PartialValue::Cid(c2) => self.set.iter().any(|c1| c1 < c2),

View file

@ -102,6 +102,14 @@ impl ValueSetT for ValueSetCredential {
false
}
fn startswith(&self, _pv: &PartialValue) -> bool {
false
}
fn endswith(&self, _pv: &PartialValue) -> bool {
false
}
fn lessthan(&self, _pv: &PartialValue) -> bool {
false
}
@ -378,6 +386,14 @@ impl ValueSetT for ValueSetIntentToken {
false
}
fn startswith(&self, _pv: &PartialValue) -> bool {
false
}
fn endswith(&self, _pv: &PartialValue) -> bool {
false
}
fn lessthan(&self, _pv: &PartialValue) -> bool {
false
}
@ -643,6 +659,14 @@ impl ValueSetT for ValueSetPasskey {
false
}
fn startswith(&self, _pv: &PartialValue) -> bool {
false
}
fn endswith(&self, _pv: &PartialValue) -> bool {
false
}
fn lessthan(&self, _pv: &PartialValue) -> bool {
false
}
@ -827,6 +851,14 @@ impl ValueSetT for ValueSetDeviceKey {
false
}
fn startswith(&self, _pv: &PartialValue) -> bool {
false
}
fn endswith(&self, _pv: &PartialValue) -> bool {
false
}
fn lessthan(&self, _pv: &PartialValue) -> bool {
false
}
@ -1003,6 +1035,14 @@ impl ValueSetT for ValueSetCredentialType {
false
}
fn startswith(&self, _pv: &PartialValue) -> bool {
false
}
fn endswith(&self, _pv: &PartialValue) -> bool {
false
}
fn lessthan(&self, _pv: &PartialValue) -> bool {
false
}

View file

@ -91,6 +91,14 @@ impl ValueSetT for ValueSetDateTime {
false
}
fn startswith(&self, _pv: &PartialValue) -> bool {
false
}
fn endswith(&self, _pv: &PartialValue) -> bool {
false
}
fn lessthan(&self, _pv: &PartialValue) -> bool {
false
}

View file

@ -102,6 +102,14 @@ impl ValueSetT for ValueSetEcKeyPrivate {
false
}
fn startswith(&self, _pv: &PartialValue) -> bool {
false
}
fn endswith(&self, _pv: &PartialValue) -> bool {
false
}
fn lessthan(&self, _pv: &crate::value::PartialValue) -> bool {
false
}

View file

@ -374,6 +374,14 @@ impl ValueSetT for ValueSetImage {
false
}
fn startswith(&self, _pv: &PartialValue) -> bool {
false
}
fn endswith(&self, _pv: &PartialValue) -> bool {
false
}
fn lessthan(&self, _pv: &PartialValue) -> bool {
false
}

View file

@ -85,6 +85,26 @@ impl ValueSetT for ValueSetIname {
}
}
fn startswith(&self, pv: &PartialValue) -> bool {
match pv {
PartialValue::Iname(s2) => self.set.iter().any(|s1| s1.starts_with(s2)),
_ => {
debug_assert!(false);
false
}
}
}
fn endswith(&self, pv: &PartialValue) -> bool {
match pv {
PartialValue::Iname(s2) => self.set.iter().any(|s1| s1.ends_with(s2)),
_ => {
debug_assert!(false);
false
}
}
}
fn lessthan(&self, _pv: &PartialValue) -> bool {
false
}

View file

@ -80,6 +80,14 @@ impl ValueSetT for ValueSetIndex {
false
}
fn startswith(&self, _pv: &PartialValue) -> bool {
false
}
fn endswith(&self, _pv: &PartialValue) -> bool {
false
}
fn lessthan(&self, _pv: &PartialValue) -> bool {
false
}

View file

@ -86,6 +86,26 @@ impl ValueSetT for ValueSetIutf8 {
}
}
fn startswith(&self, pv: &PartialValue) -> bool {
match pv {
PartialValue::Iutf8(s2) => self.set.iter().any(|s1| s1.starts_with(s2)),
_ => {
debug_assert!(false);
false
}
}
}
fn endswith(&self, pv: &PartialValue) -> bool {
match pv {
PartialValue::Iutf8(s2) => self.set.iter().any(|s1| s1.ends_with(s2)),
_ => {
debug_assert!(false);
false
}
}
}
fn lessthan(&self, _pv: &PartialValue) -> bool {
false
}

View file

@ -86,6 +86,14 @@ impl ValueSetT for ValueSetJsonFilter {
false
}
fn startswith(&self, _pv: &PartialValue) -> bool {
false
}
fn endswith(&self, _pv: &PartialValue) -> bool {
false
}
fn lessthan(&self, _pv: &PartialValue) -> bool {
false
}

View file

@ -101,6 +101,14 @@ impl ValueSetT for ValueSetJwsKeyEs256 {
false
}
fn startswith(&self, _pv: &PartialValue) -> bool {
false
}
fn endswith(&self, _pv: &PartialValue) -> bool {
false
}
fn lessthan(&self, _pv: &PartialValue) -> bool {
false
}
@ -282,6 +290,14 @@ impl ValueSetT for ValueSetJwsKeyRs256 {
false
}
fn startswith(&self, _pv: &PartialValue) -> bool {
false
}
fn endswith(&self, _pv: &PartialValue) -> bool {
false
}
fn lessthan(&self, _pv: &PartialValue) -> bool {
false
}

View file

@ -113,6 +113,10 @@ pub trait ValueSetT: std::fmt::Debug + DynClone {
fn substring(&self, pv: &PartialValue) -> bool;
fn startswith(&self, pv: &PartialValue) -> bool;
fn endswith(&self, pv: &PartialValue) -> bool;
fn lessthan(&self, pv: &PartialValue) -> bool;
fn len(&self) -> usize;

View file

@ -80,6 +80,14 @@ impl ValueSetT for ValueSetNsUniqueId {
false
}
fn startswith(&self, _pv: &PartialValue) -> bool {
false
}
fn endswith(&self, _pv: &PartialValue) -> bool {
false
}
fn lessthan(&self, _pv: &PartialValue) -> bool {
false
}

View file

@ -82,6 +82,14 @@ impl ValueSetT for ValueSetOauthScope {
false
}
fn startswith(&self, _pv: &PartialValue) -> bool {
false
}
fn endswith(&self, _pv: &PartialValue) -> bool {
false
}
fn lessthan(&self, _pv: &PartialValue) -> bool {
false
}
@ -251,6 +259,14 @@ impl ValueSetT for ValueSetOauthScopeMap {
false
}
fn startswith(&self, _pv: &PartialValue) -> bool {
false
}
fn endswith(&self, _pv: &PartialValue) -> bool {
false
}
fn lessthan(&self, _pv: &PartialValue) -> bool {
false
}

View file

@ -85,6 +85,26 @@ impl ValueSetT for ValueSetRestricted {
}
}
fn startswith(&self, pv: &PartialValue) -> bool {
match pv {
PartialValue::RestrictedString(s2) => self.set.iter().any(|s1| s1.starts_with(s2)),
_ => {
debug_assert!(false);
false
}
}
}
fn endswith(&self, pv: &PartialValue) -> bool {
match pv {
PartialValue::RestrictedString(s2) => self.set.iter().any(|s1| s1.ends_with(s2)),
_ => {
debug_assert!(false);
false
}
}
}
fn lessthan(&self, _pv: &PartialValue) -> bool {
false
}

View file

@ -70,6 +70,14 @@ impl ValueSetT for ValueSetSecret {
false
}
fn startswith(&self, _pv: &PartialValue) -> bool {
false
}
fn endswith(&self, _pv: &PartialValue) -> bool {
false
}
fn lessthan(&self, _pv: &PartialValue) -> bool {
false
}

View file

@ -435,6 +435,14 @@ impl ValueSetT for ValueSetSession {
false
}
fn startswith(&self, _pv: &PartialValue) -> bool {
false
}
fn endswith(&self, _pv: &PartialValue) -> bool {
false
}
fn lessthan(&self, _pv: &PartialValue) -> bool {
false
}
@ -1039,6 +1047,14 @@ impl ValueSetT for ValueSetOauth2Session {
false
}
fn startswith(&self, _pv: &PartialValue) -> bool {
false
}
fn endswith(&self, _pv: &PartialValue) -> bool {
false
}
fn lessthan(&self, _pv: &PartialValue) -> bool {
false
}
@ -1467,6 +1483,14 @@ impl ValueSetT for ValueSetApiToken {
false
}
fn startswith(&self, _pv: &PartialValue) -> bool {
false
}
fn endswith(&self, _pv: &PartialValue) -> bool {
false
}
fn lessthan(&self, _pv: &PartialValue) -> bool {
false
}

View file

@ -79,6 +79,14 @@ impl ValueSetT for ValueSetSpn {
false
}
fn startswith(&self, _pv: &PartialValue) -> bool {
false
}
fn endswith(&self, _pv: &PartialValue) -> bool {
false
}
fn lessthan(&self, _pv: &PartialValue) -> bool {
false
}

View file

@ -105,6 +105,14 @@ impl ValueSetT for ValueSetSshKey {
false
}
fn startswith(&self, _pv: &PartialValue) -> bool {
false
}
fn endswith(&self, _pv: &PartialValue) -> bool {
false
}
fn lessthan(&self, _pv: &PartialValue) -> bool {
false
}

View file

@ -80,6 +80,14 @@ impl ValueSetT for ValueSetSyntax {
false
}
fn startswith(&self, _pv: &PartialValue) -> bool {
false
}
fn endswith(&self, _pv: &PartialValue) -> bool {
false
}
fn lessthan(&self, _pv: &PartialValue) -> bool {
false
}

View file

@ -98,6 +98,14 @@ impl ValueSetT for ValueSetTotpSecret {
false
}
fn startswith(&self, _pv: &PartialValue) -> bool {
false
}
fn endswith(&self, _pv: &PartialValue) -> bool {
false
}
fn lessthan(&self, _pv: &PartialValue) -> bool {
false
}

View file

@ -69,6 +69,14 @@ impl ValueSetT for ValueSetUiHint {
false
}
fn startswith(&self, _pv: &PartialValue) -> bool {
false
}
fn endswith(&self, _pv: &PartialValue) -> bool {
false
}
fn lessthan(&self, _pv: &PartialValue) -> bool {
false
}

View file

@ -79,6 +79,14 @@ impl ValueSetT for ValueSetUint32 {
false
}
fn startswith(&self, _pv: &PartialValue) -> bool {
false
}
fn endswith(&self, _pv: &PartialValue) -> bool {
false
}
fn lessthan(&self, pv: &PartialValue) -> bool {
match pv {
PartialValue::Uint32(u) => self.set.iter().any(|i| i < u),

View file

@ -76,6 +76,14 @@ impl ValueSetT for ValueSetUrl {
false
}
fn startswith(&self, _pv: &PartialValue) -> bool {
false
}
fn endswith(&self, _pv: &PartialValue) -> bool {
false
}
fn lessthan(&self, _pv: &PartialValue) -> bool {
false
}

View file

@ -71,6 +71,26 @@ impl ValueSetT for ValueSetUtf8 {
}
}
fn startswith(&self, pv: &PartialValue) -> bool {
match pv {
PartialValue::Utf8(s2) => self.set.iter().any(|s1| s1.starts_with(s2)),
_ => {
debug_assert!(false);
false
}
}
}
fn endswith(&self, pv: &PartialValue) -> bool {
match pv {
PartialValue::Utf8(s2) => self.set.iter().any(|s1| s1.ends_with(s2)),
_ => {
debug_assert!(false);
false
}
}
}
fn lessthan(&self, _pv: &PartialValue) -> bool {
false
}

View file

@ -81,6 +81,14 @@ impl ValueSetT for ValueSetUuid {
false
}
fn startswith(&self, _pv: &PartialValue) -> bool {
false
}
fn endswith(&self, _pv: &PartialValue) -> bool {
false
}
fn lessthan(&self, _pv: &PartialValue) -> bool {
false
}
@ -241,6 +249,14 @@ impl ValueSetT for ValueSetRefer {
false
}
fn startswith(&self, _pv: &PartialValue) -> bool {
false
}
fn endswith(&self, _pv: &PartialValue) -> bool {
false
}
fn lessthan(&self, _pv: &PartialValue) -> bool {
false
}

View file

@ -308,7 +308,7 @@ impl CommonOpt {
/// This parses the token store and prompts the user to select their username, returns the username/token as a tuple of Strings
///
/// Used to reduce duplication in implementing [prompt_for_username_get_username] and [prompt_for_username_get_token]
/// Used to reduce duplication in implementing [prompt_for_username_get_username] and `prompt_for_username_get_token`
pub fn prompt_for_username_get_values(token_cache_path: &str) -> Result<(String, String), String> {
let tokens = match read_tokens(token_cache_path) {
Ok(value) => value,

View file

@ -96,7 +96,7 @@ impl IdProvider for KanidmProvider {
})?;
if id_key.is_none() {
let lik = tpm
let loadable_id_key = tpm
.identity_key_create(machine_key, tpm::KeyAlgorithm::Ecdsa256)
.map_err(|tpm_err| {
error!(?tpm_err);
@ -104,7 +104,7 @@ impl IdProvider for KanidmProvider {
})?;
keystore
.insert_tagged_hsm_key(TAG_IDKEY, &lik)
.insert_tagged_hsm_key(TAG_IDKEY, &loadable_id_key)
.map_err(|ks_err| {
error!(?ks_err);
IdpError::KeyStore