@@ -187,7 +187,8 @@ def __init__(
187187 'admin.conf' ,
188188 'admin.dump_conf' ,
189189 'admin.print_conf' ,
190- 'admin.strict'
190+ 'admin.strict' ,
191+ 'admin.expose_secrets' ,
191192 ]
192193 self .options_banned_from_help = options_banned_from_help
193194
@@ -292,18 +293,13 @@ def get_config(self, mapping_class=DotDictWithAcquisition):
292293 return config
293294
294295 #--------------------------------------------------------------------------
295- def output_summary (self ,
296- output_stream = sys .stdout ,
297- block_password = True ):
296+ def output_summary (self , output_stream = sys .stdout ):
298297 """outputs a usage tip and the list of acceptable commands.
299298 This is useful as the output of the 'help' option.
300299
301300 parameters:
302301 output_stream - an open file-like object suitable for use as the
303302 target of a print statement
304- block_password - a boolean driving the use of a string of * in
305- place of the value for any object containing the
306- substring 'passowrd'
307303 """
308304 if self .app_name or self .app_description :
309305 print >> output_stream , 'Application:' ,
@@ -316,10 +312,8 @@ def output_summary(self,
316312
317313 names_list = self .get_option_names ()
318314 print >> output_stream , (
319- "usage:\n " ,
320- self .app_invocation_name ,
321- "[OPTIONS]..."
322- ),
315+ "usage:\n %s [OPTIONS]..." % self .app_invocation_name
316+ )
323317 bracket_count = 0
324318 for key in names_list :
325319 an_option = self .option_definitions [key ]
@@ -359,7 +353,8 @@ def output_summary(self,
359353 except KeyError :
360354 default = option .value
361355 if default is not None :
362- if 'password' in name .lower ():
356+ if ((option .secret or 'password' in name .lower ())
357+ and not self .option_definitions .admin .expose_secrets .default ):
363358 default = '*********'
364359 if name not in ('help' ,):
365360 # don't bother with certain dead obvious ones
@@ -448,6 +443,18 @@ def write_conf(self, config_file_type, opener, skip_keys=None):
448443 else :
449444 option_defs = self .option_definitions
450445
446+ # find all of the secret options and overwrite their values with '*' * 16
447+ if not self .option_definitions .admin .expose_secrets .default :
448+ for a_key in option_defs .keys_breadth_first ():
449+ an_option = option_defs [a_key ]
450+ if ((not a_key .startswith ('admin' ))
451+ and isinstance (an_option , Option )
452+ and an_option .secret
453+ ):
454+ # force the option to be a string of *
455+ option_defs [a_key ].value = '*' * 16
456+ option_defs [a_key ].from_string_converter = str
457+
451458 value_sources .write (config_file_type ,
452459 option_defs ,
453460 opener )
@@ -468,7 +475,10 @@ def log_config(self, logger):
468475 if key not in self .admin_controls_list ]
469476 config .sort ()
470477 for key , val in config :
471- if 'password' in key .lower ():
478+ if (
479+ self .option_definitions [key ].secret
480+ or 'password' in key .lower ()
481+ ):
472482 logger .info ('%s: *********' , key )
473483 else :
474484 try :
@@ -800,6 +810,11 @@ def _setup_admin_options(self, values_source_list):
800810 doc = 'mismatched options generate exceptions rather'
801811 ' than just warnings'
802812 )
813+ admin .add_option (
814+ name = 'expose_secrets' ,
815+ default = False ,
816+ doc = 'should options marked secret get written out or hidden?'
817+ )
803818 # only offer the config file admin options if they've been requested in
804819 # the values source list
805820 if ConfigFileFutureProxy in values_source_list :
@@ -847,13 +862,6 @@ def _option_sort(x_tuple):
847862 else :
848863 return key
849864
850- #--------------------------------------------------------------------------
851- @staticmethod
852- def _block_password (qkey , key , value , block_password = True ):
853- if block_password and 'password' in key .lower ():
854- value = '*********'
855- return qkey , key , value
856-
857865 #--------------------------------------------------------------------------
858866 def _get_option (self , name ):
859867 try :
0 commit comments