-
-
Notifications
You must be signed in to change notification settings - Fork 466
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
138 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
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 { 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); | ||
|
||
const deleteExpiredOrFullyUsedSessions = async () => { | ||
const { rowCount } = await pool.query(sql` | ||
delete from ${table} | ||
where ${fields.expiresAt} < now() | ||
or (${fields.isSamlResponseSent} = true and ${fields.isOidcStateChecked} = true) | ||
`); | ||
|
||
return rowCount; | ||
}; | ||
|
||
const findSessionsByApplicationId = async (applicationId: string) => | ||
pool.any<SamlApplicationSession>(sql` | ||
select ${sql.join(Object.values(fields), sql`, `)} | ||
from ${table} | ||
where ${fields.applicationId}=${applicationId} | ||
`); | ||
|
||
const findAvailableSessionByAppIdAndState = async (applicationId: string, state: string) => | ||
pool.one<SamlApplicationSession>(sql` | ||
select ${sql.join(Object.values(fields), sql`, `)} | ||
from ${table} | ||
where ${fields.applicationId}=${applicationId} | ||
and ${fields.oidcState}=${state} and ${fields.isOidcStateChecked} = false and ${ | ||
fields.expiresAt | ||
} > now() | ||
`); | ||
|
||
const findAvailableSessionByAppIdAndSamlRequestId = async ( | ||
applicationId: string, | ||
samlRequestId: string | ||
) => | ||
pool.one<SamlApplicationSession>(sql` | ||
select ${sql.join(Object.values(fields), sql`, `)} | ||
from ${table} | ||
where ${fields.applicationId}=${applicationId} | ||
and ${fields.samlRequestId}=${samlRequestId} and ${fields.isSamlResponseSent} = false and ${ | ||
fields.expiresAt | ||
} > now() | ||
`); | ||
|
||
return { | ||
insertSession, | ||
updateSession, | ||
deleteExpiredOrFullyUsedSessions, | ||
findSessionsByApplicationId, | ||
findAvailableSessionByAppIdAndState, | ||
findAvailableSessionByAppIdAndSamlRequestId, | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
42 changes: 42 additions & 0 deletions
42
packages/schemas/alterations/next-1734059671-add-saml-application-sessions-table.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
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(21) not null, | ||
application_id varchar(21) not null | ||
references applications (id) on update cascade on delete cascade, | ||
saml_request_id varchar(21), | ||
oidc_state varchar(21), | ||
is_oidc_state_checked boolean not null default false, | ||
is_saml_response_sent boolean not null default false, | ||
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')) | ||
); | ||
create unique index saml_application_sessions__oidc_state | ||
on saml_application_sessions (tenant_id, oidc_state); | ||
create unique index saml_application_sessions__saml_request_id | ||
on saml_application_sessions (tenant_id, saml_request_id); | ||
`); | ||
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
/* 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(21) 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 varchar(21), | ||
/** The identifier of the OIDC auth request state. */ | ||
oidc_state varchar(21), | ||
/** When checking the OIDC auth state, we should have this flag to prevent replay attack. */ | ||
is_oidc_state_checked boolean not null default false, | ||
/** When sending the SAML authn response, we should have this flag to prevent replay attack. */ | ||
is_saml_response_sent boolean not null default false, | ||
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')) | ||
); | ||
|
||
create unique index saml_application_sessions__oidc_state | ||
on saml_application_sessions (tenant_id, oidc_state); | ||
create unique index saml_application_sessions__saml_request_id | ||
on saml_application_sessions (tenant_id, saml_request_id); |