Skip to content

Commit

Permalink
feat(core): undeploy worker scripts when jwt customizer is deleted (#…
Browse files Browse the repository at this point in the history
…5685)

undeloy work scripts when the jwt customizer is deleted
  • Loading branch information
simeng-li committed Apr 12, 2024
1 parent 9b3d4ef commit 543931a
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 15 deletions.
9 changes: 3 additions & 6 deletions packages/core/src/libraries/cloud-connection.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { GlobalValues } from '@logto/shared';
import { createMockUtils } from '@logto/shared/esm';
import nock from 'nock';

import { mockLogtoConfigsLibrary } from '#src/test-utils/mock-libraries.js';

import { type LogtoConfigLibrary } from './logto-config.js';

const { jest } = import.meta;
Expand All @@ -28,17 +30,12 @@ await mockEsmWithActual('#src/env-set/index.js', () => ({
const { createCloudConnectionLibrary } = await import('./cloud-connection.js');

const logtoConfigs: LogtoConfigLibrary = {
...mockLogtoConfigsLibrary,
getCloudConnectionData: jest.fn().mockResolvedValue({
appId: 'appId',
appSecret: 'appSecret',
resource: 'resource',
}),
getOidcConfigs: jest.fn(),
upsertJwtCustomizer: jest.fn(),
getJwtCustomizer: jest.fn(),
getJwtCustomizers: jest.fn(),
updateJwtCustomizer: jest.fn(),
deployJwtCustomizerScript: jest.fn(),
};

describe('getAccessToken()', () => {
Expand Down
32 changes: 32 additions & 0 deletions packages/core/src/libraries/logto-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
jwtCustomizerConfigGuard,
logtoOidcConfigGuard,
} from '@logto/schemas';
import { assert } from '@silverhand/essentials';
import chalk from 'chalk';
import { ZodError, z } from 'zod';

Expand Down Expand Up @@ -173,6 +174,36 @@ export const createLogtoConfigLibrary = ({
});
};

const undeployJwtCustomizerScript = async <T extends LogtoJwtTokenKey>(
cloudConnection: CloudConnectionLibrary,
key: T
) => {
const [client, jwtCustomizers] = await Promise.all([
cloudConnection.getClient(),
getJwtCustomizers(),
]);

assert(jwtCustomizers[key], new RequestError({ code: 'entity.not_exists', key }));

// Undeploy the worker directly if the only JWT customizer is being deleted.
if (Object.entries(jwtCustomizers).length === 1) {
await client.delete(`/api/services/custom-jwt/worker`);
return;
}

// Remove the JWT customizer script from the existing JWT customizer scripts and redeploy.
const customizerScriptsFromDatabase = getJwtCustomizerScripts(jwtCustomizers);

await client.put(`/api/services/custom-jwt/worker`, {
body: {
production: {
...customizerScriptsFromDatabase,
[key]: undefined,
},
},
});
};

return {
getOidcConfigs,
getCloudConnectionData,
Expand All @@ -181,5 +212,6 @@ export const createLogtoConfigLibrary = ({
getJwtCustomizers,
updateJwtCustomizer,
deployJwtCustomizerScript,
undeployJwtCustomizerScript,
};
};
8 changes: 2 additions & 6 deletions packages/core/src/libraries/sign-in-experience/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
} from '#src/__mocks__/index.js';
import RequestError from '#src/errors/RequestError/index.js';
import { ssoConnectorFactories } from '#src/sso/index.js';
import { mockLogtoConfigsLibrary } from '#src/test-utils/mock-libraries.js';

import { createCloudConnectionLibrary } from '../cloud-connection.js';
import { createConnectorLibrary } from '../connector.js';
Expand Down Expand Up @@ -51,17 +52,12 @@ const connectorLibrary = createConnectorLibrary(queries, {
getClient: jest.fn(),
});
const cloudConnection = createCloudConnectionLibrary({
...mockLogtoConfigsLibrary,
getCloudConnectionData: jest.fn().mockResolvedValue({
appId: 'appId',
appSecret: 'appSecret',
resource: 'resource',
}),
getOidcConfigs: jest.fn(),
upsertJwtCustomizer: jest.fn(),
getJwtCustomizer: jest.fn(),
getJwtCustomizers: jest.fn(),
updateJwtCustomizer: jest.fn(),
deployJwtCustomizerScript: jest.fn(),
});

const getLogtoConnectors = jest.spyOn(connectorLibrary, 'getLogtoConnectors');
Expand Down
5 changes: 5 additions & 0 deletions packages/core/src/routes/logto-config/jwt-customizer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const logtoConfigLibraries = {
getJwtCustomizers: jest.fn(),
updateJwtCustomizer: jest.fn(),
deployJwtCustomizerScript: jest.fn(),
undeployJwtCustomizerScript: jest.fn(),
};

const settingRoutes = await pickDefault(import('./index.js'));
Expand Down Expand Up @@ -126,6 +127,10 @@ describe('configs JWT customizer routes', () => {

it('DELETE /configs/jwt-customizer/:tokenType should delete the record', async () => {
const response = await routeRequester.delete('/configs/jwt-customizer/client-credentials');
expect(logtoConfigLibraries.undeployJwtCustomizerScript).toHaveBeenCalledWith(
tenantContext.cloudConnection,
LogtoJwtTokenKey.ClientCredentials
);
expect(logtoConfigQueries.deleteJwtCustomizer).toHaveBeenCalledWith(
LogtoJwtTokenKey.ClientCredentials
);
Expand Down
15 changes: 12 additions & 3 deletions packages/core/src/routes/logto-config/jwt-customizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export default function logtoConfigJwtCustomizerRoutes<T extends AuthedRouter>(
getJwtCustomizers,
updateJwtCustomizer,
deployJwtCustomizerScript,
undeployJwtCustomizerScript,
} = logtoConfigs;

router.put(
Expand Down Expand Up @@ -177,15 +178,23 @@ export default function logtoConfigJwtCustomizerRoutes<T extends AuthedRouter>(
status: [204, 404],
}),
async (ctx, next) => {
const { isIntegrationTest } = EnvSet.values;

const {
params: { tokenTypePath },
} = ctx.guard;

await deleteJwtCustomizer(
const tokenKey =
tokenTypePath === LogtoJwtTokenKeyType.AccessToken
? LogtoJwtTokenKey.AccessToken
: LogtoJwtTokenKey.ClientCredentials
);
: LogtoJwtTokenKey.ClientCredentials;

// Undeploy the script first to avoid the case where the JWT customizer was deleted from DB but worker script not updated successfully.
if (!isIntegrationTest) {
await undeployJwtCustomizerScript(cloudConnection, tokenKey);
}

await deleteJwtCustomizer(tokenKey);
ctx.status = 204;
return next();
}
Expand Down
14 changes: 14 additions & 0 deletions packages/core/src/test-utils/mock-libraries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { type LogtoConfigLibrary } from '#src/libraries/logto-config.js';

const { jest } = import.meta;

export const mockLogtoConfigsLibrary: LogtoConfigLibrary = {
getCloudConnectionData: jest.fn(),
getOidcConfigs: jest.fn(),
upsertJwtCustomizer: jest.fn(),
getJwtCustomizer: jest.fn(),
getJwtCustomizers: jest.fn(),
updateJwtCustomizer: jest.fn(),
deployJwtCustomizerScript: jest.fn(),
undeployJwtCustomizerScript: jest.fn(),
};

0 comments on commit 543931a

Please sign in to comment.