v2.0.4
·
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()– Exportbase_url,fmd_id,access_token,private_key(PEM), optionalpassword_hash,session_duration,token_issued_at.await client.drop_password()/drop_password=Trueincreate()– Immediately discard raw password after onboarding.
- 401 handling logic hierarchy:
- If raw password present → reauthenticate (existing flow)
- Else if
password_hashpresent → hash-based token refresh (_reauth_with_hash()) - 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 intoPhotoResult.
- Deprecated (still functional, emit
DeprecationWarning):take_front_photo(),take_rear_photo()→ usetake_front_picture(),take_rear_picture()fetch_pictures(),get_pictures()(Device) → useget_picture_blobs()download_photo(),get_picture()(Device) → usedecode_picture()
download_photo()now points directly todecode_picture()(avoids chained deprecated call).
3. Wipe (Factory Reset) Hardening
Device.wipe()now strictly requires:confirm=Truepinargument 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:
.pngvia magic bytes, default.jpgelse. - Resilient manifest entries capturing per-item decryption errors (non-fatal).
- Additional validation of location/picture list types with graceful fallbacks.
- Picture file extension detection:
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_hashstill 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:
- Onboard normally with
create(..., drop_password=True). - Persist
await client.export_auth_artifacts()securely. - Resume with
await FmdClient.from_auth_artifacts(artifacts).
- Onboard normally with
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