The source headers, LICENSE files, and license metadata had drifted apart. Align the entire project to MIT: - Convert every source-file header to the MIT text across all comment styles (Go, TS, TSX, JS, MJS, SQL, CSS, GraphQL, shell), including SPDX-License-Identifier tags - Set the root and cookie-banner LICENSE files to the MIT text with a "MIT License" title line - Switch the package.json license fields, Docker image label, and cookie-banner README to MIT - Update docs and the genmodels header generator accordingly - Normalize copyright lines to a single format (Copyright (c) <year(s)> Probo Inc <hello@probo.com>.): unify the hello@getprobo.com and hello@probo.inc emails to hello@probo.com and the comma-separated years to a hyphenated range Genuine third-party references are intentionally left untouched: the Lucide icon attributions (Lucide is ISC) and the trivy dependency license allowlist. Signed-off-by: Sacha Al Himdani <sacha@probo.com>
158 lines
6.3 KiB
PL/PgSQL
158 lines
6.3 KiB
PL/PgSQL
-- Copyright (c) 2026 Probo Inc <hello@probo.com>.
|
|
--
|
|
-- Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
-- of this software and associated documentation files (the "Software"), to deal
|
|
-- in the Software without restriction, including without limitation the rights
|
|
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
-- copies of the Software, and to permit persons to whom the Software is
|
|
-- furnished to do so, subject to the following conditions:
|
|
--
|
|
-- The above copyright notice and this permission notice shall be included in
|
|
-- all copies or substantial portions of the Software.
|
|
--
|
|
-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
-- SOFTWARE.
|
|
|
|
-- Access review: decouple campaign data from live sources.
|
|
--
|
|
-- Introduces a per-campaign source snapshot (access_review_campaign_sources)
|
|
-- that owns the source identity (name/category/connector) so a review survives
|
|
-- the deletion of the live access source. Fetch tracking becomes an append-only
|
|
-- log (access_review_campaign_source_fetch_attempts) so each fetch run keeps its own
|
|
-- error. Access entries are repointed from the live source to the snapshot.
|
|
|
|
-- Helper to mint a valid GID (base64url of: tenant 8B | entity type 2B |
|
|
-- timestamp 8B | random 6B) for back-filled rows.
|
|
CREATE FUNCTION pg_temp.gen_gid(tenant_text text, entity_type int) RETURNS text AS $$
|
|
SELECT translate(
|
|
encode(
|
|
decode(rpad(translate(tenant_text, '-_', '+/'), 12, '='), 'base64')
|
|
|| set_byte(set_byte('\x0000'::bytea, 0, (entity_type >> 8) & 255), 1, entity_type & 255)
|
|
|| int8send((extract(epoch FROM clock_timestamp()) * 1000)::bigint)
|
|
|| substring(uuid_send(gen_random_uuid()) FROM 1 FOR 6),
|
|
'base64'),
|
|
'+/', '-_')
|
|
$$ LANGUAGE sql VOLATILE;
|
|
|
|
-- 1. Per-campaign source snapshot. The live access source link is nullable and
|
|
-- ON DELETE SET NULL so deleting a source preserves the review's snapshot.
|
|
CREATE TABLE access_review_campaign_sources (
|
|
id TEXT PRIMARY KEY,
|
|
tenant_id TEXT NOT NULL,
|
|
access_review_campaign_id TEXT NOT NULL REFERENCES access_review_campaigns(id) ON DELETE CASCADE,
|
|
access_source_id TEXT REFERENCES access_sources(id) ON DELETE SET NULL,
|
|
name TEXT NOT NULL,
|
|
category access_source_category NOT NULL,
|
|
connector_id TEXT,
|
|
created_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
|
updated_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
|
UNIQUE (access_review_campaign_id, access_source_id)
|
|
);
|
|
|
|
-- 2. Append-only fetch attempts. Each run is a new row; the snapshot's current
|
|
-- state is the latest attempt.
|
|
CREATE TABLE access_review_campaign_source_fetch_attempts (
|
|
id TEXT PRIMARY KEY,
|
|
tenant_id TEXT NOT NULL,
|
|
access_review_campaign_source_id TEXT NOT NULL REFERENCES access_review_campaign_sources(id) ON DELETE CASCADE,
|
|
attempt_number INTEGER NOT NULL,
|
|
status access_review_campaign_source_fetch_status NOT NULL,
|
|
fetched_accounts_count INTEGER NOT NULL,
|
|
error TEXT,
|
|
started_at TIMESTAMP WITH TIME ZONE,
|
|
completed_at TIMESTAMP WITH TIME ZONE,
|
|
created_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
|
updated_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
|
UNIQUE (access_review_campaign_source_id, attempt_number)
|
|
);
|
|
|
|
CREATE INDEX idx_fetch_attempts_queued
|
|
ON access_review_campaign_source_fetch_attempts (created_at)
|
|
WHERE status = 'QUEUED';
|
|
|
|
-- 3. Back-fill snapshots from every (campaign, source) pair already present in
|
|
-- the scope, entries, or fetches.
|
|
INSERT INTO access_review_campaign_sources (
|
|
id, tenant_id, access_review_campaign_id, access_source_id,
|
|
name, category, connector_id, created_at, updated_at
|
|
)
|
|
SELECT
|
|
pg_temp.gen_gid(s.tenant_id, 102),
|
|
s.tenant_id,
|
|
p.access_review_campaign_id,
|
|
p.access_source_id,
|
|
s.name,
|
|
s.category,
|
|
s.connector_id,
|
|
now(),
|
|
now()
|
|
FROM (
|
|
SELECT access_review_campaign_id, access_source_id
|
|
FROM access_review_campaign_scope_systems
|
|
UNION
|
|
SELECT access_review_campaign_id, access_source_id
|
|
FROM access_entries
|
|
UNION
|
|
SELECT access_review_campaign_id, access_source_id
|
|
FROM access_review_campaign_source_fetches
|
|
) p
|
|
JOIN access_sources s ON s.id = p.access_source_id;
|
|
|
|
-- 4. Repoint access entries at the snapshot.
|
|
ALTER TABLE access_entries
|
|
ADD COLUMN access_review_campaign_source_id TEXT;
|
|
|
|
UPDATE access_entries e
|
|
SET access_review_campaign_source_id = cs.id
|
|
FROM access_review_campaign_sources cs
|
|
WHERE cs.access_review_campaign_id = e.access_review_campaign_id
|
|
AND cs.access_source_id = e.access_source_id;
|
|
|
|
ALTER TABLE access_entries
|
|
ALTER COLUMN access_review_campaign_source_id SET NOT NULL;
|
|
|
|
ALTER TABLE access_entries
|
|
ADD CONSTRAINT access_entries_campaign_source_id_fkey
|
|
FOREIGN KEY (access_review_campaign_source_id)
|
|
REFERENCES access_review_campaign_sources(id) ON DELETE CASCADE;
|
|
|
|
DROP INDEX idx_access_entries_campaign_source_account_key;
|
|
|
|
CREATE UNIQUE INDEX idx_access_entries_campaign_source_account_key
|
|
ON access_entries (access_review_campaign_source_id, account_key);
|
|
|
|
ALTER TABLE access_entries
|
|
DROP COLUMN access_source_id;
|
|
|
|
-- 5. Migrate fetch rows into the append-only attempt log (one attempt each).
|
|
INSERT INTO access_review_campaign_source_fetch_attempts (
|
|
id, tenant_id, access_review_campaign_source_id, attempt_number,
|
|
status, fetched_accounts_count, error, started_at, completed_at,
|
|
created_at, updated_at
|
|
)
|
|
SELECT
|
|
pg_temp.gen_gid(f.tenant_id, 103),
|
|
f.tenant_id,
|
|
cs.id,
|
|
GREATEST(f.attempt_count, 1),
|
|
f.status,
|
|
f.fetched_accounts_count,
|
|
f.last_error,
|
|
f.started_at,
|
|
f.completed_at,
|
|
f.created_at,
|
|
f.updated_at
|
|
FROM access_review_campaign_source_fetches f
|
|
JOIN access_review_campaign_sources cs
|
|
ON cs.access_review_campaign_id = f.access_review_campaign_id
|
|
AND cs.access_source_id = f.access_source_id;
|
|
|
|
-- 6. Drop the superseded tables.
|
|
DROP TABLE access_review_campaign_source_fetches;
|
|
DROP TABLE access_review_campaign_scope_systems;
|