aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/db
diff options
context:
space:
mode:
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.py29
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/db/models.py21
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.