generated from pure_sagacity/rust-starter
added database stuff
added clap arguments added the gen_or_get_key func in lib git ignore for .env migrations
This commit is contained in:
+12
-2
@@ -7,8 +7,18 @@ description = "An opensource secrets management and distribution platform"
|
||||
license = "GLP-3.0"
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "1.52.3", features = ["full"] }
|
||||
diesel = { version = "2.2.0", features = [
|
||||
"sqlite",
|
||||
"returning_clauses_for_sqlite_3_35",
|
||||
"chrono",
|
||||
] }
|
||||
clap = { version = "4.6.1", features = ["derive", "cargo"] }
|
||||
tokio = { version = "1.52.3", features = ["full"] }
|
||||
crypto = { path = "../crypto" }
|
||||
git-version = "0.3"
|
||||
keyring = "4.1.4"
|
||||
colored = "3.1.1"
|
||||
git-version = "0.3"
|
||||
chrono = "0.4.45"
|
||||
dotenvy = "0.15"
|
||||
uuid = "1.23.5"
|
||||
hex = "0.4"
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# For documentation on how to configure this file,
|
||||
# see https://diesel.rs/guides/configuring-diesel-cli
|
||||
|
||||
[print_schema]
|
||||
file = "src/db/schema.rs"
|
||||
custom_type_derives = ["diesel::query_builder::QueryId", "Clone"]
|
||||
|
||||
[migrations_directory]
|
||||
dir = "migrations"
|
||||
Binary file not shown.
@@ -0,0 +1,2 @@
|
||||
DROP TABLE IF EXISTS secrets;
|
||||
DROP TABLE IF EXISTS projects;
|
||||
@@ -0,0 +1,19 @@
|
||||
CREATE TABLE projects (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE secrets (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
project_id TEXT NOT NULL,
|
||||
config TEXT NOT NULL,
|
||||
secret BLOB NOT NULL,
|
||||
nonce BLOB NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
||||
|
||||
UNIQUE (project_id, config, name)
|
||||
);
|
||||
@@ -0,0 +1,14 @@
|
||||
use diesel::prelude::*;
|
||||
use dotenvy::dotenv;
|
||||
use std::env;
|
||||
|
||||
pub mod models;
|
||||
pub mod schema;
|
||||
|
||||
pub fn establish_connection() -> SqliteConnection {
|
||||
dotenv().ok();
|
||||
|
||||
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
|
||||
SqliteConnection::establish(&database_url)
|
||||
.unwrap_or_else(|_| panic!("Error connecting to {}", database_url))
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
use crate::db::schema::{projects, secrets};
|
||||
use chrono::NaiveDateTime;
|
||||
use diesel::prelude::*;
|
||||
|
||||
#[derive(Debug, Queryable, Selectable)]
|
||||
#[diesel(table_name = projects)]
|
||||
#[diesel(check_for_backend(diesel::sqlite::Sqlite))]
|
||||
pub struct Project {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub created_at: NaiveDateTime,
|
||||
}
|
||||
|
||||
#[derive(Debug, Queryable, Selectable)]
|
||||
#[diesel(table_name = secrets)]
|
||||
#[diesel(check_for_backend(diesel::sqlite::Sqlite))]
|
||||
pub struct Secret {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub project_id: String,
|
||||
pub config: String,
|
||||
pub secret: Vec<u8>,
|
||||
pub nonce: Vec<u8>,
|
||||
pub created_at: NaiveDateTime,
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// @generated automatically by Diesel CLI.
|
||||
|
||||
diesel::table! {
|
||||
projects (id) {
|
||||
id -> Text,
|
||||
name -> Text,
|
||||
created_at -> Timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
secrets (id) {
|
||||
id -> Text,
|
||||
name -> Text,
|
||||
project_id -> Text,
|
||||
config -> Text,
|
||||
secret -> Binary,
|
||||
nonce -> Binary,
|
||||
created_at -> Timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::joinable!(secrets -> projects (project_id));
|
||||
|
||||
diesel::allow_tables_to_appear_in_same_query!(projects, secrets,);
|
||||
@@ -1,4 +1,7 @@
|
||||
use clap::{Args, Parser, Subcommand};
|
||||
use keyring::Entry;
|
||||
use std::error::Error;
|
||||
mod db;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[clap(
|
||||
@@ -54,3 +57,39 @@ pub enum ConfigCommands {
|
||||
Create { project: String, name: String },
|
||||
Delete { project: String, name: String },
|
||||
}
|
||||
|
||||
pub fn gen_or_get_key() -> Result<crypto::Key, Box<dyn Error>> {
|
||||
let entry = Entry::new("harbor", "encryption-key")?;
|
||||
|
||||
match entry.get_password() {
|
||||
Ok(hex_string) => {
|
||||
let key_bytes = hex::decode(hex_string)?;
|
||||
|
||||
if key_bytes.len() != 32 {
|
||||
return Err("Retrieved key length is invalid (must be 32 bytes)".into());
|
||||
}
|
||||
|
||||
Ok(key_bytes
|
||||
.as_slice()
|
||||
.try_into()
|
||||
.map_err(|_| "Invalid key length")?)
|
||||
}
|
||||
|
||||
Err(err) => {
|
||||
if is_key_not_found(&err) {
|
||||
let key = crypto::helper::gen_key();
|
||||
|
||||
let hex_string = hex::encode(key.as_slice());
|
||||
entry.set_password(&hex_string)?;
|
||||
|
||||
Ok(key)
|
||||
} else {
|
||||
Err(Box::new(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_key_not_found(err: &keyring::Error) -> bool {
|
||||
matches!(err, keyring::Error::NoEntry)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user