Skip to content

Commit

Permalink
remove global config variable, force injection everywhere
Browse files Browse the repository at this point in the history
  • Loading branch information
heartsucker committed Dec 13, 2018
1 parent f0b7a8c commit a808d41
Show file tree
Hide file tree
Showing 19 changed files with 183 additions and 192 deletions.
3 changes: 2 additions & 1 deletion securedrop/alembic/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
# These imports are only needed for offline generation of automigrations.
# Importing them in a prod-like environment breaks things.
from journalist_app import create_app # noqa
from sdconfig import config as sdconfig # noqa
from sdconfig import SDConfig # noqa
sdconfig = SDConfig()

# App context is needed for autogenerated migrations
create_app(sdconfig).app_context().push()
Expand Down
2 changes: 1 addition & 1 deletion securedrop/bin/run-test
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ fi
mkdir -p "/tmp/test-results/logs"

DATA_DIR='/var/lib/securedrop/'
sudo mkdir "$DATA_DIR"
sudo mkdir -p "$DATA_DIR"
sudo chown "$(id -u):$(id -g)" "$DATA_DIR"

: "${PAGE_LAYOUT_LOCALES:=en_US,ar,fr_FR}"
Expand Down
7 changes: 4 additions & 3 deletions securedrop/create-dev-data.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@
os.environ["SECUREDROP_ENV"] = "dev" # noqa
import journalist_app

from sdconfig import config
from db import db
from models import Journalist, Reply, Source, Submission
from sdconfig import SDConfig


def main():
config = SDConfig()
app = journalist_app.create_app(config)
with app.app_context():
# Add two test users
Expand All @@ -34,7 +35,7 @@ def main():
# Add test sources and submissions
num_sources = 2
for _ in range(num_sources):
create_source_and_submissions()
create_source_and_submissions(config)


def add_test_user(username, password, otp_secret, is_admin=False):
Expand All @@ -53,7 +54,7 @@ def add_test_user(username, password, otp_secret, is_admin=False):
db.session.rollback()


def create_source_and_submissions(num_submissions=2, num_replies=2):
def create_source_and_submissions(config, num_submissions=2, num_replies=2):
# Store source in database
codename = current_app.crypto_util.genrandomid()
filesystem_id = current_app.crypto_util.hash_codename(codename)
Expand Down
5 changes: 2 additions & 3 deletions securedrop/journalist.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
# -*- coding: utf-8 -*-

from sdconfig import config

from journalist_app import create_app
from sdconfig import SDConfig

config = SDConfig()
app = create_app(config)


Expand Down
47 changes: 22 additions & 25 deletions securedrop/manage.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,28 +22,24 @@
from sqlalchemy.orm import sessionmaker

os.environ['SECUREDROP_ENV'] = 'dev' # noqa
from sdconfig import config
import journalist_app

from db import db
from models import Source, Journalist, PasswordError, InvalidUsernameException
from management.run import run
from sdconfig import SDConfig

logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s')
log = logging.getLogger(__name__)


def reset(args):
def reset(args, config):
"""Clears the SecureDrop development applications' state, restoring them to
the way they were immediately after running `setup_dev.sh`. This command:
1. Erases the development sqlite database file.
2. Regenerates the database.
3. Erases stored submissions and replies from the store dir.
"""
# Erase the development db file
if not hasattr(config, 'DATABASE_FILE'):
raise Exception("./manage.py doesn't know how to clear the db "
'if the backend is not sqlite')

# we need to save some data about the old DB file so we can recreate it
# with the same state
Expand Down Expand Up @@ -71,7 +67,7 @@ def reset(args):
# 3. Create the DB from the metadata directly when in 'dev' so
# developers can test application changes without first writing
# alembic migration.
with journalist_app.create_app(config).app_context():
with app_context(config):
db.create_all()
else:
# We have to override the hardcoded .ini file because during testing
Expand Down Expand Up @@ -100,12 +96,12 @@ def reset(args):
return 0


def add_admin(args):
return _add_user(is_admin=True)
def add_admin(args, config):
return _add_user(config, is_admin=True)


def add_journalist(args):
return _add_user()
def add_journalist(args, config):
return _add_user(config)


def _get_username():
Expand Down Expand Up @@ -142,8 +138,8 @@ def _make_password():
continue


def _add_user(is_admin=False):
with app_context():
def _add_user(config, is_admin=False):
with app_context(config):
username = _get_username()

print("Note: Passwords are now autogenerated.")
Expand Down Expand Up @@ -218,9 +214,9 @@ def _get_delete_confirmation(user):
return True


def delete_user(args):
def delete_user(args, config):
"""Deletes a journalist or admin from the application."""
with app_context():
with app_context(config):
username = _get_username_to_delete()
try:
selected_user = Journalist.query.filter_by(username=username).one()
Expand Down Expand Up @@ -252,7 +248,7 @@ def delete_user(args):
return 0


def clean_tmp(args):
def clean_tmp(args, config):
"""Cleanup the SecureDrop temp directory. """
if not os.path.exists(args.directory):
log.debug('{} does not exist, do nothing'.format(args.directory))
Expand All @@ -273,15 +269,15 @@ def listdir_fullpath(d):
return 0


def init_db(args):
def init_db(args, config):
user = pwd.getpwnam(args.user)
subprocess.check_call(['sqlite3', config.DATABASE_FILE, '.databases'])
os.chown(config.DATABASE_FILE, user.pw_uid, user.pw_gid)
os.chmod(config.DATABASE_FILE, 0o0640)
subprocess.check_call(['alembic', 'upgrade', 'head'])


def were_there_submissions_today(args):
def were_there_submissions_today(args, config):
db_uri = "sqlite:///{}".format(config.DATABASE_FILE)
session = sessionmaker(bind=create_engine(db_uri))()
something = session.query(Source).filter(
Expand All @@ -292,7 +288,7 @@ def were_there_submissions_today(args):
open(count_file, 'w').write(something and '1' or '0')


def get_args():
def get_args(config):
parser = argparse.ArgumentParser(prog=__file__, description='Management '
'and testing utility for SecureDrop.')
parser.add_argument('-v', '--verbose', action='store_true')
Expand Down Expand Up @@ -332,8 +328,8 @@ def get_args():
"SecureDrop application's state.")
reset_subp.set_defaults(func=reset)
# Cleanup the SD temp dir
set_clean_tmp_parser(subps, 'clean-tmp')
set_clean_tmp_parser(subps, 'clean_tmp')
set_clean_tmp_parser(subps, 'clean-tmp', config)
set_clean_tmp_parser(subps, 'clean_tmp', config)

set_were_there_submissions_today(subps)

Expand All @@ -354,7 +350,7 @@ def set_were_there_submissions_today(subps):
parser.set_defaults(func=were_there_submissions_today)


def set_clean_tmp_parser(subps, name):
def set_clean_tmp_parser(subps, name, config):
parser = subps.add_parser(name, help='Cleanup the '
'SecureDrop temp directory.')
default_days = 7
Expand All @@ -380,16 +376,17 @@ def setup_verbosity(args):


@contextmanager
def app_context():
def app_context(config):
with journalist_app.create_app(config).app_context():
yield


def _run_from_commandline(): # pragma: no cover
try:
args = get_args().parse_args()
config = SDConfig()
args = get_args(config).parse_args()
setup_verbosity(args)
rc = args.func(args)
rc = args.func(args, config)
sys.exit(rc)
except KeyboardInterrupt:
sys.exit(signal.SIGINT)
Expand Down
2 changes: 1 addition & 1 deletion securedrop/management/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ def cleanup(self):
proc.terminate()


def run(args): # pragma: no cover
def run(args, config): # pragma: no cover
"""
Starts development servers for both the Source Interface and the
Journalist Interface concurrently. Their output is collected,
Expand Down
5 changes: 3 additions & 2 deletions securedrop/qa_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from journalist_app import create_app
from models import (Journalist, Source, Submission, SourceStar, Reply,
JournalistLoginAttempt)
from sdconfig import config as sdconfig
from sdconfig import SDConfig

random.seed('~(=^–^)') # mrow?

Expand Down Expand Up @@ -241,9 +241,10 @@ def arg_parser():


def main():
config = SDConfig()
args = arg_parser().parse_args()
print('Loading data. This make take a while.')
QaLoader(sdconfig, args.multiplier).load()
QaLoader(config, args.multiplier).load()


if __name__ == '__main__':
Expand Down
9 changes: 3 additions & 6 deletions securedrop/sdconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ class _JournalistInterfaceFlaskConfig(_FlaskConfig):

class SDConfig(object):

def __init__(self):
# type: () -> None
def __init__(self, config_file=CONFIG_FILE):
# type: (str) -> None

with open(CONFIG_FILE) as f:
with open(config_file) as f:
json_config = json.loads(f.read())

self.SECUREDROP_DATA_ROOT = '/var/lib/securedrop/'
Expand Down Expand Up @@ -221,6 +221,3 @@ def WORD_LIST(self, value):
@WORD_LIST.deleter
def WORD_LIST(self):
raise AttributeError('Cannot delete WORD_LIST')


config = SDConfig() # type: SDConfig
5 changes: 2 additions & 3 deletions securedrop/source.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
# -*- coding: utf-8 -*-

from sdconfig import config

from sdconfig import SDConfig
from source_app import create_app

config = SDConfig()
app = create_app(config)


Expand Down
11 changes: 6 additions & 5 deletions securedrop/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from pyotp import TOTP

os.environ['SECUREDROP_ENV'] = 'test' # noqa
from sdconfig import SDConfig, config as original_config
from sdconfig import SDConfig

from os import path

Expand Down Expand Up @@ -58,10 +58,11 @@ def pytest_collection_modifyitems(config, items):

@pytest.fixture(scope='session')
def setUpTearDown():
_start_test_rqworker(original_config)
default_config = SDConfig()
_start_test_rqworker(default_config)
yield
_stop_test_rqworker()
_cleanup_test_securedrop_dataroot(original_config)
_cleanup_test_securedrop_dataroot(default_config)


@pytest.fixture(scope='function')
Expand Down Expand Up @@ -178,11 +179,11 @@ def test_submissions(journalist_app):


@pytest.fixture(scope='function')
def test_files(journalist_app, test_journo):
def test_files(journalist_app, test_journo, config):
with journalist_app.app_context():
source, codename = utils.db_helper.init_source()
utils.db_helper.submit(source, 2)
utils.db_helper.reply(test_journo['journalist'], source, 1)
utils.db_helper.reply(test_journo['journalist'], source, 1, config)
return {'source': source,
'codename': codename,
'filesystem_id': source.filesystem_id,
Expand Down
17 changes: 9 additions & 8 deletions securedrop/tests/functional/functional_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@
from selenium.webdriver.support import expected_conditions

os.environ['SECUREDROP_ENV'] = 'test' # noqa
from sdconfig import config
import journalist_app
import source_app
import tests.utils.env as env
from sdconfig import SDConfig

from db import db

Expand Down Expand Up @@ -82,8 +82,9 @@ def _prepare_webdriver(self):
return firefox_binary.FirefoxBinary(log_file=log_file)

def setup(self, session_expiration=30):
env.create_directories()
self.__context = journalist_app.create_app(config).app_context()
self.config = SDConfig()
env.create_directories(self.config)
self.__context = journalist_app.create_app(self.config).app_context()
self.__context.push()
# Patch the two-factor verification to avoid intermittent errors
self.patcher = mock.patch('models.Journalist.verify_token')
Expand All @@ -96,7 +97,7 @@ def setup(self, session_expiration=30):

signal.signal(signal.SIGUSR1, lambda _, s: traceback.print_stack(s))

self.gpg = env.init_gpg()
self.gpg = env.init_gpg(self.config)
db.create_all()

source_port = self._unused_port()
Expand All @@ -108,11 +109,11 @@ def setup(self, session_expiration=30):
# Allow custom session expiration lengths
self.session_expiration = session_expiration

self.source_app = source_app.create_app(config)
self.journalist_app = journalist_app.create_app(config)
self.source_app = source_app.create_app(self.config)
self.journalist_app = journalist_app.create_app(self.config)

def start_source_server(app):
config.SESSION_EXPIRATION_MINUTES = self.session_expiration
self.config.SESSION_EXPIRATION_MINUTES = self.session_expiration

app.run(
port=source_port,
Expand Down Expand Up @@ -181,7 +182,7 @@ def key_available(filesystem_id):

def teardown(self):
self.patcher.stop()
env.teardown()
env.teardown(self.config)
if not hasattr(self, 'override_driver'):
self.driver.quit()
self.source_process.terminate()
Expand Down
3 changes: 1 addition & 2 deletions securedrop/tests/functional/journalist_navigation_steps.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

import tests.utils.db_helper as db_helper
from models import Journalist
from sdconfig import config


class JournalistNavigationStepsMixin():
Expand Down Expand Up @@ -209,7 +208,7 @@ def _admin_visits_system_config_page(self):
def _admin_updates_logo_image(self):
logo_upload_input = self.driver.find_element_by_id('logo-upload')
logo_upload_input.send_keys(
os.path.join(config.SECUREDROP_ROOT, "static/i/logo.png")
os.path.join(self.config.SECUREDROP_ROOT, "static/i/logo.png")
)

submit_button = self.driver.find_element_by_id('submit-logo-update')
Expand Down
Loading

0 comments on commit a808d41

Please sign in to comment.