diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-09-30 12:37:31 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-09-30 12:37:31 +0200 |
| commit | 8a4651e9d223de856ff085b329801998f95db138 (patch) | |
| tree | 6153ffaf46030613fa9024e85b9890c7fa979154 /packages/meshbay-hub/src/meshbay_hub/db | |
| parent | 1684fcb64531eaf2e9bb8567ba4bdcbada6469d8 (diff) | |
| download | meshbay-8a4651e9d223de856ff085b329801998f95db138.tar.gz | |
fix(hub): the admin allow-list grants an account, not a username
Each name in admin_usernames is pinned to the first active account seen
holding it (admin_pins), so a name freed by a deletion grants nothing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/db')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/db/migrations/versions/b2c3d4e5f6a8_admin_pins.py | 29 | ||||
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/db/models.py | 21 |
2 files changed, 50 insertions, 0 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/db/migrations/versions/b2c3d4e5f6a8_admin_pins.py b/packages/meshbay-hub/src/meshbay_hub/db/migrations/versions/b2c3d4e5f6a8_admin_pins.py new file mode 100644 index 0000000..57de9b2 --- /dev/null +++ b/packages/meshbay-hub/src/meshbay_hub/db/migrations/versions/b2c3d4e5f6a8_admin_pins.py @@ -0,0 +1,29 @@ +"""the admin allow-list grants an account, not whoever holds the name + +Revision ID: b2c3d4e5f6a8 +Revises: a1b2c3d4e5f7 +""" + +from collections.abc import Sequence + +import sqlalchemy as sa +from alembic import op + +revision: str = "b2c3d4e5f6a8" +down_revision: str | Sequence[str] | None = "a1b2c3d4e5f7" +branch_labels: str | Sequence[str] | None = None +depends_on: str | Sequence[str] | None = None + + +def upgrade() -> None: + op.create_table( + "admin_pins", + sa.Column("username", sa.String(64), primary_key=True), + sa.Column("user_id", sa.String(36), nullable=False), + sa.Column("pinned_at", sa.DateTime(timezone=True), nullable=False, + server_default=sa.func.now()), + ) + + +def downgrade() -> None: + op.drop_table("admin_pins") diff --git a/packages/meshbay-hub/src/meshbay_hub/db/models.py b/packages/meshbay-hub/src/meshbay_hub/db/models.py index a0437d6..293b940 100644 --- a/packages/meshbay-hub/src/meshbay_hub/db/models.py +++ b/packages/meshbay-hub/src/meshbay_hub/db/models.py @@ -437,6 +437,27 @@ class LoginThrottle(Base): last_failure_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False) +class AdminPin(Base): + """ + Which account an allow-listed admin name (`hub.toml` `admin_usernames`) + belongs to, recorded the first time an active account holds it. + + The allow-list names people by username, and a username is not an identity: + deleting an account releases its name, and whoever registered it next + inherited the admin role. The allow-list now grants the pinned + account, so a name that changes hands grants nothing. No foreign key, on + purpose: the pin has to outlive the account it points at, or the name would + be free to pin again. A name removed from the allow-list loses its pin at + the next start, which is how an operator hands a listed name to a new + account. + """ + __tablename__ = "admin_pins" + + username: Mapped[str] = mapped_column(String(64), primary_key=True) + user_id: Mapped[str] = mapped_column(String(36), nullable=False) + pinned_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_now) + + class HubSetting(Base): """ Instance-wide settings an admin changes at runtime from the panel. |