mirror of
https://github.com/kanidm/kanidm.git
synced 2025-05-22 00:43:54 +02:00
20231120 2320 sssd compat (#2328)
This commit is contained in:
parent
6dc8f1db3a
commit
bb8914c70d
|
@ -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)
|
||||
|
|
132
book/src/integrations/sssd.md
Normal file
132
book/src/integrations/sssd.md
Normal 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
|
||||
```
|
|
@ -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
|
||||
|
|
|
@ -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")]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue