Allow configuring allowed CORS origins for suggest (#1282)
- Support single-server testing and a local dev server (like Next) that is on a different port from the shields server - Refactor config schema With this change, the suggestions work locally in #1273.
This commit is contained in:
@@ -139,8 +139,17 @@ If you want to host the frontend on a separate server, such as cloud storage
|
||||
or a CDN, you can do that. Just copy the built `index.html` there.
|
||||
|
||||
To help out users, you can make the Shields server redirect the server root.
|
||||
Set the `FRONTEND_REDIRECT_URL` environment variable:
|
||||
Set the `REDIRECT_URI` environment variable:
|
||||
|
||||
```sh
|
||||
FRONTEND_REDIRECT_URL=http://my-custom-shields.s3.amazonaws.com/
|
||||
REDIRECT_URI=http://my-custom-shields.s3.amazonaws.com/
|
||||
```
|
||||
|
||||
If you want to use server suggestions, you should also set `ALLOWED_ORIGIN`:
|
||||
|
||||
```sh
|
||||
ALLOWED_ORIGIN=http://my-custom-shields.s3.amazonaws.com,https://my-custom-shields.s3.amazonaws.com
|
||||
```
|
||||
|
||||
This should be a comma-separated list of allowed origin headers. They should
|
||||
not have paths or trailing slashes.
|
||||
|
||||
@@ -4,25 +4,51 @@
|
||||
// should be injected into other components needing it.
|
||||
|
||||
const url = require('url');
|
||||
const envFlag = require('node-env-flag');
|
||||
|
||||
const secureServer = !!process.env.HTTPS;
|
||||
const serverPort = +process.env.PORT || +process.argv[2] || (secureServer ? 443 : 80);
|
||||
const bindAddress = process.env.BIND_ADDRESS || process.argv[3] || '::';
|
||||
function envArray(envVar, defaultValue, delimiter) {
|
||||
delimiter = delimiter || ',';
|
||||
if (envVar) {
|
||||
return envVar.split(delimiter);
|
||||
} else {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
const isSecure = envFlag(process.env.HTTPS, false);
|
||||
const port = +process.env.PORT || +process.argv[2] || (isSecure ? 443 : 80);
|
||||
const address = process.env.BIND_ADDRESS || process.argv[3] || '::';
|
||||
const baseUri = url.format({
|
||||
protocol: isSecure ? 'https' : 'http',
|
||||
hostname: address,
|
||||
port,
|
||||
pathname: '/',
|
||||
});
|
||||
|
||||
// The base URI provides a suitable value for development. Production should
|
||||
// configure this.
|
||||
const allowedOrigin = envArray(process.env.ALLOWED_ORIGIN, baseUri.replace(/\/$/, ''), ',');
|
||||
|
||||
const config = {
|
||||
secureServer,
|
||||
secureServerKey: process.env.HTTPS_KEY,
|
||||
secureServerCert: process.env.HTTPS_CRT,
|
||||
serverPort,
|
||||
bindAddress,
|
||||
githubApiUrl: process.env.GITHUB_URL || 'https://api.github.com',
|
||||
frontendUri: url.format({
|
||||
protocol: secureServer ? 'https' : 'http',
|
||||
hostname: bindAddress,
|
||||
port: serverPort,
|
||||
pathname: '/',
|
||||
}),
|
||||
frontendRedirectUrl: process.env.FRONTEND_REDIRECT_URL || process.env.INFOSITE,
|
||||
bind: {
|
||||
port,
|
||||
address,
|
||||
},
|
||||
ssl: {
|
||||
isSecure,
|
||||
key: process.env.HTTPS_KEY,
|
||||
cert: process.env.HTTPS_CRT,
|
||||
},
|
||||
baseUri,
|
||||
redirectUri: process.env.REDIRECT_URI || process.env.INFOSITE,
|
||||
cors: {
|
||||
allowedOrigin,
|
||||
},
|
||||
services: {
|
||||
github: {
|
||||
baseUri: process.env.GITHUB_URL || 'https://api.github.com',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = config;
|
||||
|
||||
@@ -12,15 +12,20 @@ const githubApiUrl = process.env.GITHUB_URL || 'https://api.github.com';
|
||||
// - link: target as a string URL.
|
||||
// - badge: shields image URL.
|
||||
// - name: string
|
||||
function suggest (data, end, ask) {
|
||||
const origin = ask.req.headers['origin'];
|
||||
if (/^https?:\/\/shields\.io$/.test(origin)) {
|
||||
ask.res.setHeader('Access-Control-Allow-Origin', origin);
|
||||
} else {
|
||||
ask.res.setHeader('Access-Control-Allow-Origin', 'null');
|
||||
end({err: 'Disallowed'});
|
||||
return;
|
||||
function suggest (allowedOrigin, data, end, ask) {
|
||||
// Same-host requests may be made in development or in single-server
|
||||
// testing. These are legitimate, but do not have an origin header.
|
||||
const origin = ask.req.headers.origin;
|
||||
if (origin) {
|
||||
if (allowedOrigin.includes(origin)) {
|
||||
ask.res.setHeader('Access-Control-Allow-Origin', origin);
|
||||
} else {
|
||||
ask.res.setHeader('Access-Control-Allow-Origin', 'null');
|
||||
end({err: 'Disallowed'});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let url;
|
||||
try {
|
||||
url = nodeUrl.parse(data.url);
|
||||
@@ -122,8 +127,10 @@ function githubLicense (user, repo) {
|
||||
});
|
||||
}
|
||||
|
||||
function setRoutes (camp) {
|
||||
camp.ajax.on('suggest/v1', suggest);
|
||||
function setRoutes (allowedOrigin, camp) {
|
||||
camp.ajax.on(
|
||||
'suggest/v1',
|
||||
(data, end, ask) => suggest(allowedOrigin, data, end, ask));
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
21
server.js
21
server.js
@@ -95,15 +95,15 @@ const {
|
||||
} = require('./lib/github-provider');
|
||||
|
||||
const serverStartTime = new Date((new Date()).toGMTString());
|
||||
const { githubApiUrl } = config;
|
||||
const githubApiUrl = config.services.github.baseUri;
|
||||
|
||||
const camp = require('camp').start({
|
||||
documentRoot: path.join(__dirname, 'public'),
|
||||
port: config.serverPort,
|
||||
hostname: config.bindAddress,
|
||||
secure: config.secureServer,
|
||||
cert: config.secureServerCert,
|
||||
key: config.secureServerKey,
|
||||
port: config.bind.port,
|
||||
hostname: config.bind.address,
|
||||
secure: config.ssl.isSecure,
|
||||
cert: config.ssl.cert,
|
||||
key: config.ssl.key,
|
||||
});
|
||||
|
||||
function reset() {
|
||||
@@ -123,8 +123,7 @@ module.exports = {
|
||||
stop
|
||||
};
|
||||
|
||||
log('Server is starting up');
|
||||
log(config.frontendUri);
|
||||
log(`Server is starting up: ${config.baseUri}`);
|
||||
|
||||
analytics.load();
|
||||
analytics.scheduleAutosaving();
|
||||
@@ -135,7 +134,7 @@ if (serverSecrets && serverSecrets.gh_client_id) {
|
||||
githubAuth.setRoutes(camp);
|
||||
}
|
||||
|
||||
suggest.setRoutes(camp);
|
||||
suggest.setRoutes(config.cors.allowedOrigin, camp);
|
||||
|
||||
camp.notfound(/\.(svg|png|gif|jpg|json)/, function(query, match, end, request) {
|
||||
var format = match[1];
|
||||
@@ -7518,10 +7517,10 @@ function(data, match, end, ask) {
|
||||
}
|
||||
});
|
||||
|
||||
if (config.frontendRedirectUrl) {
|
||||
if (config.redirectUri) {
|
||||
camp.route(/^\/$/, (data, match, end, ask) => {
|
||||
ask.res.statusCode = 302;
|
||||
ask.res.setHeader('Location', config.frontendRedirectUrl);
|
||||
ask.res.setHeader('Location', config.redirectUri);
|
||||
ask.res.end();
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user