#config-directory #encryption #default-config #config #secret #api-key #store

encrypt_config

A Rust crate to manage, persist and encrypt your configurations

44 releases (8 stable)

new 1.0.8 Dec 12, 2024
1.0.7 Oct 19, 2024
1.0.6 Sep 16, 2024
0.5.1 Aug 1, 2024
0.1.4 Dec 26, 2023

#125 in Authentication

Download history 176/week @ 2024-08-19 7/week @ 2024-08-26 29/week @ 2024-09-02 17/week @ 2024-09-09 344/week @ 2024-09-16 127/week @ 2024-09-23 30/week @ 2024-09-30 28/week @ 2024-10-07 214/week @ 2024-10-14 38/week @ 2024-10-21 17/week @ 2024-10-28 34/week @ 2024-11-04 13/week @ 2024-11-11 29/week @ 2024-11-18 19/week @ 2024-11-25 9/week @ 2024-12-02

74 downloads per month
Used in gpt_core

MIT license

33KB
346 lines

Contributors Forks Stargazers Issues MIT License


encrypt-config

A rust crate to manage, persist and encrypt your configurations.
Explore the docs »

View Demo · Report Bug · Request Feature

Table of Contents
  1. Import
  2. About The Project
  3. Usage
  4. Changelog
  5. Roadmap
  6. Contributing
  7. License
  8. Contact
  9. Acknowledgments

Import

[dependencies]
encrypt_config = { version = "1.0", features = ["full"] }

[profile.dev.package.num-bigint-dig]
opt-level = 3

Caution

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.

About The Project

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.

(back to top)

Built With

  • Rust
  • Keyring

(back to top)

Usage

Example

# #[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

(back to top)

Changelog

  • 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 as Config dropped automatically.
  • v0.3.x -> v0.4.x: Cache inside Config now behaves more like a native cache. Changes will be saved as ConfigMut dropped automatically.
  • v0.2.x -> v0.3.x: Now, multi-config-sources can be saved and loaded through Config in one go. But add_xx_sources are removed. By the way, one can defined their own sources by implementing Source trait while NormalSource PersistSource SecretSource are still provided.
  • v0.1.x -> v0.2.x: A broken change has been made. Heavily refactored with std::any and methods from dependencies injection.

more detailed changelog

(back to top)

Roadmap

  • Enable protobuf instead of json for better performance

See the open issues for a full list of proposed features (and known issues).

(back to top)

Contributing

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!

  1. Fork the Project
  2. Create your Feature Branch (git checkout -b feature/AmazingFeature)
  3. Commit your Changes (git commit -m 'Add some AmazingFeature')
  4. Push to the Branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

(back to top)

License

Distributed under the MIT License. See LICENSE.txt for more information.

(back to top)

Contact

Louis - 836250617@qq.com

Project Link: https://github.com/kingwingfly/encrypt-config

(back to top)

Dependencies

~2–30MB
~444K SLoC