Skip to content

Commit

Permalink
Upgrade Ubuntu
Browse files Browse the repository at this point in the history
  • Loading branch information
sbrunner committed Oct 9, 2024
1 parent b848f31 commit 176a667
Show file tree
Hide file tree
Showing 54 changed files with 445 additions and 725 deletions.
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ repos:
- --output-format=pylint
additional_dependencies:
- prospector-profile-duplicated==1.6.0 # pypi
- prospector-profile-utils==1.9.1 # pypi
- repo: https://github.com/sbrunner/jsonschema-validator
rev: 0.1.0
hooks:
Expand Down
51 changes: 10 additions & 41 deletions .prospector.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
inherits:
- utils:base
- utils:no-design-checks
- utils:fix
- duplicated
strictness: veryhigh
max-line-length: 110

doc-warnings: true

ignore-paths:
Expand All @@ -15,56 +17,23 @@ pylint:
- ujson
- lxml
disable:
- too-many-return-statements
- too-many-arguments
- too-many-branches
- too-many-instance-attributes
- too-few-public-methods
- global-statement
- line-too-long
- import-outside-toplevel
- invalid-name
- no-else-return
- no-else-raise
- no-self-use
- import-error
- unused-argument
- use-symbolic-message-instead
- missing-module-docstring
- missing-function-docstring
- missing-timeout # A default timeout is set

pycodestyle:
options:
max-line-length: 110
disable:
- E722 # do not use bare 'except', duplicated with pylint
- E261 # at least two spaces before inline comment, duplicated with black

pydocstyle:
disable:
- D102 # Missing docstring in public method
- D104 # Missing docstring in public package
- D105 # Missing docstring in magic method
- D107 # Missing docstring in __init__
- D202 # No blank lines allowed after function docstring (found 1)
- D203 # 1 blank line required before class docstring (found 0)
- D212 # Multi-line docstring summary should start at the first line
- D407 # Missing dashed underline after section ('Arguments')
- D412 # No blank lines allowed between a section header and its content ('Arguments')
mypy:
run: true

pycodestyle:
disable:
# Buggy checks with Python 3.12
- E221 # multiple spaces before operator
- E702 # multiple statements on one line (semicolon)

bandit:
run: true
options:
config: .bandit.yaml

pyroma:
run: true

mccabe:
run: false

dodgy:
run: false
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ ENV C2C_BASE_PATH=/c2c \
SENTRY_CLIENT_RELEASE=latest \
SENTRY_TAG_SERVICE=app

CMD ["/usr/local/bin/gunicorn"]
CMD ["/venv/bin/gunicorn"]

COPY production.ini /app/

Expand Down
207 changes: 50 additions & 157 deletions acceptance_tests/app/poetry.lock

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion acceptance_tests/app/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ coverage = "7.6.1"

[tool.poetry.dev-dependencies]
# pylint = { version = "2.15.6" }
prospector = { extras = ["with_bandit", "with_mypy"], version = "1.11.0" }
prospector = { extras = ["with_bandit", "with_mypy"], version = "1.12.0" }
prospector-profile-duplicated = "1.6.0"
prospector-profile-utils = "1.9.1"

[build-system]
requires = ["poetry-core>=1.0.0"]
Expand Down
2 changes: 1 addition & 1 deletion acceptance_tests/tests/tests/test_db_maintenance.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import pytest

LOG = logging.getLogger(__name__)
_LOG = logging.getLogger(__name__)


def _query(app_connection, params, expected=None):
Expand Down
4 changes: 2 additions & 2 deletions c2cwsgiutils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from configparser import SectionProxy
from typing import Any

LOG = logging.getLogger(__name__)
_LOG = logging.getLogger(__name__)


def get_config_defaults() -> dict[str, str]:
Expand All @@ -22,7 +22,7 @@ def get_config_defaults() -> dict[str, str]:
lowercase_keys: set[str] = set()
for key, value in os.environ.items():
if key.lower() in lowercase_keys:
LOG.warning("The environment variable '%s' is duplicated with different case, ignoring", key)
_LOG.warning("The environment variable '%s' is duplicated with different case, ignoring", key)
continue
lowercase_keys.add(key.lower())
result[key] = value.replace("%", "%%")
Expand Down
5 changes: 2 additions & 3 deletions c2cwsgiutils/acceptance/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import typing
from functools import wraps

LOG = logging.getLogger(__name__)
_LOG = logging.getLogger(__name__)


def retry(
Expand All @@ -16,7 +16,6 @@ def retry(
original from: http://wiki.python.org/moin/PythonDecoratorLibrary#Retry
Arguments:
exception_to_check: the exception to check. may be a tuple of exceptions to check
tries: number of times to try (not retry) before giving up
delay: initial delay between retries in seconds
Expand All @@ -32,7 +31,7 @@ def f_retry(*args: typing.Any, **kwargs: typing.Any) -> typing.Any:
return f(*args, **kwargs)
except exception_to_check as e:
msg = f"{e:s}, Retrying in {mdelay:d} seconds..."
LOG.warning(msg)
_LOG.warning(msg)
time.sleep(mdelay)
mtries -= 1
mdelay *= backoff
Expand Down
3 changes: 1 addition & 2 deletions c2cwsgiutils/acceptance/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,7 @@ def get_xml(
**kwargs: Any,
) -> Any:
"""Get the given URL (relative to the root of API)."""

from lxml import etree # nosec
from lxml import etree # nosec # pylint: disable=import-outside-toplevel

with self.session.get(
self.base_url + url,
Expand Down
13 changes: 6 additions & 7 deletions c2cwsgiutils/acceptance/image.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
import subprocess # nosec
from typing import TYPE_CHECKING, Any, Optional

import numpy as np
import skimage.color
import skimage.io
import skimage.metrics
import skimage.transform
import numpy as np # pylint: disable=import-error
import skimage.color # pylint: disable=import-error
import skimage.io # pylint: disable=import-error
import skimage.metrics # pylint: disable=import-error
import skimage.transform # pylint: disable=import-error

if TYPE_CHECKING:
from typing import TypeAlias
Expand Down Expand Up @@ -189,7 +189,7 @@ def check_screenshot(
See also `check_image` for the other parameters.
Args:
Arguments:
url: The URL to screenshot
width: The width of the generated screenshot
height: The height of the generated screenshot
Expand All @@ -202,7 +202,6 @@ def check_screenshot(
generate_expected_image: See `check_image`
use_mask: See `check_image`
"""

if headers is None:
headers = {}
if media is None:
Expand Down
10 changes: 7 additions & 3 deletions c2cwsgiutils/acceptance/print.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from c2cwsgiutils.acceptance import connection, utils

LOG = logging.getLogger(__name__)
_LOG = logging.getLogger(__name__)


class PrintConnection(connection.Connection):
Expand All @@ -30,22 +30,25 @@ def wait_ready(self, timeout: int = 60, app: str = "default") -> None:
utils.retry_timeout(functools.partial(self.get_capabilities, app=app), timeout=timeout)

def get_capabilities(self, app: str) -> Any:
"""Get the capabilities for the given application."""
return self.get_json(app + "/capabilities.json", cache_expected=connection.CacheExpected.YES)

def get_example_requests(self, app: str) -> dict[str, Any]:
"""Get the example requests for the given application."""
samples = self.get_json(app + "/exampleRequest.json", cache_expected=connection.CacheExpected.YES)
out = {}
for name, value in samples.items():
out[name] = json.loads(value)
return out

def get_pdf(self, app: str, request: dict[str, Any], timeout: int = 60) -> requests.Response:
"""Create a report and wait for it to be ready."""
create_report = self.post_json(app + "/report.pdf", json=request)
LOG.debug("create_report=%s", create_report)
_LOG.debug("create_report=%s", create_report)
ref = create_report["ref"]

status = utils.retry_timeout(functools.partial(self._check_completion, ref), timeout=timeout)
LOG.debug("status=%s", repr(status))
_LOG.debug("status=%s", repr(status))
assert status["status"] == "finished"

report = self.get_raw("report/" + ref)
Expand All @@ -59,4 +62,5 @@ def _check_completion(self, ref: str) -> Optional[Any]:
return None

def get_apps(self) -> Any:
"""Get the list of available applications."""
return self.get_json("apps.json", cache_expected=connection.CacheExpected.YES)
4 changes: 1 addition & 3 deletions c2cwsgiutils/acceptance/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,10 @@ def retry_timeout(what: Callable[[], Any], timeout: float = _DEFAULT_TIMEOUT, in
Retry the function until the timeout.
Arguments:
what: the function to try
timeout: the timeout to get a success
interval: the interval between try
"""

timeout = time.perf_counter() + timeout
while True:
error = ""
Expand All @@ -58,7 +56,7 @@ def approx(struct: Any, **kwargs: Any) -> Any:
See pytest.approx
"""
import boltons.iterutils
import boltons.iterutils # pylint: disable=import-outside-toplevel

if isinstance(struct, float):
return pytest.approx(struct, **kwargs)
Expand Down
48 changes: 24 additions & 24 deletions c2cwsgiutils/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,21 @@

from c2cwsgiutils.config_utils import config_bool, env_or_config, env_or_settings

COOKIE_AGE = 7 * 24 * 3600
_COOKIE_AGE = 7 * 24 * 3600
SECRET_PROP = "c2c.secret" # nosec # noqa
SECRET_ENV = "C2C_SECRET" # nosec # noqa
GITHUB_REPOSITORY_PROP = "c2c.auth.github.repository"
GITHUB_REPOSITORY_ENV = "C2C_AUTH_GITHUB_REPOSITORY"
GITHUB_ACCESS_TYPE_PROP = "c2c.auth.github.access_type"
GITHUB_ACCESS_TYPE_ENV = "C2C_AUTH_GITHUB_ACCESS_TYPE"
_GITHUB_REPOSITORY_PROP = "c2c.auth.github.repository"
_GITHUB_REPOSITORY_ENV = "C2C_AUTH_GITHUB_REPOSITORY"
_GITHUB_ACCESS_TYPE_PROP = "c2c.auth.github.access_type"
_GITHUB_ACCESS_TYPE_ENV = "C2C_AUTH_GITHUB_ACCESS_TYPE"
GITHUB_AUTH_URL_PROP = "c2c.auth.github.auth_url"
GITHUB_AUTH_URL_ENV = "C2C_AUTH_GITHUB_AUTH_URL"
GITHUB_TOKEN_URL_PROP = "c2c.auth.github.token_url" # nosec
GITHUB_TOKEN_URL_ENV = "C2C_AUTH_GITHUB_TOKEN_URL" # nosec
GITHUB_USER_URL_PROP = "c2c.auth.github.user_url"
GITHUB_USER_URL_ENV = "C2C_AUTH_GITHUB_USER_URL"
GITHUB_REPO_URL_PROP = "c2c.auth.github.repo_url"
GITHUB_REPO_URL_ENV = "C2C_AUTH_GITHUB_REPO_URL"
_GITHUB_REPO_URL_PROP = "c2c.auth.github.repo_url"
_GITHUB_REPO_URL_ENV = "C2C_AUTH_GITHUB_REPO_URL"
GITHUB_CLIENT_ID_PROP = "c2c.auth.github.client_id"
GITHUB_CLIENT_ID_ENV = "C2C_AUTH_GITHUB_CLIENT_ID"
GITHUB_CLIENT_SECRET_PROP = "c2c.auth.github.client_secret" # nosec # noqa
Expand All @@ -44,7 +44,7 @@
USE_SESSION_ENV = "C2C_USE_SESSION"


LOG = logging.getLogger(__name__)
_LOG = logging.getLogger(__name__)


class AuthConfig(TypedDict, total=False):
Expand Down Expand Up @@ -92,11 +92,11 @@ def _is_auth_secret(request: pyramid.request.Request) -> bool:
return False
if secret_hash != _hash_secret(expected):
return False
# login or refresh the cookie
# Login or refresh the cookie
request.response.set_cookie(
SECRET_ENV, secret_hash, max_age=COOKIE_AGE, httponly=True, secure=True, samesite="Strict"
SECRET_ENV, secret_hash, max_age=_COOKIE_AGE, httponly=True, secure=True, samesite="Strict"

Check warning

Code scanning / CodeQL

Construction of a cookie using user-supplied input Medium

Cookie is constructed from a
user-supplied input
.
Cookie is constructed from a
user-supplied input
.
)
# since this could be used from outside c2cwsgiutils views, we cannot set the path to c2c
# Since this could be used from outside c2cwsgiutils views, we cannot set the path to c2c
return True
return False

Expand Down Expand Up @@ -127,7 +127,7 @@ def _is_auth_user_github(request: pyramid.request.Request) -> tuple[bool, UserDe
),
)
except jwt.exceptions.InvalidTokenError as e:
LOG.warning("Error on decoding JWT token: %s", e)
_LOG.warning("Error on decoding JWT token: %s", e)
return False, {}


Expand Down Expand Up @@ -181,11 +181,11 @@ def auth_type(settings: Optional[Mapping[str, Any]]) -> Optional[AuthenticationT
has_client_secret = (
env_or_settings(settings, GITHUB_CLIENT_SECRET_ENV, GITHUB_CLIENT_SECRET_PROP, "") != ""
)
has_repo = env_or_settings(settings, GITHUB_REPOSITORY_ENV, GITHUB_REPOSITORY_PROP, "") != ""
has_repo = env_or_settings(settings, _GITHUB_REPOSITORY_ENV, _GITHUB_REPOSITORY_PROP, "") != ""
secret = env_or_settings(settings, GITHUB_AUTH_SECRET_ENV, GITHUB_AUTH_SECRET_PROP, "")
has_secret = len(secret) >= 16
if secret and not has_secret:
LOG.error(
_LOG.error(
"You set a too short secret (length: %i) to protect the admin page, it should have "
"at lease a length of 16",
len(secret),
Expand All @@ -205,10 +205,11 @@ def check_access(
If the authentication type is not GitHub, this function is equivalent to is_auth.
`repo` is the repository to check access to (<organization>/<repository>).
`access_type` is the type of access to check (admin|push|pull).
Arguments:
request: is the request object.
repo: is the repository to check access to (<organization>/<repository>).
access_type: is the type of access to check (admin|push|pull).
"""

if not is_auth(request):
return False

Expand All @@ -222,8 +223,8 @@ def check_access(
"github_repository": (
env_or_settings(
settings,
GITHUB_REPOSITORY_ENV,
GITHUB_REPOSITORY_PROP,
_GITHUB_REPOSITORY_ENV,
_GITHUB_REPOSITORY_PROP,
"",
)
if repo is None
Expand All @@ -232,8 +233,8 @@ def check_access(
"github_access_type": (
env_or_settings(
settings,
GITHUB_ACCESS_TYPE_ENV,
GITHUB_ACCESS_TYPE_PROP,
_GITHUB_ACCESS_TYPE_ENV,
_GITHUB_ACCESS_TYPE_PROP,
"pull",
)
if access_type is None
Expand All @@ -245,7 +246,6 @@ def check_access(

def check_access_config(request: pyramid.request.Request, auth_config: AuthConfig) -> bool:
"""Check if the user has access to the resource."""

auth, user = is_auth_user(request)
if not auth:
return False
Expand All @@ -261,8 +261,8 @@ def check_access_config(request: pyramid.request.Request, auth_config: AuthConfi

repo_url = env_or_settings(
settings,
GITHUB_REPO_URL_ENV,
GITHUB_REPO_URL_PROP,
_GITHUB_REPO_URL_ENV,
_GITHUB_REPO_URL_PROP,
"https://api.github.com/repos",
)
repository = oauth.get(f"{repo_url}/{auth_config.get('github_repository')}").json()
Expand Down
Loading

0 comments on commit 176a667

Please sign in to comment.