Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -113,3 +113,7 @@ test = false
name = "functional"
path = "tests/functional/main.rs"
test = false

[lints.clippy]
unwrap_used = "deny"
expect_used = "deny"
7 changes: 6 additions & 1 deletion src/assignment/backends/sql/role.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand Down Expand Up @@ -78,7 +79,11 @@ impl TryFrom<db_role::Model> for Role {
builder.description(description.clone());
}
if let Some(extra) = &value.extra {
builder.extra(serde_json::from_str::<Value>(extra).unwrap());
builder.extra(
serde_json::from_str::<Value>(extra)
.inspect_err(|e| error!("failed to deserialize role extra: {e}"))
.unwrap_or_default(),
);
}

Ok(builder.build()?)
Expand Down
25 changes: 15 additions & 10 deletions src/bin/keystone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

//! Main Keystone executable.
//!
//! This is the entry point of the `keystone` binary.

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;
Expand Down Expand Up @@ -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")),
))
}
}
Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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();},
}
}
3 changes: 2 additions & 1 deletion src/bin/keystone_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 } => {
Expand Down
5 changes: 4 additions & 1 deletion src/catalog/backends/sql/endpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand Down Expand Up @@ -79,7 +80,9 @@ impl TryFrom<db_endpoint::Model> for Endpoint {
builder.region_id(val);
}
if let Some(extra) = &value.extra {
let extra = serde_json::from_str::<Value>(extra).unwrap();
let extra = serde_json::from_str::<Value>(extra)
.inspect_err(|e| error!("failed to deserialize endpoint extra: {e}"))
.unwrap_or_default();
builder.extra(extra);
}

Expand Down
5 changes: 4 additions & 1 deletion src/catalog/backends/sql/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand Down Expand Up @@ -70,7 +71,9 @@ impl TryFrom<db_service::Model> for Service {
}
builder.enabled(value.enabled);
if let Some(extra) = &value.extra {
let extra = serde_json::from_str::<Value>(extra).unwrap();
let extra = serde_json::from_str::<Value>(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);
}
Expand Down
5 changes: 3 additions & 2 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,9 @@ pub struct DatabaseSection {
impl DatabaseSection {
pub fn get_connection(&self) -> String {
if self.connection.contains("+") {
let re = Regex::new(r"(?<type>\w+)\+(\w+)://").unwrap();
return re.replace(&self.connection, "${type}://").to_string();
return Regex::new(r"(?<type>\w+)\+(\w+)://")
.map(|re| re.replace(&self.connection, "${type}://").to_string())
.unwrap_or(self.connection.clone());
}
self.connection.clone()
}
Expand Down
15 changes: 15 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
}
14 changes: 11 additions & 3 deletions src/identity/backends/sql/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -35,7 +36,11 @@ pub fn get_user_builder<O: IntoIterator<Item = user_option::Model>>(
// 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::<Value>(extra).unwrap());
user_builder.extra(
serde_json::from_str::<Value>(extra)
.inspect_err(|e| error!("failed to deserialize user extra: {e}"))
.unwrap_or_default(),
);
}

user_builder.options(UserOptions::from_iter(opts));
Expand All @@ -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);
Expand Down
8 changes: 4 additions & 4 deletions src/identity/backends/sql/local_user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,10 @@ pub async fn load_local_users_passwords<L: IntoIterator<Item = Option<i32>>>(

// 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
Expand Down
7 changes: 4 additions & 3 deletions src/keystone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,18 @@ 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
// may be changed in the future.
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(),
Expand Down
1 change: 0 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0

pub mod api;
pub mod assignment;
pub mod auth;
Expand Down
13 changes: 11 additions & 2 deletions src/resource/backends/sql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -153,7 +154,11 @@ impl TryFrom<db_project::Model> for Project {
}
project_builder.enabled(value.enabled.unwrap_or(false));
if let Some(extra) = &value.extra {
project_builder.extra(serde_json::from_str::<Value>(extra).unwrap());
project_builder.extra(
serde_json::from_str::<Value>(extra)
.inspect_err(|e| error!("failed to deserialize project extra properties: {e}"))
.unwrap_or_default(),
);
}

Ok(project_builder.build()?)
Expand All @@ -172,7 +177,11 @@ impl TryFrom<db_project::Model> for Domain {
}
domain_builder.enabled(value.enabled.unwrap_or(false));
if let Some(extra) = &value.extra {
domain_builder.extra(serde_json::from_str::<Value>(extra).unwrap());
domain_builder.extra(
serde_json::from_str::<Value>(extra)
.inspect_err(|e| error!("failed to deserialize domain extra: {e}"))
.unwrap_or_default(),
);
}

Ok(domain_builder.build()?)
Expand Down
7 changes: 3 additions & 4 deletions src/token/fernet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> = 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));
}
}

Expand Down
Loading