Merge branch 'change_namespace'

This commit is contained in:
Tom Alexander 2019-10-07 15:51:50 -04:00
commit d0486f8d82
No known key found for this signature in database
GPG Key ID: 76046DA92D2604F7
4 changed files with 91 additions and 16 deletions

View File

@ -165,7 +165,7 @@ pub fn encrypt_value(value: &str, master_key: [u8; 32]) -> EncryptedValue {
hmac.input(&output[..]); hmac.input(&output[..]);
EncryptedValue { EncryptedValue {
ciphertext: output, ciphertext: output,
iv: iv, iv,
mac: hmac.result(), mac: hmac.result(),
} }
} }

View File

@ -4,9 +4,10 @@ use rusqlite::{params, Connection};
use rustc_serialize::base64; use rustc_serialize::base64;
use rustc_serialize::base64::{FromBase64, ToBase64}; use rustc_serialize::base64::{FromBase64, ToBase64};
use std::error::Error; use std::error::Error;
use std::fmt;
use std::path::PathBuf; use std::path::PathBuf;
static DB_INIT_QUERY: &'static str = include_str!("init.sql"); static DB_INIT_QUERY: &str = include_str!("init.sql");
pub struct DbHandle { pub struct DbHandle {
conn: Connection, conn: Connection,
@ -23,6 +24,11 @@ pub struct DbNamespace {
pub name: EncryptedValue, pub name: EncryptedValue,
} }
pub struct Namespace {
pub id: i64,
pub name: String,
}
pub struct DbNote { pub struct DbNote {
pub id: i64, pub id: i64,
pub namespace: DbNamespace, pub namespace: DbNamespace,
@ -44,13 +50,13 @@ impl DbHandle {
pub fn new(db_path: &Option<String>) -> DbHandle { pub fn new(db_path: &Option<String>) -> DbHandle {
let path: PathBuf = db_path let path: PathBuf = db_path
.as_ref() .as_ref()
.map(|path: &String| PathBuf::from(path)) .map(PathBuf::from)
.unwrap_or_else(|| dirs::home_dir().unwrap().join(".foil").to_path_buf()); .unwrap_or_else(|| dirs::home_dir().unwrap().join(".foil").to_path_buf());
let mut conn: Connection = Connection::open(path).unwrap(); let mut conn: Connection = Connection::open(path).unwrap();
let tx = conn.transaction().unwrap(); let tx = conn.transaction().unwrap();
tx.execute_batch(DB_INIT_QUERY).unwrap(); tx.execute_batch(DB_INIT_QUERY).unwrap();
tx.commit().unwrap(); tx.commit().unwrap();
DbHandle { conn: conn } DbHandle { conn }
} }
pub fn get_namespace_id( pub fn get_namespace_id(
@ -87,7 +93,7 @@ impl DbHandle {
) )
.unwrap(); .unwrap();
let rowid: i64 = tx.last_insert_rowid(); let rowid: i64 = tx.last_insert_rowid();
let _ = tx.commit().unwrap(); tx.commit().unwrap();
Ok(rowid) Ok(rowid)
} }
@ -119,7 +125,7 @@ impl DbHandle {
) )
.unwrap(); .unwrap();
let _ = tx.commit().unwrap(); tx.commit().unwrap();
} }
pub fn read_notes(&mut self, master_key: [u8; 32]) -> rusqlite::Result<Vec<Note>> { pub fn read_notes(&mut self, master_key: [u8; 32]) -> rusqlite::Result<Vec<Note>> {
@ -146,13 +152,37 @@ impl DbHandle {
title: note.title.decrypt_to_string(master_key).unwrap(), title: note.title.decrypt_to_string(master_key).unwrap(),
value: note.value.decrypt_to_string(master_key).unwrap(), value: note.value.decrypt_to_string(master_key).unwrap(),
}), }),
Err(e) => return Err(e), Err(e) => Err(e),
}) })
.collect(); .collect();
notes notes
} }
pub fn list_namespaces(&mut self, master_key: [u8; 32]) -> rusqlite::Result<Vec<Namespace>> {
let mut stmt = self
.conn
.prepare("SELECT id, name FROM namespaces")
.unwrap();
let rows = stmt.query_map(params![], |row| {
Ok(DbNamespace {
id: row.get(0)?,
name: row.get(1)?,
})
})?;
let namespaces: Result<Vec<Namespace>, _> = rows
.map(|namespace_result| match namespace_result {
Ok(namespace) => Ok(Namespace {
id: namespace.id,
name: namespace.name.decrypt_to_string(master_key).unwrap(),
}),
Err(e) => Err(e),
})
.collect();
namespaces
}
pub fn get_db_property(&self, name: &str) -> Result<Option<String>, Box<dyn Error>> { pub fn get_db_property(&self, name: &str) -> Result<Option<String>, Box<dyn Error>> {
let mut stmt = self let mut stmt = self
.conn .conn
@ -185,8 +215,14 @@ impl DbHandle {
.map(|option| option.map(|prop| prop.from_base64().unwrap())) .map(|option| option.map(|prop| prop.from_base64().unwrap()))
} }
pub fn set_db_property_bytes(&self, name: &str, value: &Vec<u8>) { pub fn set_db_property_bytes(&self, name: &str, value: &[u8]) {
let b64value: String = value.to_base64(base64::STANDARD); let b64value: String = value.to_base64(base64::STANDARD);
self.set_db_property(name, &b64value); self.set_db_property(name, &b64value);
} }
} }
impl fmt::Display for Namespace {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name)
}
}

View File

@ -5,7 +5,7 @@ use std::iter::FromIterator;
pub fn generate(spec: &str) { pub fn generate(spec: &str) {
let (len, rest) = { let (len, rest) = {
let mut separated = spec.splitn(2, ","); let mut separated = spec.splitn(2, ',');
let len = separated.next().unwrap(); let len = separated.next().unwrap();
let rest = separated.next().unwrap(); let rest = separated.next().unwrap();
(len, rest) (len, rest)

View File

@ -1,3 +1,5 @@
#![warn(clippy::all)]
use crypto::hmac::Hmac; use crypto::hmac::Hmac;
use crypto::mac::{Mac, MacResult}; use crypto::mac::{Mac, MacResult};
use crypto::sha2::Sha256; use crypto::sha2::Sha256;
@ -14,7 +16,7 @@ pub mod crypt;
pub mod db; pub mod db;
pub mod generate; pub mod generate;
static USAGE: &'static str = " static USAGE: &str = "
foil foil
Usage: Usage:
@ -168,21 +170,58 @@ fn shell_generate() {
generate::generate(&spec); generate::generate(&spec);
} }
fn shell(db_conn: &mut db::DbHandle, master_key: [u8; 32], namespace: &str) { fn change_namespace(db_conn: &mut db::DbHandle, master_key: [u8; 32]) -> String {
let namespaces = db_conn.list_namespaces(master_key).unwrap();
let selection = Select::with_theme(&ColorfulTheme::default())
.with_prompt("Change Namespace")
.default(0)
.items(&namespaces[..])
.interact()
.unwrap();
namespaces[selection].name.to_owned()
}
fn create_namespace(db_conn: &mut db::DbHandle, master_key: [u8; 32]) -> String {
let namespace: String = Input::with_theme(&ColorfulTheme::default())
.with_prompt("Namespace")
.default("main".to_owned())
.interact()
.unwrap();
namespace
}
fn shell(db_conn: &mut db::DbHandle, master_key: [u8; 32], _namespace: &str) {
let mut namespace: String = _namespace.to_owned();
loop { loop {
let selections = &["get", "list", "set", "generate", "exit"]; let selections = &[
"get",
"list",
"set",
"generate",
"change namespace",
"create namespace",
"exit",
];
let main_menu_prompt = format!("Main Menu ({})", namespace);
let selection = Select::with_theme(&ColorfulTheme::default()) let selection = Select::with_theme(&ColorfulTheme::default())
.with_prompt("Main Menu") .with_prompt(&main_menu_prompt)
.default(0) .default(0)
.items(&selections[..]) .items(&selections[..])
.interact() .interact()
.unwrap(); .unwrap();
match selections[selection] { match selections[selection] {
"get" => get(db_conn, master_key, namespace), "get" => get(db_conn, master_key, &namespace),
"list" => list(db_conn, master_key, namespace), "list" => list(db_conn, master_key, &namespace),
"set" => set(db_conn, master_key, namespace), "set" => set(db_conn, master_key, &namespace),
"generate" => shell_generate(), "generate" => shell_generate(),
"change namespace" => {
namespace = change_namespace(db_conn, master_key);
}
"create namespace" => {
namespace = create_namespace(db_conn, master_key);
}
"exit" => break, "exit" => break,
_ => panic!("Unrecognized command"), _ => panic!("Unrecognized command"),
}; };