Tables secured_data_classification, _policy, _reveal_grant, _break_glass and _access_log; SecuredDataClassifications; the tier resolver with break-glass, table features, policy rows, channel caps and code defaults; the asynchronous access log; enumeration guardrails (rate limit, miss and distinct-value alerts, minimum entropy). Builds on QQQ RC.5 with marked stand-ins for the unreleased fieldaccess API and a reflective bridge. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
96 lines
3.5 KiB
SQL
96 lines
3.5 KiB
SQL
-- PostgreSQL 15+ (NULLS NOT DISTINCT makes the table-wide classification unique).
|
|
-- Extra value-free fingerprint column supports enumeration detection across restarts/nodes.
|
|
|
|
CREATE TABLE secured_data_classification (
|
|
id INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
|
table_name VARCHAR(255) NOT NULL,
|
|
field_name VARCHAR(255),
|
|
classification VARCHAR(255) NOT NULL,
|
|
mask_style VARCHAR(255),
|
|
low_cardinality BOOLEAN,
|
|
notes TEXT,
|
|
create_date TIMESTAMP WITH TIME ZONE,
|
|
modify_date TIMESTAMP WITH TIME ZONE,
|
|
modified_by VARCHAR(255),
|
|
CONSTRAINT secured_data_classification_target UNIQUE NULLS NOT DISTINCT (table_name, field_name)
|
|
);
|
|
|
|
CREATE TABLE secured_data_policy (
|
|
id INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
|
name VARCHAR(255) NOT NULL,
|
|
permission_name VARCHAR(255) NOT NULL,
|
|
classification VARCHAR(255) NOT NULL,
|
|
table_name VARCHAR(255),
|
|
tier VARCHAR(255) NOT NULL,
|
|
reveal_minutes INTEGER DEFAULT 5,
|
|
daily_reveal_cap INTEGER,
|
|
reason_options TEXT,
|
|
allow_free_text_reason BOOLEAN DEFAULT true,
|
|
require_free_text BOOLEAN DEFAULT false,
|
|
client_channel VARCHAR(255),
|
|
exact_match_per_hour INTEGER DEFAULT 20,
|
|
miss_alert_consecutive INTEGER DEFAULT 5,
|
|
distinct_value_alert INTEGER DEFAULT 15,
|
|
min_entropy_enabled BOOLEAN DEFAULT true,
|
|
is_active BOOLEAN DEFAULT true,
|
|
create_date TIMESTAMP WITH TIME ZONE,
|
|
modify_date TIMESTAMP WITH TIME ZONE,
|
|
modified_by VARCHAR(255),
|
|
CHECK (reveal_minutes BETWEEN 1 AND 60),
|
|
CHECK (exact_match_per_hour >= 0 AND miss_alert_consecutive >= 0 AND distinct_value_alert >= 0)
|
|
);
|
|
|
|
CREATE TABLE secured_data_reveal_grant (
|
|
id INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
|
user_id VARCHAR(255) NOT NULL,
|
|
user_name VARCHAR(255),
|
|
table_name VARCHAR(255) NOT NULL,
|
|
record_id VARCHAR(255) NOT NULL,
|
|
field_name VARCHAR(255) NOT NULL,
|
|
reason VARCHAR(255),
|
|
reason_text TEXT,
|
|
granted_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
|
expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
|
access_log_id INTEGER NOT NULL
|
|
);
|
|
|
|
CREATE TABLE secured_data_break_glass (
|
|
id INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
|
user_id VARCHAR(255) NOT NULL,
|
|
user_name VARCHAR(255),
|
|
reason VARCHAR(255),
|
|
reason_text TEXT,
|
|
classification_ceiling VARCHAR(255) DEFAULT 'PHI',
|
|
table_name VARCHAR(255),
|
|
started_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
|
expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
|
ended_at TIMESTAMP WITH TIME ZONE,
|
|
ended_by VARCHAR(255),
|
|
status VARCHAR(255) DEFAULT 'ACTIVE'
|
|
);
|
|
|
|
CREATE TABLE secured_data_access_log (
|
|
id INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
|
|
event_type VARCHAR(255) NOT NULL,
|
|
user_id VARCHAR(255),
|
|
user_name VARCHAR(255),
|
|
table_name VARCHAR(255) NOT NULL,
|
|
record_id TEXT,
|
|
field_name TEXT,
|
|
record_count INTEGER,
|
|
action_name VARCHAR(255),
|
|
reason VARCHAR(255),
|
|
reason_text TEXT,
|
|
client_channel VARCHAR(255),
|
|
outcome VARCHAR(255),
|
|
anomaly_flag BOOLEAN DEFAULT false,
|
|
break_glass_id INTEGER,
|
|
created_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
|
search_value_fingerprints TEXT
|
|
);
|
|
|
|
CREATE INDEX secured_data_policy_lookup ON secured_data_policy (is_active, permission_name, classification, table_name);
|
|
CREATE INDEX secured_data_break_glass_lookup ON secured_data_break_glass (user_id, status, expires_at);
|
|
CREATE INDEX secured_data_reveal_lookup ON secured_data_reveal_grant (user_id, table_name, field_name, record_id, expires_at);
|
|
CREATE INDEX secured_data_access_lookup ON secured_data_access_log (user_id, table_name, field_name, event_type, created_at);
|