Repository navigation
TESA: re-resolve live state in modify/revoke (auto-activation + start change) - #22
Merged
Merged
Conversation
Smartair auto-activates a pre-assignment into a check-in when its activation time passes. The grant ref froze precheckin=true at creation, so modify kept calling precheckinModifyDate on a preAssignationId that no longer exists -> [700] ERROR_BAD_PARAMETERS (and precheckinModifyDate also requires roomId, which was omitted). Revoke had the same stale-flag bug. modify/revoke now re-read the room's live state (findAllRooms) and match it back to the grant before acting: - our preAssignationId still pending -> precheckin ops (now sends roomId too) - room occupied and its keyPad equals our PIN -> check-in ops - anything else -> "gone": modify raises LockNotFoundError, revoke no-ops The PIN is the only durable handle tying a check-in back to its grant (Smartair returns it as keyPad, unique among live credentials), so the base contract gains an additive pin=None on modify_access/revoke_access. Other providers accept and ignore it (stable ref handles, no stale-flag problem). The PIN is never written to the opaque ref. "Gone" is the safe default: we never touch a stay we cannot prove is ours, so a pre-assignment cancelled elsewhere or a room taken over by another guest is left alone. Validated end-to-end against a real Smartair server.
Smartair's checkinModifyDate/precheckinModifyDate move only the expiration; the activation date is immutable. A modify that changes the start therefore cannot be expressed in place — most visibly when arrival is delayed and a live check-in must turn back into a future pre-assignment. _do_modify_access now detects a start change and, only then, revokes and recreates the grant reusing the same PIN (so the guest's credential is unchanged) and returns the fresh ref. A pure expiration change keeps the cheap in-place ModifyDate path. The trigger avoids a recreate loop: a now/past start on an already-active check-in needs no recreate (its historical activation instant is irrelevant and Smartair pins it to "now"); only a future start on a live check-in, a now/past start on a still-pending pre-assignment, or a future start whose pre-assignment activation no longer matches forces the recreate. This is a TESA peculiarity, kept in the adapter: the base contract stays vendor-agnostic and other providers (which can move the start in place) are untouched.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
TESA Smartair grants carry a
precheckinflag frozen at creation time, but Smartair auto-activates a pre-assignment into a check-in when its activation time passes. The frozen flag then lies, and bothmodifyandrevokebroke against a real server:modifykept callingprecheckinModifyDateon apreAssignationIdthat no longer existed →[700] ERROR_BAD_PARAMETERS(and that call was also missing the requiredroomId).revokehad the same stale-flag bug.A second, broader gap: Smartair's
*ModifyDateops only move the expiration, never the activation. So any change to the start date (most visibly: a delayed arrival turning a live check-in back into a future pre-assignment) could not be expressed by an in-place modify.What
1. Re-resolve live state (
b50a317).modify/revokenow read the room's live state (findAllRooms) and match it back to the grant by its PIN — Smartair returns the activekeyPad, unique among live credentials — before acting:preAssignationIdstill pending → precheckin ops (now sendsroomIdtoo)keyPadequals our PIN → check-in opsmodifyraisesLockNotFoundError,revokeno-ops"Gone" is the safe default: we never touch a stay we can't prove is ours (cancelled elsewhere, or a room taken over by another guest).
2. Recreate on start change (
7780eb9). When the requested start differs from what Smartair holds,modifyrevokes + recreates the grant reusing the same PIN (guest credential unchanged) and returns a fresh ref. A pure expiration change keeps the cheap in-place path. The trigger avoids a recreate loop (a now/past start on an already-active check-in needs no recreate).Contract change (additive, backward-compatible)
modify_access/revoke_accessgain an optionalpin=None: the only durable handle tying a check-in back to its grant. Other providers (TTLock/Omnitec/Salto) accept and ignore it — they keep stable ref handles and can move the start in place, so they have no stale-flag problem. The PIN is never written to the opaque ref.Tests
48 TESA tests (incl. the exact bug scenario, owner-takeover, the delayed-arrival recreate, and same-activation no-recreate) + 58 base/other-provider tests. All green.
Validated end-to-end against a real Smartair server (modify of an activated pre-assignment, and the delayed-arrival checkin→precheckin recreate reusing the PIN).