diff --git a/Cargo.toml b/Cargo.toml index a3db0e59..196b6494 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -113,3 +113,7 @@ test = false name = "functional" path = "tests/functional/main.rs" test = false + +[lints.clippy] +unwrap_used = "deny" +expect_used = "deny" diff --git a/src/assignment/backends/sql/role.rs b/src/assignment/backends/sql/role.rs index 7185c6f1..1e113a6f 100644 --- a/src/assignment/backends/sql/role.rs +++ b/src/assignment/backends/sql/role.rs @@ -16,6 +16,7 @@ use sea_orm::DatabaseConnection; use sea_orm::entity::*; use sea_orm::query::*; use serde_json::Value; +use tracing::error; use crate::assignment::backends::error::{AssignmentDatabaseError, db_err}; use crate::assignment::types::*; @@ -78,7 +79,11 @@ impl TryFrom for Role { builder.description(description.clone()); } if let Some(extra) = &value.extra { - builder.extra(serde_json::from_str::(extra).unwrap()); + builder.extra( + serde_json::from_str::(extra) + .inspect_err(|e| error!("failed to deserialize role extra: {e}")) + .unwrap_or_default(), + ); } Ok(builder.build()?) diff --git a/src/bin/keystone.rs b/src/bin/keystone.rs index 27a45cae..5be5f545 100644 --- a/src/bin/keystone.rs +++ b/src/bin/keystone.rs @@ -11,7 +11,6 @@ // limitations under the License. // // SPDX-License-Identifier: Apache-2.0 - //! Main Keystone executable. //! //! This is the entry point of the `keystone` binary. @@ -19,6 +18,7 @@ use axum::http::{self, HeaderName, Request, header}; use clap::{Parser, ValueEnum}; use color_eyre::eyre::{Report, Result}; +use eyre::WrapErr; use sea_orm::ConnectOptions; use sea_orm::Database; use std::io; @@ -91,7 +91,9 @@ impl MakeRequestId for OpenStackRequestId { let req_id = Uuid::new_v4().simple().to_string(); Some(RequestId::new( - http::HeaderValue::from_str(format!("req-{req_id}").as_str()).unwrap(), + http::HeaderValue::from_str(format!("req-{req_id}").as_str()) + // default to static value. This is not expected to ever happen. + .unwrap_or_else(|_| http::HeaderValue::from_static("req-unknown")), )) } } @@ -151,7 +153,7 @@ async fn main() -> Result<(), Report> { debug!("Establishing the database connection..."); let conn = Database::connect(opt) .await - .expect("Database connection failed"); + .wrap_err("Database connection failed")?; let plugin_manager = PluginManager::default(); @@ -252,26 +254,29 @@ async fn cleanup(cancel: CancellationToken, state: ServiceState) { } } +/// Install shutdown and interrupt signal handler async fn shutdown_signal(state: ServiceState) { let ctrl_c = async { signal::ctrl_c() .await - .expect("failed to install Ctrl+C handler"); + .inspect_err(|e| error!("failed to install Ctrl+C handler: {e}")) + .ok(); }; #[cfg(unix)] let terminate = async { - signal::unix::signal(signal::unix::SignalKind::terminate()) - .expect("failed to install signal handler") - .recv() - .await; + if let Ok(mut sig) = signal::unix::signal(signal::unix::SignalKind::terminate()) + .inspect_err(|e| error!("failed to install signal handler: {e}")) + { + sig.recv().await; + } }; #[cfg(not(unix))] let terminate = std::future::pending::<()>(); tokio::select! { - () = ctrl_c => {state.terminate().await.unwrap();}, - () = terminate => {state.terminate().await.unwrap();}, + () = ctrl_c => {state.terminate().await.ok();}, + () = terminate => {state.terminate().await.ok();}, } } diff --git a/src/bin/keystone_db.rs b/src/bin/keystone_db.rs index ed4b0ff1..d6e0833f 100644 --- a/src/bin/keystone_db.rs +++ b/src/bin/keystone_db.rs @@ -13,6 +13,7 @@ // SPDX-License-Identifier: Apache-2.0 use clap::{Parser, Subcommand}; use color_eyre::Report; +use eyre::WrapErr; use std::io; use std::path::PathBuf; use tracing::info; @@ -96,7 +97,7 @@ async fn main() -> Result<(), Report> { info!("Establishing the database connection..."); let conn = Database::connect(opt) .await - .expect("Database connection failed"); + .wrap_err("Database connection failed")?; match cli.command { Commands::Up { steps } => { diff --git a/src/catalog/backends/sql/endpoint.rs b/src/catalog/backends/sql/endpoint.rs index 761a97a8..09781808 100644 --- a/src/catalog/backends/sql/endpoint.rs +++ b/src/catalog/backends/sql/endpoint.rs @@ -16,6 +16,7 @@ use sea_orm::DatabaseConnection; use sea_orm::entity::*; use sea_orm::query::*; use serde_json::Value; +use tracing::error; use crate::catalog::backends::error::{CatalogDatabaseError, db_err}; use crate::catalog::types::*; @@ -79,7 +80,9 @@ impl TryFrom for Endpoint { builder.region_id(val); } if let Some(extra) = &value.extra { - let extra = serde_json::from_str::(extra).unwrap(); + let extra = serde_json::from_str::(extra) + .inspect_err(|e| error!("failed to deserialize endpoint extra: {e}")) + .unwrap_or_default(); builder.extra(extra); } diff --git a/src/catalog/backends/sql/service.rs b/src/catalog/backends/sql/service.rs index 07a6587a..2d4db58c 100644 --- a/src/catalog/backends/sql/service.rs +++ b/src/catalog/backends/sql/service.rs @@ -16,6 +16,7 @@ use sea_orm::DatabaseConnection; use sea_orm::entity::*; use sea_orm::query::*; use serde_json::Value; +use tracing::error; use crate::catalog::backends::error::{CatalogDatabaseError, db_err}; use crate::catalog::types::*; @@ -70,7 +71,9 @@ impl TryFrom for Service { } builder.enabled(value.enabled); if let Some(extra) = &value.extra { - let extra = serde_json::from_str::(extra).unwrap(); + let extra = serde_json::from_str::(extra) + .inspect_err(|e| error!("failed to deserialize service extra: {e}")) + .unwrap_or_default(); if let Some(name) = extra.get("name").and_then(|x| x.as_str()) { builder.name(name); } diff --git a/src/config.rs b/src/config.rs index 83bc75de..aba0b28f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -113,8 +113,9 @@ pub struct DatabaseSection { impl DatabaseSection { pub fn get_connection(&self) -> String { if self.connection.contains("+") { - let re = Regex::new(r"(?\w+)\+(\w+)://").unwrap(); - return re.replace(&self.connection, "${type}://").to_string(); + return Regex::new(r"(?\w+)\+(\w+)://") + .map(|re| re.replace(&self.connection, "${type}://").to_string()) + .unwrap_or(self.connection.clone()); } self.connection.clone() } diff --git a/src/error.rs b/src/error.rs index 71fbddc7..da9d0b0d 100644 --- a/src/error.rs +++ b/src/error.rs @@ -86,4 +86,19 @@ pub enum KeystoneError { #[from] source: serde_json::Error, }, + + /// Url parsing error + #[error(transparent)] + UrlParse { + #[from] + source: url::ParseError, + }, + + /// WebauthN error. + #[error("webauthn error: {}", source)] + Webauthn { + /// The source of the error. + #[from] + source: webauthn_rs::prelude::WebauthnError, + }, } diff --git a/src/identity/backends/sql/common.rs b/src/identity/backends/sql/common.rs index 4791110d..3ca07964 100644 --- a/src/identity/backends/sql/common.rs +++ b/src/identity/backends/sql/common.rs @@ -12,8 +12,9 @@ // // SPDX-License-Identifier: Apache-2.0 -use chrono::{DateTime, Days}; +use chrono::{DateTime, Days, Utc}; use serde_json::Value; +use tracing::error; use crate::config::Config; use crate::db::entity::federated_user; @@ -35,7 +36,11 @@ pub fn get_user_builder>( // TODO: default enabled logic user_builder.enabled(user.enabled.unwrap_or(false)); if let Some(extra) = &user.extra { - user_builder.extra(serde_json::from_str::(extra).unwrap()); + user_builder.extra( + serde_json::from_str::(extra) + .inspect_err(|e| error!("failed to deserialize user extra: {e}")) + .unwrap_or_default(), + ); } user_builder.options(UserOptions::from_iter(opts)); @@ -61,7 +66,10 @@ pub fn get_local_user_builder< (pass.into_iter().next(), user_builder.get_options()) && let Some(false) = options.ignore_password_expiry.or(Some(false)) && let Some(dt) = DateTime::from_timestamp_micros(current_password.created_at_int) - .expect("invalid timestamp") + .unwrap_or(DateTime::from_naive_utc_and_offset( + current_password.created_at, + Utc, + )) .checked_add_days(Days::new(password_expires_days)) { user_builder.password_expires_at(dt); diff --git a/src/identity/backends/sql/local_user.rs b/src/identity/backends/sql/local_user.rs index b04983aa..deb72927 100644 --- a/src/identity/backends/sql/local_user.rs +++ b/src/identity/backends/sql/local_user.rs @@ -89,10 +89,10 @@ pub async fn load_local_users_passwords>>( // Collect passwords into hashmap by the local_user_id passwords.into_iter().for_each(|item| { - let vec = hashmap - .get_mut(&item.local_user_id) - .expect("failed to find key on passwords hashmap"); - vec.push(item); + hashmap + .entry(item.local_user_id) + .and_modify(|e| e.push(item.clone())) + .or_insert_with(|| Vec::from([item])); }); // Prepare final result keeping the order of the requested local_users diff --git a/src/keystone.rs b/src/keystone.rs index 5c44cec7..5dae5e73 100644 --- a/src/keystone.rs +++ b/src/keystone.rs @@ -63,9 +63,10 @@ impl Service { // Effective domain name. let rp_id = "localhost"; // Url containing the effective domain name + // TODO: This must come from the configuration file. // MUST include the port number! - let rp_origin = Url::parse("http://localhost:8080").expect("Invalid URL"); - let builder = WebauthnBuilder::new(rp_id, &rp_origin).expect("Invalid configuration"); + let rp_origin = Url::parse("http://localhost:8080")?; + let builder = WebauthnBuilder::new(rp_id, &rp_origin)?; // Now, with the builder you can define other options. // Set a "nice" relying party name. Has no security properties and @@ -73,7 +74,7 @@ impl Service { let builder = builder.rp_name("Keystone"); // Consume the builder and create our webauthn instance. - let webauthn = builder.build().expect("Invalid configuration"); + let webauthn = builder.build()?; Ok(Self { config: cfg.clone(), diff --git a/src/lib.rs b/src/lib.rs index b1d2b499..142a79a6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,7 +11,6 @@ // limitations under the License. // // SPDX-License-Identifier: Apache-2.0 - pub mod api; pub mod assignment; pub mod auth; diff --git a/src/resource/backends/sql.rs b/src/resource/backends/sql.rs index 7e73d58e..e34cb3ee 100644 --- a/src/resource/backends/sql.rs +++ b/src/resource/backends/sql.rs @@ -17,6 +17,7 @@ use sea_orm::DatabaseConnection; use sea_orm::entity::*; use sea_orm::query::*; use serde_json::Value; +use tracing::error; use super::super::types::*; use crate::config::Config; @@ -153,7 +154,11 @@ impl TryFrom for Project { } project_builder.enabled(value.enabled.unwrap_or(false)); if let Some(extra) = &value.extra { - project_builder.extra(serde_json::from_str::(extra).unwrap()); + project_builder.extra( + serde_json::from_str::(extra) + .inspect_err(|e| error!("failed to deserialize project extra properties: {e}")) + .unwrap_or_default(), + ); } Ok(project_builder.build()?) @@ -172,7 +177,11 @@ impl TryFrom for Domain { } domain_builder.enabled(value.enabled.unwrap_or(false)); if let Some(extra) = &value.extra { - domain_builder.extra(serde_json::from_str::(extra).unwrap()); + domain_builder.extra( + serde_json::from_str::(extra) + .inspect_err(|e| error!("failed to deserialize domain extra: {e}")) + .unwrap_or_default(), + ); } Ok(domain_builder.build()?) diff --git a/src/token/fernet.rs b/src/token/fernet.rs index 96f164a7..4e96a1b4 100644 --- a/src/token/fernet.rs +++ b/src/token/fernet.rs @@ -136,10 +136,9 @@ impl FernetTokenProvider { self.auth_methods_code_cache.clear(); for auth_pairs in all_combinations(self.auth_map.values().cloned()) { let pair: HashSet = HashSet::from_iter(auth_pairs.into_iter()); - let res = self - .encode_auth_methods(pair.clone()) - .expect("Can encode auth_methods combination"); - self.auth_methods_code_cache.insert(res, pair); + self.encode_auth_methods(pair.clone()) + .ok() + .map(|val| self.auth_methods_code_cache.insert(val, pair)); } }