Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add storage abstractions and add server storage hook #154

Merged
merged 4 commits into from
Jul 29, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

## [Unreleased]
### Added
- [\#154](https://github.com/Manta-Network/manta-signer/pull/154) Add storage abstractions and add server storage hook

### Changed

Expand Down
7 changes: 5 additions & 2 deletions examples/test_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use manta_crypto::rand::{CryptoRng, OsRng, RngCore, Sample};
use manta_signer::{
config::Config,
secret::{Authorizer, Password, PasswordFuture, SecretString},
service::{self, Error},
service::{Error, Server},
};

/// Mock User
Expand Down Expand Up @@ -58,5 +58,8 @@ async fn main() -> Result<(), Error> {
if let Some(url) = std::env::args().nth(1) {
config.service_url = url;
}
service::start(config, MockUser::new(&mut OsRng)).await
Server::build(config, MockUser::new(&mut OsRng))
.await?
.start()
.await
}
63 changes: 63 additions & 0 deletions src/http.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright 2019-2022 Manta Network.
// This file is part of manta-signer.
//
// manta-signer is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// manta-signer is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with manta-signer. If not, see <http://www.gnu.org/licenses/>.

//! Manta Signer HTTP Utilities

use crate::serde::{de::DeserializeOwned, Serialize};
use core::future::Future;

pub use tide::{Body, Error, Request, Response, Server, StatusCode};

/// Generates the JSON body for the output of `f`, returning an HTTP reponse.
#[inline]
pub async fn into_body<R, E, F, Fut>(f: F) -> Result<Response, Error>
where
R: Serialize,
E: Into<Error>,
F: FnOnce() -> Fut,
Fut: Future<Output = Result<R, E>>,
{
Ok(Body::from_json(&f().await.map_err(Into::into)?)?.into())
}

/// Executes `f` on the incoming `request`.
#[inline]
pub async fn execute<S, T, R, E, F, Fut>(mut request: Request<S>, f: F) -> Result<Response, Error>
where
S: Clone,
T: DeserializeOwned,
R: Serialize,
E: Into<Error>,
F: FnOnce(S, T) -> Fut,
Fut: Future<Output = Result<R, E>>,
{
let args = request.body_json::<T>().await?;
into_body(move || async move { f(request.state().clone(), args).await }).await
}

/// Registers a `POST` command with the given `path` and execution `f`.
#[inline]
pub fn register_post<S, T, R, E, F, Fut>(api: &mut Server<S>, path: &'static str, f: F)
where
S: Clone + Send + Sync + 'static,
T: DeserializeOwned + Send + 'static,
R: Serialize + 'static,
E: Into<Error> + 'static,
F: Clone + Send + Sync + 'static + Fn(S, T) -> Fut,
Fut: Future<Output = Result<R, E>> + Send + 'static,
{
api.at(path).post(move |r| execute(r, f.clone()));
}
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,15 @@
#![forbid(rustdoc::broken_intra_doc_links)]
#![forbid(missing_docs)]

extern crate alloc;

pub mod config;
pub mod http;
pub mod log;
pub mod parameters;
pub mod secret;
pub mod service;
pub mod storage;

#[doc(inline)]
pub use manta_util::serde;
Expand Down
90 changes: 88 additions & 2 deletions src/secret.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ use crate::config::Setup;
use futures::future::BoxFuture;
use manta_util::serde::Serialize;
use password_hash::{PasswordHashString, SaltString};
use tokio::sync::mpsc::{channel, Receiver, Sender};

pub use password_hash::{Error as PasswordHashError, PasswordHasher, PasswordVerifier};
pub use secrecy::{ExposeSecret, Secret, SecretString};
Expand Down Expand Up @@ -95,9 +96,9 @@ pub trait Authorizer: 'static + Send {
/// # Implementation Note
///
/// For custom service implementations, this method should be called before any service is run.
/// The [`service::start`] function already calls this method internally.
/// The [`Server::start`] function already calls this method internally.
///
/// [`service::start`]: crate::service::start
/// [`Server::start`]: crate::service::Server::start
#[inline]
fn setup<'s>(&'s mut self, setup: &'s Setup) -> UnitFuture<'s> {
let _ = setup;
Expand Down Expand Up @@ -198,3 +199,88 @@ where
.to_owned()
}
}

/// Password Sender
pub struct PasswordSender {
/// Password Sender
pub password: Sender<Password>,

/// Retry Receiver
pub retry: Receiver<bool>,
}

impl PasswordSender {
/// Builds a new [`PasswordSender`] from `password` and `retry`.
#[inline]
pub fn new(password: Sender<Password>, retry: Receiver<bool>) -> Self {
Self { password, retry }
}

/// Loads the password with `password` waiting for a retry message.
#[inline]
pub async fn load(&mut self, password: SecretString) -> bool {
self.load_exact(password).await;
self.retry
.recv()
.await
.expect("Failed to receive retry message.")
}

/// Loads the password with `password` without requesting a retry message.
#[inline]
pub async fn load_exact(&mut self, password: SecretString) {
let _ = self.password.send(Password::from_known(password)).await;
}

/// Clears the currently stored password.
#[inline]
pub async fn clear(&self) {
let _ = self.password.send(Password::from_unknown()).await;
}
}

/// Password Receiver
pub struct PasswordReceiver {
/// Password Receiver
pub password: Receiver<Password>,

/// Retry Sender
pub retry: Sender<bool>,
}

impl PasswordReceiver {
/// Builds a new [`PasswordReceiver`] from `password` and `retry`.
#[inline]
pub fn new(password: Receiver<Password>, retry: Sender<bool>) -> Self {
Self { password, retry }
}

/// Sends the message `retry` across the retry channel.
#[inline]
pub async fn should_retry(&mut self, retry: bool) {
self.retry
.send(retry)
.await
.expect("Failed to send retry message.");
}

/// Loads the password from the password channel.
#[inline]
pub async fn password(&mut self) -> Password {
self.password
.recv()
.await
.expect("Failed to receive password message.")
}
}

/// Generates a new password-sending channel.
#[inline]
pub fn password_channel() -> (PasswordSender, PasswordReceiver) {
let (password_sender, password_receiver) = channel(1);
let (retry_sender, retry_receiver) = channel(1);
(
PasswordSender::new(password_sender, retry_receiver),
PasswordReceiver::new(password_receiver, retry_sender),
)
}