From 3d28f6bff2fef00e47209110cb3db49b330cf897 Mon Sep 17 00:00:00 2001 From: John Coffey Date: Sat, 19 Sep 2026 19:52:37 -0700 Subject: [PATCH] record-compat: tell reach apart from permission in a refusal "You are not an owner of account X" is not a permission the server is withholding; it is how far that identity can see. Answering it with "needs sysMaskedEmailGet" sends you off to grant something that changes nothing. A refusal that mentions ownership now says so, and says which account the run wants: the administrator with the run of the server, with tenant administrators passed as --tenant-admin. --- tools/fork/record-compat.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tools/fork/record-compat.py b/tools/fork/record-compat.py index 41f88ac..3ecdc8e 100755 --- a/tools/fork/record-compat.py +++ b/tools/fork/record-compat.py @@ -74,8 +74,18 @@ class Refused(Exception): super().__init__(str(self)) def __str__(self): - needed = PERMISSION.get(self.method) detail = self.error.get('description') or json.dumps(self.error)[:120] + # "not an owner of account X" is reach, not a permission: the + # identity can see its own account and no further, and no permission + # granted to it changes that. Saying "needs sysMaskedEmailGet" there + # sends you to grant something that won't help. + if 'owner' in detail.lower(): + return (f'{self.who} may not {self.method}: {detail}. ' + f'That is reach, not a permission: this identity can only ' + f'read its own account. Recording every account needs the ' + f'administrator that has the run of the server; a tenant ' + f'administrator belongs in --tenant-admin instead.') + needed = PERMISSION.get(self.method) return (f'{self.who} may not {self.method}: {detail}' + (f' (needs {needed})' if needed else ''))