mirror of
https://github.com/logto-io/logto.git
synced 2025-04-07 23:01:25 -05:00
feat(core,schemas): add block domain list (#5234)
This commit is contained in:
parent
ca24e20f6a
commit
cbfd00e673
20 changed files with 64 additions and 0 deletions
|
@ -45,6 +45,7 @@ beforeAll(() => {
|
|||
SystemContext.shared.hostnameProviderConfig = {
|
||||
zoneId: 'fake_zone_id',
|
||||
apiToken: '',
|
||||
blockedDomains: ['blocked.com'],
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -70,6 +71,12 @@ describe('addDomain()', () => {
|
|||
value: mockFallbackOrigin,
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw for blocked domain', async () => {
|
||||
await expect(addDomain('hi.blocked.com')).rejects.toMatchError(
|
||||
new RequestError({ code: 'domain.domain_is_not_allowed' })
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('syncDomainStatus()', () => {
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
deleteCustomHostname,
|
||||
getFallbackOrigin,
|
||||
} from '#src/utils/cloudflare/index.js';
|
||||
import { isSubdomainOf } from '#src/utils/domain.js';
|
||||
import { clearCustomDomainCache } from '#src/utils/tenant.js';
|
||||
|
||||
export type DomainLibrary = ReturnType<typeof createDomainLibrary>;
|
||||
|
@ -71,6 +72,14 @@ export const createDomainLibrary = (queries: Queries) => {
|
|||
const { hostnameProviderConfig } = SystemContext.shared;
|
||||
assertThat(hostnameProviderConfig, 'domain.not_configured');
|
||||
|
||||
const { blockedDomains } = hostnameProviderConfig;
|
||||
assertThat(
|
||||
!(blockedDomains ?? []).some(
|
||||
(domain) => hostname === domain || isSubdomainOf(hostname, domain)
|
||||
),
|
||||
'domain.domain_is_not_allowed'
|
||||
);
|
||||
|
||||
const [fallbackOrigin, cloudflareData] = await Promise.all([
|
||||
getFallbackOrigin(hostnameProviderConfig),
|
||||
createCustomHostname(hostnameProviderConfig, hostname),
|
||||
|
|
12
packages/core/src/utils/domain.test.ts
Normal file
12
packages/core/src/utils/domain.test.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
import { isSubdomainOf } from './domain.js';
|
||||
|
||||
describe('isSubdomainOf()', () => {
|
||||
it('should return true if the given domain is a subdomain of a domain', () => {
|
||||
expect(isSubdomainOf('subdomain.domain.com', 'domain.com')).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false if the given domain is not a subdomain of a domain', () => {
|
||||
expect(isSubdomainOf('subdomain.domain.com', 'domain.org')).toBe(false);
|
||||
expect(isSubdomainOf('subdomaindomain.com', 'domain.org')).toBe(false);
|
||||
});
|
||||
});
|
6
packages/core/src/utils/domain.ts
Normal file
6
packages/core/src/utils/domain.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
/**
|
||||
* Checks if the given domain is a subdomain of a domain.
|
||||
*/
|
||||
export const isSubdomainOf = (subdomain: string, domain: string): boolean => {
|
||||
return subdomain.endsWith(`.${domain}`);
|
||||
};
|
|
@ -7,6 +7,8 @@ const domain = {
|
|||
limit_to_one_domain: 'Sie können nur eine benutzerdefinierte Domain haben.',
|
||||
hostname_already_exists: 'Diese Domain existiert bereits auf unserem Server.',
|
||||
cloudflare_not_found: 'Hostname in Cloudflare nicht gefunden',
|
||||
/** UNTRANSLATED */
|
||||
domain_is_not_allowed: 'This domain is not allowed.',
|
||||
};
|
||||
|
||||
export default Object.freeze(domain);
|
||||
|
|
|
@ -6,6 +6,7 @@ const domain = {
|
|||
limit_to_one_domain: 'You can only have one custom domain.',
|
||||
hostname_already_exists: 'This domain already exists in our server.',
|
||||
cloudflare_not_found: 'Can not find hostname in Cloudflare',
|
||||
domain_is_not_allowed: 'This domain is not allowed.',
|
||||
};
|
||||
|
||||
export default Object.freeze(domain);
|
||||
|
|
|
@ -6,6 +6,8 @@ const domain = {
|
|||
limit_to_one_domain: 'Solo puedes tener un dominio personalizado.',
|
||||
hostname_already_exists: 'Este dominio ya existe en nuestro servidor.',
|
||||
cloudflare_not_found: 'No se puede encontrar el nombre de host en Cloudflare',
|
||||
/** UNTRANSLATED */
|
||||
domain_is_not_allowed: 'This domain is not allowed.',
|
||||
};
|
||||
|
||||
export default Object.freeze(domain);
|
||||
|
|
|
@ -6,6 +6,8 @@ const domain = {
|
|||
limit_to_one_domain: "Vous ne pouvez avoir qu'un seul domaine personnalisé",
|
||||
hostname_already_exists: 'Ce domaine existe déjà sur notre serveur.',
|
||||
cloudflare_not_found: "Impossible de trouver le nom d'hôte dans Cloudflare",
|
||||
/** UNTRANSLATED */
|
||||
domain_is_not_allowed: 'This domain is not allowed.',
|
||||
};
|
||||
|
||||
export default Object.freeze(domain);
|
||||
|
|
|
@ -6,6 +6,8 @@ const domain = {
|
|||
limit_to_one_domain: 'Puoi avere solo un dominio personalizzato.',
|
||||
hostname_already_exists: 'Questo dominio esiste già nel nostro server.',
|
||||
cloudflare_not_found: 'Impossibile trovare il nome host in Cloudflare.',
|
||||
/** UNTRANSLATED */
|
||||
domain_is_not_allowed: 'This domain is not allowed.',
|
||||
};
|
||||
|
||||
export default Object.freeze(domain);
|
||||
|
|
|
@ -6,6 +6,8 @@ const domain = {
|
|||
limit_to_one_domain: 'カスタムドメインは1つしか持てません。',
|
||||
hostname_already_exists: 'サーバーには既にこのドメインが存在しています。',
|
||||
cloudflare_not_found: 'Cloudflare からホスト名が見つかりませんでした。',
|
||||
/** UNTRANSLATED */
|
||||
domain_is_not_allowed: 'This domain is not allowed.',
|
||||
};
|
||||
|
||||
export default Object.freeze(domain);
|
||||
|
|
|
@ -6,6 +6,8 @@ const domain = {
|
|||
limit_to_one_domain: '하나의 맞춤 도메인만 사용할 수 있습니다.',
|
||||
hostname_already_exists: '이 도메인은 이미 서버에 존재합니다.',
|
||||
cloudflare_not_found: 'Cloudflare에서 호스트 이름을 찾을 수 없습니다.',
|
||||
/** UNTRANSLATED */
|
||||
domain_is_not_allowed: 'This domain is not allowed.',
|
||||
};
|
||||
|
||||
export default Object.freeze(domain);
|
||||
|
|
|
@ -6,6 +6,8 @@ const domain = {
|
|||
limit_to_one_domain: 'Możesz mieć tylko jedną niestandardową domenę.',
|
||||
hostname_already_exists: 'Ta domena już istnieje na naszym serwerze.',
|
||||
cloudflare_not_found: 'Nie można znaleźć nazwy hosta w Cloudflare',
|
||||
/** UNTRANSLATED */
|
||||
domain_is_not_allowed: 'This domain is not allowed.',
|
||||
};
|
||||
|
||||
export default Object.freeze(domain);
|
||||
|
|
|
@ -6,6 +6,8 @@ const domain = {
|
|||
limit_to_one_domain: 'Você só pode ter um domínio personalizado.',
|
||||
hostname_already_exists: 'Este domínio já existe em nosso servidor.',
|
||||
cloudflare_not_found: 'Não é possível encontrar o nome do host no Cloudflare',
|
||||
/** UNTRANSLATED */
|
||||
domain_is_not_allowed: 'This domain is not allowed.',
|
||||
};
|
||||
|
||||
export default Object.freeze(domain);
|
||||
|
|
|
@ -6,6 +6,8 @@ const domain = {
|
|||
limit_to_one_domain: 'Você só pode ter um domínio personalizado.',
|
||||
hostname_already_exists: 'Este domínio já existe em nosso servidor.',
|
||||
cloudflare_not_found: 'Não é possível encontrar o nome de host no Cloudflare',
|
||||
/** UNTRANSLATED */
|
||||
domain_is_not_allowed: 'This domain is not allowed.',
|
||||
};
|
||||
|
||||
export default Object.freeze(domain);
|
||||
|
|
|
@ -6,6 +6,8 @@ const domain = {
|
|||
limit_to_one_domain: 'Вы можете использовать только один пользовательский домен.',
|
||||
hostname_already_exists: 'Этот домен уже существует на нашем сервере.',
|
||||
cloudflare_not_found: 'Не удается найти имя хоста в Cloudflare',
|
||||
/** UNTRANSLATED */
|
||||
domain_is_not_allowed: 'This domain is not allowed.',
|
||||
};
|
||||
|
||||
export default Object.freeze(domain);
|
||||
|
|
|
@ -6,6 +6,8 @@ const domain = {
|
|||
limit_to_one_domain: 'Sadece bir özel alan adınız olabilir.',
|
||||
hostname_already_exists: 'Bu alan adı sunucumuzda zaten mevcut.',
|
||||
cloudflare_not_found: "Cloudflare'da alan adı bulunamadı.",
|
||||
/** UNTRANSLATED */
|
||||
domain_is_not_allowed: 'This domain is not allowed.',
|
||||
};
|
||||
|
||||
export default Object.freeze(domain);
|
||||
|
|
|
@ -6,6 +6,8 @@ const domain = {
|
|||
limit_to_one_domain: '仅限一个自定义域名。',
|
||||
hostname_already_exists: '该域名在我们的服务器中已存在。',
|
||||
cloudflare_not_found: '在 Cloudflare 中找不到主机名',
|
||||
/** UNTRANSLATED */
|
||||
domain_is_not_allowed: 'This domain is not allowed.',
|
||||
};
|
||||
|
||||
export default Object.freeze(domain);
|
||||
|
|
|
@ -6,6 +6,8 @@ const domain = {
|
|||
limit_to_one_domain: '您只能擁有一個自定義域名。',
|
||||
hostname_already_exists: '此域名已存在於我們的伺服器中。',
|
||||
cloudflare_not_found: '無法在 Cloudflare 中找到主機名',
|
||||
/** UNTRANSLATED */
|
||||
domain_is_not_allowed: 'This domain is not allowed.',
|
||||
};
|
||||
|
||||
export default Object.freeze(domain);
|
||||
|
|
|
@ -6,6 +6,8 @@ const domain = {
|
|||
limit_to_one_domain: '您只能擁有一個自訂網域。',
|
||||
hostname_already_exists: '此網域名稱已經存在我們的伺服器中。',
|
||||
cloudflare_not_found: '無法找到 Cloudflare 中的主機名',
|
||||
/** UNTRANSLATED */
|
||||
domain_is_not_allowed: 'This domain is not allowed.',
|
||||
};
|
||||
|
||||
export default Object.freeze(domain);
|
||||
|
|
|
@ -145,6 +145,7 @@ export const demoSocialGuard: Readonly<{
|
|||
export const hostnameProviderDataGuard = z.object({
|
||||
zoneId: z.string(),
|
||||
apiToken: z.string(), // Requires zone permission for "SSL and Certificates Edit"
|
||||
blockedDomains: z.string().array().optional(), // Optional list of blocked domains
|
||||
});
|
||||
|
||||
export type HostnameProviderData = z.infer<typeof hostnameProviderDataGuard>;
|
||||
|
|
Loading…
Add table
Reference in a new issue