mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-31 13:47:02 +02:00
cors setup - backend
This commit is contained in:
parent
f8a62efa72
commit
4bb80d2bff
@ -1,4 +1,3 @@
|
||||
import { ADMIN } from 'component/providers/AccessProvider/permissions';
|
||||
import type React from 'react';
|
||||
import { useState } from 'react';
|
||||
import { TextField, Box } from '@mui/material';
|
||||
@ -7,6 +6,7 @@ import { useUiConfigApi } from 'hooks/api/actions/useUiConfigApi/useUiConfigApi'
|
||||
import useToast from 'hooks/useToast';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import { useId } from 'hooks/useId';
|
||||
import { ADMIN, UPDATE_CORS } from '@server/types/permissions';
|
||||
|
||||
interface ICorsFormProps {
|
||||
frontendApiOrigins: string[] | undefined;
|
||||
@ -67,7 +67,7 @@ export const CorsForm = ({ frontendApiOrigins }: ICorsFormProps) => {
|
||||
style: { fontFamily: 'monospace', fontSize: '0.8em' },
|
||||
}}
|
||||
/>
|
||||
<UpdateButton permission={ADMIN} />
|
||||
<UpdateButton permission={[ADMIN, UPDATE_CORS]} />
|
||||
</Box>
|
||||
</form>
|
||||
);
|
||||
|
@ -1,15 +1,15 @@
|
||||
import { PermissionGuard } from 'component/common/PermissionGuard/PermissionGuard';
|
||||
import { ADMIN } from 'component/providers/AccessProvider/permissions';
|
||||
import { PageContent } from 'component/common/PageContent/PageContent';
|
||||
import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
||||
import { Box } from '@mui/material';
|
||||
import { CorsHelpAlert } from 'component/admin/cors/CorsHelpAlert';
|
||||
import { CorsForm } from 'component/admin/cors/CorsForm';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import { ADMIN, UPDATE_CORS } from '@server/types/permissions';
|
||||
|
||||
export const CorsAdmin = () => (
|
||||
<div>
|
||||
<PermissionGuard permissions={ADMIN}>
|
||||
<PermissionGuard permissions={[ADMIN, UPDATE_CORS]}>
|
||||
<CorsPage />
|
||||
</PermissionGuard>
|
||||
</div>
|
||||
|
@ -208,6 +208,23 @@ export class FrontendApiService {
|
||||
);
|
||||
}
|
||||
|
||||
async setFrontendCorsSettings(
|
||||
value: FrontendSettings['frontendApiOrigins'],
|
||||
auditUser: IAuditUser,
|
||||
): Promise<void> {
|
||||
const error = validateOrigins(value);
|
||||
if (error) {
|
||||
throw new BadDataError(error);
|
||||
}
|
||||
const settings = (await this.getFrontendSettings()) || {};
|
||||
await this.services.settingService.insert(
|
||||
frontendSettingsKey,
|
||||
{ ...settings, frontendApiOrigins: value },
|
||||
auditUser,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
async fetchFrontendSettings(): Promise<FrontendSettings> {
|
||||
try {
|
||||
this.cachedFrontendSettings =
|
||||
|
@ -180,6 +180,7 @@ export * from './search-features-schema';
|
||||
export * from './segment-schema';
|
||||
export * from './segment-strategies-schema';
|
||||
export * from './segments-schema';
|
||||
export * from './set-cors-schema';
|
||||
export * from './set-strategy-sort-order-schema';
|
||||
export * from './set-ui-config-schema';
|
||||
export * from './sort-order-schema';
|
||||
|
20
src/lib/openapi/spec/set-cors-schema.ts
Normal file
20
src/lib/openapi/spec/set-cors-schema.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import type { FromSchema } from 'json-schema-to-ts';
|
||||
|
||||
export const setCorsSchema = {
|
||||
$id: '#/components/schemas/setCorsSchema',
|
||||
type: 'object',
|
||||
additionalProperties: false,
|
||||
description: 'Unleash configuration settings affect the admin UI.',
|
||||
properties: {
|
||||
frontendApiOrigins: {
|
||||
description:
|
||||
'The list of origins that the front-end API should accept requests from.',
|
||||
example: ['*'],
|
||||
type: 'array',
|
||||
items: { type: 'string' },
|
||||
},
|
||||
},
|
||||
components: {},
|
||||
} as const;
|
||||
|
||||
export type SetCorsSchema = FromSchema<typeof setCorsSchema>;
|
@ -19,6 +19,11 @@ const uiConfig = {
|
||||
async function getSetup() {
|
||||
const base = `/random${Math.round(Math.random() * 1000)}`;
|
||||
const config = createTestConfig({
|
||||
experimental: {
|
||||
flags: {
|
||||
granularAdminPermissions: true,
|
||||
},
|
||||
},
|
||||
server: { baseUriPath: base },
|
||||
ui: uiConfig,
|
||||
});
|
||||
@ -56,3 +61,26 @@ test('should get ui config', async () => {
|
||||
expect(body.segmentValuesLimit).toEqual(DEFAULT_SEGMENT_VALUES_LIMIT);
|
||||
expect(body.strategySegmentsLimit).toEqual(DEFAULT_STRATEGY_SEGMENTS_LIMIT);
|
||||
});
|
||||
|
||||
test('should update CORS settings', async () => {
|
||||
const { body } = await request
|
||||
.get(`${base}/api/admin/ui-config`)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200);
|
||||
|
||||
expect(body.frontendApiOrigins).toEqual(['*']);
|
||||
|
||||
await request
|
||||
.post(`${base}/api/admin/ui-config/cors`)
|
||||
.send({
|
||||
frontendApiOrigins: ['https://example.com'],
|
||||
})
|
||||
.expect(204);
|
||||
|
||||
const { body: updatedBody } = await request
|
||||
.get(`${base}/api/admin/ui-config`)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect(200);
|
||||
|
||||
expect(updatedBody.frontendApiOrigins).toEqual(['https://example.com']);
|
||||
});
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
type SimpleAuthSettings,
|
||||
simpleAuthSettingsKey,
|
||||
} from '../../types/settings/simple-auth-settings';
|
||||
import { ADMIN, NONE } from '../../types/permissions';
|
||||
import { ADMIN, NONE, UPDATE_CORS } from '../../types/permissions';
|
||||
import { createResponseSchema } from '../../openapi/util/create-response-schema';
|
||||
import {
|
||||
uiConfigSchema,
|
||||
@ -22,6 +22,7 @@ import { emptyResponse } from '../../openapi/util/standard-responses';
|
||||
import type { IAuthRequest } from '../unleash-types';
|
||||
import NotFoundError from '../../error/notfound-error';
|
||||
import type { SetUiConfigSchema } from '../../openapi/spec/set-ui-config-schema';
|
||||
import type { SetCorsSchema } from '../../openapi/spec/set-cors-schema';
|
||||
import { createRequestSchema } from '../../openapi/util/create-request-schema';
|
||||
import type { FrontendApiService, SessionService } from '../../services';
|
||||
import type MaintenanceService from '../../features/maintenance/maintenance-service';
|
||||
@ -99,6 +100,7 @@ class ConfigController extends Controller {
|
||||
],
|
||||
});
|
||||
|
||||
// TODO: deprecate when removing `granularAdminPermissions` flag
|
||||
this.route({
|
||||
method: 'post',
|
||||
path: '',
|
||||
@ -116,6 +118,24 @@ class ConfigController extends Controller {
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
this.route({
|
||||
method: 'post',
|
||||
path: '/cors',
|
||||
handler: this.setCors,
|
||||
permission: [ADMIN, UPDATE_CORS],
|
||||
middleware: [
|
||||
openApiService.validPath({
|
||||
tags: ['Admin UI'],
|
||||
summary: 'Sets allowed CORS origins',
|
||||
description:
|
||||
'Sets Cross-Origin Resource Sharing headers for Frontend SDK API.',
|
||||
operationId: 'setCors',
|
||||
requestBody: createRequestSchema('setCorsSchema'),
|
||||
responses: { 204: emptyResponse },
|
||||
}),
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
async getUiConfig(
|
||||
@ -198,6 +218,30 @@ class ConfigController extends Controller {
|
||||
|
||||
throw new NotFoundError();
|
||||
}
|
||||
|
||||
async setCors(
|
||||
req: IAuthRequest<void, void, SetCorsSchema>,
|
||||
res: Response<string>,
|
||||
): Promise<void> {
|
||||
const granularAdminPermissions = this.flagResolver.isEnabled(
|
||||
'granularAdminPermissions',
|
||||
);
|
||||
|
||||
if (!granularAdminPermissions) {
|
||||
throw new NotFoundError();
|
||||
}
|
||||
|
||||
if (req.body.frontendApiOrigins) {
|
||||
await this.frontendApiService.setFrontendCorsSettings(
|
||||
req.body.frontendApiOrigins,
|
||||
req.audit,
|
||||
);
|
||||
res.sendStatus(204);
|
||||
return;
|
||||
}
|
||||
|
||||
throw new NotFoundError();
|
||||
}
|
||||
}
|
||||
|
||||
export default ConfigController;
|
||||
|
Loading…
Reference in New Issue
Block a user