kanidm_proto/
attribute.rs

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