diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9ab88c6..ca065ca 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,11 +25,11 @@ jobs: name: build sdist runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: actions/setup-python@v5 with: - python-version: 3.9 + python-version: 3.12 - name: build sdist run: | @@ -53,6 +53,12 @@ jobs: fail-fast: false matrix: include: + - os: macOS-13 + version: cp314-macosx_x86_64 + prerelease: true + - os: macOS-15 + version: cp314-macosx_arm64 + prerelease: true - os: macOS-13 version: cp313-macosx_x86_64 - os: macOS-15 @@ -73,13 +79,9 @@ jobs: version: cp39-macosx_x86_64 - os: macOS-15 version: cp39-macosx_arm64 - - os: macOS-13 - version: cp38-macosx_x86_64 - - os: macOS-15 - version: cp38-macosx_arm64 steps: - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v5 with: name: artifact-sdist path: ./ @@ -92,43 +94,21 @@ jobs: rm krb5-*.tar.gz - name: build wheel - uses: pypa/cibuildwheel@v2.23.0 + uses: pypa/cibuildwheel@v3.1.4 env: CIBW_BUILD: ${{ matrix.version }} CIBW_BUILD_VERBOSITY: 1 + CIBW_PRERELEASE_PYTHONS: ${{ matrix.prerelease || 'false' }} - uses: actions/upload-artifact@v4 with: path: ./wheelhouse/*.whl name: artifact-wheel-${{ matrix.version }} - build_cython_0_29: - name: build cython 0.29.x - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-python@v5 - with: - python-version: 3.9 - - - name: build cython 0.29 - run: | - sudo /bin/bash -c 'source ./build_helpers/lib.sh; lib::setup::system_requirements' - - python -m pip install build - echo 'Cython<3.0.0' > constraints.txt - PIP_CONSTRAINT=constraints.txt python -m build \ - --sdist \ - --wheel \ - --installer pip \ - --verbose - test: name: test needs: - build_sdist - - build_cython_0_29 - build_wheels runs-on: ${{ matrix.os }} @@ -139,12 +119,12 @@ jobs: - ubuntu-latest - macOS-latest python-version: - - 3.8 - 3.9 - '3.10' - '3.11' - '3.12' - '3.13' + - '3.14-dev' provider: - mit - heimdal @@ -154,13 +134,13 @@ jobs: provider: mit steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v5 with: pattern: artifact-* merge-multiple: true @@ -189,7 +169,7 @@ jobs: id-token: write steps: - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v5 with: pattern: artifact-* merge-multiple: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 56cb391..d89b459 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ # Changelog -## 0.8.0 - TBD +## 0.8.0 - 2025-09-01 + +* Require Python 3.9 or newer (dropped 3.8) +* Pin Cython build requirements to `3.1.3` for enabling deterministic builds per release ## 0.7.1 - 2025-03-06 diff --git a/README.md b/README.md index cc74fab..6abafc2 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Due to the complex nature of this API it is highly recommended to use something * [MIT Kebreros](https://web.mit.edu/kerberos/) - Minimum 1.17 * [Heimdal](https://github.com/heimdal/heimdal) * A C compiler, such as GCC -* Python 3.6+ +* Python 3.9+ _Note: macOS includes their own implementation of Heimdal and a compiler isn't needed on that platform if installing from the wheel._ diff --git a/build_helpers/lib.sh b/build_helpers/lib.sh index 10d6bdf..676a9ed 100755 --- a/build_helpers/lib.sh +++ b/build_helpers/lib.sh @@ -54,7 +54,7 @@ lib::setup::python_requirements() { KRB5_VERSION="$( python -c "import build.util; print(build.util.project_wheel_metadata('.').get('Version'))" )" python -m pip install krb5=="${KRB5_VERSION}" \ - --find-links "file://${PWD}/dist" \ + --find-links dist \ --verbose echo "Installing dev dependencies" diff --git a/pyproject.toml b/pyproject.toml index 5378cf2..55a264e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [build-system] requires = [ - "Cython >= 0.29.32, < 4.0.0", # 0.29.31 includes noexcept keyword support + "Cython == 3.1.3", "setuptools >= 42.0.0", # Supports license_files ] build-backend = "setuptools.build_meta" @@ -49,24 +49,3 @@ warn_unused_ignores = true [tool.pytest.ini_options] testpaths = "tests" addopts = "--import-mode=importlib" - -[tool.tox] -legacy_tox_ini = """ -[tox] -envlist = sanity,py36,py37,py38,py39,py310 -skip_missing_interpreters = true -isolated_build = True - -[testenv] -deps = - -r{toxinidir}/requirements-dev.txt - -commands = - python -m pytest -v - -[testenv:sanity] -commands = - python -m black . --check - python -m isort . --check-only - python -m mypy . -""" diff --git a/requirements-dev.txt b/requirements-dev.txt index 882211e..3b5cdb2 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,7 +1,7 @@ -black==22.10.0 -isort==5.10.1 +black==25.1.0 +isort==6.0.1 k5test>=0.10.4 # Needed for MITRealm.start_kadmind env default -mypy==0.982 +mypy==1.17.1 pre-commit pytest tox diff --git a/setup.cfg b/setup.cfg index 939da79..94ce3fc 100644 --- a/setup.cfg +++ b/setup.cfg @@ -14,21 +14,20 @@ keywords = kerberos classifiers = Development Status :: 4 - Beta - License :: OSI Approved :: MIT License Programming Language :: Python :: 3 - Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 Programming Language :: Python :: 3.12 Programming Language :: Python :: 3.13 + Programming Language :: Python :: 3.14 [options] package_dir = =src packages = find: include_package_data = True -python_requires = >= 3.8 +python_requires = >= 3.9 [options.packages.find] where = src diff --git a/src/krb5/_ccache.pyi b/src/krb5/_ccache.pyi index dc08b07..5cbe2cb 100644 --- a/src/krb5/_ccache.pyi +++ b/src/krb5/_ccache.pyi @@ -11,17 +11,17 @@ from krb5._principal import Principal class CredentialsRetrieveFlags(enum.IntEnum): """Flags used to control :meth:`cc_retrieve_cred` and :meth:`cc_remove_cred`.""" - none: CredentialsRetrieveFlags = ... #: No matching flags set - match_times: CredentialsRetrieveFlags = ... #: The requested lifetime must be at least as great as specified - match_is_skey: CredentialsRetrieveFlags = ... #: The is_skey field must match exactly - match_flags: CredentialsRetrieveFlags = ... #: All the flags set in the match credentials must be set - match_times_exact: CredentialsRetrieveFlags = ... #: All the time fields must match exactly - match_flags_exact: CredentialsRetrieveFlags = ... #: All the flags must match exactly - match_authdata: CredentialsRetrieveFlags = ... #: The authorization data must match - match_srv_nameonly: CredentialsRetrieveFlags = ... #: Only the name portion of the principal name must match - match_2nd_tkt: CredentialsRetrieveFlags = ... #: The second ticket must match - match_keytype: CredentialsRetrieveFlags = ... #: The encryption key type must match - supported_ktypes: CredentialsRetrieveFlags = ... #: The supported key types must match + none = ... #: No matching flags set + match_times = ... #: The requested lifetime must be at least as great as specified + match_is_skey = ... #: The is_skey field must match exactly + match_flags = ... #: All the flags set in the match credentials must be set + match_times_exact = ... #: All the time fields must match exactly + match_flags_exact = ... #: All the flags must match exactly + match_authdata = ... #: The authorization data must match + match_srv_nameonly = ... #: Only the name portion of the principal name must match + match_2nd_tkt = ... #: The second ticket must match + match_keytype = ... #: The encryption key type must match + supported_ktypes = ... #: The supported key types must match class CCache: """Kerberos CCache @@ -34,15 +34,19 @@ class CCache: def __iter__(self) -> typing.Iterator[Creds]: """Iterate credentials in a ccache.""" + @property def addr(self) -> typing.Optional[int]: """The raw krb5_ccache pointer address of this credential cache.""" + @property def name(self) -> typing.Optional[bytes]: """The name/residual of the credential cache.""" + @property def principal(self) -> typing.Optional[Principal]: """Default client principal of the credential cache.""" + @property def cache_type(self) -> typing.Optional[bytes]: """The type of the credential cache.""" diff --git a/src/krb5/_creds.pyi b/src/krb5/_creds.pyi index d0840c3..31ae8e2 100644 --- a/src/krb5/_creds.pyi +++ b/src/krb5/_creds.pyi @@ -59,18 +59,23 @@ class Creds: @property def client(self) -> Principal: """Client's principal identifier.""" + @property def server(self) -> Principal: """Server's principal identifier.""" + @property def keyblock(self) -> KeyBlock: """Session encryption key info.""" + @property def times(self) -> TicketTimes: """Lifetime info.""" + @property def ticket_flags_raw(self) -> int: """Flags in ticket, as returned by libkrb5.""" + @property def ticket_flags(self) -> TicketFlags: """Flags in ticket, converted to a representation where the first flag is in the lowermost bit.""" @@ -80,6 +85,7 @@ class Creds: @property def ticket(self) -> bytes: """Ticket string itself.""" + @property def second_ticket(self) -> bytes: """second ticket, if related to ticket (via DUPLICATE-SKEY or ENC-TKT-IN-SKEY)""" @@ -127,6 +133,7 @@ class Krb5Prompt: num_prompts: Number of times :meth:`prompt` is to be called for this prompt session. """ + def prompt( self, msg: bytes, diff --git a/src/krb5/_creds_opt_mit.pyi b/src/krb5/_creds_opt_mit.pyi index 1691dca..7f3d97e 100644 --- a/src/krb5/_creds_opt_mit.pyi +++ b/src/krb5/_creds_opt_mit.pyi @@ -11,8 +11,8 @@ from krb5._creds_opt import GetInitCredsOpt class FastFlags(enum.IntEnum): """Flags used to control :meth:`get_init_creds_opt_set_fast_flags`.""" - none: FastFlags = ... #: No flags set. - required: FastFlags = ... #: Require KDC to support FAST. + none = ... #: No flags set. + required = ... #: Require KDC to support FAST. def get_init_creds_opt_set_fast_ccache( context: Context, diff --git a/src/krb5/_keyblock.pyi b/src/krb5/_keyblock.pyi index 4a2fcc3..b9b0d42 100644 --- a/src/krb5/_keyblock.pyi +++ b/src/krb5/_keyblock.pyi @@ -18,6 +18,7 @@ class KeyBlock: @property def data(self) -> bytes: """The keyblock data.""" + @property def enctype(self) -> int: """The keyblock encryption type.""" diff --git a/src/krb5/_kt.pyi b/src/krb5/_kt.pyi index 857fd20..355a77f 100644 --- a/src/krb5/_kt.pyi +++ b/src/krb5/_kt.pyi @@ -23,12 +23,15 @@ class KeyTab: being iterated does not exist. It may not be possible to add/remove entries on a keytab while it is being enumerated. """ + @property def addr(self) -> typing.Optional[int]: """The raw krb5_keytab pointer address of this credential cache.""" + @property def name(self) -> typing.Optional[bytes]: """The name/residual of the keytab.""" + @property def kt_type(self) -> typing.Optional[bytes]: """The type of the keytab.""" @@ -49,12 +52,15 @@ class KeyTabEntry: @property def key(self) -> KeyBlock: """The keytab key data block.""" + @property def kvno(self) -> int: """The key version number associated with the keytab entry.""" + @property def principal(self) -> Principal: """The principal associated with the keytab entry.""" + @property def timestamp(self) -> int: """The time creation entry of the keytab entry.""" diff --git a/src/krb5/_principal.pyi b/src/krb5/_principal.pyi index 1f81fed..d2e2bff 100644 --- a/src/krb5/_principal.pyi +++ b/src/krb5/_principal.pyi @@ -9,19 +9,19 @@ from krb5._context import Context class PrincipalParseFlags(enum.IntEnum): """Flags used to control :meth:`parse_name_flags`.""" - none: PrincipalParseFlags = ... #: No parse flags set - no_realm: PrincipalParseFlags = ... #: Error if realm is present - require_realm: PrincipalParseFlags = ... #: Error if realm is not present - enterprise: PrincipalParseFlags = ... #: Create single-component enterprise principal - ignore_realm: PrincipalParseFlags = ... #: Ignore realm if present + none = ... #: No parse flags set + no_realm = ... #: Error if realm is present + require_realm = ... #: Error if realm is not present + enterprise = ... #: Create single-component enterprise principal + ignore_realm = ... #: Ignore realm if present class PrincipalUnparseFlags(enum.IntEnum): """Flags used to control :meth:`unparse_name_flags`.""" - none: PrincipalUnparseFlags = ... #: No unparse flags set - short: PrincipalUnparseFlags = ... #: Omit realm if it is the local realm - no_realm: PrincipalUnparseFlags = ... #: Omit realm always - display: PrincipalUnparseFlags = ... #: Don't escape special characters + none = ... #: No unparse flags set + short = ... #: Omit realm if it is the local realm + no_realm = ... #: Omit realm always + display = ... #: Don't escape special characters class NameType(enum.IntEnum): """A kerberos principal name type""" @@ -51,21 +51,27 @@ class Principal: def __copy__(self) -> "Principal": """Create a copy of the principal object.""" + @property def addr(self) -> typing.Optional[int]: """The raw krb5_principal pointer address of this credential cache.""" + @property def name(self) -> typing.Optional[bytes]: """The name of the principal.""" + @property def realm(self) -> bytes: """The realm of the principal.""" + @property def components(self) -> typing.List[bytes]: """The list of components of the principal.""" + @property def type(self) -> NameType: """The name type of the principal.""" + @type.setter def type(self, value: NameType) -> None: pass