-
Vasili Novikov authored51519271
use crate::api_model::AuthKey;
use crate::api_model::Bulk;
use crate::api_model::CreateEdge;
use crate::api_model::CreateItem;
use crate::api_model::GetEdges;
use crate::api_model::GetFile;
use crate::api_model::PayloadWrapper;
use crate::api_model::Search;
use crate::api_model::UpdateItem;
use crate::command_line_interface;
use crate::command_line_interface::CliOptions;
use crate::constants;
use crate::database_api;
use crate::database_migrate_refinery;
use crate::error::Error;
use crate::error::ErrorContext;
use crate::error::Result;
use crate::file_api;
use crate::internal_api;
use crate::plugin_auth_crypto;
use crate::plugin_auth_crypto::DatabaseKey;
use lazy_static::lazy_static;
use log::error;
use log::info;
use rusqlite::Connection;
use rusqlite::Transaction;
use serde_json::Value;
use sha2::digest::generic_array::typenum::U32;
use sha2::digest::generic_array::GenericArray;
use sha2::Digest;
use std::collections::HashSet;
use std::ops::Deref;
use std::path::PathBuf;
use std::sync::RwLock;
use warp::http::status::StatusCode;
use warp::hyper::body::Bytes;
//
// Items API:
//
pub fn get_item(
owner: String,
init_db: &RwLock<HashSet<String>>,
body: Bytes,
) -> Result<Vec<Value>> {
let body = &mut serde_json::Deserializer::from_slice(body.deref());
let body: PayloadWrapper<String> = serde_path_to_error::deserialize(body)?;
let auth = body.auth;
let payload = body.payload;
let database_key = auth_to_database_key(auth)?;
let mut conn: Connection = check_owner_and_initialize_db(&owner, init_db, &database_key)?;
in_transaction(&mut conn, |tx| {
let schema = database_api::get_schema(tx)?;
internal_api::get_item_tx(tx, &schema, &payload)
})
}
pub fn create_item(
owner: String,
init_db: &RwLock<HashSet<String>>,
body: Bytes,
cli: &CliOptions,
) -> Result<String> {
let body = &mut serde_json::Deserializer::from_slice(body.deref());
let body: PayloadWrapper<CreateItem> = serde_path_to_error::deserialize(body)?;
let auth = body.auth;
let payload = body.payload;
let database_key = auth_to_database_key(auth)?;
let mut conn: Connection = check_owner_and_initialize_db(&owner, init_db, &database_key)?;
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
in_transaction(&mut conn, |tx| {
let schema = database_api::get_schema(tx)?;
internal_api::create_item_tx(tx, &schema, payload, &owner, cli, &database_key)
})
}
pub fn update_item(owner: String, init_db: &RwLock<HashSet<String>>, body: Bytes) -> Result<()> {
let body = &mut serde_json::Deserializer::from_slice(body.deref());
let body: PayloadWrapper<UpdateItem> = serde_path_to_error::deserialize(body)?;
let auth = body.auth;
let payload = body.payload;
let database_key = auth_to_database_key(auth)?;
let mut conn: Connection = check_owner_and_initialize_db(&owner, init_db, &database_key)?;
in_transaction(&mut conn, |tx| {
let schema = database_api::get_schema(tx)?;
internal_api::update_item_tx(tx, &schema, &payload.id, payload.fields)
})
}
pub fn bulk(
owner: String,
init_db: &RwLock<HashSet<String>>,
body: Bytes,
cli: &CliOptions,
) -> Result<Value> {
let body = &mut serde_json::Deserializer::from_slice(body.deref());
let body: PayloadWrapper<Bulk> = serde_path_to_error::deserialize(body)?;
let auth = body.auth;
let payload = body.payload;
let database_key = auth_to_database_key(auth)?;
let mut conn: Connection = check_owner_and_initialize_db(&owner, init_db, &database_key)?;
in_transaction(&mut conn, |tx| {
let schema = database_api::get_schema(tx)?;
internal_api::bulk_tx(tx, &schema, payload, &owner, cli, &database_key)
})
}
pub fn delete_item(owner: String, init_db: &RwLock<HashSet<String>>, body: Bytes) -> Result<()> {
let body = &mut serde_json::Deserializer::from_slice(body.deref());
let body: PayloadWrapper<String> = serde_path_to_error::deserialize(body)?;
let auth = body.auth;
let payload = body.payload;
let database_key = auth_to_database_key(auth)?;
let mut conn: Connection = check_owner_and_initialize_db(&owner, init_db, &database_key)?;
in_transaction(&mut conn, |tx| {
let schema = database_api::get_schema(tx)?;
internal_api::delete_item_tx(tx, &schema, &payload)
})
}
pub fn create_edge(
owner: String,
init_db: &RwLock<HashSet<String>>,
body: Bytes,
) -> Result<String> {
let body = &mut serde_json::Deserializer::from_slice(body.deref());
let body: PayloadWrapper<CreateEdge> = serde_path_to_error::deserialize(body)?;
let auth = body.auth;
let payload = body.payload;
let database_key = auth_to_database_key(auth)?;
let mut conn: Connection = check_owner_and_initialize_db(&owner, init_db, &database_key)?;
in_transaction(&mut conn, |tx| internal_api::create_edge(tx, payload))
}
pub fn get_edges(
owner: String,
init_db: &RwLock<HashSet<String>>,
body: Bytes,
) -> Result<Vec<Value>> {
let body = &mut serde_json::Deserializer::from_slice(body.deref());
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
let body: PayloadWrapper<GetEdges> = serde_path_to_error::deserialize(body)?;
let auth = body.auth;
let payload = body.payload;
let database_key = auth_to_database_key(auth)?;
let mut conn: Connection = check_owner_and_initialize_db(&owner, init_db, &database_key)?;
in_transaction(&mut conn, |tx| {
let schema = database_api::get_schema(tx)?;
internal_api::get_edges(tx, payload, &schema)
})
}
pub fn search(owner: String, init_db: &RwLock<HashSet<String>>, body: Bytes) -> Result<Vec<Value>> {
let body = &mut serde_json::Deserializer::from_slice(body.deref());
let body: PayloadWrapper<Search> = serde_path_to_error::deserialize(body)?;
let auth = body.auth;
let payload = body.payload;
let database_key = auth_to_database_key(auth)?;
let mut conn: Connection = check_owner_and_initialize_db(&owner, init_db, &database_key)?;
in_transaction(&mut conn, |tx| {
let schema = database_api::get_schema(tx)?;
internal_api::search(tx, &schema, payload)
})
}
//
// Files API:
//
pub fn upload_file(
owner: String,
init_db: &RwLock<HashSet<String>>,
database_key: String,
expected_sha256: String,
body: &[u8],
) -> Result<()> {
// auth_wrapper and the HTTP request will be re-done for more proper structure later
let database_key = DatabaseKey::from(database_key)?;
let mut conn: Connection = check_owner_and_initialize_db(&owner, init_db, &database_key)?;
conn.execute_batch("SELECT 1 FROM items;")?; // Check DB access
in_transaction(&mut conn, |tx| {
file_api::upload_file(tx, &owner, &expected_sha256, body)
})
}
pub fn get_file(owner: String, init_db: &RwLock<HashSet<String>>, body: Bytes) -> Result<Vec<u8>> {
let body = &mut serde_json::Deserializer::from_slice(body.deref());
let body: PayloadWrapper<GetFile> = serde_path_to_error::deserialize(body)?;
let auth = body.auth;
let payload = body.payload;
let database_key = auth_to_database_key(auth)?;
let mut conn: Connection = check_owner_and_initialize_db(&owner, init_db, &database_key)?;
conn.execute_batch("SELECT 1 FROM items;")?; // Check DB access
in_transaction(&mut conn, |tx| {
file_api::get_file(tx, &owner, &payload.sha256)
})
}
//
// helper functions:
//
fn in_transaction<T, F: FnOnce(&Transaction) -> Result<T>>(
conn: &mut Connection,
func: F,
) -> Result<T> {
let tx = conn.transaction()?;
let result = func(&tx)?; // Note that this function needs to exit early in case of error
tx.commit()?;
Ok(result)
}
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
fn auth_to_database_key(auth: AuthKey) -> Result<DatabaseKey> {
match auth {
AuthKey::ClientAuth(c) => DatabaseKey::from(c.database_key),
AuthKey::PluginAuth(p) => plugin_auth_crypto::extract_database_key(&p),
}
}
/// Two methods combined into one to prevent creating a database connection without owner checks.
/// As additional failsafe to the fact that non-owners don't have the database key.
fn check_owner_and_initialize_db(
owner: &str,
init_db: &RwLock<HashSet<String>>,
database_key: &DatabaseKey,
) -> Result<Connection> {
check_owner(owner)?;
initialize_db(owner, init_db, database_key)
}
fn initialize_db(
owner: &str,
init_db: &RwLock<HashSet<String>>,
database_key: &DatabaseKey,
) -> Result<Connection> {
let database_path = format!("{}{}", &owner, constants::DATABASE_SUFFIX);
let database_path = PathBuf::from(constants::DATABASE_DIR).join(database_path);
let mut conn = Connection::open(database_path)
.context_str("Failed to open database file (does Pod have filesystem access?)")?;
DatabaseKey::execute_sqlite_pragma(database_key, &conn)
.context_str("Failed to open the database file (did databaseKey change between runs?)")?;
conn.execute_batch("PRAGMA foreign_keys = ON;")?;
let mut init_db = init_db.write()?;
if !init_db.contains(owner) {
database_migrate_refinery::migrate(&mut conn)?;
init_db.insert(owner.to_string());
}
Ok(conn)
}
fn allowed_owner_hashes_fn(cli_options: &CliOptions) -> HashSet<Vec<u8>> {
let owners: &str = &cli_options.owners;
let mut result = HashSet::new();
for owner in owners.split(',') {
let hexed = hex::decode(owner).unwrap_or_else(|err| {
error!(
"Pod owners are invalid, failed to decode hex {}, {}",
owner, err
);
std::process::exit(1);
});
result.insert(hexed);
}
result
}
lazy_static! {
static ref ALLOWED_OWNER_HASHES: HashSet<Vec<u8>> =
allowed_owner_hashes_fn(&command_line_interface::PARSED);
}
fn hash_of_hex(hex_string: &str) -> Result<GenericArray<u8, U32>> {
let hex_string: Vec<u8> = hex::decode(hex_string)?;
let mut hash = sha2::Sha256::new();
hash.update(&hex_string);
Ok(hash.finalize())
}
fn check_owner(possible_owner: &str) -> Result<()> {
if command_line_interface::PARSED.owners == "ANY" {
return Ok(());
281282283284285286287288289290291292293294295296297
};
let possible_hash = hash_of_hex(possible_owner)?;
if ALLOWED_OWNER_HASHES.contains(possible_hash.deref()) {
Ok(())
} else {
info!(
"Denying unexpected owner {} with hash {}",
possible_owner,
hex::encode(possible_hash)
);
Err(Error {
code: StatusCode::FORBIDDEN,
msg: "Unexpected owner".to_string(),
})
}
}