feat: initial public release
ConsentOS — a privacy-first cookie consent management platform. Self-hosted, source-available alternative to OneTrust, Cookiebot, and CookieYes. Full standards coverage (IAB TCF v2.2, GPP v1, Google Consent Mode v2, GPC, Shopify Customer Privacy API), multi-tenant architecture with role-based access, configuration cascade (system → org → group → site → region), dark-pattern detection in the scanner, and a tamper-evident consent record audit trail. This is the initial public release. Prior development history is retained internally. See README.md for the feature list, architecture overview, and quick-start instructions. Licensed under the Elastic Licence 2.0 — self-host freely; do not resell as a managed service.
This commit is contained in:
130
apps/api/src/models/cookie.py
Normal file
130
apps/api/src/models/cookie.py
Normal file
@@ -0,0 +1,130 @@
|
||||
import uuid
|
||||
|
||||
from sqlalchemy import ForeignKey, Integer, String, Text, UniqueConstraint
|
||||
from sqlalchemy.dialects.postgresql import JSONB, UUID
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from src.models.base import Base, TimestampMixin, UUIDPrimaryKeyMixin
|
||||
|
||||
|
||||
class CookieCategory(UUIDPrimaryKeyMixin, TimestampMixin, Base):
|
||||
"""Cookie category taxonomy (necessary, functional, analytics, marketing, personalisation)."""
|
||||
|
||||
__tablename__ = "cookie_categories"
|
||||
|
||||
name: Mapped[str] = mapped_column(String(50), unique=True, nullable=False)
|
||||
slug: Mapped[str] = mapped_column(String(50), unique=True, nullable=False)
|
||||
description: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
is_essential: Mapped[bool] = mapped_column(default=False, nullable=False)
|
||||
display_order: Mapped[int] = mapped_column(Integer, server_default="0", nullable=False)
|
||||
|
||||
# TCF purpose mapping
|
||||
tcf_purpose_ids: Mapped[list | None] = mapped_column(JSONB, nullable=True)
|
||||
|
||||
# Google Consent Mode consent type mapping
|
||||
gcm_consent_types: Mapped[list | None] = mapped_column(JSONB, nullable=True)
|
||||
|
||||
# Relationships
|
||||
cookies: Mapped[list["Cookie"]] = relationship(back_populates="category")
|
||||
allow_list_entries: Mapped[list["CookieAllowListEntry"]] = relationship(
|
||||
back_populates="category"
|
||||
)
|
||||
|
||||
|
||||
class Cookie(UUIDPrimaryKeyMixin, TimestampMixin, Base):
|
||||
"""A cookie discovered on a site via scanning or client-side reporting."""
|
||||
|
||||
__tablename__ = "cookies"
|
||||
__table_args__ = (
|
||||
UniqueConstraint(
|
||||
"site_id",
|
||||
"name",
|
||||
"domain",
|
||||
"storage_type",
|
||||
name="uq_cookies_site_name_domain_type",
|
||||
),
|
||||
)
|
||||
|
||||
site_id: Mapped[uuid.UUID] = mapped_column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("sites.id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
index=True,
|
||||
)
|
||||
category_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("cookie_categories.id", ondelete="SET NULL"),
|
||||
nullable=True,
|
||||
index=True,
|
||||
)
|
||||
|
||||
name: Mapped[str] = mapped_column(String(255), nullable=False, index=True)
|
||||
domain: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
storage_type: Mapped[str] = mapped_column(String(30), server_default="cookie", nullable=False)
|
||||
description: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
vendor: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
||||
path: Mapped[str | None] = mapped_column(String(500), nullable=True)
|
||||
max_age_seconds: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
||||
is_http_only: Mapped[bool | None] = mapped_column(nullable=True)
|
||||
is_secure: Mapped[bool | None] = mapped_column(nullable=True)
|
||||
same_site: Mapped[str | None] = mapped_column(String(10), nullable=True)
|
||||
review_status: Mapped[str] = mapped_column(String(20), server_default="pending", nullable=False)
|
||||
first_seen_at: Mapped[str | None] = mapped_column(String(50), nullable=True)
|
||||
last_seen_at: Mapped[str | None] = mapped_column(String(50), nullable=True)
|
||||
|
||||
# Relationships
|
||||
site: Mapped["Site"] = relationship(back_populates="cookies") # noqa: F821
|
||||
category: Mapped["CookieCategory | None"] = relationship(back_populates="cookies")
|
||||
|
||||
|
||||
class CookieAllowListEntry(UUIDPrimaryKeyMixin, TimestampMixin, Base):
|
||||
"""Approved cookies per site with category assignment."""
|
||||
|
||||
__tablename__ = "cookie_allow_list"
|
||||
__table_args__ = (
|
||||
UniqueConstraint(
|
||||
"site_id",
|
||||
"name_pattern",
|
||||
"domain_pattern",
|
||||
name="uq_allow_list_site_name_domain",
|
||||
),
|
||||
)
|
||||
|
||||
site_id: Mapped[uuid.UUID] = mapped_column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("sites.id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
index=True,
|
||||
)
|
||||
category_id: Mapped[uuid.UUID] = mapped_column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("cookie_categories.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
)
|
||||
name_pattern: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
domain_pattern: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
description: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
|
||||
# Relationships
|
||||
site: Mapped["Site"] = relationship(back_populates="cookie_allow_list") # noqa: F821
|
||||
category: Mapped["CookieCategory"] = relationship(back_populates="allow_list_entries")
|
||||
|
||||
|
||||
class KnownCookie(UUIDPrimaryKeyMixin, TimestampMixin, Base):
|
||||
"""Shared knowledge base of known cookie patterns for auto-categorisation."""
|
||||
|
||||
__tablename__ = "known_cookies"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("name_pattern", "domain_pattern", name="uq_known_cookies_name_domain"),
|
||||
)
|
||||
|
||||
name_pattern: Mapped[str] = mapped_column(String(255), nullable=False, index=True)
|
||||
domain_pattern: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
category_id: Mapped[uuid.UUID] = mapped_column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("cookie_categories.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
)
|
||||
vendor: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
||||
description: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
is_regex: Mapped[bool] = mapped_column(default=False, nullable=False)
|
||||
Reference in New Issue
Block a user