Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move AuthenticationError to main proxmox. namespace #125

Merged
merged 2 commits into from Nov 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
76 changes: 73 additions & 3 deletions .vscode/tasks.json
Expand Up @@ -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
},
}
]
}
28 changes: 7 additions & 21 deletions proxmoxer/backends/https.py
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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"]

Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
9 changes: 9 additions & 0 deletions proxmoxer/core.py
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion tests/test_command_base.py
Expand Up @@ -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

Expand Down
6 changes: 3 additions & 3 deletions tests/test_core.py
Expand Up @@ -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
Expand Down Expand Up @@ -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):
Expand Down
7 changes: 4 additions & 3 deletions tests/test_https.py
Expand Up @@ -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,
Expand Down Expand Up @@ -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 (
Expand All @@ -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 (
Expand Down
2 changes: 1 addition & 1 deletion 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

Expand Down
2 changes: 1 addition & 1 deletion tests/test_openssh.py
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion tests/test_paramiko.py
Expand Up @@ -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

Expand Down