HTTP Basic Authorization for WebApps server.
This commit is contained in:
parent
8f16515d82
commit
dab54cf2a7
@ -136,13 +136,19 @@ API and Console Options:
|
||||
interface. APIS is a comma-delimited list of API
|
||||
name. Possible name are web3, eth and net.
|
||||
[default: web3,eth,net,personal].
|
||||
-w --webapp Enable the web applications server (e.g. status page).
|
||||
-w --webapp Enable the web applications server (e.g.
|
||||
status page).
|
||||
--webapp-port PORT Specify the port portion of the WebApps server
|
||||
[default: 8080].
|
||||
--webapp-interface IP Specify the hostname portion of the WebApps
|
||||
server, IP should be an interface's IP address, or
|
||||
all (all interfaces) or local [default: local].
|
||||
|
||||
--webapp-user USERNAME Specify username for WebApps server. It will be
|
||||
used in HTTP Basic Authentication Scheme.
|
||||
If --webapp-pass is not specified you will be
|
||||
asked for password on startup.
|
||||
--webapp-pass PASSWORD Specify password for WebApps server. Use only in
|
||||
conjunction with --webapp-user.
|
||||
|
||||
Sealing/Mining Options:
|
||||
--usd-per-tx USD Amount of USD to be paid for a basic transaction
|
||||
@ -230,6 +236,8 @@ struct Args {
|
||||
flag_webapp: bool,
|
||||
flag_webapp_port: u16,
|
||||
flag_webapp_interface: String,
|
||||
flag_webapp_user: Option<String>,
|
||||
flag_webapp_pass: Option<String>,
|
||||
flag_author: String,
|
||||
flag_usd_per_tx: String,
|
||||
flag_usd_per_eth: String,
|
||||
@ -288,7 +296,7 @@ fn setup_rpc_server(
|
||||
miner: Arc<Miner>,
|
||||
url: &SocketAddr,
|
||||
cors_domain: &str,
|
||||
apis: Vec<&str>
|
||||
apis: Vec<&str>,
|
||||
) -> RpcServer {
|
||||
use rpc::v1::*;
|
||||
|
||||
@ -321,7 +329,8 @@ fn setup_webapp_server(
|
||||
sync: Arc<EthSync>,
|
||||
secret_store: Arc<AccountService>,
|
||||
miner: Arc<Miner>,
|
||||
url: &str
|
||||
url: &str,
|
||||
auth: Option<(String, String)>,
|
||||
) -> WebappServer {
|
||||
use rpc::v1::*;
|
||||
|
||||
@ -331,7 +340,14 @@ fn setup_webapp_server(
|
||||
server.add_delegate(EthClient::new(&client, &sync, &secret_store, &miner).to_delegate());
|
||||
server.add_delegate(EthFilterClient::new(&client, &miner).to_delegate());
|
||||
server.add_delegate(PersonalClient::new(&secret_store).to_delegate());
|
||||
let start_result = server.start_http(url, ::num_cpus::get());
|
||||
let start_result = match auth {
|
||||
None => {
|
||||
server.start_unsecure_http(url, ::num_cpus::get())
|
||||
},
|
||||
Some((username, password)) => {
|
||||
server.start_basic_auth_http(url, ::num_cpus::get(), &username, &password)
|
||||
},
|
||||
};
|
||||
match start_result {
|
||||
Err(webapp::WebappServerError::IoError(err)) => die_with_io_error(err),
|
||||
Err(e) => die!("{:?}", e),
|
||||
@ -351,7 +367,7 @@ fn setup_rpc_server(
|
||||
_miner: Arc<Miner>,
|
||||
_url: &str,
|
||||
_cors_domain: &str,
|
||||
_apis: Vec<&str>
|
||||
_apis: Vec<&str>,
|
||||
) -> ! {
|
||||
die!("Your Parity version has been compiled without JSON-RPC support.")
|
||||
}
|
||||
@ -365,7 +381,8 @@ fn setup_webapp_server(
|
||||
_sync: Arc<EthSync>,
|
||||
_secret_store: Arc<AccountService>,
|
||||
_miner: Arc<Miner>,
|
||||
_url: &str
|
||||
_url: &str,
|
||||
_auth: Option<(String, String)>,
|
||||
) -> ! {
|
||||
die!("Your Parity version has been compiled without WebApps support.")
|
||||
}
|
||||
@ -683,12 +700,24 @@ impl Configuration {
|
||||
},
|
||||
self.args.flag_webapp_port
|
||||
);
|
||||
let auth = self.args.flag_webapp_user.as_ref().map(|username| {
|
||||
let password = self.args.flag_webapp_pass.as_ref().map_or_else(|| {
|
||||
use rpassword::read_password;
|
||||
println!("Type password for WebApps server (user: {}): ", username);
|
||||
let pass = read_password().unwrap();
|
||||
println!("OK, got it. Starting server...");
|
||||
pass
|
||||
}, |pass| pass.to_owned());
|
||||
(username.to_owned(), password)
|
||||
});
|
||||
|
||||
Some(setup_webapp_server(
|
||||
service.client(),
|
||||
sync.clone(),
|
||||
account_service.clone(),
|
||||
miner.clone(),
|
||||
&url,
|
||||
auth,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
|
@ -35,6 +35,8 @@ mod apps;
|
||||
mod page;
|
||||
mod router;
|
||||
|
||||
use router::auth::{Authorization, NoAuth, HttpBasicAuth};
|
||||
|
||||
/// Http server.
|
||||
pub struct WebappServer {
|
||||
handler: Arc<IoHandler>,
|
||||
@ -53,14 +55,25 @@ impl WebappServer {
|
||||
self.handler.add_delegate(delegate);
|
||||
}
|
||||
|
||||
/// Start server asynchronously and returns result with `Listening` handle on success or an error.
|
||||
pub fn start_http(&self, addr: &str, threads: usize) -> Result<Listening, WebappServerError> {
|
||||
/// Asynchronously start server with no authentication,
|
||||
/// return result with `Listening` handle on success or an error.
|
||||
pub fn start_unsecure_http(&self, addr: &str, threads: usize) -> Result<Listening, WebappServerError> {
|
||||
self.start_http(addr, threads, NoAuth)
|
||||
}
|
||||
|
||||
/// Asynchronously start server with `HTTP Basic Authentication`,
|
||||
/// return result with `Listening` handle on success or an error.
|
||||
pub fn start_basic_auth_http(&self, addr: &str, threads: usize, username: &str, password: &str) -> Result<Listening, WebappServerError> {
|
||||
self.start_http(addr, threads, HttpBasicAuth::single_user(username, password))
|
||||
}
|
||||
|
||||
fn start_http<A: Authorization + 'static>(&self, addr: &str, threads: usize, authorization: A) -> Result<Listening, WebappServerError> {
|
||||
let addr = addr.to_owned();
|
||||
let handler = self.handler.clone();
|
||||
|
||||
let cors_domain = jsonrpc_http_server::AccessControlAllowOrigin::Null;
|
||||
let rpc = ServerHandler::new(handler, cors_domain);
|
||||
let router = router::Router::new(rpc, apps::main_page(), apps::all_pages());
|
||||
let router = router::Router::new(rpc, apps::main_page(), apps::all_pages(), authorization);
|
||||
|
||||
try!(hyper::Server::http(addr.as_ref() as &str))
|
||||
.handle_threads(router, threads)
|
||||
|
@ -29,7 +29,7 @@ pub enum Authorized<'a, 'b> where 'b : 'a {
|
||||
}
|
||||
|
||||
/// Authorization interface
|
||||
pub trait Authorization {
|
||||
pub trait Authorization : Send + Sync {
|
||||
/// Handle authorization process and return `Request` and `Response` when authorization is successful.
|
||||
fn handle<'b, 'a>(&'a self, req: server::Request<'a, 'b>, res: server::Response<'a>)-> Authorized<'a, 'b>;
|
||||
}
|
||||
|
@ -18,7 +18,7 @@
|
||||
//! Processes request handling authorization and dispatching it to proper application.
|
||||
|
||||
mod api;
|
||||
mod auth;
|
||||
pub mod auth;
|
||||
|
||||
use std::sync::Arc;
|
||||
use hyper;
|
||||
@ -27,22 +27,22 @@ use page::Page;
|
||||
use apps::Pages;
|
||||
use iron::request::Url;
|
||||
use jsonrpc_http_server::ServerHandler;
|
||||
use self::auth::{Authorization, NoAuth, Authorized};
|
||||
use self::auth::{Authorization, Authorized};
|
||||
|
||||
pub struct Router {
|
||||
auth: NoAuth,
|
||||
pub struct Router<A: Authorization> {
|
||||
authorization: A,
|
||||
rpc: ServerHandler,
|
||||
api: api::RestApi,
|
||||
main_page: Box<Page>,
|
||||
pages: Arc<Pages>,
|
||||
}
|
||||
|
||||
impl server::Handler for Router {
|
||||
impl<A: Authorization> server::Handler for Router<A> {
|
||||
fn handle<'b, 'a>(&'a self, req: server::Request<'a, 'b>, res: server::Response<'a>) {
|
||||
let auth = self.auth.handle(req, res);
|
||||
let auth = self.authorization.handle(req, res);
|
||||
|
||||
if let Authorized::Yes(req, res) = auth {
|
||||
let (path, req) = Router::extract_request_path(req);
|
||||
let (path, req) = self.extract_request_path(req);
|
||||
match path {
|
||||
Some(ref url) if self.pages.contains_key(url) => {
|
||||
self.pages.get(url).unwrap().handle(req, res);
|
||||
@ -59,11 +59,15 @@ impl server::Handler for Router {
|
||||
}
|
||||
}
|
||||
|
||||
impl Router {
|
||||
pub fn new(rpc: ServerHandler, main_page: Box<Page>, pages: Pages) -> Self {
|
||||
impl<A: Authorization> Router<A> {
|
||||
pub fn new(
|
||||
rpc: ServerHandler,
|
||||
main_page: Box<Page>,
|
||||
pages: Pages,
|
||||
authorization: A) -> Self {
|
||||
let pages = Arc::new(pages);
|
||||
Router {
|
||||
auth: NoAuth,
|
||||
authorization: authorization,
|
||||
rpc: rpc,
|
||||
api: api::RestApi { pages: pages.clone() },
|
||||
main_page: main_page,
|
||||
@ -71,7 +75,7 @@ impl Router {
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_url(req: &server::Request) -> Option<Url> {
|
||||
fn extract_url(&self, req: &server::Request) -> Option<Url> {
|
||||
match req.uri {
|
||||
uri::RequestUri::AbsoluteUri(ref url) => {
|
||||
match Url::from_generic_url(url.clone()) {
|
||||
@ -97,8 +101,8 @@ impl Router {
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_request_path<'a, 'b>(mut req: server::Request<'a, 'b>) -> (Option<String>, server::Request<'a, 'b>) {
|
||||
let url = Router::extract_url(&req);
|
||||
fn extract_request_path<'a, 'b>(&self, mut req: server::Request<'a, 'b>) -> (Option<String>, server::Request<'a, 'b>) {
|
||||
let url = self.extract_url(&req);
|
||||
match url {
|
||||
Some(ref url) if url.path.len() > 1 => {
|
||||
let part = url.path[0].clone();
|
||||
|
Loading…
Reference in New Issue
Block a user