Make revoking final, keep password rules in one place, drop a grant nobody read
Three credential and authz findings, all the same shape: a second path to a
decision, with weaker rules than the path built for it.
PUT /api/apitokens/<id> {"isactive": true} un-revoked a token, and the ORIGINAL
secret worked again - so "revoked" meant "paused until somebody flips it back",
for a credential whose secret is assumed to be out by the act of revoking it.
Revocation is one way now. Deactivating through the same route still works;
bringing one back does not, and the refusal says to mint a new token.
PUT /api/users/<self> accepted a `password` field with no current-password
proof and no length rule, while /api/auth/change-password requires both. When
two routes reach the same state the weaker one is the real rule, so the stronger
one was decoration. Changing your own password through the users route is
refused and points at the right endpoint. An ADMIN may still set one - that is
how a temporary password is issued - with the same 8-character floor, and
mustchangepassword is now set, because a password somebody else chose is
temporary by definition.
tools.secrets was declared guest-grantable, so the role grid offered it and
stored it, and the secure-send route never consulted the guest role at all: it
decides on securesend_allow_guests, which fails closed and carries the rate
limit. Ticking the box changed nothing in either direction, so the screen and
the behaviour disagreed. The declaration is gone; tools.view stays, because it
really does gate the nav entry.
Removing it needed care: update_role refuses the WHOLE request when a submitted
permission is not grantable, and the grid resubmits what the role already holds,
so a site whose guest role held tools.secrets could no longer save that role at
all - the deadlock fixed in 723a4ff, re-created from the other end. Migration
7d41 revokes the grant from the guest role, and nothing is lost because it
granted nothing.
Eight tests. The first two verified by planting: remove either guard and three
fail.
Scope: product
This commit is contained in:
56
migrations/versions/7d41_drop_guest_tools_secrets.py
Normal file
56
migrations/versions/7d41_drop_guest_tools_secrets.py
Normal file
@@ -0,0 +1,56 @@
|
||||
"""Revoke a guest grant that never did anything, before it locks the role.
|
||||
|
||||
tools.secrets was declared guest-grantable, so the role grid offered it and
|
||||
stored it - and the secure-send route never consulted the guest ROLE at all. It
|
||||
decides on the securesend_allow_guests setting, which fails closed and carries
|
||||
the rate limit. A site that ticked the box got no change in behaviour, in either
|
||||
direction.
|
||||
|
||||
The plugin no longer declares it grantable. That alone would repeat the deadlock
|
||||
fixed in 723a4ff: update_role refuses the WHOLE request when a submitted
|
||||
permission is not grantable, and the role grid resubmits what the role already
|
||||
holds - so a site whose guest role holds tools.secrets could no longer save the
|
||||
guest role at all, including to change something else entirely.
|
||||
|
||||
So the grant is removed here, from the one role that could hold it. Nothing is
|
||||
lost: it granted nothing. Guest secret-link creation stays exactly where it was,
|
||||
under securesend_allow_guests.
|
||||
|
||||
Only the GUEST role is touched. A signed-in role holding tools.secrets is
|
||||
unaffected - that permission works normally for an account.
|
||||
|
||||
Revision ID: 7d41_drop_guest_tools_secrets
|
||||
Revises: 7d40_machine_maintenance_dates
|
||||
"""
|
||||
from alembic import op
|
||||
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
revision = '7d41_drop_guest_tools_secrets'
|
||||
down_revision = '7d40_machine_maintenance_dates'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
bind = op.get_bind()
|
||||
inspector = sa.inspect(bind)
|
||||
tables = set(inspector.get_table_names())
|
||||
if not {'roles', 'permissions', 'rolepermissions'} <= tables:
|
||||
return
|
||||
|
||||
bind.execute(sa.text(
|
||||
'DELETE FROM rolepermissions WHERE roleid IN '
|
||||
'(SELECT roleid FROM roles WHERE rolename = :guest) '
|
||||
'AND permissionid IN '
|
||||
'(SELECT permissionid FROM permissions WHERE name = :permission)'),
|
||||
{'guest': 'guest', 'permission': 'tools.secrets'})
|
||||
|
||||
|
||||
def downgrade():
|
||||
"""Deliberately a no-op.
|
||||
|
||||
Re-granting a permission that nothing reads would restore the confusion
|
||||
rather than any capability.
|
||||
"""
|
||||
Reference in New Issue
Block a user