This commit is contained in:
2026-07-26 19:52:28 +05:30
parent d6dc33d9b1
commit f77605f8f3
26 changed files with 384 additions and 1 deletions

View File

@@ -36,7 +36,6 @@ cygnus:
assertion-decryption-private-key: ${CYGNUS_ASSERTION_DECRYPTION_PRIVATE_KEY:file:./config/keys/assertion-decryption-private.pem} assertion-decryption-private-key: ${CYGNUS_ASSERTION_DECRYPTION_PRIVATE_KEY:file:./config/keys/assertion-decryption-private.pem}
access-token-private-key: ${CYGNUS_ACCESS_TOKEN_PRIVATE_KEY:file:./config/keys/access-token-private.pem} access-token-private-key: ${CYGNUS_ACCESS_TOKEN_PRIVATE_KEY:file:./config/keys/access-token-private.pem}
access-token-public-key: ${CYGNUS_ACCESS_TOKEN_PUBLIC_KEY:file:./config/keys/access-token-public.pem} access-token-public-key: ${CYGNUS_ACCESS_TOKEN_PUBLIC_KEY:file:./config/keys/access-token-public.pem}
clients: {}
login-encryption: login-encryption:
key-id: ${CYGNUS_LOGIN_KEY_ID:cygnus-login-2026-01} key-id: ${CYGNUS_LOGIN_KEY_ID:cygnus-login-2026-01}
private-key-location: ${CYGNUS_LOGIN_PRIVATE_KEY:file:./config/keys/login-private.pem} private-key-location: ${CYGNUS_LOGIN_PRIVATE_KEY:file:./config/keys/login-private.pem}

View File

@@ -0,0 +1,153 @@
BEGIN;
-- DMLP phase 2: introduce the tenant, installation and license source of truth.
-- The fixed legacy tenant is a migration bridge for the identity data copied by
-- 001_identity_login_schema.sql. Replace its migration license through the
-- administration workflow before commercial enforcement is enabled.
CREATE TABLE IF NOT EXISTS identity.client_account (
tenant_id uuid PRIMARY KEY,
client_slug character varying(80) NOT NULL,
client_name character varying(200) NOT NULL,
status character varying(20) NOT NULL,
created_at timestamp with time zone NOT NULL DEFAULT now(),
updated_at timestamp with time zone NOT NULL DEFAULT now(),
CONSTRAINT uq_identity_client_account_slug UNIQUE (client_slug),
CONSTRAINT ck_identity_client_account_slug
CHECK (client_slug ~ '^[a-z0-9]+(?:-[a-z0-9]+)*$'),
CONSTRAINT ck_identity_client_account_status
CHECK (status IN ('ACTIVE', 'SUSPENDED', 'CANCELLED'))
);
CREATE TABLE IF NOT EXISTS identity.client_installation (
installation_id uuid PRIMARY KEY,
tenant_id uuid NOT NULL,
client_id character varying(80) NOT NULL,
installation_code character varying(100) NOT NULL,
assertion_public_key text NOT NULL,
allowed_scopes text[] NOT NULL DEFAULT ARRAY[]::text[],
enabled boolean NOT NULL DEFAULT true,
security_version integer NOT NULL DEFAULT 1,
last_authenticated_at timestamp with time zone,
created_at timestamp with time zone NOT NULL DEFAULT now(),
updated_at timestamp with time zone NOT NULL DEFAULT now(),
CONSTRAINT fk_identity_installation_tenant
FOREIGN KEY (tenant_id)
REFERENCES identity.client_account (tenant_id),
CONSTRAINT uq_identity_installation_client_code
UNIQUE (client_id, installation_code),
CONSTRAINT ck_identity_installation_client_id
CHECK (client_id ~ '^[A-Za-z0-9][A-Za-z0-9_-]*$'),
CONSTRAINT ck_identity_installation_code
CHECK (installation_code ~ '^[A-Za-z0-9][A-Za-z0-9_-]*$'),
CONSTRAINT ck_identity_installation_security_version
CHECK (security_version > 0)
);
CREATE TABLE IF NOT EXISTS identity.client_license (
license_id uuid PRIMARY KEY,
tenant_id uuid NOT NULL,
license_type character varying(30) NOT NULL,
package_code character varying(50) NOT NULL,
valid_from timestamp with time zone NOT NULL,
valid_until timestamp with time zone NOT NULL,
status character varying(20) NOT NULL,
max_users integer,
max_installations integer,
created_at timestamp with time zone NOT NULL DEFAULT now(),
updated_at timestamp with time zone NOT NULL DEFAULT now(),
CONSTRAINT fk_identity_license_tenant
FOREIGN KEY (tenant_id)
REFERENCES identity.client_account (tenant_id),
CONSTRAINT ck_identity_license_period
CHECK (valid_until > valid_from),
CONSTRAINT ck_identity_license_status
CHECK (status IN ('ACTIVE', 'SUSPENDED', 'EXPIRED', 'CANCELLED')),
CONSTRAINT ck_identity_license_limits
CHECK ((max_users IS NULL OR max_users > 0)
AND (max_installations IS NULL OR max_installations > 0))
);
CREATE INDEX IF NOT EXISTS ix_identity_installation_tenant_enabled
ON identity.client_installation (tenant_id, enabled);
CREATE INDEX IF NOT EXISTS ix_identity_installation_lookup
ON identity.client_installation (client_id, installation_code, enabled);
CREATE INDEX IF NOT EXISTS ix_identity_license_tenant_period
ON identity.client_license (tenant_id, status, valid_from, valid_until);
-- Add tenant ownership without changing any legacy primary-key values.
ALTER TABLE identity.company
ADD COLUMN IF NOT EXISTS tenant_id uuid;
ALTER TABLE identity.company_branch
ADD COLUMN IF NOT EXISTS tenant_id uuid;
ALTER TABLE identity.user_group
ADD COLUMN IF NOT EXISTS tenant_id uuid;
ALTER TABLE identity.app_user
ADD COLUMN IF NOT EXISTS tenant_id uuid;
ALTER TABLE identity.permission
ADD COLUMN IF NOT EXISTS tenant_id uuid;
ALTER TABLE identity.denied_pages
ADD COLUMN IF NOT EXISTS tenant_id uuid;
ALTER TABLE identity.user_loginhistory
ADD COLUMN IF NOT EXISTS tenant_id uuid;
-- Seed a stable bridge tenant for all identity data that already exists.
INSERT INTO identity.client_account
(tenant_id, client_slug, client_name, status)
VALUES ('00000000-0000-4000-8000-000000000001', 'matrix', 'Matrix', 'ACTIVE')
ON CONFLICT (tenant_id) DO UPDATE SET
client_slug = EXCLUDED.client_slug,
client_name = EXCLUDED.client_name,
updated_at = now();
-- A non-expiring migration bridge keeps current users operational. It must be
-- replaced by a commercial license before license administration goes live.
INSERT INTO identity.client_license
(license_id, tenant_id, license_type, package_code,
valid_from, valid_until, status)
VALUES ('00000000-0000-4000-8000-000000000002',
'00000000-0000-4000-8000-000000000001',
'MIGRATION', 'LEGACY_FULL',
'2020-01-01 00:00:00+00', '2099-12-31 23:59:59+00', 'ACTIVE')
ON CONFLICT (license_id) DO NOTHING;
UPDATE identity.company
SET tenant_id = '00000000-0000-4000-8000-000000000001'
WHERE tenant_id IS NULL;
UPDATE identity.company_branch
SET tenant_id = '00000000-0000-4000-8000-000000000001'
WHERE tenant_id IS NULL;
UPDATE identity.user_group
SET tenant_id = '00000000-0000-4000-8000-000000000001'
WHERE tenant_id IS NULL;
UPDATE identity.app_user
SET tenant_id = '00000000-0000-4000-8000-000000000001'
WHERE tenant_id IS NULL;
UPDATE identity.permission
SET tenant_id = '00000000-0000-4000-8000-000000000001'
WHERE tenant_id IS NULL;
UPDATE identity.denied_pages
SET tenant_id = '00000000-0000-4000-8000-000000000001'
WHERE tenant_id IS NULL;
UPDATE identity.user_loginhistory
SET tenant_id = '00000000-0000-4000-8000-000000000001'
WHERE tenant_id IS NULL;
CREATE INDEX IF NOT EXISTS ix_identity_company_tenant
ON identity.company (tenant_id, company_id);
CREATE INDEX IF NOT EXISTS ix_identity_branch_tenant
ON identity.company_branch (tenant_id, company_id, branch_id);
CREATE INDEX IF NOT EXISTS ix_identity_group_tenant
ON identity.user_group (tenant_id, group_id);
CREATE INDEX IF NOT EXISTS ix_identity_user_tenant_login
ON identity.app_user (tenant_id, upper(loginid));
CREATE INDEX IF NOT EXISTS ix_identity_permission_tenant_group_page
ON identity.permission (tenant_id, group_id, page_id)
WHERE permission <> '000';
CREATE INDEX IF NOT EXISTS ix_identity_denied_tenant_user_page
ON identity.denied_pages (tenant_id, user_id, page_id)
WHERE isdenied = 1;
CREATE INDEX IF NOT EXISTS ix_identity_login_history_tenant_user_time
ON identity.user_loginhistory (tenant_id, user_id, logintime DESC);
COMMIT;

View File

@@ -0,0 +1,231 @@
BEGIN;
-- DMLP phase 3: make tenant ownership mandatory and enforce same-tenant
-- relationships without changing any legacy primary keys.
ALTER TABLE identity.company
ALTER COLUMN tenant_id SET NOT NULL;
ALTER TABLE identity.company_branch
ALTER COLUMN tenant_id SET NOT NULL;
ALTER TABLE identity.user_group
ALTER COLUMN tenant_id SET NOT NULL;
ALTER TABLE identity.app_user
ALTER COLUMN tenant_id SET NOT NULL;
ALTER TABLE identity.permission
ALTER COLUMN tenant_id SET NOT NULL;
ALTER TABLE identity.denied_pages
ALTER COLUMN tenant_id SET NOT NULL;
ALTER TABLE identity.user_loginhistory
ALTER COLUMN tenant_id SET NOT NULL;
CREATE UNIQUE INDEX IF NOT EXISTS uq_identity_company_tenant_id
ON identity.company (tenant_id, company_id);
CREATE UNIQUE INDEX IF NOT EXISTS uq_identity_branch_tenant_id
ON identity.company_branch (tenant_id, branch_id, company_id);
CREATE UNIQUE INDEX IF NOT EXISTS uq_identity_group_tenant_id
ON identity.user_group (tenant_id, group_id);
CREATE UNIQUE INDEX IF NOT EXISTS uq_identity_user_tenant_id
ON identity.app_user (tenant_id, user_id);
DO $migration$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'fk_identity_company_tenant'
AND conrelid = 'identity.company'::regclass) THEN
ALTER TABLE identity.company
ADD CONSTRAINT fk_identity_company_tenant
FOREIGN KEY (tenant_id)
REFERENCES identity.client_account (tenant_id)
NOT VALID;
END IF;
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'fk_identity_branch_tenant'
AND conrelid = 'identity.company_branch'::regclass) THEN
ALTER TABLE identity.company_branch
ADD CONSTRAINT fk_identity_branch_tenant
FOREIGN KEY (tenant_id)
REFERENCES identity.client_account (tenant_id)
NOT VALID;
END IF;
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'fk_identity_branch_company_tenant'
AND conrelid = 'identity.company_branch'::regclass) THEN
ALTER TABLE identity.company_branch
ADD CONSTRAINT fk_identity_branch_company_tenant
FOREIGN KEY (tenant_id, company_id)
REFERENCES identity.company (tenant_id, company_id)
NOT VALID;
END IF;
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'fk_identity_group_tenant'
AND conrelid = 'identity.user_group'::regclass) THEN
ALTER TABLE identity.user_group
ADD CONSTRAINT fk_identity_group_tenant
FOREIGN KEY (tenant_id)
REFERENCES identity.client_account (tenant_id)
NOT VALID;
END IF;
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'fk_identity_user_tenant'
AND conrelid = 'identity.app_user'::regclass) THEN
ALTER TABLE identity.app_user
ADD CONSTRAINT fk_identity_user_tenant
FOREIGN KEY (tenant_id)
REFERENCES identity.client_account (tenant_id)
NOT VALID;
END IF;
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'fk_identity_user_group_tenant'
AND conrelid = 'identity.app_user'::regclass) THEN
ALTER TABLE identity.app_user
ADD CONSTRAINT fk_identity_user_group_tenant
FOREIGN KEY (tenant_id, group_id)
REFERENCES identity.user_group (tenant_id, group_id)
NOT VALID;
END IF;
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'fk_identity_user_company_tenant'
AND conrelid = 'identity.app_user'::regclass) THEN
ALTER TABLE identity.app_user
ADD CONSTRAINT fk_identity_user_company_tenant
FOREIGN KEY (tenant_id, company_id)
REFERENCES identity.company (tenant_id, company_id)
NOT VALID;
END IF;
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'fk_identity_user_branch_tenant'
AND conrelid = 'identity.app_user'::regclass) THEN
ALTER TABLE identity.app_user
ADD CONSTRAINT fk_identity_user_branch_tenant
FOREIGN KEY (tenant_id, branch_id, company_id)
REFERENCES identity.company_branch (tenant_id, branch_id, company_id)
NOT VALID;
END IF;
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'fk_identity_permission_tenant'
AND conrelid = 'identity.permission'::regclass) THEN
ALTER TABLE identity.permission
ADD CONSTRAINT fk_identity_permission_tenant
FOREIGN KEY (tenant_id)
REFERENCES identity.client_account (tenant_id)
NOT VALID;
END IF;
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'fk_identity_permission_group_tenant'
AND conrelid = 'identity.permission'::regclass) THEN
ALTER TABLE identity.permission
ADD CONSTRAINT fk_identity_permission_group_tenant
FOREIGN KEY (tenant_id, group_id)
REFERENCES identity.user_group (tenant_id, group_id)
NOT VALID;
END IF;
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'fk_identity_permission_page'
AND conrelid = 'identity.permission'::regclass) THEN
ALTER TABLE identity.permission
ADD CONSTRAINT fk_identity_permission_page
FOREIGN KEY (page_id)
REFERENCES identity.pages (page_id)
NOT VALID;
END IF;
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'fk_identity_denied_tenant'
AND conrelid = 'identity.denied_pages'::regclass) THEN
ALTER TABLE identity.denied_pages
ADD CONSTRAINT fk_identity_denied_tenant
FOREIGN KEY (tenant_id)
REFERENCES identity.client_account (tenant_id)
NOT VALID;
END IF;
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'fk_identity_denied_user_tenant'
AND conrelid = 'identity.denied_pages'::regclass) THEN
ALTER TABLE identity.denied_pages
ADD CONSTRAINT fk_identity_denied_user_tenant
FOREIGN KEY (tenant_id, user_id)
REFERENCES identity.app_user (tenant_id, user_id)
NOT VALID;
END IF;
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'fk_identity_denied_page'
AND conrelid = 'identity.denied_pages'::regclass) THEN
ALTER TABLE identity.denied_pages
ADD CONSTRAINT fk_identity_denied_page
FOREIGN KEY (page_id)
REFERENCES identity.pages (page_id)
NOT VALID;
END IF;
IF NOT EXISTS (
SELECT 1 FROM pg_constraint
WHERE conname = 'fk_identity_login_history_tenant'
AND conrelid = 'identity.user_loginhistory'::regclass) THEN
ALTER TABLE identity.user_loginhistory
ADD CONSTRAINT fk_identity_login_history_tenant
FOREIGN KEY (tenant_id)
REFERENCES identity.client_account (tenant_id)
NOT VALID;
END IF;
END
$migration$;
ALTER TABLE identity.company
VALIDATE CONSTRAINT fk_identity_company_tenant;
ALTER TABLE identity.company_branch
VALIDATE CONSTRAINT fk_identity_branch_tenant;
ALTER TABLE identity.company_branch
VALIDATE CONSTRAINT fk_identity_branch_company_tenant;
ALTER TABLE identity.user_group
VALIDATE CONSTRAINT fk_identity_group_tenant;
ALTER TABLE identity.app_user
VALIDATE CONSTRAINT fk_identity_user_tenant;
ALTER TABLE identity.app_user
VALIDATE CONSTRAINT fk_identity_user_group_tenant;
ALTER TABLE identity.app_user
VALIDATE CONSTRAINT fk_identity_user_company_tenant;
ALTER TABLE identity.app_user
VALIDATE CONSTRAINT fk_identity_user_branch_tenant;
ALTER TABLE identity.permission
VALIDATE CONSTRAINT fk_identity_permission_tenant;
ALTER TABLE identity.permission
VALIDATE CONSTRAINT fk_identity_permission_group_tenant;
ALTER TABLE identity.permission
VALIDATE CONSTRAINT fk_identity_permission_page;
ALTER TABLE identity.denied_pages
VALIDATE CONSTRAINT fk_identity_denied_tenant;
ALTER TABLE identity.denied_pages
VALIDATE CONSTRAINT fk_identity_denied_user_tenant;
ALTER TABLE identity.denied_pages
VALIDATE CONSTRAINT fk_identity_denied_page;
ALTER TABLE identity.user_loginhistory
VALIDATE CONSTRAINT fk_identity_login_history_tenant;
COMMIT;