Skip to content
This repository has been archived by the owner on Nov 3, 2022. It is now read-only.

Commit

Permalink
Support setting email without username/password
Browse files Browse the repository at this point in the history
  • Loading branch information
isaacs committed Dec 15, 2020
1 parent 6f3445d commit 9bf2ef5
Show file tree
Hide file tree
Showing 3 changed files with 191 additions and 11 deletions.
30 changes: 21 additions & 9 deletions lib/index.js
Expand Up @@ -178,6 +178,11 @@ class Config {
throw new Error('call config.load() before setting values')
if (!confTypes.has(where))
throw new Error('invalid config location param: ' + where)
if (key === '_auth') {
const { email } = this.getCredentialsByURI(this.get('registry'))
if (!email)
throw new Error('Cannot set _auth without first setting email')
}
this.data.get(where).data[key] = val

// this is now dirty, the next call to this.valid will have to check it
Expand Down Expand Up @@ -512,6 +517,9 @@ class Config {
if (where === 'user') {
const reg = this.get('registry')
const creds = this.getCredentialsByURI(reg)
// we ignore this error because the failed set already removed
// anything that might be a security hazard, and it won't be
// saved back to the .npmrc file, so we're good.
try { this.setCredentialsByURI(reg, creds) } catch (_) {}
}

Expand Down Expand Up @@ -576,18 +584,22 @@ class Config {
this.delete(`${nerfed}:email`, 'user')
this.delete(`${nerfed}:always-auth`, 'user')
} else if (username || password || email) {
if (!username)
throw new Error('must include username')
if (!password)
throw new Error('must include password')
if (username || password) {
if (!username)
throw new Error('must include username')
if (!password)
throw new Error('must include password')
}
if (!email)
throw new Error('must include email')
this.delete(`${nerfed}:_authToken`, 'user')
this.set(`${nerfed}:username`, username, 'user')
// note: not encrypted, no idea why we bothered to do this, but oh well
// protects against shoulder-hacks if password is memorable, I guess?
const encoded = Buffer.from(password, 'utf8').toString('base64')
this.set(`${nerfed}:_password`, encoded, 'user')
if (username || password) {
this.set(`${nerfed}:username`, username, 'user')
// note: not encrypted, no idea why we bothered to do this, but oh well
// protects against shoulder-hacks if password is memorable, I guess?
const encoded = Buffer.from(password, 'utf8').toString('base64')
this.set(`${nerfed}:_password`, encoded, 'user')
}
this.set(`${nerfed}:email`, email, 'user')
if (alwaysAuth !== undefined)
this.set(`${nerfed}:always-auth`, alwaysAuth, 'user')
Expand Down
63 changes: 63 additions & 0 deletions tap-snapshots/test-index.js-TAP.test.js
Expand Up @@ -20,6 +20,62 @@ Object {
}
`

exports[`test/index.js TAP credentials management def_passNoUser > default registry 1`] = `
Object {
"alwaysAuth": true,
"email": "i@izs.me",
}
`

exports[`test/index.js TAP credentials management def_passNoUser > default registry after set 1`] = `
Object {
"alwaysAuth": true,
"email": "i@izs.me",
}
`

exports[`test/index.js TAP credentials management def_passNoUser > other registry 1`] = `
Object {
"alwaysAuth": false,
"email": "i@izs.me",
}
`

exports[`test/index.js TAP credentials management def_passNoUser > other registry after set 1`] = `
Object {
"alwaysAuth": false,
"email": "i@izs.me",
}
`

exports[`test/index.js TAP credentials management def_userNoPass > default registry 1`] = `
Object {
"alwaysAuth": true,
"email": "i@izs.me",
}
`

exports[`test/index.js TAP credentials management def_userNoPass > default registry after set 1`] = `
Object {
"alwaysAuth": true,
"email": "i@izs.me",
}
`

exports[`test/index.js TAP credentials management def_userNoPass > other registry 1`] = `
Object {
"alwaysAuth": false,
"email": "i@izs.me",
}
`

exports[`test/index.js TAP credentials management def_userNoPass > other registry after set 1`] = `
Object {
"alwaysAuth": false,
"email": "i@izs.me",
}
`

exports[`test/index.js TAP credentials management def_userpass > default registry 1`] = `
Object {
"alwaysAuth": true,
Expand Down Expand Up @@ -47,6 +103,13 @@ Object {
}
`

exports[`test/index.js TAP credentials management def_userpass > other registry after set 1`] = `
Object {
"alwaysAuth": false,
"email": "i@izs.me",
}
`

exports[`test/index.js TAP credentials management nerfed_auth > default registry 1`] = `
Object {
"alwaysAuth": false,
Expand Down
109 changes: 107 additions & 2 deletions test/index.js
Expand Up @@ -562,6 +562,18 @@ t.test('credentials management', async t => {
_password = ${Buffer.from('world').toString('base64')}
email = i@izs.me
//registry.example/:always-auth = true
`,
},
def_userNoPass: {
'.npmrc': `username = hello
email = i@izs.me
//registry.example/:always-auth = true
`,
},
def_passNoUser: {
'.npmrc': `_password = ${Buffer.from('world').toString('base64')}
email = i@izs.me
//registry.example/:always-auth = true
`,
},
def_auth: {
Expand Down Expand Up @@ -595,6 +607,10 @@ always-auth = true`,
username: 'foo',
email: 'bar@baz.com',
}), { message: 'must include password' })
t.throws(() => c.setCredentialsByURI('http://x.com', {
password: 'foo',
email: 'bar@baz.com',
}), { message: 'must include username' })
c.setCredentialsByURI('http://x.com', {
username: 'foo',
password: 'bar',
Expand All @@ -617,14 +633,15 @@ always-auth = true`,
const otherAfterDelete = c.getCredentialsByURI(otherReg)
t.strictSame(Object.keys(otherAfterDelete), ['alwaysAuth'])

if (!d.token && !(d.email && d.username && d.password))
// we can have email on its own, but need both or none of user/pass
if (!d.token && (!d.email || (!!d.username !== !!d.password)))
t.throws(() => c.setCredentialsByURI(defReg, d))
else {
c.setCredentialsByURI(defReg, d)
t.matchSnapshot(c.getCredentialsByURI(defReg), 'default registry after set')
}

if (!o.token && !(o.email && o.username && o.password))
if (!o.token && (!o.email || (!!o.username !== !!o.password)))
t.throws(() => c.setCredentialsByURI(otherReg, o))
else {
c.setCredentialsByURI(otherReg, o)
Expand Down Expand Up @@ -754,3 +771,91 @@ t.test('finding the local prefix', t => {
})
t.end()
})

t.test('setting basic auth creds and email', async t => {
const registry = 'https://registry.npmjs.org/'
const path = t.testdir()
const _auth = Buffer.from('admin:admin').toString('base64')
const opts = {
shorthands: {},
argv: ['node', __filename, `--userconfig=${path}/.npmrc`],
defaults: {
registry,
'always-auth': false,
},
types: {},
npmPath: process.cwd(),
}
const c = new Config(opts)
await c.load()
t.throws(() => c.set('_auth'), 'cannot set _auth without first setting email')
c.set('email', 'name@example.com', 'user')
t.equal(c.get('email', 'user'), 'name@example.com', 'email was set')
await c.save('user')
t.equal(c.get('email', 'user'), undefined, 'email no longer top-level')
t.strictSame(c.getCredentialsByURI(registry), { email: 'name@example.com', alwaysAuth: false })
const d = new Config(opts)
await d.load()
t.strictSame(d.getCredentialsByURI(registry), { email: 'name@example.com', alwaysAuth: false })
d.set('_auth', _auth, 'user')
t.equal(d.get('_auth', 'user'), _auth, '_auth was set')
await d.save('user')
t.equal(d.get('_auth', 'user'), undefined, 'un-nerfed _auth deleted')
t.strictSame(d.getCredentialsByURI(registry), {
email: 'name@example.com',
username: 'admin',
password: 'admin',
auth: _auth,
alwaysAuth: false,
}, 'credentials saved and nerfed')
})

t.test('setting username/password/email individually', async t => {
const registry = 'https://registry.npmjs.org/'
const path = t.testdir()
const _auth = Buffer.from('admin:admin').toString('base64')
const opts = {
shorthands: {},
argv: ['node', __filename, `--userconfig=${path}/.npmrc`],
defaults: {
registry,
'always-auth': false,
},
types: {},
npmPath: process.cwd(),
}
const c = new Config(opts)
await c.load()
c.set('email', 'name@example.com', 'user')
t.equal(c.get('email'), 'name@example.com')
c.set('username', 'admin', 'user')
t.equal(c.get('username'), 'admin')
c.set('_password', Buffer.from('admin').toString('base64'), 'user')
t.equal(c.get('_password'), Buffer.from('admin').toString('base64'))
t.equal(c.get('_auth'), undefined)
await c.save('user')
t.equal(c.get('email'), undefined)
t.equal(c.get('username'), undefined)
t.equal(c.get('_password'), undefined)
t.equal(c.get('_auth'), undefined)
t.strictSame(c.getCredentialsByURI(registry), {
alwaysAuth: false,
email: 'name@example.com',
username: 'admin',
password: 'admin',
auth: Buffer.from('admin:admin').toString('base64'),
})
const d = new Config(opts)
await d.load()
t.equal(d.get('email'), undefined)
t.equal(d.get('username'), undefined)
t.equal(d.get('_password'), undefined)
t.equal(d.get('_auth'), undefined)
t.strictSame(d.getCredentialsByURI(registry), {
alwaysAuth: false,
email: 'name@example.com',
username: 'admin',
password: 'admin',
auth: Buffer.from('admin:admin').toString('base64'),
})
})

0 comments on commit 9bf2ef5

Please sign in to comment.