diff --git a/intelmq/__init__.py b/intelmq/__init__.py index 0fb77e825..ac7cbc561 100644 --- a/intelmq/__init__.py +++ b/intelmq/__init__.py @@ -32,6 +32,7 @@ DEFAULT_LOGGING_LEVEL = "INFO" HARMONIZATION_CONF_FILE = os.path.join(CONFIG_DIR, "harmonization.conf") RUNTIME_CONF_FILE = os.path.join(CONFIG_DIR, "runtime.yaml") +INTELMQ_CONF_FILE = os.path.join(CONFIG_DIR, "intelmq.yaml") old_runtime_conf_file = pathlib.Path(RUNTIME_CONF_FILE).with_suffix('.conf') if not pathlib.Path(RUNTIME_CONF_FILE).exists() and old_runtime_conf_file.exists(): old_runtime_conf_file.rename(RUNTIME_CONF_FILE) diff --git a/intelmq/app/api/config.py b/intelmq/app/api/config.py deleted file mode 100644 index 485e1f6b4..000000000 --- a/intelmq/app/api/config.py +++ /dev/null @@ -1,70 +0,0 @@ -"""Configuration for IntelMQ Manager - -SPDX-FileCopyrightText: 2020 Intevation GmbH -SPDX-License-Identifier: AGPL-3.0-or-later - -Funding: of initial version by SUNET -Author(s): - * Bernhard Herzog -""" - -from typing import List, Optional -import json -from pathlib import Path - - -class Config: - - """Configuration settings for IntelMQ Manager""" - - intelmq_ctl_cmd: List[str] = ["sudo", "-u", "intelmq", - "/usr/local/bin/intelmqctl"] - - allowed_path: Path = Path("/opt/intelmq/var/lib/bots/") - - session_store: Optional[Path] = None - - session_duration: int = 24 * 3600 - - allow_origins: List[str] = ['*'] - - def __init__(self, filename: Optional[str]): - """Load configuration from JSON file""" - raw = {} - config = False - - configfiles = [ - Path('/etc/intelmq/api-config.json'), - Path(__file__).parent.parent / 'etc/intelmq/api-config.json' - ] - - if filename: - configfiles.insert(0, Path(filename).resolve()) - - for path in configfiles: - if path.exists() and path.is_file(): - print(f"Loading config from {path}") - config = True - with path.open() as f: - try: - raw = json.load(f) - except json.decoder.JSONDecodeError: - print(f"{path} did not contain valid JSON. Using default values.") - break - if not config: - print("Was not able to load a configfile. Using default values.") - - if "intelmq_ctl_cmd" in raw: - self.intelmq_ctl_cmd = raw["intelmq_ctl_cmd"] - - if "allowed_path" in raw: - self.allowed_path = Path(raw["allowed_path"]) - - if "session_store" in raw: - self.session_store = Path(raw["session_store"]) - - if "session_duration" in raw: - self.session_duration = int(raw["session_duration"]) - - if "allow_origins" in raw: - self.allow_origins = raw['allow_origins'] diff --git a/intelmq/app/api/dependencies.py b/intelmq/app/api/dependencies.py index cbe4dcc8d..e88d0a4a5 100644 --- a/intelmq/app/api/dependencies.py +++ b/intelmq/app/api/dependencies.py @@ -9,8 +9,8 @@ from fastapi import Depends, Header, HTTPException, Response, status -import intelmq_api.config -import intelmq_api.session as session +import intelmq.app.config +import intelmq.app.api.session as session T = TypeVar("T") @@ -31,7 +31,7 @@ def __call__(self) -> Optional[T]: return self._value -api_config = OneTimeDependency[intelmq_api.config.Config]() +api_config = OneTimeDependency[intelmq.app.config.Config]() session_store = OneTimeDependency[session.SessionStore]() @@ -52,7 +52,7 @@ def token_authorization(authorization: typing.Union[str, None] = Header(default= }) -def startup(config: intelmq_api.config.Config): +def startup(config: intelmq.app.config.Config): """A starting point to one-time initialization of necessary dependencies. This needs to be called by the application on the startup.""" api_config.initialize(config) diff --git a/intelmq/app/api/exceptions.py b/intelmq/app/api/exceptions.py index f087df3a3..b117687df 100644 --- a/intelmq/app/api/exceptions.py +++ b/intelmq/app/api/exceptions.py @@ -8,7 +8,7 @@ from fastapi.responses import JSONResponse from starlette.exceptions import HTTPException as StarletteHTTPException -import intelmq_api.runctl as runctl +import intelmq.app.api.runctl as runctl def ctl_error_handler(request: Request, exc: runctl.IntelMQCtlError): diff --git a/intelmq/app/api/files.py b/intelmq/app/api/files.py index afe419690..a8d49e18a 100644 --- a/intelmq/app/api/files.py +++ b/intelmq/app/api/files.py @@ -15,7 +15,7 @@ from pathlib import PurePath, Path from typing import Optional, Tuple, Union, Dict, Any, Iterable, BinaryIO -from intelmq_api.config import Config +from intelmq.app.config import Config def path_starts_with(path: PurePath, prefix: PurePath) -> bool: @@ -40,7 +40,7 @@ def __init__(self, config: Config): self.allowed_path = config.allowed_path def file_name_allowed(self, filename: str) -> Optional[Tuple[bool, Path]]: - """Determine wether the API should allow access to a file.""" + """Determine whether the API should allow access to a file.""" resolved = Path(filename).resolve() if not path_starts_with(resolved, self.allowed_path): return None diff --git a/intelmq/app/api/main.py b/intelmq/app/api/main.py deleted file mode 100644 index c908f3af1..000000000 --- a/intelmq/app/api/main.py +++ /dev/null @@ -1,31 +0,0 @@ -"""Main entrypoint for the API application - -SPDX-FileCopyrightText: 2022 CERT.at GmbH -SPDX-License-Identifier: AGPL-3.0-or-later -""" - -import os - -from fastapi import FastAPI -from fastapi.middleware.cors import CORSMiddleware - -import intelmq_api.config -import intelmq_api.dependencies -import intelmq_api.exceptions - -from .api import api - -config = intelmq_api.config.Config(os.environ.get("INTELMQ_API_CONFIG")) - -app = FastAPI(root_path=os.environ.get("ROOT_PATH", "")) - - -@app.on_event("startup") -def init_app(): - intelmq_api.dependencies.startup(config) - - -app.add_middleware(CORSMiddleware, allow_origins=config.allow_origins, - allow_methods=("GET", "POST")) -app.include_router(api, prefix="/v1") -intelmq_api.exceptions.register(app) diff --git a/intelmq/app/api/api.py b/intelmq/app/api/router.py similarity index 75% rename from intelmq/app/api/api.py rename to intelmq/app/api/router.py index e2fbd1fd3..00d1e70ee 100644 --- a/intelmq/app/api/api.py +++ b/intelmq/app/api/router.py @@ -21,16 +21,16 @@ from intelmq.lib import utils # type: ignore from typing_extensions import Literal # Python 3.8+ -import intelmq_api.config -import intelmq_api.files as files -import intelmq_api.runctl as runctl -import intelmq_api.session as session +import intelmq.app.config +import intelmq.app.api.files as files +import intelmq.app.api.runctl as runctl +import intelmq.app.api.session as session from .dependencies import (api_config, cached_response, session_store, token_authorization) from .models import TokenResponse -api = APIRouter() +router = APIRouter() Levels = Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL", "ALL"] @@ -48,11 +48,11 @@ def ID(id: str) -> str: return id -def runner(config: intelmq_api.config.Config = Depends(api_config)): +def runner(config: intelmq.app.config.Config = Depends(api_config)): return runctl.RunIntelMQCtl(config.intelmq_ctl_cmd) -def file_access(config: intelmq_api.config.Config = Depends(api_config)): +def file_access(config: intelmq.app.config.Config = Depends(api_config)): return files.FileAccess(config) @@ -67,65 +67,71 @@ def render(self, content: runctl.JSONFile) -> bytes: return content -@api.get("/api/botnet", dependencies=[authorized]) +@router.get("/") +def api_base_url(): + """Do not rename or delete!""" + return JSONResponse({}) + + +@router.get("/botnet", dependencies=[authorized]) def botnet(action: Actions, group: typing.Optional[Groups] = None, runner: runctl.RunIntelMQCtl = Depends(runner)): return JSONFileResponse(runner.botnet(action, group)) -@api.get("/api/bot", dependencies=[authorized]) +@router.get("/bot", dependencies=[authorized]) def bot(action: Actions, id: str = Depends(ID), runner: runctl.RunIntelMQCtl = Depends(runner)): return JSONFileResponse(runner.bot(action, id)) -@api.get("/api/getlog", dependencies=[authorized, cached]) +@router.get("/getlog", dependencies=[authorized, cached]) def get_log(lines: int, id: str = Depends(ID), level: Levels = "DEBUG", runner: runctl.RunIntelMQCtl = Depends(runner)): return JSONFileResponse(runner.log(id, lines, level)) -@api.get("/api/queues", dependencies=[authorized, cached]) +@router.get("/queues", dependencies=[authorized, cached]) def queues(runner: runctl.RunIntelMQCtl = Depends(runner)): return JSONFileResponse(runner.list("queues")) -@api.get("/api/queues-and-status", dependencies=[authorized, cached]) +@router.get("/queues-and-status", dependencies=[authorized, cached]) def queues_and_status(runner: runctl.RunIntelMQCtl = Depends(runner)): return JSONFileResponse(runner.list("queues-and-status")) -@api.get("/api/bots", dependencies=[authorized, cached]) +@router.get("/bots", dependencies=[authorized, cached]) def bots(runner: runctl.RunIntelMQCtl = Depends(runner)): return JSONFileResponse(runner.list("bots")) -@api.get("/api/version", dependencies=[authorized], response_model=dict) +@router.get("/version", dependencies=[authorized], response_model=dict) def version(runner: runctl.RunIntelMQCtl = Depends(runner)): return runner.version() -@api.get("/api/check", dependencies=[authorized]) +@router.get("/check", dependencies=[authorized]) def check(runner: runctl.RunIntelMQCtl = Depends(runner)): return JSONFileResponse(runner.check()) -@api.get("/api/clear", dependencies=[authorized]) +@router.get("/clear", dependencies=[authorized]) def clear(id: str = Depends(ID), runner: runctl.RunIntelMQCtl = Depends(runner)): return JSONFileResponse(runner.clear(id)) -@api.post("/api/run", dependencies=[authorized], response_model=str) +@router.post("/run", dependencies=[authorized], response_model=str) def run(bot: str, cmd: BotCmds, show: bool = False, dry: bool = False, msg: str = Form(default=""), runner: runctl.RunIntelMQCtl = Depends(runner)): return runner.run(bot, cmd, show, dry, msg) -@api.get("/api/debug", dependencies=[authorized]) +@router.get("/debug", dependencies=[authorized]) def debug(runner: runctl.RunIntelMQCtl = Depends(runner)): return JSONFileResponse(runner.debug()) -@api.get("/api/config", dependencies=[authorized]) +@router.get("/config", dependencies=[authorized]) def config(file: str, fetch: bool = False, file_access: files.FileAccess = Depends(file_access)): result = file_access.load_file_or_directory(file, fetch) @@ -136,7 +142,7 @@ def config(file: str, fetch: bool = False, return Response(contents, headers={"content-type": content_type}) -@api.post("/api/login", status_code=status.HTTP_200_OK, response_model=TokenResponse) +@router.post("/login", status_code=status.HTTP_200_OK, response_model=TokenResponse) def login(username: str = Form(...), password: str = Form(...), session: session.SessionStore = Depends(session_store)): if session is None: @@ -156,7 +162,7 @@ def login(username: str = Form(...), password: str = Form(...), detail="Invalid username and/or password.") -@api.get("/api/harmonization", dependencies=[authorized], response_model=dict) +@router.get("/harmonization", dependencies=[authorized], response_model=dict) def get_harmonization(runner: runctl.RunIntelMQCtl = Depends(runner)): harmonization = pathlib.Path('/opt/intelmq/etc/harmonization.conf') paths = runner.get_paths() @@ -169,16 +175,13 @@ def get_harmonization(runner: runctl.RunIntelMQCtl = Depends(runner)): return {} -@api.get("/api/runtime", dependencies=[authorized], response_model=dict) +@router.get("/runtime", dependencies=[authorized], response_model=dict) def get_runtime(): return utils.get_runtime() -@api.post("/api//runtime", dependencies=[authorized], response_model=str, deprecated=True, - description="Invalid path for compatibility with older IntelMQ Manager versions", - response_class=PlainTextResponse) -@api.post("/api/runtime", dependencies=[authorized], response_model=str, - response_class=PlainTextResponse) +@router.post("/runtime", dependencies=[authorized], response_model=str, + response_class=PlainTextResponse) def post_runtime(body: dict): try: utils.set_runtime(body) @@ -188,7 +191,7 @@ def post_runtime(body: dict): return str(e) -@api.get("/api/positions", dependencies=[authorized], response_model=dict) +@router.get("/positions", dependencies=[authorized], response_model=dict) def get_positions(runner: runctl.RunIntelMQCtl = Depends(runner)): positions = pathlib.Path('/opt/intelmq/etc/manager/positions.conf') paths = runner.get_paths() @@ -201,11 +204,8 @@ def get_positions(runner: runctl.RunIntelMQCtl = Depends(runner)): return {} -@api.post("/api//positions", dependencies=[authorized], response_model=str, deprecated=True, - description="Invalid path for compatibility with older IntelMQ Manager versions", - response_class=PlainTextResponse) -@api.post("/api/positions", dependencies=[authorized], response_model=str, - response_class=PlainTextResponse) +@router.post("/positions", dependencies=[authorized], response_model=str, + response_class=PlainTextResponse) def post_positions(body: dict, runner: runctl.RunIntelMQCtl = Depends(runner)): positions = pathlib.Path('/opt/intelmq/etc/manager/positions.conf') paths = runner.get_paths() diff --git a/intelmq/app/api/runctl.py b/intelmq/app/api/runctl.py index 094b847a9..3c8d1cefa 100644 --- a/intelmq/app/api/runctl.py +++ b/intelmq/app/api/runctl.py @@ -16,8 +16,8 @@ import subprocess from typing import List, Dict, Optional -from intelmq_api.util import shell_command_for_errors -from .version import __version__ +from intelmq.app.api.util import shell_command_for_errors +from intelmq.version import __version__ # # Typing aliases for use with RunIntelMQCtl @@ -120,10 +120,7 @@ def list(self, kind: str) -> JSONFile: return self._run_json(["list", kind]) def version(self) -> Dict[str, str]: - intelmq_version = self._run_str(["--version"]).strip() - return {"intelmq": intelmq_version, - "intelmq-api": __version__, - } + return {"intelmq": __version__} def check(self) -> JSONFile: return self._run_json(["check"]) diff --git a/intelmq/app/api/version.py b/intelmq/app/api/version.py deleted file mode 100644 index 90c0876e1..000000000 --- a/intelmq/app/api/version.py +++ /dev/null @@ -1,7 +0,0 @@ -""" Version file for intelmq-api - -SPDX-FileCopyrightText: 2020-2023 Birger Schacht, Sebastian Wagner -SPDX-License-Identifier: AGPL-3.0-or-later -""" -__version_info__ = (3, 2, 0) -__version__ = '.'.join(map(str, __version_info__)) diff --git a/intelmq/app/config.py b/intelmq/app/config.py new file mode 100644 index 000000000..6e0bade92 --- /dev/null +++ b/intelmq/app/config.py @@ -0,0 +1,69 @@ +"""Configuration for IntelMQ server application + +SPDX-FileCopyrightText: 2020 Intevation GmbH , 2023 Filip Pokorný +SPDX-License-Identifier: AGPL-3.0-or-later + +Funding: of initial version by SUNET +Author(s): + * Bernhard Herzog +""" + +from typing import List, Optional +from pathlib import Path +from intelmq.lib import utils + + +class Config: + + """Configuration settings for IntelMQ Manager""" + + intelmq_ctl_cmd: List[str] = ["sudo", "-u", "intelmq", "/usr/local/bin/intelmqctl"] + + allowed_path: Path = Path("/opt/intelmq/var/lib/bots/") + + session_store: Optional[Path] = None + + session_duration: int = 24 * 3600 + + allow_origins: List[str] = ['*'] + + enable_webgui: bool = True + + host: str = "0.0.0.0" + + port: int = 8080 + + debug: bool = False + + def __init__(self): + server_settings = utils.get_server_settings() + + if "intelmq_ctl_cmd" in server_settings: + self.intelmq_ctl_cmd = server_settings["intelmq_ctl_cmd"] + + if "allowed_path" in server_settings: + self.allowed_path = Path(server_settings["allowed_path"]) + + if "session_store" in server_settings: + self.session_store = Path(server_settings["session_store"]) + + if "session_duration" in server_settings: + self.session_duration = int(server_settings["session_duration"]) + + if "allow_origins" in server_settings: + self.allow_origins = server_settings['allow_origins'] + + if "enable_webgui" in server_settings: + self.enable_webgui = server_settings["enable_webgui"] + + if "host" in server_settings: + self.host = server_settings["host"] + + if "port" in server_settings: + self.port = server_settings["port"] + + if "debug" in server_settings: + self.debug = server_settings["debug"] + + +config = Config() \ No newline at end of file diff --git a/intelmq/app/server.py b/intelmq/app/server.py new file mode 100644 index 000000000..1c46f2505 --- /dev/null +++ b/intelmq/app/server.py @@ -0,0 +1,35 @@ +"""Main entrypoint for the IntelMQ server application + +SPDX-FileCopyrightText: 2022 CERT.at GmbH , 2023 Filip Pokorný +SPDX-License-Identifier: AGPL-3.0-or-later +""" + +import os + +from fastapi import FastAPI +from fastapi.middleware.cors import CORSMiddleware + +from intelmq.version import __version__ +import intelmq.app.api.dependencies +import intelmq.app.api.exceptions + +from intelmq.app.config import config +from intelmq.app.api.router import router as api_router + +app = FastAPI( + title="IntelMQ", + version=__version__, + root_path=os.environ.get("ROOT_PATH", "") +) + + +@app.on_event("startup") +def init_app(): + intelmq.app.api.dependencies.startup(config) + + +app.add_middleware(CORSMiddleware, allow_origins=config.allow_origins, allow_methods=("GET", "POST")) + +app.include_router(api_router, prefix="/api/v1") + +intelmq.app.api.exceptions.register(app) diff --git a/intelmq/etc/intelmq.yaml b/intelmq/etc/intelmq.yaml new file mode 100644 index 000000000..299b97114 --- /dev/null +++ b/intelmq/etc/intelmq.yaml @@ -0,0 +1,10 @@ +server: + host: "0.0.0.0" + port: 8080 + enable_webgui: true + session_duration: 86400 + intelmq_ctl_cmd: + - "sudo" + - "-u" + - "intelmq" + - "/usr/local/bin/intelmqctl" \ No newline at end of file diff --git a/intelmq/lib/utils.py b/intelmq/lib/utils.py index 5701db1af..24f28c04c 100644 --- a/intelmq/lib/utils.py +++ b/intelmq/lib/utils.py @@ -48,7 +48,7 @@ from termstyle import red import intelmq -from intelmq import RUNTIME_CONF_FILE +from intelmq import RUNTIME_CONF_FILE, INTELMQ_CONF_FILE from intelmq.lib.exceptions import DecodingError try: @@ -920,11 +920,20 @@ def get_runtime() -> dict: return load_configuration(RUNTIME_CONF_FILE) +def get_intelmq_settings() -> dict: + return load_configuration(INTELMQ_CONF_FILE) + + def get_global_settings() -> dict: runtime_conf = get_runtime() return runtime_conf.get('global', {}) +def get_server_settings() -> dict: + global_settings = get_intelmq_settings() + return global_settings.get('server', {}) + + def set_runtime(runtime: dict) -> dict: write_configuration(configuration_filepath=RUNTIME_CONF_FILE, content=runtime) return get_runtime() diff --git a/intelmq/tests/app/api/test_api.py b/intelmq/tests/app/api/test_api.py index d91446077..60128242b 100644 --- a/intelmq/tests/app/api/test_api.py +++ b/intelmq/tests/app/api/test_api.py @@ -11,24 +11,27 @@ from tempfile import TemporaryDirectory from typing import Dict, List, Optional from unittest import TestCase, mock - +from unittest.mock import patch, MagicMock from fastapi.testclient import TestClient -from intelmq.lib import utils # type: ignore -from intelmq_api import dependencies -from intelmq_api.api import runner -from intelmq_api.config import Config -from intelmq_api.dependencies import session_store -from intelmq_api.main import app -from intelmq_api.runctl import RunIntelMQCtl -from intelmq_api.session import SessionStore -from intelmq_api.version import __version__ + +with patch("intelmq.lib.utils.get_intelmq_settings", MagicMock(return_value={})): + + from intelmq.app.api import dependencies + from intelmq.app.api.router import runner + from intelmq.app.api.dependencies import session_store + from intelmq.app.api.runctl import RunIntelMQCtl + from intelmq.app.api.session import SessionStore + from intelmq.app.config import Config + from intelmq.version import __version__ + from intelmq.app.server import app + from intelmq.lib import utils # type: ignore class DummyConfig(Config): + def __init__(self): - # Prevent loading from file - pass + pass # Prevent loading from file class DummyRunner(RunIntelMQCtl): @@ -55,6 +58,7 @@ def dummy_runner(): class TestApiWithCLI(TestCase): + def setUp(self) -> None: self.client = TestClient(app=app) dependencies.startup(DummyConfig()) @@ -64,10 +68,10 @@ def tearDown(self) -> None: app.dependency_overrides = {} def test_version(self): - response = self.client.get("/v1/api/version") + response = self.client.get("/api/v1/version") self.assertEqual(response.status_code, 200) self.assertIsInstance(response.json(), dict) - self.assertEqual(response.json()["intelmq-api"], __version__) + self.assertEqual(response.json()["intelmq"], __version__) def test_ensure_response_get_values_and_is_json(self): json_paths = ["botnet?action=status", "bot?action=status&id=1", @@ -76,19 +80,20 @@ def test_ensure_response_get_values_and_is_json(self): for path in json_paths: with self.subTest(path): - response = self.client.get(f"/v1/api/{path}") + response = self.client.get(f"/api/v1/{path}") self.assertEqual(response.status_code, 200) self.assertIsInstance(response.json(), dict) self.assertEqual(response.json(), {"some": "json"}) def test_run_input(self): response = self.client.post( - "/v1/api/run?bot=feodo-tracker-browse-parser&cmd=get&dry=false&show=false", + "/api/v1/run?bot=feodo-tracker-browse-parser&cmd=get&dry=false&show=false", data={"msg": "some message"}) self.assertEqual(response.status_code, 200) class TestApiWithDir(TestCase): + def setUp(self) -> None: self.client = TestClient(app=app) dependencies.startup(DummyConfig()) @@ -117,18 +122,6 @@ def tearDown(self) -> None: self.path_patcher.stop() self.conf_dir.cleanup() - def test_handle_path_with_doubled_slashes(self): - """The IntelMQ Manager doubles slashes in some paths, but FastAPI doesn't handle it. - - In addition, IntelMQ Manager doesn't respect redirection. As so, keeping the invalid - paths for backward compatibility.""" - PATHS = ["/v1/api//runtime", "/v1/api//positions"] - for path in PATHS: - with self.subTest(path): - response = self.client.post(path, json={}) - self.assertEqual(response.status_code, 200) - self.assertEqual(response.text, "success") - def test_post_runtime(self): data = { "some-bot": { @@ -146,7 +139,7 @@ def test_post_runtime(self): "run_mode": "continuous" } } - response = self.client.post("/v1/api/runtime", json=data) + response = self.client.post("/api/v1/runtime", json=data) self.assertEqual(response.status_code, 200) self.assertEqual(response.text, "success") @@ -160,7 +153,7 @@ def test_post_positions(self): "y": 314 } } - response = self.client.post("/v1/api/positions", json=data) + response = self.client.post("/api/v1/positions", json=data) self.assertEqual(response.status_code, 200) self.assertEqual(response.text, "success") @@ -187,24 +180,24 @@ def tearDown(self) -> None: app.dependency_overrides = {} def test_login(self): - response = self.client.post("/v1/api/login", data={"username": "test", "password": "pass"}) + response = self.client.post("/api/v1/login", data={"username": "test", "password": "pass"}) self.assertEqual(response.status_code, 200) self.assertIsNotNone(response.json().get("login_token")) def test_login_and_call(self): - response = self.client.post("/v1/api/login", data={"username": "test", "password": "pass"}) + response = self.client.post("/api/v1/login", data={"username": "test", "password": "pass"}) self.assertEqual(response.status_code, 200) token = response.json().get("login_token") - authorized_response = self.client.get("/v1/api/version", headers={"authorization": token}) + authorized_response = self.client.get("/api/v1/version", headers={"authorization": token}) self.assertEqual(authorized_response.status_code, 200) - self.assertEqual(authorized_response.json()["intelmq-api"], __version__) + self.assertEqual(authorized_response.json()["intelmq"], __version__) def test_unauthorized_call(self): - response = self.client.get("/v1/api/version") + response = self.client.get("/api/v1/version") self.assertEqual(response.status_code, 401) def test_bad_token(self): response = self.client.get( - "/v1/api/version", headers={"authorization": "not-a-valid-token"}) + "/api/v1/version", headers={"authorization": "not-a-valid-token"}) self.assertEqual(response.status_code, 401) diff --git a/intelmq/tests/app/api/test_sessionstore.py b/intelmq/tests/app/api/test_sessionstore.py index 89f629539..70de3bd2e 100644 --- a/intelmq/tests/app/api/test_sessionstore.py +++ b/intelmq/tests/app/api/test_sessionstore.py @@ -8,7 +8,7 @@ import os from pathlib import Path -from intelmq_api.session import SessionStore +from intelmq.app.api.session import SessionStore class TestSessionStore(unittest.TestCase):