mirror of
https://github.com/logto-io/logto.git
synced 2025-04-07 23:01:25 -05:00
Merge pull request #6881 from logto-io/yemq-add-saml-app-session-table
feat: add saml app sessions table
This commit is contained in:
commit
659ec5c298
6 changed files with 162 additions and 0 deletions
77
packages/core/src/saml-applications/queries/sessions.ts
Normal file
77
packages/core/src/saml-applications/queries/sessions.ts
Normal file
|
@ -0,0 +1,77 @@
|
|||
import { type SamlApplicationSession, SamlApplicationSessions } from '@logto/schemas';
|
||||
import type { CommonQueryMethods } from '@silverhand/slonik';
|
||||
import { sql } from '@silverhand/slonik';
|
||||
|
||||
import { buildInsertIntoWithPool } from '#src/database/insert-into.js';
|
||||
import { buildUpdateWhereWithPool } from '#src/database/update-where.js';
|
||||
import { DeletionError } from '#src/errors/SlonikError/index.js';
|
||||
import { convertToIdentifiers } from '#src/utils/sql.js';
|
||||
|
||||
const { table, fields } = convertToIdentifiers(SamlApplicationSessions);
|
||||
|
||||
export const createSamlApplicationSessionQueries = (pool: CommonQueryMethods) => {
|
||||
const insertSession = buildInsertIntoWithPool(pool)(SamlApplicationSessions, {
|
||||
returning: true,
|
||||
});
|
||||
|
||||
const updateSession = buildUpdateWhereWithPool(pool)(SamlApplicationSessions, true);
|
||||
|
||||
/**
|
||||
* Removes the OIDC state from a session, which marks OIDC state as consumed.
|
||||
*
|
||||
* @param id The ID of the session.
|
||||
* @returns The updated session.
|
||||
*/
|
||||
const removeSessionOidcStateById = async (id: string) =>
|
||||
updateSession({
|
||||
set: { oidcState: null },
|
||||
where: { id },
|
||||
jsonbMode: 'merge',
|
||||
});
|
||||
|
||||
const deleteExpiredSessions = async () => {
|
||||
const { rowCount } = await pool.query(sql`
|
||||
delete from ${table}
|
||||
where ${fields.expiresAt} < now()
|
||||
`);
|
||||
|
||||
if (rowCount < 1) {
|
||||
throw new DeletionError(SamlApplicationSessions.table);
|
||||
}
|
||||
};
|
||||
|
||||
const deleteSessionById = async (id: string) => {
|
||||
const { rowCount } = await pool.query(sql`
|
||||
delete from ${table}
|
||||
where ${fields.id} = ${id}
|
||||
`);
|
||||
|
||||
if (rowCount < 1) {
|
||||
throw new DeletionError(SamlApplicationSessions.table);
|
||||
}
|
||||
};
|
||||
|
||||
const findSessionById = async (id: string) =>
|
||||
pool.maybeOne<SamlApplicationSession>(sql`
|
||||
select ${sql.join(Object.values(fields), sql`, `)}
|
||||
from ${table}
|
||||
where ${fields.id}=${id}
|
||||
`);
|
||||
|
||||
const findSessionsByApplicationId = async (applicationId: string) =>
|
||||
pool.any<SamlApplicationSession>(sql`
|
||||
select ${sql.join(Object.values(fields), sql`, `)}
|
||||
from ${table}
|
||||
where ${fields.applicationId}=${applicationId}
|
||||
`);
|
||||
|
||||
return {
|
||||
insertSession,
|
||||
updateSession,
|
||||
removeSessionOidcStateById,
|
||||
deleteExpiredSessions,
|
||||
deleteSessionById,
|
||||
findSessionById,
|
||||
findSessionsByApplicationId,
|
||||
};
|
||||
};
|
|
@ -30,6 +30,7 @@ import { createUsersRolesQueries } from '#src/queries/users-roles.js';
|
|||
import { createVerificationStatusQueries } from '#src/queries/verification-status.js';
|
||||
import { createSamlApplicationConfigQueries } from '#src/saml-applications/queries/configs.js';
|
||||
import { createSamlApplicationSecretsQueries } from '#src/saml-applications/queries/secrets.js';
|
||||
import { createSamlApplicationSessionQueries } from '#src/saml-applications/queries/sessions.js';
|
||||
|
||||
import { AccountCenterQueries } from '../queries/account-center.js';
|
||||
import { PersonalAccessTokensQueries } from '../queries/personal-access-tokens.js';
|
||||
|
@ -64,6 +65,7 @@ export default class Queries {
|
|||
subjectTokens = createSubjectTokenQueries(this.pool);
|
||||
samlApplicationSecrets = createSamlApplicationSecretsQueries(this.pool);
|
||||
samlApplicationConfigs = createSamlApplicationConfigQueries(this.pool);
|
||||
samlApplicationSessions = createSamlApplicationSessionQueries(this.pool);
|
||||
personalAccessTokens = new PersonalAccessTokensQueries(this.pool);
|
||||
verificationRecords = new VerificationRecordQueries(this.pool);
|
||||
accountCenters = new AccountCenterQueries(this.pool);
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
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_sessions (
|
||||
tenant_id varchar(21) not null
|
||||
references tenants (id) on update cascade on delete cascade,
|
||||
id varchar(32) not null,
|
||||
application_id varchar(21) not null
|
||||
references applications (id) on update cascade on delete cascade,
|
||||
saml_request_id varchar(128) not null,
|
||||
oidc_state varchar(32),
|
||||
relay_state varchar(256),
|
||||
raw_auth_request text not null,
|
||||
created_at timestamptz not null default(now()),
|
||||
expires_at timestamptz not null,
|
||||
primary key (tenant_id, id),
|
||||
constraint saml_application_sessions__application_type
|
||||
check (check_application_type(application_id, 'SAML'))
|
||||
);
|
||||
`);
|
||||
await applyTableRls(pool, 'saml_application_sessions');
|
||||
},
|
||||
down: async (pool) => {
|
||||
await dropTableRls(pool, 'saml_application_sessions');
|
||||
await pool.query(sql`
|
||||
drop table if exists saml_application_sessions;
|
||||
`);
|
||||
},
|
||||
};
|
||||
|
||||
export default alteration;
|
|
@ -11,6 +11,7 @@ export * from './applications.js';
|
|||
export * from './verification-records.js';
|
||||
export * from './account-centers.js';
|
||||
export * from './saml-application-configs.js';
|
||||
export * from './saml-application-sessions.js';
|
||||
|
||||
export {
|
||||
configurableConnectorMetadataGuard,
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
import { type ToZodObject } from '@logto/connector-kit';
|
||||
import { z } from 'zod';
|
||||
|
||||
export type AuthRequestInfo = {
|
||||
issuer: string;
|
||||
request: {
|
||||
id: string;
|
||||
destination: string;
|
||||
issueInstant: string;
|
||||
assertionConsumerServiceUrl: string;
|
||||
};
|
||||
};
|
||||
|
||||
export const authRequestInfoGuard = z.object({
|
||||
issuer: z.string(),
|
||||
request: z.object({
|
||||
id: z.string(),
|
||||
destination: z.string(),
|
||||
issueInstant: z.string(),
|
||||
assertionConsumerServiceUrl: z.string(),
|
||||
}),
|
||||
}) satisfies ToZodObject<AuthRequestInfo>;
|
23
packages/schemas/tables/saml_application_sessions.sql
Normal file
23
packages/schemas/tables/saml_application_sessions.sql
Normal file
|
@ -0,0 +1,23 @@
|
|||
/* init_order = 2 */
|
||||
|
||||
create table saml_application_sessions (
|
||||
tenant_id varchar(21) not null
|
||||
references tenants (id) on update cascade on delete cascade,
|
||||
/** The globally unique identifier of the session. */
|
||||
id varchar(32) not null,
|
||||
application_id varchar(21) not null
|
||||
references applications (id) on update cascade on delete cascade,
|
||||
/** The identifier of the SAML SSO auth request ID, SAML request ID is pretty long. */
|
||||
saml_request_id varchar(128) not null,
|
||||
/** The identifier of the OIDC auth request state. */
|
||||
oidc_state varchar(32),
|
||||
/** The relay state of the SAML auth request. */
|
||||
relay_state varchar(256),
|
||||
/** The raw request of the SAML auth request. */
|
||||
raw_auth_request text not null,
|
||||
created_at timestamptz not null default(now()),
|
||||
expires_at timestamptz not null,
|
||||
primary key (tenant_id, id),
|
||||
constraint saml_application_sessions__application_type
|
||||
check (check_application_type(application_id, 'SAML'))
|
||||
);
|
Loading…
Add table
Reference in a new issue