Skip to content

Commit

Permalink
feat: Allow multiple origins for header Access-Control-Allow-Origin (
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcDerhammer committed May 1, 2023
1 parent 9e43bc2 commit 4f15539
Show file tree
Hide file tree
Showing 6 changed files with 49 additions and 6 deletions.
6 changes: 6 additions & 0 deletions resources/buildConfigDefinitions.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@ function mapperFor(elt, t) {
if (type == 'NumberOrBoolean') {
return wrap(t.identifier('numberOrBooleanParser'));
}
if (type === 'StringOrStringArray') {
return wrap(t.identifier('arrayParser'));
}
return wrap(t.identifier('objectParser'));
}
}
Expand Down Expand Up @@ -278,6 +281,9 @@ function inject(t, list) {
const adapterType = elt.typeAnnotation.typeParameters.params[0].id.name;
type = `Adapter<${adapterType}>`;
}
if (type === 'StringOrStringArray') {
type = 'String|String[]';
}
comments += ` * @property {${type}} ${elt.name} ${elt.help}\n`;
const obj = t.objectExpression(props);
return t.objectProperty(t.stringLiteral(elt.name), obj);
Expand Down
29 changes: 29 additions & 0 deletions spec/Middlewares.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,35 @@ describe('middlewares', () => {
expect(headers['Access-Control-Allow-Origin']).toEqual('https://parseplatform.org/');
});

it('should support multiple origins if several are defined in allowOrigin as an array', () => {
AppCache.put(fakeReq.body._ApplicationId, {
allowOrigin: ['https://a.com', 'https://b.com', 'https://c.com'],
});
const headers = {};
const res = {
header: (key, value) => {
headers[key] = value;
},
};
const allowCrossDomain = middlewares.allowCrossDomain(fakeReq.body._ApplicationId);
// Test with the first domain
fakeReq.headers.origin = 'https://a.com';
allowCrossDomain(fakeReq, res, () => {});
expect(headers['Access-Control-Allow-Origin']).toEqual('https://a.com');
// Test with the second domain
fakeReq.headers.origin = 'https://b.com';
allowCrossDomain(fakeReq, res, () => {});
expect(headers['Access-Control-Allow-Origin']).toEqual('https://b.com');
// Test with the third domain
fakeReq.headers.origin = 'https://c.com';
allowCrossDomain(fakeReq, res, () => {});
expect(headers['Access-Control-Allow-Origin']).toEqual('https://c.com');
// Test with an unauthorized domain
fakeReq.headers.origin = 'https://unauthorized.com';
allowCrossDomain(fakeReq, res, () => {});
expect(headers['Access-Control-Allow-Origin']).toEqual('https://a.com');
});

it('should use user provided on field userFromJWT', done => {
AppCache.put(fakeReq.body._ApplicationId, {
masterKey: 'masterKey',
Expand Down
4 changes: 3 additions & 1 deletion src/Options/Definitions.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ module.exports.ParseServerOptions = {
},
allowOrigin: {
env: 'PARSE_SERVER_ALLOW_ORIGIN',
help: 'Sets the origin to Access-Control-Allow-Origin',
help:
'Sets origins for Access-Control-Allow-Origin. This can be a string for a single origin or an array of strings for multiple origins.',
action: parsers.arrayParser,
},
analyticsAdapter: {
env: 'PARSE_SERVER_ANALYTICS_ADAPTER',
Expand Down
2 changes: 1 addition & 1 deletion src/Options/docs.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions src/Options/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type Adapter<T> = string | any | T;
type NumberOrBoolean = number | boolean;
type NumberOrString = number | string;
type ProtectedFields = any;
type StringOrStringArray = string | string[];
type RequestKeywordDenylist = {
key: string | any,
value: any,
Expand All @@ -61,8 +62,8 @@ export interface ParseServerOptions {
appName: ?string;
/* Add headers to Access-Control-Allow-Headers */
allowHeaders: ?(string[]);
/* Sets the origin to Access-Control-Allow-Origin */
allowOrigin: ?string;
/* Sets origins for Access-Control-Allow-Origin. This can be a string for a single origin or an array of strings for multiple origins. */
allowOrigin: ?StringOrStringArray;
/* Adapter module for the analytics */
analyticsAdapter: ?Adapter<AnalyticsAdapter>;
/* Adapter module for the files sub-system */
Expand Down
9 changes: 7 additions & 2 deletions src/middlewares.js
Original file line number Diff line number Diff line change
Expand Up @@ -384,8 +384,13 @@ export function allowCrossDomain(appId) {
if (config && config.allowHeaders) {
allowHeaders += `, ${config.allowHeaders.join(', ')}`;
}
const allowOrigin = (config && config.allowOrigin) || '*';
res.header('Access-Control-Allow-Origin', allowOrigin);

const baseOrigins =
typeof config?.allowOrigin === 'string' ? [config.allowOrigin] : config?.allowOrigin ?? ['*'];
const requestOrigin = req.headers.origin;
const allowOrigins =
requestOrigin && baseOrigins.includes(requestOrigin) ? requestOrigin : baseOrigins[0];
res.header('Access-Control-Allow-Origin', allowOrigins);
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
res.header('Access-Control-Allow-Headers', allowHeaders);
res.header('Access-Control-Expose-Headers', 'X-Parse-Job-Status-Id, X-Parse-Push-Status-Id');
Expand Down

0 comments on commit 4f15539

Please sign in to comment.