Skip to content

Commit 4f15539

Browse files
feat: Allow multiple origins for header Access-Control-Allow-Origin (parse-community#8517)
1 parent 9e43bc2 commit 4f15539

File tree

6 files changed

+49
-6
lines changed

6 files changed

+49
-6
lines changed

resources/buildConfigDefinitions.js

+6
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,9 @@ function mapperFor(elt, t) {
161161
if (type == 'NumberOrBoolean') {
162162
return wrap(t.identifier('numberOrBooleanParser'));
163163
}
164+
if (type === 'StringOrStringArray') {
165+
return wrap(t.identifier('arrayParser'));
166+
}
164167
return wrap(t.identifier('objectParser'));
165168
}
166169
}
@@ -278,6 +281,9 @@ function inject(t, list) {
278281
const adapterType = elt.typeAnnotation.typeParameters.params[0].id.name;
279282
type = `Adapter<${adapterType}>`;
280283
}
284+
if (type === 'StringOrStringArray') {
285+
type = 'String|String[]';
286+
}
281287
comments += ` * @property {${type}} ${elt.name} ${elt.help}\n`;
282288
const obj = t.objectExpression(props);
283289
return t.objectProperty(t.stringLiteral(elt.name), obj);

spec/Middlewares.spec.js

+29
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,35 @@ describe('middlewares', () => {
287287
expect(headers['Access-Control-Allow-Origin']).toEqual('https://parseplatform.org/');
288288
});
289289

290+
it('should support multiple origins if several are defined in allowOrigin as an array', () => {
291+
AppCache.put(fakeReq.body._ApplicationId, {
292+
allowOrigin: ['https://a.com', 'https://b.com', 'https://c.com'],
293+
});
294+
const headers = {};
295+
const res = {
296+
header: (key, value) => {
297+
headers[key] = value;
298+
},
299+
};
300+
const allowCrossDomain = middlewares.allowCrossDomain(fakeReq.body._ApplicationId);
301+
// Test with the first domain
302+
fakeReq.headers.origin = 'https://a.com';
303+
allowCrossDomain(fakeReq, res, () => {});
304+
expect(headers['Access-Control-Allow-Origin']).toEqual('https://a.com');
305+
// Test with the second domain
306+
fakeReq.headers.origin = 'https://b.com';
307+
allowCrossDomain(fakeReq, res, () => {});
308+
expect(headers['Access-Control-Allow-Origin']).toEqual('https://b.com');
309+
// Test with the third domain
310+
fakeReq.headers.origin = 'https://c.com';
311+
allowCrossDomain(fakeReq, res, () => {});
312+
expect(headers['Access-Control-Allow-Origin']).toEqual('https://c.com');
313+
// Test with an unauthorized domain
314+
fakeReq.headers.origin = 'https://unauthorized.com';
315+
allowCrossDomain(fakeReq, res, () => {});
316+
expect(headers['Access-Control-Allow-Origin']).toEqual('https://a.com');
317+
});
318+
290319
it('should use user provided on field userFromJWT', done => {
291320
AppCache.put(fakeReq.body._ApplicationId, {
292321
masterKey: 'masterKey',

src/Options/Definitions.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,9 @@ module.exports.ParseServerOptions = {
8181
},
8282
allowOrigin: {
8383
env: 'PARSE_SERVER_ALLOW_ORIGIN',
84-
help: 'Sets the origin to Access-Control-Allow-Origin',
84+
help:
85+
'Sets origins for Access-Control-Allow-Origin. This can be a string for a single origin or an array of strings for multiple origins.',
86+
action: parsers.arrayParser,
8587
},
8688
analyticsAdapter: {
8789
env: 'PARSE_SERVER_ANALYTICS_ADAPTER',

src/Options/docs.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Options/index.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ type Adapter<T> = string | any | T;
3535
type NumberOrBoolean = number | boolean;
3636
type NumberOrString = number | string;
3737
type ProtectedFields = any;
38+
type StringOrStringArray = string | string[];
3839
type RequestKeywordDenylist = {
3940
key: string | any,
4041
value: any,
@@ -61,8 +62,8 @@ export interface ParseServerOptions {
6162
appName: ?string;
6263
/* Add headers to Access-Control-Allow-Headers */
6364
allowHeaders: ?(string[]);
64-
/* Sets the origin to Access-Control-Allow-Origin */
65-
allowOrigin: ?string;
65+
/* Sets origins for Access-Control-Allow-Origin. This can be a string for a single origin or an array of strings for multiple origins. */
66+
allowOrigin: ?StringOrStringArray;
6667
/* Adapter module for the analytics */
6768
analyticsAdapter: ?Adapter<AnalyticsAdapter>;
6869
/* Adapter module for the files sub-system */

src/middlewares.js

+7-2
Original file line numberDiff line numberDiff line change
@@ -384,8 +384,13 @@ export function allowCrossDomain(appId) {
384384
if (config && config.allowHeaders) {
385385
allowHeaders += `, ${config.allowHeaders.join(', ')}`;
386386
}
387-
const allowOrigin = (config && config.allowOrigin) || '*';
388-
res.header('Access-Control-Allow-Origin', allowOrigin);
387+
388+
const baseOrigins =
389+
typeof config?.allowOrigin === 'string' ? [config.allowOrigin] : config?.allowOrigin ?? ['*'];
390+
const requestOrigin = req.headers.origin;
391+
const allowOrigins =
392+
requestOrigin && baseOrigins.includes(requestOrigin) ? requestOrigin : baseOrigins[0];
393+
res.header('Access-Control-Allow-Origin', allowOrigins);
389394
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
390395
res.header('Access-Control-Allow-Headers', allowHeaders);
391396
res.header('Access-Control-Expose-Headers', 'X-Parse-Job-Status-Id, X-Parse-Push-Status-Id');

0 commit comments

Comments
 (0)