2021-04-15 21:53:45 -04:00
|
|
|
const t = require('tap')
|
2024-04-30 23:53:22 -07:00
|
|
|
const { load: loadMockNpm } = require('../../fixtures/mock-npm.js')
|
|
|
|
const MockRegistry = require('@npmcli/mock-registry')
|
|
|
|
const mockGlobals = require('@npmcli/mock-globals')
|
|
|
|
const stream = require('node:stream')
|
2020-10-02 17:52:19 -04:00
|
|
|
|
2024-04-30 23:53:22 -07:00
|
|
|
const authToken = 'abcd1234'
|
|
|
|
const password = 'this is not really a password'
|
2020-10-02 17:52:19 -04:00
|
|
|
|
2024-04-30 23:53:22 -07:00
|
|
|
const auth = {
|
|
|
|
'//registry.npmjs.org/:_authToken': authToken,
|
2023-01-16 22:38:23 -05:00
|
|
|
}
|
2020-10-02 17:52:19 -04:00
|
|
|
|
2024-04-30 23:53:22 -07:00
|
|
|
const now = new Date().toISOString()
|
|
|
|
const tokens = [
|
|
|
|
{
|
|
|
|
key: 'abcd1234abcd1234',
|
|
|
|
token: 'efgh5678efgh5678',
|
|
|
|
cidr_whitelist: null,
|
|
|
|
readonly: false,
|
|
|
|
created: now,
|
|
|
|
updated: now,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
key: 'abcd1256',
|
|
|
|
token: 'hgfe8765',
|
|
|
|
cidr_whitelist: ['192.168.1.1/32'],
|
|
|
|
readonly: true,
|
|
|
|
created: now,
|
|
|
|
updated: now,
|
|
|
|
},
|
|
|
|
]
|
|
|
|
|
2023-01-16 22:38:23 -05:00
|
|
|
t.test('completion', async t => {
|
2024-04-30 23:53:22 -07:00
|
|
|
const { token } = await loadMockNpm(t, { command: 'token' })
|
2021-12-02 22:04:46 +00:00
|
|
|
|
2020-10-02 17:52:19 -04:00
|
|
|
const testComp = (argv, expect) => {
|
2023-06-08 05:24:49 -07:00
|
|
|
t.resolveMatch(token.completion({ conf: { argv: { remain: argv } } }), expect, argv.join(' '))
|
2020-10-02 17:52:19 -04:00
|
|
|
}
|
|
|
|
|
2021-03-01 11:38:43 -05:00
|
|
|
testComp(['npm', 'token'], ['list', 'revoke', 'create'])
|
2020-10-02 17:52:19 -04:00
|
|
|
testComp(['npm', 'token', 'list'], [])
|
|
|
|
testComp(['npm', 'token', 'revoke'], [])
|
|
|
|
testComp(['npm', 'token', 'create'], [])
|
|
|
|
|
2023-06-08 05:24:49 -07:00
|
|
|
t.rejects(token.completion({ conf: { argv: { remain: ['npm', 'token', 'foobar'] } } }), {
|
2021-11-18 20:58:02 +00:00
|
|
|
message: 'foobar not recognize',
|
|
|
|
})
|
2020-10-02 17:52:19 -04:00
|
|
|
})
|
|
|
|
|
2021-11-04 20:42:47 +00:00
|
|
|
t.test('token foobar', async t => {
|
2024-04-30 23:53:22 -07:00
|
|
|
const { npm } = await loadMockNpm(t)
|
2020-10-02 17:52:19 -04:00
|
|
|
|
2024-04-30 23:53:22 -07:00
|
|
|
await t.rejects(npm.exec('token', ['foobar']), /foobar is not a recognized subcommand/)
|
2020-10-02 17:52:19 -04:00
|
|
|
})
|
|
|
|
|
2021-11-04 20:42:47 +00:00
|
|
|
t.test('token list', async t => {
|
2024-04-30 23:53:22 -07:00
|
|
|
const { npm, outputs } = await loadMockNpm(t, {
|
|
|
|
config: { ...auth },
|
2020-10-02 17:52:19 -04:00
|
|
|
})
|
2024-04-30 23:53:22 -07:00
|
|
|
const registry = new MockRegistry({
|
|
|
|
tap: t,
|
|
|
|
registry: npm.config.get('registry'),
|
|
|
|
authorization: authToken,
|
|
|
|
})
|
|
|
|
registry.getTokens(tokens)
|
|
|
|
await npm.exec('token', [])
|
|
|
|
t.strictSame(outputs, [
|
|
|
|
`Publish token efgh5678efgh5678… with id abcd123 created ${now.slice(0, 10)}`,
|
|
|
|
'',
|
|
|
|
`Read only token hgfe8765… with id abcd125 created ${now.slice(0, 10)}`,
|
|
|
|
'with IP whitelist: 192.168.1.1/32',
|
|
|
|
'',
|
|
|
|
])
|
2020-10-02 17:52:19 -04:00
|
|
|
})
|
|
|
|
|
2021-11-04 20:42:47 +00:00
|
|
|
t.test('token list json output', async t => {
|
2024-04-30 23:53:22 -07:00
|
|
|
const { npm, joinedOutput } = await loadMockNpm(t, {
|
|
|
|
config: {
|
|
|
|
...auth,
|
|
|
|
json: true,
|
2020-10-02 17:52:19 -04:00
|
|
|
},
|
2023-01-16 22:38:23 -05:00
|
|
|
})
|
2024-04-30 23:53:22 -07:00
|
|
|
const registry = new MockRegistry({
|
|
|
|
tap: t,
|
|
|
|
registry: npm.config.get('registry'),
|
|
|
|
authorization: authToken,
|
|
|
|
})
|
|
|
|
registry.getTokens(tokens)
|
|
|
|
await npm.exec('token', ['list'])
|
2023-01-16 22:38:23 -05:00
|
|
|
const parsed = JSON.parse(joinedOutput())
|
|
|
|
t.match(parsed, tokens, 'prints the json parsed tokens')
|
2020-10-02 17:52:19 -04:00
|
|
|
})
|
|
|
|
|
2021-11-04 20:42:47 +00:00
|
|
|
t.test('token list parseable output', async t => {
|
2024-04-30 23:53:22 -07:00
|
|
|
const { npm, outputs } = await loadMockNpm(t, {
|
|
|
|
config: {
|
|
|
|
...auth,
|
|
|
|
parseable: true,
|
2020-10-02 17:52:19 -04:00
|
|
|
},
|
|
|
|
})
|
2024-04-30 23:53:22 -07:00
|
|
|
const registry = new MockRegistry({
|
|
|
|
tap: t,
|
|
|
|
registry: npm.config.get('registry'),
|
|
|
|
authorization: authToken,
|
|
|
|
})
|
|
|
|
registry.getTokens(tokens)
|
|
|
|
await npm.exec('token', [])
|
|
|
|
t.strictSame(outputs, [
|
|
|
|
'key\ttoken\tcreated\treadonly\tCIDR whitelist',
|
|
|
|
`abcd1234abcd1234\tefgh5678efgh5678\t${now}\tfalse\t`,
|
|
|
|
`abcd1256\thgfe8765\t${now}\ttrue\t192.168.1.1/32`,
|
|
|
|
])
|
2020-10-02 17:52:19 -04:00
|
|
|
})
|
|
|
|
|
2021-11-04 20:42:47 +00:00
|
|
|
t.test('token revoke', async t => {
|
2024-04-30 23:53:22 -07:00
|
|
|
const { npm, joinedOutput } = await loadMockNpm(t, {
|
|
|
|
config: { ...auth },
|
|
|
|
})
|
|
|
|
const registry = new MockRegistry({
|
|
|
|
tap: t,
|
|
|
|
registry: npm.config.get('registry'),
|
|
|
|
authorization: authToken,
|
2020-10-02 17:52:19 -04:00
|
|
|
})
|
|
|
|
|
2024-04-30 23:53:22 -07:00
|
|
|
registry.getTokens(tokens)
|
|
|
|
registry.nock.delete(`/-/npm/v1/tokens/token/${tokens[0].key}`).reply(200)
|
|
|
|
await npm.exec('token', ['rm', tokens[0].key.slice(0, 8)])
|
2023-01-16 22:38:23 -05:00
|
|
|
|
|
|
|
t.equal(joinedOutput(), 'Removed 1 token')
|
2020-10-02 17:52:19 -04:00
|
|
|
})
|
|
|
|
|
2021-11-04 20:42:47 +00:00
|
|
|
t.test('token revoke multiple tokens', async t => {
|
2024-04-30 23:53:22 -07:00
|
|
|
const { npm, joinedOutput } = await loadMockNpm(t, {
|
|
|
|
config: { ...auth },
|
|
|
|
})
|
|
|
|
const registry = new MockRegistry({
|
|
|
|
tap: t,
|
|
|
|
registry: npm.config.get('registry'),
|
|
|
|
authorization: authToken,
|
2020-10-02 17:52:19 -04:00
|
|
|
})
|
|
|
|
|
2024-04-30 23:53:22 -07:00
|
|
|
registry.getTokens(tokens)
|
|
|
|
registry.nock.delete(`/-/npm/v1/tokens/token/${tokens[0].key}`).reply(200)
|
|
|
|
registry.nock.delete(`/-/npm/v1/tokens/token/${tokens[1].key}`).reply(200)
|
|
|
|
await npm.exec('token', ['rm', tokens[0].key.slice(0, 8), tokens[1].key.slice(0, 8)])
|
2023-01-16 22:38:23 -05:00
|
|
|
|
|
|
|
t.equal(joinedOutput(), 'Removed 2 tokens')
|
2020-10-02 17:52:19 -04:00
|
|
|
})
|
|
|
|
|
2021-11-04 20:42:47 +00:00
|
|
|
t.test('token revoke json output', async t => {
|
2024-04-30 23:53:22 -07:00
|
|
|
const { npm, joinedOutput } = await loadMockNpm(t, {
|
|
|
|
config: {
|
|
|
|
...auth,
|
|
|
|
json: true,
|
2020-10-02 17:52:19 -04:00
|
|
|
},
|
2024-04-30 23:53:22 -07:00
|
|
|
})
|
|
|
|
const registry = new MockRegistry({
|
|
|
|
tap: t,
|
|
|
|
registry: npm.config.get('registry'),
|
|
|
|
authorization: authToken,
|
2023-01-16 22:38:23 -05:00
|
|
|
})
|
2020-10-02 17:52:19 -04:00
|
|
|
|
2024-04-30 23:53:22 -07:00
|
|
|
registry.getTokens(tokens)
|
|
|
|
registry.nock.delete(`/-/npm/v1/tokens/token/${tokens[0].key}`).reply(200)
|
|
|
|
await npm.exec('token', ['rm', tokens[0].key.slice(0, 8)])
|
2023-01-16 22:38:23 -05:00
|
|
|
|
|
|
|
const parsed = JSON.parse(joinedOutput())
|
2024-04-30 23:53:22 -07:00
|
|
|
t.same(parsed, [tokens[0].key], 'logs the token as json')
|
2020-10-02 17:52:19 -04:00
|
|
|
})
|
|
|
|
|
2021-11-04 20:42:47 +00:00
|
|
|
t.test('token revoke parseable output', async t => {
|
2024-04-30 23:53:22 -07:00
|
|
|
const { npm, joinedOutput } = await loadMockNpm(t, {
|
|
|
|
config: {
|
|
|
|
...auth,
|
|
|
|
parseable: true,
|
2020-10-02 17:52:19 -04:00
|
|
|
},
|
|
|
|
})
|
2024-04-30 23:53:22 -07:00
|
|
|
const registry = new MockRegistry({
|
|
|
|
tap: t,
|
|
|
|
registry: npm.config.get('registry'),
|
|
|
|
authorization: authToken,
|
|
|
|
})
|
2020-10-02 17:52:19 -04:00
|
|
|
|
2024-04-30 23:53:22 -07:00
|
|
|
registry.getTokens(tokens)
|
|
|
|
registry.nock.delete(`/-/npm/v1/tokens/token/${tokens[0].key}`).reply(200)
|
|
|
|
await npm.exec('token', ['rm', tokens[0].key.slice(0, 8)])
|
|
|
|
t.equal(joinedOutput(), tokens[0].key, 'logs the token as a string')
|
2020-10-02 17:52:19 -04:00
|
|
|
})
|
|
|
|
|
2021-11-04 20:42:47 +00:00
|
|
|
t.test('token revoke by token', async t => {
|
2024-04-30 23:53:22 -07:00
|
|
|
const { npm, joinedOutput } = await loadMockNpm(t, {
|
|
|
|
config: { ...auth },
|
2020-10-02 17:52:19 -04:00
|
|
|
})
|
2024-04-30 23:53:22 -07:00
|
|
|
const registry = new MockRegistry({
|
|
|
|
tap: t,
|
|
|
|
registry: npm.config.get('registry'),
|
|
|
|
authorization: authToken,
|
|
|
|
})
|
|
|
|
registry.getTokens(tokens)
|
|
|
|
registry.nock.delete(`/-/npm/v1/tokens/token/${tokens[0].token}`).reply(200)
|
|
|
|
await npm.exec('token', ['rm', tokens[0].token])
|
2023-01-16 22:38:23 -05:00
|
|
|
t.equal(joinedOutput(), 'Removed 1 token')
|
2020-10-02 17:52:19 -04:00
|
|
|
})
|
|
|
|
|
2021-11-04 20:42:47 +00:00
|
|
|
t.test('token revoke requires an id', async t => {
|
2024-04-30 23:53:22 -07:00
|
|
|
const { npm } = await loadMockNpm(t)
|
2020-10-02 17:52:19 -04:00
|
|
|
|
2024-04-30 23:53:22 -07:00
|
|
|
await t.rejects(npm.exec('token', ['rm']), {
|
|
|
|
code: 'EUSAGE',
|
|
|
|
message: '`<tokenKey>` argument is required',
|
|
|
|
})
|
2020-10-02 17:52:19 -04:00
|
|
|
})
|
|
|
|
|
2021-11-04 20:42:47 +00:00
|
|
|
t.test('token revoke ambiguous id errors', async t => {
|
2024-04-30 23:53:22 -07:00
|
|
|
const { npm } = await loadMockNpm(t, {
|
|
|
|
config: { ...auth },
|
|
|
|
})
|
|
|
|
const registry = new MockRegistry({
|
|
|
|
tap: t,
|
|
|
|
registry: npm.config.get('registry'),
|
|
|
|
authorization: authToken,
|
|
|
|
})
|
|
|
|
registry.getTokens(tokens)
|
|
|
|
await t.rejects(npm.exec('token', ['rm', 'abcd']), {
|
|
|
|
message: /Token ID "abcd" was ambiguous/,
|
2020-10-02 17:52:19 -04:00
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2024-04-30 23:53:22 -07:00
|
|
|
t.test('token revoke unknown token', async t => {
|
|
|
|
const { npm } = await loadMockNpm(t, {
|
|
|
|
config: { ...auth },
|
|
|
|
})
|
|
|
|
const registry = new MockRegistry({
|
|
|
|
tap: t,
|
|
|
|
registry: npm.config.get('registry'),
|
|
|
|
authorization: authToken,
|
2020-10-02 17:52:19 -04:00
|
|
|
})
|
|
|
|
|
2024-04-30 23:53:22 -07:00
|
|
|
registry.getTokens(tokens)
|
|
|
|
await t.rejects(npm.exec('token', ['rm', '0xnotreal']),
|
|
|
|
'Unknown token id or value 0xnotreal'
|
|
|
|
)
|
2020-10-02 17:52:19 -04:00
|
|
|
})
|
|
|
|
|
2021-11-04 20:42:47 +00:00
|
|
|
t.test('token create', async t => {
|
2024-04-30 23:53:22 -07:00
|
|
|
const cidr = ['10.0.0.0/8', '192.168.1.0/24']
|
|
|
|
const { npm, outputs } = await loadMockNpm(t, {
|
2023-01-16 22:38:23 -05:00
|
|
|
config: {
|
2024-04-30 23:53:22 -07:00
|
|
|
...auth,
|
|
|
|
cidr,
|
2020-10-02 17:52:19 -04:00
|
|
|
},
|
2023-01-16 22:38:23 -05:00
|
|
|
})
|
2024-04-30 23:53:22 -07:00
|
|
|
const registry = new MockRegistry({
|
|
|
|
tap: t,
|
|
|
|
registry: npm.config.get('registry'),
|
|
|
|
authorization: authToken,
|
|
|
|
})
|
|
|
|
const stdin = new stream.PassThrough()
|
|
|
|
stdin.write(`${password}\n`)
|
|
|
|
mockGlobals(t, {
|
|
|
|
'process.stdin': stdin,
|
|
|
|
'process.stdout': new stream.PassThrough(), // to quiet readline
|
|
|
|
}, { replace: true })
|
|
|
|
registry.createToken({ password, cidr })
|
|
|
|
await npm.exec('token', ['create'])
|
|
|
|
t.strictSame(outputs, [
|
|
|
|
'',
|
|
|
|
'Created publish token n3wt0k3n',
|
|
|
|
'with IP whitelist: 10.0.0.0/8,192.168.1.0/24',
|
|
|
|
])
|
|
|
|
})
|
2020-10-02 17:52:19 -04:00
|
|
|
|
2024-04-30 23:53:22 -07:00
|
|
|
t.test('token create read only', async t => {
|
|
|
|
const { npm, outputs } = await loadMockNpm(t, {
|
|
|
|
config: {
|
|
|
|
...auth,
|
|
|
|
'read-only': true,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
const registry = new MockRegistry({
|
|
|
|
tap: t,
|
|
|
|
registry: npm.config.get('registry'),
|
|
|
|
authorization: authToken,
|
|
|
|
})
|
|
|
|
const stdin = new stream.PassThrough()
|
|
|
|
stdin.write(`${password}\n`)
|
|
|
|
mockGlobals(t, {
|
|
|
|
'process.stdin': stdin,
|
|
|
|
'process.stdout': new stream.PassThrough(), // to quiet readline
|
|
|
|
}, { replace: true })
|
|
|
|
registry.createToken({ readonly: true, password })
|
|
|
|
await npm.exec('token', ['create'])
|
|
|
|
t.strictSame(outputs, [
|
|
|
|
'',
|
|
|
|
'Created read only token n3wt0k3n',
|
|
|
|
])
|
2020-10-02 17:52:19 -04:00
|
|
|
})
|
|
|
|
|
2021-11-04 20:42:47 +00:00
|
|
|
t.test('token create json output', async t => {
|
2024-04-30 23:53:22 -07:00
|
|
|
const cidr = ['10.0.0.0/8', '192.168.1.0/24']
|
|
|
|
const { npm, joinedOutput } = await loadMockNpm(t, {
|
|
|
|
config: {
|
|
|
|
...auth,
|
|
|
|
json: true,
|
|
|
|
cidr,
|
2020-11-17 15:37:44 -05:00
|
|
|
},
|
2020-10-02 17:52:19 -04:00
|
|
|
})
|
2024-04-30 23:53:22 -07:00
|
|
|
const registry = new MockRegistry({
|
|
|
|
tap: t,
|
|
|
|
registry: npm.config.get('registry'),
|
|
|
|
authorization: authToken,
|
|
|
|
})
|
|
|
|
const stdin = new stream.PassThrough()
|
|
|
|
stdin.write(`${password}\n`)
|
|
|
|
mockGlobals(t, {
|
|
|
|
'process.stdin': stdin,
|
|
|
|
'process.stdout': new stream.PassThrough(), // to quiet readline
|
|
|
|
}, { replace: true })
|
|
|
|
registry.createToken({ password, cidr })
|
|
|
|
await npm.exec('token', ['create'])
|
|
|
|
const parsed = JSON.parse(joinedOutput())
|
|
|
|
t.match(
|
|
|
|
parsed,
|
|
|
|
{ token: 'n3wt0k3n', readonly: false, cidr_whitelist: cidr }
|
|
|
|
)
|
|
|
|
t.ok(parsed.created, 'also returns created')
|
2020-10-02 17:52:19 -04:00
|
|
|
})
|
|
|
|
|
2021-11-04 20:42:47 +00:00
|
|
|
t.test('token create parseable output', async t => {
|
2024-04-30 23:53:22 -07:00
|
|
|
const cidr = ['10.0.0.0/8', '192.168.1.0/24']
|
|
|
|
const { npm, outputs } = await loadMockNpm(t, {
|
|
|
|
config: {
|
|
|
|
...auth,
|
|
|
|
parseable: true,
|
|
|
|
cidr,
|
2020-10-02 17:52:19 -04:00
|
|
|
},
|
|
|
|
})
|
2024-04-30 23:53:22 -07:00
|
|
|
const registry = new MockRegistry({
|
|
|
|
tap: t,
|
|
|
|
registry: npm.config.get('registry'),
|
|
|
|
authorization: authToken,
|
|
|
|
})
|
|
|
|
const stdin = new stream.PassThrough()
|
|
|
|
stdin.write(`${password}\n`)
|
|
|
|
mockGlobals(t, {
|
|
|
|
'process.stdin': stdin,
|
|
|
|
'process.stdout': new stream.PassThrough(), // to quiet readline
|
|
|
|
}, { replace: true })
|
|
|
|
registry.createToken({ password, cidr })
|
|
|
|
await npm.exec('token', ['create'])
|
|
|
|
t.equal(outputs[1], 'token\tn3wt0k3n')
|
|
|
|
t.ok(outputs[2].startsWith('created\t'))
|
|
|
|
t.equal(outputs[3], 'readonly\tfalse')
|
|
|
|
t.equal(outputs[4], 'cidr_whitelist\t10.0.0.0/8,192.168.1.0/24')
|
2020-10-02 17:52:19 -04:00
|
|
|
})
|
|
|
|
|
2021-11-04 20:42:47 +00:00
|
|
|
t.test('token create ipv6 cidr', async t => {
|
2024-04-30 23:53:22 -07:00
|
|
|
const { npm } = await loadMockNpm(t, {
|
|
|
|
config: {
|
|
|
|
...auth,
|
|
|
|
cidr: '::1/128',
|
2020-11-17 15:37:44 -05:00
|
|
|
},
|
2020-10-02 17:52:19 -04:00
|
|
|
})
|
2024-04-30 23:53:22 -07:00
|
|
|
await t.rejects(npm.exec('token', ['create'], {
|
|
|
|
code: 'EINVALIDCIDR',
|
|
|
|
message: /CIDR whitelist can only contain IPv4 addresses, ::1\/128 is IPv6/,
|
|
|
|
}))
|
2020-10-02 17:52:19 -04:00
|
|
|
})
|
|
|
|
|
2021-11-04 20:42:47 +00:00
|
|
|
t.test('token create invalid cidr', async t => {
|
2024-04-30 23:53:22 -07:00
|
|
|
const { npm } = await loadMockNpm(t, {
|
|
|
|
config: {
|
|
|
|
...auth,
|
|
|
|
cidr: 'apple/cider',
|
2020-11-17 15:37:44 -05:00
|
|
|
},
|
2020-10-02 17:52:19 -04:00
|
|
|
})
|
2024-04-30 23:53:22 -07:00
|
|
|
await t.rejects(npm.exec('token', ['create'], {
|
|
|
|
code: 'EINVALIDCIDR',
|
|
|
|
message: 'CIDR whitelist contains invalid CIDR entry: apple/cider',
|
|
|
|
}))
|
2020-10-02 17:52:19 -04:00
|
|
|
})
|