crypt-config 0.2.2

A crypt module that encrypt/decrypt json data based on the configuration. This might be used as a layer before saving data in Mongo DB or after fetching the data from the DB in order to decrypt them.
Documentation
extern crate serde;
extern crate serde_json;

use std::collections::HashMap;

use serde::{Deserialize, Serialize};

use crate::config::{Config, HasherConfig, HasherData};
use crate::error::{CryptError, CryptResult};

#[derive(Serialize, Deserialize)]
struct Cfg {
  algorithm: String,
  rounds:    u32,
  salt:      String
}

#[derive(Serialize, Deserialize)]
struct Hasher {
  configurations: HashMap<String, Cfg>
}

#[derive(Serialize, Deserialize)]
struct Hashers {
  hashers: HashMap<String, Hasher>
}

#[allow(dead_code)]
type HasherLoader = fn(&mut Config, &str) -> CryptResult<()>;

#[allow(dead_code)]
pub fn get_hasher_loader(version: &str) -> HasherLoader {
  if version.starts_with("1.0") {
    hasher_loader_1_0
  } else {
    hasher_loader_unknown
  }
}

#[allow(dead_code)]
fn hasher_loader_unknown(_config: &mut Config, _data: &str) -> CryptResult<()> {
  Err(CryptError::UnsupportedConfigVersion())
}

#[allow(dead_code)]
fn hasher_loader_1_0(config: &mut Config, data: &str) -> CryptResult<()> {
  let hashers: Hashers = serde_json::from_str(data)?;

  for (field, hasher) in hashers.hashers {
    if !config.is_hasher_exist(&field) {
      config.insert_hasher(field.clone(), Box::new(HasherData::new()));
    }

    for (version, cfg) in hasher.configurations {
      if config.is_hasher_version_exist(&field, &version) {
        return Err(CryptError::DuplicationHasherConfigVersion(version));
      }

      if cfg.rounds <= 0 {
        return Err(CryptError::BadIterationCount());
      }

      is_algorithm_supported(&cfg.algorithm)?;

      let salt = base64::decode(&cfg.salt)?;

      config.add_hasher_version(&field, &version, Box::new(HasherConfig::new(cfg.algorithm, cfg.rounds, salt)));
    }
  }

  Ok(())
}

fn is_algorithm_supported(algorithm: &str) -> CryptResult<()> {
  match algorithm {
    "bcrypt" => Ok(()),
    "pbkdf2" => Ok(()),
    _ => Err(CryptError::UnsupportedHasherAlgorithm(algorithm.to_string()))
  }
}

#[cfg(test)]
mod tests {
  use std::fs;
  use std::path::PathBuf;

  use crate::config::get_config_version;
  use crate::config::Config;

  use super::get_hasher_loader;

  fn get_test_data_path(fname: &str) -> PathBuf {
    let mut path = PathBuf::from(file!());
    path.pop();
    path.pop();
    path.pop();
    path.pop();
    path.push("test_data");
    path.push(fname);
    path
  }

  #[test]
  fn load_configuration() {
    let mut config = Config::new();

    let path = get_test_data_path("config.json");
    let content = fs::read_to_string(path).unwrap();

    let version = get_config_version(&content).unwrap();
    let hasher_loader = get_hasher_loader(&version);

    hasher_loader(&mut config, &content).unwrap();

    assert!(config.is_hasher_exist("email"));
    assert_eq!("2.0", config.get_hasher_latest_version("email"));
    assert!(config.is_hasher_version_exist("email", "1.0"));
    assert!(config.is_hasher_version_exist("email", "2.0"));
  }
}