From 98ed185032a5ac3d7b2c9684890653ef16a1aea2 Mon Sep 17 00:00:00 2001 From: John Hollowell Date: Mon, 21 Nov 2022 04:49:00 +0000 Subject: [PATCH 1/2] Move AuthenticationError (and its test) to core.py * Moved AuthenticationError to core.py * Standardized imports in tests closes #105 --- proxmoxer/backends/https.py | 28 +++++++--------------------- proxmoxer/core.py | 9 +++++++++ tests/test_command_base.py | 2 +- tests/test_core.py | 6 +++--- tests/test_https.py | 7 ++++--- tests/test_local.py | 2 +- tests/test_openssh.py | 2 +- tests/test_paramiko.py | 2 +- 8 files changed, 27 insertions(+), 31 deletions(-) diff --git a/proxmoxer/backends/https.py b/proxmoxer/backends/https.py index 9f8bc69..e29dca3 100644 --- a/proxmoxer/backends/https.py +++ b/proxmoxer/backends/https.py @@ -12,15 +12,14 @@ import time from shlex import split as shell_split -from proxmoxer.core import SERVICES, config_failure +from proxmoxer.core import SERVICES, AuthenticationError, config_failure logger = logging.getLogger(__name__) logger.setLevel(level=logging.WARNING) -STREAMING_SIZE_THRESHOLD = 100 * 1024 * 1024 # 10 MiB +STREAMING_SIZE_THRESHOLD = 10 * 1024 * 1024 # 10 MiB SSL_OVERFLOW_THRESHOLD = 2147483135 # 2^31 - 1 - 512 -# fmt: off try: import requests from requests.auth import AuthBase @@ -30,20 +29,6 @@ sys.exit(1) -def is_file(obj): return isinstance(obj, io.IOBase) -def get_time(): return time.monotonic() -# fmt: on - - -class AuthenticationError(Exception): - def __init__(self, msg): - super(AuthenticationError, self).__init__(msg) - self.msg = msg - - def __str__(self): - return self.msg - - class ProxmoxHTTPAuthBase(AuthBase): def __call__(self, req): return req @@ -99,7 +84,7 @@ def _get_new_tokens(self, password=None, otp=None): "Couldn't authenticate user: missing Two Factor Authentication (TFA)" ) - self.birth_time = get_time() + self.birth_time = time.monotonic() self.pve_auth_ticket = response_data["ticket"] self.csrf_prevention_token = response_data["CSRFPreventionToken"] @@ -111,8 +96,9 @@ def get_tokens(self): def __call__(self, req): # refresh ticket if older than `renew_age` - if (get_time() - self.birth_time) >= self.renew_age: - logger.debug(f"refreshing ticket (age {get_time() - self.birth_time})") + time_diff = time.monotonic() - self.birth_time + if time_diff >= self.renew_age: + logger.debug(f"refreshing ticket (age {time_diff})") self._get_new_tokens() # only attach CSRF token if needed (reduce interception risk) @@ -211,7 +197,7 @@ def request( data[k] = v elif "Windows" not in platform.platform(): data[k] = shell_split(v) - if is_file(v): + if isinstance(v, io.IOBase): total_file_size += get_file_size(v) # add in filename from file pointer (patch for https://github.com/requests/toolbelt/pull/316) diff --git a/proxmoxer/core.py b/proxmoxer/core.py index e4eb0a4..75f08dc 100644 --- a/proxmoxer/core.py +++ b/proxmoxer/core.py @@ -77,6 +77,15 @@ def __init__(self, status_code, status_message, content, errors=None): super().__init__(message) +class AuthenticationError(Exception): + def __init__(self, msg): + super().__init__(msg) + self.msg = msg + + def __str__(self): + return self.msg + + class ProxmoxResource(object): def __init__(self, **kwargs): self._store = kwargs diff --git a/tests/test_command_base.py b/tests/test_command_base.py index d81e176..9cc9feb 100644 --- a/tests/test_command_base.py +++ b/tests/test_command_base.py @@ -3,7 +3,7 @@ import pytest -import proxmoxer.backends.command_base as command_base +from proxmoxer.backends import command_base from .api_mock import PVERegistry diff --git a/tests/test_core.py b/tests/test_core.py index b864643..850dc54 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -3,8 +3,8 @@ import pytest -import proxmoxer.core as core -from proxmoxer import backends +from proxmoxer import core +from proxmoxer.backends import https from proxmoxer.backends.command_base import JsonSimpleSerializer, Response from .api_mock import ( # pylint: disable=unused-import # noqa: F401 @@ -267,7 +267,7 @@ def test_init_basic(self): assert isinstance(prox, core.ProxmoxAPI) assert isinstance(prox, core.ProxmoxResource) - assert isinstance(prox._backend, backends.https.Backend) + assert isinstance(prox._backend, https.Backend) assert prox._backend.auth.service == "PVE" def test_init_invalid_service(self): diff --git a/tests/test_https.py b/tests/test_https.py index 73e853f..c4c3e38 100644 --- a/tests/test_https.py +++ b/tests/test_https.py @@ -7,7 +7,8 @@ import pytest from requests import Request, Response -import proxmoxer.backends.https as https +import proxmoxer as core +from proxmoxer.backends import https from .api_mock import ( # pylint: disable=unused-import # noqa: F401 PVERegistry, @@ -214,7 +215,7 @@ def test_get_cookies(self, mock_pve): assert auth.get_cookies().get_dict() == {"PVEAuthCookie": "ticket"} def test_auth_failure(self, mock_pve): - with pytest.raises(https.AuthenticationError) as exc_info: + with pytest.raises(core.AuthenticationError) as exc_info: https.ProxmoxHTTPAuth("bad_auth", "", base_url=self.base_url) assert ( @@ -228,7 +229,7 @@ def test_auth_otp(self, mock_pve): ) def test_auth_otp_missing(self, mock_pve): - with pytest.raises(https.AuthenticationError) as exc_info: + with pytest.raises(core.AuthenticationError) as exc_info: https.ProxmoxHTTPAuth("otp", "password", base_url=self.base_url, service="PVE") assert ( diff --git a/tests/test_local.py b/tests/test_local.py index e69b7a0..9f7d95e 100644 --- a/tests/test_local.py +++ b/tests/test_local.py @@ -1,6 +1,6 @@ import tempfile -import proxmoxer.backends.local as local +from proxmoxer.backends import local # pylint: disable=no-self-use diff --git a/tests/test_openssh.py b/tests/test_openssh.py index 98db5f6..2b9a24f 100644 --- a/tests/test_openssh.py +++ b/tests/test_openssh.py @@ -4,7 +4,7 @@ import openssh_wrapper import pytest -import proxmoxer.backends.openssh as openssh +from proxmoxer.backends import openssh # pylint: disable=no-self-use diff --git a/tests/test_paramiko.py b/tests/test_paramiko.py index 9923616..82e3f35 100644 --- a/tests/test_paramiko.py +++ b/tests/test_paramiko.py @@ -4,7 +4,7 @@ import pytest -import proxmoxer.backends.ssh_paramiko as ssh_paramiko +from proxmoxer.backends import ssh_paramiko # pylint: disable=no-self-use From c30d5166432a33f68eec0e21db4c1186e61cafed Mon Sep 17 00:00:00 2001 From: John Hollowell Date: Mon, 21 Nov 2022 05:02:50 +0000 Subject: [PATCH 2/2] Improve VSCode task definitions --- .vscode/tasks.json | 76 ++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 3 deletions(-) diff --git a/.vscode/tasks.json b/.vscode/tasks.json index a4971c4..daeaf7b 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -7,19 +7,89 @@ "label": "Run Tests (with coverage)", "type": "shell", "command": "pytest -v --cov --cov-report xml:coverage.xml tests/", - "problemMatcher": [] + "problemMatcher": [], + "icon": { + "id": "beaker", + "color": "terminal.ansiGreen" + }, + "runOptions": { + "instanceLimit": 1 + }, + "group": { + "kind": "test", + "isDefault": true + }, + "presentation": { + "echo": true, + "reveal": "silent", + "focus": false, + "panel": "dedicated", + "showReuseMessage": false, + "clear": true + }, }, { "label": "Run Tests", "type": "shell", "command": "pytest -v tests/", - "problemMatcher": [] + "problemMatcher": [], + "icon": { + "id": "beaker", + }, + "group": { + "kind": "test" + }, + "presentation": { + "echo": true, + "reveal": "always", + "focus": false, + "panel": "dedicated", + "showReuseMessage": false, + "clear": true + }, }, { "label": "Update bandit baseline", "type": "shell", "command": "bandit --configfile .bandit -f json -r tests/ proxmoxer/ >| tests/known_issues.json", - "problemMatcher": [] + "problemMatcher": [], + "runOptions": { + "instanceLimit": 1 + }, + "group": { + "kind": "none" + }, + "icon": { + "id": "bookmark" + }, + "presentation": { + "echo": true, + "reveal": "never", + "focus": false, + "panel": "shared", + "showReuseMessage": false, + "clear": false + }, + }, + { + "label": "Clean Cache/tmp files", + "type": "shell", + "command": "rm -rf ./.mypy_cache/ ./.pytest_cache/ ./.coverage.xml ./.coverage", + "problemMatcher": [], + "group": { + "kind": "none" + }, + "icon": { + "id": "trashcan" + }, + "presentation": { + "echo": true, + "reveal": "never", + "focus": false, + "panel": "shared", + "showReuseMessage": false, + "clear": false + }, } ] }