-
Notifications
You must be signed in to change notification settings - Fork 41
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: respect registry-scoped certfile and keyfile options
Closes #118 RFC: npm/rfcs#591 Add support for registry-scoped certfile and keyfile options, e.g. ``` { "//my.registry.example/npm/:certfile": "~/.secret/stuff.crt", "//my.registry.example/npm/:keyfile": "~/.secret/stuff.key" } ``` Since these are registry-specific, they will override top-level cert and key options (if set). Like the top-level `cafile` option, these registry-scoped options are silently ignored if invalid.
- Loading branch information
Showing
3 changed files
with
122 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,6 +33,8 @@ t.test('basic auth', t => { | |
token: null, | ||
isBasicAuth: true, | ||
auth: Buffer.from('user:pass').toString('base64'), | ||
cert: null, | ||
key: null, | ||
}, 'basic auth details generated') | ||
|
||
const opts = Object.assign({}, OPTS, config) | ||
|
@@ -62,6 +64,8 @@ t.test('token auth', t => { | |
isBasicAuth: false, | ||
token: 'c0ffee', | ||
auth: null, | ||
cert: null, | ||
key: null, | ||
}, 'correct auth token picked out') | ||
|
||
const opts = Object.assign({}, OPTS, config) | ||
|
@@ -77,24 +81,37 @@ t.test('token auth', t => { | |
}) | ||
|
||
t.test('forceAuth', t => { | ||
const dir = t.testdir({ | ||
'my.cert': 'my cert', | ||
'my.key': 'my key', | ||
'other.cert': 'other cert', | ||
'other.key': 'other key', | ||
}) | ||
|
||
const config = { | ||
registry: 'https://my.custom.registry/here/', | ||
token: 'deadbeef', | ||
'always-auth': false, | ||
'//my.custom.registry/here/:_authToken': 'c0ffee', | ||
'//my.custom.registry/here/:token': 'nope', | ||
'//my.custom.registry/here/:certfile': `${dir}/my.cert`, | ||
'//my.custom.registry/here/:keyfile': `${dir}/my.key`, | ||
forceAuth: { | ||
username: 'user', | ||
password: Buffer.from('pass', 'utf8').toString('base64'), | ||
email: '[email protected]', | ||
'always-auth': true, | ||
certfile: `${dir}/other.cert`, | ||
keyfile: `${dir}/other.key`, | ||
}, | ||
} | ||
t.same(getAuth(config.registry, config), { | ||
scopeAuthKey: null, | ||
token: null, | ||
isBasicAuth: true, | ||
auth: Buffer.from('user:pass').toString('base64'), | ||
cert: 'other cert', | ||
key: 'other key', | ||
}, 'only forceAuth details included') | ||
|
||
const opts = Object.assign({}, OPTS, config) | ||
|
@@ -126,6 +143,8 @@ t.test('forceAuth token', t => { | |
isBasicAuth: false, | ||
token: 'cafebad', | ||
auth: null, | ||
cert: null, | ||
key: null, | ||
}, 'correct forceAuth token picked out') | ||
|
||
const opts = Object.assign({}, OPTS, config) | ||
|
@@ -152,6 +171,8 @@ t.test('_auth auth', t => { | |
token: null, | ||
isBasicAuth: false, | ||
auth: 'c0ffee', | ||
cert: null, | ||
key: null, | ||
}, 'correct _auth picked out') | ||
|
||
const opts = Object.assign({}, OPTS, config) | ||
|
@@ -177,6 +198,8 @@ t.test('_auth username:pass auth', t => { | |
token: null, | ||
isBasicAuth: false, | ||
auth: auth, | ||
cert: null, | ||
key: null, | ||
}, 'correct _auth picked out') | ||
|
||
const opts = Object.assign({}, OPTS, config) | ||
|
@@ -226,6 +249,8 @@ t.test('globally-configured auth', t => { | |
token: null, | ||
isBasicAuth: true, | ||
auth: Buffer.from('globaluser:globalpass').toString('base64'), | ||
cert: null, | ||
key: null, | ||
}, 'basic auth details generated from global settings') | ||
|
||
const tokenConfig = { | ||
|
@@ -239,6 +264,8 @@ t.test('globally-configured auth', t => { | |
token: 'deadbeef', | ||
isBasicAuth: false, | ||
auth: null, | ||
cert: null, | ||
key: null, | ||
}, 'correct global auth token picked out') | ||
|
||
const _authConfig = { | ||
|
@@ -252,6 +279,8 @@ t.test('globally-configured auth', t => { | |
token: null, | ||
isBasicAuth: false, | ||
auth: 'deadbeef', | ||
cert: null, | ||
key: null, | ||
}, 'correct _auth picked out') | ||
|
||
t.end() | ||
|
@@ -270,6 +299,8 @@ t.test('otp token passed through', t => { | |
token: 'c0ffee', | ||
isBasicAuth: false, | ||
auth: null, | ||
cert: null, | ||
key: null, | ||
}, 'correct auth token picked out') | ||
|
||
const opts = Object.assign({}, OPTS, config) | ||
|
@@ -337,6 +368,8 @@ t.test('always-auth', t => { | |
token: 'c0ffee', | ||
isBasicAuth: false, | ||
auth: null, | ||
cert: null, | ||
key: null, | ||
}, 'correct auth token picked out') | ||
|
||
const opts = Object.assign({}, OPTS, config) | ||
|
@@ -349,25 +382,36 @@ t.test('always-auth', t => { | |
}) | ||
|
||
t.test('scope-based auth', t => { | ||
const dir = t.testdir({ | ||
'my.cert': 'my cert', | ||
'my.key': 'my key', | ||
}) | ||
|
||
const config = { | ||
registry: 'https://my.custom.registry/here/', | ||
scope: '@myscope', | ||
'@myscope:registry': 'https://my.custom.registry/here/', | ||
token: 'deadbeef', | ||
'//my.custom.registry/here/:_authToken': 'c0ffee', | ||
'//my.custom.registry/here/:token': 'nope', | ||
'//my.custom.registry/here/:certfile': `${dir}/my.cert`, | ||
'//my.custom.registry/here/:keyfile': `${dir}/my.key`, | ||
} | ||
t.same(getAuth(config['@myscope:registry'], config), { | ||
scopeAuthKey: null, | ||
auth: null, | ||
isBasicAuth: false, | ||
token: 'c0ffee', | ||
cert: 'my cert', | ||
key: 'my key', | ||
}, 'correct auth token picked out') | ||
t.same(getAuth(config['@myscope:registry'], config), { | ||
scopeAuthKey: null, | ||
auth: null, | ||
isBasicAuth: false, | ||
token: 'c0ffee', | ||
cert: 'my cert', | ||
key: 'my key', | ||
}, 'correct auth token picked out without scope config having an @') | ||
|
||
const opts = Object.assign({}, OPTS, config) | ||
|
@@ -392,6 +436,32 @@ t.test('auth needs a uri', t => { | |
t.end() | ||
}) | ||
|
||
t.test('certfile and keyfile errors', t => { | ||
const dir = t.testdir({ | ||
'my.cert': 'my cert', | ||
}) | ||
|
||
t.same(getAuth('https://my.custom.registry/here/', { | ||
'//my.custom.registry/here/:certfile': `${dir}/my.cert`, | ||
'//my.custom.registry/here/:keyfile': `${dir}/nosuch.key`, | ||
}), { | ||
scopeAuthKey: null, | ||
auth: null, | ||
isBasicAuth: false, | ||
token: null, | ||
cert: null, | ||
key: null, | ||
}, 'cert and key ignored if one doesn\'t exist') | ||
|
||
t.throws(() => { | ||
getAuth('https://my.custom.registry/here/', { | ||
'//my.custom.registry/here/:certfile': `${dir}/my.cert`, | ||
'//my.custom.registry/here/:keyfile': dir, | ||
}) | ||
}, /EISDIR/, 'other read errors are propagated') | ||
t.end() | ||
}) | ||
|
||
t.test('do not be thrown by other weird configs', t => { | ||
const opts = { | ||
scope: '@asdf', | ||
|
@@ -412,6 +482,8 @@ t.test('do not be thrown by other weird configs', t => { | |
token: 'correct bearer token', | ||
isBasicAuth: false, | ||
auth: null, | ||
cert: null, | ||
key: null, | ||
}) | ||
t.end() | ||
}) | ||
|
@@ -430,27 +502,35 @@ t.test('scopeAuthKey tests', t => { | |
auth: null, | ||
isBasicAuth: false, | ||
token: null, | ||
cert: null, | ||
key: null, | ||
}, 'regular scoped spec') | ||
|
||
t.same(getAuth(uri, { ...opts, spec: 'foo@npm:@scope/foo@latest' }), { | ||
scopeAuthKey: '//scope-host.com/', | ||
auth: null, | ||
isBasicAuth: false, | ||
token: null, | ||
cert: null, | ||
key: null, | ||
}, 'scoped pkg aliased to unscoped name') | ||
|
||
t.same(getAuth(uri, { ...opts, spec: '@other-scope/foo@npm:@scope/foo@latest' }), { | ||
scopeAuthKey: '//scope-host.com/', | ||
auth: null, | ||
isBasicAuth: false, | ||
token: null, | ||
cert: null, | ||
key: null, | ||
}, 'scoped name aliased to other scope with auth') | ||
|
||
t.same(getAuth(uri, { ...opts, spec: '@scope/foo@npm:foo@latest' }), { | ||
scopeAuthKey: null, | ||
auth: null, | ||
isBasicAuth: false, | ||
token: null, | ||
cert: null, | ||
key: null, | ||
}, 'unscoped aliased to scoped name') | ||
|
||
t.end() | ||
|
@@ -470,18 +550,24 @@ t.test('registry host matches, path does not, send auth', t => { | |
token: 'c0ffee', | ||
auth: null, | ||
isBasicAuth: false, | ||
cert: null, | ||
key: null, | ||
}) | ||
t.same(getAuth(uri, { ...opts, spec: '@other-scope/foo' }), { | ||
scopeAuthKey: '//other-scope-registry.com/other/scope/', | ||
token: null, | ||
auth: null, | ||
isBasicAuth: false, | ||
cert: null, | ||
key: null, | ||
}) | ||
t.same(getAuth(uri, { ...opts, registry: 'https://scope-host.com/scope/host/' }), { | ||
scopeAuthKey: null, | ||
token: 'c0ffee', | ||
auth: null, | ||
isBasicAuth: false, | ||
cert: null, | ||
key: null, | ||
}) | ||
t.end() | ||
}) |