Client: add new password-file command line option

- add new global command line argument to the signer
  -f --password-file <filename>: Absolute path of the password file
- add password_filename to wallet context
This commit is contained in:
Pietro 2018-08-02 17:35:34 +02:00 committed by Grégoire Henry
parent 28238fce9e
commit 1c0561ee53
No known key found for this signature in database
GPG Key ID: 50D984F20BD445D2
9 changed files with 81 additions and 16 deletions

View File

@ -259,10 +259,19 @@ let require_auth_arg () =
~doc:"Require a signature from the caller to sign." ~doc:"Require a signature from the caller to sign."
() ()
let password_filename_arg () =
arg
~long:"password-file"
~short:'f'
~placeholder:"filename"
~doc:"Absolute path of the password file"
(string_parameter ())
let global_options () = let global_options () =
args2 args3
(base_dir_arg ()) (base_dir_arg ())
(require_auth_arg ()) (require_auth_arg ())
(password_filename_arg ())
(* Main (lwt) entry *) (* Main (lwt) entry *)
let main () = let main () =
@ -286,12 +295,13 @@ let main () =
begin begin
begin begin
parse_global_options parse_global_options
(global_options ()) () original_args >>=? fun ((base_dir, require_auth), remaining) -> (global_options ()) () original_args >>=?
fun ((base_dir, require_auth, password_filename), remaining) ->
let base_dir = Option.unopt ~default:default_base_dir base_dir in let base_dir = Option.unopt ~default:default_base_dir base_dir in
let cctxt = object let cctxt = object
inherit Client_context_unix.unix_logger ~base_dir inherit Client_context_unix.unix_logger ~base_dir
inherit Client_context_unix.unix_prompter inherit Client_context_unix.unix_prompter
inherit Client_context_unix.unix_wallet ~base_dir inherit Client_context_unix.unix_wallet ~base_dir ~password_filename
end in end in
Client_keys.register_signer Client_keys.register_signer
(module Tezos_signer_backends.Encrypted.Make(struct (module Tezos_signer_backends.Encrypted.Make(struct

View File

@ -65,6 +65,7 @@ class simple_printer log =
end end
class type wallet = object class type wallet = object
method password_filename : string option
method with_lock : (unit -> 'a Lwt.t) -> 'a Lwt.t method with_lock : (unit -> 'a Lwt.t) -> 'a Lwt.t
method load : string -> default:'a -> 'a Data_encoding.encoding -> 'a tzresult Lwt.t method load : string -> default:'a -> 'a Data_encoding.encoding -> 'a tzresult Lwt.t
method write : string -> 'a -> 'a Data_encoding.encoding -> unit tzresult Lwt.t method write : string -> 'a -> 'a Data_encoding.encoding -> unit tzresult Lwt.t
@ -96,6 +97,7 @@ class type full = object
end end
class proxy_context (obj : full) = object class proxy_context (obj : full) = object
method password_filename = obj#password_filename
method base = obj#base method base = obj#base
method block = obj#block method block = obj#block
method confirmations = obj#confirmations method confirmations = obj#confirmations

View File

@ -45,6 +45,7 @@ class type io = object
end end
class type wallet = object class type wallet = object
method password_filename : string option
method with_lock : (unit -> 'a Lwt.t) -> 'a Lwt.t method with_lock : (unit -> 'a Lwt.t) -> 'a Lwt.t
method load : string -> default:'a -> 'a Data_encoding.encoding -> 'a tzresult Lwt.t method load : string -> default:'a -> 'a Data_encoding.encoding -> 'a tzresult Lwt.t
method write : string -> 'a -> 'a Data_encoding.encoding -> unit tzresult Lwt.t method write : string -> 'a -> 'a Data_encoding.encoding -> unit tzresult Lwt.t

View File

@ -106,6 +106,7 @@ module Cfg_file = struct
web_port: int ; web_port: int ;
remote_signer: Uri.t option ; remote_signer: Uri.t option ;
confirmations: int option ; confirmations: int option ;
password_filename: string option ;
} }
let default = { let default = {
@ -116,6 +117,7 @@ module Cfg_file = struct
web_port = 8080 ; web_port = 8080 ;
remote_signer = None ; remote_signer = None ;
confirmations = Some 0 ; confirmations = Some 0 ;
password_filename = None ;
} }
open Data_encoding open Data_encoding
@ -123,25 +125,27 @@ module Cfg_file = struct
let encoding = let encoding =
conv conv
(fun { base_dir ; node_addr ; node_port ; tls ; web_port ; (fun { base_dir ; node_addr ; node_port ; tls ; web_port ;
remote_signer ; confirmations } -> remote_signer ; confirmations ; password_filename } ->
(base_dir, Some node_addr, Some node_port, (base_dir, Some node_addr, Some node_port,
Some tls, Some web_port, remote_signer, confirmations)) Some tls, Some web_port, remote_signer, confirmations, password_filename ))
(fun (base_dir, node_addr, node_port, tls, web_port, (fun (base_dir, node_addr, node_port, tls, web_port,
remote_signer, confirmations) -> remote_signer, confirmations, password_filename) ->
let node_addr = Option.unopt ~default:default.node_addr node_addr in let node_addr = Option.unopt ~default:default.node_addr node_addr in
let node_port = Option.unopt ~default:default.node_port node_port in let node_port = Option.unopt ~default:default.node_port node_port in
let tls = Option.unopt ~default:default.tls tls in let tls = Option.unopt ~default:default.tls tls in
let web_port = Option.unopt ~default:default.web_port web_port in let web_port = Option.unopt ~default:default.web_port web_port in
{ base_dir ; node_addr ; node_port ; tls ; web_port ; { base_dir ; node_addr ; node_port ; tls ; web_port ;
remote_signer ; confirmations }) remote_signer ; confirmations ; password_filename })
(obj7 (obj8
(req "base_dir" string) (req "base_dir" string)
(opt "node_addr" string) (opt "node_addr" string)
(opt "node_port" int16) (opt "node_port" int16)
(opt "tls" bool) (opt "tls" bool)
(opt "web_port" int16) (opt "web_port" int16)
(opt "remote_signer" RPC_client.uri_encoding) (opt "remote_signer" RPC_client.uri_encoding)
(opt "confirmations" int8)) (opt "confirmations" int8)
(opt "password_filename" string)
)
let from_json json = let from_json json =
Data_encoding.Json.destruct encoding json Data_encoding.Json.destruct encoding json
@ -159,6 +163,7 @@ end
type cli_args = { type cli_args = {
block: Shell_services.block ; block: Shell_services.block ;
confirmations: int option ; confirmations: int option ;
password_filename: string option ;
protocol: Protocol_hash.t option ; protocol: Protocol_hash.t option ;
print_timings: bool ; print_timings: bool ;
log_requests: bool ; log_requests: bool ;
@ -167,6 +172,7 @@ type cli_args = {
let default_cli_args = { let default_cli_args = {
block = default_block ; block = default_block ;
confirmations = Some 0 ; confirmations = Some 0 ;
password_filename = None ;
protocol = None ; protocol = None ;
print_timings = false ; print_timings = false ;
log_requests = false ; log_requests = false ;
@ -298,6 +304,13 @@ let remote_signer_arg () =
~doc:"URI of the remote signer" ~doc:"URI of the remote signer"
(parameter (parameter
(fun _ x -> Tezos_signer_backends.Remote.parse_base_uri x)) (fun _ x -> Tezos_signer_backends.Remote.parse_base_uri x))
let password_filename_arg () =
arg
~long:"password-filename"
~short:'f'
~placeholder:"filename"
~doc:"path to the passowrd filename"
(string_parameter ())
let read_config_file config_file = let read_config_file config_file =
Lwt_utils_unix.Json.read_file config_file >>=? fun cfg_json -> Lwt_utils_unix.Json.read_file config_file >>=? fun cfg_json ->
@ -372,7 +385,7 @@ let commands config_file cfg =
] ]
let global_options () = let global_options () =
args11 args12
(base_dir_arg ()) (base_dir_arg ())
(config_file_arg ()) (config_file_arg ())
(timings_switch ()) (timings_switch ())
@ -384,6 +397,7 @@ let global_options () =
(port_arg ()) (port_arg ())
(tls_switch ()) (tls_switch ())
(remote_signer_arg ()) (remote_signer_arg ())
(password_filename_arg ())
let parse_config_args (ctx : #Client_context.full) argv = let parse_config_args (ctx : #Client_context.full) argv =
parse_global_options parse_global_options
@ -400,7 +414,8 @@ let parse_config_args (ctx : #Client_context.full) argv =
node_addr, node_addr,
node_port, node_port,
tls, tls,
remote_signer), remaining) -> remote_signer,
password_filename), remaining) ->
begin match base_dir with begin match base_dir with
| None -> | None ->
let base_dir = default_base_dir in let base_dir = default_base_dir in
@ -443,7 +458,7 @@ let parse_config_args (ctx : #Client_context.full) argv =
(Option.first_some remote_signer_env cfg.remote_signer) in (Option.first_some remote_signer_env cfg.remote_signer) in
let confirmations = Option.unopt ~default:cfg.confirmations confirmations in let confirmations = Option.unopt ~default:cfg.confirmations confirmations in
let cfg = { cfg with tls ; node_port ; node_addr ; let cfg = { cfg with tls ; node_port ; node_addr ;
remote_signer ; confirmations } in remote_signer ; confirmations ; password_filename } in
if Sys.file_exists base_dir && not (Sys.is_directory base_dir) then begin if Sys.file_exists base_dir && not (Sys.is_directory base_dir) then begin
Format.eprintf "%s is not a directory.@." base_dir ; Format.eprintf "%s is not a directory.@." base_dir ;
exit 1 ; exit 1 ;
@ -455,6 +470,6 @@ let parse_config_args (ctx : #Client_context.full) argv =
Lwt_utils_unix.create_dir config_dir >>= fun () -> Lwt_utils_unix.create_dir config_dir >>= fun () ->
return return
(cfg, (cfg,
{ block ; confirmations ; { block ; confirmations ; password_filename ;
print_timings = timings ; log_requests ; protocol }, print_timings = timings ; log_requests ; protocol },
commands config_file cfg, remaining) commands config_file cfg, remaining)

View File

@ -28,7 +28,9 @@ include Tezos_stdlib.Logging.Make_semantic(struct let name = "client.context.uni
let filename_tag = Tag.def ~doc:"Filename" "filename" Format.pp_print_string let filename_tag = Tag.def ~doc:"Filename" "filename" Format.pp_print_string
class unix_wallet ~base_dir : wallet = object (self) class unix_wallet ~base_dir ~password_filename : wallet = object (self)
method password_filename = password_filename
method private filename alias_name = method private filename alias_name =
Filename.concat Filename.concat
@ -130,11 +132,11 @@ class unix_logger ~base_dir =
inherit Client_context.simple_printer log inherit Client_context.simple_printer log
end end
class unix_full ~base_dir ~block ~confirmations ~rpc_config : Client_context.full = class unix_full ~base_dir ~block ~confirmations ~password_filename ~rpc_config : Client_context.full =
object object
inherit unix_logger ~base_dir inherit unix_logger ~base_dir
inherit unix_prompter inherit unix_prompter
inherit unix_wallet ~base_dir inherit unix_wallet ~base_dir ~password_filename
inherit RPC_client.http_ctxt rpc_config Media_type.all_media_types inherit RPC_client.http_ctxt rpc_config Media_type.all_media_types
method block = block method block = block
method confirmations = confirmations method confirmations = confirmations

View File

@ -25,6 +25,7 @@
class unix_wallet : class unix_wallet :
base_dir:string -> base_dir:string ->
password_filename: string option ->
Client_context.wallet Client_context.wallet
class unix_prompter : class unix_prompter :
Client_context.prompter Client_context.prompter
@ -35,5 +36,6 @@ class unix_full :
base_dir:string -> base_dir:string ->
block:Shell_services.block -> block:Shell_services.block ->
confirmations:int option -> confirmations:int option ->
password_filename: string option ->
rpc_config:RPC_client.config -> rpc_config:RPC_client.config ->
Client_context.full Client_context.full

View File

@ -67,6 +67,7 @@ let main select_commands =
(new unix_full (new unix_full
~block:Client_config.default_block ~block:Client_config.default_block
~confirmations:None ~confirmations:None
~password_filename:None
~base_dir:Client_config.default_base_dir ~base_dir:Client_config.default_base_dir
~rpc_config:RPC_client.default_config) ~rpc_config:RPC_client.default_config)
original_args original_args
@ -90,6 +91,7 @@ let main select_commands =
new unix_full new unix_full
~block:parsed_args.block ~block:parsed_args.block
~confirmations:parsed_args.confirmations ~confirmations:parsed_args.confirmations
~password_filename: parsed_args.password_filename
~base_dir:parsed_config_file.base_dir ~base_dir:parsed_config_file.base_dir
~rpc_config:rpc_config in ~rpc_config:rpc_config in
Client_keys.register_signer Client_keys.register_signer

View File

@ -132,6 +132,9 @@ module Encodings = struct
end end
let decrypted = Hashtbl.create 13 let decrypted = Hashtbl.create 13
(* we cache the password in this list to avoid
asking the user all the time *)
let passwords = ref [] let passwords = ref []
let rec interactive_decrypt_loop let rec interactive_decrypt_loop
@ -152,6 +155,31 @@ let rec interactive_decrypt_loop
| None -> | None ->
interactive_decrypt_loop cctxt ?name ~encrypted_sk algo interactive_decrypt_loop cctxt ?name ~encrypted_sk algo
(* FixMe : this could be done more elegantly *)
let read_all_lines filename =
let lines = ref [] in
let chan = open_in filename in
try
while true; do
lines := input_line chan :: !lines
done; !lines
with End_of_file ->
close_in chan;
List.rev !lines
(* add all passwords in [filename] to the list of known passwords *)
let password_file_load = function
|Some filename ->
if Sys.file_exists filename then begin
let l = read_all_lines filename in
let l = List.map MBytes.of_string l in
passwords := !passwords @ l ;
return_unit
end
else
return_unit
| None -> return_unit
let rec noninteractive_decrypt_loop algo ~encrypted_sk = function let rec noninteractive_decrypt_loop algo ~encrypted_sk = function
| [] -> return_none | [] -> return_none
| password :: passwords -> | password :: passwords ->
@ -181,6 +209,7 @@ let decrypt (cctxt : #Client_context.prompter) ?name sk_uri =
let decrypt_all (cctxt : #Client_context.io_wallet) = let decrypt_all (cctxt : #Client_context.io_wallet) =
Secret_key.load cctxt >>=? fun sks -> Secret_key.load cctxt >>=? fun sks ->
password_file_load cctxt#password_filename >>=? fun () ->
iter_s begin fun (name, sk_uri) -> iter_s begin fun (name, sk_uri) ->
if Uri.scheme (sk_uri : sk_uri :> Uri.t) <> Some scheme then if Uri.scheme (sk_uri : sk_uri :> Uri.t) <> Some scheme then
return_unit return_unit
@ -191,6 +220,7 @@ let decrypt_all (cctxt : #Client_context.io_wallet) =
let decrypt_list (cctxt : #Client_context.io_wallet) keys = let decrypt_list (cctxt : #Client_context.io_wallet) keys =
Secret_key.load cctxt >>=? fun sks -> Secret_key.load cctxt >>=? fun sks ->
password_file_load cctxt#password_filename >>=? fun () ->
iter_s begin fun (name, sk_uri) -> iter_s begin fun (name, sk_uri) ->
if Uri.scheme (sk_uri : sk_uri :> Uri.t) = Some scheme && if Uri.scheme (sk_uri : sk_uri :> Uri.t) = Some scheme &&
(keys = [] || List.mem name keys) then (keys = [] || List.mem name keys) then

View File

@ -56,6 +56,7 @@ let no_write_context ?(block = `Head 0) config : #Client_context.full = object
method with_lock : type a. (unit -> a Lwt.t) -> a Lwt.t = fun f -> f () method with_lock : type a. (unit -> a Lwt.t) -> a Lwt.t = fun f -> f ()
method block = block method block = block
method confirmations = None method confirmations = None
method password_filename = None
method prompt : type a. (a, string tzresult) Client_context.lwt_format -> a = method prompt : type a. (a, string tzresult) Client_context.lwt_format -> a =
Format.kasprintf (fun _ -> return "") Format.kasprintf (fun _ -> return "")
method prompt_password : type a. (a, MBytes.t tzresult) Client_context.lwt_format -> a = method prompt_password : type a. (a, MBytes.t tzresult) Client_context.lwt_format -> a =