diff --git a/packages/schemas/alterations/next-1731904029-add-saml-application-configs-table.ts b/packages/schemas/alterations/next-1731904029-add-saml-application-configs-table.ts new file mode 100644 index 000000000..ac9fdd137 --- /dev/null +++ b/packages/schemas/alterations/next-1731904029-add-saml-application-configs-table.ts @@ -0,0 +1,33 @@ +import { sql } from '@silverhand/slonik'; + +import type { AlterationScript } from '../lib/types/alteration.js'; + +import { applyTableRls, dropTableRls } from './utils/1704934999-tables.js'; + +const alteration: AlterationScript = { + up: async (pool) => { + await pool.query(sql` + create table saml_application_configs ( + application_id varchar(21) not null + references applications (id) on update cascade on delete cascade, + tenant_id varchar(21) not null + references tenants (id) on update cascade on delete cascade, + attribute_mapping jsonb /* @use SamlAttributeMapping */ not null default '{}'::jsonb, + entity_id varchar(128), + acs_url jsonb /* @use SamlAcsUrl */, + primary key (tenant_id, application_id), + constraint application_type + check (check_application_type(application_id, 'SAML')) + ); + `); + await applyTableRls(pool, 'saml_application_configs'); + }, + down: async (pool) => { + await dropTableRls(pool, 'saml_application_configs'); + await pool.query(sql` + drop table saml_application_configs; + `); + }, +}; + +export default alteration; diff --git a/packages/schemas/src/foundations/jsonb-types/index.ts b/packages/schemas/src/foundations/jsonb-types/index.ts index 4cbc95dc9..3c8512083 100644 --- a/packages/schemas/src/foundations/jsonb-types/index.ts +++ b/packages/schemas/src/foundations/jsonb-types/index.ts @@ -10,6 +10,7 @@ export * from './sso-connector.js'; export * from './applications.js'; export * from './verification-records.js'; export * from './account-centers.js'; +export * from './saml-application-configs.js'; export { configurableConnectorMetadataGuard, diff --git a/packages/schemas/src/foundations/jsonb-types/saml-application-configs.ts b/packages/schemas/src/foundations/jsonb-types/saml-application-configs.ts new file mode 100644 index 000000000..db985ac0e --- /dev/null +++ b/packages/schemas/src/foundations/jsonb-types/saml-application-configs.ts @@ -0,0 +1,23 @@ +import { type ToZodObject } from '@logto/connector-kit'; +import { z } from 'zod'; + +export type SamlAttributeMapping = Record; + +export const samlAttributeMappingGuard = z.record( + z.string() +) satisfies z.ZodType; + +export enum BindingType { + POST = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST', + REDIRECT = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect', +} + +export type SamlAcsUrl = { + binding?: BindingType; + url: string; +}; + +export const samlAcsUrlGuard = z.object({ + binding: z.nativeEnum(BindingType), + url: z.string(), +}) satisfies ToZodObject; diff --git a/packages/schemas/tables/saml_application_configs.sql b/packages/schemas/tables/saml_application_configs.sql new file mode 100644 index 000000000..7ff2a859b --- /dev/null +++ b/packages/schemas/tables/saml_application_configs.sql @@ -0,0 +1,19 @@ +/* init_order = 2 */ + +/** + * The SAML application config and SAML-type application have a one-to-one correspondence: + * - a SAML-type application can only have one SAML application config + * - a SAML application config can only configure one SAML-type application + */ +create table saml_application_configs ( + application_id varchar(21) not null + references applications (id) on update cascade on delete cascade, + tenant_id varchar(21) not null + references tenants (id) on update cascade on delete cascade, + attribute_mapping jsonb /* @use SamlAttributeMapping */ not null default '{}'::jsonb, + entity_id varchar(128), + acs_url jsonb /* @use SamlAcsUrl */, + primary key (tenant_id, application_id), + constraint application_type + check (check_application_type(application_id, 'SAML')) +);