Skip to content

Commit

Permalink
- fix: (bug) always use the same encrypter even if different keyring …
Browse files Browse the repository at this point in the history
…entries are given

- doc: (derive macros) add doc for `PersistSource` and `SecretSource`
  • Loading branch information
kingwingfly committed Jun 19, 2024
1 parent fc59da8 commit 05ca931
Show file tree
Hide file tree
Showing 8 changed files with 126 additions and 50 deletions.
22 changes: 11 additions & 11 deletions .github/scripts/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,20 @@ export TERM=xterm-256color

# Statements waiting to be executed
statements=(
"cargo clippy --all-targets -- -D warnings"
"cargo clippy --all-targets --features persist -- -D warnings"
"cargo clippy --all-targets --features secret -- -D warnings"
"cargo clippy --all-targets --features secret,mock -- -D warnings"
"cargo clippy --all-targets --no-default-features -- -D warnings"
"cargo clippy --all-targets --no-default-features --features persist -- -D warnings"
"cargo clippy --all-targets --no-default-features --features secret -- -D warnings"
"cargo clippy --all-targets --no-default-features --features secret,mock -- -D warnings"

"cargo test"
"cargo test --features persist"
"cargo test --features persist,default_config_dir"
"cargo test --features secret,mock"
"cargo test --features secret,mock,default_config_dir"
"cargo test --no-default-features"
"cargo test --no-default-features --features persist"
"cargo test --no-default-features --features persist,default_config_dir"
"cargo test --no-default-features --features secret,mock"
"cargo test --no-default-features --features secret,mock,default_config_dir"

"cargo run --example example --features full,mock"
"cargo run --example example --no-default-features --features full,mock"

"cargo doc --no-deps --features full"
"cargo doc --no-deps --no-default-features --features full"
)

# loop echo and executing statements
Expand Down
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Changelog

All notable changes to this project will be documented in this file.

This project adheres to [Semantic Versioning](https://semver.org).

<!--
Note: In this file, do not use the hard wrap in the middle of a sentence for compatibility with GitHub comment style markdown rendering.
-->

## [Unreleased]

## [0.2.4] - 2024-06-19

- fix: (bug) always use the same encrypter even if different keyring entries are given
- doc: (derive macros) add doc for `PersistSource` and `SecretSource`
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ members = ["encrypt-config", "encrypt-config-derive", "tests", "examples"]
resolver = "2"

[workspace.package]
version = "0.2.3"
version = "0.2.4"
authors = ["Louis <[email protected]>"]
description = "A Rust crate to manage, persist and encrypt your configurations."
license = "MIT"
Expand All @@ -13,7 +13,7 @@ documentation = "https://docs.rs/encrypt-config"

[workspace.dependencies]
encrypt_config = { path = "encrypt-config" }
encrypt_config_derive = { path = "encrypt-config-derive", version = "0.2.2" }
encrypt_config_derive = { path = "encrypt-config-derive", version = "0.2.4" }

[profile.dev]
opt-level = 3
12 changes: 6 additions & 6 deletions encrypt-config-derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "encrypt_config_derive"
version = "0.2.2"
version.workspace = true
authors.workspace = true
description.workspace = true
license.workspace = true
Expand All @@ -22,11 +22,11 @@ quote = "1.0"

[dev-dependencies]
serde = { version = "1", features = ["derive"] }
encrypt_config = { workspace = true }
encrypt_config = { workspace = true, default-features = false }

[features]
default = []
full = ["persist", "secret"]
persist = []
secret = ["persist"]
default_config_dir = []
full = ["persist", "secret", "encrypt_config/full"]
persist = ["encrypt_config/persist"]
secret = ["persist", "encrypt_config/secret"]
default_config_dir = ["encrypt_config/default_config_dir"]
47 changes: 45 additions & 2 deletions encrypt-config-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,63 @@ mod secret;
use proc_macro::TokenStream;

/// Derive macro for `NormalSource`.
/// # Example
/// ```
/// # use encrypt_config_derive::NormalSource;
/// #[derive(Default, NormalSource)]
/// struct NormalConfig {
/// count: i32,
/// }
/// ```
#[proc_macro_derive(NormalSource)]
pub fn derive_normal_source(input: TokenStream) -> TokenStream {
normal::derive_normal_source(input)
}

#[cfg(feature = "persist")]
/// Derive macro for `PersistSource`.
/// # Example
/// ```
/// # use encrypt_config_derive::PersistSource;
/// # use serde::{Deserialize, Serialize};
/// #[derive(Serialize, Deserialize, Default, PersistSource)]
#[cfg_attr(
feature = "default_config_dir",
doc = "#[source(name = \"persist_config.json\")]"
)]
#[cfg_attr(
not(feature = "default_config_dir"),
doc = "#[source(path = \"/path/to/persist_config.json\")]"
)]
/// struct PersistConfig {
/// name: String,
/// age: i32,
/// }
/// ```
#[cfg(feature = "persist")]
#[proc_macro_derive(PersistSource, attributes(source))]
pub fn derive_persist_source(input: TokenStream) -> TokenStream {
persist::derive_persist_source(input)
}

#[cfg(feature = "secret")]
/// Derive macro for `SecretSource`.
/// # Example
/// ```
/// # use encrypt_config_derive::SecretSource;
/// # use serde::{Deserialize, Serialize};
/// #[derive(Serialize, Deserialize, Default, SecretSource)]
#[cfg_attr(
feature = "default_config_dir",
doc = "#[source(name = \"secret_config.json\", keyring_entry = \"secret\")]"
)]
#[cfg_attr(
not(feature = "default_config_dir"),
doc = "#[source(path = \"/path/to/secret_config\", keyring_entry = \"secret\")]"
)]
/// struct SecretConfig {
/// password: String,
/// }
/// ```
#[cfg(feature = "secret")]
#[proc_macro_derive(SecretSource, attributes(source))]
pub fn derive_secret_source(input: TokenStream) -> TokenStream {
secret::derive_secret_source(input)
Expand Down
2 changes: 0 additions & 2 deletions encrypt-config/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ keyring = { version = "2.1.0", optional = true }
rand = { version = "0.8.5", optional = true }
dirs = { version = "5.0.1", optional = true }

[build-dependencies]

[features]
default = []
full = ["persist", "secret", "derive"]
Expand Down
8 changes: 4 additions & 4 deletions encrypt-config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ _(You may see many `#[cfg(feature = "...")]` in the example below, if you are no
You can implement the [`NormalSource`], [`PersistSource`] and [`SecretSource`] yourself.

```rust no_run
# #[cfg(feature = "full")]
# #[cfg(all(feature = "full", feature = "default_config_dir"))]
# {
use encrypt_config::{Config, NormalSource, PersistSource, SecretSource, TEST_OUT_DIR};
use serde::{Deserialize, Serialize};
Expand All @@ -115,14 +115,14 @@ struct NormalConfig {
}

#[derive(Default, Serialize, Deserialize, PersistSource)]
#[source(path = const_str::concat!(TEST_OUT_DIR, "/persist_config.json"))]
#[source(name = "persist_config.json")]
struct PersistConfig {
name: String,
age: usize,
}

#[derive(Default, Serialize, Deserialize, SecretSource)]
#[source(path = const_str::concat!(TEST_OUT_DIR, "/secret_config.json"), keyring_entry = "secret")]
#[source(name = "secret_config", keyring_entry = "secret")]
struct SecretConfig {
password: String,
}
Expand Down Expand Up @@ -190,7 +190,7 @@ _For more examples, please refer to the [Example](https://github.com/kingwingfly
<!-- CHANGELOG -->
## Changelog

- v0.1.x -> v0.2.x: A broken change has been made. Heavily refactored with `std::any` and methods from `dependencies injection`.
- v0.1.x -> v0.2.x: A broken change has been made. Heavily refactored with `std::any` and methods from `dependencies injection`.

<p align="right">(<a href="#readme-top">back to top</a>)</p>

Expand Down
65 changes: 42 additions & 23 deletions encrypt-config/src/encrypt_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
use crate::error::{ConfigError, ConfigResult};
use keyring::Entry;
use rsa::{Pkcs1v15Encrypt, RsaPrivateKey, RsaPublicKey};
use std::sync::OnceLock;
use std::{
collections::HashMap,
sync::{OnceLock, RwLock},
};

#[derive(Debug, serde::Serialize, serde::Deserialize)]
#[cfg_attr(feature = "mock", derive(Clone))]
#[cfg_attr(test, derive(PartialEq))]
#[derive(serde::Serialize, serde::Deserialize)]
#[cfg_attr(test, derive(PartialEq, Debug))]
pub struct Encrypter {
priv_key: RsaPrivateKey,
}
Expand All @@ -27,25 +29,39 @@ impl Default for Encrypter {

impl Encrypter {
pub(crate) fn new(secret_name: impl AsRef<str>) -> ConfigResult<&'static Self> {
static ENCRYPTER: OnceLock<ConfigResult<Encrypter>> = OnceLock::new();
ENCRYPTER
.get_or_init(|| {
let entry = keyring_entry(secret_name)?;
match entry.get_password() {
Ok(serded_enc) => serde_json::from_str(&serded_enc)
.map_err(|_| ConfigError::LoadEncrypterFailed),
Err(keyring::Error::NoEntry) => {
let new_enc = Encrypter::default();
entry
.set_password(&serde_json::to_string(&new_enc).unwrap())
.map_err(|_| ConfigError::KeyringError)?;
Ok(new_enc)
}
Err(_) => Err(ConfigError::KeyringError),
}
})
.as_ref()
.map_err(|_| ConfigError::KeyringError)
static ENCRYPTERS: OnceLock<RwLock<HashMap<String, &'static Encrypter>>> = OnceLock::new();
let encrypters = ENCRYPTERS.get_or_init(|| RwLock::new(HashMap::new()));
{
let encrypters = encrypters.read().unwrap();
if let Some(encrypter) = encrypters.get(secret_name.as_ref()) {
return Ok(encrypter);
}
}
// Should create a new encrypter.
{
let mut encrypters = encrypters.write().unwrap();
let new = Box::leak(Box::new(Self::init(secret_name.as_ref())?));
encrypters.insert(secret_name.as_ref().to_owned(), new);
Ok(new)
}
}

/// Init a encrypter. Load if exists, otherwise create and save a new one.
fn init(secret_name: &str) -> ConfigResult<Self> {
let entry = keyring_entry(secret_name)?;
match entry.get_password() {
Ok(serded_enc) => {
serde_json::from_str(&serded_enc).map_err(|_| ConfigError::LoadEncrypterFailed)
}
Err(keyring::Error::NoEntry) => {
let new_enc = Encrypter::default();
entry
.set_password(&serde_json::to_string(&new_enc).unwrap())
.map_err(|_| ConfigError::KeyringError)?;
Ok(new_enc)
}
Err(_) => Err(ConfigError::KeyringError),
}
}

pub(crate) fn encrypt<T: serde::Serialize>(&self, to_encrypt: &T) -> ConfigResult<Vec<u8>> {
Expand Down Expand Up @@ -91,6 +107,7 @@ fn keyring_entry(secret_name: impl AsRef<str>) -> ConfigResult<Entry> {
}

#[cfg(test)]
#[cfg(feature = "mock")]
mod tests {
use super::*;

Expand All @@ -99,5 +116,7 @@ mod tests {
let encrypter1 = Encrypter::new("test").unwrap();
let encrypter2 = Encrypter::new("test").unwrap();
assert_eq!(encrypter1, encrypter2);
let encrypter3 = Encrypter::new("another").unwrap();
assert_ne!(encrypter1, encrypter3);
}
}

0 comments on commit 05ca931

Please sign in to comment.