From ca75b3851818fae4387092c44399c64563581abd Mon Sep 17 00:00:00 2001 From: Artem Goncharov Date: Mon, 16 Mar 2026 17:09:17 +0100 Subject: [PATCH] refactor: Split out token-fernet driver Make the FernetTokenProvider be a standalone crate. --- Cargo.lock | 39 +++-- Cargo.toml | 1 + Dockerfile | 7 +- crates/core/Cargo.toml | 23 +-- crates/core/src/tests.rs | 2 +- crates/core/src/token/backend.rs | 3 - crates/core/src/token/error.rs | 100 +----------- crates/core/src/token/service.rs | 6 +- crates/core/src/token/types.rs | 2 +- crates/keystone/Cargo.toml | 1 + crates/keystone/src/plugin_manager.rs | 2 +- .../src/token/token_restriction/sql.rs | 4 +- crates/token-fernet/Cargo.toml | 43 ++++++ .../benches/fernet_token.rs | 4 +- .../src}/application_credential.rs | 16 +- .../src}/domain_scoped.rs | 16 +- crates/token-fernet/src/error.rs | 128 +++++++++++++++ .../src}/federation_domain_scoped.rs | 16 +- .../src}/federation_project_scoped.rs | 16 +- .../src}/federation_unscoped.rs | 16 +- .../fernet.rs => token-fernet/src/lib.rs} | 146 +++++++++--------- .../src}/project_scoped.rs | 16 +- .../fernet => token-fernet/src}/restricted.rs | 16 +- .../src}/system_scoped.rs | 16 +- .../fernet => token-fernet/src}/trust.rs | 16 +- .../fernet => token-fernet/src}/unscoped.rs | 16 +- .../fernet => token-fernet/src}/utils.rs | 100 ++++++------ tests/integration/Cargo.toml | 3 +- tests/integration/src/assignment/grant.rs | 2 +- tests/integration/src/common.rs | 2 +- tests/integration/src/token/validate.rs | 2 +- tools/Dockerfile.functest | 6 +- tools/setup-db.sh | 2 +- 33 files changed, 424 insertions(+), 364 deletions(-) create mode 100644 crates/token-fernet/Cargo.toml rename crates/{core => token-fernet}/benches/fernet_token.rs (93%) rename crates/{core/src/token/backend/fernet => token-fernet/src}/application_credential.rs (89%) rename crates/{core/src/token/backend/fernet => token-fernet/src}/domain_scoped.rs (88%) create mode 100644 crates/token-fernet/src/error.rs rename crates/{core/src/token/backend/fernet => token-fernet/src}/federation_domain_scoped.rs (90%) rename crates/{core/src/token/backend/fernet => token-fernet/src}/federation_project_scoped.rs (90%) rename crates/{core/src/token/backend/fernet => token-fernet/src}/federation_unscoped.rs (89%) rename crates/{core/src/token/backend/fernet.rs => token-fernet/src/lib.rs} (87%) rename crates/{core/src/token/backend/fernet => token-fernet/src}/project_scoped.rs (88%) rename crates/{core/src/token/backend/fernet => token-fernet/src}/restricted.rs (90%) rename crates/{core/src/token/backend/fernet => token-fernet/src}/system_scoped.rs (88%) rename crates/{core/src/token/backend/fernet => token-fernet/src}/trust.rs (89%) rename crates/{core/src/token/backend/fernet => token-fernet/src}/unscoped.rs (87%) rename crates/{core/src/token/backend/fernet => token-fernet/src}/utils.rs (83%) diff --git a/Cargo.lock b/Cargo.lock index 98fe09c1..12edef6d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3304,6 +3304,7 @@ dependencies = [ "openstack-keystone-api-types", "openstack-keystone-core", "openstack-keystone-distributed-storage", + "openstack-keystone-token-fernet", "reqwest 0.13.2", "rstest", "sea-orm", @@ -3357,30 +3358,19 @@ dependencies = [ "async-trait", "axum", "base64 0.22.1", - "base64urlsafedata", "bcrypt", - "byteorder", - "bytes", "chrono", "config", - "criterion", "derive_builder", "eyre", - "fernet", - "futures-util", "httpmock", - "itertools 0.14.0", "jsonwebtoken", "mockall", - "nix", "openstack-keystone-api-types", "rand 0.10.0", "regex", "reqwest 0.13.2", - "rmp", - "rstest", "schemars 1.2.1", - "scopeguard", "sea-orm", "secrecy", "serde", @@ -3421,6 +3411,32 @@ dependencies = [ "tracing-test", ] +[[package]] +name = "openstack-keystone-token-fernet" +version = "0.1.1" +dependencies = [ + "base64 0.22.1", + "base64urlsafedata", + "byteorder", + "bytes", + "chrono", + "config", + "criterion", + "fernet", + "itertools 0.14.0", + "nix", + "openstack-keystone-core", + "rmp", + "scopeguard", + "secrecy", + "tempfile", + "thiserror 2.0.18", + "tokio", + "tracing", + "uuid", + "validator", +] + [[package]] name = "openstack_sdk_core" version = "0.22.5" @@ -5769,6 +5785,7 @@ dependencies = [ "itertools 0.14.0", "openstack-keystone", "openstack-keystone-core", + "openstack-keystone-token-fernet", "sea-orm", "secrecy", "serde", diff --git a/Cargo.toml b/Cargo.toml index 803ee57d..9cad935e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,7 @@ members = [ "crates/api-types", "crates/core", "crates/keystone", + "crates/token-fernet", "crates/storage", "tests/api", "tests/integration", diff --git a/Dockerfile b/Dockerfile index bc6c23dd..6f4570c2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,6 +19,7 @@ COPY crates/api-types/Cargo.toml /usr/src/keystone/crates/api-types/ COPY crates/core/Cargo.toml /usr/src/keystone/crates/core/ COPY crates/keystone/Cargo.toml /usr/src/keystone/crates/keystone/ COPY crates/storage/Cargo.toml /usr/src/keystone/crates/storage/ +COPY crates/token-fernet/Cargo.toml /usr/src/keystone/crates/token-fernet/ COPY tests/federation/Cargo.toml /usr/src/keystone/tests/federation/ COPY tests/integration/Cargo.toml /usr/src/keystone/tests/integration/ COPY tests/api/Cargo.toml /usr/src/keystone/tests/api/ @@ -30,8 +31,9 @@ RUN mkdir -p keystone/crates/keystone/src/bin && touch keystone/crates/keystone/ cp keystone/src/main.rs keystone/tests/loadtest/src/main.rs &&\ mkdir -p keystone/crates/api-types/src && touch keystone/crates/api-types/src/lib.rs &&\ mkdir -p keystone/crates/core/src && touch keystone/crates/core/src/lib.rs &&\ - mkdir -p keystone/crates/core/benches && touch keystone/crates/core/benches/fernet_token.rs &&\ - mkdir -p keystone/crates/storage/src && touch keystone/crates/storage/src/lib.rs + mkdir -p keystone/crates/storage/src && touch keystone/crates/storage/src/lib.rs &&\ + mkdir -p keystone/crates/token-fernet/src && touch keystone/crates/token-fernet/src/lib.rs &&\ + mkdir -p keystone/crates/token-fernet/benches && touch keystone/crates/token-fernet/benches/fernet_token.rs # Set the working directory WORKDIR /usr/src/keystone @@ -45,6 +47,7 @@ COPY crates/keystone/ /usr/src/keystone/crates/keystone COPY crates/core/ /usr/src/keystone/crates/core COPY crates/api-types/ /usr/src/keystone/crates/api-types COPY crates/storage/ /usr/src/keystone/crates/storage +COPY crates/token-fernet/ /usr/src/keystone/crates/token-fernet ## Touch main.rs to prevent cached release build RUN touch crates/keystone/src/lib.rs && touch crates/keystone/src/bin/keystone.rs diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index aa5e9298..5bfaf2e2 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -7,37 +7,23 @@ edition.workspace = true license.workspace = true homepage.workspace = true repository.workspace = true -autobenches = false -autobins = false - -[[bench]] -name = "fernet_token" -harness = false [dependencies] async-trait.workspace = true -axum = { workspace = true } +axum.workspace = true base64.workspace = true bcrypt = { workspace = true, features = ["alloc"] } -byteorder.workspace = true -bytes.workspace = true chrono.workspace = true config = { workspace = true, features = ["async", "ini"] } derive_builder.workspace = true eyre.workspace = true -fernet = { workspace = true, features = ["rustcrypto"] } -futures-util.workspace = true -itertools.workspace = true jsonwebtoken = { version = "10.3", features = ["rust_crypto"] } openstack-keystone-api-types = { version = "0.1", path = "../api-types/"} mockall = { workspace = true, optional = true } -nix = { workspace = true, features = ["fs", "user"] } rand.workspace = true regex.workspace = true reqwest = { workspace = true, features = ["json", "http2", "gzip", "deflate"] } -rmp.workspace = true schemars.workspace = true -scopeguard.workspace = true sea-orm.workspace = true secrecy = { workspace = true, features = ["serde"] } serde.workspace = true @@ -45,7 +31,7 @@ serde_json.workspace = true serde_urlencoded.workspace = true tempfile.workspace = true thiserror.workspace = true -tokio = { workspace = true , features = ["fs"]} +tokio = { workspace = true, features = ["fs"] } tracing.workspace = true url = { workspace = true, features = ["serde"] } url-macro.workspace = true @@ -53,13 +39,8 @@ uuid = { workspace = true, features = ["v4"] } validator = { workspace = true, features = ["derive"] } [dev-dependencies] -base64urlsafedata.workspace = true -criterion = { workspace = true, features = ["async_tokio"] } httpmock = { version = "0.8", features = ["http2"] } mockall.workspace = true -rstest.workspace = true -sea-orm = { workspace = true, features = ["mock" ]} -tempfile.workspace = true tracing-test = { workspace = true, features = ["no-env-filter"] } url.workspace = true diff --git a/crates/core/src/tests.rs b/crates/core/src/tests.rs index 9aa6e94d..6cd07e98 100644 --- a/crates/core/src/tests.rs +++ b/crates/core/src/tests.rs @@ -20,7 +20,7 @@ use crate::keystone::{Service, ServiceState}; use crate::policy::MockPolicy; use crate::provider::{Provider, ProviderBuilder}; -pub(crate) mod token; +//pub(crate) mod token; pub fn get_mocked_state( config: Option, diff --git a/crates/core/src/token/backend.rs b/crates/core/src/token/backend.rs index 685295a0..cc8020df 100644 --- a/crates/core/src/token/backend.rs +++ b/crates/core/src/token/backend.rs @@ -18,9 +18,6 @@ use crate::token::{TokenProviderError, types::*}; use crate::keystone::ServiceState; -pub mod fernet; -pub use fernet::*; - /// Token Provider backend interface. #[cfg_attr(test, mockall::automock)] pub trait TokenBackend: Send + Sync { diff --git a/crates/core/src/token/error.rs b/crates/core/src/token/error.rs index c01a4b44..61972cce 100644 --- a/crates/core/src/token/error.rs +++ b/crates/core/src/token/error.rs @@ -13,14 +13,13 @@ // SPDX-License-Identifier: Apache-2.0 //! Token provider errors. -use std::num::TryFromIntError; - use thiserror::Error; use crate::error::BuilderError; /// Token provider error. #[derive(Error, Debug)] +#[non_exhaustive] pub enum TokenProviderError { /// Actor has no roles on the target scope. #[error("actor has no roles on scope")] @@ -54,32 +53,24 @@ pub enum TokenProviderError { source: crate::assignment::error::AssignmentProviderError, }, - /// AuditID must be urlsafe base64 encoded value. - #[error("audit_id must be urlsafe base64 encoded value")] - AuditIdWrongFormat, - /// Authentication error. #[error(transparent)] Authentication(#[from] crate::auth::AuthenticationError), - /// Base64 Decode error. - #[error("b64 decryption error")] - Base64Decode(#[from] base64::DecodeError), - /// Conflict. #[error("{message}")] Conflict { message: String, context: String }, - ///// Database error. - //#[error(transparent)] - //Database(#[from] DatabaseError), /// The domain is disabled. #[error("domain is disabled")] DomainDisabled(String), /// Driver error. - #[error("backend driver error: {0}")] - Driver(String), + #[error("backend driver error: {source}")] + Driver { + #[source] + source: Box, + }, /// Expired token. #[error("token expired")] @@ -93,64 +84,10 @@ pub enum TokenProviderError { #[error("federated payload must contain idp_id and protocol_id")] FederatedPayloadMissingData, - /// Fernet Decryption. - #[error("fernet decryption error")] - FernetDecryption(#[from] fernet::DecryptionError), - - /// Missing fernet keys. - #[error("no usable fernet keys has been found")] - FernetKeysMissing, - - /// Fernet key read error. - #[error("fernet key read error: {}", source)] - FernetKeyRead { - /// The source of the error. - source: std::io::Error, - /// Key file name. - path: std::path::PathBuf, - }, - /// Identity provider error. #[error(transparent)] IdentityProvider(#[from] crate::identity::error::IdentityProviderError), - /// Invalid token data. - #[error("invalid token error")] - InvalidToken, - - /// Unsupported token version. - #[error("token version {0} is not supported")] - InvalidTokenType(u8), - - /// Unsupported token uuid. - #[error("token uuid is not supported")] - InvalidTokenUuid, - - /// Unsupported token uuid coding. - #[error("token uuid coding {0:?} is not supported")] - InvalidTokenUuidMarker(rmp::Marker), - - /// IO error. - #[error("io error: {}", source)] - Io { - /// The source of the error. - #[from] - source: std::io::Error, - }, - - /// Nix errno. - #[error("unix error {source} while {context}")] - NixErrno { - /// Context. - context: String, - /// The source of the error. - source: nix::errno::Errno, - }, - - /// tempfile persisting error. - #[error(transparent)] - Persist(#[from] tempfile::PersistError), - /// The project is disabled. #[error("project disabled")] ProjectDisabled(String), @@ -167,14 +104,6 @@ pub enum TokenProviderError { #[error(transparent)] RevokeProvider(#[from] crate::revoke::error::RevokeProviderError), - /// MSGPack Encryption. - #[error("rmp value encoding error")] - RmpEncode(String), - - /// MSGPack Decryption. - #[error("rmp value error")] - RmpValueRead(#[from] rmp::decode::ValueReadError), - /// Role provider error. #[error(transparent)] RoleProvider { @@ -195,15 +124,6 @@ pub enum TokenProviderError { #[error("subject information missing")] SubjectMissing, - /// Fernet payload timestamp overflow error. - #[error("fernet payload timestamp overflow ({value}): {}", source)] - TokenTimestampOverflow { - /// Token timestamp. - value: u64, - /// The source of the error. - source: std::num::TryFromIntError, - }, - /// Token restriction not found error. #[error("token restriction {0} not found")] TokenRestrictionNotFound(String), @@ -220,14 +140,6 @@ pub enum TokenProviderError { #[error("trustee domain disabled")] TrustorDomainDisabled, - /// Integer conversion error. - #[error("int parse")] - TryFromIntError(#[from] TryFromIntError), - - /// Unsupported authentication methods in token payload. - #[error("unsupported authentication methods {0} in token payload")] - UnsupportedAuthMethods(String), - /// Unsupported token restriction driver. #[error("driver `{0}` is not supported for the token provider")] UnsupportedDriver(String), diff --git a/crates/core/src/token/service.rs b/crates/core/src/token/service.rs index 0f832954..78e5013c 100644 --- a/crates/core/src/token/service.rs +++ b/crates/core/src/token/service.rs @@ -66,14 +66,14 @@ impl TokenService { plugin_manager: &P, ) -> Result { let backend_driver = plugin_manager - .get_token_backend(&config.token.provider.to_string())? + .get_token_backend(config.token.provider.to_string())? .clone(); let tr_backend_driver = plugin_manager .get_token_restriction_backend(&config.token_restriction.driver)? .clone(); Ok(Self { config: config.clone(), - backend_driver: backend_driver, + backend_driver, tr_backend_driver, }) } @@ -1045,7 +1045,7 @@ mod tests { fn get_provider(config: &Config, token_mock: Option) -> TokenService { TokenService { config: config.clone(), - backend_driver: Arc::new(token_mock.unwrap_or(MockTokenBackend::default())), + backend_driver: Arc::new(token_mock.unwrap_or_default()), tr_backend_driver: Arc::new(MockTokenRestrictionBackend::default()), } } diff --git a/crates/core/src/token/types.rs b/crates/core/src/token/types.rs index 87913094..e60aa53b 100644 --- a/crates/core/src/token/types.rs +++ b/crates/core/src/token/types.rs @@ -114,7 +114,7 @@ impl Token { /// /// An internal method (available only within the module) to set the /// `issued_at` into the token payload. - pub(super) fn set_issued_at(&mut self, issued_at: DateTime) -> &mut Self { + pub fn set_issued_at(&mut self, issued_at: DateTime) -> &mut Self { match self { Self::ApplicationCredential(x) => x.issued_at = issued_at, Self::DomainScope(x) => x.issued_at = issued_at, diff --git a/crates/keystone/Cargo.toml b/crates/keystone/Cargo.toml index 469d478c..958d0d1c 100644 --- a/crates/keystone/Cargo.toml +++ b/crates/keystone/Cargo.toml @@ -32,6 +32,7 @@ eyre.workspace = true openstack-keystone-api-types = { version = "0.1", path = "../api-types/"} openstack-keystone-core = { version = "0.1", path = "../core" } openstack-keystone-distributed-storage = { version = "0.1", path = "../storage/"} +openstack-keystone-token-fernet = { version = "0.1", path = "../token-fernet/" } openidconnect.workspace = true reqwest = { workspace = true, features = ["json", "http2", "gzip", "deflate"] } sea-orm = { workspace = true, features = ["debug-print", "sqlx-mysql", "sqlx-postgres", "runtime-tokio", "runtime-tokio-native-tls"] } diff --git a/crates/keystone/src/plugin_manager.rs b/crates/keystone/src/plugin_manager.rs index e16b0a50..6d8369e6 100644 --- a/crates/keystone/src/plugin_manager.rs +++ b/crates/keystone/src/plugin_manager.rs @@ -433,7 +433,7 @@ impl PluginManager { slf.register_sql_drivers(); slf.register_token_backend( "fernet", - Arc::new(crate::token::backend::FernetTokenProvider::new( + Arc::new(openstack_keystone_token_fernet::FernetTokenProvider::new( config.clone(), )), ); diff --git a/crates/keystone/src/token/token_restriction/sql.rs b/crates/keystone/src/token/token_restriction/sql.rs index e865f059..65df023f 100644 --- a/crates/keystone/src/token/token_restriction/sql.rs +++ b/crates/keystone/src/token/token_restriction/sql.rs @@ -167,7 +167,9 @@ impl From for TokenProviderError { message: cfl.to_string(), context: String::new(), }, - other => Self::Driver(other.to_string()), + other => Self::Driver { + source: Box::new(other), + }, } } } diff --git a/crates/token-fernet/Cargo.toml b/crates/token-fernet/Cargo.toml new file mode 100644 index 00000000..c9fc3705 --- /dev/null +++ b/crates/token-fernet/Cargo.toml @@ -0,0 +1,43 @@ +[package] +name = "openstack-keystone-token-fernet" +description = "OpenStack Keystone Fernet token driver" +version = "0.1.1" +rust-version.workspace = true +edition.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true + +[[bench]] +name = "fernet_token" +harness = false + +[dependencies] +base64.workspace = true +byteorder.workspace = true +bytes.workspace = true +chrono.workspace = true +fernet = { workspace = true, features = ["rustcrypto"] } +itertools.workspace = true +openstack-keystone-core = { version = "0.1", path = "../core/"} +nix = { workspace = true, features = ["fs", "user"] } +rmp.workspace = true +scopeguard.workspace = true +secrecy = { workspace = true } +tokio = { workspace = true, features = ["fs"] } +tempfile.workspace = true +thiserror.workspace = true +tracing.workspace = true +uuid = { workspace = true, features = ["v4"] } +validator = { workspace = true, features = ["derive"] } + +[dev-dependencies] +base64urlsafedata.workspace = true +config.workspace = true +criterion = { workspace = true, features = ["async_tokio"] } +tempfile.workspace = true +tokio = { workspace = true, features = ["macros"]} + +[features] +default = [] +bench_internals = [] diff --git a/crates/core/benches/fernet_token.rs b/crates/token-fernet/benches/fernet_token.rs similarity index 93% rename from crates/core/benches/fernet_token.rs rename to crates/token-fernet/benches/fernet_token.rs index e081d742..c6563102 100644 --- a/crates/core/benches/fernet_token.rs +++ b/crates/token-fernet/benches/fernet_token.rs @@ -7,8 +7,8 @@ use std::io::Write; use tempfile::tempdir; use openstack_keystone_core::config::Config; -use openstack_keystone_core::token::backend::fernet::FernetTokenProvider; -use openstack_keystone_core::token::backend::fernet::bench_get_fernet_timestamp; +use openstack_keystone_token_fernet::FernetTokenProvider; +use openstack_keystone_token_fernet::bench_get_fernet_timestamp; //use openstack_keystone::token::types::TokenBackend; fn decode(backend: &FernetTokenProvider, token: &str) { diff --git a/crates/core/src/token/backend/fernet/application_credential.rs b/crates/token-fernet/src/application_credential.rs similarity index 89% rename from crates/core/src/token/backend/fernet/application_credential.rs rename to crates/token-fernet/src/application_credential.rs index 333cc3fb..240b5976 100644 --- a/crates/core/src/token/backend/fernet/application_credential.rs +++ b/crates/token-fernet/src/application_credential.rs @@ -15,11 +15,9 @@ use rmp::{decode::read_pfix, encode::write_pfix}; use std::io::Write; -use crate::token::{ - backend::fernet::{FernetTokenProvider, MsgPackToken, utils}, - error::TokenProviderError, - types::ApplicationCredentialPayload, -}; +use openstack_keystone_core::token::types::ApplicationCredentialPayload; + +use crate::{FernetDriverError, FernetTokenProvider, MsgPackToken, utils}; impl MsgPackToken for ApplicationCredentialPayload { type Token = Self; @@ -28,13 +26,13 @@ impl MsgPackToken for ApplicationCredentialPayload { &self, wd: &mut W, fernet_provider: &FernetTokenProvider, - ) -> Result<(), TokenProviderError> { + ) -> Result<(), FernetDriverError> { utils::write_uuid(wd, &self.user_id)?; write_pfix( wd, fernet_provider.encode_auth_methods(self.methods.clone())?, ) - .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; + .map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; utils::write_uuid(wd, &self.project_id)?; utils::write_time(wd, self.expires_at)?; utils::write_audit_ids(wd, self.audit_ids.clone())?; @@ -46,7 +44,7 @@ impl MsgPackToken for ApplicationCredentialPayload { fn disassemble( rd: &mut &[u8], fernet_provider: &FernetTokenProvider, - ) -> Result { + ) -> Result { // Order of reading is important let user_id = utils::read_uuid(rd)?; let methods: Vec = fernet_provider @@ -76,7 +74,7 @@ mod tests { use uuid::Uuid; use super::*; - use crate::token::tests::setup_config; + use crate::tests::setup_config; #[test] fn test_roundtrip() { diff --git a/crates/core/src/token/backend/fernet/domain_scoped.rs b/crates/token-fernet/src/domain_scoped.rs similarity index 88% rename from crates/core/src/token/backend/fernet/domain_scoped.rs rename to crates/token-fernet/src/domain_scoped.rs index a5f44c36..bae91c00 100644 --- a/crates/core/src/token/backend/fernet/domain_scoped.rs +++ b/crates/token-fernet/src/domain_scoped.rs @@ -15,11 +15,9 @@ use rmp::{decode::read_pfix, encode::write_pfix}; use std::io::Write; -use crate::token::{ - backend::fernet::{FernetTokenProvider, MsgPackToken, utils}, - error::TokenProviderError, - types::DomainScopePayload, -}; +use openstack_keystone_core::token::types::DomainScopePayload; + +use crate::{FernetDriverError, FernetTokenProvider, MsgPackToken, utils}; impl MsgPackToken for DomainScopePayload { type Token = Self; @@ -28,13 +26,13 @@ impl MsgPackToken for DomainScopePayload { &self, wd: &mut W, fernet_provider: &FernetTokenProvider, - ) -> Result<(), TokenProviderError> { + ) -> Result<(), FernetDriverError> { utils::write_uuid(wd, &self.user_id)?; write_pfix( wd, fernet_provider.encode_auth_methods(self.methods.clone())?, ) - .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; + .map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; utils::write_uuid(wd, &self.domain_id)?; utils::write_time(wd, self.expires_at)?; utils::write_audit_ids(wd, self.audit_ids.clone())?; @@ -45,7 +43,7 @@ impl MsgPackToken for DomainScopePayload { fn disassemble( rd: &mut &[u8], fernet_provider: &FernetTokenProvider, - ) -> Result { + ) -> Result { // Order of reading is important let user_id = utils::read_uuid(rd)?; let methods: Vec = fernet_provider @@ -72,7 +70,7 @@ mod tests { use uuid::Uuid; use super::*; - use crate::token::tests::setup_config; + use crate::tests::setup_config; #[test] fn test_roundtrip() { diff --git a/crates/token-fernet/src/error.rs b/crates/token-fernet/src/error.rs new file mode 100644 index 00000000..4afece24 --- /dev/null +++ b/crates/token-fernet/src/error.rs @@ -0,0 +1,128 @@ +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 +//! Token provider errors. + +use std::num::TryFromIntError; + +use thiserror::Error; + +use openstack_keystone_core::token::TokenProviderError; + +/// Token provider error. +#[derive(Error, Debug)] +#[non_exhaustive] +pub enum FernetDriverError { + /// AuditID must be urlsafe base64 encoded value. + #[error("audit_id must be urlsafe base64 encoded value")] + AuditIdWrongFormat, + + /// Base64 Decode error. + #[error("b64 decryption error")] + Base64Decode(#[from] base64::DecodeError), + + /// Fernet Decryption. + #[error("fernet decryption error")] + FernetDecryption(#[from] fernet::DecryptionError), + + /// Missing fernet keys. + #[error("no usable fernet keys has been found")] + FernetKeysMissing, + + /// Fernet key read error. + #[error("fernet key read error: {}", source)] + FernetKeyRead { + /// The source of the error. + source: std::io::Error, + /// Key file name. + path: std::path::PathBuf, + }, + + /// Invalid token data. + #[error("invalid token error")] + InvalidToken, + + /// Unsupported token version. + #[error("token version {0} is not supported")] + InvalidTokenType(u8), + + /// Unsupported token uuid. + #[error("token uuid is not supported")] + InvalidTokenUuid, + + /// Unsupported token uuid coding. + #[error("token uuid coding {0:?} is not supported")] + InvalidTokenUuidMarker(rmp::Marker), + + /// IO error. + #[error("io error: {}", source)] + Io { + /// The source of the error. + #[from] + source: std::io::Error, + }, + + /// Nix errno. + #[error("unix error {source} while {context}")] + NixErrno { + /// Context. + context: String, + /// The source of the error. + source: nix::errno::Errno, + }, + + /// tempfile persisting error. + #[error(transparent)] + Persist(#[from] tempfile::PersistError), + + /// MSGPack Encryption. + #[error("rmp value encoding error")] + RmpEncode(String), + + /// MSGPack Decryption. + #[error("rmp value error")] + RmpValueRead(#[from] rmp::decode::ValueReadError), + + /// Fernet payload timestamp overflow error. + #[error("fernet payload timestamp overflow ({value}): {}", source)] + TokenTimestampOverflow { + /// Token timestamp. + value: u64, + /// The source of the error. + source: std::num::TryFromIntError, + }, + + /// Integer conversion error. + #[error("int parse")] + TryFromIntError(#[from] TryFromIntError), + + /// Unsupported authentication methods in token payload. + #[error("unsupported authentication methods {0} in token payload")] + UnsupportedAuthMethods(String), + + /// UUID decryption error. + #[error("uuid decryption error")] + Uuid(#[from] uuid::Error), + + /// Validation error. + #[error("Token validation error: {0}")] + Validation(#[from] validator::ValidationErrors), +} + +impl From for TokenProviderError { + fn from(value: FernetDriverError) -> Self { + Self::Driver { + source: Box::new(value), + } + } +} diff --git a/crates/core/src/token/backend/fernet/federation_domain_scoped.rs b/crates/token-fernet/src/federation_domain_scoped.rs similarity index 90% rename from crates/core/src/token/backend/fernet/federation_domain_scoped.rs rename to crates/token-fernet/src/federation_domain_scoped.rs index 919e3202..dee193d3 100644 --- a/crates/core/src/token/backend/fernet/federation_domain_scoped.rs +++ b/crates/token-fernet/src/federation_domain_scoped.rs @@ -15,11 +15,9 @@ use rmp::{decode::read_pfix, encode::write_pfix}; use std::io::Write; -use crate::token::{ - backend::fernet::{FernetTokenProvider, MsgPackToken, utils}, - error::TokenProviderError, - types::FederationDomainScopePayload, -}; +use openstack_keystone_core::token::types::FederationDomainScopePayload; + +use crate::{FernetDriverError, FernetTokenProvider, MsgPackToken, utils}; impl MsgPackToken for FederationDomainScopePayload { type Token = Self; @@ -28,13 +26,13 @@ impl MsgPackToken for FederationDomainScopePayload { &self, wd: &mut W, fernet_provider: &FernetTokenProvider, - ) -> Result<(), TokenProviderError> { + ) -> Result<(), FernetDriverError> { utils::write_uuid(wd, &self.user_id)?; write_pfix( wd, fernet_provider.encode_auth_methods(self.methods.clone())?, ) - .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; + .map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; utils::write_uuid(wd, &self.domain_id)?; utils::write_list_of_uuids(wd, self.group_ids.iter())?; utils::write_uuid(wd, &self.idp_id)?; @@ -48,7 +46,7 @@ impl MsgPackToken for FederationDomainScopePayload { fn disassemble( rd: &mut &[u8], fernet_provider: &FernetTokenProvider, - ) -> Result { + ) -> Result { // Order of reading is important let user_id = utils::read_uuid(rd)?; let methods: Vec = fernet_provider @@ -81,7 +79,7 @@ mod tests { use uuid::Uuid; use super::*; - use crate::token::tests::setup_config; + use crate::tests::setup_config; #[test] fn test_roundtrip() { diff --git a/crates/core/src/token/backend/fernet/federation_project_scoped.rs b/crates/token-fernet/src/federation_project_scoped.rs similarity index 90% rename from crates/core/src/token/backend/fernet/federation_project_scoped.rs rename to crates/token-fernet/src/federation_project_scoped.rs index 78bbb40e..cb55be7e 100644 --- a/crates/core/src/token/backend/fernet/federation_project_scoped.rs +++ b/crates/token-fernet/src/federation_project_scoped.rs @@ -15,11 +15,9 @@ use rmp::{decode::read_pfix, encode::write_pfix}; use std::io::Write; -use crate::token::{ - backend::fernet::{FernetTokenProvider, MsgPackToken, utils}, - error::TokenProviderError, - types::FederationProjectScopePayload, -}; +use openstack_keystone_core::token::types::FederationProjectScopePayload; + +use crate::{FernetDriverError, FernetTokenProvider, MsgPackToken, utils}; impl MsgPackToken for FederationProjectScopePayload { type Token = Self; @@ -28,13 +26,13 @@ impl MsgPackToken for FederationProjectScopePayload { &self, wd: &mut W, fernet_provider: &FernetTokenProvider, - ) -> Result<(), TokenProviderError> { + ) -> Result<(), FernetDriverError> { utils::write_uuid(wd, &self.user_id)?; write_pfix( wd, fernet_provider.encode_auth_methods(self.methods.clone())?, ) - .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; + .map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; utils::write_uuid(wd, &self.project_id)?; utils::write_list_of_uuids(wd, self.group_ids.iter())?; utils::write_uuid(wd, &self.idp_id)?; @@ -48,7 +46,7 @@ impl MsgPackToken for FederationProjectScopePayload { fn disassemble( rd: &mut &[u8], fernet_provider: &FernetTokenProvider, - ) -> Result { + ) -> Result { // Order of reading is important let user_id = utils::read_uuid(rd)?; let methods: Vec = fernet_provider @@ -81,7 +79,7 @@ mod tests { use uuid::Uuid; use super::*; - use crate::token::tests::setup_config; + use crate::tests::setup_config; #[test] fn test_roundtrip() { diff --git a/crates/core/src/token/backend/fernet/federation_unscoped.rs b/crates/token-fernet/src/federation_unscoped.rs similarity index 89% rename from crates/core/src/token/backend/fernet/federation_unscoped.rs rename to crates/token-fernet/src/federation_unscoped.rs index 132de1e9..cb7e82ce 100644 --- a/crates/core/src/token/backend/fernet/federation_unscoped.rs +++ b/crates/token-fernet/src/federation_unscoped.rs @@ -15,11 +15,9 @@ use rmp::{decode::read_pfix, encode::write_pfix}; use std::io::Write; -use crate::token::{ - backend::fernet::{FernetTokenProvider, MsgPackToken, utils}, - error::TokenProviderError, - types::FederationUnscopedPayload, -}; +use openstack_keystone_core::token::types::FederationUnscopedPayload; + +use crate::{FernetDriverError, FernetTokenProvider, MsgPackToken, utils}; impl MsgPackToken for FederationUnscopedPayload { type Token = Self; @@ -28,13 +26,13 @@ impl MsgPackToken for FederationUnscopedPayload { &self, wd: &mut W, fernet_provider: &FernetTokenProvider, - ) -> Result<(), TokenProviderError> { + ) -> Result<(), FernetDriverError> { utils::write_uuid(wd, &self.user_id)?; write_pfix( wd, fernet_provider.encode_auth_methods(self.methods.clone())?, ) - .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; + .map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; utils::write_list_of_uuids(wd, self.group_ids.iter())?; utils::write_uuid(wd, &self.idp_id)?; utils::write_str(wd, &self.protocol_id)?; @@ -47,7 +45,7 @@ impl MsgPackToken for FederationUnscopedPayload { fn disassemble( rd: &mut &[u8], fernet_provider: &FernetTokenProvider, - ) -> Result { + ) -> Result { // Order of reading is important let user_id = utils::read_uuid(rd)?; let methods: Vec = fernet_provider @@ -78,7 +76,7 @@ mod tests { use uuid::Uuid; use super::*; - use crate::token::tests::setup_config; + use crate::tests::setup_config; #[test] fn test_roundtrip() { diff --git a/crates/core/src/token/backend/fernet.rs b/crates/token-fernet/src/lib.rs similarity index 87% rename from crates/core/src/token/backend/fernet.rs rename to crates/token-fernet/src/lib.rs index 725adf71..91a154ee 100644 --- a/crates/core/src/token/backend/fernet.rs +++ b/crates/token-fernet/src/lib.rs @@ -11,6 +11,12 @@ // limitations under the License. // // SPDX-License-Identifier: Apache-2.0 +//! # Fernet token driver for the `openstack_keystone` crate + +use std::collections::BTreeMap; +use std::collections::HashSet; +use std::fmt; +use std::io::{Cursor, Write}; use base64::Engine; use byteorder::ReadBytesExt; @@ -23,29 +29,23 @@ use rmp::{ decode::{ValueReadError, read_marker, read_u8}, encode::{write_array_len, write_pfix}, }; -use std::collections::BTreeMap; -use std::collections::HashSet; -use std::fmt; -use std::io::{Cursor, Write}; use tracing::trace; use validator::Validate; -use crate::config::Config; -use crate::token::backend::TokenBackend; -use crate::token::{ - TokenProviderError, - types::{ - application_credential::ApplicationCredentialPayload, domain_scoped::DomainScopePayload, - federation_domain_scoped::FederationDomainScopePayload, - federation_project_scoped::FederationProjectScopePayload, - federation_unscoped::FederationUnscopedPayload, project_scoped::ProjectScopePayload, - restricted::RestrictedPayload, trust::TrustPayload, unscoped::UnscopedPayload, *, - }, +use openstack_keystone_core::config::Config; +use openstack_keystone_core::token::types::{ + application_credential::ApplicationCredentialPayload, domain_scoped::DomainScopePayload, + federation_domain_scoped::FederationDomainScopePayload, + federation_project_scoped::FederationProjectScopePayload, + federation_unscoped::FederationUnscopedPayload, project_scoped::ProjectScopePayload, + restricted::RestrictedPayload, trust::TrustPayload, unscoped::UnscopedPayload, *, }; +use openstack_keystone_core::token::{TokenProviderError, backend::TokenBackend}; use utils::FernetUtils; mod application_credential; mod domain_scoped; +mod error; mod federation_domain_scoped; mod federation_project_scoped; mod federation_unscoped; @@ -56,6 +56,8 @@ mod trust; mod unscoped; pub mod utils; +pub use error::FernetDriverError; + /// Fernet token provider. #[derive(Clone, Default)] pub struct FernetTokenProvider { @@ -76,7 +78,7 @@ pub trait MsgPackToken { &self, _wd: &mut W, _fernet_provider: &FernetTokenProvider, - ) -> Result<(), TokenProviderError> { + ) -> Result<(), FernetDriverError> { Ok(()) } @@ -84,7 +86,7 @@ pub trait MsgPackToken { fn disassemble( rd: &mut &[u8], fernet_provider: &FernetTokenProvider, - ) -> Result; + ) -> Result; } impl fmt::Debug for FernetTokenProvider { @@ -94,11 +96,11 @@ impl fmt::Debug for FernetTokenProvider { } /// Read the payload version. -fn read_payload_token_type(rd: &mut &[u8]) -> Result { +fn read_payload_token_type(rd: &mut &[u8]) -> Result { match read_marker(rd).map_err(ValueReadError::from)? { Marker::FixPos(dt) => Ok(dt), Marker::U8 => Ok(read_u8(rd)?), - _ => Err(TokenProviderError::InvalidToken), + _ => Err(FernetDriverError::InvalidToken), } } @@ -165,7 +167,7 @@ impl FernetTokenProvider { /// Encode the list of auth_methods into a single integer #[tracing::instrument(level = "trace", skip(self, methods))] - pub(crate) fn encode_auth_methods(&self, methods: I) -> Result + pub(crate) fn encode_auth_methods(&self, methods: I) -> Result where I: IntoIterator, { @@ -178,7 +180,7 @@ impl FernetTokenProvider { // TODO: Improve unit tests to ensure unsupported auth method immediately raises // error. if res == 0 { - return Err(TokenProviderError::UnsupportedAuthMethods( + return Err(FernetDriverError::UnsupportedAuthMethods( me.iter().join(","), )); } @@ -187,7 +189,7 @@ impl FernetTokenProvider { /// Decode the integer into the list of auth_methods #[tracing::instrument(level = "trace", skip(self))] - pub(crate) fn decode_auth_methods(&self, value: u8) -> Result, TokenProviderError> { + pub(crate) fn decode_auth_methods(&self, value: u8) -> Result, FernetDriverError> { if let Some(res) = self.auth_methods_code_cache.get(&value) { Ok(res.iter().cloned().collect()) } else { @@ -215,11 +217,7 @@ impl FernetTokenProvider { } /// Parse binary blob as MessagePack after encrypting it with Fernet. - fn decode( - &self, - rd: &mut &[u8], - timestamp: DateTime, - ) -> Result { + fn decode(&self, rd: &mut &[u8], timestamp: DateTime) -> Result { if let Marker::FixArray(_) = read_marker(rd).map_err(ValueReadError::from)? { let mut token: Token = match read_payload_token_type(rd)? { 0 => Ok(UnscopedPayload::disassemble(rd, self)?.into()), @@ -232,88 +230,79 @@ impl FernetTokenProvider { 8 => Ok(SystemScopePayload::disassemble(rd, self)?.into()), 9 => Ok(ApplicationCredentialPayload::disassemble(rd, self)?.into()), 11 => Ok(RestrictedPayload::disassemble(rd, self)?.into()), - other => Err(TokenProviderError::InvalidTokenType(other)), + other => Err(FernetDriverError::InvalidTokenType(other)), }?; token.set_issued_at(timestamp); Ok(token.to_owned()) } else { - Err(TokenProviderError::InvalidToken) + Err(FernetDriverError::InvalidToken) } } /// Encode Token as binary blob as MessagePack. - fn encode(&self, token: &Token) -> Result { + fn encode(&self, token: &Token) -> Result { token.validate()?; let mut buf = vec![]; match token { Token::ApplicationCredential(data) => { write_array_len(&mut buf, 7) - .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; - write_pfix(&mut buf, 9) - .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; + .map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; + write_pfix(&mut buf, 9).map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; data.assemble(&mut buf, self)?; } Token::DomainScope(data) => { write_array_len(&mut buf, 6) - .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; - write_pfix(&mut buf, 1) - .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; + .map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; + write_pfix(&mut buf, 1).map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; data.assemble(&mut buf, self)?; } Token::Trust(data) => { write_array_len(&mut buf, 7) - .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; - write_pfix(&mut buf, 3) - .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; + .map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; + write_pfix(&mut buf, 3).map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; data.assemble(&mut buf, self)?; } Token::FederationUnscoped(data) => { write_array_len(&mut buf, 8) - .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; - write_pfix(&mut buf, 4) - .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; + .map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; + write_pfix(&mut buf, 4).map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; data.assemble(&mut buf, self)?; } Token::FederationProjectScope(data) => { write_array_len(&mut buf, 9) - .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; - write_pfix(&mut buf, 5) - .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; + .map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; + write_pfix(&mut buf, 5).map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; data.assemble(&mut buf, self)?; } Token::FederationDomainScope(data) => { write_array_len(&mut buf, 9) - .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; - write_pfix(&mut buf, 6) - .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; + .map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; + write_pfix(&mut buf, 6).map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; data.assemble(&mut buf, self)?; } Token::ProjectScope(data) => { write_array_len(&mut buf, 6) - .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; - write_pfix(&mut buf, 2) - .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; + .map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; + write_pfix(&mut buf, 2).map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; data.assemble(&mut buf, self)?; } Token::Restricted(data) => { write_array_len(&mut buf, 9) - .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; + .map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; write_pfix(&mut buf, 11) - .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; + .map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; data.assemble(&mut buf, self)?; } Token::SystemScope(data) => { write_array_len(&mut buf, 6) - .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; - write_pfix(&mut buf, 8) - .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; + .map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; + write_pfix(&mut buf, 8).map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; data.assemble(&mut buf, self)?; } Token::Unscoped(data) => { write_array_len(&mut buf, 5) - .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; - write_pfix(&mut buf, 0) - .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; + .map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; + write_pfix(&mut buf, 0).map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; data.assemble(&mut buf, self)?; } } @@ -322,7 +311,7 @@ impl FernetTokenProvider { /// Get MultiFernet initialized with repository keys. #[tracing::instrument(level = "trace", skip(self))] - pub fn get_fernet(&self) -> Result { + pub fn get_fernet(&self) -> Result { Ok(MultiFernet::new( self.utils.load_keys()?.into_iter().collect::>(), )) @@ -330,7 +319,7 @@ impl FernetTokenProvider { /// Load fernet keys from FS. #[tracing::instrument(level = "trace", skip(self))] - pub fn load_keys(&mut self) -> Result<(), TokenProviderError> { + pub fn load_keys(&mut self) -> Result<(), FernetDriverError> { self.fernet = Some(self.get_fernet()?); Ok(()) } @@ -339,7 +328,7 @@ impl FernetTokenProvider { /// /// 1. Decrypt as Fernet. /// 2. Unpack MessagePack payload. - pub fn decrypt(&self, credential: &str) -> Result { + pub fn decrypt(&self, credential: &str) -> Result { // TODO: Implement fernet keys change watching. Keystone loads them from FS on // every request and in the best case it costs 15µs. let fernet = match &self.fernet { @@ -352,7 +341,7 @@ impl FernetTokenProvider { } /// Encrypt the token. - pub fn encrypt(&self, token: &Token) -> Result { + pub fn encrypt(&self, token: &Token) -> Result { let payload = self.encode(token)?; let res = match &self.fernet { Some(fernet) => fernet.encrypt(&payload), @@ -372,13 +361,13 @@ impl TokenBackend for FernetTokenProvider { /// Decrypt the token. #[tracing::instrument(level = "trace", skip(self, credential))] fn decode(&self, credential: &str) -> Result { - self.decrypt(credential) + Ok(self.decrypt(credential)?) } /// Encrypt the token. #[tracing::instrument(level = "trace", skip(self, token))] fn encode(&self, token: &Token) -> Result { - self.encrypt(token) + Ok(self.encrypt(token)?) } } @@ -390,7 +379,7 @@ fn b64_decode_url(input: &str) -> std::result::Result, base64::DecodeErr /// Get the fernet payload creation timestamp. /// /// Extract the payload creation timestamp in the UTC. -fn get_fernet_timestamp(payload: &str) -> Result, TokenProviderError> { +fn get_fernet_timestamp(payload: &str) -> Result, FernetDriverError> { let data = match b64_decode_url(payload) { Ok(data) => data, Err(_) => return Err(fernet::DecryptionError)?, @@ -405,36 +394,41 @@ fn get_fernet_timestamp(payload: &str) -> Result, TokenProviderErr input .read_u64::() - .map_err(|_| TokenProviderError::FernetDecryption(fernet::DecryptionError)) + .map_err(|_| FernetDriverError::FernetDecryption(fernet::DecryptionError)) .and_then(|val| { - TryInto::try_into(val).map_err(|err| TokenProviderError::TokenTimestampOverflow { + TryInto::try_into(val).map_err(|err| FernetDriverError::TokenTimestampOverflow { value: val, source: err, }) }) .and_then(|val| { DateTime::from_timestamp_secs(val) - .ok_or_else(|| TokenProviderError::FernetDecryption(fernet::DecryptionError)) + .ok_or_else(|| FernetDriverError::FernetDecryption(fernet::DecryptionError)) }) } +#[cfg(feature = "bench_internals")] /// Conditionally expose the function when the 'bench_internals' feature is /// enabled -#[cfg(feature = "bench_internals")] -pub fn bench_get_fernet_timestamp(payload: &str) -> Result, TokenProviderError> { +pub fn bench_get_fernet_timestamp(payload: &str) -> Result, FernetDriverError> { get_fernet_timestamp(payload) } #[cfg(test)] -pub(super) mod tests { - use super::*; - use chrono::{Local, SubsecRound}; +pub mod tests { use std::fs::File; use std::io::Write; + + use chrono::{Local, SubsecRound}; + //use config; use tempfile::tempdir; use uuid::Uuid; - pub(super) fn setup_config() -> Config { + use openstack_keystone_core::config::Config; + + use super::*; + + pub(crate) fn setup_config() -> Config { let keys_dir = tempdir().unwrap(); // write fernet key used to generate tokens in python let file_path = keys_dir.path().join("0"); @@ -720,7 +714,7 @@ pub(super) mod tests { ..Default::default() }); - let config = crate::tests::token::setup_config(); + let config = setup_config(); let mut provider = FernetTokenProvider::new(config); provider.load_keys().unwrap(); diff --git a/crates/core/src/token/backend/fernet/project_scoped.rs b/crates/token-fernet/src/project_scoped.rs similarity index 88% rename from crates/core/src/token/backend/fernet/project_scoped.rs rename to crates/token-fernet/src/project_scoped.rs index 4c37b54b..6213fc36 100644 --- a/crates/core/src/token/backend/fernet/project_scoped.rs +++ b/crates/token-fernet/src/project_scoped.rs @@ -15,11 +15,9 @@ use rmp::{decode::read_pfix, encode::write_pfix}; use std::io::Write; -use crate::token::{ - backend::fernet::{FernetTokenProvider, MsgPackToken, utils}, - error::TokenProviderError, - types::ProjectScopePayload, -}; +use openstack_keystone_core::token::types::ProjectScopePayload; + +use crate::{FernetDriverError, FernetTokenProvider, MsgPackToken, utils}; impl MsgPackToken for ProjectScopePayload { type Token = Self; @@ -28,13 +26,13 @@ impl MsgPackToken for ProjectScopePayload { &self, wd: &mut W, fernet_provider: &FernetTokenProvider, - ) -> Result<(), TokenProviderError> { + ) -> Result<(), FernetDriverError> { utils::write_uuid(wd, &self.user_id)?; write_pfix( wd, fernet_provider.encode_auth_methods(self.methods.clone())?, ) - .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; + .map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; utils::write_uuid(wd, &self.project_id)?; utils::write_time(wd, self.expires_at)?; utils::write_audit_ids(wd, self.audit_ids.clone())?; @@ -45,7 +43,7 @@ impl MsgPackToken for ProjectScopePayload { fn disassemble( rd: &mut &[u8], fernet_provider: &FernetTokenProvider, - ) -> Result { + ) -> Result { // Order of reading is important let user_id = utils::read_uuid(rd)?; let methods: Vec = fernet_provider @@ -72,7 +70,7 @@ mod tests { use uuid::Uuid; use super::*; - use crate::token::tests::setup_config; + use crate::tests::setup_config; #[test] fn test_roundtrip() { diff --git a/crates/core/src/token/backend/fernet/restricted.rs b/crates/token-fernet/src/restricted.rs similarity index 90% rename from crates/core/src/token/backend/fernet/restricted.rs rename to crates/token-fernet/src/restricted.rs index 6ccb44c7..f827f518 100644 --- a/crates/core/src/token/backend/fernet/restricted.rs +++ b/crates/token-fernet/src/restricted.rs @@ -16,11 +16,9 @@ use rmp::{decode::read_pfix, encode::write_pfix}; use std::io::Write; -use crate::token::{ - backend::fernet::{FernetTokenProvider, MsgPackToken, utils}, - error::TokenProviderError, - types::RestrictedPayload, -}; +use openstack_keystone_core::token::types::RestrictedPayload; + +use crate::{FernetDriverError, FernetTokenProvider, MsgPackToken, utils}; impl MsgPackToken for RestrictedPayload { type Token = Self; @@ -29,13 +27,13 @@ impl MsgPackToken for RestrictedPayload { &self, wd: &mut W, fernet_provider: &FernetTokenProvider, - ) -> Result<(), TokenProviderError> { + ) -> Result<(), FernetDriverError> { utils::write_uuid(wd, &self.user_id)?; write_pfix( wd, fernet_provider.encode_auth_methods(self.methods.clone())?, ) - .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; + .map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; utils::write_uuid(wd, &self.token_restriction_id)?; utils::write_time(wd, self.expires_at)?; utils::write_uuid(wd, &self.project_id)?; @@ -49,7 +47,7 @@ impl MsgPackToken for RestrictedPayload { fn disassemble( rd: &mut &[u8], fernet_provider: &FernetTokenProvider, - ) -> Result { + ) -> Result { // Order of reading is important let user_id = utils::read_uuid(rd)?; let methods: Vec = fernet_provider @@ -83,7 +81,7 @@ mod tests { use uuid::Uuid; use super::*; - use crate::token::tests::setup_config; + use crate::tests::setup_config; #[test] fn test_roundtrip() { diff --git a/crates/core/src/token/backend/fernet/system_scoped.rs b/crates/token-fernet/src/system_scoped.rs similarity index 88% rename from crates/core/src/token/backend/fernet/system_scoped.rs rename to crates/token-fernet/src/system_scoped.rs index dff768c5..3ceff3c4 100644 --- a/crates/core/src/token/backend/fernet/system_scoped.rs +++ b/crates/token-fernet/src/system_scoped.rs @@ -15,11 +15,9 @@ use rmp::{decode::read_pfix, encode::write_pfix}; use std::io::Write; -use crate::token::{ - backend::fernet::{FernetTokenProvider, MsgPackToken, utils}, - error::TokenProviderError, - types::SystemScopePayload, -}; +use openstack_keystone_core::token::types::SystemScopePayload; + +use crate::{FernetDriverError, FernetTokenProvider, MsgPackToken, utils}; impl MsgPackToken for SystemScopePayload { type Token = Self; @@ -28,13 +26,13 @@ impl MsgPackToken for SystemScopePayload { &self, wd: &mut W, fernet_provider: &FernetTokenProvider, - ) -> Result<(), TokenProviderError> { + ) -> Result<(), FernetDriverError> { utils::write_uuid(wd, &self.user_id)?; write_pfix( wd, fernet_provider.encode_auth_methods(self.methods.clone())?, ) - .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; + .map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; utils::write_str(wd, &self.system_id)?; utils::write_time(wd, self.expires_at)?; utils::write_audit_ids(wd, self.audit_ids.clone())?; @@ -45,7 +43,7 @@ impl MsgPackToken for SystemScopePayload { fn disassemble( rd: &mut &[u8], fernet_provider: &FernetTokenProvider, - ) -> Result { + ) -> Result { // Order of reading is important let user_id = utils::read_uuid(rd)?; let methods: Vec = fernet_provider @@ -72,7 +70,7 @@ mod tests { use uuid::Uuid; use super::*; - use crate::token::tests::setup_config; + use crate::tests::setup_config; #[test] fn test_roundtrip() { diff --git a/crates/core/src/token/backend/fernet/trust.rs b/crates/token-fernet/src/trust.rs similarity index 89% rename from crates/core/src/token/backend/fernet/trust.rs rename to crates/token-fernet/src/trust.rs index bf68294b..0d5ff7b3 100644 --- a/crates/core/src/token/backend/fernet/trust.rs +++ b/crates/token-fernet/src/trust.rs @@ -15,11 +15,9 @@ use rmp::{decode::read_pfix, encode::write_pfix}; use std::io::Write; -use crate::token::{ - backend::fernet::{FernetTokenProvider, MsgPackToken, utils}, - error::TokenProviderError, - types::TrustPayload, -}; +use openstack_keystone_core::token::types::TrustPayload; + +use crate::{FernetDriverError, FernetTokenProvider, MsgPackToken, utils}; impl MsgPackToken for TrustPayload { type Token = Self; @@ -28,13 +26,13 @@ impl MsgPackToken for TrustPayload { &self, wd: &mut W, fernet_provider: &FernetTokenProvider, - ) -> Result<(), TokenProviderError> { + ) -> Result<(), FernetDriverError> { utils::write_uuid(wd, &self.user_id)?; write_pfix( wd, fernet_provider.encode_auth_methods(self.methods.clone())?, ) - .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; + .map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; utils::write_uuid(wd, &self.project_id)?; utils::write_time(wd, self.expires_at)?; utils::write_audit_ids(wd, self.audit_ids.clone())?; @@ -46,7 +44,7 @@ impl MsgPackToken for TrustPayload { fn disassemble( rd: &mut &[u8], fernet_provider: &FernetTokenProvider, - ) -> Result { + ) -> Result { // Order of reading is important let user_id = utils::read_uuid(rd)?; let methods: Vec = fernet_provider @@ -76,7 +74,7 @@ mod tests { use uuid::Uuid; use super::*; - use crate::token::tests::setup_config; + use crate::tests::setup_config; #[test] fn test_roundtrip() { diff --git a/crates/core/src/token/backend/fernet/unscoped.rs b/crates/token-fernet/src/unscoped.rs similarity index 87% rename from crates/core/src/token/backend/fernet/unscoped.rs rename to crates/token-fernet/src/unscoped.rs index 0ba35b76..46864600 100644 --- a/crates/core/src/token/backend/fernet/unscoped.rs +++ b/crates/token-fernet/src/unscoped.rs @@ -15,11 +15,9 @@ use rmp::{decode::read_pfix, encode::write_pfix}; use std::io::Write; -use crate::token::{ - backend::fernet::{FernetTokenProvider, MsgPackToken, utils}, - error::TokenProviderError, - types::UnscopedPayload, -}; +use openstack_keystone_core::token::types::UnscopedPayload; + +use crate::{FernetDriverError, FernetTokenProvider, MsgPackToken, utils}; impl MsgPackToken for UnscopedPayload { type Token = Self; @@ -28,13 +26,13 @@ impl MsgPackToken for UnscopedPayload { &self, wd: &mut W, fernet_provider: &FernetTokenProvider, - ) -> Result<(), TokenProviderError> { + ) -> Result<(), FernetDriverError> { utils::write_uuid(wd, &self.user_id)?; write_pfix( wd, fernet_provider.encode_auth_methods(self.methods.clone())?, ) - .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; + .map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; utils::write_time(wd, self.expires_at)?; utils::write_audit_ids(wd, self.audit_ids.clone())?; @@ -44,7 +42,7 @@ impl MsgPackToken for UnscopedPayload { fn disassemble( rd: &mut &[u8], fernet_provider: &FernetTokenProvider, - ) -> Result { + ) -> Result { // Order of writing is important let user_id = utils::read_uuid(rd)?; let methods: Vec = fernet_provider @@ -69,7 +67,7 @@ mod tests { use uuid::Uuid; use super::*; - use crate::token::tests::setup_config; + use crate::tests::setup_config; #[test] fn test_roundtrip() { diff --git a/crates/core/src/token/backend/fernet/utils.rs b/crates/token-fernet/src/utils.rs similarity index 83% rename from crates/core/src/token/backend/fernet/utils.rs rename to crates/token-fernet/src/utils.rs index d9766af3..c03c1819 100644 --- a/crates/core/src/token/backend/fernet/utils.rs +++ b/crates/token-fernet/src/utils.rs @@ -25,7 +25,6 @@ use rmp::{ use secrecy::{ExposeSecret, SecretString}; use std::collections::BTreeMap; use std::fs; -//use std::io; use std::io::{self, Read, Write}; use std::path::PathBuf; use tempfile::NamedTempFile; @@ -33,7 +32,7 @@ use tokio::fs as fs_async; use tracing::{error, info, trace, warn}; use uuid::Uuid; -use crate::token::error::TokenProviderError; +use crate::error::FernetDriverError; /// Fernet utils. #[derive(Clone, Debug, Default)] @@ -46,7 +45,7 @@ impl FernetUtils { /// Validate fernet key key_repository. /// /// Perform validation of the fernet keys repository. - fn validate_key_repository(&self) -> Result { + fn validate_key_repository(&self) -> Result { Ok(self.key_repository.exists()) } @@ -58,7 +57,7 @@ impl FernetUtils { &self, user_id: Option, group_id: Option, - ) -> Result<(), TokenProviderError> { + ) -> Result<(), FernetDriverError> { // 1. Generate key and wrap in a secret-protecting type let key = SecretString::new(Fernet::generate_key().into()); let target_path = self.key_repository.join("0.tmp"); @@ -72,11 +71,11 @@ impl FernetUtils { if let (Some(uid), Some(gid)) = (user_id, group_id) { let (old_euid, old_egid) = (geteuid(), getegid()); - setegid(Gid::from_raw(gid)).map_err(|e| TokenProviderError::NixErrno { + setegid(Gid::from_raw(gid)).map_err(|e| FernetDriverError::NixErrno { context: "setting effective process GID".into(), source: e, })?; - seteuid(Uid::from_raw(uid)).map_err(|e| TokenProviderError::NixErrno { + seteuid(Uid::from_raw(uid)).map_err(|e| FernetDriverError::NixErrno { context: "setting effective process UID".into(), source: e, })?; @@ -107,14 +106,14 @@ impl FernetUtils { /// Make the tmp new key a valid new key. /// Renames '0.tmp' to '0' atomically. - fn become_valid_new_key(&self) -> Result<(), TokenProviderError> { + fn become_valid_new_key(&self) -> Result<(), FernetDriverError> { let tmp_key_file = self.key_repository.join("0.tmp"); let valid_key_file = self.key_repository.join("0"); // Check if the source exists before attempting rename to provide better errors if !tmp_key_file.exists() { error!("Temporary key file not found: {:?}", tmp_key_file); - return Err(TokenProviderError::FernetKeysMissing); + return Err(FernetDriverError::FernetKeysMissing); } // std::fs::rename is atomic on most Unix-like systems. @@ -130,13 +129,13 @@ impl FernetUtils { Ok(()) } - pub fn initialize_key_repository(&self) -> Result<(), TokenProviderError> { + pub fn initialize_key_repository(&self) -> Result<(), FernetDriverError> { self.create_tmp_new_key(None, None)?; self.become_valid_new_key()?; Ok(()) } - pub fn load_keys(&self) -> Result, TokenProviderError> { + pub fn load_keys(&self) -> Result, FernetDriverError> { info!("loading keys from {:?}", self.key_repository); let mut keys: BTreeMap = BTreeMap::new(); if self.validate_key_repository()? { @@ -149,7 +148,7 @@ impl FernetUtils { trace!("Loading key {:?}", entry.file_name()); if let Some(fernet) = Fernet::new( fs::read_to_string(entry.path()) - .map_err(|e| TokenProviderError::FernetKeyRead { + .map_err(|e| FernetDriverError::FernetKeyRead { source: e, path: entry.path(), })? @@ -166,14 +165,14 @@ impl FernetUtils { } } if keys.is_empty() { - return Err(TokenProviderError::FernetKeysMissing); + return Err(FernetDriverError::FernetKeysMissing); } Ok(keys.into_values().rev()) } pub async fn load_keys_async( &self, - ) -> Result, TokenProviderError> { + ) -> Result, FernetDriverError> { let mut keys: BTreeMap = BTreeMap::new(); if self.validate_key_repository()? { let mut entries = fs_async::read_dir(&self.key_repository).await?; @@ -185,7 +184,7 @@ impl FernetUtils { trace!("Loading key {:?}", entry.file_name()); if let Some(fernet) = Fernet::new( fs::read_to_string(entry.path()) - .map_err(|e| TokenProviderError::FernetKeyRead { + .map_err(|e| FernetDriverError::FernetKeyRead { source: e, path: entry.path(), })? @@ -202,7 +201,7 @@ impl FernetUtils { } } if keys.is_empty() { - return Err(TokenProviderError::FernetKeysMissing); + return Err(FernetDriverError::FernetKeysMissing); } Ok(keys.into_values().rev()) } @@ -224,19 +223,19 @@ pub fn read_str_data(len: u32, rd: &mut R) -> Result } /// Write string. -pub fn write_str(wd: &mut W, data: &str) -> Result<(), TokenProviderError> { - encode::write_str(wd, data).map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; +pub fn write_str(wd: &mut W, data: &str) -> Result<(), FernetDriverError> { + encode::write_str(wd, data).map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; Ok(()) } /// Read bytes as string. -pub fn read_str(rd: &mut R) -> Result { +pub fn read_str(rd: &mut R) -> Result { match read_marker(rd).map_err(ValueReadError::from)? { Marker::Bin8 => { Ok(String::from_utf8_lossy(&read_bin_data(read_pfix(rd)?.into(), rd)?).to_string()) } Marker::FixStr(len) => Ok(read_str_data(len.into(), rd)?), - other => Err(TokenProviderError::InvalidTokenUuidMarker(other)), + other => Err(FernetDriverError::InvalidTokenUuidMarker(other)), } } @@ -244,7 +243,7 @@ pub fn read_str(rd: &mut R) -> Result { /// It is represented as an Array[bool, bytes] where first bool indicates /// whether following bytes are UUID or just bytes that should be treated as a /// string (for cases where ID is not a valid UUID). -pub fn read_uuid(rd: &mut &[u8]) -> Result { +pub fn read_uuid(rd: &mut &[u8]) -> Result { match read_marker(rd).map_err(ValueReadError::from)? { Marker::FixArray(_) => { match read_marker(rd).map_err(ValueReadError::from)? { @@ -272,12 +271,12 @@ pub fn read_uuid(rd: &mut &[u8]) -> Result { return Ok(read_str_data(len.into(), rd)?); } other => { - return Err(TokenProviderError::InvalidTokenUuidMarker(other)); + return Err(FernetDriverError::InvalidTokenUuidMarker(other)); } } } other => { - return Err(TokenProviderError::InvalidTokenUuidMarker(other)); + return Err(FernetDriverError::InvalidTokenUuidMarker(other)); } } } @@ -285,51 +284,50 @@ pub fn read_uuid(rd: &mut &[u8]) -> Result { return Ok(read_str_data(len.into(), rd)?); } other => { - return Err(TokenProviderError::InvalidTokenUuidMarker(other)); + return Err(FernetDriverError::InvalidTokenUuidMarker(other)); } } - Err(TokenProviderError::InvalidTokenUuid) + Err(FernetDriverError::InvalidTokenUuid) } /// Write the UUID to the payload. /// It is represented as an Array[bool, bytes] where first bool indicates /// whether following bytes are UUID or just bytes that should be treated as a /// string (for cases where ID is not a valid UUID). -pub fn write_uuid(wd: &mut W, uid: &str) -> Result<(), TokenProviderError> { +pub fn write_uuid(wd: &mut W, uid: &str) -> Result<(), FernetDriverError> { match Uuid::parse_str(uid) { Ok(uuid) => { - write_array_len(wd, 2).map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; - write_bool(wd, true).map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; + write_array_len(wd, 2).map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; + write_bool(wd, true).map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; write_bin(wd, uuid.as_bytes()) - .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; + .map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; } _ => { - write_array_len(wd, 2).map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; - write_bool(wd, false).map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; + write_array_len(wd, 2).map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; + write_bool(wd, false).map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; write_bin(wd, uid.as_bytes()) - .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; + .map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; } } Ok(()) } /// Read the time represented as a f64 of the UTC seconds. -pub fn read_time(rd: &mut &[u8]) -> Result, TokenProviderError> { - DateTime::from_timestamp(read_f64(rd)?.round() as i64, 0) - .ok_or(TokenProviderError::InvalidToken) +pub fn read_time(rd: &mut &[u8]) -> Result, FernetDriverError> { + DateTime::from_timestamp(read_f64(rd)?.round() as i64, 0).ok_or(FernetDriverError::InvalidToken) } /// Write the time represented as a f64 of the UTC seconds. -pub fn write_time(wd: &mut W, time: DateTime) -> Result<(), TokenProviderError> { +pub fn write_time(wd: &mut W, time: DateTime) -> Result<(), FernetDriverError> { write_f64(wd, time.timestamp() as f64) - .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; + .map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; Ok(()) } /// Decode array of audit ids from the payload. pub fn read_audit_ids( rd: &mut &[u8], -) -> Result + use<>, TokenProviderError> { +) -> Result + use<>, FernetDriverError> { if let Marker::FixArray(len) = read_marker(rd).map_err(ValueReadError::from)? { let mut result: Vec = Vec::new(); for _ in 0..len { @@ -338,30 +336,30 @@ pub fn read_audit_ids( let audit_id: String = URL_SAFE_NO_PAD.encode(dt); result.push(audit_id); } else { - return Err(TokenProviderError::InvalidToken); + return Err(FernetDriverError::InvalidToken); } } return Ok(result.into_iter()); } - Err(TokenProviderError::InvalidToken) + Err(FernetDriverError::InvalidToken) } /// Encode array of audit ids into the payload. pub fn write_audit_ids>( wd: &mut W, data: I, -) -> Result<(), TokenProviderError> { +) -> Result<(), FernetDriverError> { let vals = Vec::from_iter(data); write_array_len(wd, vals.len() as u32) - .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; + .map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; for val in vals.iter() { write_bin( wd, &URL_SAFE_NO_PAD .decode(val) - .map_err(|_| TokenProviderError::AuditIdWrongFormat)?, + .map_err(|_| FernetDriverError::AuditIdWrongFormat)?, ) - .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; + .map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; } Ok(()) } @@ -369,7 +367,7 @@ pub fn write_audit_ids>( /// Decode array of strings ids from the payload. pub fn read_list_of_uuids( rd: &mut &[u8], -) -> Result + use<>, TokenProviderError> { +) -> Result + use<>, FernetDriverError> { if let Marker::FixArray(len) = read_marker(rd).map_err(ValueReadError::from)? { let mut result: Vec = Vec::new(); for _ in 0..len { @@ -377,17 +375,17 @@ pub fn read_list_of_uuids( } return Ok(result.into_iter()); } - Err(TokenProviderError::InvalidToken) + Err(FernetDriverError::InvalidToken) } /// Encode array of bytes into the payload. pub fn write_list_of_uuids, V: AsRef>( wd: &mut W, data: I, -) -> Result<(), TokenProviderError> { +) -> Result<(), FernetDriverError> { let vals = Vec::from_iter(data); write_array_len(wd, vals.len() as u32) - .map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; + .map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; for val in vals.iter() { write_uuid(wd, val.as_ref())?; } @@ -395,13 +393,13 @@ pub fn write_list_of_uuids, V: AsRef } /// Read boolean. -pub fn read_bool(rd: &mut R) -> Result { +pub fn read_bool(rd: &mut R) -> Result { Ok(decode::read_bool(rd)?) } /// Write boolean. -pub fn write_bool(wd: &mut W, data: bool) -> Result<(), TokenProviderError> { - encode::write_bool(wd, data).map_err(|x| TokenProviderError::RmpEncode(x.to_string()))?; +pub fn write_bool(wd: &mut W, data: bool) -> Result<(), FernetDriverError> { + encode::write_bool(wd, data).map_err(|x| FernetDriverError::RmpEncode(x.to_string()))?; Ok(()) } @@ -450,7 +448,7 @@ mod tests { }; let res = utils.load_keys(); - if let Err(TokenProviderError::FernetKeysMissing) = res { + if let Err(FernetDriverError::FernetKeysMissing) = res { } else { panic!("Should have raised an exception"); } diff --git a/tests/integration/Cargo.toml b/tests/integration/Cargo.toml index 1c92fb80..e46ae39f 100644 --- a/tests/integration/Cargo.toml +++ b/tests/integration/Cargo.toml @@ -14,7 +14,8 @@ chrono.workspace = true eyre.workspace = true itertools.workspace = true openstack-keystone-core = { version = "0.1", path = "../../crates/core", features = ["mock"] } -openstack-keystone = { version = "0.1", path = "../../crates/keystone" } +openstack-keystone-token-fernet = { version = "0.1", path = "../../crates/token-fernet/" } +openstack-keystone = { version = "0.1", path = "../../crates/keystone/" } sea-orm = { workspace = true, features = ["sqlx-sqlite"] } secrecy = { workspace = true, features = ["serde"] } serde.workspace = true diff --git a/tests/integration/src/assignment/grant.rs b/tests/integration/src/assignment/grant.rs index 82ae4399..21a6be85 100644 --- a/tests/integration/src/assignment/grant.rs +++ b/tests/integration/src/assignment/grant.rs @@ -80,7 +80,7 @@ async fn get_state() -> Result<(Arc, TempDir), Report> { let mut cfg: Config = Config::default(); cfg.auth.methods = vec!["application_credential".into(), "password".into()]; cfg.fernet_tokens.key_repository = tmp_fernet_repo.path().to_path_buf(); - let fernet_utils = openstack_keystone::token::backend::fernet::utils::FernetUtils { + let fernet_utils = openstack_keystone_token_fernet::utils::FernetUtils { key_repository: cfg.fernet_tokens.key_repository.clone(), max_active_keys: cfg.fernet_tokens.max_active_keys, }; diff --git a/tests/integration/src/common.rs b/tests/integration/src/common.rs index 4984442c..1a163f79 100644 --- a/tests/integration/src/common.rs +++ b/tests/integration/src/common.rs @@ -238,7 +238,7 @@ pub async fn get_state() -> Result<(Arc, TempDir)> { let mut cfg: Config = Config::default(); cfg.auth.methods = vec!["application_credential".into(), "password".into()]; cfg.fernet_tokens.key_repository = tmp_fernet_repo.path().to_path_buf(); - let fernet_utils = openstack_keystone::token::backend::fernet::utils::FernetUtils { + let fernet_utils = openstack_keystone_token_fernet::utils::FernetUtils { key_repository: cfg.fernet_tokens.key_repository.clone(), max_active_keys: cfg.fernet_tokens.max_active_keys, }; diff --git a/tests/integration/src/token/validate.rs b/tests/integration/src/token/validate.rs index 14f50393..bbd60aff 100644 --- a/tests/integration/src/token/validate.rs +++ b/tests/integration/src/token/validate.rs @@ -73,7 +73,7 @@ async fn get_state() -> Result<(Arc, TempDir), Report> { let mut cfg: Config = Config::default(); cfg.auth.methods = vec!["application_credential".into(), "password".into()]; cfg.fernet_tokens.key_repository = tmp_fernet_repo.path().to_path_buf(); - let fernet_utils = openstack_keystone::token::backend::fernet::utils::FernetUtils { + let fernet_utils = openstack_keystone_token_fernet::utils::FernetUtils { key_repository: cfg.fernet_tokens.key_repository.clone(), max_active_keys: cfg.fernet_tokens.max_active_keys, }; diff --git a/tools/Dockerfile.functest b/tools/Dockerfile.functest index 7fa0bbcc..c6bba90c 100644 --- a/tools/Dockerfile.functest +++ b/tools/Dockerfile.functest @@ -18,6 +18,7 @@ COPY crates/api-types/Cargo.toml /usr/src/keystone/crates/api-types/ COPY crates/core/Cargo.toml /usr/src/keystone/crates/core/ COPY crates/keystone/Cargo.toml /usr/src/keystone/crates/keystone/ COPY crates/storage/Cargo.toml /usr/src/keystone/crates/storage/ +COPY crates/token-fernet/Cargo.toml /usr/src/keystone/crates/token-fernet/ COPY tests/federation/Cargo.toml /usr/src/keystone/tests/federation/ COPY tests/integration/Cargo.toml /usr/src/keystone/tests/integration/ COPY tests/api/Cargo.toml /usr/src/keystone/tests/api/ @@ -29,8 +30,9 @@ RUN mkdir -p keystone/crates/keystone/src/bin && touch keystone/crates/keystone/ cp keystone/src/main.rs keystone/tests/loadtest/src/main.rs &&\ mkdir -p keystone/crates/api-types/src && touch keystone/crates/api-types/src/lib.rs &&\ mkdir -p keystone/crates/core/src && touch keystone/crates/core/src/lib.rs &&\ - mkdir -p keystone/crates/core/benches && touch keystone/crates/core/benches/fernet_token.rs &&\ - mkdir -p keystone/crates/storage/src && touch keystone/crates/storage/src/lib.rs + mkdir -p keystone/crates/storage/src && touch keystone/crates/storage/src/lib.rs &&\ + mkdir -p keystone/crates/token-fernet/src && touch keystone/crates/token-fernet/src/lib.rs &&\ + mkdir -p keystone/crates/token-fernet/benches && touch keystone/crates/token-fernet/benches/fernet_token.rs # Set the working directory WORKDIR /usr/src/keystone diff --git a/tools/setup-db.sh b/tools/setup-db.sh index 16559fa8..170b0a4d 100755 --- a/tools/setup-db.sh +++ b/tools/setup-db.sh @@ -29,7 +29,7 @@ if [ "$DB_TYPE" == "postgres" ]; then # Postgres check until $CMD exec "$TARGET" pg_isready -U postgres > /dev/null 2>&1; do printf "." - sleep 1 + sleep 3 done echo "DATABASE_URL=postgres://postgres:password@127.0.0.1:15432/postgres" >> "$NEXTEST_ENV" else