mirror of
https://github.com/logto-io/logto.git
synced 2025-03-10 22:22:45 -05:00
feat(core): check application custom domain conflict (#5256)
This commit is contained in:
parent
ef29f490af
commit
e74c0cbc92
4 changed files with 66 additions and 3 deletions
|
@ -23,6 +23,7 @@ const {
|
|||
findTotalNumberOfApplications,
|
||||
findApplicationById,
|
||||
findApplicationByProtectedAppHost,
|
||||
findApplicationByProtectedAppCustomDomain,
|
||||
insertApplication,
|
||||
updateApplicationById,
|
||||
deleteApplicationById,
|
||||
|
@ -88,6 +89,28 @@ describe('application query', () => {
|
|||
await findApplicationByProtectedAppHost(host);
|
||||
});
|
||||
|
||||
it('findApplicationByProtectedAppCustomDomain', async () => {
|
||||
const domain = 'my.blog.com';
|
||||
const rowData = { domain };
|
||||
|
||||
const expectSql = sql`
|
||||
select ${sql.join(Object.values(fields), sql`, `)}
|
||||
from ${table}
|
||||
where ${fields.protectedAppMetadata} ? 'customDomains'
|
||||
and ${fields.protectedAppMetadata}->'customDomains' @> $1::jsonb
|
||||
and ${fields.type} = $2
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([JSON.stringify([domain]), ApplicationType.Protected]);
|
||||
|
||||
return createMockQueryResult([rowData]);
|
||||
});
|
||||
|
||||
await findApplicationByProtectedAppCustomDomain(domain);
|
||||
});
|
||||
|
||||
it('insertApplication', async () => {
|
||||
const keys = excludeAutoSetFields(Applications.fieldKeys);
|
||||
|
||||
|
|
|
@ -138,6 +138,19 @@ export const createApplicationQueries = (pool: CommonQueryMethods) => {
|
|||
and ${fields.type} = ${ApplicationType.Protected}
|
||||
`);
|
||||
|
||||
/**
|
||||
* Find an protected application by its custom domain.
|
||||
* the domain is stored in the `customDomains` field of the `protectedAppMetadata` field.
|
||||
*/
|
||||
const findApplicationByProtectedAppCustomDomain = async (domain: string) =>
|
||||
pool.maybeOne<Application>(sql`
|
||||
select ${sql.join(Object.values(fields), sql`, `)}
|
||||
from ${table}
|
||||
where ${fields.protectedAppMetadata} ? 'customDomains'
|
||||
and ${fields.protectedAppMetadata}->'customDomains' @> ${sql.jsonb([domain])}
|
||||
and ${fields.type} = ${ApplicationType.Protected}
|
||||
`);
|
||||
|
||||
const insertApplication = buildInsertIntoWithPool(pool)(Applications, {
|
||||
returning: true,
|
||||
});
|
||||
|
@ -240,6 +253,7 @@ export const createApplicationQueries = (pool: CommonQueryMethods) => {
|
|||
findTotalNumberOfApplications,
|
||||
findApplicationById,
|
||||
findApplicationByProtectedAppHost,
|
||||
findApplicationByProtectedAppCustomDomain,
|
||||
insertApplication,
|
||||
updateApplication,
|
||||
updateApplicationById,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { DomainStatus } from '@logto/schemas';
|
||||
import { type Application, DomainStatus } from '@logto/schemas';
|
||||
import { pickDefault } from '@logto/shared/esm';
|
||||
import { type Nullable } from '@silverhand/essentials';
|
||||
|
||||
import { mockProtectedApplication } from '#src/__mocks__/index.js';
|
||||
import { mockIdGenerators } from '#src/test-utils/nanoid.js';
|
||||
|
@ -11,6 +12,9 @@ const mockDomain = 'app.example.com';
|
|||
|
||||
const updateApplicationById = jest.fn();
|
||||
const findApplicationById = jest.fn(async () => mockProtectedApplication);
|
||||
const findApplicationByProtectedAppCustomDomain = jest.fn(
|
||||
async (): Promise<Nullable<Application>> => null
|
||||
);
|
||||
|
||||
const mockDomainResponse = {
|
||||
domain: mockDomain,
|
||||
|
@ -35,6 +39,7 @@ const tenantContext = new MockTenant(
|
|||
applications: {
|
||||
findApplicationById,
|
||||
updateApplicationById,
|
||||
findApplicationByProtectedAppCustomDomain,
|
||||
},
|
||||
},
|
||||
undefined,
|
||||
|
@ -86,5 +91,15 @@ describe('application protected app metadata routes', () => {
|
|||
});
|
||||
expect(response.status).toEqual(400);
|
||||
});
|
||||
|
||||
it('throw when the domain is already in use', async () => {
|
||||
findApplicationByProtectedAppCustomDomain.mockResolvedValueOnce(mockProtectedApplication);
|
||||
const response = await requester
|
||||
.post(`/applications/asdf/protected-app-metadata/custom-domains`)
|
||||
.send({
|
||||
domain: mockDomain,
|
||||
});
|
||||
expect(response.status).toEqual(422);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { z } from 'zod';
|
||||
|
||||
import RequestError from '#src/errors/RequestError/index.js';
|
||||
import koaGuard from '#src/middleware/koa-guard.js';
|
||||
import assertThat from '#src/utils/assert-that.js';
|
||||
|
||||
|
@ -10,7 +11,11 @@ export default function applicationProtectedAppMetadataRoutes<T extends AuthedRo
|
|||
router,
|
||||
{
|
||||
queries: {
|
||||
applications: { findApplicationById, updateApplicationById },
|
||||
applications: {
|
||||
findApplicationById,
|
||||
updateApplicationById,
|
||||
findApplicationByProtectedAppCustomDomain,
|
||||
},
|
||||
},
|
||||
libraries: {
|
||||
applications: { validateProtectedApplicationById },
|
||||
|
@ -51,7 +56,13 @@ export default function applicationProtectedAppMetadataRoutes<T extends AuthedRo
|
|||
'domain.limit_to_one_domain'
|
||||
);
|
||||
|
||||
// TODO: LOG-8066 check if domain is already in use
|
||||
assertThat(
|
||||
!(await findApplicationByProtectedAppCustomDomain(domain)),
|
||||
new RequestError({
|
||||
code: 'domain.hostname_already_exists',
|
||||
status: 422,
|
||||
})
|
||||
);
|
||||
|
||||
const customDomain = await addDomainToRemote(domain);
|
||||
await updateApplicationById(id, {
|
||||
|
|
Loading…
Add table
Reference in a new issue