001-identity-init.xml 5.79 KB
<?xml version="1.0" encoding="UTF-8"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog
                                       https://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.27.xsd">

    <!--
        pbc-identity initial schema.

        Owns: identity__user, identity__role, identity__user_role.

        Conventions enforced for every business table in vibe_erp:
          • UUID primary key
          • tenant_id varchar(64) NOT NULL
          • Audit columns: created_at, created_by, updated_at, updated_by
          • Optimistic-locking version column
          • ext jsonb NOT NULL DEFAULT '{}' for key-user custom fields
          • GIN index on ext for fast custom-field queries
          • Composite index on (tenant_id, ...) for tenant-scoped lookups
          • Postgres Row-Level Security enabled, with a simple per-tenant
            policy bound to the GUC `vibeerp.current_tenant` set by the
            platform on every transaction (see TODO below)

        TODO(v0.2): the platform's `RlsTransactionHook` is not yet
        implemented, so the RLS policy in this file uses
        `current_setting('vibeerp.current_tenant', true)` and is enabled
        but not yet enforced (NO FORCE). When the hook lands, change to
        FORCE ROW LEVEL SECURITY in a follow-up changeset.
    -->

    <changeSet id="identity-init-001" author="vibe_erp">
        <comment>Create identity__user table</comment>
        <sql>
            CREATE TABLE identity__user (
                id           uuid PRIMARY KEY,
                tenant_id    varchar(64)  NOT NULL,
                username     varchar(128) NOT NULL,
                display_name varchar(256) NOT NULL,
                email        varchar(320),
                enabled      boolean      NOT NULL DEFAULT true,
                ext          jsonb        NOT NULL DEFAULT '{}'::jsonb,
                created_at   timestamptz  NOT NULL,
                created_by   varchar(128) NOT NULL,
                updated_at   timestamptz  NOT NULL,
                updated_by   varchar(128) NOT NULL,
                version      bigint       NOT NULL DEFAULT 0
            );
            CREATE UNIQUE INDEX identity__user_tenant_username_uk
                ON identity__user (tenant_id, username);
            CREATE INDEX identity__user_ext_gin
                ON identity__user USING GIN (ext jsonb_path_ops);
        </sql>
        <rollback>
            DROP TABLE identity__user;
        </rollback>
    </changeSet>

    <changeSet id="identity-init-002" author="vibe_erp">
        <comment>Enable Row-Level Security on identity__user (advisory until RlsTransactionHook lands)</comment>
        <sql>
            ALTER TABLE identity__user ENABLE ROW LEVEL SECURITY;
            CREATE POLICY identity__user_tenant_isolation ON identity__user
                USING (tenant_id = current_setting('vibeerp.current_tenant', true));
        </sql>
        <rollback>
            DROP POLICY IF EXISTS identity__user_tenant_isolation ON identity__user;
            ALTER TABLE identity__user DISABLE ROW LEVEL SECURITY;
        </rollback>
    </changeSet>

    <changeSet id="identity-init-003" author="vibe_erp">
        <comment>Create identity__role table</comment>
        <sql>
            CREATE TABLE identity__role (
                id           uuid PRIMARY KEY,
                tenant_id    varchar(64)  NOT NULL,
                code         varchar(64)  NOT NULL,
                name         varchar(256) NOT NULL,
                description  text,
                ext          jsonb        NOT NULL DEFAULT '{}'::jsonb,
                created_at   timestamptz  NOT NULL,
                created_by   varchar(128) NOT NULL,
                updated_at   timestamptz  NOT NULL,
                updated_by   varchar(128) NOT NULL,
                version      bigint       NOT NULL DEFAULT 0
            );
            CREATE UNIQUE INDEX identity__role_tenant_code_uk
                ON identity__role (tenant_id, code);
            CREATE INDEX identity__role_ext_gin
                ON identity__role USING GIN (ext jsonb_path_ops);
            ALTER TABLE identity__role ENABLE ROW LEVEL SECURITY;
            CREATE POLICY identity__role_tenant_isolation ON identity__role
                USING (tenant_id = current_setting('vibeerp.current_tenant', true));
        </sql>
        <rollback>
            DROP TABLE identity__role;
        </rollback>
    </changeSet>

    <changeSet id="identity-init-004" author="vibe_erp">
        <comment>Create identity__user_role join table</comment>
        <sql>
            CREATE TABLE identity__user_role (
                id           uuid PRIMARY KEY,
                tenant_id    varchar(64)  NOT NULL,
                user_id      uuid         NOT NULL REFERENCES identity__user(id) ON DELETE CASCADE,
                role_id      uuid         NOT NULL REFERENCES identity__role(id) ON DELETE CASCADE,
                created_at   timestamptz  NOT NULL,
                created_by   varchar(128) NOT NULL,
                updated_at   timestamptz  NOT NULL,
                updated_by   varchar(128) NOT NULL,
                version      bigint       NOT NULL DEFAULT 0
            );
            CREATE UNIQUE INDEX identity__user_role_uk
                ON identity__user_role (tenant_id, user_id, role_id);
            ALTER TABLE identity__user_role ENABLE ROW LEVEL SECURITY;
            CREATE POLICY identity__user_role_tenant_isolation ON identity__user_role
                USING (tenant_id = current_setting('vibeerp.current_tenant', true));
        </sql>
        <rollback>
            DROP TABLE identity__user_role;
        </rollback>
    </changeSet>

</databaseChangeLog>