Skip to content

feat(registry): permission separation and audit trail for restaurant deactivation#295

Merged
Leothosine merged 1 commit into
tosirano:mainfrom
devwums:feat/permission-separation-audit-trail-284
Jun 25, 2026
Merged

feat(registry): permission separation and audit trail for restaurant deactivation#295
Leothosine merged 1 commit into
tosirano:mainfrom
devwums:feat/permission-separation-audit-trail-284

Conversation

@devwums

@devwums devwums commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Summary

Replaces the permissionless set_active() function with three strictly separated operations, adds a full audit trail to the Restaurant struct, and covers all actor/action permutations with unit tests.

Root cause

set_active() allowed either the admin or the restaurant owner to flip is_active on any restaurant with no record of who performed the action. A compromised admin key could silently deactivate every restaurant, and there was no way to distinguish a voluntary owner closure from an admin suspension.

Changes

Restaurant struct

  • Added deactivated_by: Option - the address that last deactivated the restaurant
  • Added deactivated_at: Option - the ledger timestamp of the last deactivation

Removed

  • set_active() - eliminated to enforce strict role separation

Added

owner_close_restaurant(env, caller, restaurant_id)

  • Owner-only; permanent closure
  • Panics with "unauthorized: only the restaurant owner can close their restaurant" if called by anyone else, including the admin
  • Emits (rest, close) event with (restaurant_id, caller, timestamp)

�dmin_suspend_restaurant(env, caller, restaurant_id, reason: Symbol)

  • Admin-only; recoverable suspension
  • Panics with "unauthorized: only the admin can suspend a restaurant" if called by a non-admin
  • Emits (rest, suspend) event with (restaurant_id, caller, timestamp, reason)

�dmin_unsuspend_restaurant(env, caller, restaurant_id)

  • Admin-only; clears deactivated_by and deactivated_at, restores is_active = true
  • Emits (rest, unsuspnd) event with (restaurant_id, caller, timestamp)

Tests added / updated

Test Actor Action Expected
est_owner_can_close_own_restaurant Owner owner_close_restaurant ? succeeds, audit fields set
est_admin_cannot_call_owner_close Admin owner_close_restaurant ? panics unauthorized
est_admin_can_suspend_and_unsuspend Admin suspend then unsuspend ? state toggled correctly
est_owner_cannot_call_admin_suspend Owner �dmin_suspend_restaurant ? panics unauthorized

closes #284

…d operations with audit trail

- Add deactivated_by: Option<Address> and deactivated_at: Option<u64> to Restaurant struct for full audit trail
- Add owner_close_restaurant(): owner-only permanent closure; panics with 'unauthorized' if called by non-owner (including admin)
- Add admin_suspend_restaurant(reason: Symbol): admin-only recoverable suspension; emits (rest, suspend) event with caller, timestamp, and reason
- Add admin_unsuspend_restaurant(): admin-only; clears deactivated_by and deactivated_at; emits (rest, unsuspnd) event
- Remove set_active() to enforce strict role separation — admins and owners can no longer silently cross permission boundaries
- Add Symbol to imports
- Add tests: owner closes own restaurant, admin cannot call owner_close, admin suspends and unsuspends, owner cannot call admin_suspend, plus retained legacy tests
@Leothosine Leothosine merged commit d90197c into tosirano:main Jun 25, 2026
2 of 3 checks passed
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.

[CO-06] Add permission separation and audit trail to restaurant_registry admin operations

2 participants