Repository navigation
Time & pay for every workspace: multi-tenant audit fixes and per-workspace switches - #650
Merged
Merged
Conversation
A multi-workspace audit of the time system (docs/audits/2026-11-10-time-multi-tenant-audit.md). No cross-workspace read was found; these are the fixes and the settings that came out of it. Audit fixes - Payroll: timesheets import paid hours (unpaid breaks left out), so the timesheet, the Pay page and the payroll export agree (ClockShift#paid_hours_between). - Time zones: tenants.time_zone (blank = instance default). Web, staff API, kiosk and time & pay jobs run on the workspace's clock (Current.with_tenant, use_business_zone). - Push: a token registers to one account across workspaces; forgetting needs only the token digest; iPhone sign-out, offboarding and deactivation stop delivery. - PIN clock: an anonymous browser gets one answer for every PIN; a signed-in phone is compared with its own person's PIN only; tries are counted per device or network address. - Privacy: Team schedule and the punch report no longer show colleagues' hours and pay rates to people who do not review them or lack payroll access. - Store scope is enforced on punch-fix, timesheet and time-off decisions, not only in the inbox. - Jobs: sync schedulers rescue per workspace; leave accrual uses each workspace's date. - iPhone app: Switch workspace, handoff accepts only its own control plane, notifications open only a path on the current instance, push registration carries the web views' cookie. - Expo client: no prefilled workspace; sign-out clears the name and workspace. Per-workspace switches (Settings -> Time rules, settings.write) - What is turned on: web clock, phone apps, register PIN, kiosk, breaks, scheduling, trades, time-off requests, tips and pay for staff, punch fixes by staff, self-submitted timesheets, missed clock-out reminders. All on by default; off is refused by the server (pages, staff API 403 feature_disabled, PIN clock) and hidden from menus. - Punch logging: device and register name, an optional detailed audit trail (PunchAudit, time_clock.* activity lines) with or without the network address, and its retention. - Every change is logged old -> new. One additive migration: tenants.time_zone, an index on push_devices.token_digest. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
A transitive dependency of the Expo client; the advisory published 2026-11-10 fails the mobile audit on every branch. Lockfile only. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…d device A signed-in phone with no store device gets one answer for every PIN that is not its own (a colleague's real PIN included), and a locked own PIN says it is locked. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
The sync-health tool test created a sync ten minutes ago and expected it to count for today. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
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.
What this is
An end-to-end audit of the time-keeping system for more than one workspace, the fixes it led to, and per-workspace switches for each part of the time system. Full write-up:
docs/audits/2026-11-10-time-multi-tenant-audit.md.No cross-workspace read was found: every time & pay model is
TenantScoped, and ids from another workspace 404 on the web, the staff API, exports and the PIN clock (now proven by tests). The findings are at the edges.Findings and fixes
ClockShift#paid_hours_between;TimesheetWorkflowimport and staleness checks use it.tenants.time_zone(blank = unchanged). Web, staff API, kiosk and jobs run on the workspace's clock (Current.with_tenant,use_business_zone).PushDevice.register!moves the token across workspaces;forget_token!needs no session; iPhone sign-out, offboarding and deactivated accounts stop delivery.payroll.read.People::StoreScope.covers?in the service and policies.controlorigin; a notification could name another workspace; no way to leave a workspace.Current.with_tenant./clock/workers500 when anonymous; client-chosen punchsource; Expo builds prefilled one workspace; unclear refusal for an email used in another workspace; settings audit lacked values.Known limits left for later are listed in the audit document (one zone per workspace, Eastern cron times, the
.icsfeed, cross-store trades as a warning).What administrators can now turn on and off
Settings → Time rules (
/settings/time_rules, needssettings.write), for the whole workspace:time_clock.*activity lines), the network address, and how long the trail is kept (3, 4 or 7 years)Every switch defaults to what Rupert did before, so the live workspace is unchanged until someone opens the page. Off is refused server-side (pages redirect with a message, the staff API answers 403
feature_disabled, the PIN clock refuses) and hidden from menus. Every change is an activity-log line with who, when and old → new.Migration
One additive migration:
tenants.time_zone(nullable) and a short-named index onpush_devices.token_digest.Tests
time_multi_tenant_isolation_test.rb: two workspaces with the same staff name, employee number and PIN (pages, staff API, PIN clock, exports, jobs, clock zone, settings, push, setup links).time_features_settings_test.rb: defaults, permission, audit, every switch on web + API + PIN clock, logging, retention, and a brand-new worker in a brand-new workspace from setup link to payroll export.Behaviour changes to know about
🤖 Generated with Claude Code