Realms #3854
Realms #3854
Changes from 5 commits
f6e2b48
2dda3ca
7d81daa
8c7b0d9
a76701f
a38a69f
527c4f5
28152c7
bc5c8a9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"realm": ["http://localhost:10001", "http://127.0.0.1:10001"] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
/* 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/. */ | ||
|
||
const fs = require('fs'); | ||
|
||
const request = require('./request'); | ||
const logger = require('./logging').logger; | ||
const util = require('util'); | ||
const validate = require('./validate'); | ||
|
||
const WELL_KNOWN_PATH = '/.well-known/browserid-realm'; | ||
|
||
const SHIMMED_REALMS = {}; | ||
|
||
// Support for "shimmed realms" for local development. | ||
// CSV values: | ||
// <realm>|<browserid-realm filepath>, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This took me a while to figure out how to do from bash command line: SHIMMED_REALMS=127.0.0.1\|example/rp/.well-known/browserid-realm npm start Can you add a comment about how to make it so? This info should probably go into the front end wiki as well. |
||
if (process.env.SHIMMED_REALMS) { | ||
process.env.SHIMMED_REALMS.split(',').forEach(function(shim) { | ||
var parts = shim.split('|'); | ||
SHIMMED_REALMS[parts[0]] = parts[1]; // realm name, filepath | ||
logger.info("shimmed realm info for " + parts[0]); | ||
}); | ||
} | ||
|
||
|
||
function isJsonType(res) { | ||
var contentType = res.headers['content-type']; | ||
return contentType && contentType.indexOf('application/json') === 0; | ||
} | ||
|
||
function onRealmInfo(err, body, callback) { | ||
if (err) { | ||
// just pass err | ||
} else if (!(body && body.realm)) { | ||
err = new Error('missing realm json'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it make the code clearer to use these as guard clauses and immediately return: if (err) return callback(err);
if (!(body && body.realm)) return callback(new Error('missing realm json'));
... |
||
} else if (!util.isArray(body.realm)) { | ||
err = new Error('realm property is not an array'); | ||
} else { | ||
body.realm = body.realm.filter(function(value) { | ||
try { | ||
validate.is.origin(value); | ||
return true; | ||
} catch(err) { | ||
return false; | ||
} | ||
}); | ||
} | ||
callback(err, body); | ||
} | ||
|
||
function loadShimmedRealm(realm, callback) { | ||
fs.readFile(SHIMMED_REALMS[realm], function(err, str) { | ||
var body; | ||
if (err) { | ||
logger.debug(err); // io error? | ||
} else { | ||
try { | ||
body = JSON.parse(str); | ||
} catch (jsonErr) { | ||
err = jsonErr; | ||
} | ||
} | ||
onRealmInfo(err, body, callback); | ||
}); | ||
} | ||
|
||
exports.checkSupport = function realmSupport(realm, callback) { | ||
if (SHIMMED_REALMS[realm]) { | ||
loadShimmedRealm(realm, callback); | ||
} else { | ||
request('https://' + realm + WELL_KNOWN_PATH, { json: true }, function onRequest(err, res, body) { | ||
if (err) { | ||
logger.debug(err); | ||
} else if (!isJsonType(res)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same comment about guard clauses here. Secondly, if onRealmInfo just calls |
||
// strict here so that file isn't added to site by mistake. admin | ||
// must want this file to exist by explicitly setting headers | ||
err = new Error("content-type was not application/json"); | ||
body = null; | ||
} | ||
|
||
onRealmInfo(err, body, callback); | ||
}); | ||
} | ||
}; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
const request = require('request'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i dearly wanted to fix up There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add an issue for it! |
||
const config = require('./configuration'); | ||
|
||
const REQUEST_TIMEOUT = config.get('declaration_of_support_timeout_ms'); | ||
const HTTP_PROXY = config.has('http_proxy') ? config.get('http_proxy') : null; | ||
|
||
module.exports = function browserid_request(url, options, callback) { | ||
if (typeof options === 'function') { | ||
callback = options; | ||
options = {}; | ||
} | ||
|
||
if (HTTP_PROXY) { | ||
options.proxy = { | ||
protocol: 'http:', | ||
host: HTTP_PROXY.host, | ||
port: HTTP_PROXY.port | ||
}; | ||
} | ||
|
||
if (!('timeout' in options)) { | ||
options.timeout = REQUEST_TIMEOUT; | ||
} | ||
|
||
options.strictSSL = true; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah yes, it looks like declaration of support must be SSL. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, it doesn't have to be. This option is only used for HTTPS requests. But considering we pretty much always use an outgoing proxy, it's likely never actually needed. Still, doesn't hurt, and is safer. |
||
|
||
request(url, options, callback); | ||
}; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,7 @@ httputils = require('./httputils.js'), | |
check = require('validator').check; | ||
|
||
var hostnameRegex = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/; | ||
var hostRegex = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])(:\d{1,5})?$/; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Head explosion. What are the differences between this and hostnameRegex? Both allow IP addresses, both allow domain names. It looks like hostRegex allows a port, if that is so, can you add a comment? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That reminds me, is an RP able to specify the port the realm's support document is located on? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now that I think about it, it doesn't really make sense, since https is required. I added this mostly to allow local testing, but now with shimmed realms, it's not needed. I'll just use the |
||
|
||
var types = { | ||
email: function(x) { | ||
|
@@ -48,6 +49,9 @@ var types = { | |
hostname: function(x) { | ||
check(x).is(hostnameRegex); | ||
}, | ||
host: function(x) { | ||
check(x).is(hostRegex); | ||
}, | ||
origin: function(x) { | ||
/* origin regex | ||
/^ // beginning | ||
|
@@ -143,3 +147,5 @@ module.exports = function (params) { | |
next(); | ||
}; | ||
}; | ||
|
||
module.exports.is = types; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
/* 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/. */ | ||
const realm = require('../realm'); | ||
const logger = require('../logging').logger; | ||
|
||
exports.method = 'get'; | ||
exports.writes_db = false; | ||
exports.authed = false; | ||
exports.args = { | ||
realm: 'host' | ||
}; | ||
exports.i18n = false; | ||
|
||
exports.process = function realm_info(req, res) { | ||
var domain = req.params.realm.toLowerCase(); | ||
realm.checkSupport(domain, function onRealm(err, body) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure whether this comment is better here or in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ideally, we could let squid handle caching this file, just as we do when requesting the IdP support document. possible @gene1wood ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @seanmonstar The squid proxy server will respect whatever caching directives it receives from the target site in the HTTP headers that come with the /.well-known/browserid-realm document. If the target asserts long cache times, the squid proxy will respect that and not make an outbound request. |
||
var json = {}; | ||
if (err) { | ||
logger.info('"' + domain + '" realm support is misconfigured: ' + err); | ||
json.realm = []; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IIUC, the intent here is to not block users from signing in to a site if the support document is misconfigured? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. correct. i'll add a comment say as much |
||
} else { | ||
json.realm = body.realm; | ||
} | ||
|
||
res.json(json); | ||
}); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will this also require changes to our production proxy?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Possibly, @gene1wood or @jrgm?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@shane-tomlinson Will this change result in persona initiating outbound connections out to the internet to new destinations (destinations other than sites' well-known/.browserid files and identity bridge endpoints [yahoo, gmail, etc])?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@gene1wood - indeed, the new outbound request will search for realm files at <rp_location>/.well-known/browserid-realm
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Proxy server support added here mozilla/identity-ops@e6e2931
When QA wants to see this code in stage they'll need to request that new proxy server AMIs be built with this new code in the process. (cc @jrgm)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent, thanks @gene1wood