Let a site open secure send to guests, and choose how many times a link opens
Two asks, and the first is a real security decision so it is a SETTING that defaults to the safe answer rather than a code change somebody inherits. securesend_allow_guests, OFF by default. With it off the model is unchanged: a login plus tools.secrets. With it on, somebody with no account - at a shared bay terminal, a contractor - can create a link too. The cost is named in the setting's own description and pinned by a test: a guest secret has NO CREATOR, so the audit trail can say when and from where but never who. That is the whole reason it is a switch a person turns on deliberately. IT FAILS CLOSED. A missing row, a half-migrated site, an unhappy database - the answer is no. An error must never be the thing that opens an unauthenticated write endpoint. A SIGNED-IN USER STILL NEEDS THE PERMISSION even when guests are allowed, which is surprising enough that a test pins it: opening the door to guests is a decision about the door, not about revoking what a role holds. Guest creation is rate limited per address, same shape as the login limiter. Not a security boundary - a determined person has more addresses - but an open endpoint should not be an easy place to park data or fill a disk. VIEWS ARE NOW A NUMBER, 1 to 100, instead of a four-item dropdown. How many people need a password is the sender's business: a shift handover is not one colleague on a call. The floor is what matters, not the ceiling - unlimited views would just be a password on a web page. Also folded in, from the retention question: nothing removed a secret that EXPIRED WITHOUT BEING OPENED. The recipient goes on holiday, the link is never clicked, and the ciphertext sat there for good - not readable, but it made "nothing lingers" untrue and grew the table in exactly the case where the feature failed. Any create now sweeps expired rows and a late click tidies its own, so it stays bounded without a scheduler an air-gapped site would never add. Two gates caught mistakes on the way: current_user is a LocalProxy and is never None, so `current_user is None` silently took the signed-in branch for a guest; and cache was imported straight from shopdb.extensions when it is already on the shopdb.api contract surface. 1836 tests green.
This commit is contained in:
@@ -54,21 +54,44 @@ class ToolsPlugin(BasePlugin):
|
||||
return [SecureSecret]
|
||||
|
||||
def get_permissions(self) -> List:
|
||||
"""One permission, on CREATING a secret only.
|
||||
"""Who may CREATE a secret, when guests are not allowed.
|
||||
|
||||
There is deliberately no read permission: the recipient usually has no
|
||||
account here, and the unguessable token is what authorises the read.
|
||||
Gating that side would make the tool useless for the case it exists for -
|
||||
handing somebody a credential while you are on a call with them.
|
||||
The default access model: signed in, and holding this. Reading needs no
|
||||
permission at all - the recipient usually has no account here and the
|
||||
unguessable token is the authorisation.
|
||||
|
||||
No delete permission either. A secret is spent by being read or by
|
||||
expiring, and both remove the row; a hand-delete route would only add a
|
||||
way to destroy the record of a secret that had not been read yet.
|
||||
A site that turns on securesend_allow_guests bypasses this for people
|
||||
with no account; it still applies to anyone who IS signed in, so a
|
||||
member without it cannot create one while a guest can. That is
|
||||
deliberate: opening the door to guests is a decision about the door, not
|
||||
about revoking what a role does or does not hold.
|
||||
"""
|
||||
return [
|
||||
('tools.secrets', 'Create secure send links', 'tools'),
|
||||
]
|
||||
|
||||
def get_settings_defaults(self) -> List[dict]:
|
||||
"""The one switch this plugin owns.
|
||||
|
||||
Public so the create page can read it BEFORE login and show a guest the
|
||||
form rather than a sign-in wall. It says nothing sensitive: whether the
|
||||
door is open is visible to anyone who tries the door.
|
||||
"""
|
||||
return [
|
||||
{
|
||||
'key': 'securesend_allow_guests',
|
||||
'value': 'false',
|
||||
'valuetype': 'boolean',
|
||||
'category': 'tools',
|
||||
'public': True,
|
||||
'description': ('Let people with no account create secure send '
|
||||
'links. Off by default: a guest secret has no '
|
||||
'creator, so the audit trail can say when and '
|
||||
'from where but never who. Receiving a secret '
|
||||
'never needs an account either way.'),
|
||||
},
|
||||
]
|
||||
|
||||
def get_navigation_items(self) -> List[dict]:
|
||||
return [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user