Ctrl K
ComunidadGuías…

migrations/20261024000000_hub_creation_policy.up.sql

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
ALTER TABLE shardline_hub_repos ADD COLUMN creation_authorized BOOLEAN NOT NULL DEFAULT FALSE;
ALTER TABLE shardline_hub_repos ADD COLUMN creation_issuer TEXT;
ALTER TABLE shardline_hub_repos ADD COLUMN creation_subject TEXT;

CREATE TABLE shardline_hub_creation_policies (
    namespace TEXT PRIMARY KEY CHECK (length(namespace) > 0 AND position('/' in namespace) = 0),
    configured BOOLEAN NOT NULL DEFAULT FALSE,
    enabled BOOLEAN NOT NULL DEFAULT TRUE,
    max_repositories BIGINT CHECK (max_repositories IS NULL OR max_repositories >= 0),
    max_creations BIGINT CHECK (max_creations IS NULL OR max_creations >= 0),
    window_seconds BIGINT NOT NULL DEFAULT 3600 CHECK (window_seconds BETWEEN 1 AND 86400)
);
CREATE TABLE shardline_hub_creation_denials (
    namespace TEXT NOT NULL REFERENCES shardline_hub_creation_policies(namespace) ON DELETE CASCADE,
    issuer TEXT NOT NULL,
    subject TEXT NOT NULL,
    PRIMARY KEY (namespace, issuer, subject)
);
CREATE TABLE shardline_hub_creation_events (
    namespace TEXT NOT NULL,
    created_at_unix_seconds BIGINT NOT NULL
);
CREATE INDEX shardline_hub_creation_events_window ON shardline_hub_creation_events(namespace, created_at_unix_seconds);
CREATE INDEX shardline_hub_repos_creation_namespace ON shardline_hub_repos((split_part(repo_id, '/', 1)));
INSERT INTO shardline_hub_creation_events(namespace, created_at_unix_seconds)
SELECT split_part(repo_id, '/', 1), created_at_unix_seconds FROM shardline_hub_repos
WHERE created_at_unix_seconds >= EXTRACT(EPOCH FROM clock_timestamp())::bigint - 86400;

-- One namespace lock serializes creation and policy mutation, including the
-- absent-policy case. Never derive a namespace lock from a repository identity.
CREATE FUNCTION shardline_hub_creation_policy_lock() RETURNS trigger LANGUAGE plpgsql AS $$
BEGIN
    IF TG_OP = 'DELETE' THEN
        PERFORM pg_advisory_xact_lock(1397506896, hashtext(OLD.namespace));
        RETURN OLD;
    END IF;
    IF TG_OP = 'UPDATE' AND NEW.namespace <> OLD.namespace THEN
        RAISE EXCEPTION 'creation policy namespace is immutable';
    END IF;
    PERFORM pg_advisory_xact_lock(1397506896, hashtext(NEW.namespace));
    RETURN NEW;
END;
$$;
CREATE TRIGGER shardline_hub_creation_policy_lock BEFORE INSERT OR UPDATE OR DELETE ON shardline_hub_creation_policies
FOR EACH ROW EXECUTE FUNCTION shardline_hub_creation_policy_lock();
CREATE TRIGGER shardline_hub_creation_denial_lock BEFORE INSERT OR UPDATE OR DELETE ON shardline_hub_creation_denials
FOR EACH ROW EXECUTE FUNCTION shardline_hub_creation_policy_lock();

CREATE FUNCTION shardline_hub_creation_guard() RETURNS trigger LANGUAGE plpgsql AS $$
DECLARE
    ns TEXT := split_part(NEW.repo_id, '/', 1);
    policy shardline_hub_creation_policies%ROWTYPE;
    now_seconds BIGINT;
BEGIN
    PERFORM pg_advisory_xact_lock(1397506896, hashtext(ns));
    -- Touch a shared row even for the absent-policy case. PostgreSQL rejects
    -- stale repeatable-read snapshots rather than letting them miss a policy
    -- installed after the snapshot. Default rows retain legacy behavior.
    INSERT INTO shardline_hub_creation_policies(namespace) VALUES (ns)
    ON CONFLICT(namespace) DO UPDATE SET namespace = EXCLUDED.namespace
    RETURNING * INTO policy;
    IF FOUND THEN
        IF policy.configured AND (NOT policy.enabled OR NOT NEW.creation_authorized OR EXISTS (
            SELECT 1 FROM shardline_hub_creation_denials
            WHERE namespace = ns AND issuer = NEW.creation_issuer AND subject = NEW.creation_subject
        )) THEN
            RAISE EXCEPTION USING ERRCODE = 'SL403', MESSAGE = 'shardline_hub_creation_denied';
        END IF;
        IF NOT EXISTS (SELECT 1 FROM shardline_hub_repos WHERE repo_id = NEW.repo_id) THEN
            IF policy.max_repositories IS NOT NULL AND policy.max_repositories <= (
                SELECT count(*) FROM shardline_hub_repos WHERE split_part(repo_id, '/', 1) = ns
            ) THEN
                RAISE EXCEPTION USING ERRCODE = 'SL409', MESSAGE = 'shardline_hub_creation_quota';
            END IF;
            now_seconds := EXTRACT(EPOCH FROM clock_timestamp())::bigint;
            IF policy.max_creations IS NOT NULL AND policy.max_creations <= (
                SELECT count(*) FROM shardline_hub_creation_events
                WHERE namespace = ns AND created_at_unix_seconds >= now_seconds - policy.window_seconds
            ) THEN
                RAISE EXCEPTION USING ERRCODE = 'SL429', MESSAGE = 'shardline_hub_creation_rate';
            END IF;
        END IF;
    END IF;
    RETURN NEW;
END;
$$;
CREATE TRIGGER shardline_hub_creation_guard BEFORE INSERT ON shardline_hub_repos
FOR EACH ROW EXECUTE FUNCTION shardline_hub_creation_guard();

CREATE FUNCTION shardline_hub_creation_record() RETURNS trigger LANGUAGE plpgsql AS $$
DECLARE
    ns TEXT := split_part(NEW.repo_id, '/', 1);
    now_seconds BIGINT := EXTRACT(EPOCH FROM clock_timestamp())::bigint;
BEGIN
    DELETE FROM shardline_hub_creation_events WHERE namespace = ns AND created_at_unix_seconds < now_seconds - 86400;
    INSERT INTO shardline_hub_creation_events(namespace, created_at_unix_seconds) VALUES (ns, now_seconds);
    RETURN NEW;
END;
$$;
CREATE TRIGGER shardline_hub_creation_record AFTER INSERT ON shardline_hub_repos
FOR EACH ROW EXECUTE FUNCTION shardline_hub_creation_record();

-- Bind table resolution to the owner-installed schema, never a caller's
-- search_path. Functions retain invoker privileges; no elevated role is added.
DO $$
DECLARE
    schema_name TEXT := current_schema();
    function_name TEXT;
BEGIN
    FOREACH function_name IN ARRAY ARRAY['shardline_hub_creation_policy_lock','shardline_hub_creation_guard','shardline_hub_creation_record'] LOOP
        EXECUTE format('ALTER FUNCTION %I.%I() SET search_path TO pg_catalog, %I', schema_name, function_name, schema_name);
    END LOOP;
END;
$$;