Merge pull request 'configurable-scheme' (#164) from aspensmonster/keyoxide-web:configurable-scheme into dev

Reviewed-on: https://codeberg.org/keyoxide/keyoxide-web/pulls/164
This commit is contained in:
Yarmo Mackenbach 2023-09-15 13:18:57 +00:00
commit 1dc3a08ccc
8 changed files with 172 additions and 24 deletions

View file

@ -30,6 +30,7 @@
"chai": "^4.3.6", "chai": "^4.3.6",
"copy-webpack-plugin": "^11.0.0", "copy-webpack-plugin": "^11.0.0",
"css-loader": "^6.6.0", "css-loader": "^6.6.0",
"esmock": "^2.5.0",
"license-check-and-add": "^4.0.5", "license-check-and-add": "^4.0.5",
"mini-css-extract-plugin": "^2.5.3", "mini-css-extract-plugin": "^2.5.3",
"mocha": "^10.1.0", "mocha": "^10.1.0",

View file

@ -48,6 +48,7 @@ app.set('env', process.env.NODE_ENV || 'production')
app.engine('pug', pug.__express).set('view engine', 'pug') app.engine('pug', pug.__express).set('view engine', 'pug')
app.set('port', process.env.PORT || 3000) app.set('port', process.env.PORT || 3000)
app.set('domain', process.env.DOMAIN) app.set('domain', process.env.DOMAIN)
app.set('scheme', process.env.SCHEME || 'https')
app.set('keyoxide_version', packageData.version) app.set('keyoxide_version', packageData.version)
app.set('onion_url', process.env.ONION_URL) app.set('onion_url', process.env.ONION_URL)
@ -65,7 +66,8 @@ if (app.get('onion_url')) {
} }
app.use(stringReplace({ app.use(stringReplace({
PLACEHOLDER__PROXY_HOSTNAME: process.env.PROXY_HOSTNAME || process.env.DOMAIN || 'null' PLACEHOLDER__PROXY_HOSTNAME: process.env.PROXY_HOSTNAME || process.env.DOMAIN || 'null',
PLACEHOLDER__PROXY_SCHEME: process.env.PROXY_SCHEME || process.env.SCHEME || 'https'
}, { }, {
contentTypeFilterRegexp: /application\/javascript/ contentTypeFilterRegexp: /application\/javascript/
})) }))

View file

@ -38,7 +38,7 @@ const generateAspeProfile = async (id) => {
return doipjs.asp.fetchASPE(id) return doipjs.asp.fetchASPE(id)
.then(profile => { .then(profile => {
profile.addVerifier('keyoxide', `https://${process.env.DOMAIN}/${id}`) profile.addVerifier('keyoxide', `${getScheme()}://${process.env.DOMAIN}/${id}`)
profile = processAspProfile(profile) profile = processAspProfile(profile)
return profile return profile
}) })
@ -58,7 +58,7 @@ const generateWKDProfile = async (id) => {
return fetchWKD(id) return fetchWKD(id)
.then(async profile => { .then(async profile => {
profile.addVerifier('keyoxide', `https://${process.env.DOMAIN}/wkd/${id}`) profile.addVerifier('keyoxide', `${getScheme()}://${process.env.DOMAIN}/wkd/${id}`)
profile = processOpenPgpProfile(profile) profile = processOpenPgpProfile(profile)
logger.debug('Generating a WKD profile', logger.debug('Generating a WKD profile',
@ -84,9 +84,9 @@ const generateHKPProfile = async (id, keyserverDomain) => {
.then(async profile => { .then(async profile => {
let keyoxideUrl let keyoxideUrl
if (!keyserverDomain || keyserverDomain === 'keys.openpgp.org') { if (!keyserverDomain || keyserverDomain === 'keys.openpgp.org') {
keyoxideUrl = `https://${process.env.DOMAIN}/hkp/${id}` keyoxideUrl = `${getScheme()}://${process.env.DOMAIN}/hkp/${id}`
} else { } else {
keyoxideUrl = `https://${process.env.DOMAIN}/hkp/${keyserverDomain}/${id}` keyoxideUrl = `${getScheme()}://${process.env.DOMAIN}/hkp/${keyserverDomain}/${id}`
} }
profile.addVerifier('keyoxide', keyoxideUrl) profile.addVerifier('keyoxide', keyoxideUrl)
@ -168,7 +168,7 @@ const generateKeybaseProfile = async (username, fingerprint) => {
return fetchKeybase(username, fingerprint) return fetchKeybase(username, fingerprint)
.then(async profile => { .then(async profile => {
profile.addVerifier('keyoxide', `https://${process.env.DOMAIN}/keybase/${username}/${fingerprint}`) profile.addVerifier('keyoxide', `${getScheme()}://${process.env.DOMAIN}/keybase/${username}/${fingerprint}`)
profile = processOpenPgpProfile(profile) profile = processOpenPgpProfile(profile)
logger.debug('Generating a Keybase profile', logger.debug('Generating a Keybase profile',
@ -254,6 +254,14 @@ const processOpenPgpProfile = async (/** @type {import('doipjs').Profile */ prof
return profile return profile
} }
const getScheme = () => {
return process.env.PROXY_SCHEME
? process.env.PROXY_SCHEME
: process.env.SCHEME
? process.env.SCHEME
: 'https'
}
export { generateAspeProfile } export { generateAspeProfile }
export { generateWKDProfile } export { generateWKDProfile }
export { generateHKPProfile } export { generateHKPProfile }

View file

@ -49,7 +49,8 @@ export class Claim extends HTMLElement {
await claim.verify({ await claim.verify({
proxy: { proxy: {
policy: 'adaptive', policy: 'adaptive',
hostname: 'PLACEHOLDER__PROXY_HOSTNAME' hostname: 'PLACEHOLDER__PROXY_HOSTNAME',
scheme: 'PLACEHOLDER__PROXY_SCHEME'
} }
}); });
this.setAttribute('data-claim', JSON.stringify(claim)); this.setAttribute('data-claim', JSON.stringify(claim));
@ -182,7 +183,7 @@ export class Claim extends HTMLElement {
const subsection_info_text = subsection_info.appendChild(document.createElement('div')); const subsection_info_text = subsection_info.appendChild(document.createElement('div'));
const result_proxyUsed = subsection_info_text.appendChild(document.createElement('p')); const result_proxyUsed = subsection_info_text.appendChild(document.createElement('p'));
result_proxyUsed.innerHTML = `A proxy was used to fetch the proof: <a href="https://PLACEHOLDER__PROXY_HOSTNAME" aria-label="Link to proxy server">PLACEHOLDER__PROXY_HOSTNAME</a>`; result_proxyUsed.innerHTML = `A proxy was used to fetch the proof: <a href="PLACEHOLDER__PROXY_SCHEME://PLACEHOLDER__PROXY_HOSTNAME" aria-label="Link to proxy server">PLACEHOLDER__PROXY_HOSTNAME</a>`;
} }
// TODO Display errors // TODO Display errors
@ -207,4 +208,4 @@ export class Claim extends HTMLElement {
// }); // });
// } // }
} }
} }

View file

@ -46,19 +46,20 @@ export async function computeWKDLocalPart(localPart) {
// Generate Keyoxide profile URL // Generate Keyoxide profile URL
export async function generateProfileURL(data) { export async function generateProfileURL(data) {
let hostname = data.hostname || window.location.hostname; let hostname = data.hostname || window.location.hostname;
let scheme = data.scheme || window.location.protocol.slice(0,-1);
if (data.input == "") { if (data.input == "") {
return "Waiting for input…"; return "Waiting for input…";
} }
switch (data.source) { switch (data.source) {
case "wkd": case "wkd":
return `https://${hostname}/${data.input}`; return `${scheme}://${hostname}/${data.input}`;
break; break;
case "hkp": case "hkp":
if (/.*@.*\..*/.test(data.input)) { if (/.*@.*\..*/.test(data.input)) {
return `https://${hostname}/hkp/${data.input}`; return `${scheme}://${hostname}/hkp/${data.input}`;
} else { } else {
return `https://${hostname}/${data.input}`; return `${scheme}://${hostname}/${data.input}`;
} }
break; break;
case "keybase": case "keybase":
@ -67,7 +68,7 @@ export async function generateProfileURL(data) {
return "Incorrect Keybase public key URL."; return "Incorrect Keybase public key URL.";
} }
const match = data.input.match(re); const match = data.input.match(re);
return `https://${hostname}/keybase/${match[1]}/${match[2]}`; return `${scheme}://${hostname}/keybase/${match[1]}/${match[2]}`;
break; break;
} }
} }
@ -229,4 +230,4 @@ export async function verifyBcryptHash(input, hash) {
} catch (_) { } catch (_) {
return false; return false;
} }
} }

View file

@ -66,38 +66,78 @@ describe('browser', function () {
}) })
}) })
describe('generateProfileURL()', function () { describe('generateProfileURL()', function () {
it('should handle a WKD URL', async function () { it('should handle a https WKD URL', async function () {
const local = await utils.generateProfileURL({ const local = await utils.generateProfileURL({
source: 'wkd', source: 'wkd',
input: 'test@doip.rocks', input: 'test@doip.rocks',
hostname: 'keyoxide.instance' hostname: 'keyoxide.instance',
scheme: 'https'
}) })
local.should.equal('https://keyoxide.instance/test@doip.rocks') local.should.equal('https://keyoxide.instance/test@doip.rocks')
}) })
it('should handle a HKP+email URL', async function () { it('should handle a http WKD URL', async function () {
const local = await utils.generateProfileURL({
source: 'wkd',
input: 'test@doip.rocks',
hostname: 'keyoxide.instance',
scheme: 'http'
})
local.should.equal('http://keyoxide.instance/test@doip.rocks')
})
it('should handle a https HKP+email URL', async function () {
const local = await utils.generateProfileURL({ const local = await utils.generateProfileURL({
source: 'hkp', source: 'hkp',
input: 'test@doip.rocks', input: 'test@doip.rocks',
hostname: 'keyoxide.instance' hostname: 'keyoxide.instance',
scheme: 'https'
}) })
local.should.equal('https://keyoxide.instance/hkp/test@doip.rocks') local.should.equal('https://keyoxide.instance/hkp/test@doip.rocks')
}) })
it('should handle a HKP+fingerprint URL', async function () { it('should handle a http HKP+email URL', async function () {
const local = await utils.generateProfileURL({
source: 'hkp',
input: 'test@doip.rocks',
hostname: 'keyoxide.instance',
scheme: 'http'
})
local.should.equal('http://keyoxide.instance/hkp/test@doip.rocks')
})
it('should handle a https HKP+fingerprint URL', async function () {
const local = await utils.generateProfileURL({ const local = await utils.generateProfileURL({
source: 'hkp', source: 'hkp',
input: '3637202523E7C1309AB79E99EF2DC5827B445F4B', input: '3637202523E7C1309AB79E99EF2DC5827B445F4B',
hostname: 'keyoxide.instance' hostname: 'keyoxide.instance',
scheme: 'https'
}) })
local.should.equal('https://keyoxide.instance/3637202523E7C1309AB79E99EF2DC5827B445F4B') local.should.equal('https://keyoxide.instance/3637202523E7C1309AB79E99EF2DC5827B445F4B')
}) })
it('should handle a keybase URL', async function () { it('should handle a http HKP+fingerprint URL', async function () {
const local = await utils.generateProfileURL({
source: 'hkp',
input: '3637202523E7C1309AB79E99EF2DC5827B445F4B',
hostname: 'keyoxide.instance',
scheme: 'http'
})
local.should.equal('http://keyoxide.instance/3637202523E7C1309AB79E99EF2DC5827B445F4B')
})
it('should handle a https keybase URL', async function () {
const local = await utils.generateProfileURL({ const local = await utils.generateProfileURL({
source: 'keybase', source: 'keybase',
input: 'https://keybase.io/doip/pgp_keys.asc?fingerprint=3637202523E7C1309AB79E99EF2DC5827B445F4B', input: 'https://keybase.io/doip/pgp_keys.asc?fingerprint=3637202523E7C1309AB79E99EF2DC5827B445F4B',
hostname: 'keyoxide.instance' hostname: 'keyoxide.instance',
scheme: 'https'
}) })
local.should.equal('https://keyoxide.instance/keybase/doip/3637202523E7C1309AB79E99EF2DC5827B445F4B') local.should.equal('https://keyoxide.instance/keybase/doip/3637202523E7C1309AB79E99EF2DC5827B445F4B')
}) })
it('should handle a http keybase URL', async function () {
const local = await utils.generateProfileURL({
source: 'keybase',
input: 'https://keybase.io/doip/pgp_keys.asc?fingerprint=3637202523E7C1309AB79E99EF2DC5827B445F4B',
hostname: 'keyoxide.instance',
scheme: 'http'
})
local.should.equal('http://keyoxide.instance/keybase/doip/3637202523E7C1309AB79E99EF2DC5827B445F4B')
})
}) })
}) })
}) })

View file

@ -1,6 +1,11 @@
import 'chai/register-should.js' import 'chai/register-should.js'
import esmock from 'esmock'
import * as doipjs from 'doipjs'
import * as utils from '../src/server/utils.js' import * as utils from '../src/server/utils.js'
const _env = Object.assign({},process.env)
describe('server', function () { describe('server', function () {
describe('utils', function () { describe('utils', function () {
describe('computeWKDLocalPart()', function () { describe('computeWKDLocalPart()', function () {
@ -26,4 +31,89 @@ describe('server', function () {
}) })
}) })
}) })
})
// NOTE: This is necessarily brittle. If these tests fail
// in the future, start looking here for what new behaviour
// in the implementation is or isn't getting mocked
// appropriately.
describe('index', function () {
describe('generateHKPProfile()', function() {
let index;
let fingerprint;
/** @type {import('doipjs').Profile */
let profile;
this.beforeEach(async () => {
// Common arrangement pieces that don't change per test
fingerprint = '79895B2E0F87503F1DDE80B649765D7F0DDD9BD5'
process.env.DOMAIN = "keyoxide.org"
const persona = new doipjs.Persona("test", [new doipjs.Claim('dns:domain.tld?type=TXT')])
profile = new doipjs.Profile(doipjs.enums.ProfileType.OPENPGP, fingerprint, [persona])
// mock the appropriate pieces of our dependencies so we
// can test just the `keyoxide.url` return value.
index = await esmock('../src/server/index.js', {
'../src/server/openpgpProfiles.js': {
fetchHKP: () => {
return Promise.resolve(profile)
}
},
'libravatar': {
get_avatar_url: () => {
return "example.org/avatar.png"
}
}
})
})
this.afterEach(() => {
process.env = _env
})
it('should handle implicit scheme for keyoxide URL', async function () {
// Arrange
// no setting process.env.SCHEME
// Act
const local = await index.generateHKPProfile(fingerprint)
// Assert
local.verifiers[0].url.should.equal(`https://keyoxide.org/hkp/${fingerprint}`)
})
it('should handle explicit http scheme for keyoxide URL', async function () {
// Arrange
process.env.SCHEME = "http"
// Act
const local = await index.generateHKPProfile(fingerprint)
// Assert
local.verifiers[0].url.should.equal(`http://keyoxide.org/hkp/${fingerprint}`)
})
it('should handle explicit https scheme for keyoxide URL', async function () {
// Arrange
process.env.SCHEME = "https"
// Act
const local = await index.generateHKPProfile(fingerprint)
// Assert
local.verifiers[0].url.should.equal(`https://keyoxide.org/hkp/${fingerprint}`)
})
})
})
})

View file

@ -2216,6 +2216,11 @@ eslint@^8.41.0:
strip-json-comments "^3.1.0" strip-json-comments "^3.1.0"
text-table "^0.2.0" text-table "^0.2.0"
esmock@^2.5.0:
version "2.5.1"
resolved "https://registry.yarnpkg.com/esmock/-/esmock-2.5.1.tgz#cef05c9cd23c46edbfb2e0add34466f6c52e37f6"
integrity sha512-3pu+ri9kNrRjahR8c+FWXphK3xpKrgBwLHu+A+Xj3vw84fGsScWY3SWTH1v5nSiheYQAdlz5Ny+a319tlle1mA==
espree@^9.6.0: espree@^9.6.0:
version "9.6.0" version "9.6.0"
resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.0.tgz#80869754b1c6560f32e3b6929194a3fe07c5b82f" resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.0.tgz#80869754b1c6560f32e3b6929194a3fe07c5b82f"