Repository: freedomofpress/securedrop

Issue:
Add test data to development VM
This is one of several developer workflow issues. We should have some test data and accounts loaded upon provisioning of the development VM. This prevents a developer from doing the "make a test admin account, make a test journalist account, load the source interface and submit a bunch of documents" dance every time she wants to manually test something. 


Discussion:
Agreed! We could also automatically start the development interfaces, so the forwarded ports are accessible right out of the gate. 

Related: in the context of #749, I'm considering generating prod-specific.yml files so prod environments can be provisioned in an unattended manner, as well—doing so would greatly aid in fleshing out the CI pipeline (#1067).

Resolving patch:
diff --git a/securedrop/create-demo-user.py b/securedrop/create-demo-user.py
deleted file mode 100755
--- a/securedrop/create-demo-user.py
+++ /dev/null
@@ -1,42 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-
-import os
-from sqlalchemy.exc import IntegrityError
-
-os.environ["SECUREDROP_ENV"] = "dev"  # noqa
-import journalist_app
-from sdconfig import config
-from db import db
-from models import Journalist
-
-
-def add_test_user(username, password, otp_secret, is_admin=False):
-    context = journalist_app.create_app(config).app_context()
-    context.push()
-    valid_password = "correct horse battery staple profanity oil chewy"
-
-    try:
-        user = Journalist(username=username,
-                          password=valid_password,
-                          is_admin=is_admin)
-        user.otp_secret = otp_secret
-        user.pw_salt = user._gen_salt()
-        user.pw_hash = user._scrypt_hash(password, user.pw_salt)
-        db.session.add(user)
-        db.session.commit()
-        print('Test user successfully added: '
-              'username={}, password={}, otp_secret={}'
-              ''.format(username, password, otp_secret))
-    except IntegrityError:
-        print("Test user already added")
-        db.session.rollback()
-
-    context.pop()
-
-
-if __name__ == "__main__":  # pragma: no cover
-    add_test_user("journalist",
-                  "WEjwn8ZyczDhQSK24YKM8C9a",
-                  "JHCOGO7VCER3EJ4L",
-                  is_admin=True)
diff --git a/securedrop/create-dev-data.py b/securedrop/create-dev-data.py
new file mode 100755
--- /dev/null
+++ b/securedrop/create-dev-data.py
@@ -0,0 +1,90 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import os
+from sqlalchemy.exc import IntegrityError
+
+os.environ["SECUREDROP_ENV"] = "dev"  # noqa
+import journalist_app
+from sdconfig import config
+from db import db
+from models import Journalist, Source, Submission
+
+
+def add_test_user(username, password, otp_secret, is_admin=False):
+    context = journalist_app.create_app(config).app_context()
+    context.push()
+    valid_password = "correct horse battery staple profanity oil chewy"
+
+    try:
+        user = Journalist(username=username,
+                          password=valid_password,
+                          is_admin=is_admin)
+        user.otp_secret = otp_secret
+        user.pw_salt = user._gen_salt()
+        user.pw_hash = user._scrypt_hash(password, user.pw_salt)
+        db.session.add(user)
+        db.session.commit()
+        print('Test user successfully added: '
+              'username={}, password={}, otp_secret={}, is_admin={}'
+              ''.format(username, password, otp_secret, is_admin))
+    except IntegrityError:
+        print("Test user already added")
+        db.session.rollback()
+
+    context.pop()
+
+
+def create_source_and_submissions(num_submissions=2):
+    app = journalist_app.create_app(config)
+
+    with app.app_context():
+        # Store source in database
+        codename = app.crypto_util.genrandomid()
+        filesystem_id = app.crypto_util.hash_codename(codename)
+        journalist_designation = app.crypto_util.display_id()
+        source = Source(filesystem_id, journalist_designation)
+        source.pending = False
+        db.session.add(source)
+        db.session.commit()
+
+        # Generate submissions directory and generate source key
+        os.mkdir(app.storage.path(source.filesystem_id))
+        app.crypto_util.genkeypair(source.filesystem_id, codename)
+
+        # Generate some test submissions
+        for _ in range(num_submissions):
+            source.interaction_count += 1
+            fpath = app.storage.save_message_submission(
+                source.filesystem_id,
+                source.interaction_count,
+                source.journalist_filename,
+                'test submission!'
+            )
+            submission = Submission(source, fpath)
+            db.session.add(submission)
+
+        db.session.commit()
+        print("Test source '{}' added with {} submissions".format(
+            journalist_designation, num_submissions)
+        )
+
+
+if __name__ == "__main__":  # pragma: no cover
+    # Add two test users
+    test_password = "WEjwn8ZyczDhQSK24YKM8C9a"
+    test_otp_secret = "JHCOGO7VCER3EJ4L"
+
+    add_test_user("journalist",
+                  test_password,
+                  test_otp_secret,
+                  is_admin=True)
+    add_test_user("dellsberg",
+                  test_password,
+                  test_otp_secret,
+                  is_admin=False)
+
+    # Add test sources and submissions
+    num_sources = 2
+    for _ in range(num_sources):
+        create_source_and_submissions()


Test patch: