Skip to content

Enforce Conventional commits, add deployment name to pfx filename, add endpoint for OIDC self reg token request #121

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

Merged
merged 7 commits into from
May 17, 2025
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 16 additions & 7 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,18 @@
# See https://pre-commit.com/hooks.html for more hooks
default_language_version:
python: python3.11
default_install_hook_types:
- pre-commit
- commit-msg
repos:
- repo: https://github.com/compilerla/conventional-pre-commit
rev: v4.0.0
hooks:
- id: conventional-pre-commit
stages: [commit-msg]
args: []
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
rev: v5.0.0
hooks:
- id: no-commit-to-branch
- id: check-executables-have-shebangs
Expand All @@ -22,29 +31,29 @@ repos:
args:
- --autofix
- repo: https://github.com/psf/black
rev: 23.7.0
rev: 25.1.0
hooks:
- id: black
language_version: python3
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.5.1
rev: v1.15.0
hooks:
- id: mypy
language: system
- repo: https://github.com/pycqa/pylint
rev: v2.17.5
rev: v3.3.6
hooks:
- id: pylint
language: system
- repo: https://github.com/Lucas-C/pre-commit-hooks
rev: v1.5.0
rev: v1.5.5
hooks:
- id: forbid-crlf
- id: remove-crlf
- id: forbid-tabs
- id: remove-tabs
- repo: https://github.com/PyCQA/bandit
rev: 1.7.5
rev: 1.8.3
hooks:
- id: bandit
args: ["--skip=B101"]
Expand All @@ -53,7 +62,7 @@ repos:
hooks:
- id: rst-linter
- repo: https://github.com/Yelp/detect-secrets
rev: v1.4.0
rev: v1.5.0
hooks:
- id: detect-secrets
language: system
Expand Down
4 changes: 2 additions & 2 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ pre-commit considerations

If working in Docker instead of native env you need to run the pre-commit checks in docker too::

docker exec -i rasenmaeher_api_devel /bin/bash -c "pre-commit install"
docker exec -i rasenmaeher_api_devel /bin/bash -c "pre-commit install --install-hooks"
docker exec -i rasenmaeher_api_devel /bin/bash -c "pre-commit run --all-files"

You need to have the container running, see above. Or alternatively use the docker run syntax but using
Expand Down Expand Up @@ -290,7 +290,7 @@ TLDR:
- Install project deps and pre-commit hooks::

poetry install
pre-commit install
pre-commit install --install-hooks
pre-commit run --all-files

- Ready to go.
Expand Down
293 changes: 75 additions & 218 deletions poetry.lock

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,10 @@ psycopg2 = "^2.9"
pytest = "^7.4"
coverage = "^7.3"
pytest-cov = "^4.1"
pylint = "^2.17"
black = "^23.7"
bandit = "^1.7"
mypy = "^1.5"
pylint = "^3.3"
black = "^25.1"
bandit = "^1.8"
mypy = "^1.15"
pre-commit = "^3.3"
pytest-asyncio = ">=0.23,<1.0" # caret behaviour on 0.x is to lock to 0.x.*
bump2version = "^1.0"
Expand Down
3 changes: 2 additions & 1 deletion src/rasenmaeher_api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
""" python-rasenmaeher-api """
"""python-rasenmaeher-api"""

__version__ = "1.6.4" # NOTE Use `bump2version --config-file patch` to bump versions correctly
3 changes: 2 additions & 1 deletion src/rasenmaeher_api/cfssl/anoncsr.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""This needs to be separated to avoid circular imports"""

import logging

import aiohttp
Expand All @@ -18,7 +19,7 @@ async def anon_sign_csr(csr: str, bundle: bool = True) -> str:
params: csr
returns: certificate
"""
async with (await anon_session()) as session:
async with await anon_session() as session:
url = f"{ocsprest_base()}/api/v1/csr/sign"
payload = {"certificate_request": csr, "profile": "client", "bundle": bundle}
try:
Expand Down
1 change: 1 addition & 0 deletions src/rasenmaeher_api/cfssl/base.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Base helpers etc"""

from typing import Any, Mapping, Union, List, cast
import logging
import ssl
Expand Down
1 change: 1 addition & 0 deletions src/rasenmaeher_api/cfssl/mtls.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""mTLS stuff, needs to be away from base to avoid cyclic imports"""

import logging

import aiohttp
Expand Down
11 changes: 6 additions & 5 deletions src/rasenmaeher_api/cfssl/private.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Private apis"""

from typing import Union, Optional, Any, Dict
import asyncio
import logging
Expand Down Expand Up @@ -26,7 +27,7 @@ async def post_ocsprest(
"""Do a POST with the mTLS client"""
if timeout is None:
timeout = RMSettings.singleton().cfssl_timeout
async with (await mtls_session()) as session:
async with await mtls_session() as session:
try:
LOGGER.debug("POSTing to {}, payload={}".format(url, send_payload))
async with session.post(url, data=send_payload, timeout=aiohttp.ClientTimeout(total=timeout)) as response:
Expand Down Expand Up @@ -54,7 +55,7 @@ async def sign_csr(csr: str, bundle: bool = True) -> str:
params: csr, whether to return cert of full bundle
returns: certificate as PEM
"""
async with (await mtls_session()) as session:
async with await mtls_session() as session:
url = f"{ocsprest_base()}/api/v1/csr/sign"
payload = {"certificate_request": csr, "profile": "client", "bundle": bundle}
try:
Expand All @@ -76,7 +77,7 @@ async def sign_ocsp(cert: str, status: str = "good") -> Any:
Call ocspsign endpoint
"""

async with (await mtls_session()) as session:
async with await mtls_session() as session:
url = f"{base_url()}/api/v1/cfssl/ocspsign"
payload = {"certificate": cert, "status": status}
try:
Expand Down Expand Up @@ -129,7 +130,7 @@ async def revoke_serial(serialno: str, authority_key_id: str, reason: ReasonType
Reason must be one of the enumerations of cryptography.x509.ReasonFlags or it's string values (see REASONS_BY_VALUE)
"""
reason = validate_reason(reason)
async with (await mtls_session()) as session:
async with await mtls_session() as session:
url = f"{base_url()}/api/v1/cfssl/revoke"
payload = {
"serial": serialno,
Expand Down Expand Up @@ -168,7 +169,7 @@ async def certadd_pem(pem: Union[str, Path], status: str = "good") -> Any:
if not kid:
raise ValueError("Cannot resolve authority_key_id from the cert")

async with (await mtls_session()) as session:
async with await mtls_session() as session:
url = f"{base_url()}/api/v1/cfssl/certadd"
payload = {
"pem": pem,
Expand Down
9 changes: 5 additions & 4 deletions src/rasenmaeher_api/cfssl/public.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Public things, CA cert, CRL etc"""

from typing import Dict, Any
import logging
import base64
Expand Down Expand Up @@ -28,7 +29,7 @@ async def get_ca() -> str:
returns: CA certificate
"""

async with (await anon_session()) as session:
async with await anon_session() as session:
url = f"{base_url()}/api/v1/cfssl/info"
payload: Dict[str, Any] = {}
# PONDER: Why does this need to be a POST ??
Expand All @@ -42,7 +43,7 @@ async def get_ca() -> str:
async def get_ocsprest_crl(suffix: str) -> bytes:
"""Fetch CRL from OCSPREST"""

async with (await anon_session()) as session:
async with await anon_session() as session:
url = f"{ocsprest_base()}/api/v1/crl/{suffix}"
try:
async with session.get(url) as response:
Expand All @@ -59,7 +60,7 @@ async def get_crl() -> bytes:
returns: DER binary encoded Certificate Revocation List
"""

async with (await anon_session()) as session:
async with await anon_session() as session:
url = f"{base_url()}/api/v1/cfssl/crl"
try:
async with session.get(url, params={"expiry": CRL_LIFETIME}, timeout=default_timeout()) as response:
Expand All @@ -78,7 +79,7 @@ async def get_bundle(cert: str) -> str:
# FIXME: This is not a good way but I don't have a better one right now either
# Force OCSP refresh before getting the bundle so we hopefully get all we need
await refresh_ocsp()
async with (await anon_session()) as session:
async with await anon_session() as session:
url = f"{base_url()}/api/v1/cfssl/bundle"
payload: Dict[str, Any] = {"certificate": cert, "flavor": "optimal"}
try:
Expand Down
1 change: 1 addition & 0 deletions src/rasenmaeher_api/console.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""CLI entrypoints for python-rasenmaeher-api"""

from typing import Dict, Any
import logging
import json
Expand Down
1 change: 1 addition & 0 deletions src/rasenmaeher_api/db/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Gino based database abstraction"""

from .config import DBConfig
from .people import Person, Role
from .enrollments import Enrollment, EnrollmentPool, EnrollmentState
Expand Down
1 change: 1 addition & 0 deletions src/rasenmaeher_api/db/base.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""The Gino baseclass with db connection wrapping"""

from typing import Self, Union
import uuid
import logging
Expand Down
1 change: 1 addition & 0 deletions src/rasenmaeher_api/db/config.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Read database configuration from ENV or .env -file"""

from typing import Optional, cast, Callable, ClassVar, Any
import logging
import functools
Expand Down
1 change: 1 addition & 0 deletions src/rasenmaeher_api/db/dbinit.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Ensure all models are defined and then create tables"""

import logging
from pathlib import Path
import tempfile
Expand Down
1 change: 1 addition & 0 deletions src/rasenmaeher_api/db/engine.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Engine stuff"""

from typing import ClassVar, Optional, Any
import logging
from dataclasses import dataclass, field
Expand Down
1 change: 1 addition & 0 deletions src/rasenmaeher_api/db/enrollments.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Abstractions for enrollments"""

from typing import Dict, Any, Optional, AsyncGenerator, Union
import string
import secrets
Expand Down
1 change: 1 addition & 0 deletions src/rasenmaeher_api/db/errors.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Errors"""

from typing import Sequence, Any

from starlette import status
Expand Down
1 change: 1 addition & 0 deletions src/rasenmaeher_api/db/logincodes.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""DB abstraction for storing nonces etc things needed to prevent re-use of certain tokens"""

from typing import Dict, Any, Optional
import logging
import string
Expand Down
1 change: 1 addition & 0 deletions src/rasenmaeher_api/db/middleware.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Middleware stuff"""

from dataclasses import dataclass, field
import logging
import asyncio
Expand Down
1 change: 1 addition & 0 deletions src/rasenmaeher_api/db/nonces.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""DB abstraction for storing nonces etc things needed to prevent re-use of certain tokens"""

from typing import Dict, Any, Optional
import logging

Expand Down
1 change: 1 addition & 0 deletions src/rasenmaeher_api/db/people.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Abstractions for people"""

from typing import Self, Optional, AsyncGenerator, Dict, Any, Set, Union
import asyncio
import uuid
Expand Down
1 change: 1 addition & 0 deletions src/rasenmaeher_api/jwtinit.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Init JWT issuer"""

from typing import Optional, Union
import logging
import os
Expand Down
8 changes: 7 additions & 1 deletion src/rasenmaeher_api/kchelpers.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
"""Keycloak helpers"""
from typing import Optional, Any, ClassVar, Dict, Set, Union

from typing import Optional, Any, ClassVar, Dict, Set, Union, cast
from dataclasses import dataclass, field
import logging
import uuid
import json

from libpvarki.schemas.product import UserCRUDRequest
from pydantic import BaseModel, Extra, Field
Expand Down Expand Up @@ -233,6 +235,10 @@ async def delete_kc_user(self, user: KCUserData) -> bool:
await self.kcadmin.a_delete_user(user.kc_id)
return True

async def client_access_token(self) -> Dict[str, Union[str, int]]:
"""Create initial access token for a client to register for OIDC"""
return cast(Dict[str, Union[str, int]], json.loads(await self.kcadmin.a_create_initial_access_token()))

async def ensure_product_groups(self) -> Optional[bool]:
"""Make sure each product in manifest has a root level group and initial child-group"""
conf = RMSettings.singleton()
Expand Down
1 change: 1 addition & 0 deletions src/rasenmaeher_api/mtlsinit.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Init mTLS client cert for RASENMAEHER itself"""

import asyncio
from pathlib import Path
import logging
Expand Down
1 change: 1 addition & 0 deletions src/rasenmaeher_api/productapihelpers.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Product integration API helpers"""

from typing import Dict, Optional, Type, Any, Mapping, Tuple
import asyncio
import logging
Expand Down
14 changes: 13 additions & 1 deletion src/rasenmaeher_api/rmsettings.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
""" Application settings. """
"""Application settings."""

from typing import Optional, Any, Dict, ClassVar, List
import enum
from pathlib import Path
Expand Down Expand Up @@ -136,6 +137,17 @@ def load_manifest(self) -> None:
self.kraftwerk_manifest_dict = json.loads(pth.read_text(encoding="utf-8"))
self.kraftwerk_manifest_bool = True

@property
def deployment_name(self) -> str:
"""Resolve the deployment name"""
if not self.kraftwerk_manifest_bool:
self.load_manifest()
if "dns" in self.kraftwerk_manifest_dict:
my_dn = str(self.kraftwerk_manifest_dict["dns"])
return my_dn.split(".", maxsplit=1)[0]
LOGGER.warning("DNS name not defined")
return "undefined"

@property
def valid_product_cns(self) -> List[str]:
"""Get valid CNs for productapi certs"""
Expand Down
1 change: 1 addition & 0 deletions src/rasenmaeher_api/testhelpers.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Helpers for testing, unit and manual"""

from typing import Tuple, List
import logging

Expand Down
1 change: 1 addition & 0 deletions src/rasenmaeher_api/user_integrations.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Functions that routes that deal with users MUST call after changes"""

import uuid
import logging

Expand Down
1 change: 1 addition & 0 deletions src/rasenmaeher_api/web/api/checkauth/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Takreg API."""

from rasenmaeher_api.web.api.checkauth.views import router

__all__ = ["router"]
1 change: 1 addition & 0 deletions src/rasenmaeher_api/web/api/checkauth/views.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Info API views."""

from typing import cast


Expand Down
1 change: 1 addition & 0 deletions src/rasenmaeher_api/web/api/descriptions.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""product descriptions endpoints"""

from typing import Optional, cast
import logging

Expand Down
1 change: 1 addition & 0 deletions src/rasenmaeher_api/web/api/enduserpfx/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Enduserpfx API."""

from rasenmaeher_api.web.api.enduserpfx.views import router

__all__ = ["router"]
Loading