Skip to content

feat: revalidate device permissions against OS state before use - #471

Open
TarikGul wants to merge 8 commits into
mainfrom
tg/os-permission-revalidation
Open

feat: revalidate device permissions against OS state before use#471
TarikGul wants to merge 8 commits into
mainfrom
tg/os-permission-revalidation

Conversation

@TarikGul

Copy link
Copy Markdown
Member

The decision rule

The two gates are combined, not substituted:

  • An OS refusal denies the request without prompting, because only system settings can reach it. The persisted product decision is left in place, so restoring the OS grant does not re-ask a question the user has already answered.
  • An OS grant the platform has reset resolves through a prompt, because that is what reaches the OS dialog again.
  • A stored product denial stands. An OS that forgot its own state is not a reason to put the question again.
  • A failed status query falls back to the stored decision. A dropped IPC is transient, and reading it as a refusal would let a flaky channel revoke a working capability.

The host admin surface (CoreAdmin::get_permission_authorization_status) keeps reporting stored state on its own. It exists to show what the user decided about a product, and folding OS state into it would make that reading wrong.

Hosts

truapi-host-cli serves the capability, driven by TRUAPI_OS_DENIED_PERMISSIONS (comma-separated capability names, matched case-insensitively). Capabilities it does not name report NotApplicable rather than Granted, since a terminal has no OS gate to grant anything.

The WASM hosts install the adapter when the JS side supplies devicePermissionStatus.

Not in scope

Native hosts cannot answer yet: HostCallbacks has no status method, so iOS and Android adoption is a follow-up on each. Until then they resolve from stored state, exactly as now.

granted: false still covers both a product-scoped refusal and an OS-level one, so a product cannot tell "you declined this" from "the OS revoked this, open system settings". The two have different remedies. Distinguishing them needs a richer device-permission response than the current boolean, so it waits for a breaking-change window.

Verification

Nine unit tests cover the decision matrix in host_logic::permissions. Three integration tests drive a real permissions_request_device_permission frame through the generated dispatcher, which is the only place the plumbing from installer to runtime to service is exercised.

Every one of those tests was confirmed to fail under a deliberate mutation: dropping the OS-denial override, never re-prompting after a reset, mapping a query failure to a refusal, clearing the stored grant on refusal, re-prompting on any NotDetermined, treating an absent adapter as a refusal, an installer that stores nothing, and a runtime that builds the service without the adapter.

Live, against the truapi-host CLI with persisted state across processes:

run state TRUAPI_OS_DENIED_PERMISSIONS camera microphone
A cold, auto-accept unset granted: true granted: true
B warm unset granted: true granted: true
C warm camera granted: false granted: true
E warm unset granted: true granted: true
F warm, no auto-accept unset granted: true granted: true
G cold, no auto-accept unset granted: false granted: false

C is the fix: a persisted grant stops answering while the OS refuses it, and only for the capability named. E shows the refusal did not erase the grant. F and G separate stored state from prompting: with no auto-accept and stdin closed, warm state still answers true and cold state cannot.

rel: #334

@TarikGul
TarikGul requested review from a team August 21, 2026 21:11
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.

1 participant