mirror of
https://github.com/logto-io/logto.git
synced 2025-03-24 22:41:28 -05:00
feat(core,schemas): add isThirdParty column to the applications table (#5090)
* feat(core,schemas): add isThirdParty column to the applications table add isThirdParty column to the applications table * refactor(core): group the application routes under applications directory (#5091) * refactor(core): group the application routes under applications directory group the application routes under applications directory * refactor(core,schemas): refactor the application api guard refactor the application api guard * fix(schemas): fix application patch guard fix application patch guard * fix(test): fix ut fix ut
This commit is contained in:
parent
630a8c52cf
commit
a93a39aa1b
14 changed files with 90 additions and 30 deletions
|
@ -38,6 +38,7 @@ export const mockApplication: Application = {
|
|||
idTokenTtl: 5000,
|
||||
refreshTokenTtl: 6_000_000,
|
||||
},
|
||||
isThirdParty: false,
|
||||
createdAt: 1_645_334_775_356,
|
||||
};
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import koaRoleRlsErrorHandler from '#src/middleware/koa-role-rls-error-handler.j
|
|||
import assertThat from '#src/utils/assert-that.js';
|
||||
import { parseSearchParamsForSearch } from '#src/utils/search.js';
|
||||
|
||||
import type { AuthedRouter, RouterInitArgs } from './types.js';
|
||||
import type { AuthedRouter, RouterInitArgs } from '../types.js';
|
||||
|
||||
export default function applicationRoleRoutes<T extends AuthedRouter>(
|
||||
...[router, { queries }]: RouterInitArgs<T>
|
|
@ -76,6 +76,7 @@ describe('application route', () => {
|
|||
const response = await applicationRequest
|
||||
.post('/applications')
|
||||
.send({ name, type, description });
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.body).toEqual({
|
||||
...mockApplication,
|
||||
|
@ -153,19 +154,26 @@ describe('application route', () => {
|
|||
});
|
||||
|
||||
it('PATCH /applications/:applicationId expect to throw with invalid properties', async () => {
|
||||
await expect(
|
||||
applicationRequest.patch('/applications/doo').send({ type: 'node' })
|
||||
).resolves.toHaveProperty('status', 400);
|
||||
await expect(
|
||||
applicationRequest.patch('/applications/doo').send({
|
||||
customClientMetadata: {
|
||||
...customClientMetadata,
|
||||
corsAllowedOrigins: [''],
|
||||
},
|
||||
customClientMetadata: 'test',
|
||||
})
|
||||
).resolves.toHaveProperty('status', 400);
|
||||
});
|
||||
|
||||
it('PATCH /applications/:applicationId should not allow update application secret, isThirdParty and type', async () => {
|
||||
const response = await applicationRequest.patch('/applications/foo').send({
|
||||
type: ApplicationType.Native,
|
||||
isThirdParty: true,
|
||||
secret: 'test',
|
||||
});
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
|
||||
// Should not update the secret, isThirdParty and type
|
||||
expect(response.body).toEqual(mockApplication);
|
||||
});
|
||||
|
||||
it('PATCH /applications/:applicationId should save the formatted URIs as per RFC', async () => {
|
||||
await expect(
|
||||
applicationRequest.patch('/applications/foo').send({
|
|
@ -2,9 +2,9 @@ import type { Role } from '@logto/schemas';
|
|||
import {
|
||||
demoAppApplicationId,
|
||||
buildDemoAppDataForTenant,
|
||||
Applications,
|
||||
InternalRole,
|
||||
ApplicationType,
|
||||
applicationPatchGuard,
|
||||
} from '@logto/schemas';
|
||||
import { generateStandardId, generateStandardSecret } from '@logto/shared';
|
||||
import { boolean, object, string, z } from 'zod';
|
||||
|
@ -16,7 +16,9 @@ import { buildOidcClientMetadata } from '#src/oidc/utils.js';
|
|||
import assertThat from '#src/utils/assert-that.js';
|
||||
import { parseSearchParamsForSearch } from '#src/utils/search.js';
|
||||
|
||||
import type { AuthedRouter, RouterInitArgs } from './types.js';
|
||||
import type { AuthedRouter, RouterInitArgs } from '../types.js';
|
||||
|
||||
import { applicationResponseGuard, applicationCreateGuard } from './types.js';
|
||||
|
||||
const includesInternalAdminRole = (roles: Readonly<Array<{ role: Role }>>) =>
|
||||
roles.some(({ role: { name } }) => name === InternalRole.Admin);
|
||||
|
@ -63,7 +65,7 @@ export default function applicationRoutes<T extends AuthedRouter>(
|
|||
.or(applicationTypeGuard.transform((type) => [type]))
|
||||
.optional(),
|
||||
}),
|
||||
response: z.array(Applications.guard),
|
||||
response: z.array(applicationResponseGuard),
|
||||
status: 200,
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
|
@ -103,11 +105,8 @@ export default function applicationRoutes<T extends AuthedRouter>(
|
|||
router.post(
|
||||
'/applications',
|
||||
koaGuard({
|
||||
body: Applications.createGuard
|
||||
.omit({ id: true, createdAt: true })
|
||||
.partial()
|
||||
.merge(Applications.createGuard.pick({ name: true, type: true })),
|
||||
response: Applications.guard,
|
||||
body: applicationCreateGuard,
|
||||
response: applicationResponseGuard,
|
||||
status: [200, 422],
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
|
@ -134,7 +133,7 @@ export default function applicationRoutes<T extends AuthedRouter>(
|
|||
'/applications/:id',
|
||||
koaGuard({
|
||||
params: object({ id: string().min(1) }),
|
||||
response: Applications.guard.merge(z.object({ isAdmin: z.boolean() })),
|
||||
response: applicationResponseGuard.merge(z.object({ isAdmin: z.boolean() })),
|
||||
status: [200, 404],
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
|
@ -165,15 +164,12 @@ export default function applicationRoutes<T extends AuthedRouter>(
|
|||
'/applications/:id',
|
||||
koaGuard({
|
||||
params: object({ id: string().min(1) }),
|
||||
body: Applications.createGuard
|
||||
.omit({ id: true, createdAt: true })
|
||||
.deepPartial()
|
||||
.merge(
|
||||
object({
|
||||
isAdmin: boolean().optional(),
|
||||
})
|
||||
),
|
||||
response: Applications.guard,
|
||||
body: applicationPatchGuard.deepPartial().merge(
|
||||
object({
|
||||
isAdmin: boolean().optional(),
|
||||
})
|
||||
),
|
||||
response: applicationResponseGuard,
|
||||
status: [200, 404, 422, 500],
|
||||
}),
|
||||
async (ctx, next) => {
|
15
packages/core/src/routes/applications/types.ts
Normal file
15
packages/core/src/routes/applications/types.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
import {
|
||||
Applications,
|
||||
applicationCreateGuard as originalApplicationCreateGuard,
|
||||
} from '@logto/schemas';
|
||||
|
||||
import { EnvSet } from '#src/env-set/index.js';
|
||||
|
||||
// FIXME: @simeng-li Remove this guard once Logto as IdP is ready
|
||||
export const applicationResponseGuard = EnvSet.values.isDevFeaturesEnabled
|
||||
? Applications.guard
|
||||
: Applications.guard.omit({ isThirdParty: true });
|
||||
|
||||
export const applicationCreateGuard = EnvSet.values.isDevFeaturesEnabled
|
||||
? originalApplicationCreateGuard
|
||||
: originalApplicationCreateGuard.omit({ isThirdParty: true });
|
|
@ -11,8 +11,8 @@ import type TenantContext from '#src/tenants/TenantContext.js';
|
|||
import koaAuth from '../middleware/koa-auth/index.js';
|
||||
|
||||
import adminUserRoutes from './admin-user/index.js';
|
||||
import applicationRoleRoutes from './application-role.js';
|
||||
import applicationRoutes from './application.js';
|
||||
import applicationRoleRoutes from './applications/application-role.js';
|
||||
import applicationRoutes from './applications/application.js';
|
||||
import authnRoutes from './authn.js';
|
||||
import connectorRoutes from './connector/index.js';
|
||||
import customPhraseRoutes from './custom-phrase.js';
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { Applications } from '@logto/schemas';
|
||||
import { generateStandardId } from '@logto/shared';
|
||||
import { tryThat } from '@silverhand/essentials';
|
||||
import { object, string } from 'zod';
|
||||
|
@ -8,6 +7,7 @@ import koaGuard from '#src/middleware/koa-guard.js';
|
|||
import koaPagination from '#src/middleware/koa-pagination.js';
|
||||
import { parseSearchParamsForSearch } from '#src/utils/search.js';
|
||||
|
||||
import { applicationResponseGuard } from './applications/types.js';
|
||||
import type { AuthedRouter, RouterInitArgs } from './types.js';
|
||||
|
||||
export default function roleApplicationRoutes<T extends AuthedRouter>(
|
||||
|
@ -29,7 +29,7 @@ export default function roleApplicationRoutes<T extends AuthedRouter>(
|
|||
koaPagination(),
|
||||
koaGuard({
|
||||
params: object({ id: string().min(1) }),
|
||||
response: Applications.guard.array(),
|
||||
response: applicationResponseGuard.array(),
|
||||
status: [200, 204, 400, 404],
|
||||
}),
|
||||
async (ctx, next) => {
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
import { sql } from 'slonik';
|
||||
|
||||
import type { AlterationScript } from '../lib/types/alteration.js';
|
||||
|
||||
const alteration: AlterationScript = {
|
||||
up: async (pool) => {
|
||||
await pool.query(sql`
|
||||
alter table applications add is_third_party boolean not null default false;
|
||||
create index applications__is_third_party on applications (tenant_id, is_third_party);
|
||||
`);
|
||||
},
|
||||
down: async (pool) => {
|
||||
await pool.query(sql`
|
||||
drop index applications__is_third_party;
|
||||
alter table applications drop is_third_party;
|
||||
`);
|
||||
},
|
||||
};
|
||||
|
||||
export default alteration;
|
|
@ -27,6 +27,7 @@ export const buildDemoAppDataForTenant = (tenantId: string): Application => ({
|
|||
type: ApplicationType.SPA,
|
||||
oidcClientMetadata: { redirectUris: [], postLogoutRedirectUris: [] },
|
||||
customClientMetadata: {},
|
||||
isThirdParty: false,
|
||||
createdAt: 0,
|
||||
});
|
||||
|
||||
|
|
|
@ -16,3 +16,18 @@ export const featuredApplicationGuard = Applications.guard.pick({
|
|||
name: true,
|
||||
type: true,
|
||||
}) satisfies z.ZodType<FeaturedApplication>;
|
||||
|
||||
export const applicationCreateGuard = Applications.createGuard
|
||||
.omit({
|
||||
id: true,
|
||||
createdAt: true,
|
||||
secret: true,
|
||||
tenantId: true,
|
||||
})
|
||||
.partial()
|
||||
.merge(Applications.createGuard.pick({ name: true, type: true }));
|
||||
|
||||
export const applicationPatchGuard = applicationCreateGuard.partial().omit({
|
||||
type: true,
|
||||
isThirdParty: true,
|
||||
});
|
||||
|
|
|
@ -12,9 +12,13 @@ create table applications (
|
|||
type application_type not null,
|
||||
oidc_client_metadata jsonb /* @use OidcClientMetadata */ not null,
|
||||
custom_client_metadata jsonb /* @use CustomClientMetadata */ not null default '{}'::jsonb,
|
||||
is_third_party boolean not null default false,
|
||||
created_at timestamptz not null default(now()),
|
||||
primary key (id)
|
||||
);
|
||||
|
||||
create index applications__id
|
||||
on applications (tenant_id, id);
|
||||
|
||||
create index applications__is_third_party
|
||||
on applications (tenant_id, is_third_party);
|
||||
|
|
Loading…
Add table
Reference in a new issue