scripts: Make generate_secrets.py idempotent.

Now, generate_secrets.py will never overwrite existing secrets.  In
addition to being a safer model in generate, this fixes 2 significant
issues:

(1) It makes it much easier to preserve secrets like Oauth tokens in a
development environment (previously, provision would destroy them).
(2) It makes it possible to automatically add new secrets as part of
the upgrade process.  In particular, this is useful for the
zulip_org_id settings.

Fixes #4797.
This commit is contained in:
Tim Abbott
2017-05-16 22:02:24 -07:00
parent 03b5200d8b
commit 2c6a91e24a

View File

@@ -61,14 +61,7 @@ def get_old_conf(output_filename):
secrets_file = six.moves.configparser.RawConfigParser() # type: ignore # https://github.com/python/typeshed/issues/307
secrets_file.read(output_filename)
def get_secret(key):
# type: (str) -> Optional[Text]
if secrets_file.has_option('secrets', key):
return secrets_file.get('secrets', key)
return None
fields = AUTOGENERATED_SETTINGS + ['secret_key', 'camo_key']
return {name: get_secret(name) for name in fields}
return dict(secrets_file.items("secrets"))
def generate_secrets(development=False):
# type: (bool) -> None
@@ -76,38 +69,49 @@ def generate_secrets(development=False):
OUTPUT_SETTINGS_FILENAME = "zproject/dev-secrets.conf"
else:
OUTPUT_SETTINGS_FILENAME = "/etc/zulip/zulip-secrets.conf"
current_conf = get_old_conf(OUTPUT_SETTINGS_FILENAME)
lines = [u'[secrets]\n']
lines = []
if len(current_conf) == 0:
lines = [u'[secrets]\n']
def config_line(var, value):
# type: (Text, Text) -> Text
return "%s = %s\n" % (var, value)
def need_secret(name):
# type: (Text) -> bool
return name not in current_conf
def add_secret(name, value):
# type: (Text, Text) -> None
lines.append("%s = %s\n" % (name, value))
current_conf[name] = value
old_conf = get_old_conf(OUTPUT_SETTINGS_FILENAME)
for name in AUTOGENERATED_SETTINGS:
lines.append(config_line(name, old_conf.get(name, generate_random_token(64))))
if need_secret(name):
add_secret(name, generate_random_token(64))
secret_key = old_conf.get('secret_key', generate_django_secretkey())
lines.append(config_line('secret_key', secret_key))
if need_secret('secret_key'):
add_secret('secret_key', generate_django_secretkey())
camo_key = old_conf.get('camo_key', get_random_string(64))
lines.append(config_line('camo_key', camo_key))
if need_secret('camo_key'):
add_secret('camo_key', get_random_string(64))
zulip_org_key = old_conf.get('zulip_org_key', get_random_string(64))
lines.append(config_line('zulip_org_key', zulip_org_key))
zulip_org_id = old_conf.get('zulip_org_id', str(uuid.uuid4()))
lines.append(config_line('zulip_org_id', zulip_org_id))
if need_secret('zulip_org_key'):
add_secret('zulip_org_key', get_random_string(64))
if need_secret('zulip_org_id'):
add_secret('zulip_org_id', str(uuid.uuid4()))
if not development:
# Write the Camo config file directly
generate_camo_config_file(camo_key)
generate_camo_config_file(current_conf['camo_key'])
out = open(OUTPUT_SETTINGS_FILENAME, 'w')
if len(lines) == 0:
print("generate_secrets: No new secrets to generate.")
return
out = open(OUTPUT_SETTINGS_FILENAME, 'a')
out.write(force_str("".join(lines)))
out.close()
print("Generated %s with auto-generated secrets!" % (OUTPUT_SETTINGS_FILENAME,))
print("Generated new secrets in %s." % (OUTPUT_SETTINGS_FILENAME,))
if __name__ == '__main__':