mirror of
https://github.com/logto-io/logto.git
synced 2025-02-17 22:04:19 -05:00
feat(core): delete protected app (#5191)
This commit is contained in:
parent
5bc649e10d
commit
67be81e6df
4 changed files with 63 additions and 5 deletions
|
@ -2,10 +2,30 @@ import { EnvSet } from '#src/env-set/index.js';
|
|||
import type Queries from '#src/tenants/Queries.js';
|
||||
import SystemContext from '#src/tenants/SystemContext.js';
|
||||
import assertThat from '#src/utils/assert-that.js';
|
||||
import { updateProtectedAppSiteConfigs } from '#src/utils/cloudflare/index.js';
|
||||
import {
|
||||
deleteProtectedAppSiteConfigs,
|
||||
updateProtectedAppSiteConfigs,
|
||||
} from '#src/utils/cloudflare/index.js';
|
||||
|
||||
export type ProtectedAppLibrary = ReturnType<typeof createProtectedAppLibrary>;
|
||||
|
||||
const getProviderConfig = async () => {
|
||||
const { protectedAppConfigProviderConfig } = SystemContext.shared;
|
||||
assertThat(protectedAppConfigProviderConfig, 'application.protected_app_not_configured');
|
||||
|
||||
return protectedAppConfigProviderConfig;
|
||||
};
|
||||
|
||||
const deleteRemoteAppConfigs = async (host: string): Promise<void> => {
|
||||
if (EnvSet.values.isIntegrationTest) {
|
||||
return;
|
||||
}
|
||||
|
||||
const protectedAppConfigProviderConfig = await getProviderConfig();
|
||||
|
||||
await deleteProtectedAppSiteConfigs(protectedAppConfigProviderConfig, host);
|
||||
};
|
||||
|
||||
export const createProtectedAppLibrary = (queries: Queries) => {
|
||||
const {
|
||||
applications: { findApplicationById },
|
||||
|
@ -17,8 +37,7 @@ export const createProtectedAppLibrary = (queries: Queries) => {
|
|||
return;
|
||||
}
|
||||
|
||||
const { protectedAppConfigProviderConfig } = SystemContext.shared;
|
||||
assertThat(protectedAppConfigProviderConfig, 'application.protected_app_not_configured');
|
||||
const protectedAppConfigProviderConfig = await getProviderConfig();
|
||||
|
||||
const { protectedAppMetadata, id, secret } = await findApplicationById(applicationId);
|
||||
if (!protectedAppMetadata) {
|
||||
|
@ -41,5 +60,6 @@ export const createProtectedAppLibrary = (queries: Queries) => {
|
|||
|
||||
return {
|
||||
syncAppConfigsToRemote,
|
||||
deleteRemoteAppConfigs,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -12,6 +12,7 @@ const { jest } = import.meta;
|
|||
const findApplicationById = jest.fn(async () => mockApplication);
|
||||
const deleteApplicationById = jest.fn();
|
||||
const syncAppConfigsToRemote = jest.fn();
|
||||
const deleteRemoteAppConfigs = jest.fn();
|
||||
const updateApplicationById = jest.fn(
|
||||
async (_, data: Partial<CreateApplication>): Promise<Application> => ({
|
||||
...mockApplication,
|
||||
|
@ -43,7 +44,10 @@ const tenantContext = new MockTenant(
|
|||
},
|
||||
},
|
||||
undefined,
|
||||
{ quota: createMockQuotaLibrary(), protectedApps: { syncAppConfigsToRemote } }
|
||||
{
|
||||
quota: createMockQuotaLibrary(),
|
||||
protectedApps: { syncAppConfigsToRemote, deleteRemoteAppConfigs },
|
||||
}
|
||||
);
|
||||
|
||||
const { createRequester } = await import('#src/utils/test-utils.js');
|
||||
|
@ -64,6 +68,7 @@ describe('application route', () => {
|
|||
afterEach(() => {
|
||||
updateApplicationById.mockClear();
|
||||
syncAppConfigsToRemote.mockClear();
|
||||
deleteRemoteAppConfigs.mockClear();
|
||||
});
|
||||
|
||||
const applicationRequest = createRequester({ authedRoutes: applicationRoutes, tenantContext });
|
||||
|
@ -303,11 +308,15 @@ describe('application route', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('DELETE /applications/:applicationId', async () => {
|
||||
it('DELETE /applications/:applicationId for protected app', async () => {
|
||||
findApplicationById.mockResolvedValueOnce(mockProtectedApplication);
|
||||
await expect(applicationRequest.delete('/applications/foo')).resolves.toHaveProperty(
|
||||
'status',
|
||||
204
|
||||
);
|
||||
expect(deleteRemoteAppConfigs).toHaveBeenCalledWith(
|
||||
mockProtectedApplication.protectedAppMetadata?.host
|
||||
);
|
||||
});
|
||||
|
||||
it('DELETE /applications/:applicationId should throw if application not found', async () => {
|
||||
|
|
|
@ -306,6 +306,10 @@ export default function applicationRoutes<T extends AuthedRouter>(
|
|||
}),
|
||||
async (ctx, next) => {
|
||||
const { id } = ctx.guard.params;
|
||||
const { type, protectedAppMetadata } = await findApplicationById(id);
|
||||
if (type === ApplicationType.Protected && protectedAppMetadata) {
|
||||
await protectedApps.deleteRemoteAppConfigs(protectedAppMetadata.host);
|
||||
}
|
||||
// Note: will need delete cascade when application is joint with other tables
|
||||
await deleteApplicationById(id);
|
||||
ctx.status = 204;
|
||||
|
|
|
@ -54,3 +54,28 @@ export const updateProtectedAppSiteConfigs = async (
|
|||
|
||||
handleResponse(response);
|
||||
};
|
||||
|
||||
export const deleteProtectedAppSiteConfigs = async (
|
||||
auth: ProtectedAppConfigProviderData,
|
||||
host: string
|
||||
) => {
|
||||
const response = await got.delete(
|
||||
new URL(
|
||||
path.join(
|
||||
baseUrl.pathname,
|
||||
`/accounts/${auth.accountIdentifier}/storage/kv/namespaces/${
|
||||
auth.namespaceIdentifier
|
||||
}/values/${encodeURIComponent(`${auth.keyName}:${host}`)}`
|
||||
),
|
||||
baseUrl
|
||||
),
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${auth.apiToken}`,
|
||||
},
|
||||
throwHttpErrors: false,
|
||||
}
|
||||
);
|
||||
|
||||
handleResponse(response);
|
||||
};
|
||||
|
|
Loading…
Add table
Reference in a new issue