mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 12:37:00 +01:00
I might have become clippy this time (#449)
* clippiness * it is really handy that we have tests * it is still really handy that we have tests
This commit is contained in:
parent
35c1de4c45
commit
ca446ddca5
|
@ -13,19 +13,18 @@ will override even if applicable. They should only be created by system access p
|
|||
because we have certain requirements to deny certain changes.
|
||||
|
||||
Access profiles are stored as entries and are dynamically loaded into a structure that is
|
||||
more efficent for use at runtime. Schema and it's transactions are a similar implementation.
|
||||
more efficent for use at runtime. Schema and its transactions are a similar implementation.
|
||||
|
||||
Search Requirements
|
||||
-------------------
|
||||
|
||||
A search access profile, must be able to limit the content of a search request and it's
|
||||
scoping.
|
||||
A search access profile must be able to limit:
|
||||
|
||||
A search access profile, must be able to limit the returned set of data from the objects
|
||||
visible.
|
||||
1. the content of a search request and its scoping.
|
||||
2. the returned set of data from the objects visible.
|
||||
|
||||
An example is that user Alice should only be able to search for objects where the class
|
||||
is person, and where they are a memberOf "visible" group. Alice should only be able to
|
||||
is person and the object is a memberOf "visible" group. Alice should only be able to
|
||||
see those users displayNames (not their legalName for example), and their public email.
|
||||
|
||||
Worded a bit differently. You need permission over the scope of entries, you need to be able
|
||||
|
@ -142,7 +141,7 @@ An example is that user Alice should only be able to delete objects where the me
|
|||
Create Requirements
|
||||
-------------------
|
||||
|
||||
A create profile defines a filtering limit on what content can be created and it's requirements.
|
||||
A create profile defines a filtering limit on what content can be created and its requirements.
|
||||
|
||||
A create profile defines a limit on what attributes can be created in addition to the filtering
|
||||
requirements.
|
||||
|
@ -152,7 +151,7 @@ only name the group - they can not add members to the group.
|
|||
|
||||
A content requirement could be something such as the value an attribute can contain must conform to a
|
||||
regex, IE, you can create a group of any name, except where the name contains "admin" somewhere
|
||||
in it's name. Arguable, this is partially possible with filtering.
|
||||
in its name. Arguable, this is partially possible with filtering.
|
||||
|
||||
For example, we want to be able to limit the classes that someone *could* create on something
|
||||
because classes often are used as a security type.
|
||||
|
@ -329,7 +328,7 @@ user is collected. These then are added to the users requested search as:
|
|||
In this manner, the search security is easily applied, as if the targets to conform to one of the
|
||||
required search profile filters, the outer And condition is nullified and no results returned.
|
||||
|
||||
Once complete, in the translation of the entry -> proto_entry, each access control and it's allowed
|
||||
Once complete, in the translation of the entry -> proto_entry, each access control and its allowed
|
||||
set of attrs has to be checked to determine what of that entry can be displayed. Consider there are
|
||||
three entries, A, B, C. An ACI that allows read of "name" on A, B exists, and a read of "mail" on
|
||||
B, C. The correct behaviour is then:
|
||||
|
@ -395,7 +394,7 @@ filter_no_index to the entry to entry. If all of this passes, the create is allo
|
|||
A key point, is that there is no union of create aci's - the WHOLE aci must pass, not parts of
|
||||
multiple. This means if a control say "allows creating group with member" and "allows creating
|
||||
user with name", creating a gorup with name is not allowed - despite your ability to create
|
||||
an entry with "name" it's classes don't match. This way, the admin of the service can define
|
||||
an entry with "name", its classes don't match. This way, the admin of the service can define
|
||||
create controls with really specific intent to how they'll be used, without risk of two
|
||||
controls causing un-intended effects (users that are also groups, or allowing values that
|
||||
were not intended).
|
||||
|
|
|
@ -46,14 +46,15 @@ Rate Limiting is the process of delaying authentication responses to slow the nu
|
|||
against an account to deter attackers. This is often used to prevent attackers from bruteforcing
|
||||
passwords at a high rate.
|
||||
|
||||
The best defence again these attacks is MFA. Due to the design of Kanidm, the second factor
|
||||
(ie the webauthn token or the otp) is always checked *before* the password, meaning that the
|
||||
attacker is unable to attack the password *unless* they also have the corresponding MFA token.
|
||||
The best defence again these attacks is Multi-factor authentication (MFA). Due to the design of Kanidm,
|
||||
the second factor (ie the webauthn token or the OTP) is always checked *before* the password,
|
||||
meaning that the attacker is unable to attack the password *unless* they also have
|
||||
the corresponding MFA token.
|
||||
|
||||
However, not all accounts will have MFA enabled, which means that defences are still required to
|
||||
prevent these attacks for password-only accounts. Accounts protected with TOTP must also be rate
|
||||
limited according to NIST sp800 63b. Webauthn does *not* require ratelimiting as a single factor
|
||||
or multi factor device.
|
||||
limited according to [NIST SP800-63B](https://pages.nist.gov/800-63-3/sp800-63b.html).
|
||||
Webauthn does *not* require rate-limiting as a single factor or multi factor device.
|
||||
|
||||
As an account can only have a single proceeding authentication session at a time, this provides
|
||||
serialisation and rate limiting per account of the service. However, as Kanidm will in the future
|
||||
|
@ -78,7 +79,7 @@ For accounts with password-only:
|
|||
* If the attempts continue, the account is hard locked and signalled to an external system that this has occured.
|
||||
|
||||
The value of X should be less than 100, so that the NIST guidelines can be met. This is beacuse when there are
|
||||
many replicas, each replica maintains it's own locking state, so "eventually" as each replica is attempted to be
|
||||
many replicas, each replica maintains its own locking state, so "eventually" as each replica is attempted to be
|
||||
bruteforced, then they will all eventually soft lock the account. In larger environments, we require
|
||||
external signalling to coordinate the locking of the account.
|
||||
|
||||
|
@ -101,7 +102,7 @@ It must be possible to expire an account so it no longer operates (IE temporary
|
|||
accounts that can only operate after a known point in time (Student enrollments and their course
|
||||
commencment date).
|
||||
|
||||
This expiry must exist at the account level, but also on issued token/api password levels. This allows revocation of
|
||||
This expiry must exist at the account level, but also on issued token/API password levels. This allows revocation of
|
||||
individual tokens, but also the expiry of the account and all tokens as a whole. This expiry may be
|
||||
undone, allowing the credentials to become valid once again.
|
||||
|
||||
|
@ -111,7 +112,7 @@ are stored on the server in unix epoch to account for timezones and geographic d
|
|||
* Interaction with already issued tokens.
|
||||
* it prevents them from working?
|
||||
|
||||
Must prevent creation of radius auth tokens
|
||||
Must prevent creation of RADIUS auth tokens
|
||||
|
||||
Must prevent login via unix.
|
||||
|
||||
|
|
|
@ -886,7 +886,7 @@ impl QueryServerWriteV1 {
|
|||
"class".into(),
|
||||
Value::new_class("person"),
|
||||
)))
|
||||
.filter_map(|v| v)
|
||||
.flatten()
|
||||
.collect();
|
||||
|
||||
let ml = ModifyList::new_list(mods);
|
||||
|
@ -939,7 +939,7 @@ impl QueryServerWriteV1 {
|
|||
.chain(iter::once(shell.map(|s| {
|
||||
Modify::Present("loginshell".into(), Value::new_iutf8(s.as_str()))
|
||||
})))
|
||||
.filter_map(|v| v)
|
||||
.flatten()
|
||||
.collect();
|
||||
|
||||
let ml = ModifyList::new_list(mods);
|
||||
|
@ -980,7 +980,7 @@ impl QueryServerWriteV1 {
|
|||
.chain(iter::once(gx.gidnumber.map(|n| {
|
||||
Modify::Present("gidnumber".into(), Value::new_uint32(n))
|
||||
})))
|
||||
.filter_map(|v| v)
|
||||
.flatten()
|
||||
.collect();
|
||||
|
||||
let ml = ModifyList::new_list(mods);
|
||||
|
|
|
@ -1005,7 +1005,7 @@ pub async fn auth(mut req: tide::Request<AppState>) -> tide::Result {
|
|||
Ok(ProtoAuthState::Denied(reason))
|
||||
}
|
||||
}
|
||||
.map(|state| AuthResponse { state, sessionid })
|
||||
.map(|state| AuthResponse { sessionid, state })
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
};
|
||||
|
|
|
@ -194,7 +194,7 @@ impl SchemaAttribute {
|
|||
SyntaxType::Credential => v.is_credential(),
|
||||
SyntaxType::RadiusUtf8String => v.is_radius_string(),
|
||||
SyntaxType::SshKey => v.is_sshkey(),
|
||||
SyntaxType::ServicePrincipalName => v.is_spn(),
|
||||
SyntaxType::SecurityPrincipalName => v.is_spn(),
|
||||
SyntaxType::UINT32 => v.is_uint32(),
|
||||
SyntaxType::Cid => v.is_cid(),
|
||||
SyntaxType::NsUniqueId => v.is_nsuniqueid(),
|
||||
|
@ -338,7 +338,7 @@ impl SchemaAttribute {
|
|||
}
|
||||
})
|
||||
}),
|
||||
SyntaxType::ServicePrincipalName => ava.iter().fold(Ok(()), |acc, v| {
|
||||
SyntaxType::SecurityPrincipalName => ava.iter().fold(Ok(()), |acc, v| {
|
||||
acc.and_then(|_| {
|
||||
if v.is_spn() {
|
||||
Ok(())
|
||||
|
@ -708,13 +708,13 @@ impl<'a> SchemaWriteTransaction<'a> {
|
|||
name: AttrString::from("spn"),
|
||||
uuid: *UUID_SCHEMA_ATTR_SPN,
|
||||
description: String::from(
|
||||
"The service principle name of an object, unique across all domain trusts",
|
||||
"The Security Principal Name of an object, unique across all domain trusts",
|
||||
),
|
||||
multivalue: false,
|
||||
unique: true,
|
||||
phantom: false,
|
||||
index: vec![IndexType::Equality],
|
||||
syntax: SyntaxType::ServicePrincipalName,
|
||||
syntax: SyntaxType::SecurityPrincipalName,
|
||||
},
|
||||
);
|
||||
self.attributes.insert(
|
||||
|
@ -1085,7 +1085,7 @@ impl<'a> SchemaWriteTransaction<'a> {
|
|||
unique: false,
|
||||
phantom: true,
|
||||
index: vec![],
|
||||
syntax: SyntaxType::ServicePrincipalName,
|
||||
syntax: SyntaxType::SecurityPrincipalName,
|
||||
},
|
||||
);
|
||||
self.attributes.insert(
|
||||
|
|
|
@ -530,7 +530,7 @@ pub trait QueryServerTransaction<'a> {
|
|||
SyntaxType::Credential => Err(OperationError::InvalidAttribute("Credentials can not be supplied through modification - please use the IDM api".to_string())),
|
||||
SyntaxType::RadiusUtf8String => Err(OperationError::InvalidAttribute("Radius secrets can not be supplied through modification - please use the IDM api".to_string())),
|
||||
SyntaxType::SshKey => Err(OperationError::InvalidAttribute("SSH public keys can not be supplied through modification - please use the IDM api".to_string())),
|
||||
SyntaxType::ServicePrincipalName => Err(OperationError::InvalidAttribute("SPNs are generated and not able to be set.".to_string())),
|
||||
SyntaxType::SecurityPrincipalName => Err(OperationError::InvalidAttribute("SPNs are generated and not able to be set.".to_string())),
|
||||
SyntaxType::UINT32 => Value::new_uint32_str(value)
|
||||
.ok_or_else(|| OperationError::InvalidAttribute("Invalid uint32 syntax".to_string())),
|
||||
SyntaxType::Cid => Err(OperationError::InvalidAttribute("CIDs are generated and not able to be set.".to_string())),
|
||||
|
@ -612,7 +612,7 @@ pub trait QueryServerTransaction<'a> {
|
|||
SyntaxType::Credential => Ok(PartialValue::new_credential_tag(value)),
|
||||
SyntaxType::RadiusUtf8String => Ok(PartialValue::new_radius_string()),
|
||||
SyntaxType::SshKey => Ok(PartialValue::new_sshkey_tag_s(value)),
|
||||
SyntaxType::ServicePrincipalName => {
|
||||
SyntaxType::SecurityPrincipalName => {
|
||||
PartialValue::new_spn_s(value).ok_or_else(|| {
|
||||
OperationError::InvalidAttribute("Invalid spn syntax".to_string())
|
||||
})
|
||||
|
|
|
@ -123,7 +123,7 @@ pub enum SyntaxType {
|
|||
Credential,
|
||||
RadiusUtf8String,
|
||||
SshKey,
|
||||
ServicePrincipalName,
|
||||
SecurityPrincipalName,
|
||||
UINT32,
|
||||
Cid,
|
||||
NsUniqueId,
|
||||
|
@ -148,7 +148,7 @@ impl TryFrom<&str> for SyntaxType {
|
|||
"CREDENTIAL" => Ok(SyntaxType::Credential),
|
||||
"RADIUS_UTF8STRING" => Ok(SyntaxType::RadiusUtf8String),
|
||||
"SSHKEY" => Ok(SyntaxType::SshKey),
|
||||
"SERVICE_PRINCIPLE_NAME" => Ok(SyntaxType::ServicePrincipalName),
|
||||
"SECURITY_PRINCIPAL_NAME" => Ok(SyntaxType::SecurityPrincipalName),
|
||||
"UINT32" => Ok(SyntaxType::UINT32),
|
||||
"CID" => Ok(SyntaxType::Cid),
|
||||
"NSUNIQUEID" => Ok(SyntaxType::NsUniqueId),
|
||||
|
@ -174,7 +174,7 @@ impl TryFrom<usize> for SyntaxType {
|
|||
8 => Ok(SyntaxType::Credential),
|
||||
9 => Ok(SyntaxType::RadiusUtf8String),
|
||||
10 => Ok(SyntaxType::SshKey),
|
||||
11 => Ok(SyntaxType::ServicePrincipalName),
|
||||
11 => Ok(SyntaxType::SecurityPrincipalName),
|
||||
12 => Ok(SyntaxType::UINT32),
|
||||
13 => Ok(SyntaxType::Cid),
|
||||
14 => Ok(SyntaxType::Utf8StringIname),
|
||||
|
@ -199,7 +199,7 @@ impl SyntaxType {
|
|||
SyntaxType::Credential => 8,
|
||||
SyntaxType::RadiusUtf8String => 9,
|
||||
SyntaxType::SshKey => 10,
|
||||
SyntaxType::ServicePrincipalName => 11,
|
||||
SyntaxType::SecurityPrincipalName => 11,
|
||||
SyntaxType::UINT32 => 12,
|
||||
SyntaxType::Cid => 13,
|
||||
SyntaxType::Utf8StringIname => 14,
|
||||
|
@ -227,7 +227,7 @@ impl fmt::Display for SyntaxType {
|
|||
SyntaxType::Credential => "CREDENTIAL",
|
||||
SyntaxType::RadiusUtf8String => "RADIUS_UTF8STRING",
|
||||
SyntaxType::SshKey => "SSHKEY",
|
||||
SyntaxType::ServicePrincipalName => "SERVICE_PRINCIPLE_NAME",
|
||||
SyntaxType::SecurityPrincipalName => "SECURITY_PRINCIPAL_NAME",
|
||||
SyntaxType::UINT32 => "UINT32",
|
||||
SyntaxType::Cid => "CID",
|
||||
SyntaxType::NsUniqueId => "NSUNIQUEID",
|
||||
|
|
Loading…
Reference in a new issue