From a34aedfda6d9c39d6d38c91f1ae9a342f98ca0aa Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Mon, 2 Jul 2018 23:01:46 +0200 Subject: Throwing stuff against walls --- lockchain-core/src/errors.rs | 28 +- lockchain-core/src/users/auth.rs | 9 +- lockchain-http/src/handlers.rs | 486 +++++++++++++++++---------------- lockchain-http/src/lib.rs | 88 +++--- lockchain-http/src/models/mod.rs | 20 +- lockchain-http/src/models/responses.rs | 20 +- lockchain-http/src/state.rs | 6 +- 7 files changed, 355 insertions(+), 302 deletions(-) diff --git a/lockchain-core/src/errors.rs b/lockchain-core/src/errors.rs index 18f23f3..6a5b632 100644 --- a/lockchain-core/src/errors.rs +++ b/lockchain-core/src/errors.rs @@ -8,9 +8,12 @@ //! turning a `VaultAlreadyExists` failure to //! a `FailedInitialise`. +use std::error; +use std::fmt::{Display, Formatter, Result}; + /// A collection of common error codes that can be /// returned by lockchain API functions -#[derive(Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize)] pub enum Error { /// Creating a vault where one already exists VaultAlreadyExists, @@ -35,3 +38,26 @@ pub enum Error { // #[hidden_docs] __NonExhaustive, } + +impl error::Error for Error {} + +impl Display for Error { + fn fmt(&self, f: &mut Formatter) -> Result { + write!( + f, + "{}", + match self { + Error::VaultAlreadyExists => "Vault already exists", + Error::InvalidPath => "Path invalid", + Error::InvalidName => "Name invalid", + Error::InvalidCryptoLayer => "Cryptography layer incompatible", + Error::FailedCrypto => "Failed cryptographic operation", + Error::FailedSelfTest => "Failed self text", + Error::FailedLoading => "Failed to load", + Error::FailedInitalise => "Failed to initialise", + Error::FailedCreation => "Failed to create", + _ => "Unknown failure", + } + ) + } +} diff --git a/lockchain-core/src/users/auth.rs b/lockchain-core/src/users/auth.rs index ad25757..ec2aea7 100644 --- a/lockchain-core/src/users/auth.rs +++ b/lockchain-core/src/users/auth.rs @@ -6,7 +6,7 @@ // use nix::sys::wait::*; // use nix::unistd::{fork, ForkResult}; -use pam_auth::{self, Authenticator, PamError, Result as PamResult}; +use pam_auth::Authenticator; #[derive(Debug)] pub enum AuthError { @@ -17,22 +17,21 @@ pub enum AuthError { } /// Simple way to authenticate a user for administrative actions -/// +/// /// Attempts to open a PAM session for the provided user/pw combination /// then attempts to write to a tmpfile in the lockchain config directory. /// If this action is successful the user is either the same running the /// lockchain server *or* has access to the file via group permissions. -/// +/// /// This does rely on `lockchain` being properly configured on the server /// i.e. not using public permissions for the configuration/ state directory. -/// +/// /// **Note** as of `lockchain v0.9.0` this function has not been implemented /// yet due to issues in the `pam-auth` dependency. #[allow(unused_variables)] pub fn pam_authenticate(username: &str, password: &str) -> Result<(), AuthError> { // Err(AuthError::FailedPAM) - // match fork().map_err(|_| AuthError::FailedFork)? { // ForkResult::Parent { child } => { // waitpid(child, None).unwrap(); diff --git a/lockchain-http/src/handlers.rs b/lockchain-http/src/handlers.rs index 2b220a2..354b4c9 100644 --- a/lockchain-http/src/handlers.rs +++ b/lockchain-http/src/handlers.rs @@ -1,236 +1,250 @@ -//! Definition of the core lockchain API - -use actix_web::{HttpRequest, Json, Responder}; -use lockchain::{ - traits::{Body, Vault}, Record, -}; - -use models::{inputs::*, responses::*}; -use state::ApiState; - -use std::intrinsics; -use std::sync::{Arc, Mutex}; - -type HttpRequestState = HttpRequest>>; - -/// GET /vault -/// -/// Check the documentation for more information about how to provide payloads -pub fn get_vaults(req: HttpRequestState>) -> impl Responder -where - B: Body, - V: Vault, -{ - let state = req.state().lock().unwrap(); - Json(VaultList { - vaults: state.vaults().iter().map(|s| s.to_string()).collect(), - count: state.count(), - }) -} - -/// PUT /vault -/// -/// Check the documentation for more information about how to provide payloads -pub fn create_vault( - (item, req): (Json, HttpRequestState>), -) -> impl Responder -where - B: Body, - V: Vault, -{ - let mut state = req.state().lock().unwrap(); - let location = if state.bound_scope { - state.working_dir.clone().join(&item.location) - } else { - (&item.location).into() - }; - - state.add_vault(&item.name, V::new(&item.name, location.to_str().unwrap())); - Json(VaultCreateResponse { - name: item.name.clone(), - created: true, - error: None, - }) -} - -pub fn delete_vault( - (item, req): (Json, HttpRequestState>), -) -> impl Responder -where - B: Body, - V: Vault, -{ - Json(OperationFailed { - reason: "Not implemented".into(), - code: 255, - }) -} - -pub fn scope_vault( - (item, req): (Json, HttpRequestState>), -) -> impl Responder -where - B: Body, - V: Vault, -{ - Json(OperationFailed { - reason: "Not implemented".into(), - code: 255, - }) -} - -pub fn unscope_vault( - (item, req): (Json, HttpRequestState>), -) -> impl Responder -where - B: Body, - V: Vault, -{ - Json(OperationFailed { - reason: "Not implemented".into(), - code: 255, - }) -} - -/// POST /vault/{vault-id} -pub fn update_vault(_req: HttpRequestState>) -> impl Responder -where - B: Body, - V: Vault, -{ - Json(OperationFailed { - reason: "Not implemented".into(), - code: 255, - }) -} - -pub fn get_all_records(_req: HttpRequestState>) -> impl Responder -where - B: Body, - V: Vault, -{ - Json(OperationFailed { - reason: "Not implemented".into(), - code: 255, - }) -} - -/// PUT /vault/{vault-id}/records -pub fn create_record(_req: HttpRequestState>) -> impl Responder -where - B: Body, - V: Vault, -{ - Json(OperationFailed { - reason: "Not implemented".into(), - code: 255, - }) -} - -/// GET /vault/{vault-id}/records/{record-id} -pub fn get_record( - (item, req): (Json, HttpRequestState>), -) -> impl Responder -where - B: Body, - V: Vault, -{ - let mut state = req.state().lock().unwrap(); - let vault = state.get_vault(""); - - Json(OperationFailed { - reason: "Not implemented".into(), - code: 255, - }) - - // Ok(Json(CarrierMessage { - // error: Ok(()), - // data: Some(Record::new("name", "category", vec!["test", "foo"])), - // })) -} - -/// POST /vault/{vault-id}/records/{record-id} -pub fn update_record(_req: HttpRequestState>) -> impl Responder -where - B: Body, - V: Vault, -{ - Json(OperationFailed { - reason: "Not implemented".into(), - code: 255, - }) -} - -/// DELETE /vault/{vault-id}/records/{record-id} -pub fn delete_record(_req: HttpRequestState>) -> impl Responder -where - B: Body, - V: Vault, -{ - Json(OperationFailed { - reason: "Not implemented".into(), - code: 255, - }) -} - -/// PUT /authenticate -pub fn authenticate( - (item, req): (Json, HttpRequestState>), -) -> impl Responder -where - B: Body, - V: Vault, -{ - use lockchain::users::*; - let Authenticate { username, password } = item.into_inner(); - - Json(match pam_authenticate(&username, &password) { - Ok(()) => CarrierMessage { - error: Ok(()), - data: Some(TokenMessage { - username, - token: String::new(), - }), - }, - Err(e) => CarrierMessage { - error: Err(e.into()), - data: Some(OperationFailed { - reason: "Meh!".into(), - code: 1, - }), - }, - }) -} - -/// PUT /de-authenticate -pub fn deauthenticate( - (item, req): (Json, HttpRequestState>), -) -> impl Responder -where - B: Body, - V: Vault, -{ - Json(OperationFailed { - reason: "Not implemented".into(), - code: 255, - }) -} - -/// GET /api -/// -/// Check the documentation for more information about how to provide payloads -pub fn api_data>(_: HttpRequestState>) -> impl Responder -where - B: Body, - V: Vault, -{ - Json(ApiInformation { - version: "1.0".into(), - providers: vec![ - unsafe { intrinsics::type_name::() }.into(), - unsafe { intrinsics::type_name::() }.into(), - ], - hostname: None, - supported: "1.0".into(), - }) -} +// //! Definition of the core lockchain API + +// use actix_web::{HttpRequest, Json, Responder}; +// use lockchain::{ +// traits::{Body, Vault}, Record, +// }; + +// use models::{inputs::*, responses::*, NoneError, Response}; +// use state::ApiState; + +// use std::intrinsics; +// use std::sync::{Arc, Mutex}; + +// type HttpRequestState = HttpRequest>>; + +// /// GET /vault +// /// +// /// Check the documentation for more information about how to provide payloads +// pub fn get_vaults(req: HttpRequestState>) -> impl Responder +// where +// B: Body, +// V: Vault, +// { +// let state = req.state().lock().unwrap(); +// Json(VaultList { +// vaults: state.vaults().iter().map(|s| s.to_string()).collect(), +// count: state.count(), +// }) +// } + +// /// PUT /vault +// /// +// /// Check the documentation for more information about how to provide payloads +// pub fn create_vault( +// (item, req): (Json, HttpRequestState>), +// ) -> impl Responder +// where +// B: Body, +// V: Vault, +// { +// let mut state = req.state().lock().unwrap(); +// let location = if state.bound_scope { +// state.working_dir.clone().join(&item.location) +// } else { +// (&item.location).into() +// }; + +// state.add_vault(&item.name, V::new(&item.name, location.to_str().unwrap())); +// Json(Response::Success::) +// } + +// pub fn delete_vault( +// (item, req): (Json, HttpRequestState>), +// ) -> impl Responder +// where +// B: Body, +// V: Vault, +// { +// use lockchain::errors::Error as LockError; + +// Json(OperationFailed:: { +// reason: "Not implemented".into(), +// error: LockError::UnknownFailure.into(), +// }) +// } + +// pub fn scope_vault( +// (item, req): (Json, HttpRequestState>), +// ) -> impl Responder +// where +// B: Body, +// V: Vault, +// { +// use lockchain::errors::Error as LockError; + +// Json(OperationFailed:: { +// reason: "Not implemented".into(), +// error: LockError::UnknownFailure.into(), +// }) +// } + +// pub fn unscope_vault( +// (item, req): (Json, HttpRequestState>), +// ) -> impl Responder +// where +// B: Body, +// V: Vault, +// { +// use lockchain::errors::Error as LockError; + +// Json(Response::Failure::(OperationFailed { +// reason: "Not implemented".into(), +// error: LockError::UnknownFailure.into(), +// })) + +// // Json() +// } + +// /// POST /vault/{vault-id} +// pub fn update_vault(_req: HttpRequestState>) -> impl Responder +// where +// B: Body, +// V: Vault, +// { +// use lockchain::errors::Error as LockError; + +// Json(OperationFailed:: { +// reason: "Not implemented".into(), +// error: LockError::UnknownFailure.into(), +// }) +// } + +// pub fn get_all_records(_req: HttpRequestState>) -> impl Responder +// where +// B: Body, +// V: Vault, +// { +// use lockchain::errors::Error as LockError; + +// Json(OperationFailed:: { +// reason: "Not implemented".into(), +// error: LockError::UnknownFailure.into(), +// }) +// } + +// /// PUT /vault/{vault-id}/records +// pub fn create_record(_req: HttpRequestState>) -> impl Responder +// where +// B: Body, +// V: Vault, +// { +// use lockchain::errors::Error as LockError; + +// Json(OperationFailed:: { +// reason: "Not implemented".into(), +// error: LockError::UnknownFailure.into(), +// }) +// } + +// /// GET /vault/{vault-id}/records/{record-id} +// pub fn get_record( +// (item, req): (Json, HttpRequestState>), +// ) -> impl Responder +// where +// B: Body, +// V: Vault, +// { +// let mut state = req.state().lock().unwrap(); +// let vault = state.get_vault(""); + +// use lockchain::errors::Error as LockError; + +// Json(OperationFailed:: { +// reason: "Not implemented".into(), +// error: LockError::UnknownFailure.into(), +// }) +// } + +// /// POST /vault/{vault-id}/records/{record-id} +// pub fn update_record(_req: HttpRequestState>) -> impl Responder +// where +// B: Body, +// V: Vault, +// { +// use lockchain::errors::Error as LockError; + +// Json(OperationFailed:: { +// reason: "Not implemented".into(), +// error: LockError::UnknownFailure.into(), +// }) +// } + +// /// DELETE /vault/{vault-id}/records/{record-id} +// pub fn delete_record(_req: HttpRequestState>) -> impl Responder +// where +// B: Body, +// V: Vault, +// { +// use lockchain::errors::Error as LockError; + +// Json(OperationFailed:: { +// reason: "Not implemented".into(), +// error: LockError::UnknownFailure.into(), +// }) +// } + +// /// PUT /authenticate +// pub fn authenticate( +// (item, req): (Json, HttpRequestState>), +// ) -> impl Responder +// where +// B: Body, +// V: Vault, +// { +// use lockchain::users::*; +// let Authenticate { username, password } = item.into_inner(); + +// Json(match pam_authenticate(&username, &password) { +// Ok(()) => { +// /* Store the token for auth later */ +// let state = req.state().lock().unwrap(); +// let token = String::new(); +// state.tokens.insert(token.clone()); + +// Response::Token(TokenMessage { +// username, +// token: token, +// }) +// } +// Err(e) => Response::Failure(OperationFailed { +// reason: "Failed to authenticate user".into(), +// error: e.into(), +// }), +// }) +// } + +// /// PUT /de-authenticate +// pub fn deauthenticate( +// (item, req): (Json, HttpRequestState>), +// ) -> impl Responder +// where +// B: Body, +// V: Vault, +// { +// use lockchain::errors::Error as LockError; + +// Json(OperationFailed:: { +// reason: "Not implemented".into(), +// error: LockError::UnknownFailure.into(), +// }) +// } + +// /// GET /api +// /// +// /// Check the documentation for more information about how to provide payloads +// pub fn api_data>(_: HttpRequestState>) -> impl Responder +// where +// B: Body, +// V: Vault, +// { +// Json(ApiInformation { +// version: "1.0".into(), +// providers: vec![ +// unsafe { intrinsics::type_name::() }.into(), +// unsafe { intrinsics::type_name::() }.into(), +// ], +// hostname: None, +// supported: "1.0".into(), +// }) +// } diff --git a/lockchain-http/src/lib.rs b/lockchain-http/src/lib.rs index dfe1211..e51e23f 100644 --- a/lockchain-http/src/lib.rs +++ b/lockchain-http/src/lib.rs @@ -71,52 +71,52 @@ where server::new(move || { vec![ - App::with_state(Arc::clone(&state)) - .resource("/vaults", |r| { - // Get existing vaults - r.method(http::Method::GET).with(handlers::get_vaults); + // App::with_state(Arc::clone(&state)) + // .resource("/vaults", |r| { + // // Get existing vaults + // r.method(http::Method::GET).with(handlers::get_vaults); - // Create new vault (if authorised) - r.method(http::Method::PUT).with(handlers::create_vault); + // // Create new vault (if authorised) + // r.method(http::Method::PUT).with(handlers::create_vault); - // Delete entire vault (if authorised) - r.method(http::Method::DELETE).with(handlers::delete_vault); - }) - .resource("/vaults/scope", |r| { - // Bring an existing vault into scope (if authorised) - r.method(http::Method::PUT).with(handlers::scope_vault); - // Remove an existing vault from API scope (if authorised) - r.method(http::Method::DELETE).with(handlers::unscope_vault); - }) - .resource("/vaults/{vaultid}", |r| { - // Update vault metadata (access rights, users, indices, etc) - r.method(http::Method::POST).with(handlers::update_vault) - }) - .resource("/vaults/{vaultid}/records", |r| { - // Get the vault record index (omits records without access) - r.method(http::Method::GET).with(handlers::get_all_records); - // Create a new record (if authorised) in the vault - r.method(http::Method::PUT).with(handlers::create_record); - }) - .resource("/vaults/{vaultid}/records/{recordid}", |r| { - // Get a specific record from a vault - r.method(http::Method::GET).with(handlers::get_record); - // Update a specific record - r.method(http::Method::POST).with(handlers::update_record); - // Delete a specific record from a vault - r.method(http::Method::DELETE).with(handlers::delete_record); - }) - .resource("/users/login", |r| { - // Request a new auth token - r.method(http::Method::POST).with(handlers::authenticate) - }) - .resource("/users/logout", |r| { - // Hand-in active auth token - r.method(http::Method::POST).with(handlers::deauthenticate) - }) - .resource("/api", |r| { - r.method(http::Method::GET).with(handlers::api_data); - }), + // // Delete entire vault (if authorised) + // r.method(http::Method::DELETE).with(handlers::delete_vault); + // }) + // .resource("/vaults/scope", |r| { + // // Bring an existing vault into scope (if authorised) + // r.method(http::Method::PUT).with(handlers::scope_vault); + // // Remove an existing vault from API scope (if authorised) + // r.method(http::Method::DELETE).with(handlers::unscope_vault); + // }) + // .resource("/vaults/{vaultid}", |r| { + // // Update vault metadata (access rights, users, indices, etc) + // r.method(http::Method::POST).with(handlers::update_vault) + // }) + // .resource("/vaults/{vaultid}/records", |r| { + // // Get the vault record index (omits records without access) + // r.method(http::Method::GET).with(handlers::get_all_records); + // // Create a new record (if authorised) in the vault + // r.method(http::Method::PUT).with(handlers::create_record); + // }) + // .resource("/vaults/{vaultid}/records/{recordid}", |r| { + // // Get a specific record from a vault + // r.method(http::Method::GET).with(handlers::get_record); + // // Update a specific record + // r.method(http::Method::POST).with(handlers::update_record); + // // Delete a specific record from a vault + // r.method(http::Method::DELETE).with(handlers::delete_record); + // }) + // .resource("/users/login", |r| { + // // Request a new auth token + // r.method(http::Method::POST).with(handlers::authenticate) + // }) + // .resource("/users/logout", |r| { + // // Hand-in active auth token + // r.method(http::Method::POST).with(handlers::deauthenticate) + // }) + // .resource("/api", |r| { + // r.method(http::Method::GET).with(handlers::api_data); + // }), ] }).bind(format!("{}:{}", bind, port)) .map_err(|e| e.into()) diff --git a/lockchain-http/src/models/mod.rs b/lockchain-http/src/models/mod.rs index 3e22197..0ca7480 100644 --- a/lockchain-http/src/models/mod.rs +++ b/lockchain-http/src/models/mod.rs @@ -1,4 +1,22 @@ //! Data models specific to the lockchain API pub mod inputs; -pub mod responses; \ No newline at end of file +pub mod responses; + +use serde::{de::DeserializeOwned, Serialize}; +use std::error::Error; + +/// A wrapper model for various API response types +#[derive(Serialize, Deserialize)] +pub enum Response{ + /// Indicate general success of an operation + Success, + /// Indicate a failure of some kind + Failure(responses::OperationFailed), + /// Returns a login token + Token(responses::TokenMessage), + /// Returns general API information + Api(responses::ApiInformation), + /// Returns a list of all vaults + Vaults(responses::VaultList), +} diff --git a/lockchain-http/src/models/responses.rs b/lockchain-http/src/models/responses.rs index 436d3e6..cda9ec4 100644 --- a/lockchain-http/src/models/responses.rs +++ b/lockchain-http/src/models/responses.rs @@ -1,5 +1,7 @@ +use lockchain::errors::Error as LockError; use serde::{de::DeserializeOwned, Serialize}; use std::error::Error; +use std::fmt::{Debug, Display}; /// A generic container that json/error wraps lockchain-types /// @@ -7,22 +9,22 @@ use std::error::Error; /// to send both encrypted and cleartext data via the API endpoint, using /// the same code. #[derive(Serialize, Deserialize)] -pub struct CarrierMessage +pub struct CarrierMessage where T: Serialize + DeserializeOwned, - E: Error + Serialize + DeserializeOwned, { - #[serde(bound(deserialize = "E: Serialize + DeserializeOwned"))] - pub error: Result<(), E>, + pub error: Result<(), LockError>, #[serde(bound(deserialize = "T: Serialize + DeserializeOwned"))] pub data: Option, } +// pub trait SerialError: Sized + Error + Serialize + DeserializeOwned + Debug + Display {} + /// A simple message that describes an invalid operation #[derive(Serialize, Deserialize)] pub struct OperationFailed { pub reason: String, - pub code: u32, + pub error: Box, } /// Message that returns a token @@ -47,11 +49,3 @@ pub struct VaultList { pub vaults: Vec, pub count: usize, } - -/// Response to creating a new vault -#[derive(Serialize, Deserialize)] -pub struct VaultCreateResponse { - pub name: String, - pub created: bool, - pub error: Option, -} diff --git a/lockchain-http/src/state.rs b/lockchain-http/src/state.rs index 650660f..5cfc8ad 100644 --- a/lockchain-http/src/state.rs +++ b/lockchain-http/src/state.rs @@ -1,6 +1,6 @@ use lockchain::traits::{AutoEncoder, Body, FileIO, Vault}; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::marker::PhantomData; use std::path::PathBuf; @@ -34,7 +34,8 @@ where pub vaults: HashMap>, #[doc(hidden)] pub _phantom: PhantomData, - + #[doc(hidden)] + pub tokens: HashSet, /// Signal if the API handlers are allowed outside their working dir pub bound_scope: bool, /// Provide a working directory @@ -87,6 +88,7 @@ where _phantom: PhantomData, bound_scope: true, vaults: HashMap::new(), + tokens: HashSet::new(), administrative: false, ..Default::default() } -- cgit v1.2.3