From aa147461b07cf704df873ec45880e68943e5cda8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Fri, 11 Nov 2016 17:38:45 +0100 Subject: [PATCH 1/7] Generating browser link on signer new-token --- parity/configuration.rs | 4 +++- parity/signer.rs | 17 ++++++++++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/parity/configuration.rs b/parity/configuration.rs index 75d319272..928e9ba7a 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -104,7 +104,9 @@ impl Configuration { Cmd::Version } else if self.args.cmd_signer && self.args.cmd_new_token { Cmd::SignerToken(SignerCommand { - path: dirs.signer + path: dirs.signer, + signer_interface: signer_conf.interface, + signer_port: signer_conf.port, }) } else if self.args.cmd_tools && self.args.cmd_hash { Cmd::Hash(self.args.arg_file) diff --git a/parity/signer.rs b/parity/signer.rs index a26cc431a..fd1258bf6 100644 --- a/parity/signer.rs +++ b/parity/signer.rs @@ -71,12 +71,23 @@ fn codes_path(path: String) -> PathBuf { #[derive(Debug, PartialEq)] pub struct SignerCommand { pub path: String, + pub signer_interface: String, + pub signer_port: u16, } pub fn execute(cmd: SignerCommand) -> Result { - generate_new_token(cmd.path) - .map(|code| format!("This key code will authorise your System Signer UI: {}", Colour::White.bold().paint(code))) - .map_err(|err| format!("Error generating token: {:?}", err)) + let code = try!(generate_new_token(cmd.path).map_err(|err| format!("Error generating token: {:?}", err))); + + let url = format!("http://{}:{}/#/auth?token={}", cmd.signer_interface, cmd.signer_port, code); + Ok(format!( + r#" +Open: {} +to authorize your browser. +Or use the code: {} + "#, + Colour::White.bold().paint(url), + code + )) } pub fn generate_new_token(path: String) -> io::Result { From be6023b6020aec54563b9e7beea49c58787702af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Fri, 11 Nov 2016 17:56:52 +0100 Subject: [PATCH 2/7] Opening a browser --- parity/signer.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/parity/signer.rs b/parity/signer.rs index fd1258bf6..835cc397a 100644 --- a/parity/signer.rs +++ b/parity/signer.rs @@ -20,6 +20,7 @@ use std::path::PathBuf; use ansi_term::Colour; use io::{ForwardPanic, PanicHandler}; use util::path::restrict_permissions_owner; +use url; use rpc_apis; use ethcore_signer as signer; use helpers::replace_home; @@ -77,15 +78,16 @@ pub struct SignerCommand { pub fn execute(cmd: SignerCommand) -> Result { let code = try!(generate_new_token(cmd.path).map_err(|err| format!("Error generating token: {:?}", err))); - - let url = format!("http://{}:{}/#/auth?token={}", cmd.signer_interface, cmd.signer_port, code); + let auth_url = format!("http://{}:{}/#/auth?token={}", cmd.signer_interface, cmd.signer_port, code); + // Open a browser + url::open(&auth_url); + // And print in to the console Ok(format!( r#" Open: {} to authorize your browser. -Or use the code: {} - "#, - Colour::White.bold().paint(url), +Or use the code: {}"#, + Colour::White.bold().paint(auth_url), code )) } From ba699fdb2569498115dd1a349c6801d160aaedca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Fri, 11 Nov 2016 18:02:36 +0100 Subject: [PATCH 3/7] new line [ci:skip] --- parity/signer.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/parity/signer.rs b/parity/signer.rs index 835cc397a..09bab3de9 100644 --- a/parity/signer.rs +++ b/parity/signer.rs @@ -86,7 +86,8 @@ pub fn execute(cmd: SignerCommand) -> Result { r#" Open: {} to authorize your browser. -Or use the code: {}"#, +Or use the code: +{}"#, Colour::White.bold().paint(auth_url), code )) From 7ca317912fc7ea7eec420560de6adfc8cdb0f222 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Sat, 12 Nov 2016 17:09:55 +0100 Subject: [PATCH 4/7] Set signer token via #/auth=token={} --- js/src/index.js | 10 +++++++++- js/src/secureApi.js | 25 +++++++++++++++++++------ 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/js/src/index.js b/js/src/index.js index c0f4f94ad..fda785842 100644 --- a/js/src/index.js +++ b/js/src/index.js @@ -25,6 +25,7 @@ import ReactDOM from 'react-dom'; import injectTapEventPlugin from 'react-tap-event-plugin'; import { createHashHistory } from 'history'; import { Redirect, Router, Route, useRouterHistory } from 'react-router'; +import qs from 'querystring'; import SecureApi from './secureApi'; import ContractInstances from './contracts'; @@ -45,6 +46,7 @@ import './index.html'; injectTapEventPlugin(); +const AUTH_HASH = '#/auth?'; const parityUrl = process.env.PARITY_URL || ( process.env.NODE_ENV === 'production' @@ -52,7 +54,12 @@ const parityUrl = process.env.PARITY_URL || : '127.0.0.1:8180' ); -const api = new SecureApi(`ws://${parityUrl}`); +let token = null; +if (window.location.hash && window.location.hash.indexOf(AUTH_HASH) === 0) { + token = qs.parse(window.location.hash.substr(AUTH_HASH.length)).token; +} + +const api = new SecureApi(`ws://${parityUrl}`, token); ContractInstances.create(api); const store = initStore(api); @@ -67,6 +74,7 @@ ReactDOM.render( + diff --git a/js/src/secureApi.js b/js/src/secureApi.js index ed665a5e6..67b38ff1b 100644 --- a/js/src/secureApi.js +++ b/js/src/secureApi.js @@ -19,23 +19,36 @@ import Api from './api'; const sysuiToken = window.localStorage.getItem('sysuiToken'); export default class SecureApi extends Api { - constructor (url) { - super(new Api.Transport.Ws(url, sysuiToken)); + constructor (url, _token) { + super(new Api.Transport.Ws(url, SecureApi.sanitizeToken(_token || sysuiToken))); + + const token = _token || sysuiToken; this._isConnecting = true; - this._connectState = sysuiToken === 'initial' ? 1 : 0; + this._connectState = token === 'initial' ? 1 : 0; this._needsToken = false; this._dappsPort = 8080; this._dappsInterface = null; this._signerPort = 8180; - console.log('SecureApi:constructor', sysuiToken); + console.log('SecureApi:constructor', token); + this.storeToken(token); this._followConnection(); } + static sanitizeToken (token) { + return token + ? token.replace(/[^a-zA-Z0-9]/g, '') + : null; + } + + storeToken (token) { + window.localStorage.setItem('sysuiToken', SecureApi.sanitizeToken(token)); + } + setToken = () => { - window.localStorage.setItem('sysuiToken', this._transport.token); + this.storeToken(this._transport.token); console.log('SecureApi:setToken', this._transport.token); } @@ -115,7 +128,7 @@ export default class SecureApi extends Api { updateToken (token, connectState = 0) { this._connectState = connectState; - this._transport.updateToken(token.replace(/[^a-zA-Z0-9]/g, '')); + this._transport.updateToken(SecureApi.sanitizeToken(token)); this._followConnection(); console.log('SecureApi:updateToken', this._transport.token, connectState); } From 665504c4140f974b0ab3c0283a6482a6de2e730a Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Mon, 14 Nov 2016 12:10:03 +0100 Subject: [PATCH 5/7] Only use #/auth token as fallback --- js/src/secureApi.js | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/js/src/secureApi.js b/js/src/secureApi.js index 67b38ff1b..3e1d9eab0 100644 --- a/js/src/secureApi.js +++ b/js/src/secureApi.js @@ -19,36 +19,24 @@ import Api from './api'; const sysuiToken = window.localStorage.getItem('sysuiToken'); export default class SecureApi extends Api { - constructor (url, _token) { - super(new Api.Transport.Ws(url, SecureApi.sanitizeToken(_token || sysuiToken))); - - const token = _token || sysuiToken; + constructor (url, nextToken) { + super(new Api.Transport.Ws(url, sysuiToken)); this._isConnecting = true; - this._connectState = token === 'initial' ? 1 : 0; + this._connectState = sysuiToken === 'initial' ? 1 : 0; this._needsToken = false; + this._nextToken = nextToken; this._dappsPort = 8080; this._dappsInterface = null; this._signerPort = 8180; - console.log('SecureApi:constructor', token); + console.log('SecureApi:constructor', sysuiToken); - this.storeToken(token); this._followConnection(); } - static sanitizeToken (token) { - return token - ? token.replace(/[^a-zA-Z0-9]/g, '') - : null; - } - - storeToken (token) { - window.localStorage.setItem('sysuiToken', SecureApi.sanitizeToken(token)); - } - setToken = () => { - this.storeToken(this._transport.token); + window.localStorage.setItem('sysuiToken', this._transport.token); console.log('SecureApi:setToken', this._transport.token); } @@ -70,7 +58,11 @@ export default class SecureApi extends Api { if (isConnected) { return this.connectSuccess(); } else if (lastError) { - this.updateToken('initial', 1); + const nextToken = this._nextToken || 'initial'; + const nextState = this._nextToken ? 0 : 1; + + this._nextToken = null; + this.updateToken(nextToken, nextState); } break; @@ -128,7 +120,7 @@ export default class SecureApi extends Api { updateToken (token, connectState = 0) { this._connectState = connectState; - this._transport.updateToken(SecureApi.sanitizeToken(token)); + this._transport.updateToken(token.replace(/[^a-zA-Z0-9]/g, '')); this._followConnection(); console.log('SecureApi:updateToken', this._transport.token, connectState); } From 7f011afacbbb30fb9c3840b302e61062a62d51e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Mon, 14 Nov 2016 11:56:01 +0100 Subject: [PATCH 6/7] Tokens retention policy --- parity/configuration.rs | 20 ++-- parity/run.rs | 25 +++-- parity/signer.rs | 16 ++-- signer/src/authcode_store.rs | 160 ++++++++++++++++++++++++++++---- signer/src/ws_server/session.rs | 3 + 5 files changed, 181 insertions(+), 43 deletions(-) diff --git a/parity/configuration.rs b/parity/configuration.rs index 928e9ba7a..61063aa18 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -35,7 +35,7 @@ use params::{ResealPolicy, AccountsConfig, GasPricerConfig, MinerExtras}; use ethcore_logger::Config as LogConfig; use dir::Directories; use dapps::Configuration as DappsConfiguration; -use signer::{Configuration as SignerConfiguration, SignerCommand}; +use signer::{Configuration as SignerConfiguration}; use run::RunCmd; use blockchain::{BlockchainCmd, ImportBlockchain, ExportBlockchain, DataFormat}; use presale::ImportWallet; @@ -49,7 +49,7 @@ pub enum Cmd { Account(AccountCmd), ImportPresaleWallet(ImportWallet), Blockchain(BlockchainCmd), - SignerToken(SignerCommand), + SignerToken(SignerConfiguration), Snapshot(SnapshotCommand), Hash(Option), } @@ -103,11 +103,7 @@ impl Configuration { let cmd = if self.args.flag_version { Cmd::Version } else if self.args.cmd_signer && self.args.cmd_new_token { - Cmd::SignerToken(SignerCommand { - path: dirs.signer, - signer_interface: signer_conf.interface, - signer_port: signer_conf.port, - }) + Cmd::SignerToken(signer_conf) } else if self.args.cmd_tools && self.args.cmd_hash { Cmd::Hash(self.args.arg_file) } else if self.args.cmd_account { @@ -692,7 +688,7 @@ mod tests { use ethcore::miner::{MinerOptions, PrioritizationStrategy}; use helpers::{replace_home, default_network_config}; use run::RunCmd; - use signer::{Configuration as SignerConfiguration, SignerCommand}; + use signer::{Configuration as SignerConfiguration}; use blockchain::{BlockchainCmd, ImportBlockchain, ExportBlockchain, DataFormat}; use presale::ImportWallet; use account::{AccountCmd, NewAccount, ImportAccounts}; @@ -829,8 +825,12 @@ mod tests { let args = vec!["parity", "signer", "new-token"]; let conf = parse(&args); let expected = replace_home("$HOME/.parity/signer"); - assert_eq!(conf.into_command().unwrap().cmd, Cmd::SignerToken(SignerCommand { - path: expected, + assert_eq!(conf.into_command().unwrap().cmd, Cmd::SignerToken(SignerConfiguration { + enabled: true, + signer_path: expected, + interface: "127.0.0.1".into(), + port: 8180, + skip_origin_validation: false, })); } diff --git a/parity/run.rs b/parity/run.rs index 56ff92c25..085fcfa34 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -48,7 +48,6 @@ use signer; use modules; use rpc_apis; use rpc; -use url; // how often to take periodic snapshots. const SNAPSHOT_PERIOD: u64 = 10000; @@ -93,13 +92,26 @@ pub struct RunCmd { pub check_seal: bool, } +pub fn open_ui(dapps_conf: &dapps::Configuration, signer_conf: &signer::Configuration) -> Result<(), String> { + if !dapps_conf.enabled { + return Err("Cannot use UI command with Dapps turned off.".into()) + } + + if !signer_conf.enabled { + return Err("Cannot use UI command with UI turned off.".into()) + } + + let token = try!(signer::generate_token_and_open_ui(signer_conf)); + println!("{}", token); + Ok(()) +} + pub fn execute(cmd: RunCmd, logger: Arc) -> Result<(), String> { if cmd.ui && cmd.dapps_conf.enabled { // Check if Parity is already running let addr = format!("{}:{}", cmd.dapps_conf.interface, cmd.dapps_conf.port); if !TcpListener::bind(&addr as &str).is_ok() { - url::open(&format!("http://{}:{}/", cmd.dapps_conf.interface, cmd.dapps_conf.port)); - return Ok(()); + return open_ui(&cmd.dapps_conf, &cmd.signer_conf); } } @@ -309,7 +321,7 @@ pub fn execute(cmd: RunCmd, logger: Arc) -> Result<(), String> { }; // start signer server - let signer_server = try!(signer::start(cmd.signer_conf, signer_deps)); + let signer_server = try!(signer::start(cmd.signer_conf.clone(), signer_deps)); let informant = Arc::new(Informant::new( service.client(), @@ -363,10 +375,7 @@ pub fn execute(cmd: RunCmd, logger: Arc) -> Result<(), String> { // start ui if cmd.ui { - if !cmd.dapps_conf.enabled { - return Err("Cannot use UI command with Dapps turned off.".into()) - } - url::open(&format!("http://{}:{}/", cmd.dapps_conf.interface, cmd.dapps_conf.port)); + try!(open_ui(&cmd.dapps_conf, &cmd.signer_conf)); } // Handle exit diff --git a/parity/signer.rs b/parity/signer.rs index 09bab3de9..4ce94eae4 100644 --- a/parity/signer.rs +++ b/parity/signer.rs @@ -28,7 +28,7 @@ pub use ethcore_signer::Server as SignerServer; const CODES_FILENAME: &'static str = "authcodes"; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct Configuration { pub enabled: bool, pub port: u16, @@ -69,16 +69,13 @@ fn codes_path(path: String) -> PathBuf { p } -#[derive(Debug, PartialEq)] -pub struct SignerCommand { - pub path: String, - pub signer_interface: String, - pub signer_port: u16, +pub fn execute(cmd: Configuration) -> Result { + generate_token_and_open_ui(&cmd) } -pub fn execute(cmd: SignerCommand) -> Result { - let code = try!(generate_new_token(cmd.path).map_err(|err| format!("Error generating token: {:?}", err))); - let auth_url = format!("http://{}:{}/#/auth?token={}", cmd.signer_interface, cmd.signer_port, code); +pub fn generate_token_and_open_ui(conf: &Configuration) -> Result { + let code = try!(generate_new_token(conf.signer_path.clone()).map_err(|err| format!("Error generating token: {:?}", err))); + let auth_url = format!("http://{}:{}/#/auth?token={}", conf.interface, conf.port, code); // Open a browser url::open(&auth_url); // And print in to the console @@ -96,6 +93,7 @@ Or use the code: pub fn generate_new_token(path: String) -> io::Result { let path = codes_path(path); let mut codes = try!(signer::AuthCodes::from_file(&path)); + codes.clear_garbage(); let code = try!(codes.generate_new()); try!(codes.to_file(&path)); trace!("New key code created: {}", Colour::White.bold().paint(&code[..])); diff --git a/signer/src/authcode_store.rs b/signer/src/authcode_store.rs index d8474441c..cbb78db41 100644 --- a/signer/src/authcode_store.rs +++ b/signer/src/authcode_store.rs @@ -16,12 +16,10 @@ use rand::Rng; use rand::os::OsRng; -use std::io; -use std::io::{Read, Write}; -use std::fs; +use std::io::{self, Read, Write}; use std::path::Path; -use std::time; -use util::{H256, Hashable}; +use std::{fs, time, mem}; +use util::{H256, Hashable, Itertools}; /// Providing current time in seconds pub trait TimeProvider { @@ -47,12 +45,35 @@ impl TimeProvider for DefaultTimeProvider { /// No of seconds the hash is valid const TIME_THRESHOLD: u64 = 7; +/// minimal length of hash const TOKEN_LENGTH: usize = 16; +/// special "initial" token used for authorization when there are no tokens yet. const INITIAL_TOKEN: &'static str = "initial"; +/// Separator between fields in serialized tokens file. +const SEPARATOR: &'static str = ";"; +/// Number of seconds to keep unused tokens. +const UNUSED_TOKEN_TIMEOUT: u64 = 3600 * 24; // a day + +struct Code { + code: String, + /// Duration since unix_epoch + created_at: time::Duration, + /// Duration since unix_epoch + last_used_at: Option, +} + +fn decode_time(val: &str) -> Option { + let time = val.parse::().ok(); + time.map(time::Duration::from_secs) +} + +fn encode_time(time: time::Duration) -> String { + format!("{}", time.as_secs()) +} /// Manages authorization codes for `SignerUIs` pub struct AuthCodes { - codes: Vec, + codes: Vec, now: T, } @@ -69,13 +90,32 @@ impl AuthCodes { "".into() } }; + let time_provider = DefaultTimeProvider::default(); + let codes = content.lines() - .filter(|f| f.len() >= TOKEN_LENGTH) - .map(String::from) + .filter_map(|line| { + let mut parts = line.split(SEPARATOR); + let token = parts.next(); + let created = parts.next(); + let used = parts.next(); + + match token { + None => None, + Some(token) if token.len() < TOKEN_LENGTH => None, + Some(token) => { + Some(Code { + code: token.into(), + last_used_at: used.and_then(decode_time), + created_at: created.and_then(decode_time) + .unwrap_or_else(|| time::Duration::from_secs(time_provider.now())), + }) + } + } + }) .collect(); Ok(AuthCodes { codes: codes, - now: DefaultTimeProvider::default(), + now: time_provider, }) } @@ -86,19 +126,30 @@ impl AuthCodes { /// Writes all `AuthCodes` to a disk. pub fn to_file(&self, file: &Path) -> io::Result<()> { let mut file = try!(fs::File::create(file)); - let content = self.codes.join("\n"); + let content = self.codes.iter().map(|code| { + let mut data = vec![code.code.clone(), encode_time(code.created_at.clone())]; + if let Some(used_at) = code.last_used_at.clone() { + data.push(encode_time(used_at)); + } + data.join(SEPARATOR) + }).join("\n"); file.write_all(content.as_bytes()) } /// Creates a new `AuthCodes` store with given `TimeProvider`. pub fn new(codes: Vec, now: T) -> Self { AuthCodes { - codes: codes, + codes: codes.into_iter().map(|code| Code { + code: code, + created_at: time::Duration::from_secs(now.now()), + last_used_at: None, + }).collect(), now: now, } } - /// Checks if given hash is correct identifier of `SignerUI` + /// Checks if given hash is correct authcode of `SignerUI` + /// Updates this hash last used field in case it's valid. #[cfg_attr(feature="dev", allow(wrong_self_convention))] pub fn is_valid(&mut self, hash: &H256, time: u64) -> bool { let now = self.now.now(); @@ -121,8 +172,14 @@ impl AuthCodes { } // look for code - self.codes.iter() - .any(|code| &as_token(code) == hash) + for mut code in &mut self.codes { + if &as_token(&code.code) == hash { + code.last_used_at = Some(time::Duration::from_secs(now)); + return true; + } + } + + false } /// Generates and returns a new code that can be used by `SignerUIs` @@ -135,7 +192,11 @@ impl AuthCodes { .collect::>() .join("-"); trace!(target: "signer", "New authentication token generated."); - self.codes.push(code); + self.codes.push(Code { + code: code, + created_at: time::Duration::from_secs(self.now.now()), + last_used_at: None, + }); Ok(readable_code) } @@ -143,12 +204,31 @@ impl AuthCodes { pub fn is_empty(&self) -> bool { self.codes.is_empty() } -} + /// Removes old tokens that have not been used since creation. + pub fn clear_garbage(&mut self) { + let now = self.now.now(); + let threshold = time::Duration::from_secs(now.saturating_sub(UNUSED_TOKEN_TIMEOUT)); + + let codes = mem::replace(&mut self.codes, Vec::new()); + for code in codes { + // Skip codes that are old and were never used. + if code.last_used_at.is_none() && code.created_at <= threshold { + continue; + } + self.codes.push(code); + } + } +} #[cfg(test)] mod tests { + use devtools; + use std::io::{Read, Write}; + use std::{time, fs}; + use std::cell::Cell; + use util::{H256, Hashable}; use super::*; @@ -217,6 +297,54 @@ mod tests { assert_eq!(res2, false); } + #[test] + fn should_read_old_format_from_file() { + // given + let path = devtools::RandomTempPath::new(); + let code = "23521352asdfasdfadf"; + { + let mut file = fs::File::create(&path).unwrap(); + file.write_all(b"a\n23521352asdfasdfadf\nb\n").unwrap(); + } + + // when + let mut authcodes = AuthCodes::from_file(&path).unwrap(); + let time = time::UNIX_EPOCH.elapsed().unwrap().as_secs(); + + // then + assert!(authcodes.is_valid(&generate_hash(code, time), time), "Code should be read from file"); + } + + #[test] + fn should_remove_old_unused_tokens() { + // given + let path = devtools::RandomTempPath::new(); + let code1 = "11111111asdfasdf111"; + let code2 = "22222222asdfasdf222"; + let code3 = "33333333asdfasdf333"; + + let time = Cell::new(100); + let mut codes = AuthCodes::new(vec![code1.into(), code2.into(), code3.into()], || time.get()); + // `code2` should not be removed (we never remove tokens that were used) + codes.is_valid(&generate_hash(code2, time.get()), time.get()); + + // when + time.set(100 + 10_000_000); + // mark `code1` as used now + codes.is_valid(&generate_hash(code1, time.get()), time.get()); + + let new_code = codes.generate_new().unwrap().replace('-', ""); + codes.clear_garbage(); + codes.to_file(&path).unwrap(); + + // then + let mut content = String::new(); + let mut file = fs::File::open(&path).unwrap(); + file.read_to_string(&mut content).unwrap(); + + assert_eq!(content, format!("{};100;10000100\n{};100;100\n{};10000100", code1, code2, new_code)); + } + } diff --git a/signer/src/ws_server/session.rs b/signer/src/ws_server/session.rs index b99ef48ef..5adc3fa80 100644 --- a/signer/src/ws_server/session.rs +++ b/signer/src/ws_server/session.rs @@ -94,6 +94,9 @@ fn auth_is_valid(codes_path: &Path, protocols: ws::Result>) -> bool { // Check if the code is valid AuthCodes::from_file(codes_path) .map(|mut codes| { + // remove old tokens + codes.clear_garbage(); + let res = codes.is_valid(&auth, time); // make sure to save back authcodes - it might have been modified if let Err(_) = codes.to_file(codes_path) { From 29271383dd9a03ccf63fee1a64ff041d33b7cb76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 16 Nov 2016 09:37:48 +0100 Subject: [PATCH 7/7] signer new-token doesn't open the browser --- parity/run.rs | 8 ++++++-- parity/signer.rs | 29 ++++++++++++++++++----------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/parity/run.rs b/parity/run.rs index 085fcfa34..22d00fb1b 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -48,6 +48,7 @@ use signer; use modules; use rpc_apis; use rpc; +use url; // how often to take periodic snapshots. const SNAPSHOT_PERIOD: u64 = 10000; @@ -101,8 +102,11 @@ pub fn open_ui(dapps_conf: &dapps::Configuration, signer_conf: &signer::Configur return Err("Cannot use UI command with UI turned off.".into()) } - let token = try!(signer::generate_token_and_open_ui(signer_conf)); - println!("{}", token); + let token = try!(signer::generate_token_and_url(signer_conf)); + // Open a browser + url::open(&token.url); + // Print a message + println!("{}", token.message); Ok(()) } diff --git a/parity/signer.rs b/parity/signer.rs index 4ce94eae4..6905fbb3c 100644 --- a/parity/signer.rs +++ b/parity/signer.rs @@ -20,7 +20,6 @@ use std::path::PathBuf; use ansi_term::Colour; use io::{ForwardPanic, PanicHandler}; use util::path::restrict_permissions_owner; -use url; use rpc_apis; use ethcore_signer as signer; use helpers::replace_home; @@ -54,6 +53,12 @@ pub struct Dependencies { pub apis: Arc, } +pub struct NewToken { + pub token: String, + pub url: String, + pub message: String, +} + pub fn start(conf: Configuration, deps: Dependencies) -> Result, String> { if !conf.enabled { Ok(None) @@ -70,24 +75,26 @@ fn codes_path(path: String) -> PathBuf { } pub fn execute(cmd: Configuration) -> Result { - generate_token_and_open_ui(&cmd) + Ok(try!(generate_token_and_url(&cmd)).message) } -pub fn generate_token_and_open_ui(conf: &Configuration) -> Result { +pub fn generate_token_and_url(conf: &Configuration) -> Result { let code = try!(generate_new_token(conf.signer_path.clone()).map_err(|err| format!("Error generating token: {:?}", err))); let auth_url = format!("http://{}:{}/#/auth?token={}", conf.interface, conf.port, code); - // Open a browser - url::open(&auth_url); // And print in to the console - Ok(format!( - r#" + Ok(NewToken { + token: code.clone(), + url: auth_url.clone(), + message: format!( + r#" Open: {} to authorize your browser. -Or use the code: +Or use the generated token: {}"#, - Colour::White.bold().paint(auth_url), - code - )) + Colour::White.bold().paint(auth_url), + code + ) + }) } pub fn generate_new_token(path: String) -> io::Result {