Harden compliance portal data migrations
Mint certificate and domain GIDs with generate_gid, preserve org profile and domain rows that lack a trust center, and skip managed default-domain backfill when no base domain is set. Signed-off-by: Bryan Frimin <bryan@probo.com>
This commit is contained in:
@@ -18,6 +18,37 @@ ALTER TABLE custom_domains ALTER COLUMN managed DROP DEFAULT;
|
||||
ALTER TABLE trust_centers ADD COLUMN default_domain_id TEXT REFERENCES custom_domains(id) ON DELETE SET NULL;
|
||||
ALTER TABLE trust_centers ADD COLUMN custom_domain_id TEXT REFERENCES custom_domains(id) ON DELETE SET NULL;
|
||||
|
||||
INSERT INTO trust_centers (
|
||||
id,
|
||||
organization_id,
|
||||
tenant_id,
|
||||
active,
|
||||
slug,
|
||||
created_at,
|
||||
updated_at
|
||||
)
|
||||
SELECT
|
||||
generate_gid(decode_base64_unpadded(o.tenant_id), 22),
|
||||
o.id,
|
||||
o.tenant_id,
|
||||
false,
|
||||
LOWER(
|
||||
REGEXP_REPLACE(
|
||||
REGEXP_REPLACE(
|
||||
unaccent(o.name),
|
||||
'[^a-zA-Z0-9\s]', '', 'g'
|
||||
),
|
||||
'\s+', '-', 'g'
|
||||
)
|
||||
),
|
||||
NOW(),
|
||||
NOW()
|
||||
FROM organizations o
|
||||
WHERE o.custom_domain_id IS NOT NULL
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM trust_centers tc WHERE tc.organization_id = o.id
|
||||
);
|
||||
|
||||
UPDATE trust_centers tc
|
||||
SET custom_domain_id = o.custom_domain_id
|
||||
FROM organizations o
|
||||
|
||||
@@ -42,10 +42,9 @@ CREATE INDEX idx_certificates_http_challenge_token ON certificates(http_challeng
|
||||
WHERE http_challenge_token IS NOT NULL;
|
||||
|
||||
-- Backfill one certificate per existing custom domain. A fresh GID is minted
|
||||
-- for each row: the source domain's tenant (first 8 bytes of its GID) is kept,
|
||||
-- the entity type is set to 104 (CertificateEntityType), and a millisecond
|
||||
-- timestamp plus random suffix guarantee uniqueness. The certificate hostname
|
||||
-- equals the custom domain name, which lets us link the two afterwards.
|
||||
-- for each row with generate_gid (entity type 104 = CertificateEntityType).
|
||||
-- The certificate hostname equals the custom domain name, which lets us link
|
||||
-- the two afterwards.
|
||||
INSERT INTO certificates (
|
||||
id,
|
||||
tenant_id,
|
||||
@@ -66,17 +65,7 @@ INSERT INTO certificates (
|
||||
updated_at
|
||||
)
|
||||
SELECT
|
||||
translate(
|
||||
encode(
|
||||
substring(decode(translate(cd.id, '-_', '+/'), 'base64') FROM 1 FOR 8)
|
||||
|| int2send(104::smallint)
|
||||
|| int8send((floor(extract(epoch FROM clock_timestamp()) * 1000))::bigint)
|
||||
|| substring(decode(md5(random()::text || cd.id), 'hex') FROM 1 FOR 6),
|
||||
'base64'
|
||||
),
|
||||
'+/',
|
||||
'-_'
|
||||
),
|
||||
generate_gid(decode_base64_unpadded(cd.tenant_id), 104),
|
||||
cd.tenant_id,
|
||||
cd.domain,
|
||||
cd.ssl_certificate,
|
||||
@@ -100,7 +89,8 @@ ALTER TABLE custom_domains ADD COLUMN certificate_id TEXT REFERENCES certificate
|
||||
UPDATE custom_domains cd
|
||||
SET certificate_id = c.id
|
||||
FROM certificates c
|
||||
WHERE c.hostname = cd.domain;
|
||||
WHERE c.hostname = cd.domain
|
||||
AND c.tenant_id = cd.tenant_id;
|
||||
|
||||
-- Repoint the certificate cache from the custom domain to the certificate.
|
||||
ALTER TABLE cached_certificates ADD COLUMN certificate_id TEXT REFERENCES certificates(id) ON DELETE CASCADE;
|
||||
|
||||
@@ -18,6 +18,42 @@ ALTER TABLE trust_centers
|
||||
ADD COLUMN email CITEXT,
|
||||
ADD COLUMN headquarter_address TEXT;
|
||||
|
||||
INSERT INTO trust_centers (
|
||||
id,
|
||||
organization_id,
|
||||
tenant_id,
|
||||
active,
|
||||
slug,
|
||||
created_at,
|
||||
updated_at
|
||||
)
|
||||
SELECT
|
||||
generate_gid(decode_base64_unpadded(o.tenant_id), 22),
|
||||
o.id,
|
||||
o.tenant_id,
|
||||
false,
|
||||
LOWER(
|
||||
REGEXP_REPLACE(
|
||||
REGEXP_REPLACE(
|
||||
unaccent(o.name),
|
||||
'[^a-zA-Z0-9\s]', '', 'g'
|
||||
),
|
||||
'\s+', '-', 'g'
|
||||
)
|
||||
),
|
||||
NOW(),
|
||||
NOW()
|
||||
FROM organizations o
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM trust_centers tc WHERE tc.organization_id = o.id
|
||||
)
|
||||
AND (
|
||||
o.description IS NOT NULL
|
||||
OR o.website_url IS NOT NULL
|
||||
OR o.email IS NOT NULL
|
||||
OR o.headquarter_address IS NOT NULL
|
||||
);
|
||||
|
||||
UPDATE trust_centers tc
|
||||
SET
|
||||
description = o.description,
|
||||
|
||||
@@ -14,8 +14,9 @@
|
||||
|
||||
-- Backfill managed default domains for compliance pages created before
|
||||
-- default-domain provisioning existed. Hostnames use the managed base domain
|
||||
-- suffix configured on the database (probo.trust_center_base_domain), falling
|
||||
-- back to probopage.com for production installs.
|
||||
-- suffix configured on the database (probo.trust_center_base_domain). Self-
|
||||
-- managed installs without that setting are left alone — they do not get a
|
||||
-- SaaS default domain invented for them.
|
||||
|
||||
WITH pending_pages AS (
|
||||
SELECT
|
||||
@@ -24,14 +25,10 @@ WITH pending_pages AS (
|
||||
tc.organization_id,
|
||||
tc.slug,
|
||||
tc.created_at,
|
||||
(
|
||||
tc.slug || '.' || COALESCE(
|
||||
NULLIF(current_setting('probo.trust_center_base_domain', true), ''),
|
||||
'probopage.com'
|
||||
)
|
||||
)::citext AS hostname
|
||||
(tc.slug || '.' || current_setting('probo.trust_center_base_domain', true))::citext AS hostname
|
||||
FROM trust_centers tc
|
||||
WHERE tc.default_domain_id IS NULL
|
||||
AND NULLIF(current_setting('probo.trust_center_base_domain', true), '') IS NOT NULL
|
||||
),
|
||||
minted_certificates AS (
|
||||
INSERT INTO certificates (
|
||||
@@ -44,17 +41,7 @@ minted_certificates AS (
|
||||
updated_at
|
||||
)
|
||||
SELECT
|
||||
translate(
|
||||
encode(
|
||||
substring(decode(translate(pp.trust_center_id, '-_', '+/'), 'base64') FROM 1 FOR 8)
|
||||
|| int2send(104::smallint)
|
||||
|| int8send((floor(extract(epoch FROM clock_timestamp()) * 1000))::bigint)
|
||||
|| substring(decode(md5(random()::text || pp.trust_center_id), 'hex') FROM 1 FOR 6),
|
||||
'base64'
|
||||
),
|
||||
'+/',
|
||||
'-_'
|
||||
),
|
||||
generate_gid(decode_base64_unpadded(pp.tenant_id), 104),
|
||||
pp.tenant_id,
|
||||
pp.hostname,
|
||||
'PENDING'::custom_domain_ssl_status,
|
||||
@@ -76,17 +63,7 @@ minted_domains AS (
|
||||
updated_at
|
||||
)
|
||||
SELECT
|
||||
translate(
|
||||
encode(
|
||||
substring(decode(translate(mc.id, '-_', '+/'), 'base64') FROM 1 FOR 8)
|
||||
|| int2send(37::smallint)
|
||||
|| int8send((floor(extract(epoch FROM clock_timestamp()) * 1000))::bigint)
|
||||
|| substring(decode(md5(random()::text || mc.id), 'hex') FROM 1 FOR 6),
|
||||
'base64'
|
||||
),
|
||||
'+/',
|
||||
'-_'
|
||||
),
|
||||
generate_gid(decode_base64_unpadded(mc.tenant_id), 37),
|
||||
mc.tenant_id,
|
||||
pp.organization_id,
|
||||
pp.hostname,
|
||||
|
||||
@@ -12,6 +12,9 @@
|
||||
-- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
-- PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
-- page_title (later renamed to title) is a full page heading, not an org-name
|
||||
-- fragment for possessives like "{{name}}'s trust center". Seed from the org
|
||||
-- name so existing pages keep a sensible default until editors customize it.
|
||||
ALTER TABLE trust_centers
|
||||
ADD COLUMN page_title TEXT;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user