Skip to content

Commit

Permalink
Auto-create encryption keys for projects in init
Browse files Browse the repository at this point in the history
  • Loading branch information
jkmassel committed Nov 28, 2020
1 parent 67b8a99 commit 164d0a3
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 25 deletions.
17 changes: 17 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ console = "0.13.0"
dialoguer = "0.7.1"
indicatif = "0.15.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_json = {version = "1.0", features = ["preserve_order"]}

thiserror = "1.0"
ring = "0.16.18"
base64 = "0.13.0"
Expand Down
34 changes: 15 additions & 19 deletions src/configure.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,25 +23,6 @@ impl ConfigurationFile {
self == &ConfigurationFile::default()
}

pub fn get_encryption_key(&self) -> String {
let keys_file_path = crate::fs::find_keys_file().unwrap();

debug!("Reading keys from {:?}", keys_file_path);

let file = std::fs::File::open(keys_file_path).expect("keys file is not readable");

let json: serde_json::Value =
serde_json::from_reader(file).expect("keys.json should be valid JSON");

let encryption_key = json
.get(&self.project_name)
.expect("keys.json does not contain an encryption key for this project")
.as_str()
.expect("The encryption key for this project is invalid");

String::from(encryption_key)
}

fn needs_project_name(&self) -> bool {
self.project_name == ""
}
Expand Down Expand Up @@ -83,6 +64,15 @@ pub enum ConfigureError {

#[error("An encrypted file is missing – unable to apply secrets to project. Run `configure update` to fix this")]
EncryptedFileMissing,

#[error("Unable to read keys.json file in your secrets repo")]
KeysFileCannotBeRead,

#[error("keys.json file in your secrets repo is not valid json")]
KeysFileIsNotValidJSON,

#[error("That project key is not defined in keys.json")]
MissingProjectKey
}

#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
Expand Down Expand Up @@ -262,7 +252,13 @@ pub fn setup_configuration(mut configuration: ConfigurationFile) {

info!("Writing changes to .configure");


save_configuration(&configuration).expect("Unable to save configure file");

// Create a key in `keys.json` for the project if one doesn't already exist
if read_encryption_key(&configuration).unwrap() == None {
generate_encryption_key(&configuration).expect("Unable to automatically generate an encryption key for this project");
}
}

fn prompt_for_project_name_if_needed(mut configuration: ConfigurationFile) -> ConfigurationFile {
Expand Down
66 changes: 61 additions & 5 deletions src/fs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ use std::fs::{create_dir_all, remove_file, rename, File};
use std::io::{BufReader, Error, Read, Write};
use std::path::PathBuf;

#[macro_use]
use serde_json::json;

/// Find the .configure file in the current project
pub fn find_configure_file() -> PathBuf {
let project_root = find_project_root();
Expand Down Expand Up @@ -40,7 +43,7 @@ pub fn find_keys_file() -> Result<PathBuf, ConfigureError> {
"No keys file found at: {:?}. Creating one for you",
keys_file_path
);
create_file_with_contents(&keys_file_path, "{}").expect(
write_file_with_contents(&keys_file_path, "{}").expect(
"There is no `keys.json` file in your secrets repository, and creating one failed",
);
}
Expand Down Expand Up @@ -108,11 +111,58 @@ pub fn save_configuration(configuration: &ConfigurationFile) -> Result<(), Error
Ok(())
}

pub fn read_encryption_key(configuration: &ConfigurationFile) -> Result<Option<String>, ConfigureError> {
let keys_file_path = find_keys_file()?;

debug!("Reading keys from {:?}", keys_file_path);

let file = match File::open(keys_file_path) {
Ok(file) => file,
Err(_) => return Err(ConfigureError::KeysFileCannotBeRead),
};

let json: serde_json::Value = match serde_json::from_reader(file) {
Ok(json) => json,
Err(_) => return Err(ConfigureError::KeysFileIsNotValidJSON),
};

match json.get(&configuration.project_name) {
Some(key) => return Ok(Some(String::from(key.as_str().unwrap()))),
None => return Ok(None),
};
}

pub fn generate_encryption_key(configuration: &ConfigurationFile) -> Result<(), ConfigureError> {
let keys_file_path = find_keys_file()?;

let file = match File::open(&keys_file_path) {
Ok(file) => file,
Err(_) => return Err(ConfigureError::KeysFileCannotBeRead),
};

let mut json: serde_json::Value = match serde_json::from_reader(file) {
Ok(json) => json,
Err(_) => return Err(ConfigureError::KeysFileIsNotValidJSON),
};

json[&configuration.project_name] = json!("Foo!");

write_file_with_contents(&keys_file_path, &serde_json::to_string_pretty(&json).unwrap())?;

Ok(())
}

pub fn decrypt_files_for_configuration(
configuration: &ConfigurationFile,
) -> Result<(), ConfigureError> {
let project_root = find_project_root();
let encryption_key = configuration.get_encryption_key();
let encryption_key = match read_encryption_key(configuration) {
Ok(key) => match key {
Some(value) => value,
None => return Err(ConfigureError::MissingProjectKey),
},
Err(err) => return Err(err),
};

for file in &configuration.files_to_copy {
let source = project_root.join(&file.get_encrypted_destination());
Expand Down Expand Up @@ -174,10 +224,16 @@ pub fn decrypt_files_for_configuration(

pub fn write_encrypted_files_for_configuration(
configuration: &ConfigurationFile,
) -> Result<(), Error> {
) -> Result<(), ConfigureError> {
let project_root = find_project_root();
let secrets_root = find_secrets_repo().unwrap();
let encryption_key = configuration.get_encryption_key();
let encryption_key = match read_encryption_key(configuration) {
Ok(key) => match key {
Some(value) => value,
None => return Err(ConfigureError::MissingProjectKey),
},
Err(err) => return Err(err),
};

for file in &configuration.files_to_copy {
let source = &secrets_root.join(&file.source);
Expand All @@ -198,7 +254,7 @@ pub fn write_encrypted_files_for_configuration(
}

/// Helper method to create an empty file
fn create_file_with_contents(path: &PathBuf, contents: &str) -> Result<(), std::io::Error> {
fn write_file_with_contents(path: &PathBuf, contents: &str) -> Result<(), std::io::Error> {
let mut file = File::create(path)?;
file.write_all(contents.as_bytes())?;
Ok(())
Expand Down

0 comments on commit 164d0a3

Please sign in to comment.