diff --git a/.github/check_entitlements.sh b/.github/check_entitlements.sh index ea98f1a..401f7bf 100755 --- a/.github/check_entitlements.sh +++ b/.github/check_entitlements.sh @@ -1,4 +1,6 @@ -#!/bin/bash +#!/usr/bin/env bash + +set -euo pipefail # Derive additional environment variables TOKEN_URL="${OIDC_OP_TOKEN_ENDPOINT}" @@ -24,6 +26,11 @@ get_token() { echo "🔐 Getting access token..." BEARER=$( get_token | jq -r '.access_token' ) +if [ -z "$BEARER" ]; then + echo "❌ Failed to get access token." + exit 1 +fi + # NOTE: It's always okay to print this token, because it will # only be valid / available in dummy / dev scenarios [[ "${DEBUG:-}" == "1" ]] && echo "Got Access Token: ${BEARER}" diff --git a/.github/workflows/platform-integration-test.yaml b/.github/workflows/platform-integration-test.yaml index f4d8420..6c280c5 100644 --- a/.github/workflows/platform-integration-test.yaml +++ b/.github/workflows/platform-integration-test.yaml @@ -154,6 +154,7 @@ jobs: with: enable-cache: true cache-dependency-glob: "uv.lock" + python-version: ${{ inputs.python_version }} - name: Run all tests, minus integration tests env: diff --git a/README.md b/README.md index f99c32a..9b63337 100644 --- a/README.md +++ b/README.md @@ -13,65 +13,6 @@ Unofficial OpenTDF SDK for Python A legacy version (0.2.x) of this project is available for users who need the previous implementation. For more information, see [LEGACY_VERSION.md](docs/LEGACY_VERSION.md) or visit the [legacy branch on GitHub](https://github.com/b-long/opentdf-python-sdk/tree/0.2.x). -## Prerequisites - -This project uses [uv](https://docs.astral.sh/uv/) for dependency management and task running. - -### Installing uv - -Install `uv` using one of the following methods: - -**macOS/Linux:** -```bash -curl -LsSf https://astral.sh/uv/install.sh | sh -``` - -**Windows:** -```powershell -powershell -c "irm https://astral.sh/uv/install.ps1 | iex" -``` - -**Using Homebrew (macOS):** -```bash -brew install uv -``` - -For more installation options, see the [uv installation guide](https://docs.astral.sh/uv/getting-started/installation/). - -## Development Setup - -1. Clone the repository: -```bash -git clone -cd opentdf-python-sdk -``` - -2. Install dependencies: -```bash -uv sync -``` - -## Running Tests - -Run the full test suite: -```bash -uv run pytest tests/ -``` - -Run specific test files: -```bash -uv run pytest tests/test_sdk.py -``` - -Run tests with verbose output: -```bash -uv run pytest tests/ -v -``` - -Run integration tests only: -```bash -uv run pytest tests/ -m integration -``` ## Installation @@ -80,21 +21,6 @@ Install from PyPI: pip install otdf-python ``` - -## Protobuf & Connect RPC Generation - -This project uses a dedicated submodule, `otdf-python-proto/`, for generating Python protobuf files and Connect RPC clients from OpenTDF platform proto definitions. - -### Regenerating Protobuf & Connect RPC Files - -From the submodule: -```bash -cd otdf-python-proto -uv run python scripts/generate_connect_proto.py -``` - -See [`otdf-python-proto/README.md`](otdf-python-proto/README.md) and [`PROTOBUF_SETUP.md`](PROTOBUF_SETUP.md) for details. - ## Quick Start ### Basic Configuration @@ -141,7 +67,7 @@ sdk = builder.build() from io import BytesIO # Create TDF configuration with attributes -config = sdk.new_tdf_config(attributes=["https://example.com/attr/classification/value/public"]) +config = sdk.new_tdf_config(attributes=["https://example.net/attr/attr1/value/value1"]) # Encrypt data to TDF format input_data = b"Hello, World!" @@ -164,8 +90,7 @@ with open("encrypted.tdf", "rb") as f: encrypted_data = f.read() # Decrypt TDF -reader_config = TDFReaderConfig() -tdf_reader = sdk.load_tdf(encrypted_data, reader_config) +tdf_reader = sdk.load_tdf(encrypted_data) decrypted_data = tdf_reader.payload # Save decrypted data @@ -189,6 +114,7 @@ src/otdf_python/ └── ... # Additional modules tests/ └── ... # Various tests +``` ## Contributing @@ -196,14 +122,14 @@ tests/ 2. Create a feature branch: `git checkout -b feature-name` 3. Make your changes 4. Run tests: `uv run pytest tests/` -5. Commit your changes: `git commit -am 'Add feature'` +5. Commit your changes: `git commit -am 'feat: add feature'` 6. Push to the branch: `git push origin feature-name` 7. Submit a pull request ### Release Process For maintainers and contributors working on releases: -- See [RELEASES.md](RELEASES.md) for comprehensive release documentation +- See [RELEASES.md](docs/RELEASES.md) for comprehensive release documentation - Feature branch alpha releases available for testing changes before merge - Automated releases via Release Please on the main branch diff --git a/docs/DEVELOPING.md b/docs/DEVELOPING.md index 2affa49..dfa5aac 100644 --- a/docs/DEVELOPING.md +++ b/docs/DEVELOPING.md @@ -16,3 +16,80 @@ You can run the following command in your terminal: ``` Using this script will automatically enable direct access grants in Keycloak for you. + +## Dependency Management + +### Prerequisites + +This project uses [uv](https://docs.astral.sh/uv/) for dependency management and task running. + +#### Installing uv + +Install `uv` using one of the following methods: + +**macOS/Linux:** +```bash +curl -LsSf https://astral.sh/uv/install.sh | sh +``` + +**Windows:** +```powershell +powershell -c "irm https://astral.sh/uv/install.ps1 | iex" +``` + +**Using Homebrew (macOS):** +```bash +brew install uv +``` + +For more installation options, see the [uv installation guide](https://docs.astral.sh/uv/getting-started/installation/). + +## Development Setup + +1. Clone the repository: +```bash +git clone https://github.com/b-long/opentdf-python-sdk.git +cd opentdf-python-sdk +``` + +2. Install dependencies: +```bash +uv sync +``` + +### Running Tests + +Run the full test suite: +```bash +uv run pytest tests/ +``` + +Run specific test files: +```bash +uv run pytest tests/test_sdk.py +``` + +Run tests with verbose output: +```bash +uv run pytest tests/ -v +``` + +Run integration tests only: +```bash +uv run pytest tests/ -m integration +``` + + +### Protobuf & Connect RPC Generation + +This project uses a dedicated submodule, `otdf-python-proto/`, for generating Python protobuf files and Connect RPC clients from OpenTDF platform proto definitions. + +#### Regenerating Protobuf & Connect RPC Files + +From the submodule: +```bash +cd otdf-python-proto +uv run python scripts/generate_connect_proto.py +``` + +See [`otdf-python-proto/README.md`](../otdf-python-proto/README.md) and [`PROTOBUF_SETUP.md`](./PROTOBUF_SETUP.md) for details. diff --git a/src/otdf_python/cli.py b/src/otdf_python/cli.py index 3c99be3..48a1019 100644 --- a/src/otdf_python/cli.py +++ b/src/otdf_python/cli.py @@ -20,7 +20,6 @@ from otdf_python.sdk import SDK from otdf_python.sdk_builder import SDKBuilder from otdf_python.sdk_exceptions import SDKException -from otdf_python.tdf import TDFReaderConfig try: __version__ = metadata.version("otdf-python") @@ -310,10 +309,7 @@ def cmd_decrypt(args): if encrypted_data.startswith(b"PK"): # Regular TDF (ZIP format) logger.debug("Decrypting TDF") - reader_config = TDFReaderConfig() - tdf_reader = sdk.load_tdf_with_config( - encrypted_data, reader_config - ) + tdf_reader = sdk.load_tdf(encrypted_data) # Access payload directly from TDFReader payload_bytes = tdf_reader.payload output_file.write(payload_bytes) @@ -336,8 +332,7 @@ def cmd_decrypt(args): if encrypted_data.startswith(b"PK"): # Regular TDF (ZIP format) logger.debug("Decrypting TDF") - reader_config = TDFReaderConfig() - tdf_reader = sdk.load_tdf_with_config(encrypted_data, reader_config) + tdf_reader = sdk.load_tdf(encrypted_data) payload_bytes = tdf_reader.payload output_file.write(payload_bytes) logger.info("Successfully decrypted TDF") @@ -370,10 +365,7 @@ def cmd_inspect(args): if encrypted_data.startswith(b"PK"): # Regular TDF logger.debug("Inspecting TDF") - reader_config = TDFReaderConfig() - tdf_reader = sdk.load_tdf_with_config( - BytesIO(encrypted_data), reader_config - ) + tdf_reader = sdk.load_tdf(BytesIO(encrypted_data)) manifest = tdf_reader.manifest # Try to get data attributes diff --git a/src/otdf_python/sdk.py b/src/otdf_python/sdk.py index 407db14..b23d9d9 100644 --- a/src/otdf_python/sdk.py +++ b/src/otdf_python/sdk.py @@ -348,11 +348,13 @@ def get_platform_url(self) -> str | None: """Returns the platform URL if set""" return self.platform_url - def load_tdf_with_config( - self, tdf_data: bytes | BinaryIO | BytesIO, config: TDFReaderConfig + def load_tdf( + self, + tdf_data: bytes | BinaryIO | BytesIO, + config: TDFReaderConfig | None = None, ) -> TDFReader: """ - Loads a TDF from the provided data according to the config. + Loads a TDF from the provided data, optionally according to the config. Args: tdf_data: The TDF data as bytes, file object, or BytesIO @@ -365,26 +367,10 @@ def load_tdf_with_config( SDKException: If there's an error loading the TDF """ tdf = TDF(self.services) - return tdf.load_tdf(tdf_data, config) - - def load_tdf_without_config( - self, tdf_data: bytes | BinaryIO | BytesIO - ) -> TDFReader: - """ - Loads a TDF from the provided data. - - Args: - tdf_data: The TDF data as bytes, file object, or BytesIO + if config is None: + config = TDFReaderConfig() - Returns: - TDFReader: Contains payload and manifest - - Raises: - SDKException: If there's an error loading the TDF - """ - tdf = TDF(self.services) - default = TDFReaderConfig() - return tdf.load_tdf(tdf_data, default) + return tdf.load_tdf(tdf_data, config) def create_tdf( self, diff --git a/tests/integration/test_pe_interaction.py b/tests/integration/test_pe_interaction.py index 00e4447..e9828a3 100644 --- a/tests/integration/test_pe_interaction.py +++ b/tests/integration/test_pe_interaction.py @@ -25,7 +25,7 @@ def decrypt(input_path: Path, output_path: Path, sdk: SDK): with open(input_path, "rb") as infile, open(output_path, "wb") as outfile: try: logger.debug("Decrypting TDF") - tdf_reader = sdk.load_tdf_without_config(infile.read()) + tdf_reader = sdk.load_tdf(infile.read()) # Access payload directly from TDFReader payload_bytes = tdf_reader.payload outfile.write(payload_bytes) diff --git a/tests/test_validate_otdf_python.py b/tests/test_validate_otdf_python.py index 10b3860..369b40f 100644 --- a/tests/test_validate_otdf_python.py +++ b/tests/test_validate_otdf_python.py @@ -13,7 +13,6 @@ import pytest -from otdf_python.tdf import TDFReaderConfig from tests.integration.support_sdk import get_sdk # Set up detailed logging @@ -52,13 +51,8 @@ def decrypt_file(encrypted_path: Path) -> Path: output_path = encrypted_path.with_suffix(".decrypted") with open(encrypted_path, "rb") as infile, open(output_path, "wb") as outfile: - # Include attributes for policy enforcement - reader_config = TDFReaderConfig( - attributes=_test_attributes # Same attributes used in encrypt_file - ) - # Use KAS client for key unwrapping - reader = sdk.load_tdf_with_config(infile.read(), reader_config) + reader = sdk.load_tdf(infile.read()) # TDFReader is a dataclass with payload attribute outfile.write(reader.payload) return output_path