A rust crate to manage, persist and encrypt your configurations.
Explore the docs »
View Demo
·
Report Bug
·
Request Feature
Table of Contents
[dependencies]
encrypt_config = { version = "1.0", features = ["full"] }
[profile.dev.package.num-bigint-dig]
opt-level = 3
On linux, the keys will expired or removed after reboot or long-term unused. So the rsa private key will be lost
which leads this crate unable to decrypt the encrypted config file. So we recommend to easily use PersistSource
instead of SecretSource
on linux even other platforms, they are actually safe enough.
Sometimes, we need to store config in our application that we don't want to expose to the public. For example, the database password, the api key, etc.
One solution is to store them in the OS' secret manager, such as Keychain
on macOS, Credential Manager
on Windows, libsecret
on Linux.
However, they usually have limitation on the secret length. For example, Keychain
only allows 255 bytes for the secret, Credential Manager
is even shorter. So we can't store a long secret in it.
Another solution is to store the secret in a file and encrypt it with a rsa public key, and store the private key in the OS' secret manager. This is what this crate does.
This crate provides 3 ways to manage your config:
- [
NormalSource
]: A normal source, not persisted or encrypted - [
PersistSource
]: A source that will be persisted to local file, not encrypted - [
SecretSource
]: A source that will be persisted to local file and encrypted
This crate also has some optional features:
persist
: If enabled, you can use the [PersistSource
] trait.secret
: If enabled, you can use the [PersistSource
] and the [SecretSource
] trait.mock
: If enabled, you can use the mock for testing, which will not use the OS' secret manager.default_config_dir
: If enabled, the default config dir will be used. Implemented through dirs.
Moreover, as development progresses, a memory cache design is added for persistent data access speeding up. This leads this crate actually behaving more like bevy_ecs's resource system (or dependencies injecion with only args retrieving implemented). The cache is released as an independent crate rom_cache.
The Config
in this crate is a wrapper of rom_cache::Cache
, only if the config is modified and marked dirty will the data be persisted to the storage.
- Rust
- Keyring
# #[cfg(all(feature = "full", feature = "mock", feature = "default_config_dir"))]
# {
use encrypt_config::{Config, NormalSource, PersistSource, SecretSource};
use serde::{Deserialize, Serialize};
use std::sync::OnceLock;
#[derive(Default, NormalSource)]
struct NormalConfig {
count: usize,
}
#[derive(Default, Serialize, Deserialize, PersistSource)]
#[source(name = "persist_config.json")]
struct PersistConfig {
name: String,
age: usize,
}
#[derive(Default, Serialize, Deserialize, SecretSource)]
#[source(name = "secret_config", keyring_entry = "secret")]
struct SecretConfig {
password: String,
}
{
// Here we have 2 kinds of config at the same time at most, so N is 2
let cfg: Config<2> = Config::default();
{
let normal = cfg.get::<NormalConfig>();
// default value
assert_eq!(normal.count, 0);
}
{
let mut normal = cfg.get_mut::<NormalConfig>();
normal.count = 42;
assert_eq!(normal.count, 42);
}
{
let mut persist = cfg.get_mut::<PersistConfig>();
persist.name = "Louis".to_string();
persist.age = 22;
let mut secret = cfg.get_mut::<SecretConfig>();
secret.password = "123456".to_string();
}
// Changes will be saved automatically as Config dropped
}
{
// Assume this is a new config in the next start
// Here we have 1 kinds of config at the same time at most, so N is 1
let cfg: Config<1> = Config::default();
{
// normal config will not be saved
assert_eq!(cfg.get::<NormalConfig>().count, 0);
// persist config will be saved
assert_eq!(cfg.get::<PersistConfig>().name, "Louis");
// secret config will be encrypted
assert_eq!(cfg.get::<SecretConfig>().password, "123456");
}
// The secret config file should not be able to load directly
let encrypted_file = std::fs::File::open(SecretConfig::path()).unwrap();
assert!(serde_json::from_reader::<_, SecretConfig>(encrypted_file).is_err());
}
# }
Surely, you can also easily use methods provided by PersistSource
and SecretSource
to load and save the config manually, instead of the complex Config
cache.
For more examples, please refer to the tests, Example or Documentation
- 0.5.x -> 1.0.x: no feature difference between linux and others; user define cache size
- 0.4.x -> 0.5.x: Cache inside
Config
now behaves totally like a native cache. Changes will be saved asConfig
dropped automatically. - v0.3.x -> v0.4.x: Cache inside
Config
now behaves more like a native cache. Changes will be saved asConfigMut
dropped automatically. - v0.2.x -> v0.3.x: Now, multi-config-sources can be saved and loaded through
Config
in one go. Butadd_xx_source
s are removed. By the way, one can defined their own sources by implementingSource
trait whileNormalSource
PersistSource
SecretSource
are still provided. - v0.1.x -> v0.2.x: A broken change has been made. Heavily refactored with
std::any
and methods fromdependencies injection
.
- Enable protobuf instead of json for better performance
See the open issues for a full list of proposed features (and known issues).
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.
If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". Don't forget to give the project a star! Thanks again!
- Fork the Project
- Create your Feature Branch (
git checkout -b feature/AmazingFeature
) - Commit your Changes (
git commit -m 'Add some AmazingFeature'
) - Push to the Branch (
git push origin feature/AmazingFeature
) - Open a Pull Request
Distributed under the MIT License. See LICENSE.txt
for more information.
Louis - [email protected]
Project Link: https://github.com/kingwingfly/encrypt-config