Add multi-IP binding modes and deployment guide
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from sqlalchemy import text
|
||||
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, async_sessionmaker, create_async_engine
|
||||
from sqlalchemy.orm import DeclarativeBase
|
||||
|
||||
@@ -40,6 +41,31 @@ def get_session_factory() -> async_sessionmaker[AsyncSession]:
|
||||
return _session_factory
|
||||
|
||||
|
||||
async def ensure_schema_compatibility() -> None:
|
||||
engine = get_engine()
|
||||
statements = [
|
||||
"DROP INDEX IF EXISTS idx_token_bindings_ip",
|
||||
"ALTER TABLE token_bindings ALTER COLUMN bound_ip TYPE TEXT USING bound_ip::text",
|
||||
"ALTER TABLE intercept_logs ALTER COLUMN bound_ip TYPE TEXT USING bound_ip::text",
|
||||
"ALTER TABLE token_bindings ADD COLUMN IF NOT EXISTS binding_mode VARCHAR(16) DEFAULT 'single'",
|
||||
"ALTER TABLE token_bindings ADD COLUMN IF NOT EXISTS allowed_ips JSONB DEFAULT '[]'::jsonb",
|
||||
"UPDATE token_bindings SET binding_mode = 'single' WHERE binding_mode IS NULL OR binding_mode = ''",
|
||||
"""
|
||||
UPDATE token_bindings
|
||||
SET allowed_ips = jsonb_build_array(bound_ip)
|
||||
WHERE allowed_ips IS NULL OR allowed_ips = '[]'::jsonb
|
||||
""",
|
||||
"ALTER TABLE token_bindings ALTER COLUMN binding_mode SET NOT NULL",
|
||||
"ALTER TABLE token_bindings ALTER COLUMN allowed_ips SET NOT NULL",
|
||||
"ALTER TABLE token_bindings ALTER COLUMN binding_mode SET DEFAULT 'single'",
|
||||
"ALTER TABLE token_bindings ALTER COLUMN allowed_ips SET DEFAULT '[]'::jsonb",
|
||||
"CREATE INDEX IF NOT EXISTS idx_token_bindings_ip ON token_bindings(bound_ip)",
|
||||
]
|
||||
async with engine.begin() as connection:
|
||||
for statement in statements:
|
||||
await connection.execute(text(statement))
|
||||
|
||||
|
||||
async def close_db() -> None:
|
||||
global _engine, _session_factory
|
||||
if _engine is not None:
|
||||
|
||||
@@ -2,8 +2,8 @@ from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import Boolean, DateTime, Index, String, func, text
|
||||
from sqlalchemy.dialects.postgresql import CIDR, INET
|
||||
from sqlalchemy import Boolean, DateTime, Index, String, Text, func, text
|
||||
from sqlalchemy.dialects.postgresql import INET
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.models.db import Base
|
||||
@@ -19,7 +19,7 @@ class InterceptLog(Base):
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
token_hash: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
token_display: Mapped[str] = mapped_column(String(20), nullable=False)
|
||||
bound_ip: Mapped[str] = mapped_column(CIDR, nullable=False)
|
||||
bound_ip: Mapped[str] = mapped_column(Text, nullable=False)
|
||||
attempt_ip: Mapped[str] = mapped_column(INET, nullable=False)
|
||||
alerted: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, server_default=text("FALSE"))
|
||||
intercepted_at: Mapped[datetime] = mapped_column(
|
||||
|
||||
@@ -2,27 +2,42 @@ from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import DateTime, Index, SmallInteger, String, func, text
|
||||
from sqlalchemy.dialects.postgresql import CIDR
|
||||
from sqlalchemy import DateTime, Index, SmallInteger, String, Text, func, text
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.models.db import Base
|
||||
|
||||
STATUS_ACTIVE = 1
|
||||
STATUS_BANNED = 2
|
||||
BINDING_MODE_SINGLE = "single"
|
||||
BINDING_MODE_MULTIPLE = "multiple"
|
||||
BINDING_MODE_ALL = "all"
|
||||
|
||||
|
||||
class TokenBinding(Base):
|
||||
__tablename__ = "token_bindings"
|
||||
__table_args__ = (
|
||||
Index("idx_token_bindings_hash", "token_hash"),
|
||||
Index("idx_token_bindings_ip", "bound_ip", postgresql_using="gist", postgresql_ops={"bound_ip": "inet_ops"}),
|
||||
Index("idx_token_bindings_ip", "bound_ip"),
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
token_hash: Mapped[str] = mapped_column(String(64), unique=True, nullable=False)
|
||||
token_display: Mapped[str] = mapped_column(String(20), nullable=False)
|
||||
bound_ip: Mapped[str] = mapped_column(CIDR, nullable=False)
|
||||
bound_ip: Mapped[str] = mapped_column(Text, nullable=False)
|
||||
binding_mode: Mapped[str] = mapped_column(
|
||||
String(16),
|
||||
nullable=False,
|
||||
default=BINDING_MODE_SINGLE,
|
||||
server_default=text("'single'"),
|
||||
)
|
||||
allowed_ips: Mapped[list[str]] = mapped_column(
|
||||
JSONB,
|
||||
nullable=False,
|
||||
default=list,
|
||||
server_default=text("'[]'::jsonb"),
|
||||
)
|
||||
status: Mapped[int] = mapped_column(
|
||||
SmallInteger,
|
||||
nullable=False,
|
||||
|
||||
Reference in New Issue
Block a user