feat: Allow multiple origins for header Access-Control-Allow-Origin (#8517)
This commit is contained in:
@@ -161,6 +161,9 @@ function mapperFor(elt, t) {
|
|||||||
if (type == 'NumberOrBoolean') {
|
if (type == 'NumberOrBoolean') {
|
||||||
return wrap(t.identifier('numberOrBooleanParser'));
|
return wrap(t.identifier('numberOrBooleanParser'));
|
||||||
}
|
}
|
||||||
|
if (type === 'StringOrStringArray') {
|
||||||
|
return wrap(t.identifier('arrayParser'));
|
||||||
|
}
|
||||||
return wrap(t.identifier('objectParser'));
|
return wrap(t.identifier('objectParser'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -278,6 +281,9 @@ function inject(t, list) {
|
|||||||
const adapterType = elt.typeAnnotation.typeParameters.params[0].id.name;
|
const adapterType = elt.typeAnnotation.typeParameters.params[0].id.name;
|
||||||
type = `Adapter<${adapterType}>`;
|
type = `Adapter<${adapterType}>`;
|
||||||
}
|
}
|
||||||
|
if (type === 'StringOrStringArray') {
|
||||||
|
type = 'String|String[]';
|
||||||
|
}
|
||||||
comments += ` * @property {${type}} ${elt.name} ${elt.help}\n`;
|
comments += ` * @property {${type}} ${elt.name} ${elt.help}\n`;
|
||||||
const obj = t.objectExpression(props);
|
const obj = t.objectExpression(props);
|
||||||
return t.objectProperty(t.stringLiteral(elt.name), obj);
|
return t.objectProperty(t.stringLiteral(elt.name), obj);
|
||||||
|
|||||||
@@ -287,6 +287,35 @@ describe('middlewares', () => {
|
|||||||
expect(headers['Access-Control-Allow-Origin']).toEqual('https://parseplatform.org/');
|
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 => {
|
it('should use user provided on field userFromJWT', done => {
|
||||||
AppCache.put(fakeReq.body._ApplicationId, {
|
AppCache.put(fakeReq.body._ApplicationId, {
|
||||||
masterKey: 'masterKey',
|
masterKey: 'masterKey',
|
||||||
|
|||||||
@@ -81,7 +81,9 @@ module.exports.ParseServerOptions = {
|
|||||||
},
|
},
|
||||||
allowOrigin: {
|
allowOrigin: {
|
||||||
env: 'PARSE_SERVER_ALLOW_ORIGIN',
|
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: {
|
analyticsAdapter: {
|
||||||
env: 'PARSE_SERVER_ANALYTICS_ADAPTER',
|
env: 'PARSE_SERVER_ANALYTICS_ADAPTER',
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
* @property {Boolean} allowCustomObjectId Enable (or disable) custom objectId
|
* @property {Boolean} allowCustomObjectId Enable (or disable) custom objectId
|
||||||
* @property {Boolean} allowExpiredAuthDataToken Allow a user to log in even if the 3rd party authentication token that was used to sign in to their account has expired. If this is set to `false`, then the token will be validated every time the user signs in to their account. This refers to the token that is stored in the `_User.authData` field. Defaults to `true`.
|
* @property {Boolean} allowExpiredAuthDataToken Allow a user to log in even if the 3rd party authentication token that was used to sign in to their account has expired. If this is set to `false`, then the token will be validated every time the user signs in to their account. This refers to the token that is stored in the `_User.authData` field. Defaults to `true`.
|
||||||
* @property {String[]} allowHeaders Add headers to Access-Control-Allow-Headers
|
* @property {String[]} allowHeaders Add headers to Access-Control-Allow-Headers
|
||||||
* @property {String} allowOrigin Sets the origin to Access-Control-Allow-Origin
|
* @property {String|String[]} allowOrigin Sets origins for Access-Control-Allow-Origin. This can be a string for a single origin or an array of strings for multiple origins.
|
||||||
* @property {Adapter<AnalyticsAdapter>} analyticsAdapter Adapter module for the analytics
|
* @property {Adapter<AnalyticsAdapter>} analyticsAdapter Adapter module for the analytics
|
||||||
* @property {String} appId Your Parse Application ID
|
* @property {String} appId Your Parse Application ID
|
||||||
* @property {String} appName Sets the app name
|
* @property {String} appName Sets the app name
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ type Adapter<T> = string | any | T;
|
|||||||
type NumberOrBoolean = number | boolean;
|
type NumberOrBoolean = number | boolean;
|
||||||
type NumberOrString = number | string;
|
type NumberOrString = number | string;
|
||||||
type ProtectedFields = any;
|
type ProtectedFields = any;
|
||||||
|
type StringOrStringArray = string | string[];
|
||||||
type RequestKeywordDenylist = {
|
type RequestKeywordDenylist = {
|
||||||
key: string | any,
|
key: string | any,
|
||||||
value: any,
|
value: any,
|
||||||
@@ -61,8 +62,8 @@ export interface ParseServerOptions {
|
|||||||
appName: ?string;
|
appName: ?string;
|
||||||
/* Add headers to Access-Control-Allow-Headers */
|
/* Add headers to Access-Control-Allow-Headers */
|
||||||
allowHeaders: ?(string[]);
|
allowHeaders: ?(string[]);
|
||||||
/* Sets the origin to Access-Control-Allow-Origin */
|
/* 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: ?string;
|
allowOrigin: ?StringOrStringArray;
|
||||||
/* Adapter module for the analytics */
|
/* Adapter module for the analytics */
|
||||||
analyticsAdapter: ?Adapter<AnalyticsAdapter>;
|
analyticsAdapter: ?Adapter<AnalyticsAdapter>;
|
||||||
/* Adapter module for the files sub-system */
|
/* Adapter module for the files sub-system */
|
||||||
|
|||||||
@@ -384,8 +384,13 @@ export function allowCrossDomain(appId) {
|
|||||||
if (config && config.allowHeaders) {
|
if (config && config.allowHeaders) {
|
||||||
allowHeaders += `, ${config.allowHeaders.join(', ')}`;
|
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-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
|
||||||
res.header('Access-Control-Allow-Headers', allowHeaders);
|
res.header('Access-Control-Allow-Headers', allowHeaders);
|
||||||
res.header('Access-Control-Expose-Headers', 'X-Parse-Job-Status-Id, X-Parse-Push-Status-Id');
|
res.header('Access-Control-Expose-Headers', 'X-Parse-Job-Status-Id, X-Parse-Push-Status-Id');
|
||||||
|
|||||||
Reference in New Issue
Block a user