Repository: freedomofpress/securedrop

Issue:
securedrop-admin sdconfig erases additional values in site-specific
# Bug

## Description

securedrop-admin sdconfig erases values in site-specific when they are not prompted for. `securedrop-admin sdconfig` should not erase entries in site-specific, which would help testing (e.g.: releases that are in development or perhaps alpha/beta features).

## Steps to Reproduce

* edit `/install_files/ansible-base/group-vars/all/site-specific` and add another value
* run ./securedrop-admin sdconfig
* open `/install_files/ansible-base/group-vars/all/site-specific` and observe your value has disappeared

## Expected Behavior

`securedrop-admin sdconfig` should not erase entries in `/install_files/ansible-base/group-vars/all/site-specific`.

## Actual Behavior
`ecuredrop-admin sdconfig` erases entries in `/install_files/ansible-base/group-vars/all/site-specific`.




Discussion:
@emkll additional values that Ansible would use can be set in a (for instance) `install_files/ansible-base/group-vars/all/myvars` file and it will be loaded. This is indeed convenient for testing.

I think it is good that `site-config` is owned by `securedrop-admin sdconfig` (in the sense that there is no other way to edit it), but it is entirely possible that I missed a use case.

Is there a specific reason that I'm missing for user defined variables to be in the `site-specific` file ?
@dachary This came up when I was testing https://github.com/freedomofpress/securedrop/pull/2803, which required some journalist email specific variables in `site-specific`. This behavior may also come up during development of new features, and perhaps in production, where admins use extra variables to store potentially useful information (e.g. `smtp_relay_old`). I opened this issue because I think it's a regression compared to the behavior of the old `securedrop-admin sdconfig` , but I understand this is more of an edge-case scenario. 

EDIT: checked out `0.5.1` branch and ran `./securedrop-admin sdconfig` and the extra variables in `site-specific` are *not* deleted after the run.
@emkll thanks a lot for filling this, it would have been problematic if discovered after the release. The regression is as follows:

* An admin stored extra variables in `site-config` (they should not and it is best served in another file but it is entirely possible that they did)
* The admin key is updated to 0.6
* The admin runs `securedrop-admin sdconfig` (for whatever motive)
* The extra variables in `site-specific` are silentely removed
* The admin runs `securedrop-admin install` and unexpectedly modifies the production instances because the variable is no longer in `site-specific`, leading to unknown regressions

I propose that it is fixed as follows:

* `securedrop-admin sdconfig` loads `site-specific`
* for each top level YAML variable it does not know a message is displayed (**WARNING: custom variable ABC must not be stored in `site-config`, it must be stored in the `custom-variables` file instead**)
* when `securedrop-admin sdconfig` runs it keeps the unknown variables instead of removing them

An alternative would be to write all unknown variables into another file `custom-variables`. But I'm not a fan of this option as it would likely confuse the admin expecting the variables to be in `site-specific`.

That approach makes sense to me @dachary. The alternative `custom-variables` option is not only confusing but may lead to more issues (`.gitignore`?).

Perhaps the error message could also be worded to suggest the user entered a valid (non-custom) but misspelled variable name in`site-specific`.

Resolving patch:
diff --git a/admin/securedrop_admin/__init__.py b/admin/securedrop_admin/__init__.py
--- a/admin/securedrop_admin/__init__.py
+++ b/admin/securedrop_admin/__init__.py
@@ -291,7 +291,7 @@ def load_and_update_config(self):
         return self.update_config()
 
     def update_config(self):
-        self.config = self.user_prompt_config()
+        self.config.update(self.user_prompt_config())
         self.save()
         self.validate_gpg_keys()
         return True


Test patch:
diff --git a/admin/tests/files/site-specific b/admin/tests/files/site-specific
--- a/admin/tests/files/site-specific
+++ b/admin/tests/files/site-specific
@@ -17,3 +17,4 @@ securedrop_supported_locales:
 smtp_relay: smtp.gmail.com
 smtp_relay_port: 587
 ssh_users: sd
+user_defined_variable: "must not be discarded"
diff --git a/admin/tests/test_securedrop-admin.py b/admin/tests/test_securedrop-admin.py
--- a/admin/tests/test_securedrop-admin.py
+++ b/admin/tests/test_securedrop-admin.py
@@ -297,6 +297,7 @@ def test_update_config(self, mock_save, mock_validate_input):
         site_config = securedrop_admin.SiteConfig(args)
 
         assert site_config.load_and_update_config()
+        assert 'user_defined_variable' in site_config.config
         mock_save.assert_called_once()
         mock_validate_input.assert_called()