Skip to content

v2.0.4

Choose a tag to compare

@devinslick devinslick released this 10 Nov 01:06
· 21 commits to main since this release
Immutable release. Only release title and notes can be modified.

Summary

v2.0.4 is a hardening + ergonomics release focused on secure password-free resume flows, clarified and modernized picture APIs, safer destructive actions, and significantly expanded test coverage (now ~98%). It remains fully backward compatible for consumers relying on deprecated method names; all new functionality is additive.

Highlights

1. Authentication Artifacts (Password-Free Resume)

  • New methods:
    • FmdClient.from_auth_artifacts(artifacts: dict) – Restore a client without the raw password.
    • FmdClient.resume(...) – Lower-level variant accepting explicit fields.
    • await client.export_auth_artifacts() – Export base_url, fmd_id, access_token, private_key (PEM), optional password_hash, session_duration, token_issued_at.
    • await client.drop_password() / drop_password=True in create() – Immediately discard raw password after onboarding.
  • 401 handling logic hierarchy:
    1. If raw password present → reauthenticate (existing flow)
    2. Else if password_hash present → hash-based token refresh (_reauth_with_hash())
    3. Else → raise FmdApiException (caller must re-onboard)
  • Private key load now supports both PEM and DER (fallback path tested).

2. Picture API Renaming & Deprecations

  • New canonical methods on Device:
    • get_picture_blobs() – fetch encrypted Base64 blobs.
    • decode_picture() – decrypt + decode into PhotoResult.
  • Deprecated (still functional, emit DeprecationWarning):
    • take_front_photo(), take_rear_photo() → use take_front_picture(), take_rear_picture()
    • fetch_pictures(), get_pictures() (Device) → use get_picture_blobs()
    • download_photo(), get_picture() (Device) → use decode_picture()
  • download_photo() now points directly to decode_picture() (avoids chained deprecated call).

3. Wipe (Factory Reset) Hardening

  • Device.wipe() now strictly requires:
    • confirm=True
    • pin argument present
    • PIN must be alphanumeric ASCII (no spaces). Future enforcement of 16+ length is noted (fmd-android MR 379).
  • Removed redundant space validation branch.

4. Lock Message Support

  • Device.lock(message=...) allows an optional user message; sanitized (quotes / backticks / semicolons removed, whitespace collapsed, 120 char cap). Falls back cleanly if server ignores payload.

5. Expanded Export Functionality & Robustness

  • export_data_zip() improvements:
    • Picture file extension detection: .png via magic bytes, default .jpg else.
    • Resilient manifest entries capturing per-item decryption errors (non-fatal).
    • Additional validation of location/picture list types with graceful fallbacks.

6. Coverage & Testing Improvements

  • Overall coverage ~98% (client.py ~98%, device.py mid/high 90s, models 100%).
  • New targeted tests exercise:
    • DER private key resume path
    • Missing artifact field errors
    • 401 reauth when neither password nor hash is available (expected exception)
    • Hash-based 401 reauth success path
    • PNG export branch + unknown image default .jpg
    • Deprecated wrapper warning emission
    • Non-dict JSON fallback handling & non-list picture responses
    • Error recording during export (location & picture failures)
    • Retry logic: 429 (Retry-After numeric/date/negative), 500/502 sequences, connection errors, backoff jitter/no-jitter paths
    • Masking helpers and retry-after parsing edge cases

7. Documentation Updates

  • README: Added password-free artifact usage, wipe PIN notes, lock message mention, community listing.
  • MIGRATE_FROM_V1.md: Corrected camera method naming, clarified wipe requirements, updated picture usage.
  • AUTH_ARTIFACTS_DESIGN.md: Formal specification of artifact-based resume workflow.

8. Internal / Quality Enhancements

  • Removed chained deprecation in Device.download_photo().
  • Simplified wipe() validation logic (single branch covers spaces & non-alphanumeric).
  • Eliminated redundant PIN space check.
  • Minor consistency and defensive branches now covered or documented.

Deprecations (No Immediate Removal)

Deprecated Replacement
Device.take_front_photo() Device.take_front_picture()
Device.take_rear_photo() Device.take_rear_picture()
Device.fetch_pictures() Device.get_picture_blobs()
Device.get_pictures() (Device wrapper) Device.get_picture_blobs()
Device.download_photo() Device.decode_picture()
Device.get_picture() Device.decode_picture()

Plan: Monitor usage; consider removal or formal EOL notice in a future minor/major once ecosystem migrates.

Security Considerations

  • Encourages immediate password discarding (drop_password=True), reducing exposure of raw credentials.
  • password_hash still sensitive—store using platform secret storage if possible.
  • Wipe PIN validation prevents accidental destructive actions with weak/empty inputs.
  • Lock message sanitization avoids command injection edge cases in future server parsing contexts.

Migration Notes (2.0.3 → 2.0.4)

  • Existing code continues to function; deprecation warnings guide picture API migration.
  • To adopt password-free resume:
    1. Onboard normally with create(..., drop_password=True).
    2. Persist await client.export_auth_artifacts() securely.
    3. Resume with await FmdClient.from_auth_artifacts(artifacts).

Example: Password-Free Cycle

client = await FmdClient.create(url, fmd_id, password, drop_password=True)
artifacts = await client.export_auth_artifacts()
# Persist artifacts securely...
await client.close()

client2 = await FmdClient.from_auth_artifacts(artifacts)
locations = await client2.get_locations(1)

Potential Follow-Ups (Not Included)

  • Enforce future 16+ PIN length once upstream server mandates it.
  • Add a CHANGELOG.md consolidating releases (this file can seed that entry).
  • Provide optional encrypted artifact export (password-protected ZIP or keyring integration).
  • Add coverage for final remaining defensive lines (currently low-risk).

Version

fmd_api.__version__ == "2.0.4"

Acknowledgements

Thanks to contributors and early testers providing feedback on artifact-based auth and API naming clarity.


Released: 2025-11-09