diff --git a/montage/mw/__init__.py b/montage/mw/__init__.py index dd8185cf..a82652e4 100644 --- a/montage/mw/__init__.py +++ b/montage/mw/__init__.py @@ -143,10 +143,14 @@ def endpoint(self, next, cookie, rdb_session, _route, config, user = rdb_session.query(User).filter(User.id == userid).first() - if user is None and not ep_is_public: - err = 'unknown cookie userid, try logging in again' - response_dict['errors'].append(err) - return {} + if user is None: + if not ep_is_public: + err = 'unknown cookie userid, try logging in again' + response_dict['errors'].append(err) + return {} + # A public endpoint without a known user, e.g. /complete_login in debug mode + # before the first login, when the sentinel userid 0 has no row. + return next(user=None, user_dao=None) superuser = config.get('superuser') superusers = config.get('superusers', [superuser] if superuser else []) diff --git a/montage/tests/test_web_basic.py b/montage/tests/test_web_basic.py index fec5b342..e7f2d5a9 100644 --- a/montage/tests/test_web_basic.py +++ b/montage/tests/test_web_basic.py @@ -2049,3 +2049,44 @@ def test_csv_import_warnings_are_dicts(montage_app, api_client, mock_external_ap assert data['warnings'] and all(isinstance(w, dict) for w in data['warnings']) assert any('Gone.jpg' in w.get('import issues', '') for w in data['warnings']) + + +@pytest.fixture +def debug_app(tmpdir, monkeypatch): + """App as run for local development: debug mode, and no user row for the sentinel id.""" + monkeypatch.setenv('MONTAGE_PDB', '1') # no Sentry from tests + config = utils.load_env_config(env_name='devtest') + config['db_url'] = config['db_url'].replace('///', '///' + str(tmpdir) + '/') + # devtest supplies a fixture user to requests without a cookie, which would hide this + config['__env__'] = 'dev' + config['debug'] = True + config['debug_userid'] = 6024474 + config['debug_username'] = 'Slaporte' + config['superuser'] = 'Slaporte' + _create_schema(db_url=config['db_url']) + + index_path = STATIC_PATH + '/index.html' + if not os.path.exists(index_path): + mkdir_p(STATIC_PATH) + with open(index_path, 'w') as f: + f.write('just for tests') + + return create_app('dev', config=config) + + +def test_debug_complete_login_works_before_first_login(debug_app): + """Visiting /complete_login in debug mode without a session logs in, as dev.md says.""" + client = ClasticTestClient(debug_app) + resp = client.get('/complete_login') + assert resp.status_code == 302 + data = _get_cookie_data(client, debug_app.resources['config']['cookie_secret']) + assert data['userid'] == 6024474 + + +def test_debug_without_login_still_refuses_protected_endpoints(debug_app): + """The sentinel user is only accepted by public endpoints.""" + client = ClasticTestClient(debug_app) + resp = client.get('/v1/admin', headers={'Accept': 'application/json'}) + data = json.loads(resp.get_data(as_text=True)) + assert data['status'] == 'failure' + assert data['errors']