From 79eeb3508ca8df2bc726090b9aab8ae887f18ebb Mon Sep 17 00:00:00 2001 From: Darcy Ye Date: Thu, 28 Nov 2024 12:52:52 +0800 Subject: [PATCH] chore: fix code --- .../routes/index.openapi.json | 273 ------------------ .../src/saml-applications/routes/index.ts | 20 +- .../src/api/saml-application.ts | 3 + .../api/application/saml-application.test.ts | 29 +- .../schemas/src/types/saml-application.ts | 2 + 5 files changed, 47 insertions(+), 280 deletions(-) delete mode 100644 packages/core/src/saml-applications/routes/index.openapi.json diff --git a/packages/core/src/saml-applications/routes/index.openapi.json b/packages/core/src/saml-applications/routes/index.openapi.json deleted file mode 100644 index 9c4f7bcf3..000000000 --- a/packages/core/src/saml-applications/routes/index.openapi.json +++ /dev/null @@ -1,273 +0,0 @@ -{ - "tags": [ - { - "name": "SAML applications", - "description": "SAML applications enable Single Sign-On (SSO) integration between Logto (acting as Identity Provider/IdP) and third-party Service Providers (SP) using the SAML 2.0 protocol. These endpoints allow you to manage SAML application configurations." - }, - { - "name": "Dev feature" - } - ], - "paths": { - "/api/saml-applications": { - "post": { - "summary": "Create SAML application", - "description": "Create a new SAML application with the given configuration. This will create both the application entity and its SAML-specific configurations.", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "properties": { - "name": { - "type": "string", - "description": "The name of the SAML application." - }, - "description": { - "type": "string", - "description": "The description of the SAML application." - }, - "customData": { - "type": "object", - "description": "Custom data for the application." - }, - "config": { - "type": "object", - "properties": { - "attributeMapping": { - "type": "object", - "description": "Mapping of SAML attributes to Logto user properties." - }, - "entityId": { - "type": "string", - "description": "Service provider's entityId." - }, - "acsUrl": { - "type": "object", - "description": "Service provider assertion consumer service URL configuration." - } - } - } - } - } - } - } - }, - "responses": { - "201": { - "description": "The SAML application was created successfully." - }, - "400": { - "description": "Invalid request body or SAML configuration." - } - } - } - }, - "/api/saml-applications/{id}": { - "get": { - "summary": "Get SAML application", - "description": "Get a SAML application by ID. This will return both the application entity and its SAML-specific configurations.", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The ID of the SAML application to retrieve." - } - ], - "responses": { - "200": { - "description": "The SAML application was retrieved successfully." - }, - "400": { - "description": "Invalid application ID, the application is not a SAML application." - }, - "404": { - "description": "The SAML application was not found." - } - } - }, - "patch": { - "summary": "Update SAML application", - "description": "Update a SAML application by ID. This will update both the application entity and its SAML-specific configurations.", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The ID of the SAML application to update." - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "properties": { - "name": { - "type": "string", - "description": "The name of the SAML application." - }, - "description": { - "type": "string", - "description": "The description of the SAML application." - }, - "customData": { - "type": "object", - "description": "Custom data for the application." - }, - "config": { - "type": "object", - "properties": { - "attributeMapping": { - "type": "object", - "description": "Mapping of SAML attributes to Logto user properties." - }, - "entityId": { - "type": "string", - "description": "Service provider's entityId." - }, - "acsUrl": { - "type": "object", - "description": "Service provider assertion consumer service URL configuration." - } - } - } - } - } - } - } - }, - "responses": { - "200": { - "description": "The SAML application was updated successfully." - }, - "400": { - "description": "Invalid application ID or request body." - }, - "404": { - "description": "The SAML application was not found." - }, - "422": { - "description": "Invalid SAML configuration." - } - } - }, - "delete": { - "summary": "Delete SAML application", - "description": "Delete a SAML application by ID. This will remove both the application entity and its SAML-specific configurations.", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The ID of the SAML application to delete." - } - ], - "responses": { - "204": { - "description": "The SAML application was deleted successfully." - }, - "400": { - "description": "Invalid application ID, the application is not a SAML application." - }, - "404": { - "description": "The SAML application was not found." - } - } - } - }, - "/api/saml-applications/{id}/secrets": { - "post": { - "summary": "Create SAML application secret", - "description": "Create a new secret for the specified SAML application.", - "parameters": [], - "requestBody": { - "required": false, - "content": { - "application/json": { - "schema": { - "properties": { - "lifeSpanInDays": { - "type": "number", - "description": "The lifespan of the secret in days." - } - } - } - } - } - }, - "responses": { - "201": { - "description": "The SAML application secret was created successfully." - }, - "400": { - "description": "Invalid application ID or request body." - }, - "404": { - "description": "The SAML application was not found." - } - } - } - }, - "/api/saml-applications/{id}/secrets/{secretId}": { - "delete": { - "summary": "Delete SAML application secret", - "description": "Delete a secret from the specified SAML application. Active secrets cannot be deleted.", - "parameters": [], - "responses": { - "204": { - "description": "The SAML application secret was deleted successfully." - }, - "400": { - "description": "Invalid application ID or secret ID, or attempting to delete an active secret." - }, - "404": { - "description": "The SAML application or secret was not found." - } - } - }, - "patch": { - "summary": "Update SAML application secret status", - "description": "Update the active status of a SAML application secret.", - "parameters": [], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "properties": { - "active": { - "required": true, - "type": "boolean", - "description": "The new active status of the secret." - } - } - } - } - } - }, - "responses": { - "200": { - "description": "The SAML application secret was updated successfully." - }, - "400": { - "description": "Invalid application ID, secret ID or request body." - }, - "404": { - "description": "The SAML application or secret was not found." - } - } - } - } - } -} diff --git a/packages/core/src/saml-applications/routes/index.ts b/packages/core/src/saml-applications/routes/index.ts index 24e6b033b..1d7e0366c 100644 --- a/packages/core/src/saml-applications/routes/index.ts +++ b/packages/core/src/saml-applications/routes/index.ts @@ -27,6 +27,7 @@ export default function samlApplicationRoutes( samlApplicationConfigs: { insertSamlApplicationConfig }, samlApplicationSecrets: { deleteSamlApplicationSecretById, + findSamlApplicationSecretsByApplicationId, findSamlApplicationSecretByApplicationIdAndId, updateSamlApplicationSecretStatusByApplicationIdAndSecretId, }, @@ -168,7 +169,24 @@ export default function samlApplicationRoutes( } = ctx.guard; ctx.status = 201; - ctx.body = await createNewSamlApplicationSecretForApplication(id, lifeSpanInDays); + ctx.body = await createSamlApplicationSecret(id, lifeSpanInDays); + + return next(); + } + ); + + router.get( + '/saml-applications/:id/secrets', + koaGuard({ + params: z.object({ id: z.string() }), + response: samlApplicationSecretResponseGuard.array(), + status: [200, 400, 404], + }), + async (ctx, next) => { + const { id } = ctx.guard.params; + + ctx.status = 200; + ctx.body = await findSamlApplicationSecretsByApplicationId(id); return next(); } diff --git a/packages/integration-tests/src/api/saml-application.ts b/packages/integration-tests/src/api/saml-application.ts index 8d83a871c..33e1b9a6e 100644 --- a/packages/integration-tests/src/api/saml-application.ts +++ b/packages/integration-tests/src/api/saml-application.ts @@ -33,6 +33,9 @@ export const createSamlApplicationSecret = async (id: string, lifeSpanInDays: nu .post(`saml-applications/${id}/secrets`, { json: { lifeSpanInDays } }) .json(); +export const getSamlApplicationSecrets = async (id: string) => + authedAdminApi.get(`saml-applications/${id}/secrets`).json(); + export const deleteSamlApplicationSecret = async (id: string, secretId: string) => authedAdminApi.delete(`saml-applications/${id}/secrets/${secretId}`); diff --git a/packages/integration-tests/src/tests/api/application/saml-application.test.ts b/packages/integration-tests/src/tests/api/application/saml-application.test.ts index 2fd7c8472..23eead64d 100644 --- a/packages/integration-tests/src/tests/api/application/saml-application.test.ts +++ b/packages/integration-tests/src/tests/api/application/saml-application.test.ts @@ -9,6 +9,7 @@ import { deleteSamlApplicationSecret, createSamlApplicationSecret, updateSamlApplicationSecret, + getSamlApplicationSecrets, } from '#src/api/saml-application.js'; import { expectRejects } from '#src/helpers/index.js'; import { devFeatureTest } from '#src/utils.js'; @@ -114,11 +115,17 @@ describe('SAML application', () => { }); const createdSecret = await createSamlApplicationSecret(id, 30); - const samlApplication = await getSamlApplication(id); - expect(samlApplication.secrets.length).toBe(2); + + // @ts-expect-error - Make sure the `privateKey` is not exposed in the response. + expect(createdSecret.privateKey).toBeUndefined(); + + const samlApplicationSecrets = await getSamlApplicationSecrets(id); + expect(samlApplicationSecrets.length).toBe(2); expect( - samlApplication.secrets.find((secret) => secret.id === createdSecret.id) + samlApplicationSecrets.find((secret) => secret.id === createdSecret.id) ).not.toBeUndefined(); + // @ts-expect-error - Make sure the `privateKey` is not exposed in the response. + expect(samlApplicationSecrets.every((secret) => secret.privateKey === undefined)).toBe(true); await deleteSamlApplicationSecret(id, createdSecret.id); @@ -132,14 +139,23 @@ describe('SAML application', () => { }); const createdSecret = await createSamlApplicationSecret(id, 30); - const samlApplication = await getSamlApplication(id); - expect(samlApplication.secrets.length).toBe(2); + + // @ts-expect-error - Make sure the `privateKey` is not exposed in the response. + expect(createdSecret.privateKey).toBeUndefined(); + + const samlApplicationSecrets = await getSamlApplicationSecrets(id); + expect(samlApplicationSecrets.length).toBe(2); expect( - samlApplication.secrets.find((secret) => secret.id === createdSecret.id) + samlApplicationSecrets.find((secret) => secret.id === createdSecret.id) ).not.toBeUndefined(); + // @ts-expect-error - Make sure the `privateKey` is not exposed in the response. + expect(samlApplicationSecrets.every((secret) => secret.privateKey === undefined)).toBe(true); + const updatedSecret = await updateSamlApplicationSecret(id, createdSecret.id, true); expect(updatedSecret.active).toBe(true); + // @ts-expect-error - Make sure the `privateKey` is not exposed in the response. + expect(updatedSecret.privateKey).toBeUndefined(); await expectRejects(deleteSamlApplicationSecret(id, createdSecret.id), { code: 'application.saml.can_not_delete_active_secret', @@ -147,6 +163,7 @@ describe('SAML application', () => { }); await updateSamlApplicationSecret(id, createdSecret.id, false); + await deleteSamlApplicationSecret(id, createdSecret.id); await deleteSamlApplication(id); }); }); diff --git a/packages/schemas/src/types/saml-application.ts b/packages/schemas/src/types/saml-application.ts index e78058bbe..a098541be 100644 --- a/packages/schemas/src/types/saml-application.ts +++ b/packages/schemas/src/types/saml-application.ts @@ -2,6 +2,7 @@ import { type z } from 'zod'; import { Applications } from '../db-entries/application.js'; import { SamlApplicationConfigs } from '../db-entries/saml-application-config.js'; +import { SamlApplicationSecrets } from '../db-entries/saml-application-secret.js'; import { applicationCreateGuard, applicationPatchGuard } from './application.js'; @@ -37,6 +38,7 @@ export const samlApplicationPatchGuard = applicationPatchGuard export type PatchSamlApplication = z.infer; +// Make sure the `privateKey` is not exposed in the response. export const samlApplicationSecretResponseGuard = SamlApplicationSecrets.guard.omit({ tenantId: true, applicationId: true,