diff --git a/examples/fetch-key-hkp.js b/examples/fetch-key-hkp.js index b8aed23..2875b0d 100644 --- a/examples/fetch-key-hkp.js +++ b/examples/fetch-key-hkp.js @@ -1,4 +1,4 @@ -const doip = require('../src') +import * as doip from '../src/index.js' const main = async () => { // Fetch the key using HKP diff --git a/examples/fetch-key-plaintext.js b/examples/fetch-key-plaintext.js index b09e81d..1790179 100644 --- a/examples/fetch-key-plaintext.js +++ b/examples/fetch-key-plaintext.js @@ -1,4 +1,4 @@ -const doip = require('../src') +import * as doip from '../src/index.js' const main = async () => { // Obtain the plaintext public key diff --git a/examples/fetch-key-wkd.js b/examples/fetch-key-wkd.js index 91a36ad..2fdfe1e 100644 --- a/examples/fetch-key-wkd.js +++ b/examples/fetch-key-wkd.js @@ -1,4 +1,4 @@ -const doip = require('../src') +import * as doip from '../src/index.js' const main = async () => { // Fetch the key using WKD diff --git a/examples/fetch-profile-aspe.js b/examples/fetch-profile-aspe.js index 9b26b6c..a7474f7 100644 --- a/examples/fetch-profile-aspe.js +++ b/examples/fetch-profile-aspe.js @@ -1,4 +1,4 @@ -const doip = require('../src') +import * as doip from '../src/index.js' const main = async () => { // Fetch the profile using ASPE diff --git a/examples/process-signature-profile.js b/examples/process-signature-profile.js index e8fad19..dc6af9c 100644 --- a/examples/process-signature-profile.js +++ b/examples/process-signature-profile.js @@ -1,4 +1,4 @@ -const doip = require('../src') +import * as doip from '../src/index.js' const signature = `-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA512 diff --git a/examples/verify-claim.js b/examples/verify-claim.js index 6f2613c..aa3d584 100644 --- a/examples/verify-claim.js +++ b/examples/verify-claim.js @@ -1,4 +1,4 @@ -const doip = require('../src') +import * as doip from '../src/index.js' const main = async () => { // Generate the claim diff --git a/package.json b/package.json index 336a6ce..eff21de 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "doipjs", "version": "0.19.0", "description": "Decentralized Online Identity Proofs library in Node.js", + "type": "module", "main": "./src/index.js", "packageManager": "yarn@1.22.19", "dependencies": { diff --git a/src/asp.js b/src/asp.js index 433e5ac..316aa0d 100644 --- a/src/asp.js +++ b/src/asp.js @@ -13,12 +13,12 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const axios = require('axios').default -const jose = require('jose') -const { base32, base64url } = require('rfc4648') -const Claim = require('./claim') -const Persona = require('./persona') -const Profile = require('./profile') +import axios from 'axios' +import { decodeProtectedHeader, importJWK, compactVerify, calculateJwkThumbprint } from 'jose' +import { base32, base64url } from 'rfc4648' +import { Claim } from './claim.js' +import { Persona } from './persona.js' +import { Profile } from './profile.js' const SupportedCryptoAlg = ['EdDSA', 'ES256', 'ES256K', 'ES384', 'ES512'] @@ -35,7 +35,7 @@ const SupportedCryptoAlg = ['EdDSA', 'ES256', 'ES256K', 'ES384', 'ES512'] * @example * const key = doip.aspe.fetchASPE('aspe:domain.tld:1234567890'); */ -const fetchASPE = async uri => { +export async function fetchASPE (uri) { const re = /aspe:(.*):(.*)/ if (!re.test(uri)) { @@ -78,12 +78,12 @@ const fetchASPE = async uri => { * @example * const key = doip.aspe.parseProfileJws('...'); */ -const parseProfileJws = async (profileJws, uri) => { +export async function parseProfileJws (profileJws, uri) { const matches = uri.match(/aspe:(.*):(.*)/) const localPart = matches[2].toUpperCase() // Decode the headers - const protectedHeader = jose.decodeProtectedHeader(profileJws) + const protectedHeader = decodeProtectedHeader(profileJws) // Extract the JWK if (!SupportedCryptoAlg.includes(protectedHeader.alg)) { @@ -95,7 +95,7 @@ const parseProfileJws = async (profileJws, uri) => { if (!protectedHeader.jwk) { throw new Error('Invalid profile JWS: missing key') } - const publicKey = await jose.importJWK(protectedHeader.jwk, protectedHeader.alg) + const publicKey = await importJWK(protectedHeader.jwk, protectedHeader.alg) // Compute and verify the fingerprint const fp = await computeJwkFingerprint(protectedHeader.jwk) @@ -108,7 +108,7 @@ const parseProfileJws = async (profileJws, uri) => { } // Decode the payload - const { payload } = await jose.compactVerify(profileJws, publicKey) + const { payload } = await compactVerify(profileJws, publicKey) const payloadJson = JSON.parse(new TextDecoder().decode(payload)) // Verify the payload @@ -139,16 +139,13 @@ const parseProfileJws = async (profileJws, uri) => { /** * Compute the fingerprint for JWK keys * @function - * @param {jose.JWK} key + * @param {import('jose').JWK} key * @returns {Promise} */ -const computeJwkFingerprint = async key => { - const thumbprint = await jose.calculateJwkThumbprint(key, 'sha512') +export async function computeJwkFingerprint (key) { + const thumbprint = await calculateJwkThumbprint(key, 'sha512') const fingerprintBytes = base64url.parse(thumbprint, { loose: true }).slice(0, 16) const fingerprint = base32.stringify(fingerprintBytes, { pad: false }) return fingerprint } - -exports.fetchASPE = fetchASPE -exports.parseProfileJws = parseProfileJws diff --git a/src/claim.js b/src/claim.js index 1a9bd6d..494a484 100644 --- a/src/claim.js +++ b/src/claim.js @@ -13,14 +13,14 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const validator = require('validator').default -const validUrl = require('valid-url') -const mergeOptions = require('merge-options') -const proofs = require('./proofs') -const verifications = require('./verifications') -const claimDefinitions = require('./claimDefinitions') -const defaults = require('./defaults') -const E = require('./enums') +import isAlphanumeric from 'validator/lib/isAlphanumeric.js' +import { isUri } from 'valid-url' +import mergeOptions from 'merge-options' +import { fetch } from './proofs.js' +import { run } from './verifications.js' +import { list, data as _data } from './claimDefinitions/index.js' +import { opts as _opts } from './defaults.js' +import { ClaimStatus } from './enums.js' /** * @class @@ -31,7 +31,7 @@ const E = require('./enums') * @property {Array} matches - The claim definitions matched against the URI * @property {object} verification - The result of the verification process */ -class Claim { +export class Claim { /** * Initialize a Claim object * @constructor @@ -63,14 +63,15 @@ class Claim { } // Verify validity of URI - if (uri && !validUrl.isUri(uri)) { + if (uri && !isUri(uri)) { throw new Error('Invalid URI') } // Verify validity of fingerprint if (fingerprint) { try { - validator.isAlphanumeric(fingerprint) + // @ts-ignore + isAlphanumeric.default(fingerprint) } catch (err) { throw new Error('Invalid fingerprint') } @@ -78,7 +79,7 @@ class Claim { this._uri = uri || '' this._fingerprint = fingerprint || '' - this._status = E.ClaimStatus.INIT + this._status = ClaimStatus.INIT this._matches = [] this._verification = {} } @@ -96,27 +97,27 @@ class Claim { } get matches () { - if (this._status === E.ClaimStatus.INIT) { + if (this._status === ClaimStatus.INIT) { throw new Error('This claim has not yet been matched') } return this._matches } get verification () { - if (this._status !== E.ClaimStatus.VERIFIED) { + if (this._status !== ClaimStatus.VERIFIED) { throw new Error('This claim has not yet been verified') } return this._verification } set uri (uri) { - if (this._status !== E.ClaimStatus.INIT) { + if (this._status !== ClaimStatus.INIT) { throw new Error( 'Cannot change the URI, this claim has already been matched' ) } // Verify validity of URI - if (uri.length > 0 && !validUrl.isUri(uri)) { + if (uri.length > 0 && !isUri(uri)) { throw new Error('The URI was invalid') } // Remove leading and trailing spaces @@ -126,7 +127,7 @@ class Claim { } set fingerprint (fingerprint) { - if (this._status === E.ClaimStatus.VERIFIED) { + if (this._status === ClaimStatus.VERIFIED) { throw new Error( 'Cannot change the fingerprint, this claim has already been verified' ) @@ -151,17 +152,17 @@ class Claim { * @function */ match () { - if (this._status !== E.ClaimStatus.INIT) { + if (this._status !== ClaimStatus.INIT) { throw new Error('This claim was already matched') } - if (this._uri.length === 0 || !validUrl.isUri(this._uri)) { + if (this._uri.length === 0 || !isUri(this._uri)) { throw new Error('This claim has no URI') } this._matches = [] - claimDefinitions.list.every((name, i) => { - const def = claimDefinitions.data[name] + list.every((name, i) => { + const def = _data[name] // If the candidate is invalid, continue matching if (!def.reURI.test(this._uri)) { @@ -187,7 +188,7 @@ class Claim { return true }) - this._status = E.ClaimStatus.MATCHED + this._status = ClaimStatus.MATCHED } /** @@ -200,10 +201,10 @@ class Claim { * @param {object} [opts] - Options for proxy, fetchers */ async verify (opts) { - if (this._status === E.ClaimStatus.INIT) { + if (this._status === ClaimStatus.INIT) { throw new Error('This claim has not yet been matched') } - if (this._status === E.ClaimStatus.VERIFIED) { + if (this._status === ClaimStatus.VERIFIED) { throw new Error('This claim has already been verified') } if (this._fingerprint.length === 0) { @@ -211,7 +212,7 @@ class Claim { } // Handle options - opts = mergeOptions(defaults.opts, opts || {}) + opts = mergeOptions(_opts, opts || {}) // If there are no matches if (this._matches.length === 0) { @@ -232,14 +233,14 @@ class Claim { let proofFetchError try { - proofData = await proofs.fetch(claimData, opts) + proofData = await fetch(claimData, opts) } catch (err) { proofFetchError = err } if (proofData) { // Run the verification process - verificationResult = await verifications.run( + verificationResult = await run( proofData.result, claimData, this._fingerprint @@ -250,7 +251,7 @@ class Claim { } // Post process the data - const def = claimDefinitions.data[claimData.serviceprovider.name] + const def = _data[claimData.serviceprovider.name] if (def.functions?.postprocess) { try { ({ claimData, proofData } = def.functions.postprocess(claimData, proofData)) @@ -289,7 +290,7 @@ class Claim { errors: [] } - this._status = E.ClaimStatus.VERIFIED + this._status = ClaimStatus.VERIFIED } /** @@ -300,7 +301,7 @@ class Claim { * @returns {boolean} */ isAmbiguous () { - if (this._status === E.ClaimStatus.INIT) { + if (this._status === ClaimStatus.INIT) { throw new Error('The claim has not been matched yet') } if (this._matches.length === 0) { @@ -326,5 +327,3 @@ class Claim { } } } - -module.exports = Claim diff --git a/src/claimDefinitions/activitypub.js b/src/claimDefinitions/activitypub.js index 202569e..311b588 100644 --- a/src/claimDefinitions/activitypub.js +++ b/src/claimDefinitions/activitypub.js @@ -13,11 +13,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const E = require('../enums') +import * as E from '../enums.js' -const reURI = /^https:\/\/(.*)\/?/ +export const reURI = /^https:\/\/(.*)\/?/ -const processURI = (uri) => { +/** + * @function + * @param {string} uri + */ +export function processURI (uri) { return { serviceprovider: { type: 'web', @@ -66,14 +70,14 @@ const processURI = (uri) => { } } -const functions = { +export const functions = { postprocess: (claimData, proofData) => { claimData.profile.display = `@${proofData.result.preferredUsername}@${new URL(proofData.result.url).hostname}` return { claimData, proofData } } } -const tests = [ +export const tests = [ { uri: 'https://domain.org', shouldMatch: true @@ -107,8 +111,3 @@ const tests = [ shouldMatch: false } ] - -exports.reURI = reURI -exports.processURI = processURI -exports.functions = functions -exports.tests = tests diff --git a/src/claimDefinitions/discourse.js b/src/claimDefinitions/discourse.js index 99fad8c..c1ca992 100644 --- a/src/claimDefinitions/discourse.js +++ b/src/claimDefinitions/discourse.js @@ -13,11 +13,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const E = require('../enums') +import * as E from '../enums.js' -const reURI = /^https:\/\/(.*)\/u\/(.*)\/?/ +export const reURI = /^https:\/\/(.*)\/u\/(.*)\/?/ -const processURI = (uri) => { +/** + * @function + * @param {string} uri + */ +export function processURI (uri) { const match = uri.match(reURI) return { @@ -55,7 +59,7 @@ const processURI = (uri) => { } } -const tests = [ +export const tests = [ { uri: 'https://domain.org/u/alice', shouldMatch: true @@ -69,7 +73,3 @@ const tests = [ shouldMatch: false } ] - -exports.reURI = reURI -exports.processURI = processURI -exports.tests = tests diff --git a/src/claimDefinitions/dns.js b/src/claimDefinitions/dns.js index cc7a418..a6f4a5d 100644 --- a/src/claimDefinitions/dns.js +++ b/src/claimDefinitions/dns.js @@ -13,11 +13,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const E = require('../enums') +import * as E from '../enums.js' -const reURI = /^dns:([a-zA-Z0-9.\-_]*)(?:\?(.*))?/ +export const reURI = /^dns:([a-zA-Z0-9.\-_]*)(?:\?(.*))?/ -const processURI = (uri) => { +/** + * @function + * @param {string} uri + */ +export function processURI (uri) { const match = uri.match(reURI) return { @@ -54,7 +58,7 @@ const processURI = (uri) => { } } -const tests = [ +export const tests = [ { uri: 'dns:domain.org', shouldMatch: true @@ -68,7 +72,3 @@ const tests = [ shouldMatch: false } ] - -exports.reURI = reURI -exports.processURI = processURI -exports.tests = tests diff --git a/src/claimDefinitions/forem.js b/src/claimDefinitions/forem.js index d9c0491..97e3088 100644 --- a/src/claimDefinitions/forem.js +++ b/src/claimDefinitions/forem.js @@ -13,11 +13,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const E = require('../enums') +import * as E from '../enums.js' -const reURI = /^https:\/\/(.*)\/(.*)\/(.*)\/?/ +export const reURI = /^https:\/\/(.*)\/(.*)\/(.*)\/?/ -const processURI = (uri) => { +/** + * @function + * @param {string} uri + */ +export function processURI (uri) { const match = uri.match(reURI) return { @@ -55,7 +59,7 @@ const processURI = (uri) => { } } -const tests = [ +export const tests = [ { uri: 'https://domain.org/alice/post', shouldMatch: true @@ -69,7 +73,3 @@ const tests = [ shouldMatch: false } ] - -exports.reURI = reURI -exports.processURI = processURI -exports.tests = tests diff --git a/src/claimDefinitions/forgejo.js b/src/claimDefinitions/forgejo.js index 55792c1..7f25bf7 100644 --- a/src/claimDefinitions/forgejo.js +++ b/src/claimDefinitions/forgejo.js @@ -13,11 +13,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const E = require('../enums') +import * as E from '../enums.js' -const reURI = /^https:\/\/(.*)\/(.*)\/(.*)\/?/ +export const reURI = /^https:\/\/(.*)\/(.*)\/(.*)\/?/ -const processURI = (uri) => { +/** + * @function + * @param {string} uri + */ +export function processURI (uri) { const match = uri.match(reURI) return { @@ -55,7 +59,7 @@ const processURI = (uri) => { } } -const tests = [ +export const tests = [ { uri: 'https://domain.org/alice/forgejo_proof', shouldMatch: true @@ -73,7 +77,3 @@ const tests = [ shouldMatch: false } ] - -exports.reURI = reURI -exports.processURI = processURI -exports.tests = tests diff --git a/src/claimDefinitions/gitea.js b/src/claimDefinitions/gitea.js index 72a5195..1c593ca 100644 --- a/src/claimDefinitions/gitea.js +++ b/src/claimDefinitions/gitea.js @@ -13,11 +13,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const E = require('../enums') +import * as E from '../enums.js' -const reURI = /^https:\/\/(.*)\/(.*)\/(.*)\/?/ +export const reURI = /^https:\/\/(.*)\/(.*)\/(.*)\/?/ -const processURI = (uri) => { +/** + * @function + * @param {string} uri + */ +export function processURI (uri) { const match = uri.match(reURI) return { @@ -55,7 +59,7 @@ const processURI = (uri) => { } } -const tests = [ +export const tests = [ { uri: 'https://domain.org/alice/gitea_proof', shouldMatch: true @@ -73,7 +77,3 @@ const tests = [ shouldMatch: false } ] - -exports.reURI = reURI -exports.processURI = processURI -exports.tests = tests diff --git a/src/claimDefinitions/github.js b/src/claimDefinitions/github.js index f330e7e..d3c964a 100644 --- a/src/claimDefinitions/github.js +++ b/src/claimDefinitions/github.js @@ -13,11 +13,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const E = require('../enums') +import * as E from '../enums.js' -const reURI = /^https:\/\/gist\.github\.com\/(.*)\/(.*)\/?/ +export const reURI = /^https:\/\/gist\.github\.com\/(.*)\/(.*)\/?/ -const processURI = (uri) => { +/** + * @function + * @param {string} uri + */ +export function processURI (uri) { const match = uri.match(reURI) return { @@ -55,7 +59,7 @@ const processURI = (uri) => { } } -const tests = [ +export const tests = [ { uri: 'https://gist.github.com/Alice/123456789', shouldMatch: true @@ -69,7 +73,3 @@ const tests = [ shouldMatch: false } ] - -exports.reURI = reURI -exports.processURI = processURI -exports.tests = tests diff --git a/src/claimDefinitions/gitlab.js b/src/claimDefinitions/gitlab.js index a685fc1..9825593 100644 --- a/src/claimDefinitions/gitlab.js +++ b/src/claimDefinitions/gitlab.js @@ -13,11 +13,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const E = require('../enums') +import * as E from '../enums.js' -const reURI = /^https:\/\/(.*)\/(.*)\/gitlab_proof\/?/ +export const reURI = /^https:\/\/(.*)\/(.*)\/gitlab_proof\/?/ -const processURI = (uri) => { +/** + * @function + * @param {string} uri + */ +export function processURI (uri) { const match = uri.match(reURI) return { @@ -55,7 +59,7 @@ const processURI = (uri) => { } } -const tests = [ +export const tests = [ { uri: 'https://gitlab.domain.org/alice/gitlab_proof', shouldMatch: true @@ -69,7 +73,3 @@ const tests = [ shouldMatch: false } ] - -exports.reURI = reURI -exports.processURI = processURI -exports.tests = tests diff --git a/src/claimDefinitions/hackernews.js b/src/claimDefinitions/hackernews.js index 8571738..8088e55 100644 --- a/src/claimDefinitions/hackernews.js +++ b/src/claimDefinitions/hackernews.js @@ -13,11 +13,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const E = require('../enums') +import * as E from '../enums.js' -const reURI = /^https:\/\/news\.ycombinator\.com\/user\?id=(.*)\/?/ +export const reURI = /^https:\/\/news\.ycombinator\.com\/user\?id=(.*)\/?/ -const processURI = (uri) => { +/** + * @function + * @param {string} uri + */ +export function processURI (uri) { const match = uri.match(reURI) return { @@ -55,7 +59,7 @@ const processURI = (uri) => { } } -const tests = [ +export const tests = [ { uri: 'https://news.ycombinator.com/user?id=Alice', shouldMatch: true @@ -69,7 +73,3 @@ const tests = [ shouldMatch: false } ] - -exports.reURI = reURI -exports.processURI = processURI -exports.tests = tests diff --git a/src/claimDefinitions/index.js b/src/claimDefinitions/index.js index 4e11d8c..7879203 100644 --- a/src/claimDefinitions/index.js +++ b/src/claimDefinitions/index.js @@ -13,31 +13,53 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ +import * as dns from './dns.js' +import * as irc from './irc.js' +import * as xmpp from './xmpp.js' +import * as matrix from './matrix.js' +import * as telegram from './telegram.js' +import * as twitter from './twitter.js' +import * as reddit from './reddit.js' +import * as liberapay from './liberapay.js' +import * as lichess from './lichess.js' +import * as hackernews from './hackernews.js' +import * as lobsters from './lobsters.js' +import * as forem from './forem.js' +// import * as forgejo from './forgejo.js' +import * as gitea from './gitea.js' +import * as gitlab from './gitlab.js' +import * as github from './github.js' +import * as activitypub from './activitypub.js' +import * as discourse from './discourse.js' +import * as owncast from './owncast.js' +import * as stackexchange from './stackexchange.js' +import * as keybase from './keybase.js' +import * as opencollective from './opencollective.js' -const data = { - dns: require('./dns'), - irc: require('./irc'), - xmpp: require('./xmpp'), - matrix: require('./matrix'), - telegram: require('./telegram'), - twitter: require('./twitter'), - reddit: require('./reddit'), - liberapay: require('./liberapay'), - lichess: require('./lichess'), - hackernews: require('./hackernews'), - lobsters: require('./lobsters'), - forem: require('./forem'), - // forgejo: require('./forgejo'), - gitea: require('./gitea'), - gitlab: require('./gitlab'), - github: require('./github'), - activitypub: require('./activitypub'), - discourse: require('./discourse'), - owncast: require('./owncast'), - stackexchange: require('./stackexchange'), - keybase: require('./keybase'), - opencollective: require('./opencollective') +const _data = { + dns, + irc, + xmpp, + matrix, + telegram, + twitter, + reddit, + liberapay, + lichess, + hackernews, + lobsters, + forem, + // forgejo, + gitea, + gitlab, + github, + activitypub, + discourse, + owncast, + stackexchange, + keybase, + opencollective } -exports.list = Object.keys(data) -exports.data = data +export const list = Object.keys(_data) +export { _data as data } diff --git a/src/claimDefinitions/irc.js b/src/claimDefinitions/irc.js index 6e5c798..f54dcf1 100644 --- a/src/claimDefinitions/irc.js +++ b/src/claimDefinitions/irc.js @@ -13,11 +13,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const E = require('../enums') +import * as E from '../enums.js' -const reURI = /^irc:\/\/(.*)\/([a-zA-Z0-9\-[\]\\`_^{|}]*)/ +export const reURI = /^irc:\/\/(.*)\/([a-zA-Z0-9\-[\]\\`_^{|}]*)/ -const processURI = (uri) => { +/** + * @function + * @param {string} uri + */ +export function processURI (uri) { const match = uri.match(reURI) return { @@ -55,7 +59,7 @@ const processURI = (uri) => { } } -const tests = [ +export const tests = [ { uri: 'irc://chat.ircserver.org/Alice1', shouldMatch: true @@ -73,7 +77,3 @@ const tests = [ shouldMatch: false } ] - -exports.reURI = reURI -exports.processURI = processURI -exports.tests = tests diff --git a/src/claimDefinitions/keybase.js b/src/claimDefinitions/keybase.js index 99b5986..a8b237f 100644 --- a/src/claimDefinitions/keybase.js +++ b/src/claimDefinitions/keybase.js @@ -13,11 +13,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const E = require('../enums') +import * as E from '../enums.js' -const reURI = /^https:\/\/keybase.io\/(.*)\/?/ +export const reURI = /^https:\/\/keybase.io\/(.*)\/?/ -const processURI = (uri) => { +/** + * @function + * @param {string} uri + */ +export function processURI (uri) { const match = uri.match(reURI) return { @@ -55,7 +59,7 @@ const processURI = (uri) => { } } -const tests = [ +export const tests = [ { uri: 'https://keybase.io/Alice', shouldMatch: true @@ -69,7 +73,3 @@ const tests = [ shouldMatch: false } ] - -exports.reURI = reURI -exports.processURI = processURI -exports.tests = tests diff --git a/src/claimDefinitions/liberapay.js b/src/claimDefinitions/liberapay.js index e17bddc..eb4a799 100644 --- a/src/claimDefinitions/liberapay.js +++ b/src/claimDefinitions/liberapay.js @@ -13,11 +13,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const E = require('../enums') +import * as E from '../enums.js' -const reURI = /^https:\/\/liberapay\.com\/(.*)\/?/ +export const reURI = /^https:\/\/liberapay\.com\/(.*)\/?/ -const processURI = (uri) => { +/** + * @function + * @param {string} uri + */ +export function processURI (uri) { const match = uri.match(reURI) return { @@ -55,7 +59,7 @@ const processURI = (uri) => { } } -const tests = [ +export const tests = [ { uri: 'https://liberapay.com/alice', shouldMatch: true @@ -69,7 +73,3 @@ const tests = [ shouldMatch: false } ] - -exports.reURI = reURI -exports.processURI = processURI -exports.tests = tests diff --git a/src/claimDefinitions/lichess.js b/src/claimDefinitions/lichess.js index e806d85..cc53c84 100644 --- a/src/claimDefinitions/lichess.js +++ b/src/claimDefinitions/lichess.js @@ -13,11 +13,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const E = require('../enums') +import * as E from '../enums.js' -const reURI = /^https:\/\/lichess\.org\/@\/(.*)\/?/ +export const reURI = /^https:\/\/lichess\.org\/@\/(.*)\/?/ -const processURI = (uri) => { +/** + * @function + * @param {string} uri + */ +export function processURI (uri) { const match = uri.match(reURI) return { @@ -55,7 +59,7 @@ const processURI = (uri) => { } } -const tests = [ +export const tests = [ { uri: 'https://lichess.org/@/Alice', shouldMatch: true @@ -69,7 +73,3 @@ const tests = [ shouldMatch: false } ] - -exports.reURI = reURI -exports.processURI = processURI -exports.tests = tests diff --git a/src/claimDefinitions/lobsters.js b/src/claimDefinitions/lobsters.js index 4f17067..dd9b297 100644 --- a/src/claimDefinitions/lobsters.js +++ b/src/claimDefinitions/lobsters.js @@ -13,11 +13,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const E = require('../enums') +import * as E from '../enums.js' -const reURI = /^https:\/\/lobste\.rs\/u\/(.*)\/?/ +export const reURI = /^https:\/\/lobste\.rs\/u\/(.*)\/?/ -const processURI = (uri) => { +/** + * @function + * @param {string} uri + */ +export function processURI (uri) { const match = uri.match(reURI) return { @@ -55,7 +59,7 @@ const processURI = (uri) => { } } -const tests = [ +export const tests = [ { uri: 'https://lobste.rs/u/Alice', shouldMatch: true @@ -69,7 +73,3 @@ const tests = [ shouldMatch: false } ] - -exports.reURI = reURI -exports.processURI = processURI -exports.tests = tests diff --git a/src/claimDefinitions/matrix.js b/src/claimDefinitions/matrix.js index c93069d..0a74d7f 100644 --- a/src/claimDefinitions/matrix.js +++ b/src/claimDefinitions/matrix.js @@ -13,11 +13,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const E = require('../enums') +import * as E from '../enums.js' -const reURI = /^matrix:u\/(?:@)?([^@:]*:[^?]*)(\?.*)?/ +export const reURI = /^matrix:u\/(?:@)?([^@:]*:[^?]*)(\?.*)?/ -const processURI = (uri) => { +/** + * @function + * @param {string} uri + */ +export function processURI (uri) { const match = uri.match(reURI) if (!match[2]) { @@ -71,7 +75,7 @@ const processURI = (uri) => { } } -const tests = [ +export const tests = [ { uri: 'matrix:u/alice:matrix.domain.org?org.keyoxide.r=123:domain.org&org.keyoxide.e=123', @@ -95,7 +99,3 @@ const tests = [ shouldMatch: false } ] - -exports.reURI = reURI -exports.processURI = processURI -exports.tests = tests diff --git a/src/claimDefinitions/opencollective.js b/src/claimDefinitions/opencollective.js index 3abbfdb..de593d0 100644 --- a/src/claimDefinitions/opencollective.js +++ b/src/claimDefinitions/opencollective.js @@ -13,11 +13,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const E = require('../enums') +import * as E from '../enums.js' -const reURI = /^https:\/\/opencollective\.com\/(.*)\/?/ +export const reURI = /^https:\/\/opencollective\.com\/(.*)\/?/ -const processURI = (uri) => { +/** + * @function + * @param {string} uri + */ +export function processURI (uri) { const match = uri.match(reURI) return { @@ -55,7 +59,7 @@ const processURI = (uri) => { } } -const tests = [ +export const tests = [ { uri: 'https://opencollective.com/Alice', shouldMatch: true @@ -69,7 +73,3 @@ const tests = [ shouldMatch: false } ] - -exports.reURI = reURI -exports.processURI = processURI -exports.tests = tests diff --git a/src/claimDefinitions/owncast.js b/src/claimDefinitions/owncast.js index 499e938..dff3f4e 100644 --- a/src/claimDefinitions/owncast.js +++ b/src/claimDefinitions/owncast.js @@ -13,11 +13,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const E = require('../enums') +import * as E from '../enums.js' -const reURI = /^https:\/\/(.*)/ +export const reURI = /^https:\/\/(.*)/ -const processURI = (uri) => { +/** + * @function + * @param {string} uri + */ +export function processURI (uri) { const match = uri.match(reURI) return { @@ -55,7 +59,7 @@ const processURI = (uri) => { } } -const tests = [ +export const tests = [ { uri: 'https://live.domain.org', shouldMatch: true @@ -73,7 +77,3 @@ const tests = [ shouldMatch: true } ] - -exports.reURI = reURI -exports.processURI = processURI -exports.tests = tests diff --git a/src/claimDefinitions/reddit.js b/src/claimDefinitions/reddit.js index 493dff7..09705b5 100644 --- a/src/claimDefinitions/reddit.js +++ b/src/claimDefinitions/reddit.js @@ -13,11 +13,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const E = require('../enums') +import * as E from '../enums.js' -const reURI = /^https:\/\/(?:www\.)?reddit\.com\/user\/(.*)\/comments\/(.*)\/(.*)\/?/ +export const reURI = /^https:\/\/(?:www\.)?reddit\.com\/user\/(.*)\/comments\/(.*)\/(.*)\/?/ -const processURI = (uri) => { +/** + * @function + * @param {string} uri + */ +export function processURI (uri) { const match = uri.match(reURI) return { @@ -55,7 +59,7 @@ const processURI = (uri) => { } } -const tests = [ +export const tests = [ { uri: 'https://www.reddit.com/user/Alice/comments/123456/post', shouldMatch: true @@ -77,7 +81,3 @@ const tests = [ shouldMatch: false } ] - -exports.reURI = reURI -exports.processURI = processURI -exports.tests = tests diff --git a/src/claimDefinitions/stackexchange.js b/src/claimDefinitions/stackexchange.js index c98f916..0b8ca9c 100644 --- a/src/claimDefinitions/stackexchange.js +++ b/src/claimDefinitions/stackexchange.js @@ -13,12 +13,16 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const E = require('../enums') +import * as E from '../enums.js' -const reURI = /^https:\/\/(.*(?:askubuntu|mathoverflow|serverfault|stackapps|stackoverflow|superuser)|.+\.stackexchange)\.com\/users\/(\d+)/ +export const reURI = /^https:\/\/(.*(?:askubuntu|mathoverflow|serverfault|stackapps|stackoverflow|superuser)|.+\.stackexchange)\.com\/users\/(\d+)/ const reStackExchange = /\.stackexchange$/ -const processURI = (uri) => { +/** + * @function + * @param {string} uri + */ +export function processURI (uri) { const [, domain, id] = uri.match(reURI) const site = domain.replace(reStackExchange, '') @@ -57,7 +61,7 @@ const processURI = (uri) => { } } -const tests = [ +export const tests = [ { uri: 'https://stackoverflow.com/users/1234', shouldMatch: true @@ -112,7 +116,3 @@ const tests = [ } ] - -exports.reURI = reURI -exports.processURI = processURI -exports.tests = tests diff --git a/src/claimDefinitions/telegram.js b/src/claimDefinitions/telegram.js index 84d64fa..0b22b34 100644 --- a/src/claimDefinitions/telegram.js +++ b/src/claimDefinitions/telegram.js @@ -13,11 +13,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const E = require('../enums') +import * as E from '../enums.js' -const reURI = /https:\/\/t.me\/([A-Za-z0-9_]{5,32})\?proof=([A-Za-z0-9_]{5,32})/ +export const reURI = /https:\/\/t.me\/([A-Za-z0-9_]{5,32})\?proof=([A-Za-z0-9_]{5,32})/ -const processURI = (uri) => { +/** + * @function + * @param {string} uri + */ +export function processURI (uri) { const match = uri.match(reURI) return { @@ -55,7 +59,7 @@ const processURI = (uri) => { } } -const tests = [ +export const tests = [ { uri: 'https://t.me/alice?proof=foobar', shouldMatch: true @@ -77,7 +81,3 @@ const tests = [ shouldMatch: false } ] - -exports.reURI = reURI -exports.processURI = processURI -exports.tests = tests diff --git a/src/claimDefinitions/twitter.js b/src/claimDefinitions/twitter.js index 996a33b..f7f804e 100644 --- a/src/claimDefinitions/twitter.js +++ b/src/claimDefinitions/twitter.js @@ -13,13 +13,21 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const E = require('../enums') +import * as E from '../enums.js' -const reURI = /^https:\/\/twitter\.com\/(.*)\/status\/([0-9]*)(?:\?.*)?/ +export const reURI = /^https:\/\/twitter\.com\/(.*)\/status\/([0-9]*)(?:\?.*)?/ -const processURI = (uri) => { +/** + * @function + * @param {string} uri + */ +export function processURI (uri) { const match = uri.match(reURI) + const urlsp = new URLSearchParams() + urlsp.set('url', match[0]) + urlsp.set('omit_script', '1') + return { serviceprovider: { type: 'web', @@ -42,7 +50,7 @@ const processURI = (uri) => { format: E.ProofFormat.JSON, data: { // Returns an oembed json object with the tweet content in html form - url: `https://publish.twitter.com/oembed?${new URLSearchParams({ url: match[0], omit_script: 1 })}`, + url: `https://publish.twitter.com/oembed?${urlsp}`, format: E.ProofFormat.JSON } } @@ -56,7 +64,7 @@ const processURI = (uri) => { } } -const tests = [ +export const tests = [ { uri: 'https://twitter.com/alice/status/1234567890123456789', shouldMatch: true @@ -70,7 +78,3 @@ const tests = [ shouldMatch: false } ] - -exports.reURI = reURI -exports.processURI = processURI -exports.tests = tests diff --git a/src/claimDefinitions/xmpp.js b/src/claimDefinitions/xmpp.js index 56ea5ea..cae36ca 100644 --- a/src/claimDefinitions/xmpp.js +++ b/src/claimDefinitions/xmpp.js @@ -13,11 +13,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const E = require('../enums') +import * as E from '../enums.js' -const reURI = /^xmpp:([a-zA-Z0-9.\-_]*)@([a-zA-Z0-9.\-_]*)(?:\?(.*))?/ +export const reURI = /^xmpp:([a-zA-Z0-9.\-_]*)@([a-zA-Z0-9.\-_]*)(?:\?(.*))?/ -const processURI = (uri) => { +/** + * @function + * @param {string} uri + */ +export function processURI (uri) { const match = uri.match(reURI) return { @@ -54,7 +58,7 @@ const processURI = (uri) => { } } -const tests = [ +export const tests = [ { uri: 'xmpp:alice@domain.org', shouldMatch: true @@ -68,7 +72,3 @@ const tests = [ shouldMatch: false } ] - -exports.reURI = reURI -exports.processURI = processURI -exports.tests = tests diff --git a/src/constants.js b/src/constants.js new file mode 100644 index 0000000..538155d --- /dev/null +++ b/src/constants.js @@ -0,0 +1,25 @@ +/* +Copyright 2023 Yarmo Mackenbach + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +/** + * Contains constant values + * @module constants + */ + +/** + * doip.js library version + * @constant {string} + */ +export const version = '0.20.0' diff --git a/src/defaults.js b/src/defaults.js index 9fa4088..024cc19 100644 --- a/src/defaults.js +++ b/src/defaults.js @@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const E = require('./enums') +import { ProxyPolicy } from './enums.js' /** * Contains default values @@ -42,10 +42,10 @@ const E = require('./enums') * @property {string|null} claims.xmpp.username - The username used to log in * @property {string|null} claims.xmpp.password - The password used to log in */ -const opts = { +export const opts = { proxy: { hostname: null, - policy: E.ProxyPolicy.NEVER + policy: ProxyPolicy.NEVER }, claims: { activitypub: { @@ -69,5 +69,3 @@ const opts = { } } } - -exports.opts = opts diff --git a/src/enums.js b/src/enums.js index f36155f..661d39d 100644 --- a/src/enums.js +++ b/src/enums.js @@ -23,7 +23,7 @@ limitations under the License. * @readonly * @enum {string} */ -const ProxyPolicy = { +export const ProxyPolicy = { /** Proxy usage decision depends on environment and service provider */ ADAPTIVE: 'adaptive', /** Always use a proxy */ @@ -31,14 +31,13 @@ const ProxyPolicy = { /** Never use a proxy, skip a verification if a proxy is inevitable */ NEVER: 'never' } -Object.freeze(ProxyPolicy) /** * Methods for fetching proofs * @readonly * @enum {string} */ -const Fetcher = { +export const Fetcher = { /** HTTP requests to ActivityPub */ ACTIVITYPUB: 'activitypub', /** DNS module from Node.js */ @@ -56,14 +55,13 @@ const Fetcher = { /** XMPP module from Node.js */ XMPP: 'xmpp' } -Object.freeze(Fetcher) /** * Entity encoding format * @readonly * @enum {string} */ -const EntityEncodingFormat = { +export const EntityEncodingFormat = { /** No special formatting */ PLAIN: 'plain', /** HTML encoded entities */ @@ -71,14 +69,13 @@ const EntityEncodingFormat = { /** XML encoded entities */ XML: 'xml' } -Object.freeze(EntityEncodingFormat) /** * Levels of access restriction for proof fetching * @readonly * @enum {string} */ -const ProofAccess = { +export const ProofAccess = { /** Any HTTP request will work */ GENERIC: 'generic', /** CORS requests are denied */ @@ -88,40 +85,37 @@ const ProofAccess = { /** Not accessible by HTTP request, needs server software */ SERVER: 'server' } -Object.freeze(ProofAccess) /** * Format of proof * @readonly * @enum {string} */ -const ProofFormat = { +export const ProofFormat = { /** JSON format */ JSON: 'json', /** Plaintext format */ TEXT: 'text' } -Object.freeze(ProofFormat) /** * Format of claim * @readonly * @enum {string} */ -const ClaimFormat = { +export const ClaimFormat = { /** `openpgp4fpr:123123123` */ URI: 'uri', /** `123123123` */ FINGERPRINT: 'fingerprint' } -Object.freeze(ClaimFormat) /** * How to find the claim inside the proof's JSON data * @readonly * @enum {string} */ -const ClaimRelation = { +export const ClaimRelation = { /** Claim is somewhere in the JSON field's textual content */ CONTAINS: 'contains', /** Claim is equal to the JSON field's textual content */ @@ -129,14 +123,13 @@ const ClaimRelation = { /** Claim is equal to an element of the JSON field's array of strings */ ONEOF: 'oneof' } -Object.freeze(ClaimRelation) /** * Status of the Claim instance * @readonly * @enum {string} */ -const ClaimStatus = { +export const ClaimStatus = { /** Claim has been initialized */ INIT: 'init', /** Claim has matched its URI to candidate claim definitions */ @@ -144,13 +137,3 @@ const ClaimStatus = { /** Claim has verified one or multiple candidate claim definitions */ VERIFIED: 'verified' } -Object.freeze(ClaimStatus) - -exports.ProxyPolicy = ProxyPolicy -exports.Fetcher = Fetcher -exports.EntityEncodingFormat = EntityEncodingFormat -exports.ProofAccess = ProofAccess -exports.ProofFormat = ProofFormat -exports.ClaimFormat = ClaimFormat -exports.ClaimRelation = ClaimRelation -exports.ClaimStatus = ClaimStatus diff --git a/src/fetcher/activitypub.js b/src/fetcher/activitypub.js index 1201adf..8809b57 100644 --- a/src/fetcher/activitypub.js +++ b/src/fetcher/activitypub.js @@ -13,19 +13,13 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const axios = require('axios').default -const validator = require('validator').default -const jsEnv = require('browser-or-node') +import axios from 'axios' +import isURL from 'validator/lib/isURL.js' +import { isNode } from 'browser-or-node' +import crypto from 'crypto' +import { version } from '../constants.js' -/** - * @module fetcher/activitypub - */ - -/** - * The request's timeout value in milliseconds - * @constant {number} timeout - */ -module.exports.timeout = 5000 +export const timeout = 5000 /** * Execute a fetch request @@ -41,17 +35,12 @@ module.exports.timeout = 5000 * @param {string} opts.claims.activitypub.privateKey - The private key to sign the request * @returns {Promise} */ -module.exports.fn = async (data, opts) => { - let crypto - if (jsEnv.isNode) { - crypto = require('crypto') - } - +export async function fn (data, opts) { let timeoutHandle const timeoutPromise = new Promise((resolve, reject) => { timeoutHandle = setTimeout( () => reject(new Error('Request was timed out')), - data.fetcherTimeout ? data.fetcherTimeout : module.exports.timeout + data.fetcherTimeout ? data.fetcherTimeout : timeout ) }) @@ -59,7 +48,7 @@ module.exports.fn = async (data, opts) => { (async () => { let isConfigured = false try { - validator.isURL(opts.claims.activitypub.url) + isURL(opts.claims.activitypub.url) isConfigured = true } catch (_) {} @@ -71,10 +60,10 @@ module.exports.fn = async (data, opts) => { date: now.toUTCString(), accept: 'application/activity+json', // @ts-ignore - 'User-Agent': `doipjs/${require('../../package.json').version}` + 'User-Agent': `doipjs/${version}` } - if (isConfigured && jsEnv.isNode) { + if (isConfigured && isNode) { // Generate the signature const signedString = `(request-target): get ${pathname}${search}\nhost: ${host}\ndate: ${now.toUTCString()}` const sign = crypto.createSign('SHA256') diff --git a/src/fetcher/dns.js b/src/fetcher/dns.js index c8960bc..1cd53a7 100644 --- a/src/fetcher/dns.js +++ b/src/fetcher/dns.js @@ -13,60 +13,51 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const jsEnv = require('browser-or-node') +import { isBrowser } from 'browser-or-node' +import dns from 'dns' + +export const timeout = 5000 /** - * @module fetcher/dns + * Execute a fetch request + * @function + * @async + * @param {object} data - Data used in the request + * @param {string} data.domain - The targeted domain + * @param {number} [data.fetcherTimeout] - Optional timeout for the fetcher + * @returns {Promise} */ +export async function fn (data, opts) { + if (isBrowser) { + return null + } -/** - * The request's timeout value in milliseconds - * @constant {number} timeout - */ -module.exports.timeout = 5000 + let timeoutHandle + const timeoutPromise = new Promise((resolve, reject) => { + timeoutHandle = setTimeout( + () => reject(new Error('Request was timed out')), + data.fetcherTimeout ? data.fetcherTimeout : timeout + ) + }) -if (jsEnv.isNode) { - const dns = require('dns') + const fetchPromise = new Promise((resolve, reject) => { + dns.resolveTxt(data.domain, (err, records) => { + if (err) { + reject(err) + return + } - /** - * Execute a fetch request - * @function - * @async - * @param {object} data - Data used in the request - * @param {string} data.domain - The targeted domain - * @param {number} [data.fetcherTimeout] - Optional timeout for the fetcher - * @returns {Promise} - */ - module.exports.fn = async (data, opts) => { - let timeoutHandle - const timeoutPromise = new Promise((resolve, reject) => { - timeoutHandle = setTimeout( - () => reject(new Error('Request was timed out')), - data.fetcherTimeout ? data.fetcherTimeout : module.exports.timeout - ) - }) - - const fetchPromise = new Promise((resolve, reject) => { - dns.resolveTxt(data.domain, (err, records) => { - if (err) { - reject(err) - return + resolve({ + domain: data.domain, + records: { + txt: records } - - resolve({ - domain: data.domain, - records: { - txt: records - } - }) }) }) + }) - return Promise.race([fetchPromise, timeoutPromise]).then((result) => { - clearTimeout(timeoutHandle) - return result - }) - } -} else { - module.exports.fn = null + return Promise.race([fetchPromise, timeoutPromise]).then((result) => { + clearTimeout(timeoutHandle) + return result + }) } diff --git a/src/fetcher/graphql.js b/src/fetcher/graphql.js index e92f8bd..a140916 100644 --- a/src/fetcher/graphql.js +++ b/src/fetcher/graphql.js @@ -13,17 +13,10 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const axios = require('axios').default +import axios from 'axios' +import { version } from '../constants.js' -/** - * @module fetcher/graphql - */ - -/** - * The request's timeout value in milliseconds - * @constant {number} timeout - */ -module.exports.timeout = 5000 +export const timeout = 5000 /** * Execute a GraphQL query via HTTP request @@ -35,12 +28,12 @@ module.exports.timeout = 5000 * @param {number} [data.fetcherTimeout] - Optional timeout for the fetcher * @returns {Promise} */ -module.exports.fn = async (data, opts) => { +export async function fn (data, opts) { let timeoutHandle const timeoutPromise = new Promise((resolve, reject) => { timeoutHandle = setTimeout( () => reject(new Error('Request was timed out')), - data.fetcherTimeout ? data.fetcherTimeout : module.exports.timeout + data.fetcherTimeout ? data.fetcherTimeout : timeout ) }) @@ -61,7 +54,7 @@ module.exports.fn = async (data, opts) => { headers: { 'Content-Type': 'application/json', // @ts-ignore - 'User-Agent': `doipjs/${require('../../package.json').version}` + 'User-Agent': `doipjs/${version}` }, validateStatus: function (status) { return status >= 200 && status < 400 diff --git a/src/fetcher/http.js b/src/fetcher/http.js index 497caf9..d18a93b 100644 --- a/src/fetcher/http.js +++ b/src/fetcher/http.js @@ -13,18 +13,11 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const axios = require('axios').default -const E = require('../enums') +import axios from 'axios' +import { ProofFormat } from '../enums.js' +import { version } from '../constants.js' -/** - * @module fetcher/http - */ - -/** - * The request's timeout value in milliseconds - * @constant {number} timeout - */ -module.exports.timeout = 5000 +export const timeout = 5000 /** * Execute a fetch request @@ -36,12 +29,12 @@ module.exports.timeout = 5000 * @param {number} [data.fetcherTimeout] - Optional timeout for the fetcher * @returns {Promise} */ -module.exports.fn = async (data, opts) => { +export async function fn (data, opts) { let timeoutHandle const timeoutPromise = new Promise((resolve, reject) => { timeoutHandle = setTimeout( () => reject(new Error('Request was timed out')), - data.fetcherTimeout ? data.fetcherTimeout : module.exports.timeout + data.fetcherTimeout ? data.fetcherTimeout : timeout ) }) @@ -52,12 +45,12 @@ module.exports.fn = async (data, opts) => { } switch (data.format) { - case E.ProofFormat.JSON: + case ProofFormat.JSON: axios.get(data.url, { headers: { Accept: 'application/json', // @ts-ignore - 'User-Agent': `doipjs/${require('../../package.json').version}` + 'User-Agent': `doipjs/${version}` }, validateStatus: function (status) { return status >= 200 && status < 400 @@ -70,7 +63,7 @@ module.exports.fn = async (data, opts) => { reject(e) }) break - case E.ProofFormat.TEXT: + case ProofFormat.TEXT: axios.get(data.url, { validateStatus: function (status) { return status >= 200 && status < 400 diff --git a/src/fetcher/index.js b/src/fetcher/index.js index dcaa36e..172cd61 100644 --- a/src/fetcher/index.js +++ b/src/fetcher/index.js @@ -13,12 +13,14 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ +import dotenv from 'dotenv' +dotenv.config() -exports.activitypub = require('./activitypub') -exports.dns = require('./dns') -exports.graphql = require('./graphql') -exports.http = require('./http') -exports.irc = require('./irc') -exports.matrix = require('./matrix') -exports.telegram = require('./telegram') -exports.xmpp = require('./xmpp') +export * as activitypub from './activitypub.js' +export * as dns from './dns.js' +export * as graphql from './graphql.js' +export * as http from './http.js' +export * as irc from './irc.js' +export * as matrix from './matrix.js' +export * as telegram from './telegram.js' +export * as xmpp from './xmpp.js' diff --git a/src/fetcher/index.minimal.js b/src/fetcher/index.minimal.js new file mode 100644 index 0000000..bc752fe --- /dev/null +++ b/src/fetcher/index.minimal.js @@ -0,0 +1,20 @@ +/* +Copyright 2021 Yarmo Mackenbach + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +export * as activitypub from './activitypub.js' +export * as graphql from './graphql.js' +export * as http from './http.js' +export * as matrix from './matrix.js' +export * as telegram from './telegram.js' diff --git a/src/fetcher/irc.js b/src/fetcher/irc.js index 4177225..f07a32a 100644 --- a/src/fetcher/irc.js +++ b/src/fetcher/irc.js @@ -13,89 +13,75 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const jsEnv = require('browser-or-node') +import irc from 'irc-upd' +import isAscii from 'validator/lib/isAscii.js' + +export const timeout = 20000 /** - * @module fetcher/irc + * Execute a fetch request + * @function + * @async + * @param {object} data - Data used in the request + * @param {string} data.nick - The nick of the targeted account + * @param {string} data.domain - The domain on which the targeted account is registered + * @param {number} [data.fetcherTimeout] - Optional timeout for the fetcher + * @param {object} opts - Options used to enable the request + * @param {object} opts.claims + * @param {object} opts.claims.irc + * @param {string} opts.claims.irc.nick - The nick to be used by the library to log in + * @returns {Promise} */ +export async function fn (data, opts) { + let timeoutHandle + const timeoutPromise = new Promise((resolve, reject) => { + timeoutHandle = setTimeout( + () => reject(new Error('Request was timed out')), + data.fetcherTimeout ? data.fetcherTimeout : timeout + ) + }) -/** - * The request's timeout value in milliseconds - * @constant {number} timeout - */ -module.exports.timeout = 20000 + const fetchPromise = new Promise((resolve, reject) => { + try { + isAscii(opts.claims.irc.nick) + } catch (err) { + throw new Error(`IRC fetcher was not set up properly (${err.message})`) + } -if (jsEnv.isNode) { - const irc = require('irc-upd') - const validator = require('validator').default + try { + const client = new irc.Client(data.domain, opts.claims.irc.nick, { + port: 6697, + secure: true, + channels: [], + showErrors: false, + debug: false + }) + const reKey = /[a-zA-Z0-9\-_]+\s+:\s(openpgp4fpr:.*)/ + const reEnd = /End\sof\s.*\staxonomy./ + const keys = [] - /** - * Execute a fetch request - * @function - * @async - * @param {object} data - Data used in the request - * @param {string} data.nick - The nick of the targeted account - * @param {string} data.domain - The domain on which the targeted account is registered - * @param {number} [data.fetcherTimeout] - Optional timeout for the fetcher - * @param {object} opts - Options used to enable the request - * @param {object} opts.claims - * @param {object} opts.claims.irc - * @param {string} opts.claims.irc.nick - The nick to be used by the library to log in - * @returns {Promise} - */ - module.exports.fn = async (data, opts) => { - let timeoutHandle - const timeoutPromise = new Promise((resolve, reject) => { - timeoutHandle = setTimeout( - () => reject(new Error('Request was timed out')), - data.fetcherTimeout ? data.fetcherTimeout : module.exports.timeout - ) - }) + // @ts-ignore + client.addListener('registered', (message) => { + client.send(`PRIVMSG NickServ TAXONOMY ${data.nick}`) + }) + // @ts-ignore + client.addListener('notice', (nick, to, text, message) => { + if (reKey.test(text)) { + const match = text.match(reKey) + keys.push(match[1]) + } + if (reEnd.test(text)) { + client.disconnect() + resolve(keys) + } + }) + } catch (error) { + reject(error) + } + }) - const fetchPromise = new Promise((resolve, reject) => { - try { - validator.isAscii(opts.claims.irc.nick) - } catch (err) { - throw new Error(`IRC fetcher was not set up properly (${err.message})`) - } - - try { - const client = new irc.Client(data.domain, opts.claims.irc.nick, { - port: 6697, - secure: true, - channels: [], - showErrors: false, - debug: false - }) - const reKey = /[a-zA-Z0-9\-_]+\s+:\s(openpgp4fpr:.*)/ - const reEnd = /End\sof\s.*\staxonomy./ - const keys = [] - - // @ts-ignore - client.addListener('registered', (message) => { - client.send(`PRIVMSG NickServ TAXONOMY ${data.nick}`) - }) - // @ts-ignore - client.addListener('notice', (nick, to, text, message) => { - if (reKey.test(text)) { - const match = text.match(reKey) - keys.push(match[1]) - } - if (reEnd.test(text)) { - client.disconnect() - resolve(keys) - } - }) - } catch (error) { - reject(error) - } - }) - - return Promise.race([fetchPromise, timeoutPromise]).then((result) => { - clearTimeout(timeoutHandle) - return result - }) - } -} else { - module.exports.fn = null + return Promise.race([fetchPromise, timeoutPromise]).then((result) => { + clearTimeout(timeoutHandle) + return result + }) } diff --git a/src/fetcher/matrix.js b/src/fetcher/matrix.js index e264611..ab01711 100644 --- a/src/fetcher/matrix.js +++ b/src/fetcher/matrix.js @@ -13,18 +13,12 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const axios = require('axios').default -const validator = require('validator').default +import axios from 'axios' +import isFQDN from 'validator/lib/isFQDN.js' +import isAscii from 'validator/lib/isAscii.js' +import { version } from '../constants.js' -/** - * @module fetcher/matrix - */ - -/** - * The request's timeout value in milliseconds - * @constant {number} timeout - */ -module.exports.timeout = 5000 +export const timeout = 5000 /** * Execute a fetch request @@ -41,19 +35,19 @@ module.exports.timeout = 5000 * @param {string} opts.claims.matrix.accessToken - The access token required to identify the library ({@link https://www.matrix.org/docs/guides/client-server-api|Matrix docs}) * @returns {Promise} */ -module.exports.fn = async (data, opts) => { +export async function fn (data, opts) { let timeoutHandle const timeoutPromise = new Promise((resolve, reject) => { timeoutHandle = setTimeout( () => reject(new Error('Request was timed out')), - data.fetcherTimeout ? data.fetcherTimeout : module.exports.timeout + data.fetcherTimeout ? data.fetcherTimeout : timeout ) }) const fetchPromise = new Promise((resolve, reject) => { try { - validator.isFQDN(opts.claims.matrix.instance) - validator.isAscii(opts.claims.matrix.accessToken) + isFQDN(opts.claims.matrix.instance) + isAscii(opts.claims.matrix.accessToken) } catch (err) { throw new Error(`Matrix fetcher was not set up properly (${err.message})`) } @@ -64,7 +58,7 @@ module.exports.fn = async (data, opts) => { headers: { Accept: 'application/json', // @ts-ignore - 'User-Agent': `doipjs/${require('../../package.json').version}` + 'User-Agent': `doipjs/${version}` } }) .then(res => { diff --git a/src/fetcher/telegram.js b/src/fetcher/telegram.js index 5d80fe8..4d3e011 100644 --- a/src/fetcher/telegram.js +++ b/src/fetcher/telegram.js @@ -13,19 +13,11 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const axios = require('axios').default -const validator = require('validator').default +import axios from 'axios' +import isAscii from 'validator/lib/isAscii.js' +import { version } from '../constants.js' -/** - * @module fetcher/telegram - */ - -/** - * The single request's timeout value in milliseconds - * This fetcher makes two requests in total - * @constant {number} timeout - */ -module.exports.timeout = 5000 +export const timeout = 5000 /** * Execute a fetch request @@ -41,18 +33,18 @@ module.exports.timeout = 5000 * @param {string} opts.claims.telegram.token - The Telegram Bot API token * @returns {Promise} */ -module.exports.fn = async (data, opts) => { +export async function fn (data, opts) { let timeoutHandle const timeoutPromise = new Promise((resolve, reject) => { timeoutHandle = setTimeout( () => reject(new Error('Request was timed out')), - data.fetcherTimeout ? data.fetcherTimeout : module.exports.timeout + data.fetcherTimeout ? data.fetcherTimeout : timeout ) }) const apiPromise = (/** @type {string} */ method) => new Promise((resolve, reject) => { try { - validator.isAscii(opts.claims.telegram.token) + isAscii(opts.claims.telegram.token) } catch (err) { throw new Error(`Telegram fetcher was not set up properly (${err.message})`) } @@ -67,7 +59,7 @@ module.exports.fn = async (data, opts) => { headers: { Accept: 'application/json', // @ts-ignore - 'User-Agent': `doipjs/${require('../../package.json').version}` + 'User-Agent': `doipjs/${version}` }, validateStatus: (status) => status === 200 }) diff --git a/src/fetcher/xmpp.js b/src/fetcher/xmpp.js index 607a738..01cc5c6 100644 --- a/src/fetcher/xmpp.js +++ b/src/fetcher/xmpp.js @@ -13,184 +13,171 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const jsEnv = require('browser-or-node') +import { client, xml } from '@xmpp/client' +import debug from '@xmpp/debug' +import isFQDN from 'validator/lib/isFQDN.js' +import isAscii from 'validator/lib/isAscii.js' -/** - * @module fetcher/xmpp - */ +export const timeout = 5000 -/** - * The request's timeout value in milliseconds - * @constant {number} timeout - */ -module.exports.timeout = 5000 +let xmpp = null +let iqCaller = null -if (jsEnv.isNode) { - const { client, xml } = require('@xmpp/client') - const debug = require('@xmpp/debug') - const validator = require('validator').default - - let xmpp = null - let iqCaller = null - - const xmppStart = async (service, username, password) => { - return new Promise((resolve, reject) => { - const xmpp = client({ - service, - username, - password - }) - if (process.env.NODE_ENV !== 'production') { - debug(xmpp, true) - } - const { iqCaller } = xmpp - xmpp.start() - xmpp.on('online', _ => { - resolve({ xmpp, iqCaller }) - }) - xmpp.on('error', error => { - reject(error) - }) +const xmppStart = async (service, username, password) => { + return new Promise((resolve, reject) => { + const xmpp = client({ + service, + username, + password }) - } - - /** - * Execute a fetch request - * @function - * @async - * @param {object} data - Data used in the request - * @param {string} data.id - The identifier of the targeted account - * @param {number} [data.fetcherTimeout] - Optional timeout for the fetcher - * @param {object} opts - Options used to enable the request - * @param {object} opts.claims - * @param {object} opts.claims.xmpp - * @param {string} opts.claims.xmpp.service - The server hostname on which the library can log in - * @param {string} opts.claims.xmpp.username - The username used to log in - * @param {string} opts.claims.xmpp.password - The password used to log in - * @returns {Promise} - */ - module.exports.fn = async (data, opts) => { - try { - validator.isFQDN(opts.claims.xmpp.service) - validator.isAscii(opts.claims.xmpp.username) - validator.isAscii(opts.claims.xmpp.password) - } catch (err) { - throw new Error(`XMPP fetcher was not set up properly (${err.message})`) + if (process.env.NODE_ENV !== 'production') { + debug(xmpp, true) } - - if (!xmpp || xmpp.status !== 'online') { - const xmppStartRes = await xmppStart( - opts.claims.xmpp.service, - opts.claims.xmpp.username, - opts.claims.xmpp.password - ) - xmpp = xmppStartRes.xmpp - iqCaller = xmppStartRes.iqCaller - } - - let timeoutHandle - const timeoutPromise = new Promise((resolve, reject) => { - timeoutHandle = setTimeout( - () => reject(new Error('Request was timed out')), - data.fetcherTimeout ? data.fetcherTimeout : module.exports.timeout - ) + const { iqCaller } = xmpp + xmpp.start() + xmpp.on('online', _ => { + resolve({ xmpp, iqCaller }) }) - - const fetchPromise = new Promise((resolve, reject) => { - (async () => { - let completed = false - const proofs = [] - - // Try the ariadne-id pubsub request - if (!completed) { - try { - const response = await iqCaller.request( - xml('iq', { type: 'get', to: data.id }, xml('pubsub', 'http://jabber.org/protocol/pubsub', xml('items', { node: 'http://ariadne.id/protocol/proof' }))), - 30 * 1000 - ) - - // Traverse the XML response - response.getChild('pubsub').getChildren('items').forEach(items => { - if (items.attrs.node === 'http://ariadne.id/protocol/proof') { - items.getChildren('item').forEach(item => { - proofs.push(item.getChildText('value')) - }) - } - }) - - resolve(proofs) - completed = true - } catch (_) {} - } - - // Try the vcard4 pubsub request [backward compatibility] - if (!completed) { - try { - const response = await iqCaller.request( - xml('iq', { type: 'get', to: data.id }, xml('pubsub', 'http://jabber.org/protocol/pubsub', xml('items', { node: 'urn:xmpp:vcard4', max_items: '1' }))), - 30 * 1000 - ) - - // Traverse the XML response - response.getChild('pubsub').getChildren('items').forEach(items => { - if (items.attrs.node === 'urn:xmpp:vcard4') { - items.getChildren('item').forEach(item => { - if (item.attrs.id === 'current') { - const itemVcard = item.getChild('vcard', 'urn:ietf:params:xml:ns:vcard-4.0') - // Find the vCard URLs - itemVcard.getChildren('url').forEach(url => { - proofs.push(url.getChildText('uri')) - }) - // Find the vCard notes - itemVcard.getChildren('note').forEach(note => { - proofs.push(note.getChildText('text')) - }) - } - }) - } - }) - - resolve(proofs) - completed = true - } catch (_) {} - } - - // Try the vcard-temp IQ request [backward compatibility] - if (!completed) { - try { - const response = await iqCaller.request( - xml('iq', { type: 'get', to: data.id }, xml('vCard', 'vcard-temp')), - 30 * 1000 - ) - - // Find the vCard URLs - response.getChild('vCard', 'vcard-temp').getChildren('URL').forEach(url => { - proofs.push(url.children[0]) - }) - // Find the vCard notes - response.getChild('vCard', 'vcard-temp').getChildren('NOTE').forEach(note => { - proofs.push(note.children[0]) - }) - response.getChild('vCard', 'vcard-temp').getChildren('DESC').forEach(note => { - proofs.push(note.children[0]) - }) - - resolve(proofs) - completed = true - } catch (error) { - reject(error) - } - } - - xmpp.stop() - })() + xmpp.on('error', error => { + reject(error) }) - - return Promise.race([fetchPromise, timeoutPromise]).then((result) => { - clearTimeout(timeoutHandle) - return result - }) - } -} else { - module.exports.fn = null + }) +} + +/** + * Execute a fetch request + * @function + * @async + * @param {object} data - Data used in the request + * @param {string} data.id - The identifier of the targeted account + * @param {number} [data.fetcherTimeout] - Optional timeout for the fetcher + * @param {object} opts - Options used to enable the request + * @param {object} opts.claims + * @param {object} opts.claims.xmpp + * @param {string} opts.claims.xmpp.service - The server hostname on which the library can log in + * @param {string} opts.claims.xmpp.username - The username used to log in + * @param {string} opts.claims.xmpp.password - The password used to log in + * @returns {Promise} + */ +export async function fn (data, opts) { + try { + isFQDN(opts.claims.xmpp.service) + isAscii(opts.claims.xmpp.username) + isAscii(opts.claims.xmpp.password) + } catch (err) { + throw new Error(`XMPP fetcher was not set up properly (${err.message})`) + } + + if (!xmpp || xmpp.status !== 'online') { + const xmppStartRes = await xmppStart( + opts.claims.xmpp.service, + opts.claims.xmpp.username, + opts.claims.xmpp.password + ) + xmpp = xmppStartRes.xmpp + iqCaller = xmppStartRes.iqCaller + } + + let timeoutHandle + const timeoutPromise = new Promise((resolve, reject) => { + timeoutHandle = setTimeout( + () => reject(new Error('Request was timed out')), + data.fetcherTimeout ? data.fetcherTimeout : timeout + ) + }) + + const fetchPromise = new Promise((resolve, reject) => { + (async () => { + let completed = false + const proofs = [] + + // Try the ariadne-id pubsub request + if (!completed) { + try { + const response = await iqCaller.request( + xml('iq', { type: 'get', to: data.id }, xml('pubsub', 'http://jabber.org/protocol/pubsub', xml('items', { node: 'http://ariadne.id/protocol/proof' }))), + 30 * 1000 + ) + + // Traverse the XML response + response.getChild('pubsub').getChildren('items').forEach(items => { + if (items.attrs.node === 'http://ariadne.id/protocol/proof') { + items.getChildren('item').forEach(item => { + proofs.push(item.getChildText('value')) + }) + } + }) + + resolve(proofs) + completed = true + } catch (_) {} + } + + // Try the vcard4 pubsub request [backward compatibility] + if (!completed) { + try { + const response = await iqCaller.request( + xml('iq', { type: 'get', to: data.id }, xml('pubsub', 'http://jabber.org/protocol/pubsub', xml('items', { node: 'urn:xmpp:vcard4', max_items: '1' }))), + 30 * 1000 + ) + + // Traverse the XML response + response.getChild('pubsub').getChildren('items').forEach(items => { + if (items.attrs.node === 'urn:xmpp:vcard4') { + items.getChildren('item').forEach(item => { + if (item.attrs.id === 'current') { + const itemVcard = item.getChild('vcard', 'urn:ietf:params:xml:ns:vcard-4.0') + // Find the vCard URLs + itemVcard.getChildren('url').forEach(url => { + proofs.push(url.getChildText('uri')) + }) + // Find the vCard notes + itemVcard.getChildren('note').forEach(note => { + proofs.push(note.getChildText('text')) + }) + } + }) + } + }) + + resolve(proofs) + completed = true + } catch (_) {} + } + + // Try the vcard-temp IQ request [backward compatibility] + if (!completed) { + try { + const response = await iqCaller.request( + xml('iq', { type: 'get', to: data.id }, xml('vCard', 'vcard-temp')), + 30 * 1000 + ) + + // Find the vCard URLs + response.getChild('vCard', 'vcard-temp').getChildren('URL').forEach(url => { + proofs.push(url.children[0]) + }) + // Find the vCard notes + response.getChild('vCard', 'vcard-temp').getChildren('NOTE').forEach(note => { + proofs.push(note.children[0]) + }) + response.getChild('vCard', 'vcard-temp').getChildren('DESC').forEach(note => { + proofs.push(note.children[0]) + }) + + resolve(proofs) + completed = true + } catch (error) { + reject(error) + } + } + + xmpp.stop() + })() + }) + + return Promise.race([fetchPromise, timeoutPromise]).then((result) => { + clearTimeout(timeoutHandle) + return result + }) } diff --git a/src/index.js b/src/index.js index e30ab41..7a77877 100644 --- a/src/index.js +++ b/src/index.js @@ -13,30 +13,16 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const Profile = require('./profile') -const Persona = require('./persona') -const Claim = require('./claim') -const claimDefinitions = require('./claimDefinitions') -const proofs = require('./proofs') -const keys = require('./keys') -const asp = require('./asp') -const signatures = require('./signatures') -const enums = require('./enums') -const defaults = require('./defaults') -const utils = require('./utils') -const verifications = require('./verifications') -const fetcher = require('./fetcher') - -exports.Profile = Profile -exports.Persona = Persona -exports.Claim = Claim -exports.claimDefinitions = claimDefinitions -exports.proofs = proofs -exports.keys = keys -exports.asp = asp -exports.signatures = signatures -exports.enums = enums -exports.defaults = defaults -exports.utils = utils -exports.verifications = verifications -exports.fetcher = fetcher +export { Profile } from './profile.js' +export { Persona } from './persona.js' +export { Claim } from './claim.js' +export * as claimDefinitions from './claimDefinitions/index.js' +export * as proofs from './proofs.js' +export * as keys from './keys.js' +export * as asp from './asp.js' +export * as signatures from './signatures.js' +export * as enums from './enums.js' +export * as defaults from './defaults.js' +export * as utils from './utils.js' +export * as verifications from './verifications.js' +export * as fetcher from './fetcher/index.js' diff --git a/src/keys.js b/src/keys.js index b132fb5..67dbf1c 100644 --- a/src/keys.js +++ b/src/keys.js @@ -13,12 +13,12 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const axios = require('axios').default -const validUrl = require('valid-url') -const openpgp = require('openpgp') -const HKP = require('@openpgp/hkp-client') -const WKD = require('@openpgp/wkd-client') -const Claim = require('./claim') +import axios from 'axios' +import { isUri } from 'valid-url' +import { readKey, PublicKey } from 'openpgp' +import HKP from '@openpgp/hkp-client' +import WKD from '@openpgp/wkd-client' +import { Claim } from './claim.js' /** * Functions related to the fetching and handling of keys @@ -30,12 +30,12 @@ const Claim = require('./claim') * @function * @param {string} identifier - Fingerprint or email address * @param {string} [keyserverDomain=keys.openpgp.org] - Domain of the keyserver - * @returns {Promise} + * @returns {Promise} * @example * const key1 = doip.keys.fetchHKP('alice@domain.tld'); * const key2 = doip.keys.fetchHKP('123abc123abc'); */ -const fetchHKP = async (identifier, keyserverDomain) => { +export async function fetchHKP (identifier, keyserverDomain) { const keyserverBaseUrl = keyserverDomain ? `https://${keyserverDomain}` : 'https://keys.openpgp.org' @@ -56,7 +56,7 @@ const fetchHKP = async (identifier, keyserverDomain) => { throw new Error('Key does not exist or could not be fetched') } - return await openpgp.readKey({ + return await readKey({ armoredKey: publicKey }) .catch((error) => { @@ -68,11 +68,11 @@ const fetchHKP = async (identifier, keyserverDomain) => { * Fetch a public key using Web Key Directory * @function * @param {string} identifier - Identifier of format 'username@domain.tld` - * @returns {Promise} + * @returns {Promise} * @example * const key = doip.keys.fetchWKD('alice@domain.tld'); */ -const fetchWKD = async (identifier) => { +export async function fetchWKD (identifier) { // @ts-ignore const wkd = new WKD() const lookupOpts = { @@ -89,7 +89,7 @@ const fetchWKD = async (identifier) => { throw new Error('Key does not exist or could not be fetched') } - return await openpgp.readKey({ + return await readKey({ binaryKey: publicKey }) .catch((error) => { @@ -102,11 +102,11 @@ const fetchWKD = async (identifier) => { * @function * @param {string} username - Keybase username * @param {string} fingerprint - Fingerprint of key - * @returns {Promise} + * @returns {Promise} * @example * const key = doip.keys.fetchKeybase('alice', '123abc123abc'); */ -const fetchKeybase = async (username, fingerprint) => { +export async function fetchKeybase (username, fingerprint) { const keyLink = `https://keybase.io/${username}/pgp_keys.asc?fingerprint=${fingerprint}` let rawKeyContent try { @@ -126,7 +126,7 @@ const fetchKeybase = async (username, fingerprint) => { throw new Error(`Error fetching Keybase key: ${e.message}`) } - return await openpgp.readKey({ + return await readKey({ armoredKey: rawKeyContent }) .catch((error) => { @@ -138,7 +138,7 @@ const fetchKeybase = async (username, fingerprint) => { * Get a public key from plaintext data * @function * @param {string} rawKeyContent - Plaintext ASCII-formatted public key data - * @returns {Promise} + * @returns {Promise} * @example * const plainkey = `-----BEGIN PGP PUBLIC KEY BLOCK----- * @@ -148,8 +148,8 @@ const fetchKeybase = async (username, fingerprint) => { * -----END PGP PUBLIC KEY BLOCK-----` * const key = doip.keys.fetchPlaintext(plainkey); */ -const fetchPlaintext = async (rawKeyContent) => { - const publicKey = await openpgp.readKey({ +export async function fetchPlaintext (rawKeyContent) { + const publicKey = await readKey({ armoredKey: rawKeyContent }) .catch((error) => { @@ -163,14 +163,14 @@ const fetchPlaintext = async (rawKeyContent) => { * Fetch a public key using an URI * @function * @param {string} uri - URI that defines the location of the key - * @returns {Promise} + * @returns {Promise} * @example * const key1 = doip.keys.fetchURI('hkp:alice@domain.tld'); * const key2 = doip.keys.fetchURI('hkp:123abc123abc'); * const key3 = doip.keys.fetchURI('wkd:alice@domain.tld'); */ -const fetchURI = async (uri) => { - if (!validUrl.isUri(uri)) { +export async function fetchURI (uri) { + if (!isUri(uri)) { throw new Error('Invalid URI') } @@ -209,12 +209,12 @@ const fetchURI = async (uri) => { * This function will also try and parse the input as a plaintext key * @function * @param {string} identifier - URI that defines the location of the key - * @returns {Promise} + * @returns {Promise} * @example * const key1 = doip.keys.fetch('alice@domain.tld'); * const key2 = doip.keys.fetch('123abc123abc'); */ -const fetch = async (identifier) => { +export async function fetch (identifier) { const re = /([a-zA-Z0-9@._=+-]*)(?::([a-zA-Z0-9@._=+-]*))?/ const match = identifier.match(re) @@ -252,7 +252,7 @@ const fetch = async (identifier) => { /** * Process a public key to get user data and claims * @function - * @param {openpgp.PublicKey} publicKey - The public key to process + * @param {PublicKey} publicKey - The public key to process * @returns {Promise} * @example * const key = doip.keys.fetchURI('hkp:alice@domain.tld'); @@ -261,8 +261,8 @@ const fetch = async (identifier) => { * console.log(claim.uri); * }); */ -const process = async (publicKey) => { - if (!(publicKey && (publicKey instanceof openpgp.PublicKey))) { +export async function process (publicKey) { + if (!(publicKey && (publicKey instanceof PublicKey))) { throw new Error('Invalid public key') } @@ -313,11 +313,3 @@ const process = async (publicKey) => { } } } - -exports.fetchHKP = fetchHKP -exports.fetchWKD = fetchWKD -exports.fetchKeybase = fetchKeybase -exports.fetchPlaintext = fetchPlaintext -exports.fetchURI = fetchURI -exports.fetch = fetch -exports.process = process diff --git a/src/persona.js b/src/persona.js index 3fe7b3b..eae2db0 100644 --- a/src/persona.js +++ b/src/persona.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ // eslint-disable-next-line -const Claim = require('./claim') +import { Claim } from './claim.js' /** * A persona with identity claims @@ -25,7 +25,7 @@ const Claim = require('./claim') * const claim = Claim('https://alice.tld', '123'); * const pers = Persona('Alice', 'About Alice', [claim]); */ -class Persona { +export class Persona { /** * @param {string} name * @param {string} [description] @@ -52,5 +52,3 @@ class Persona { this.claims = claims } } - -module.exports = Persona diff --git a/src/profile.js b/src/profile.js index 32f53bb..08b70c9 100644 --- a/src/profile.js +++ b/src/profile.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ // eslint-disable-next-line -const Persona = require('./persona') +import { Persona } from './persona.js' /** * A profile of personas with identity claims @@ -26,7 +26,7 @@ const Persona = require('./persona') * const pers = Persona('Alice', 'About Alice', [claim]); * const profile = Profile([pers]); */ -class Profile { +export class Profile { /** * Create a new profile * @function @@ -48,5 +48,3 @@ class Profile { this.primaryPersona = -1 } } - -module.exports = Profile diff --git a/src/proofs.js b/src/proofs.js index bc222fb..e37cd94 100644 --- a/src/proofs.js +++ b/src/proofs.js @@ -13,10 +13,10 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const jsEnv = require('browser-or-node') -const fetcher = require('./fetcher') -const utils = require('./utils') -const E = require('./enums') +import { isNode } from 'browser-or-node' +import * as fetcher from './fetcher/index.js' +import { generateProxyURL } from './utils.js' +import { Fetcher, ProxyPolicy, ProofAccess } from './enums.js' /** * @module proofs @@ -33,9 +33,9 @@ const E = require('./enums') * @param {object} opts - Options to enable the request * @returns {Promise} */ -const fetch = (data, opts) => { +export async function fetch (data, opts) { switch (data.proof.request.fetcher) { - case E.Fetcher.HTTP: + case Fetcher.HTTP: data.proof.request.data.format = data.proof.request.format break @@ -43,7 +43,7 @@ const fetch = (data, opts) => { break } - if (jsEnv.isNode) { + if (isNode) { return handleNodeRequests(data, opts) } @@ -52,16 +52,16 @@ const fetch = (data, opts) => { const handleBrowserRequests = (data, opts) => { switch (opts.proxy.policy) { - case E.ProxyPolicy.ALWAYS: + case ProxyPolicy.ALWAYS: return createProxyRequestPromise(data, opts) - case E.ProxyPolicy.NEVER: + case ProxyPolicy.NEVER: switch (data.proof.request.access) { - case E.ProofAccess.GENERIC: - case E.ProofAccess.GRANTED: + case ProofAccess.GENERIC: + case ProofAccess.GRANTED: return createDefaultRequestPromise(data, opts) - case E.ProofAccess.NOCORS: - case E.ProofAccess.SERVER: + case ProofAccess.NOCORS: + case ProofAccess.SERVER: throw new Error( 'Impossible to fetch proof (bad combination of service access and proxy policy)' ) @@ -69,15 +69,15 @@ const handleBrowserRequests = (data, opts) => { throw new Error('Invalid proof access value') } - case E.ProxyPolicy.ADAPTIVE: + case ProxyPolicy.ADAPTIVE: switch (data.proof.request.access) { - case E.ProofAccess.GENERIC: + case ProofAccess.GENERIC: return createFallbackRequestPromise(data, opts) - case E.ProofAccess.NOCORS: + case ProofAccess.NOCORS: return createProxyRequestPromise(data, opts) - case E.ProofAccess.GRANTED: + case ProofAccess.GRANTED: return createFallbackRequestPromise(data, opts) - case E.ProofAccess.SERVER: + case ProofAccess.SERVER: return createProxyRequestPromise(data, opts) default: throw new Error('Invalid proof access value') @@ -90,13 +90,13 @@ const handleBrowserRequests = (data, opts) => { const handleNodeRequests = (data, opts) => { switch (opts.proxy.policy) { - case E.ProxyPolicy.ALWAYS: + case ProxyPolicy.ALWAYS: return createProxyRequestPromise(data, opts) - case E.ProxyPolicy.NEVER: + case ProxyPolicy.NEVER: return createDefaultRequestPromise(data, opts) - case E.ProxyPolicy.ADAPTIVE: + case ProxyPolicy.ADAPTIVE: return createFallbackRequestPromise(data, opts) default: @@ -126,7 +126,7 @@ const createProxyRequestPromise = (data, opts) => { return new Promise((resolve, reject) => { let proxyUrl try { - proxyUrl = utils.generateProxyURL( + proxyUrl = generateProxyURL( data.proof.request.fetcher, data.proof.request.data, opts @@ -173,5 +173,3 @@ const createFallbackRequestPromise = (data, opts) => { }) }) } - -exports.fetch = fetch diff --git a/src/signatures.js b/src/signatures.js index c70e358..3f1bbf8 100644 --- a/src/signatures.js +++ b/src/signatures.js @@ -13,9 +13,9 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const openpgp = require('openpgp') -const Claim = require('./claim') -const keys = require('./keys') +import { readCleartextMessage, verify } from 'openpgp' +import { Claim } from './claim.js' +import { fetchURI } from './keys.js' /** * @module signatures @@ -27,8 +27,8 @@ const keys = require('./keys') * @param {string} signature - The plaintext signature to process * @returns {Promise} */ -const process = async (signature) => { - /** @type {openpgp.CleartextMessage} */ +export async function process (signature) { + /** @type {import('openpgp').CleartextMessage} */ let sigData const result = { fingerprint: null, @@ -48,7 +48,7 @@ const process = async (signature) => { // Read the signature try { - sigData = await openpgp.readCleartextMessage({ + sigData = await readCleartextMessage({ cleartextMessage: signature }) } catch (e) { @@ -89,7 +89,7 @@ const process = async (signature) => { if (sigKeys.length > 0) { try { result.key.uri = sigKeys[0] - result.key.data = await keys.fetchURI(result.key.uri) + result.key.data = await fetchURI(result.key.uri) result.key.fetchMethod = result.key.uri.split(':')[0] } catch (e) {} } @@ -97,7 +97,7 @@ const process = async (signature) => { if (!result.key.data && signersUserID) { try { result.key.uri = `wkd:${signersUserID}` - result.key.data = await keys.fetchURI(result.key.uri) + result.key.data = await fetchURI(result.key.uri) result.key.fetchMethod = 'wkd' } catch (e) {} } @@ -106,7 +106,7 @@ const process = async (signature) => { try { const match = preferredKeyServer.match(/^(.*:\/\/)?([^/]*)(?:\/)?$/i) result.key.uri = `hkp:${match[2]}:${issuerKeyID || signersUserID}` - result.key.data = await keys.fetchURI(result.key.uri) + result.key.data = await fetchURI(result.key.uri) result.key.fetchMethod = 'hkp' } catch (e) { throw new Error('Public key not found') @@ -114,7 +114,7 @@ const process = async (signature) => { } // Verify the signature - const verificationResult = await openpgp.verify({ + const verificationResult = await verify({ // @ts-ignore message: sigData, verificationKeys: result.key.data @@ -158,5 +158,3 @@ const process = async (signature) => { return result } - -exports.process = process diff --git a/src/utils.js b/src/utils.js index 9f6ad8b..ed6bcc7 100644 --- a/src/utils.js +++ b/src/utils.js @@ -13,8 +13,8 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const validator = require('validator').default -const E = require('./enums') +import isFQDN from 'validator/lib/isFQDN.js' +import { ClaimFormat } from './enums.js' /** * @module utils @@ -26,12 +26,13 @@ const E = require('./enums') * @param {object} data - The data the proxy must provide to the fetcher * @param {object} opts - Options to enable the request * @param {object} opts.proxy - Proxy related options + * @param {object} opts.proxy.scheme - The scheme used by the proxy server * @param {object} opts.proxy.hostname - The hostname of the proxy server * @returns {string} */ -const generateProxyURL = (type, data, opts) => { +export function generateProxyURL (type, data, opts) { try { - validator.isFQDN(opts.proxy.hostname) + isFQDN(opts.proxy.hostname) } catch (err) { throw new Error('Invalid proxy hostname') } @@ -55,14 +56,14 @@ const generateProxyURL = (type, data, opts) => { * @param {string} format - The claim's format (see {@link module:enums~ClaimFormat|enums.ClaimFormat}) * @returns {string} */ -const generateClaim = (fingerprint, format) => { +export function generateClaim (fingerprint, format) { switch (format) { - case E.ClaimFormat.URI: + case ClaimFormat.URI: if (fingerprint.match(/^(openpgp4fpr|aspe):/)) { return fingerprint } return `openpgp4fpr:${fingerprint}` - case E.ClaimFormat.FINGERPRINT: + case ClaimFormat.FINGERPRINT: return fingerprint default: throw new Error('No valid claim format') @@ -74,7 +75,7 @@ const generateClaim = (fingerprint, format) => { * @param {string} text - The text that may contain URIs * @returns {Array} */ -const getUriFromString = (text) => { +export function getUriFromString (text) { const re = /((([A-Za-z0-9]+:(?:\/\/)?)(?:[-;:&=+$,\w]+@)?[A-Za-z0-9.-]+|(?:www\.|[-;:&=+$,\w]+@)[A-Za-z0-9.-]+)((?:\/[+~%/.\w\-_]*)?\??(?:[-+=&;%@.\w_]*)#?(?:[.!/\\\w]*))?)/gi const res = text.match(re) @@ -102,7 +103,3 @@ const getUriFromString = (text) => { return urls } - -exports.generateProxyURL = generateProxyURL -exports.generateClaim = generateClaim -exports.getUriFromString = getUriFromString diff --git a/src/verifications.js b/src/verifications.js index 451578f..c421507 100644 --- a/src/verifications.js +++ b/src/verifications.js @@ -13,10 +13,10 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const utils = require('./utils') -const E = require('./enums') -const { bcryptVerify, argon2Verify } = require('hash-wasm') -const entities = require('entities') +import { generateClaim, getUriFromString } from './utils.js' +import { ClaimFormat, EntityEncodingFormat, ClaimRelation, ProofFormat } from './enums.js' +import { bcryptVerify, argon2Verify } from 'hash-wasm' +import { decodeHTML, decodeXML } from 'entities' /** * @module verifications @@ -34,25 +34,25 @@ const entities = require('entities') * @returns {Promise} */ const containsProof = async (data, params) => { - const fingerprintFormatted = utils.generateClaim(params.target, params.claimFormat) - const fingerprintURI = utils.generateClaim(params.target, E.ClaimFormat.URI) + const fingerprintFormatted = generateClaim(params.target, params.claimFormat) + const fingerprintURI = generateClaim(params.target, ClaimFormat.URI) let result = false // Decode eventual special entities switch (params.proofEncodingFormat) { - case E.EntityEncodingFormat.HTML: - data = entities.decodeHTML(data) + case EntityEncodingFormat.HTML: + data = decodeHTML(data) break - case E.EntityEncodingFormat.XML: - data = entities.decodeXML(data) + case EntityEncodingFormat.XML: + data = decodeXML(data) break - case E.EntityEncodingFormat.PLAIN: + case EntityEncodingFormat.PLAIN: default: break } - data = entities.decodeHTML(data) + data = decodeHTML(data) // Check for plaintext proof result = data @@ -132,7 +132,7 @@ const containsProof = async (data, params) => { // Check for HTTP proof if (!result) { - const uris = utils.getUriFromString(data) + const uris = getUriFromString(data) for (let index = 0; index < uris.length; index++) { if (result) continue @@ -207,11 +207,11 @@ const runJSON = async (proofData, checkPath, params) => { if (checkPath.length === 0) { switch (params.claimRelation) { - case E.ClaimRelation.ONEOF: + case ClaimRelation.ONEOF: return await containsProof(proofData.join('|'), params) - case E.ClaimRelation.CONTAINS: - case E.ClaimRelation.EQUALS: + case ClaimRelation.CONTAINS: + case ClaimRelation.EQUALS: default: return await containsProof(proofData, params) } @@ -236,7 +236,7 @@ const runJSON = async (proofData, checkPath, params) => { * @param {string} fingerprint - The fingerprint * @returns {Promise} */ -const run = async (proofData, claimData, fingerprint) => { +export async function run (proofData, claimData, fingerprint) { const res = { result: false, completed: false, @@ -244,7 +244,7 @@ const run = async (proofData, claimData, fingerprint) => { } switch (claimData.proof.request.format) { - case E.ProofFormat.JSON: + case ProofFormat.JSON: for (let index = 0; index < claimData.claim.length; index++) { const claimMethod = claimData.claim[index] try { @@ -264,7 +264,7 @@ const run = async (proofData, claimData, fingerprint) => { } res.completed = true break - case E.ProofFormat.TEXT: + case ProofFormat.TEXT: for (let index = 0; index < claimData.claim.length; index++) { const claimMethod = claimData.claim[index] try { @@ -292,5 +292,3 @@ const run = async (proofData, claimData, fingerprint) => { return res } - -exports.run = run diff --git a/test/asp.test.js b/test/asp.test.js index 75ee065..e78f0ef 100644 --- a/test/asp.test.js +++ b/test/asp.test.js @@ -13,12 +13,11 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const chai = require('chai') -const expect = chai.expect -chai.use(require('chai-as-promised')) +import { expect, use } from 'chai' +import chaiAsPromised from 'chai-as-promised' +use(chaiAsPromised) -const doipjs = require('../src') -const { log } = require('console') +import { asp, Profile } from '../src/index.js' const asp25519Uri = "aspe:domain.tld:QPRGVPJNWDXH4ESK2RYDTZJLTE" const asp25519ProfileName = "test" @@ -33,20 +32,21 @@ const asp25519ProfileJwk = { describe('asp.fetchASPE', () => { it('should be a function (1 argument)', () => { - expect(doipjs.asp.fetchASPE).to.be.a('function') - expect(doipjs.asp.fetchASPE).to.have.length(1) + expect(asp.fetchASPE).to.be.a('function') + expect(asp.fetchASPE).to.have.length(1) }) }) describe('asp.parseProfileJws', () => { it('should be a function (2 arguments)', () => { - expect(doipjs.asp.parseProfileJws).to.be.a('function') - expect(doipjs.asp.parseProfileJws).to.have.length(2) + expect(asp.parseProfileJws).to.be.a('function') + expect(asp.parseProfileJws).to.have.length(2) }) it('should return a valid Profile object when provided a valid JWS', async () => { - let profile = await doipjs.asp.parseProfileJws(asp25519ProfileJws, asp25519Uri) + let profile = await asp.parseProfileJws(asp25519ProfileJws, asp25519Uri) + console.log(profile); - expect(profile).to.be.instanceOf(doipjs.Profile) + expect(profile).to.be.instanceOf(Profile) expect(profile.personas).to.be.length(1) expect(profile.personas[0].name).to.be.equal(asp25519ProfileName) expect(profile.personas[0].claims).to.be.length(2) diff --git a/test/claimDefinitions.test.js b/test/claimDefinitions.test.js index 222939b..41577bd 100644 --- a/test/claimDefinitions.test.js +++ b/test/claimDefinitions.test.js @@ -13,14 +13,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const chai = require('chai') -const expect = chai.expect -const chaiMatchPattern = require('chai-match-pattern') -chai.use(chaiMatchPattern) -chai.use(require('chai-as-promised')) +import { expect, use } from 'chai' +import chaiAsPromised from 'chai-as-promised' +import chaiMatchPattern from 'chai-match-pattern' +use(chaiAsPromised) +use(chaiMatchPattern) const _ = chaiMatchPattern.getLodashModule() -const doipjs = require('../src') + +import { claimDefinitions } from '../src/index.js' const pattern = { serviceprovider: { @@ -52,12 +53,12 @@ const pattern = { claim: _.isArray } -doipjs.claimDefinitions.list.forEach((claimDefName, i) => { - const claimDef = doipjs.claimDefinitions.data[claimDefName] +claimDefinitions.list.forEach((claimDefName, i) => { + const claimDef = claimDefinitions.data[claimDefName] describe(`claimDefinitions.${claimDefName}`, () => { it('should be an object', () => { - expect(claimDef).to.be.a('object') + expect(typeof claimDef).to.equal('object') }) it('should have a RegExp instance named "reURI"', () => { expect(claimDef.reURI).to.be.instanceof(RegExp) diff --git a/test/keys.test.js b/test/keys.test.js index b6884c2..776841b 100644 --- a/test/keys.test.js +++ b/test/keys.test.js @@ -13,13 +13,12 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const chai = require('chai') -const expect = chai.expect -chai.use(require('chai-as-promised')) +import { expect, use } from 'chai' +import chaiAsPromised from 'chai-as-promised' +use(chaiAsPromised) -const path = require('path') -const openpgp = require('openpgp') -const doipjs = require('../src') +import { PublicKey } from 'openpgp' +import { keys } from '../src/index.js' const pubKeyFingerprint = "3637202523e7c1309ab79e99ef2dc5827b445f4b" const pubKeyEmail = "test@doip.rocks" @@ -93,68 +92,68 @@ Q+AZdYCbM0hdBjP4xdKZcpqak8ksb+aQFXjGacDL/XN4VrP+tBGxkqIqreoDcgIb describe('keys.fetch', () => { it('should be a function (1 argument)', () => { - expect(doipjs.keys.fetch).to.be.a('function') - expect(doipjs.keys.fetch).to.have.length(1) + expect(keys.fetch).to.be.a('function') + expect(keys.fetch).to.have.length(1) }) it('should return a Key object when provided a valid fingerprint', async () => { expect( - await doipjs.keys.fetch(pubKeyFingerprint) - ).to.be.instanceOf(openpgp.PublicKey) + await keys.fetch(pubKeyFingerprint) + ).to.be.instanceOf(PublicKey) }).timeout('12s') it('should return a Key object when provided a valid email address', async () => { expect( - await doipjs.keys.fetch(pubKeyEmail) - ).to.be.instanceOf(openpgp.PublicKey) + await keys.fetch(pubKeyEmail) + ).to.be.instanceOf(PublicKey) }).timeout('12s') it('should reject when provided an invalid email address', () => { return expect( - doipjs.keys.fetch('invalid@doip.rocks') + keys.fetch('invalid@doip.rocks') ).to.eventually.be.rejectedWith('Key does not exist or could not be fetched') }).timeout('12s') }) describe('keys.fetchURI', () => { it('should be a function (1 argument)', () => { - expect(doipjs.keys.fetchURI).to.be.a('function') - expect(doipjs.keys.fetchURI).to.have.length(1) + expect(keys.fetchURI).to.be.a('function') + expect(keys.fetchURI).to.have.length(1) }) it('should return a Key object when provided a hkp: uri', async () => { expect( - await doipjs.keys.fetchURI(`hkp:${pubKeyFingerprint}`) - ).to.be.instanceOf(openpgp.PublicKey) + await keys.fetchURI(`hkp:${pubKeyFingerprint}`) + ).to.be.instanceOf(PublicKey) }).timeout('12s') it('should reject when provided an invalid uri', () => { return expect( - doipjs.keys.fetchURI(`inv:${pubKeyFingerprint}`) + keys.fetchURI(`inv:${pubKeyFingerprint}`) ).to.eventually.be.rejectedWith('Invalid URI protocol') }).timeout('12s') }) describe('keys.fetchHKP', () => { it('should be a function (2 arguments)', () => { - expect(doipjs.keys.fetchHKP).to.be.a('function') - expect(doipjs.keys.fetchHKP).to.have.length(2) + expect(keys.fetchHKP).to.be.a('function') + expect(keys.fetchHKP).to.have.length(2) }) it('should return a Key object when provided a valid fingerprint', async () => { - expect(await doipjs.keys.fetchHKP(pubKeyFingerprint)).to.be.instanceOf( - openpgp.PublicKey + expect(await keys.fetchHKP(pubKeyFingerprint)).to.be.instanceOf( + PublicKey ) }).timeout('12s') it('should return a Key object when provided a valid email address', async () => { - expect(await doipjs.keys.fetchHKP(pubKeyEmail)).to.be.instanceOf( - openpgp.PublicKey + expect(await keys.fetchHKP(pubKeyEmail)).to.be.instanceOf( + PublicKey ) }).timeout('12s') it('should reject when provided an invalid fingerprint', async () => { return expect( - doipjs.keys.fetchHKP('4637202523e7c1309ab79e99ef2dc5827b445f4b') + keys.fetchHKP('4637202523e7c1309ab79e99ef2dc5827b445f4b') ).to.eventually.be.rejectedWith( 'Key does not exist or could not be fetched' ) }).timeout('12s') it('should reject when provided an invalid email address', async () => { return expect( - doipjs.keys.fetchHKP('invalid@doip.rocks') + keys.fetchHKP('invalid@doip.rocks') ).to.eventually.be.rejectedWith( 'Key does not exist or could not be fetched' ) @@ -163,24 +162,24 @@ describe('keys.fetchHKP', () => { describe('keys.fetchPlaintext', () => { it('should be a function (1 argument)', () => { - expect(doipjs.keys.fetchPlaintext).to.be.a('function') - expect(doipjs.keys.fetchPlaintext).to.have.length(1) + expect(keys.fetchPlaintext).to.be.a('function') + expect(keys.fetchPlaintext).to.have.length(1) }) it('should return a Key object', async () => { - expect(await doipjs.keys.fetchPlaintext(pubKeyPlaintext)).to.be.instanceOf( - openpgp.PublicKey + expect(await keys.fetchPlaintext(pubKeyPlaintext)).to.be.instanceOf( + PublicKey ) }).timeout('12s') }) describe('keys.process', () => { it('should be a function (1 argument)', () => { - expect(doipjs.keys.process).to.be.a('function') - expect(doipjs.keys.process).to.have.length(1) + expect(keys.process).to.be.a('function') + expect(keys.process).to.have.length(1) }) it('should return an object with specific keys', async () => { - const pubKey = await doipjs.keys.fetchPlaintext(pubKeyPlaintext) - const obj = await doipjs.keys.process(pubKey) + const pubKey = await keys.fetchPlaintext(pubKeyPlaintext) + const obj = await keys.process(pubKey) expect(obj).to.have.keys([ 'users', 'fingerprint', @@ -189,15 +188,15 @@ describe('keys.process', () => { ]) }) it('should ignore non-proof notations', async () => { - const pubKey = await doipjs.keys.fetchPlaintext(pubKeyWithOtherNotations) - const obj = await doipjs.keys.process(pubKey) + const pubKey = await keys.fetchPlaintext(pubKeyWithOtherNotations) + const obj = await keys.process(pubKey) expect(obj.users).to.be.lengthOf(1) expect(obj.users[0].claims).to.be.lengthOf(1) expect(obj.users[0].claims[0].uri).to.be.equal('dns:yarmo.eu?type=TXT') }) it('should properly handle revoked UIDs', async () => { - const pubKey = await doipjs.keys.fetchPlaintext(pubKeyWithRevokedUID) - const obj = await doipjs.keys.process(pubKey) + const pubKey = await keys.fetchPlaintext(pubKeyWithRevokedUID) + const obj = await keys.process(pubKey) expect(obj.users).to.be.lengthOf(2) expect(obj.users[0].userData.isRevoked).to.be.true expect(obj.users[1].userData.isRevoked).to.be.false diff --git a/test/signatures.test.js b/test/signatures.test.js index 1ea3ee5..0c1a4e1 100644 --- a/test/signatures.test.js +++ b/test/signatures.test.js @@ -13,10 +13,9 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const chai = require('chai') -const expect = chai.expect +import { expect } from 'chai' -const doipjs = require('../src') +import { signatures } from '../src/index.js' const sigProfile = `-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA512 @@ -83,11 +82,11 @@ YCKJPotiqe50nBijHHbuABtBianiMZOm2BbaPnsmdHIX5ynWhOI8LHR1CVmTI/0o describe('signatures.process', () => { it('should be a function (2 arguments)', () => { - expect(doipjs.signatures.process).to.be.a('function') - expect(doipjs.signatures.process).to.have.length(1) + expect(signatures.process).to.be.a('function') + expect(signatures.process).to.have.length(1) }) it('should verify a valid signature', async () => { - const verification = await doipjs.signatures.process(sigProfile) + const verification = await signatures.process(sigProfile) expect(verification.fingerprint).to.be.equal( '3637202523e7c1309ab79e99ef2dc5827b445f4b' ) @@ -95,14 +94,14 @@ describe('signatures.process', () => { }) it('should reject an invalid signature', async () => { return expect( - doipjs.signatures.process(invalidSigProfileMessage) + signatures.process(invalidSigProfileMessage) ).to.eventually.be.rejectedWith( 'Signature could not be verified (Signed digest did not match)' ) }) it('should reject an invalid signature', async () => { return expect( - doipjs.signatures.process(invalidSigProfileHash) + signatures.process(invalidSigProfileHash) ).to.eventually.be.rejectedWith( 'Signature could not be read (Ascii armor integrity check failed)' ) diff --git a/test/utils.test.js b/test/utils.test.js index f4dd71e..23c2b5b 100644 --- a/test/utils.test.js +++ b/test/utils.test.js @@ -13,10 +13,9 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const chai = require('chai') -const expect = chai.expect +import { expect } from 'chai' -const doipjs = require('../src') +import { utils, enums } from '../src/index.js' const textWithUrls = `This is text with URLs like https://domain.tld. Ow, a trailing dot. What about (https://between.parentheses)? What about [https://between.brackets]? @@ -28,19 +27,19 @@ const urlsFromText = ["https://domain.tld", "https://between.parentheses", describe('utils.generateClaim', () => { it('should be a function (2 arguments)', () => { - expect(doipjs.utils.generateClaim).to.be.a('function') - expect(doipjs.utils.generateClaim).to.have.length(2) + expect(utils.generateClaim).to.be.a('function') + expect(utils.generateClaim).to.have.length(2) }) it('should generate a correct "uri" claim', () => { expect( - doipjs.utils.generateClaim('123456789', doipjs.enums.ClaimFormat.URI) + utils.generateClaim('123456789', enums.ClaimFormat.URI) ).to.equal('openpgp4fpr:123456789') }) it('should generate a correct "fingerprint" claim', () => { expect( - doipjs.utils.generateClaim( + utils.generateClaim( '123456789', - doipjs.enums.ClaimFormat.FINGERPRINT + enums.ClaimFormat.FINGERPRINT ) ).to.equal('123456789') }) @@ -48,8 +47,8 @@ describe('utils.generateClaim', () => { describe('utils.generateProxyURL', () => { it('should be a function (3 arguments)', () => { - expect(doipjs.utils.generateProxyURL).to.be.a('function') - expect(doipjs.utils.generateProxyURL).to.have.length(3) + expect(utils.generateProxyURL).to.be.a('function') + expect(utils.generateProxyURL).to.have.length(3) }) it('should generate correct proxy URLs for explicit https scheme', () => { const opts = { @@ -59,10 +58,10 @@ describe('utils.generateProxyURL', () => { }, } expect( - doipjs.utils.generateProxyURL('http', { domain: 'domain.org' }, opts) + utils.generateProxyURL('http', { domain: 'domain.org' }, opts) ).to.equal('https://localhost/api/2/get/http?domain=domain.org') expect( - doipjs.utils.generateProxyURL('dns', { domain: 'domain.org' }, opts) + utils.generateProxyURL('dns', { domain: 'domain.org' }, opts) ).to.equal('https://localhost/api/2/get/dns?domain=domain.org') }) it('should generate correct proxy URLs for explicit http scheme', () => { @@ -73,10 +72,10 @@ describe('utils.generateProxyURL', () => { }, } expect( - doipjs.utils.generateProxyURL('http', { domain: 'domain.org' }, opts) + utils.generateProxyURL('http', { domain: 'domain.org' }, opts) ).to.equal('http://localhost/api/2/get/http?domain=domain.org') expect( - doipjs.utils.generateProxyURL('dns', { domain: 'domain.org' }, opts) + utils.generateProxyURL('dns', { domain: 'domain.org' }, opts) ).to.equal('http://localhost/api/2/get/dns?domain=domain.org') }) it('should generate correct proxy URLs for default scheme', () => { @@ -86,10 +85,10 @@ describe('utils.generateProxyURL', () => { }, } expect( - doipjs.utils.generateProxyURL('http', { domain: 'domain.org' }, opts) + utils.generateProxyURL('http', { domain: 'domain.org' }, opts) ).to.equal('https://localhost/api/2/get/http?domain=domain.org') expect( - doipjs.utils.generateProxyURL('dns', { domain: 'domain.org' }, opts) + utils.generateProxyURL('dns', { domain: 'domain.org' }, opts) ).to.equal('https://localhost/api/2/get/dns?domain=domain.org') }) @@ -98,12 +97,12 @@ describe('utils.generateProxyURL', () => { describe('utils.getUriFromString', () => { it('should be a function (1 arguments)', () => { - expect(doipjs.utils.getUriFromString).to.be.a('function') - expect(doipjs.utils.getUriFromString).to.have.length(1) + expect(utils.getUriFromString).to.be.a('function') + expect(utils.getUriFromString).to.have.length(1) }) it('should extract URLs from text', () => { expect( - doipjs.utils.getUriFromString(textWithUrls) + utils.getUriFromString(textWithUrls) ).to.have.length(urlsFromText.length) }) // TODO Properly check each URL diff --git a/test/verifications.test.js b/test/verifications.test.js index 1a50642..a34be2d 100644 --- a/test/verifications.test.js +++ b/test/verifications.test.js @@ -13,11 +13,11 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -const chai = require('chai') -const expect = chai.expect -chai.use(require('chai-as-promised')) +import { expect, use } from 'chai' +import chaiAsPromised from 'chai-as-promised' +use(chaiAsPromised) -const doipjs = require('../src') +import { claimDefinitions, verifications } from '../src/index.js' const fingerprint = '3637202523e7c1309ab79e99ef2dc5827b445f4b' const plaintextCorrectProofData = [ @@ -44,40 +44,40 @@ const bcryptIncorrectProofData = [ const bcryptCostlyProofData = [ '$2y$16$4Knuu11ZyPXa1qxEbEsKQemKY6ZHM8Bk7WElYfL8q5kmzNjY1Ty8W' ] -const claimData = doipjs.claimDefinitions.data.irc.processURI('irc://domain.tld/test') +const claimData = claimDefinitions.data.irc.processURI('irc://domain.tld/test') describe('verifications.run', () => { it('should verify a plaintext proof', async () => { - const result = await doipjs.verifications.run(plaintextCorrectProofData, claimData, fingerprint) + const result = await verifications.run(plaintextCorrectProofData, claimData, fingerprint) expect(result.result).to.be.true }) // issue #22 it('should handle a plaintext proof with whitespace', async () => { - const result = await doipjs.verifications.run(plaintextCorrectProofDataWithWhitespace, claimData, fingerprint) + const result = await verifications.run(plaintextCorrectProofDataWithWhitespace, claimData, fingerprint) expect(result.result).to.be.true }) it('should reject a wrong plaintext proof', async () => { - const result = await doipjs.verifications.run(plaintextIncorrectProofData, claimData, fingerprint) + const result = await verifications.run(plaintextIncorrectProofData, claimData, fingerprint) expect(result.result).to.be.false }) it('should verify a argon2-hashed proof', async () => { - const result = await doipjs.verifications.run(argon2CorrectProofData, claimData, fingerprint) + const result = await verifications.run(argon2CorrectProofData, claimData, fingerprint) expect(result.result).to.be.true }) it('should reject a wrong argon2-hashed proof', async () => { - const result = await doipjs.verifications.run(argon2IncorrectProofData, claimData, fingerprint) + const result = await verifications.run(argon2IncorrectProofData, claimData, fingerprint) expect(result.result).to.be.false }) it('should verify a bcrypt-hashed proof', async () => { - const result = await doipjs.verifications.run(bcryptCorrectProofData, claimData, fingerprint) + const result = await verifications.run(bcryptCorrectProofData, claimData, fingerprint) expect(result.result).to.be.true }) it('should reject a wrong bcrypt-hashed proof', async () => { - const result = await doipjs.verifications.run(bcryptIncorrectProofData, claimData, fingerprint) + const result = await verifications.run(bcryptIncorrectProofData, claimData, fingerprint) expect(result.result).to.be.false }) it('should reject a too costly hashed proof', async () => { - const result = await doipjs.verifications.run(bcryptCostlyProofData, claimData, fingerprint) + const result = await verifications.run(bcryptCostlyProofData, claimData, fingerprint) expect(result.result).to.be.false }) })