kanidm_proto/
attribute.rs

1use serde::{Deserialize, Serialize};
2use utoipa::ToSchema;
3
4use crate::constants::*;
5use crate::internal::OperationError;
6use std::convert::Infallible;
7use std::fmt;
8use std::str::FromStr;
9
10pub use smartstring::alias::String as AttrString;
11
12#[derive(
13    Serialize, Deserialize, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Default, ToSchema,
14)]
15#[cfg_attr(test, derive(enum_iterator::Sequence))]
16#[serde(rename_all = "lowercase", try_from = "&str", into = "AttrString")]
17pub enum Attribute {
18    Account,
19    AccountExpire,
20    AccountValidFrom,
21    AcpCreateAttr,
22    AcpCreateClass,
23    AcpEnable,
24    AcpModifyClass,
25    AcpModifyPresentClass,
26    AcpModifyRemoveClass,
27    AcpModifyPresentAttr,
28    AcpModifyRemovedAttr,
29    AcpReceiver,
30    AcpReceiverGroup,
31    AcpSearchAttr,
32    AcpTargetScope,
33    ApiTokenSession,
34    ApplicationPassword,
35    ApplicationUrl,
36    AttestedPasskeys,
37    #[default]
38    Attr,
39    AttributeName,
40    AttributeType,
41    AuthSessionExpiry,
42    AuthPasswordMinimumLength,
43    BadlistPassword,
44    Certificate,
45    CascadeDeleted,
46    Claim,
47    Class,
48    ClassName,
49    Cn,
50    CookiePrivateKey,
51    CreatedAtCid,
52    CredentialUpdateIntentToken,
53    CredentialTypeMinimum,
54    DeniedName,
55    Description,
56    DirectMemberOf,
57    DisplayName,
58    Dn,
59    Domain,
60    DomainAllowEasterEggs,
61    DomainDevelopmentTaint,
62    DomainDisplayName,
63    DomainLdapBasedn,
64    DomainName,
65    DomainSsid,
66    DomainTokenKey,
67    DomainUuid,
68    DynGroup,
69    DynGroupFilter,
70    DynMember,
71    Email,
72    EmailAlternative,
73    EmailPrimary,
74    EntryDn,
75    EntryManagedBy,
76    EntryUuid,
77    Es256PrivateKeyDer,
78    Excludes,
79    FernetPrivateKeyStr,
80    Gecos,
81    GidNumber,
82    GrantUiHint,
83    Group,
84    IdVerificationEcKey,
85    Image,
86    Index,
87    Indexed,
88    IpaNtHash,
89    IpaSshPubKey,
90    JwsEs256PrivateKey,
91    KeyActionRotate,
92    KeyActionRevoke,
93    KeyActionImportJwsEs256,
94    KeyActionImportJwsRs256,
95    KeyInternalData,
96    KeyProvider,
97    LastModifiedCid,
98    LdapAllowUnixPwBind,
99    /// An LDAP Compatible emailAddress
100    LdapEmailAddress,
101    /// An LDAP Compatible sshkeys virtual attribute
102    LdapKeys,
103    LdapMaxQueryableAttrs,
104    LegalName,
105    LimitSearchMaxResults,
106    LimitSearchMaxFilterTest,
107    LinkedGroup,
108    LoginShell,
109    Mail,
110    May,
111    Member,
112    MemberOf,
113    MultiValue,
114    Must,
115    Name,
116    NameHistory,
117    NoIndex,
118    NsUniqueId,
119    NsAccountLock,
120    OAuth2AllowInsecureClientDisablePkce,
121    OAuth2AllowLocalhostRedirect,
122    OAuth2ConsentScopeMap,
123    OAuth2DeviceFlowEnable,
124    OAuth2JwtLegacyCryptoEnable,
125    OAuth2PreferShortUsername,
126    OAuth2RsBasicSecret,
127    OAuth2RsClaimMap,
128    OAuth2RsImplicitScopes,
129    OAuth2RsName,
130    OAuth2RsOrigin,
131    OAuth2RsOriginLanding,
132    OAuth2RsScopeMap,
133    OAuth2RsSupScopeMap,
134    OAuth2RsTokenKey,
135    OAuth2Session,
136    OAuth2StrictRedirectUri,
137    ObjectClass,
138    OtherNoIndex,
139    PassKeys,
140    PasswordImport,
141    PatchLevel,
142    Phantom,
143    PrimaryCredential,
144    PrivateCookieKey,
145    PrivilegeExpiry,
146    RadiusSecret,
147    RecycledDirectMemberOf,
148    Refers,
149    Replicated,
150    Rs256PrivateKeyDer,
151    /// A set of scim schemas. This is similar to a kanidm class.
152    #[serde(rename = "schemas")]
153    ScimSchemas,
154    Scope,
155    SourceUuid,
156    Spn,
157    /// An LDAP-compatible sshpublickey
158    LdapSshPublicKey,
159    /// The Kanidm-local ssh_publickey
160    SshPublicKey,
161    SudoHost,
162    Supplements,
163    SystemSupplements,
164    SyncAllowed,
165    SyncClass,
166    SyncCookie,
167    SyncCredentialPortal,
168    SyncExternalId,
169    SyncParentUuid,
170    SyncTokenSession,
171    SyncYieldAuthority,
172    Syntax,
173    SystemExcludes,
174    SystemMay,
175    SystemMust,
176    Term,
177    TotpImport,
178    Uid,
179    UidNumber,
180    Unique,
181    UnixPassword,
182    UnixPasswordImport,
183    UserAuthTokenSession,
184    UserId,
185    UserPassword,
186    Uuid,
187    Version,
188    WebauthnAttestationCaList,
189    AllowPrimaryCredFallback,
190
191    #[cfg(any(debug_assertions, test, feature = "test"))]
192    NonExist,
193    #[cfg(any(debug_assertions, test, feature = "test"))]
194    TestAttr,
195    #[cfg(test)]
196    TestAttrA,
197    #[cfg(test)]
198    TestAttrB,
199    #[cfg(test)]
200    TestAttrC,
201    #[cfg(test)]
202    TestAttrD,
203    #[cfg(any(debug_assertions, test, feature = "test"))]
204    TestNumber,
205    #[cfg(any(debug_assertions, test, feature = "test"))]
206    Extra,
207    #[cfg(any(debug_assertions, test, feature = "test"))]
208    TestNotAllowed,
209
210    #[cfg(not(test))]
211    #[schema(value_type = String)]
212    Custom(AttrString),
213}
214
215impl AsRef<str> for Attribute {
216    fn as_ref(&self) -> &str {
217        self.as_str()
218    }
219}
220
221impl AsRef<Attribute> for Attribute {
222    fn as_ref(&self) -> &Attribute {
223        self
224    }
225}
226
227impl TryFrom<&AttrString> for Attribute {
228    type Error = OperationError;
229
230    fn try_from(value: &AttrString) -> Result<Self, Self::Error> {
231        Ok(Attribute::inner_from_str(value.as_str()))
232    }
233}
234
235impl From<&str> for Attribute {
236    fn from(value: &str) -> Self {
237        Self::inner_from_str(value)
238    }
239}
240
241impl<'a> From<&'a Attribute> for &'a str {
242    fn from(val: &'a Attribute) -> Self {
243        val.as_str()
244    }
245}
246
247impl From<Attribute> for AttrString {
248    fn from(val: Attribute) -> Self {
249        AttrString::from(val.as_str())
250    }
251}
252
253impl FromStr for Attribute {
254    type Err = Infallible;
255
256    fn from_str(value: &str) -> Result<Self, Self::Err> {
257        Ok(Self::inner_from_str(value))
258    }
259}
260
261impl Attribute {
262    pub fn as_str(&self) -> &str {
263        match self {
264            Attribute::Account => ATTR_ACCOUNT,
265            Attribute::AccountExpire => ATTR_ACCOUNT_EXPIRE,
266            Attribute::AccountValidFrom => ATTR_ACCOUNT_VALID_FROM,
267            Attribute::AcpCreateAttr => ATTR_ACP_CREATE_ATTR,
268            Attribute::AcpCreateClass => ATTR_ACP_CREATE_CLASS,
269            Attribute::AcpEnable => ATTR_ACP_ENABLE,
270            Attribute::AcpModifyClass => ATTR_ACP_MODIFY_CLASS,
271            Attribute::AcpModifyPresentClass => ATTR_ACP_MODIFY_PRESENT_CLASS,
272            Attribute::AcpModifyRemoveClass => ATTR_ACP_MODIFY_REMOVE_CLASS,
273            Attribute::AcpModifyPresentAttr => ATTR_ACP_MODIFY_PRESENTATTR,
274            Attribute::AcpModifyRemovedAttr => ATTR_ACP_MODIFY_REMOVEDATTR,
275            Attribute::AcpReceiver => ATTR_ACP_RECEIVER,
276            Attribute::AcpReceiverGroup => ATTR_ACP_RECEIVER_GROUP,
277            Attribute::AcpSearchAttr => ATTR_ACP_SEARCH_ATTR,
278            Attribute::AcpTargetScope => ATTR_ACP_TARGET_SCOPE,
279            Attribute::ApiTokenSession => ATTR_API_TOKEN_SESSION,
280            Attribute::ApplicationPassword => ATTR_APPLICATION_PASSWORD,
281            Attribute::ApplicationUrl => ATTR_APPLICATION_URL,
282            Attribute::AttestedPasskeys => ATTR_ATTESTED_PASSKEYS,
283            Attribute::Attr => ATTR_ATTR,
284            Attribute::AttributeName => ATTR_ATTRIBUTENAME,
285            Attribute::AttributeType => ATTR_ATTRIBUTETYPE,
286            Attribute::AuthSessionExpiry => ATTR_AUTH_SESSION_EXPIRY,
287            Attribute::AuthPasswordMinimumLength => ATTR_AUTH_PASSWORD_MINIMUM_LENGTH,
288            Attribute::BadlistPassword => ATTR_BADLIST_PASSWORD,
289            Attribute::Certificate => ATTR_CERTIFICATE,
290            Attribute::CascadeDeleted => ATTR_CASCADE_DELETED,
291            Attribute::Claim => ATTR_CLAIM,
292            Attribute::Class => ATTR_CLASS,
293            Attribute::ClassName => ATTR_CLASSNAME,
294            Attribute::Cn => ATTR_CN,
295            Attribute::CookiePrivateKey => ATTR_COOKIE_PRIVATE_KEY,
296            Attribute::CreatedAtCid => ATTR_CREATED_AT_CID,
297            Attribute::CredentialUpdateIntentToken => ATTR_CREDENTIAL_UPDATE_INTENT_TOKEN,
298            Attribute::CredentialTypeMinimum => ATTR_CREDENTIAL_TYPE_MINIMUM,
299            Attribute::DeniedName => ATTR_DENIED_NAME,
300            Attribute::Description => ATTR_DESCRIPTION,
301            Attribute::DirectMemberOf => ATTR_DIRECTMEMBEROF,
302            Attribute::DisplayName => ATTR_DISPLAYNAME,
303            Attribute::Dn => ATTR_DN,
304            Attribute::Domain => ATTR_DOMAIN,
305            Attribute::DomainAllowEasterEggs => ATTR_DOMAIN_ALLOW_EASTER_EGGS,
306            Attribute::DomainDevelopmentTaint => ATTR_DOMAIN_DEVELOPMENT_TAINT,
307            Attribute::DomainDisplayName => ATTR_DOMAIN_DISPLAY_NAME,
308            Attribute::DomainLdapBasedn => ATTR_DOMAIN_LDAP_BASEDN,
309            Attribute::DomainName => ATTR_DOMAIN_NAME,
310            Attribute::DomainSsid => ATTR_DOMAIN_SSID,
311            Attribute::DomainTokenKey => ATTR_DOMAIN_TOKEN_KEY,
312            Attribute::DomainUuid => ATTR_DOMAIN_UUID,
313            Attribute::DynGroup => ATTR_DYNGROUP,
314            Attribute::DynGroupFilter => ATTR_DYNGROUP_FILTER,
315            Attribute::DynMember => ATTR_DYNMEMBER,
316            Attribute::Email => ATTR_EMAIL,
317            Attribute::EmailAlternative => ATTR_EMAIL_ALTERNATIVE,
318            Attribute::EmailPrimary => ATTR_EMAIL_PRIMARY,
319            Attribute::EntryDn => ATTR_ENTRYDN,
320            Attribute::EntryManagedBy => ATTR_ENTRY_MANAGED_BY,
321            Attribute::EntryUuid => ATTR_ENTRYUUID,
322            Attribute::Es256PrivateKeyDer => ATTR_ES256_PRIVATE_KEY_DER,
323            Attribute::Excludes => ATTR_EXCLUDES,
324            Attribute::FernetPrivateKeyStr => ATTR_FERNET_PRIVATE_KEY_STR,
325            Attribute::Gecos => ATTR_GECOS,
326            Attribute::GidNumber => ATTR_GIDNUMBER,
327            Attribute::GrantUiHint => ATTR_GRANT_UI_HINT,
328            Attribute::Group => ATTR_GROUP,
329            Attribute::IdVerificationEcKey => ATTR_ID_VERIFICATION_ECKEY,
330            Attribute::Image => ATTR_IMAGE,
331            Attribute::Index => ATTR_INDEX,
332            Attribute::Indexed => ATTR_INDEXED,
333            Attribute::IpaNtHash => ATTR_IPANTHASH,
334            Attribute::IpaSshPubKey => ATTR_IPASSHPUBKEY,
335            Attribute::JwsEs256PrivateKey => ATTR_JWS_ES256_PRIVATE_KEY,
336            Attribute::KeyActionRotate => ATTR_KEY_ACTION_ROTATE,
337            Attribute::KeyActionRevoke => ATTR_KEY_ACTION_REVOKE,
338            Attribute::KeyActionImportJwsEs256 => ATTR_KEY_ACTION_IMPORT_JWS_ES256,
339            Attribute::KeyActionImportJwsRs256 => ATTR_KEY_ACTION_IMPORT_JWS_RS256,
340            Attribute::KeyInternalData => ATTR_KEY_INTERNAL_DATA,
341            Attribute::KeyProvider => ATTR_KEY_PROVIDER,
342            Attribute::LastModifiedCid => ATTR_LAST_MODIFIED_CID,
343            Attribute::LdapAllowUnixPwBind => ATTR_LDAP_ALLOW_UNIX_PW_BIND,
344            Attribute::LdapEmailAddress => ATTR_LDAP_EMAIL_ADDRESS,
345            Attribute::LdapKeys => ATTR_LDAP_KEYS,
346            Attribute::LdapMaxQueryableAttrs => ATTR_LDAP_MAX_QUERYABLE_ATTRS,
347            Attribute::LdapSshPublicKey => ATTR_LDAP_SSHPUBLICKEY,
348            Attribute::LegalName => ATTR_LEGALNAME,
349            Attribute::LimitSearchMaxResults => ATTR_LIMIT_SEARCH_MAX_RESULTS,
350            Attribute::LimitSearchMaxFilterTest => ATTR_LIMIT_SEARCH_MAX_FILTER_TEST,
351            Attribute::LinkedGroup => ATTR_LINKEDGROUP,
352            Attribute::LoginShell => ATTR_LOGINSHELL,
353            Attribute::Mail => ATTR_MAIL,
354            Attribute::May => ATTR_MAY,
355            Attribute::Member => ATTR_MEMBER,
356            Attribute::MemberOf => ATTR_MEMBEROF,
357            Attribute::MultiValue => ATTR_MULTIVALUE,
358            Attribute::Must => ATTR_MUST,
359            Attribute::Name => ATTR_NAME,
360            Attribute::NameHistory => ATTR_NAME_HISTORY,
361            Attribute::NoIndex => ATTR_NO_INDEX,
362            Attribute::NsUniqueId => ATTR_NSUNIQUEID,
363            Attribute::NsAccountLock => ATTR_NSACCOUNTLOCK,
364            Attribute::OAuth2AllowInsecureClientDisablePkce => {
365                ATTR_OAUTH2_ALLOW_INSECURE_CLIENT_DISABLE_PKCE
366            }
367            Attribute::OAuth2AllowLocalhostRedirect => ATTR_OAUTH2_ALLOW_LOCALHOST_REDIRECT,
368            Attribute::OAuth2ConsentScopeMap => ATTR_OAUTH2_CONSENT_SCOPE_MAP,
369            Attribute::OAuth2DeviceFlowEnable => ATTR_OAUTH2_DEVICE_FLOW_ENABLE,
370            Attribute::OAuth2JwtLegacyCryptoEnable => ATTR_OAUTH2_JWT_LEGACY_CRYPTO_ENABLE,
371            Attribute::OAuth2PreferShortUsername => ATTR_OAUTH2_PREFER_SHORT_USERNAME,
372            Attribute::OAuth2RsBasicSecret => ATTR_OAUTH2_RS_BASIC_SECRET,
373            Attribute::OAuth2RsClaimMap => ATTR_OAUTH2_RS_CLAIM_MAP,
374            Attribute::OAuth2RsImplicitScopes => ATTR_OAUTH2_RS_IMPLICIT_SCOPES,
375            Attribute::OAuth2RsName => ATTR_OAUTH2_RS_NAME,
376            Attribute::OAuth2RsOrigin => ATTR_OAUTH2_RS_ORIGIN,
377            Attribute::OAuth2RsOriginLanding => ATTR_OAUTH2_RS_ORIGIN_LANDING,
378            Attribute::OAuth2RsScopeMap => ATTR_OAUTH2_RS_SCOPE_MAP,
379            Attribute::OAuth2RsSupScopeMap => ATTR_OAUTH2_RS_SUP_SCOPE_MAP,
380            Attribute::OAuth2RsTokenKey => ATTR_OAUTH2_RS_TOKEN_KEY,
381            Attribute::OAuth2Session => ATTR_OAUTH2_SESSION,
382            Attribute::OAuth2StrictRedirectUri => ATTR_OAUTH2_STRICT_REDIRECT_URI,
383            Attribute::ObjectClass => ATTR_OBJECTCLASS,
384            Attribute::OtherNoIndex => ATTR_OTHER_NO_INDEX,
385            Attribute::PassKeys => ATTR_PASSKEYS,
386            Attribute::PasswordImport => ATTR_PASSWORD_IMPORT,
387            Attribute::PatchLevel => ATTR_PATCH_LEVEL,
388            Attribute::Phantom => ATTR_PHANTOM,
389            Attribute::PrimaryCredential => ATTR_PRIMARY_CREDENTIAL,
390            Attribute::PrivateCookieKey => ATTR_PRIVATE_COOKIE_KEY,
391            Attribute::PrivilegeExpiry => ATTR_PRIVILEGE_EXPIRY,
392            Attribute::RadiusSecret => ATTR_RADIUS_SECRET,
393            Attribute::RecycledDirectMemberOf => ATTR_RECYCLEDDIRECTMEMBEROF,
394            Attribute::Refers => ATTR_REFERS,
395            Attribute::Replicated => ATTR_REPLICATED,
396            Attribute::Rs256PrivateKeyDer => ATTR_RS256_PRIVATE_KEY_DER,
397            Attribute::Scope => ATTR_SCOPE,
398            Attribute::ScimSchemas => ATTR_SCIM_SCHEMAS,
399            Attribute::SourceUuid => ATTR_SOURCE_UUID,
400            Attribute::Spn => ATTR_SPN,
401            Attribute::SshPublicKey => ATTR_SSH_PUBLICKEY,
402            Attribute::SudoHost => ATTR_SUDOHOST,
403            Attribute::Supplements => ATTR_SUPPLEMENTS,
404            Attribute::SyncAllowed => ATTR_SYNC_ALLOWED,
405            Attribute::SyncClass => ATTR_SYNC_CLASS,
406            Attribute::SyncCookie => ATTR_SYNC_COOKIE,
407            Attribute::SyncCredentialPortal => ATTR_SYNC_CREDENTIAL_PORTAL,
408            Attribute::SyncExternalId => ATTR_SYNC_EXTERNAL_ID,
409            Attribute::SyncParentUuid => ATTR_SYNC_PARENT_UUID,
410            Attribute::SyncTokenSession => ATTR_SYNC_TOKEN_SESSION,
411            Attribute::SyncYieldAuthority => ATTR_SYNC_YIELD_AUTHORITY,
412            Attribute::Syntax => ATTR_SYNTAX,
413            Attribute::SystemExcludes => ATTR_SYSTEMEXCLUDES,
414            Attribute::SystemMay => ATTR_SYSTEMMAY,
415            Attribute::SystemMust => ATTR_SYSTEMMUST,
416            Attribute::SystemSupplements => ATTR_SYSTEMSUPPLEMENTS,
417            Attribute::Term => ATTR_TERM,
418            Attribute::TotpImport => ATTR_TOTP_IMPORT,
419            Attribute::Uid => ATTR_UID,
420            Attribute::UidNumber => ATTR_UIDNUMBER,
421            Attribute::Unique => ATTR_UNIQUE,
422            Attribute::UnixPassword => ATTR_UNIX_PASSWORD,
423            Attribute::UnixPasswordImport => ATTR_UNIX_PASSWORD_IMPORT,
424            Attribute::UserAuthTokenSession => ATTR_USER_AUTH_TOKEN_SESSION,
425            Attribute::UserId => ATTR_USERID,
426            Attribute::UserPassword => ATTR_USERPASSWORD,
427            Attribute::Uuid => ATTR_UUID,
428            Attribute::Version => ATTR_VERSION,
429            Attribute::WebauthnAttestationCaList => ATTR_WEBAUTHN_ATTESTATION_CA_LIST,
430            Attribute::AllowPrimaryCredFallback => ATTR_ALLOW_PRIMARY_CRED_FALLBACK,
431
432            #[cfg(any(debug_assertions, test, feature = "test"))]
433            Attribute::NonExist => TEST_ATTR_NON_EXIST,
434            #[cfg(any(debug_assertions, test, feature = "test"))]
435            Attribute::TestAttr => TEST_ATTR_TEST_ATTR,
436
437            #[cfg(test)]
438            Attribute::TestAttrA => TEST_ATTR_TEST_ATTR_A,
439            #[cfg(test)]
440            Attribute::TestAttrB => TEST_ATTR_TEST_ATTR_B,
441            #[cfg(test)]
442            Attribute::TestAttrC => TEST_ATTR_TEST_ATTR_C,
443            #[cfg(test)]
444            Attribute::TestAttrD => TEST_ATTR_TEST_ATTR_D,
445
446            #[cfg(any(debug_assertions, test, feature = "test"))]
447            Attribute::Extra => TEST_ATTR_EXTRA,
448            #[cfg(any(debug_assertions, test, feature = "test"))]
449            Attribute::TestNumber => TEST_ATTR_NUMBER,
450            #[cfg(any(debug_assertions, test, feature = "test"))]
451            Attribute::TestNotAllowed => TEST_ATTR_NOTALLOWED,
452
453            #[cfg(not(test))]
454            Attribute::Custom(value) => value.as_str(),
455        }
456    }
457
458    // We allow this because the standard lib from_str is fallible, and we want an infallible version.
459    #[allow(clippy::should_implement_trait)]
460    fn inner_from_str(value: &str) -> Self {
461        // Could this be something like heapless to save allocations? Also gives a way
462        // to limit length of str?
463        match value.to_lowercase().as_str() {
464            ATTR_ACCOUNT => Attribute::Account,
465            ATTR_ACCOUNT_EXPIRE => Attribute::AccountExpire,
466            ATTR_ACCOUNT_VALID_FROM => Attribute::AccountValidFrom,
467            ATTR_ACP_CREATE_ATTR => Attribute::AcpCreateAttr,
468            ATTR_ACP_CREATE_CLASS => Attribute::AcpCreateClass,
469            ATTR_ACP_ENABLE => Attribute::AcpEnable,
470            ATTR_ACP_MODIFY_CLASS => Attribute::AcpModifyClass,
471            ATTR_ACP_MODIFY_PRESENT_CLASS => Attribute::AcpModifyPresentClass,
472            ATTR_ACP_MODIFY_REMOVE_CLASS => Attribute::AcpModifyRemoveClass,
473            ATTR_ACP_MODIFY_PRESENTATTR => Attribute::AcpModifyPresentAttr,
474            ATTR_ACP_MODIFY_REMOVEDATTR => Attribute::AcpModifyRemovedAttr,
475            ATTR_ACP_RECEIVER => Attribute::AcpReceiver,
476            ATTR_ACP_RECEIVER_GROUP => Attribute::AcpReceiverGroup,
477            ATTR_ACP_SEARCH_ATTR => Attribute::AcpSearchAttr,
478            ATTR_ACP_TARGET_SCOPE => Attribute::AcpTargetScope,
479            ATTR_API_TOKEN_SESSION => Attribute::ApiTokenSession,
480            ATTR_APPLICATION_PASSWORD => Attribute::ApplicationPassword,
481            ATTR_APPLICATION_URL => Attribute::ApplicationUrl,
482            ATTR_ATTESTED_PASSKEYS => Attribute::AttestedPasskeys,
483            ATTR_ATTR => Attribute::Attr,
484            ATTR_ATTRIBUTENAME => Attribute::AttributeName,
485            ATTR_ATTRIBUTETYPE => Attribute::AttributeType,
486            ATTR_AUTH_SESSION_EXPIRY => Attribute::AuthSessionExpiry,
487            ATTR_AUTH_PASSWORD_MINIMUM_LENGTH => Attribute::AuthPasswordMinimumLength,
488            ATTR_BADLIST_PASSWORD => Attribute::BadlistPassword,
489            ATTR_CERTIFICATE => Attribute::Certificate,
490            ATTR_CASCADE_DELETED => Attribute::CascadeDeleted,
491            ATTR_CLAIM => Attribute::Claim,
492            ATTR_CLASS => Attribute::Class,
493            ATTR_CLASSNAME => Attribute::ClassName,
494            ATTR_CN => Attribute::Cn,
495            ATTR_COOKIE_PRIVATE_KEY => Attribute::CookiePrivateKey,
496            ATTR_CREATED_AT_CID => Attribute::CreatedAtCid,
497            ATTR_CREDENTIAL_UPDATE_INTENT_TOKEN => Attribute::CredentialUpdateIntentToken,
498            ATTR_CREDENTIAL_TYPE_MINIMUM => Attribute::CredentialTypeMinimum,
499            ATTR_DENIED_NAME => Attribute::DeniedName,
500            ATTR_DESCRIPTION => Attribute::Description,
501            ATTR_DIRECTMEMBEROF => Attribute::DirectMemberOf,
502            ATTR_DISPLAYNAME => Attribute::DisplayName,
503            ATTR_DN => Attribute::Dn,
504            ATTR_DOMAIN => Attribute::Domain,
505            ATTR_DOMAIN_ALLOW_EASTER_EGGS => Attribute::DomainAllowEasterEggs,
506            ATTR_DOMAIN_DISPLAY_NAME => Attribute::DomainDisplayName,
507            ATTR_DOMAIN_DEVELOPMENT_TAINT => Attribute::DomainDevelopmentTaint,
508            ATTR_DOMAIN_LDAP_BASEDN => Attribute::DomainLdapBasedn,
509            ATTR_DOMAIN_NAME => Attribute::DomainName,
510            ATTR_DOMAIN_SSID => Attribute::DomainSsid,
511            ATTR_DOMAIN_TOKEN_KEY => Attribute::DomainTokenKey,
512            ATTR_DOMAIN_UUID => Attribute::DomainUuid,
513            ATTR_DYNGROUP => Attribute::DynGroup,
514            ATTR_DYNGROUP_FILTER => Attribute::DynGroupFilter,
515            ATTR_DYNMEMBER => Attribute::DynMember,
516            ATTR_EMAIL => Attribute::Email,
517            ATTR_EMAIL_ALTERNATIVE => Attribute::EmailAlternative,
518            ATTR_EMAIL_PRIMARY => Attribute::EmailPrimary,
519            ATTR_ENTRYDN => Attribute::EntryDn,
520            ATTR_ENTRY_MANAGED_BY => Attribute::EntryManagedBy,
521            ATTR_ENTRYUUID => Attribute::EntryUuid,
522            ATTR_ES256_PRIVATE_KEY_DER => Attribute::Es256PrivateKeyDer,
523            ATTR_EXCLUDES => Attribute::Excludes,
524            ATTR_FERNET_PRIVATE_KEY_STR => Attribute::FernetPrivateKeyStr,
525            ATTR_GECOS => Attribute::Gecos,
526            ATTR_GIDNUMBER => Attribute::GidNumber,
527            ATTR_GRANT_UI_HINT => Attribute::GrantUiHint,
528            ATTR_GROUP => Attribute::Group,
529            ATTR_ID_VERIFICATION_ECKEY => Attribute::IdVerificationEcKey,
530            ATTR_IMAGE => Attribute::Image,
531            ATTR_INDEX => Attribute::Index,
532            ATTR_INDEXED => Attribute::Indexed,
533            ATTR_IPANTHASH => Attribute::IpaNtHash,
534            ATTR_IPASSHPUBKEY => Attribute::IpaSshPubKey,
535            ATTR_JWS_ES256_PRIVATE_KEY => Attribute::JwsEs256PrivateKey,
536            ATTR_KEY_ACTION_ROTATE => Attribute::KeyActionRotate,
537            ATTR_KEY_ACTION_REVOKE => Attribute::KeyActionRevoke,
538            ATTR_KEY_ACTION_IMPORT_JWS_ES256 => Attribute::KeyActionImportJwsEs256,
539            ATTR_KEY_ACTION_IMPORT_JWS_RS256 => Attribute::KeyActionImportJwsRs256,
540            ATTR_KEY_INTERNAL_DATA => Attribute::KeyInternalData,
541            ATTR_KEY_PROVIDER => Attribute::KeyProvider,
542            ATTR_LAST_MODIFIED_CID => Attribute::LastModifiedCid,
543            ATTR_LDAP_ALLOW_UNIX_PW_BIND => Attribute::LdapAllowUnixPwBind,
544            ATTR_LDAP_EMAIL_ADDRESS => Attribute::LdapEmailAddress,
545            ATTR_LDAP_KEYS => Attribute::LdapKeys,
546            ATTR_LDAP_MAX_QUERYABLE_ATTRS => Attribute::LdapMaxQueryableAttrs,
547            ATTR_SSH_PUBLICKEY => Attribute::SshPublicKey,
548            ATTR_LEGALNAME => Attribute::LegalName,
549            ATTR_LINKEDGROUP => Attribute::LinkedGroup,
550            ATTR_LOGINSHELL => Attribute::LoginShell,
551            ATTR_LIMIT_SEARCH_MAX_RESULTS => Attribute::LimitSearchMaxResults,
552            ATTR_LIMIT_SEARCH_MAX_FILTER_TEST => Attribute::LimitSearchMaxFilterTest,
553            ATTR_MAIL => Attribute::Mail,
554            ATTR_MAY => Attribute::May,
555            ATTR_MEMBER => Attribute::Member,
556            ATTR_MEMBEROF => Attribute::MemberOf,
557            ATTR_MULTIVALUE => Attribute::MultiValue,
558            ATTR_MUST => Attribute::Must,
559            ATTR_NAME => Attribute::Name,
560            ATTR_NAME_HISTORY => Attribute::NameHistory,
561            ATTR_NO_INDEX => Attribute::NoIndex,
562            ATTR_NSUNIQUEID => Attribute::NsUniqueId,
563            ATTR_NSACCOUNTLOCK => Attribute::NsAccountLock,
564            ATTR_OAUTH2_ALLOW_INSECURE_CLIENT_DISABLE_PKCE => {
565                Attribute::OAuth2AllowInsecureClientDisablePkce
566            }
567            ATTR_OAUTH2_ALLOW_LOCALHOST_REDIRECT => Attribute::OAuth2AllowLocalhostRedirect,
568            ATTR_OAUTH2_CONSENT_SCOPE_MAP => Attribute::OAuth2ConsentScopeMap,
569            ATTR_OAUTH2_DEVICE_FLOW_ENABLE => Attribute::OAuth2DeviceFlowEnable,
570            ATTR_OAUTH2_JWT_LEGACY_CRYPTO_ENABLE => Attribute::OAuth2JwtLegacyCryptoEnable,
571            ATTR_OAUTH2_PREFER_SHORT_USERNAME => Attribute::OAuth2PreferShortUsername,
572            ATTR_OAUTH2_RS_BASIC_SECRET => Attribute::OAuth2RsBasicSecret,
573            ATTR_OAUTH2_RS_CLAIM_MAP => Attribute::OAuth2RsClaimMap,
574            ATTR_OAUTH2_RS_IMPLICIT_SCOPES => Attribute::OAuth2RsImplicitScopes,
575            ATTR_OAUTH2_RS_NAME => Attribute::OAuth2RsName,
576            ATTR_OAUTH2_RS_ORIGIN => Attribute::OAuth2RsOrigin,
577            ATTR_OAUTH2_RS_ORIGIN_LANDING => Attribute::OAuth2RsOriginLanding,
578            ATTR_OAUTH2_RS_SCOPE_MAP => Attribute::OAuth2RsScopeMap,
579            ATTR_OAUTH2_RS_SUP_SCOPE_MAP => Attribute::OAuth2RsSupScopeMap,
580            ATTR_OAUTH2_RS_TOKEN_KEY => Attribute::OAuth2RsTokenKey,
581            ATTR_OAUTH2_SESSION => Attribute::OAuth2Session,
582            ATTR_OAUTH2_STRICT_REDIRECT_URI => Attribute::OAuth2StrictRedirectUri,
583            ATTR_OBJECTCLASS => Attribute::ObjectClass,
584            ATTR_OTHER_NO_INDEX => Attribute::OtherNoIndex,
585            ATTR_PASSKEYS => Attribute::PassKeys,
586            ATTR_PASSWORD_IMPORT => Attribute::PasswordImport,
587            ATTR_PATCH_LEVEL => Attribute::PatchLevel,
588            ATTR_PHANTOM => Attribute::Phantom,
589            ATTR_PRIMARY_CREDENTIAL => Attribute::PrimaryCredential,
590            ATTR_PRIVATE_COOKIE_KEY => Attribute::PrivateCookieKey,
591            ATTR_PRIVILEGE_EXPIRY => Attribute::PrivilegeExpiry,
592            ATTR_RADIUS_SECRET => Attribute::RadiusSecret,
593            ATTR_RECYCLEDDIRECTMEMBEROF => Attribute::RecycledDirectMemberOf,
594            ATTR_REFERS => Attribute::Refers,
595            ATTR_REPLICATED => Attribute::Replicated,
596            ATTR_RS256_PRIVATE_KEY_DER => Attribute::Rs256PrivateKeyDer,
597            ATTR_SCIM_SCHEMAS => Attribute::ScimSchemas,
598            ATTR_SCOPE => Attribute::Scope,
599            ATTR_SOURCE_UUID => Attribute::SourceUuid,
600            ATTR_SPN => Attribute::Spn,
601            ATTR_LDAP_SSHPUBLICKEY => Attribute::LdapSshPublicKey,
602            ATTR_SUDOHOST => Attribute::SudoHost,
603            ATTR_SUPPLEMENTS => Attribute::Supplements,
604            ATTR_SYNC_ALLOWED => Attribute::SyncAllowed,
605            ATTR_SYNC_CLASS => Attribute::SyncClass,
606            ATTR_SYNC_COOKIE => Attribute::SyncCookie,
607            ATTR_SYNC_CREDENTIAL_PORTAL => Attribute::SyncCredentialPortal,
608            ATTR_SYNC_EXTERNAL_ID => Attribute::SyncExternalId,
609            ATTR_SYNC_PARENT_UUID => Attribute::SyncParentUuid,
610            ATTR_SYNC_TOKEN_SESSION => Attribute::SyncTokenSession,
611            ATTR_SYNC_YIELD_AUTHORITY => Attribute::SyncYieldAuthority,
612            ATTR_SYNTAX => Attribute::Syntax,
613            ATTR_SYSTEMEXCLUDES => Attribute::SystemExcludes,
614            ATTR_SYSTEMMAY => Attribute::SystemMay,
615            ATTR_SYSTEMMUST => Attribute::SystemMust,
616            ATTR_SYSTEMSUPPLEMENTS => Attribute::SystemSupplements,
617            ATTR_TERM => Attribute::Term,
618            ATTR_TOTP_IMPORT => Attribute::TotpImport,
619            ATTR_UID => Attribute::Uid,
620            ATTR_UIDNUMBER => Attribute::UidNumber,
621            ATTR_UNIQUE => Attribute::Unique,
622            ATTR_UNIX_PASSWORD => Attribute::UnixPassword,
623            ATTR_UNIX_PASSWORD_IMPORT => Attribute::UnixPasswordImport,
624            ATTR_USER_AUTH_TOKEN_SESSION => Attribute::UserAuthTokenSession,
625            ATTR_USERID => Attribute::UserId,
626            ATTR_USERPASSWORD => Attribute::UserPassword,
627            ATTR_UUID => Attribute::Uuid,
628            ATTR_VERSION => Attribute::Version,
629            ATTR_WEBAUTHN_ATTESTATION_CA_LIST => Attribute::WebauthnAttestationCaList,
630            ATTR_ALLOW_PRIMARY_CRED_FALLBACK => Attribute::AllowPrimaryCredFallback,
631
632            #[cfg(any(debug_assertions, test, feature = "test"))]
633            TEST_ATTR_NON_EXIST => Attribute::NonExist,
634            #[cfg(any(debug_assertions, test, feature = "test"))]
635            TEST_ATTR_TEST_ATTR => Attribute::TestAttr,
636
637            #[cfg(test)]
638            TEST_ATTR_TEST_ATTR_A => Attribute::TestAttrA,
639            #[cfg(test)]
640            TEST_ATTR_TEST_ATTR_B => Attribute::TestAttrB,
641            #[cfg(test)]
642            TEST_ATTR_TEST_ATTR_C => Attribute::TestAttrC,
643            #[cfg(test)]
644            TEST_ATTR_TEST_ATTR_D => Attribute::TestAttrD,
645
646            #[cfg(any(debug_assertions, test, feature = "test"))]
647            TEST_ATTR_EXTRA => Attribute::Extra,
648            #[cfg(any(debug_assertions, test, feature = "test"))]
649            TEST_ATTR_NUMBER => Attribute::TestNumber,
650            #[cfg(any(debug_assertions, test, feature = "test"))]
651            TEST_ATTR_NOTALLOWED => Attribute::TestNotAllowed,
652
653            #[cfg(not(test))]
654            _ => Attribute::Custom(AttrString::from(value)),
655            // Allowed only in tests
656            #[allow(clippy::unreachable)]
657            #[cfg(test)]
658            _ => {
659                unreachable!(
660                    "Check that you've implemented the Attribute conversion for {:?}",
661                    value
662                );
663            }
664        }
665    }
666}
667
668impl fmt::Display for Attribute {
669    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
670        write!(f, "{}", self.as_str())
671    }
672}
673
674impl From<Attribute> for String {
675    fn from(attr: Attribute) -> String {
676        attr.to_string()
677    }
678}
679
680/// Sub attributes are a component of SCIM, allowing tagged sub properties of a complex
681/// attribute to be accessed.
682#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
683#[serde(rename_all = "lowercase", try_from = "&str", into = "AttrString")]
684pub enum SubAttribute {
685    /// Denotes a primary value.
686    Primary,
687    /// The type of value
688    Type,
689    /// The data associated to a value
690    Value,
691
692    #[cfg(not(test))]
693    Custom(AttrString),
694}
695
696impl fmt::Display for SubAttribute {
697    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
698        write!(f, "{}", self.as_str())
699    }
700}
701
702impl From<SubAttribute> for AttrString {
703    fn from(val: SubAttribute) -> Self {
704        AttrString::from(val.as_str())
705    }
706}
707
708impl From<&str> for SubAttribute {
709    fn from(value: &str) -> Self {
710        Self::inner_from_str(value)
711    }
712}
713
714impl FromStr for SubAttribute {
715    type Err = Infallible;
716
717    fn from_str(value: &str) -> Result<Self, Self::Err> {
718        Ok(Self::inner_from_str(value))
719    }
720}
721
722impl SubAttribute {
723    pub fn as_str(&self) -> &str {
724        match self {
725            SubAttribute::Primary => SUB_ATTR_PRIMARY,
726            SubAttribute::Type => SUB_ATTR_TYPE,
727            SubAttribute::Value => SUB_ATTR_VALUE,
728            #[cfg(not(test))]
729            SubAttribute::Custom(s) => s,
730        }
731    }
732
733    // We allow this because the standard lib from_str is fallible, and we want an infallible version.
734    #[allow(clippy::should_implement_trait)]
735    fn inner_from_str(value: &str) -> Self {
736        // Could this be something like heapless to save allocations? Also gives a way
737        // to limit length of str?
738        match value.to_lowercase().as_str() {
739            SUB_ATTR_PRIMARY => SubAttribute::Primary,
740            SUB_ATTR_TYPE => SubAttribute::Type,
741            SUB_ATTR_VALUE => SubAttribute::Value,
742
743            #[cfg(not(test))]
744            _ => SubAttribute::Custom(AttrString::from(value)),
745
746            // Allowed only in tests
747            #[allow(clippy::unreachable)]
748            #[cfg(test)]
749            _ => {
750                unreachable!(
751                    "Check that you've implemented the SubAttribute conversion for {:?}",
752                    value
753                );
754            }
755        }
756    }
757}
758
759#[cfg(test)]
760mod test {
761    use super::Attribute;
762
763    #[test]
764    fn test_valueattribute_from_str() {
765        assert_eq!(Attribute::Uuid, Attribute::from("UUID"));
766        assert_eq!(Attribute::Uuid, Attribute::from("UuiD"));
767        assert_eq!(Attribute::Uuid, Attribute::from("uuid"));
768    }
769
770    #[test]
771    fn test_valueattribute_as_str() {
772        assert_eq!(Attribute::Class.as_str(), "class");
773        assert_eq!(Attribute::Class.to_string(), "class".to_string());
774    }
775
776    #[test]
777    // this ensures we cover both ends of the conversion to/from string-types
778    fn test_valueattribute_round_trip() {
779        use enum_iterator::all;
780        let the_list = all::<Attribute>().collect::<Vec<_>>();
781        for attr in the_list {
782            let attr2 = Attribute::from(attr.as_str());
783            assert!(
784                attr == attr2,
785                "Round-trip failed for {attr} <=> {attr2} check you've implemented a from and to string"
786            );
787        }
788    }
789}