Repository: freedomofpress/securedrop

Issue:
[xenial] Add base OS info to metadata endpoint
## Description

Part of #3204. In order to have better visibility into which SecureDrop instances are actively updating to from Ubuntu Trusty to Ubuntu Xenial (and actively engage with admins as needed), it makes sense to expose this info via the existing metadata endpoint.

To avoid breaking existing uses, this should be done in a new `server_os` variable.

From a security perspective, this does not give an attacker key information -- for now, both releases receive security updates; once 14.04 has reached EOL, it will also no longer receive SecureDrop package updates, which will be reflected in its already exposed SecureDrop version.

# User Stories

As a SecureDrop administrator, I'd like to be actively alerted by the support team when I have to perform critical security updates, so that I can act on them with appropriate priority.

As a SecureDrop support team member, I'd like to know whether an instance is following recommended upgrade procedures, so I can increase the urgency of outreach efforts if required.


Discussion:
@nightwarrior-xxx By all means! We're in a crunch over the next couple of weeks to get all the must-do functionality in #3204 in; if you have time to help with this issue over the next few days, it would be much appreciated. :)
@nightwarrior-xxx you commented on this last night but have since deleted your comment. Are you going to work on this or should someone else?
@heartsucker Yes,I would love to work.I thought to ask you on gitter first before taking up this issue

Yeah then it's all yours
@nightwarrior-xxx see the changes added in #4055 for a pretty basic way to check OS versions. 

This would be a really useful addition from a support perspective. Thanks for grabbing it!
@zenmonkeykstop  [Just to clear confusion] I have to import platform and store linux_distribution  in a variable called ```server_os``` in the file ```securedrop/admin/securedrop_admin/init.py```

Or in ```securedrop/admin/tests/test_securedrop-admin-setup.py```
You don't have to add a variable to that file. You can just add it under the route in `source_app/api.py`.

Resolving patch:
diff --git a/securedrop/source_app/api.py b/securedrop/source_app/api.py
--- a/securedrop/source_app/api.py
+++ b/securedrop/source_app/api.py
@@ -1,4 +1,5 @@
 import json
+import platform
 
 from flask import Blueprint, make_response
 
@@ -12,6 +13,7 @@ def make_blueprint(config):
     def metadata():
         meta = {'gpg_fpr': config.JOURNALIST_KEY,
                 'sd_version': version.__version__,
+                'server_os': platform.linux_distribution()[1],
                 }
         resp = make_response(json.dumps(meta))
         resp.headers['Content-Type'] = 'application/json'


Test patch:
diff --git a/securedrop/tests/test_source.py b/securedrop/tests/test_source.py
--- a/securedrop/tests/test_source.py
+++ b/securedrop/tests/test_source.py
@@ -16,6 +16,7 @@
 from db import db
 from models import Source, Reply
 from source_app import main as source_app_main
+from source_app import api as source_app_api
 from utils.db_helper import new_codename
 from utils.instrument import InstrumentedApp
 
@@ -516,12 +517,16 @@ def test_why_journalist_key(source_app):
 
 
 def test_metadata_route(source_app):
-    with source_app.test_client() as app:
-        resp = app.get(url_for('api.metadata'))
-        assert resp.status_code == 200
-        assert resp.headers.get('Content-Type') == 'application/json'
-        assert json.loads(resp.data.decode('utf-8')).get('sd_version') \
-            == version.__version__
+    with patch.object(source_app_api.platform, "linux_distribution") as mocked_platform:
+        mocked_platform.return_value = ("Ubuntu", "16.04", "xenial")
+        with source_app.test_client() as app:
+            resp = app.get(url_for('api.metadata'))
+            assert resp.status_code == 200
+            assert resp.headers.get('Content-Type') == 'application/json'
+            assert json.loads(resp.data.decode('utf-8')).get('sd_version') \
+                == version.__version__
+            assert json.loads(resp.data.decode('utf-8')).get('server_os') \
+                == '16.04'
 
 
 def test_login_with_overly_long_codename(source_app):