From e58a4f18f0ec0820fe57ccaff34c4135ece12558 Mon Sep 17 00:00:00 2001 From: Jon Jensen Date: Mon, 18 Jul 2022 11:14:02 -0600 Subject: [PATCH] feat: detect registry-scoped certfile and keyfile options (#69) RFC: https://github.com/npm/rfcs/pull/591 See also: https://github.com/npm/npm-registry-fetch/pull/125 By itself this change doesn't do much, but it enables us to resolve https://github.com/npm/cli/issues/4765 and surface these options anywhere else they may be needed. --- lib/index.js | 19 +++++++- tap-snapshots/test/index.js.test.cjs | 66 ++++++++++++++++++++++++++++ test/index.js | 18 +++++++- 3 files changed, 99 insertions(+), 4 deletions(-) diff --git a/lib/index.js b/lib/index.js index 5b7ea68..8c2b181 100644 --- a/lib/index.js +++ b/lib/index.js @@ -698,9 +698,11 @@ class Config { this.delete(`${nerfed}:_password`, 'user') this.delete(`${nerfed}:username`, 'user') this.delete(`${nerfed}:email`, 'user') + this.delete(`${nerfed}:certfile`, 'user') + this.delete(`${nerfed}:keyfile`, 'user') } - setCredentialsByURI (uri, { token, username, password, email }) { + setCredentialsByURI (uri, { token, username, password, email, certfile, keyfile }) { const nerfed = nerfDart(uri) const def = nerfDart(this.get('registry')) @@ -733,6 +735,11 @@ class Config { this.delete(`${nerfed}:-authtoken`, 'user') this.delete(`${nerfed}:_authtoken`, 'user') this.delete(`${nerfed}:email`, 'user') + if (certfile && keyfile) { + this.set(`${nerfed}:certfile`, certfile, 'user') + this.set(`${nerfed}:keyfile`, keyfile, 'user') + // cert/key may be used in conjunction with other credentials, thus no `else` + } if (token) { this.set(`${nerfed}:_authToken`, token, 'user') this.delete(`${nerfed}:_password`, 'user') @@ -750,7 +757,7 @@ class Config { // protects against shoulder-hacks if password is memorable, I guess? const encoded = Buffer.from(password, 'utf8').toString('base64') this.set(`${nerfed}:_password`, encoded, 'user') - } else { + } else if (!certfile || !keyfile) { throw new Error('No credentials to set.') } } @@ -765,6 +772,14 @@ class Config { creds.email = email } + const certfileReg = this.get(`${nerfed}:certfile`) + const keyfileReg = this.get(`${nerfed}:keyfile`) + if (certfileReg && keyfileReg) { + creds.certfile = certfileReg + creds.keyfile = keyfileReg + // cert/key may be used in conjunction with other credentials, thus no `return` + } + const tokenReg = this.get(`${nerfed}:_authToken`) || this.get(`${nerfed}:_authtoken`) || this.get(`${nerfed}:-authtoken`) || diff --git a/tap-snapshots/test/index.js.test.cjs b/tap-snapshots/test/index.js.test.cjs index be43272..3b91b26 100644 --- a/tap-snapshots/test/index.js.test.cjs +++ b/tap-snapshots/test/index.js.test.cjs @@ -125,6 +125,72 @@ exports[`test/index.js TAP credentials management nerfed_lcAuthToken > other reg Object {} ` +exports[`test/index.js TAP credentials management nerfed_mtls > default registry 1`] = ` +Object { + "certfile": "/path/to/cert", + "keyfile": "/path/to/key", +} +` + +exports[`test/index.js TAP credentials management nerfed_mtls > default registry after set 1`] = ` +Object { + "certfile": "/path/to/cert", + "keyfile": "/path/to/key", +} +` + +exports[`test/index.js TAP credentials management nerfed_mtls > other registry 1`] = ` +Object {} +` + +exports[`test/index.js TAP credentials management nerfed_mtlsAuthToken > default registry 1`] = ` +Object { + "certfile": "/path/to/cert", + "keyfile": "/path/to/key", + "token": "0bad1de4", +} +` + +exports[`test/index.js TAP credentials management nerfed_mtlsAuthToken > default registry after set 1`] = ` +Object { + "certfile": "/path/to/cert", + "keyfile": "/path/to/key", + "token": "0bad1de4", +} +` + +exports[`test/index.js TAP credentials management nerfed_mtlsAuthToken > other registry 1`] = ` +Object {} +` + +exports[`test/index.js TAP credentials management nerfed_mtlsUserPass > default registry 1`] = ` +Object { + "auth": "aGVsbG86d29ybGQ=", + "certfile": "/path/to/cert", + "email": "i@izs.me", + "keyfile": "/path/to/key", + "password": "world", + "username": "hello", +} +` + +exports[`test/index.js TAP credentials management nerfed_mtlsUserPass > default registry after set 1`] = ` +Object { + "auth": "aGVsbG86d29ybGQ=", + "certfile": "/path/to/cert", + "email": "i@izs.me", + "keyfile": "/path/to/key", + "password": "world", + "username": "hello", +} +` + +exports[`test/index.js TAP credentials management nerfed_mtlsUserPass > other registry 1`] = ` +Object { + "email": "i@izs.me", +} +` + exports[`test/index.js TAP credentials management nerfed_userpass > default registry 1`] = ` Object { "auth": "aGVsbG86d29ybGQ=", diff --git a/test/index.js b/test/index.js index 869cc71..ddbc6c2 100644 --- a/test/index.js +++ b/test/index.js @@ -625,6 +625,20 @@ t.test('credentials management', async t => { nerfed_auth: { // note: does not load, because we don't do _auth per reg '.npmrc': `//registry.example/:_auth = ${Buffer.from('hello:world').toString('base64')}`, }, + nerfed_mtls: { '.npmrc': `//registry.example/:certfile = /path/to/cert +//registry.example/:keyfile = /path/to/key`, + }, + nerfed_mtlsAuthToken: { '.npmrc': `//registry.example/:_authToken = 0bad1de4 +//registry.example/:certfile = /path/to/cert +//registry.example/:keyfile = /path/to/key`, + }, + nerfed_mtlsUserPass: { '.npmrc': `//registry.example/:username = hello +//registry.example/:_password = ${Buffer.from('world').toString('base64')} +//registry.example/:email = i@izs.me +//registry.example/:always-auth = "false" +//registry.example/:certfile = /path/to/cert +//registry.example/:keyfile = /path/to/key`, + }, def_userpass: { '.npmrc': `username = hello _password = ${Buffer.from('world').toString('base64')} @@ -712,14 +726,14 @@ always-auth = true`, } // need both or none of user/pass - if (!d.token && (!d.username || !d.password)) { + if (!d.token && (!d.username || !d.password) && (!d.certfile || !d.keyfile)) { t.throws(() => c.setCredentialsByURI(defReg, d)) } else { c.setCredentialsByURI(defReg, d) t.matchSnapshot(c.getCredentialsByURI(defReg), 'default registry after set') } - if (!o.token && (!o.username || !o.password)) { + if (!o.token && (!o.username || !o.password) && (!o.certfile || !o.keyfile)) { t.throws(() => c.setCredentialsByURI(otherReg, o), {}, { otherReg, o }) } else { c.setCredentialsByURI(otherReg, o)