mirror of
https://github.com/kanidm/kanidm.git
synced 2025-02-23 04:27:02 +01:00
parent
2a5e8113e6
commit
2a7a009482
|
@ -70,7 +70,7 @@ tools to interact with Kanidm.
|
|||
- Replication for two node environments is now supported
|
||||
- Account policy supports password minimum length
|
||||
- Improve performance of webui
|
||||
- Add transitional compatability with SSSD
|
||||
- Add transitional compatibility with SSSD
|
||||
- Improve TPM interfaces in unix clients
|
||||
- Allow importing more weak password schemes from FreeIPA
|
||||
- Support Attestation of Passkeys/Webauthn - this makes us the first IDM to support this!
|
||||
|
@ -227,7 +227,7 @@ for a future supported release.
|
|||
|
||||
The project is shaping up very nicely, and a beta will be coming soon!
|
||||
|
||||
### Upgrade Note!
|
||||
### Upgrade Note
|
||||
|
||||
This version will _require_ TLS on all servers, even if behind a load balancer or TLS terminating
|
||||
proxy. You should be ready for this change when you upgrade to the latest version.
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
//! These components should be "per server". Any "per domain" config should be in the system
|
||||
//! or domain entries that are able to be replicated.
|
||||
|
||||
use std::fmt;
|
||||
use std::fmt::{self, Display};
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
@ -431,12 +431,12 @@ pub enum ServerRole {
|
|||
ReadOnlyReplica,
|
||||
}
|
||||
|
||||
impl ToString for ServerRole {
|
||||
fn to_string(&self) -> String {
|
||||
impl Display for ServerRole {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
ServerRole::WriteReplica => "write replica".to_string(),
|
||||
ServerRole::WriteReplicaNoUI => "write replica (no ui)".to_string(),
|
||||
ServerRole::ReadOnlyReplica => "read only replica".to_string(),
|
||||
ServerRole::WriteReplica => f.write_str("write replica"),
|
||||
ServerRole::WriteReplicaNoUI => f.write_str("write replica (no ui)"),
|
||||
ServerRole::ReadOnlyReplica => f.write_str("read only replica"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -537,8 +537,8 @@ impl fmt::Display for Configuration {
|
|||
self.integration_test_config.is_some()
|
||||
)?;
|
||||
write!(f, "console output format: {:?} ", self.output_mode)?;
|
||||
write!(f, "log_level: {}", self.log_level.clone().to_string())?;
|
||||
write!(f, "role: {}, ", self.role.to_string())?;
|
||||
write!(f, "log_level: {}", self.log_level)?;
|
||||
write!(f, "role: {}, ", self.role)?;
|
||||
match &self.repl_config {
|
||||
Some(repl) => {
|
||||
write!(f, "replication: enabled")?;
|
||||
|
@ -662,7 +662,7 @@ impl Configuration {
|
|||
}
|
||||
|
||||
pub fn update_db_fs_type(&mut self, p: &Option<FsType>) {
|
||||
self.db_fs_type = p.to_owned();
|
||||
p.clone_into(&mut self.db_fs_type);
|
||||
}
|
||||
|
||||
pub fn update_bind(&mut self, b: &Option<String>) {
|
||||
|
@ -673,12 +673,12 @@ impl Configuration {
|
|||
}
|
||||
|
||||
pub fn update_ldapbind(&mut self, l: &Option<String>) {
|
||||
self.ldapaddress = l.clone();
|
||||
self.ldapaddress.clone_from(l);
|
||||
}
|
||||
|
||||
pub fn update_admin_bind_path(&mut self, p: &Option<String>) {
|
||||
if let Some(p) = p {
|
||||
self.adminbindpath = p.clone();
|
||||
self.adminbindpath.clone_from(p);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -148,7 +148,7 @@ impl FromRequestParts<ServerState> for VerifiedClientInformation {
|
|||
if authz_type == "basic" {
|
||||
(Some(authz_data.to_string()), None)
|
||||
} else if authz_type == "bearer" {
|
||||
if let Some(jwsc) = JwsCompact::from_str(authz_data).ok() {
|
||||
if let Ok(jwsc) = JwsCompact::from_str(authz_data) {
|
||||
(None, Some(jwsc))
|
||||
} else {
|
||||
warn!("bearer jws invalid");
|
||||
|
|
|
@ -46,6 +46,7 @@ use kanidm_lib_crypto::x509_cert::{der::Decode, x509_public_key_s256, Certificat
|
|||
|
||||
use serde::de::DeserializeOwned;
|
||||
use sketching::*;
|
||||
use std::fmt::Write;
|
||||
use tokio::{
|
||||
net::{TcpListener, TcpStream},
|
||||
sync::broadcast,
|
||||
|
@ -227,8 +228,10 @@ pub async fn create_https_server(
|
|||
|
||||
let js_checksums: String = js_directives
|
||||
.iter()
|
||||
.map(|value| format!(" 'sha384-{}'", value))
|
||||
.collect();
|
||||
.fold(String::new(), |mut output, value| {
|
||||
let _ = write!(output, " 'sha384-{}'", value);
|
||||
output
|
||||
});
|
||||
|
||||
let csp_header = format!(
|
||||
concat!(
|
||||
|
|
|
@ -422,10 +422,7 @@ pub async fn json_rest_event_delete_attr(
|
|||
kopid: KOpId,
|
||||
client_auth_info: ClientAuthInfo,
|
||||
) -> Result<Json<()>, WebError> {
|
||||
let values = match values {
|
||||
Some(val) => val,
|
||||
None => vec![],
|
||||
};
|
||||
let values = values.unwrap_or_default();
|
||||
|
||||
if values.is_empty() {
|
||||
state
|
||||
|
@ -1993,7 +1990,6 @@ pub async fn person_id_unix_post(
|
|||
security(("token_jwt" = [])),
|
||||
tag = "v1/service_account",
|
||||
)]
|
||||
///
|
||||
#[instrument(, level = "INFO", skip(id, state, kopid))]
|
||||
pub async fn service_account_id_unix_post(
|
||||
State(state): State<ServerState>,
|
||||
|
|
|
@ -38,7 +38,7 @@ impl IntoResponse for HtmxError {
|
|||
// }
|
||||
HtmxError::OperationError(_kopid, inner) => {
|
||||
let body = serde_json::to_string(&inner).unwrap_or(inner.to_string());
|
||||
let response = match &inner {
|
||||
match &inner {
|
||||
OperationError::NotAuthenticated | OperationError::SessionExpired => {
|
||||
Redirect::to("/ui").into_response()
|
||||
}
|
||||
|
@ -55,8 +55,7 @@ impl IntoResponse for HtmxError {
|
|||
(StatusCode::BAD_REQUEST, body).into_response()
|
||||
}
|
||||
_ => (StatusCode::INTERNAL_SERVER_ERROR, body).into_response(),
|
||||
};
|
||||
response
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -335,7 +335,7 @@ pub async fn view_login_totp_post(
|
|||
Form(login_totp_form): Form<LoginTotpForm>,
|
||||
) -> Response {
|
||||
// trim leading and trailing white space.
|
||||
let Ok(totp) = u32::from_str(&login_totp_form.totp.trim()) else {
|
||||
let Ok(totp) = u32::from_str(login_totp_form.totp.trim()) else {
|
||||
// If not an int, we need to re-render with an error
|
||||
return HtmlTemplate(LoginTotpView {
|
||||
totp: String::default(),
|
||||
|
@ -591,7 +591,8 @@ async fn view_login_step(
|
|||
HtmlTemplate(LoginBackupCodeView {}).into_response()
|
||||
}
|
||||
AuthAllowed::SecurityKey(chal) => {
|
||||
let chal_json = serde_json::to_string(&chal).unwrap();
|
||||
let chal_json = serde_json::to_string(&chal)
|
||||
.map_err(|_| OperationError::SerdeJsonError)?;
|
||||
HtmlTemplate(LoginWebauthnView {
|
||||
passkey: false,
|
||||
chal: chal_json,
|
||||
|
@ -599,7 +600,8 @@ async fn view_login_step(
|
|||
.into_response()
|
||||
}
|
||||
AuthAllowed::Passkey(chal) => {
|
||||
let chal_json = serde_json::to_string(&chal).unwrap();
|
||||
let chal_json = serde_json::to_string(&chal)
|
||||
.map_err(|_| OperationError::SerdeJsonError)?;
|
||||
HtmlTemplate(LoginWebauthnView {
|
||||
passkey: true,
|
||||
chal: chal_json,
|
||||
|
|
|
@ -137,7 +137,7 @@ async fn oauth2_auth_req(
|
|||
// We store the auth_req into the cookie.
|
||||
let kref = &state.jws_signer;
|
||||
|
||||
let token = match Jws::into_json(&auth_req)
|
||||
let token = Jws::into_json(&auth_req)
|
||||
.map_err(|err| {
|
||||
error!(?err, "Failed to serialise AuthorisationRequest");
|
||||
OperationError::InvalidSessionState
|
||||
|
@ -148,8 +148,9 @@ async fn oauth2_auth_req(
|
|||
OperationError::InvalidSessionState
|
||||
})
|
||||
})
|
||||
.map(|jwss| jwss.to_string())
|
||||
{
|
||||
.map(|jwss| jwss.to_string());
|
||||
|
||||
let token = match token {
|
||||
Ok(jws) => jws,
|
||||
Err(err_code) => {
|
||||
return HtmlTemplate(UnrecoverableErrorView {
|
||||
|
|
|
@ -242,12 +242,9 @@ async fn repl_run_consumer(
|
|||
idms: &IdmServer,
|
||||
consumer_conn_settings: &ConsumerConnSettings,
|
||||
) -> Option<SocketAddr> {
|
||||
let Some((socket_addr, mut supplier_conn)) =
|
||||
let (socket_addr, mut supplier_conn) =
|
||||
repl_consumer_connect_supplier(domain, sock_addrs, tls_connector, consumer_conn_settings)
|
||||
.await
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
.await?;
|
||||
|
||||
// Perform incremental.
|
||||
let consumer_ruv_range = {
|
||||
|
|
|
@ -582,7 +582,7 @@ pub trait BackendTransaction {
|
|||
Some(idx_key) => {
|
||||
match self
|
||||
.get_idlayer()
|
||||
.get_idl(attr, IndexType::SubString, &idx_key)?
|
||||
.get_idl(attr, IndexType::SubString, idx_key)?
|
||||
{
|
||||
Some(idl) => idl,
|
||||
None => return Ok((IdList::AllIds, FilterPlan::SubCorrupt(attr.clone()))),
|
||||
|
|
|
@ -1496,7 +1496,7 @@ impl AuthSession {
|
|||
|
||||
(
|
||||
Some(AuthSessionState::Success),
|
||||
Ok(AuthState::Success(token, self.issue)),
|
||||
Ok(AuthState::Success(Box::new(token), self.issue)),
|
||||
)
|
||||
}
|
||||
CredState::Continue(allowed) => {
|
||||
|
@ -1858,7 +1858,7 @@ mod tests {
|
|||
let jws_verifier = JwsDangerReleaseWithoutVerify::default();
|
||||
|
||||
jws_verifier
|
||||
.verify(&jwsc)
|
||||
.verify(&*jwsc)
|
||||
.unwrap()
|
||||
.from_json::<UserAuthToken>()
|
||||
.unwrap()
|
||||
|
|
|
@ -2603,7 +2603,7 @@ mod tests {
|
|||
let da = idms_delayed.try_recv().expect("invalid");
|
||||
assert!(matches!(da, DelayedAction::AuthSessionRecord(_)));
|
||||
|
||||
Some(token)
|
||||
Some(*token)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
|
@ -2669,7 +2669,7 @@ mod tests {
|
|||
// Process the auth session
|
||||
let da = idms_delayed.try_recv().expect("invalid");
|
||||
assert!(matches!(da, DelayedAction::AuthSessionRecord(_)));
|
||||
Some(token)
|
||||
Some(*token)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
|
@ -2737,7 +2737,7 @@ mod tests {
|
|||
// Process the auth session
|
||||
let da = idms_delayed.try_recv().expect("invalid");
|
||||
assert!(matches!(da, DelayedAction::AuthSessionRecord(_)));
|
||||
Some(token)
|
||||
Some(*token)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
|
@ -2812,7 +2812,7 @@ mod tests {
|
|||
let da = idms_delayed.try_recv().expect("invalid");
|
||||
assert!(matches!(da, DelayedAction::AuthSessionRecord(_)));
|
||||
|
||||
Some(token)
|
||||
Some(*token)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ pub enum AuthState {
|
|||
Choose(Vec<AuthMech>),
|
||||
Continue(Vec<AuthAllowed>),
|
||||
Denied(String),
|
||||
Success(JwsCompact, AuthIssueSession),
|
||||
Success(Box<JwsCompact>, AuthIssueSession),
|
||||
}
|
||||
|
||||
impl fmt::Debug for AuthState {
|
||||
|
|
|
@ -396,7 +396,7 @@ mod tests {
|
|||
let r = idms.delayed_action(ct, da).await;
|
||||
assert!(r.is_ok());
|
||||
|
||||
Some(token)
|
||||
Some(*token)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
|
@ -467,7 +467,7 @@ mod tests {
|
|||
let r = idms.delayed_action(ct, da).await;
|
||||
assert!(r.is_ok());
|
||||
|
||||
Some(token)
|
||||
Some(*token)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
|
@ -545,7 +545,7 @@ mod tests {
|
|||
// NOTE: Unlike initial auth we don't need to check the auth session in the queue
|
||||
// since we don't re-issue it.
|
||||
|
||||
Some(token)
|
||||
Some(*token)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
@ -615,7 +615,7 @@ mod tests {
|
|||
// Process the auth session
|
||||
let da = idms_delayed.try_recv().expect("invalid");
|
||||
assert!(matches!(da, DelayedAction::AuthSessionRecord(_)));
|
||||
Some(token)
|
||||
Some(*token)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
|
|
|
@ -2367,7 +2367,7 @@ mod tests {
|
|||
|
||||
idms_auth.commit().expect("Must not fail");
|
||||
|
||||
token
|
||||
*token
|
||||
}
|
||||
|
||||
#[idm_test]
|
||||
|
|
|
@ -110,7 +110,7 @@ impl<'a> GraphemeClusterIter<'a> {
|
|||
char_bounds
|
||||
};
|
||||
|
||||
let window_max = char_bounds.len().checked_sub(window).unwrap_or(0);
|
||||
let window_max = char_bounds.len().saturating_sub(window);
|
||||
let range = 0..window_max;
|
||||
|
||||
GraphemeClusterIter {
|
||||
|
@ -134,7 +134,7 @@ impl<'a> Iterator for GraphemeClusterIter<'a> {
|
|||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let clusters = self.char_bounds.len().checked_sub(1).unwrap_or(0);
|
||||
let clusters = self.char_bounds.len().saturating_sub(1);
|
||||
(clusters, Some(clusters))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -840,7 +840,7 @@ pub fn from_db_valueset_v2(dbvs: DbValueSetV2) -> Result<ValueSet, OperationErro
|
|||
DbValueSetV2::EmailAddress(primary, set) => ValueSetEmailAddress::from_dbvs2(primary, set),
|
||||
DbValueSetV2::Passkey(set) => ValueSetPasskey::from_dbvs2(set),
|
||||
DbValueSetV2::AttestedPasskey(set) => ValueSetAttestedPasskey::from_dbvs2(set),
|
||||
DbValueSetV2::Session(set) => ValueSetSession::from_dbvs2(set),
|
||||
DbValueSetV2::Session(set) => ValueSetSession::from_dbvs2(&set),
|
||||
DbValueSetV2::ApiToken(set) => ValueSetApiToken::from_dbvs2(set),
|
||||
DbValueSetV2::Oauth2Session(set) => ValueSetOauth2Session::from_dbvs2(set),
|
||||
DbValueSetV2::JwsKeyEs256(set) => ValueSetJwsKeyEs256::from_dbvs2(&set),
|
||||
|
|
|
@ -191,7 +191,7 @@ impl ValueSetSession {
|
|||
Ok(Box::new(ValueSetSession { map }))
|
||||
}
|
||||
|
||||
pub fn from_dbvs2(data: Vec<DbValueSession>) -> Result<ValueSet, OperationError> {
|
||||
pub fn from_dbvs2(data: &[DbValueSession]) -> Result<ValueSet, OperationError> {
|
||||
Self::from_dbv_iter(data.iter())
|
||||
}
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ pub async fn setup_async_test(mut config: Configuration) -> (KanidmClient, CoreH
|
|||
config.address = format!("127.0.0.1:{}", port);
|
||||
config.integration_test_config = Some(int_config);
|
||||
config.domain = "localhost".to_string();
|
||||
config.origin = addr.clone();
|
||||
config.origin.clone_from(&addr);
|
||||
|
||||
let core_handle = match create_server_core(config, false).await {
|
||||
Ok(val) => val,
|
||||
|
@ -320,7 +320,7 @@ pub async fn test_read_attrs(
|
|||
.await
|
||||
.unwrap()
|
||||
.is_some(),
|
||||
_ => e.attrs.get(attr.as_ref()).is_some(),
|
||||
_ => e.attrs.contains_key(attr.as_ref()),
|
||||
};
|
||||
trace!("is_ok: {}, is_readable: {}", is_ok, is_readable);
|
||||
assert!(is_ok == is_readable)
|
||||
|
|
|
@ -539,7 +539,7 @@ fn ldap_to_scim_entry(
|
|||
entry
|
||||
.get_ava_single(&sync_config.person_attr_gidnumber)
|
||||
.map(|gid| {
|
||||
u32::from_str(&gid).map_err(|_| {
|
||||
u32::from_str(gid).map_err(|_| {
|
||||
error!(
|
||||
"Invalid gidnumber - {} is not a u32 (person_attr_gidnumber)",
|
||||
sync_config.person_attr_gidnumber
|
||||
|
@ -664,7 +664,7 @@ fn ldap_to_scim_entry(
|
|||
let gidnumber = entry
|
||||
.get_ava_single(&sync_config.group_attr_gidnumber)
|
||||
.map(|gid| {
|
||||
u32::from_str(&gid).map_err(|_| {
|
||||
u32::from_str(gid).map_err(|_| {
|
||||
error!(
|
||||
"Invalid gidnumber - {} is not a u32 (group_attr_gidnumber)",
|
||||
sync_config.group_attr_gidnumber
|
||||
|
|
|
@ -104,7 +104,7 @@ fn create_home_directory(
|
|||
.map_err(|e| format!("{:?}", e))?;
|
||||
|
||||
let home_mount_prefix_path = home_mount_prefix_path
|
||||
.unwrap_or_else(|| &home_prefix_path)
|
||||
.unwrap_or(&home_prefix_path)
|
||||
.canonicalize()
|
||||
.map_err(|e| format!("{:?}", e))?;
|
||||
|
||||
|
|
Loading…
Reference in a new issue