Newer
Older
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Lloyd Hilaiel
committed
const
metrics = require('../metrics.js'),
url = require('url'),
Lloyd Hilaiel
committed
logger = require('../logging.js').logger,
fs = require('fs'),
Lloyd Hilaiel
committed
connect = require('connect'),
und = require('underscore'),
Lloyd Hilaiel
committed
util = require('util'),
httputils = require('../httputils.js'),
Zachary Carter
committed
etagify = require('etagify'),
Shane Tomlinson
committed
secrets = require('../secrets'),
version = require('../version');
Lloyd Hilaiel
committed
require("jwcrypto/lib/algs/rs");
// the underbar decorator to allow getext to extract strings
function _(str) { return str; }
Lloyd Hilaiel
committed
// all templated content, redirects, and renames are handled here.
// anything that is not an api, and not static
const
path = require('path');
Lloyd Hilaiel
committed
const VIEW_PATH = path.join(__dirname, "..", "..", "resources", "views");
// none of our views include dynamic data. all of them should be served
// with reasonable cache headers. This wrapper around rendering handles
// cache headers maximally leveraging the same logic that connect uses
// issue #910
function renderCachableView(req, res, template, options) {
if (config.get('env') !== 'local') {
// allow caching, but require revalidation via ETag
res.etagify();
res.setHeader('Cache-Control', 'public, max-age=0');
} else {
// disable all caching for local dev
res.setHeader('Cache-Control', 'no-store');
}
res.setHeader('Date', new Date().toUTCString());
res.setHeader('Vary', 'Accept-Encoding,Accept-Language');
res.setHeader('Content-Type', 'text/html; charset=utf8');
Shane Tomlinson
committed
options.enable_development_menu = config.get('enable_development_menu');
Shane Tomlinson
committed
// The real version number is not ready until sometime after initial load,
// until it is ready a fake randomly generated string is used. Go get
// the real SHA whenever it is actually needed so that the randomly
// generated SHA is not returned to the user.
options.commit = version();
res.local('util', util);
res.render(template, options);
Lloyd Hilaiel
committed
}
Lloyd Hilaiel
committed
exports.setup = function(app) {
Austin King
committed
// Issue#1353 This is kind of dirty, but this is our last chance
// to fixup headers for an ETag cache hit
// x-frame-options - Allow these to be run within a frame
app.use(function (req, res, next) {
Austin King
committed
if (req.path === '/communication_iframe') {
res.removeHeader('x-frame-options');
Austin King
committed
} else if (req.path === '/relay') {
res.removeHeader('x-frame-options');
}
next();
});
// Caching for dynamic resources
app.use(etagify());
Lloyd Hilaiel
committed
app.set("views", VIEW_PATH);
Lloyd Hilaiel
committed
app.set('view options', {
production: config.get('use_minified_resources')
});
app.get('/include.js', function(req, res, next) {
req.url = "/include_js/include.js";
if (config.get('use_minified_resources') === true) {
req.url = "/production/include.js"
next();
});
app.get('/include.orig.js', function(req, res, next) {
req.url = "/include_js/include.js";
next();
Lloyd Hilaiel
committed
// this should probably be an internal redirect
// as soon as relative paths are figured out.
app.get('/sign_in', function(req, res, next ) {
Lloyd Hilaiel
committed
renderCachableView(req, res, 'dialog.ejs', {
title: _('A Better Way to Sign In'),
Lloyd Hilaiel
committed
layout: 'dialog_layout.ejs',
useJavascript: true,
production: config.get('use_minified_resources')
});
});
app.get('/communication_iframe', function(req, res, next ) {
Lloyd Hilaiel
committed
renderCachableView(req, res, 'communication_iframe.ejs', {
Lloyd Hilaiel
committed
layout: false,
production: config.get('use_minified_resources')
});
});
app.get("/unsupported_dialog", function(req,res) {
renderCachableView(req, res, 'unsupported_dialog.ejs', {
title: _('Unsupported Browser'),
layout: 'dialog_layout.ejs',
useJavascript: false
});
Lloyd Hilaiel
committed
});
Shane Tomlinson
committed
app.get("/cookies_disabled", function(req,res) {
renderCachableView(req, res, 'cookies_disabled.ejs', {
title: _('Cookies Are Disabled'),
layout: 'dialog_layout.ejs',
useJavascript: false
});
Shane Tomlinson
committed
});
Lloyd Hilaiel
committed
// Used for a relay page for communication.
Lloyd Hilaiel
committed
app.get("/relay", function(req, res, next) {
renderCachableView(req, res, 'relay.ejs', {
Lloyd Hilaiel
committed
layout: false,
production: config.get('use_minified_resources')
});
});
app.get("/authenticate_with_primary", function(req,res, next) {
Lloyd Hilaiel
committed
renderCachableView(req, res, 'authenticate_with_primary.ejs', { layout: false });
Lloyd Hilaiel
committed
app.get('/', function(req,res) {
renderCachableView(req, res, 'index.ejs', {title: _('A Better Way to Sign In'), fullpage: true});
Lloyd Hilaiel
committed
});
Shane Tomlinson
committed
app.get("/idp_auth_complete", function(req, res) {
Lloyd Hilaiel
committed
renderCachableView(req, res, 'idp_auth_complete.ejs', {
title: _('Sign In Complete'),
Shane Tomlinson
committed
fullpage: false
});
});
Lloyd Hilaiel
committed
app.get("/forgot", function(req, res) {
res.local('util', util);
renderCachableView(req, res, 'forgot.ejs', {
title: _('Forgot Password'),
fullpage: false,
enable_development_menu: config.get('enable_development_menu')
});
Lloyd Hilaiel
committed
});
app.get("/signup", function(req, res) {
res.header('Location', '/signin');
res.send(301);
});
Lloyd Hilaiel
committed
app.get("/signin", function(req, res) {
renderCachableView(req, res, 'signin.ejs', {title: _('Sign In'), fullpage: false});
Lloyd Hilaiel
committed
});
app.get("/about", function(req, res) {
renderCachableView(req, res, 'about.ejs', {title: _('About'), fullpage: false});
Lloyd Hilaiel
committed
});
app.get("/tos", function(req, res) {
renderCachableView(req, res, 'tos.ejs', {title: _('Terms of Service'), fullpage: false});
Lloyd Hilaiel
committed
});
app.get("/privacy", function(req, res) {
renderCachableView(req, res, 'privacy.ejs', {title: _('Privacy Policy'), fullpage: false});
Lloyd Hilaiel
committed
});
app.get("/verify_email_address", function(req, res) {
res.local('util', util);
renderCachableView(req, res, 'verify_email_address.ejs', {
title: _('Complete Registration'),
fullpage: true,
enable_development_menu: config.get('enable_development_menu')
});
Lloyd Hilaiel
committed
});
Lloyd Hilaiel
committed
// This page can be removed a couple weeks after this code ships into production,
// we're leaving it here to not break outstanding emails
Lloyd Hilaiel
committed
app.get("/add_email_address", function(req,res) {
renderCachableView(req, res, 'confirm.ejs', {title: _('Verify Email Address'), fullpage: false});
Lloyd Hilaiel
committed
});
Shane Tomlinson
committed
app.get("/reset_password", function(req,res) {
renderCachableView(req, res, 'confirm.ejs', {title: _('Reset Password')});
Shane Tomlinson
committed
});
app.get("/confirm", function(req,res) {
renderCachableView(req, res, 'confirm.ejs', {title: _('Confirm Email')});
Shane Tomlinson
committed
});
Lloyd Hilaiel
committed
// serve up testing templates. but NOT in staging or production. see GH-1044
Lloyd Hilaiel
committed
if ([ 'https://login.persona.org', 'https://login.anosrep.org' ].indexOf(config.get('public_url')) === -1) {
Lloyd Hilaiel
committed
// serve test.ejs to /test or /test/ or /test/index.html
Lloyd Hilaiel
committed
app.get(/^\/test\/(?:index.html)?$/, function (req, res) {
res.render('test.ejs', {title: 'Mozilla Persona QUnit Test', layout: false});
Lloyd Hilaiel
committed
});
// l10n test template
Lloyd Hilaiel
committed
var testPath = path.join(__dirname, '..', '..', 'tests', 'i18n_test_templates');
app.get('/i18n_test', function(req, res) {
Lloyd Hilaiel
committed
renderCachableView(req, res, path.join(testPath, 'i18n_test.ejs'), { layout: false, title: 'l10n testing title' });
});
app.get('/i18n_fallback_test', function(req, res) {
Lloyd Hilaiel
committed
renderCachableView(req, res, path.join(testPath, 'i18n_fallback_test.ejs'), { layout: false, title: 'l10n testing title' });
Lloyd Hilaiel
committed
} else {
// this is stage or production, explicitly disable all resources under /test
app.get(/^\/test/, function(req, res) {
httputils.notFound(res, "Cannot " + req.method + " " + req.url);
Lloyd Hilaiel
committed
});
Lloyd Hilaiel
committed
// REDIRECTS
Zachary Carter
committed
const REDIRECTS = {
"/developers" : "https://developer.mozilla.org/docs/persona"
Lloyd Hilaiel
committed
};
// set up all the redirects
// oh my watch out for scope issues on var url - closure time
for (var url in REDIRECTS) {
(function(from,to) {
app.get(from, function(req, res) {
res.redirect(to);
});
})(url, REDIRECTS[url]);
}
Lloyd Hilaiel
committed
try {
const publicKey = secrets.loadPublicKey();
Lloyd Hilaiel
committed
} catch(e){
logger.error("can't read public key, exiting: " + e);
Lloyd Hilaiel
committed
process.nextTick(function() { process.exit(1); });
Lloyd Hilaiel
committed
}
Lloyd Hilaiel
committed
// the public key (This location is DEPRECATED)
Lloyd Hilaiel
committed
app.get("/pk", function(req, res) {
Lloyd Hilaiel
committed
res.json(publicKey.toSimpleObject());
Lloyd Hilaiel
committed
});
Lloyd Hilaiel
committed
// the "declaration of support" style publishing of the public key.
Lloyd Hilaiel
committed
// login.persona.org is a (uh, THE) secondary, it should publish its key
Lloyd Hilaiel
committed
// in a manner that is symmetric with how primaries do. At present,
// the absence of 'provisioning' and 'authentication' keys indicates
// that this is a secondary, and verifiers should only trust
Lloyd Hilaiel
committed
// login.persona.org as a secondary (and anyone else they decide to for
Lloyd Hilaiel
committed
// whatever reason).
app.get("/.well-known/browserid", function(req, res) {
res.json({ 'public-key': publicKey.toSimpleObject() });
Lloyd Hilaiel
committed
});
Lloyd Hilaiel
committed
// now for static redirects for cach busting - issue #225
var versionRegex = /^(\/production\/[a-zA-Z\-]+)_v[a-zA-Z0-9]{7}(\.(?:css|js))$/;
app.use(function(req, res, next) {
var m = versionRegex.exec(req.url);
if (m) {
var newURL = m[1] + m[2];
logger.debug('internal redirect of ' + req.url + ' to ' + newURL);
req.url = newURL;
}
next();
});