Skip to content

Release Python SDK v17.2.2#1007

Open
stas-schaller wants to merge 11 commits into
masterfrom
release/sdk/python/core/v17.2.2
Open

Release Python SDK v17.2.2#1007
stas-schaller wants to merge 11 commits into
masterfrom
release/sdk/python/core/v17.2.2

Conversation

@stas-schaller
Copy link
Copy Markdown
Contributor

@stas-schaller stas-schaller commented May 8, 2026

Summary

Python Core SDK v17.2.2 and Python Helper v1.1.2. The release adds opt-in support for callers that need to supply a custom server public key (KSM-932) for isolated deployments where the server public key is not shipped with the SDK, and fixes a customer-reported bug where the config-decoding utilities raised cryptic TypeError instead of an actionable KeeperError when passed None from an incomplete config (KSM-808). Default behaviour is unchanged for all standard Keeper deployments.

Changes

New Features

  • Custom server public key support (KSM-932): three provisioning paths for injecting a non-default EC P-256 server public key at runtime, with explicit precedence (programmatic > one-time token > pre-existing config). Provisioning surfaces: a serverPublicKey field in storage, a 4-segment one-time-token extension (REGION:clientKey:keyId:serverPublicKeyBase64), and new server_public_key / server_public_key_id parameters on SecretsManager. Also fixes a long-standing missing raise in generate_transmission_key and prevents __init__ from silently resetting a non-standard key_id back to the default when a custom key is configured.

Bug Fixes

  • Cryptic TypeError on None-valued config keys (KSM-808): config-decoding utilities (base64_to_bytes, url_safe_str_to_bytes, base64_to_string, CryptoUtils.url_safe_str_to_bytes) now raise KeeperError with actionable messages instead of TypeError when passed None. Per-call-site guards in core.py (lines 814, 863, 874) name the specific missing ConfigKey (appKey, clientKey) and direct the user to reinitialize with a fresh One-Time Token. 7 new regression tests in tests/config_error_test.py. Customer-reported.
  • Populate empty LICENSE files across 4 packages (core, helper, CLI, storage). All four shipped as 0-byte files declaring MIT in setup.py — legal/compliance gap that would publish to PyPI as license-claim-without-text.
  • Runtime Python version guard mismatch: core.py runtime check raised on < 3.6 while setup.py python_requires and classifiers required >= 3.9 (announced in v17.2.0). Updated guard to match.
  • 4-segment one-time-token parser hardening: malformed tokens (wrong segment count, empty keyId or serverPublicKey segments, invalid base64) now raise ValueError at construction. Previously these silently degraded to default-key behavior — wrong fail-mode for a credential-handling code path.

Maintenance

  • Python Helper bumped to 1.1.2; core dependency tightened to >=17.2.2. Helper version pairs 1:1 with core.
  • Hardcoded fallback version updated (17.2.1 → 17.2.2) in keeper_globals.py and smoke test assertions.
  • Docstrings added to the new public API surface: SecretsManager.__init__'s new constructor params and generate_transmission_key's new arg, including precedence documentation.
  • README section documenting the three provisioning paths in generic terms (deployment-specific details deferred to the official docs).
  • Tests: 8 new test cases covering Layer 1 (config-file) propagation, layer precedence (programmatic > token > config), parser-negative cases (3 segments, 5+ segments, empty segments, invalid base64), and the non-IL5-prefix backwards-compatibility guard. 72/72 tests passing.

Breaking Changes

None. The new field, parameter, and 4-segment token format are opt-in. The new parser errors only fire on inputs that previously silently degraded — no caller relying on documented v17.2.1 behavior is affected. Default behaviour is unchanged for all standard Keeper deployments.

Related Issues

Three-layer mechanism for injecting a custom EC public key at runtime,
allowing IL5 (key_id=20) and other isolated deployments to connect
without adding environment-specific keys to the hardcoded map.

Layer 1 (core): generate_transmission_key() accepts custom_public_key_b64;
key rotation handler retries instead of raising when a custom key is set.

Layer 2 (OTT): 4-segment IL5 token (IL5:clientKey:keyId:b64Key) writes
key material to config at init time so Layer 1 picks it up automatically.

Layer 3 (API): SecretsManager() accepts server_public_key and
server_public_key_id constructor params for code-first initialization.

Also fixes the long-standing missing 'raise' in generate_transmission_key
and prevents __init__ from silently resetting a non-standard key_id to
the default when a custom key is present.

Tests: 5 new tests (one per layer + raise fix + end-to-end retry flow),
all 64 tests pass.
KSM-901 is the JavaScript IL5 ticket; the Python IL5 dynamic-key work
is tracked under KSM-932.
…mic-key

KSM-932: Python IL5 custom server public key support
…ser hardening, docstrings, tests, README

- Populate empty LICENSE files in core, helper, cli, and storage packages
  with the root MIT text. PyPI was about to publish packages declaring
  license=MIT with zero license text.
- Update core.py runtime Python version guard from <3.6 to <3.9 to match
  setup.py python_requires and the 3.9 floor announced in v17.2.0.
- Harden the 4-segment IL5 OTT parser: raise ValueError on wrong segment
  count, empty keyId/serverPublicKey segments, or invalid base64 in the
  serverPublicKey segment. Previously these silently degraded to
  default-key behavior; a credential-handling code path should fail loud.
- Add docstrings to SecretsManager.__init__'s new server_public_key /
  server_public_key_id params and generate_transmission_key's new
  custom_public_key_b64 param. Document precedence between provisioning
  paths. Generic wording for isolated deployments.
- Add 8 new tests (72/72 passing):
  - Layer 1 config-file path supplies custom key
  - Layer precedence: programmatic > token > config
  - Token > config when no programmatic params
  - 4 parser-negative cases (3 segments, 5+ segments, empty segments,
    invalid base64)
  - Non-IL5 prefix with extra segments stays backwards-compatible
- Add a short "Custom Server Public Key (Isolated Deployments)" section
  to the core README pointing at the new constructor params and OTT
  format. Generic language; deployment-specific details deferred to the
  official docs.
…owup

KSM-932: Python SDK v17.2.2 follow-up — LICENSE, Python 3.9 guard, parser hardening, docs
@stas-schaller stas-schaller marked this pull request as ready for review May 18, 2026 19:50
Copy link
Copy Markdown

@claude claude Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Code review skipped — your organization has reached its monthly code review spending cap.

An organization admin can view or raise the cap at claude.ai/admin-settings/claude-code. The cap resets at the start of the next billing period.

Once the cap resets or is raised, push a new commit or reopen this pull request to trigger a review.

idimov-keeper
idimov-keeper previously approved these changes May 22, 2026
Raise KeeperError with actionable messages instead of cryptic TypeError
when None reaches the base64/url-safe decoders from an incomplete config
or empty server response field.

Utility-level guards (utils.py, crypto.py): base64_to_bytes,
url_safe_str_to_bytes, base64_to_string, CryptoUtils.url_safe_str_to_bytes.
base64_to_string also gains a binascii.Error handler for parity.

Per-call-site guards in core.py at the three unguarded config.get sites
(lines 814, 863, 874) name the specific missing ConfigKey ('appKey',
'clientKey') and direct the user to reinitialize with a fresh OTT.

Adds 7 regression tests in tests/config_error_test.py. Full core suite
(79 tests) green.

CHANGELOG and README updated.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants