SystemUIs authorization (#1233)

* Initial implementation of AuthCodeStore for SystemUIs

* SystemUIs authorization

* Renaming SystemUI -> SignerUI

* Fixing clippy warnings

* Lowering time threshold

* Bumping sysui

* Fixing test
This commit is contained in:
Tomasz Drwięga
2016-06-07 17:21:19 +02:00
committed by Gav Wood
parent e6d141e14f
commit f61ee1a5f1
12 changed files with 316 additions and 24 deletions

View File

@@ -19,6 +19,7 @@
use ws;
use std;
use std::thread;
use std::path::PathBuf;
use std::default::Default;
use std::ops::Drop;
use std::sync::Arc;
@@ -51,6 +52,7 @@ impl From<ws::Error> for ServerError {
pub struct ServerBuilder {
queue: Arc<ConfirmationsQueue>,
handler: Arc<IoHandler>,
authcodes_path: PathBuf,
}
impl Extendable for ServerBuilder {
@@ -61,17 +63,18 @@ impl Extendable for ServerBuilder {
impl ServerBuilder {
/// Creates new `ServerBuilder`
pub fn new(queue: Arc<ConfirmationsQueue>) -> Self {
pub fn new(queue: Arc<ConfirmationsQueue>, authcodes_path: PathBuf) -> Self {
ServerBuilder {
queue: queue,
handler: Arc::new(IoHandler::new()),
authcodes_path: authcodes_path,
}
}
/// Starts a new `WebSocket` server in separate thread.
/// Returns a `Server` handle which closes the server when droped.
pub fn start(self, addr: SocketAddr) -> Result<Server, ServerError> {
Server::start(addr, self.handler, self.queue)
Server::start(addr, self.handler, self.queue, self.authcodes_path)
}
}
@@ -86,7 +89,7 @@ pub struct Server {
impl Server {
/// Starts a new `WebSocket` server in separate thread.
/// Returns a `Server` handle which closes the server when droped.
fn start(addr: SocketAddr, handler: Arc<IoHandler>, queue: Arc<ConfirmationsQueue>) -> Result<Server, ServerError> {
fn start(addr: SocketAddr, handler: Arc<IoHandler>, queue: Arc<ConfirmationsQueue>, authcodes_path: PathBuf) -> Result<Server, ServerError> {
let config = {
let mut config = ws::Settings::default();
config.max_connections = 10;
@@ -96,7 +99,7 @@ impl Server {
// Create WebSocket
let origin = format!("{}", addr);
let ws = try!(ws::Builder::new().with_settings(config).build(session::Factory::new(handler, origin)));
let ws = try!(ws::Builder::new().with_settings(config).build(session::Factory::new(handler, origin, authcodes_path)));
let panic_handler = PanicHandler::new_in_arc();
let ph = panic_handler.clone();

View File

@@ -18,8 +18,12 @@
use ws;
use sysui;
use authcode_store::AuthCodes;
use std::path::{PathBuf, Path};
use std::sync::Arc;
use std::str::FromStr;
use jsonrpc_core::IoHandler;
use util::H256;
fn origin_is_allowed(self_origin: &str, header: Option<&Vec<u8>>) -> bool {
match header {
@@ -36,13 +40,32 @@ fn origin_is_allowed(self_origin: &str, header: Option<&Vec<u8>>) -> bool {
}
}
fn auth_is_valid(_header: Option<&Vec<u8>>) -> bool {
true
fn auth_is_valid(codes: &Path, protocols: ws::Result<Vec<&str>>) -> bool {
match protocols {
Ok(ref protocols) if protocols.len() == 1 => {
protocols.iter().any(|protocol| {
let mut split = protocol.split('_');
let auth = split.next().and_then(|v| H256::from_str(v).ok());
let time = split.next().and_then(|v| u64::from_str_radix(v, 10).ok());
if let (Some(auth), Some(time)) = (auth, time) {
// Check if the code is valid
AuthCodes::from_file(codes)
.map(|codes| codes.is_valid(&auth, time))
.unwrap_or(false)
} else {
false
}
})
},
_ => false
}
}
pub struct Session {
out: ws::Sender,
self_origin: String,
authcodes_path: PathBuf,
handler: Arc<IoHandler>,
}
@@ -53,17 +76,25 @@ impl ws::Handler for Session {
// Check request origin and host header.
if !origin_is_allowed(&self.self_origin, origin) && !origin_is_allowed(&self.self_origin, host) {
warn!(target: "signer", "Blocked connection to Signer API from untrusted origin.");
return Ok(ws::Response::forbidden("You are not allowed to access system ui.".into()));
}
// Check authorization
if !auth_is_valid(req.header("authorization")) {
return Ok(ws::Response::forbidden("You are not authorized.".into()));
}
// Detect if it's a websocket request.
if req.header("sec-websocket-key").is_some() {
return ws::Response::from_request(req);
// Check authorization
if !auth_is_valid(&self.authcodes_path, req.protocols()) {
info!(target: "signer", "Unauthorized connection to Signer API blocked.");
return Ok(ws::Response::forbidden("You are not authorized.".into()));
}
let protocols = req.protocols().expect("Existence checked by authorization.");
let protocol = protocols.get(0).expect("Proved by authorization.");
return ws::Response::from_request(req).map(|mut res| {
// To make WebSockets connection successful we need to send back the protocol header.
res.set_protocol(protocol);
res
});
}
// Otherwise try to serve a page.
@@ -101,13 +132,15 @@ impl ws::Handler for Session {
pub struct Factory {
handler: Arc<IoHandler>,
self_origin: String,
authcodes_path: PathBuf,
}
impl Factory {
pub fn new(handler: Arc<IoHandler>, self_origin: String) -> Self {
pub fn new(handler: Arc<IoHandler>, self_origin: String, authcodes_path: PathBuf) -> Self {
Factory {
handler: handler,
self_origin: self_origin,
authcodes_path: authcodes_path,
}
}
}
@@ -118,8 +151,9 @@ impl ws::Factory for Factory {
fn connection_made(&mut self, sender: ws::Sender) -> Self::Handler {
Session {
out: sender,
self_origin: self.self_origin.clone(),
handler: self.handler.clone(),
self_origin: self.self_origin.clone(),
authcodes_path: self.authcodes_path.clone(),
}
}
}