Skip to content
This repository was archived by the owner on Mar 29, 2024. It is now read-only.

Commit 3e3c72a

Browse files
committed
Merge pull request #130 from twobraids/secrets
my take on not exposing secrets
2 parents f6be470 + 4c3b824 commit 3e3c72a

File tree

5 files changed

+192
-51
lines changed

5 files changed

+192
-51
lines changed

configman/config_manager.py

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -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:

configman/namespace.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,8 @@ def add_option(self, name, *args, **kwargs):
8282
setattr(current_namespace, an_option.name, an_option)
8383

8484
#--------------------------------------------------------------------------
85-
def add_aggregation(self, name, function):
86-
an_aggregation = Aggregation(name, function)
85+
def add_aggregation(self, name, function, secret=False):
86+
an_aggregation = Aggregation(name, function, secret)
8787
setattr(self, name, an_aggregation)
8888

8989
#--------------------------------------------------------------------------
@@ -137,4 +137,3 @@ def ref_value_namespace(self):
137137
# the __setattr__ method, this is the only way to actually force a
138138
# value to become an attribute rather than member of the dict
139139
object.__setattr__(self, '_reference_value_from', True)
140-

configman/option.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ def __init__(
6060
likely_to_be_changed=False,
6161
not_for_definition=False,
6262
reference_value_from=None,
63+
secret=False,
6364
):
6465
self.name = name
6566
self.short_form = short_form
@@ -86,6 +87,7 @@ def __init__(
8687
self.likely_to_be_changed = likely_to_be_changed
8788
self.not_for_definition = not_for_definition
8889
self.reference_value_from = reference_value_from
90+
self.secret = secret
8991

9092
#--------------------------------------------------------------------------
9193
def __str__(self):
@@ -205,7 +207,8 @@ def copy(self):
205207
is_argument=self.is_argument,
206208
likely_to_be_changed=self.likely_to_be_changed,
207209
not_for_definition=self.not_for_definition,
208-
reference_value_from=self.reference_value_from
210+
reference_value_from=self.reference_value_from,
211+
secret=self.secret,
209212
)
210213
return o
211214

@@ -216,14 +219,16 @@ class Aggregation(object):
216219
def __init__(
217220
self,
218221
name,
219-
function
222+
function,
223+
secret=False,
220224
):
221225
self.name = name
222226
if isinstance(function, basestring):
223227
self.function = conv.class_converter(function)
224228
else:
225229
self.function = function
226230
self.value = None
231+
self.secret = secret
227232

228233
#--------------------------------------------------------------------------
229234
def aggregate(self, all_options, local_namespace, args):

0 commit comments

Comments
 (0)