Skip to content
This repository has been archived by the owner on Aug 11, 2021. It is now read-only.

Commit

Permalink
Add support for npm access to set per-package 2fa requirements
Browse files Browse the repository at this point in the history
  • Loading branch information
iarna committed Jul 13, 2018
1 parent 778e2ef commit 25901ab
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 12 deletions.
14 changes: 14 additions & 0 deletions lib/access.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ subcommands.public = function (uri, params, cb) {
subcommands.restricted = function (uri, params, cb) {
return setAccess.call(this, 'restricted', uri, params, cb)
}
subcommands['2fa-required'] = function (uri, params, cb) {
return setRequires2fa.call(this, true, uri, params, cb)
}
subcommands['2fa-not-required'] = function (uri, params, cb) {
return setRequires2fa.call(this, false, uri, params, cb)
}

function setAccess (access, uri, params, cb) {
return this.request(apiUri(uri, 'package', params.package, 'access'), {
Expand All @@ -25,6 +31,14 @@ function setAccess (access, uri, params, cb) {
}, cb)
}

function setRequires2fa (requires2fa, uri, params, cb) {
return this.request(apiUri(uri, 'package', params.package, 'access'), {
method: 'POST',
auth: params.auth,
body: JSON.stringify({ publish_requires_tfa: requires2fa })
}, cb)
}

subcommands.grant = function (uri, params, cb) {
var reqUri = apiUri(uri, 'team', params.scope, params.team, 'package')
return this.request(reqUri, {
Expand Down
35 changes: 24 additions & 11 deletions lib/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -289,13 +289,26 @@ function requestDone (method, where, cb) {
}

if (!parsed.error) {
er = makeError(
'Registry returned ' + response.statusCode +
' for ' + method +
' on ' + where,
name,
response.statusCode
)
if (response.statusCode === 401 && response.headers['www-authenticate']) {
const auth = response.headers['www-authenticate'].split(/,\s*/).map(s => s.toLowerCase())
if (auth.indexOf('ipaddress') !== -1) {
er = makeError('Login is not allowed from your IP address', name, response.statusCode, 'EAUTHIP')
} else if (auth.indexOf('otp') !== -1) {
er = makeError('OTP required for this operation', name, response.statusCode, 'EOTP')
} else {
er = makeError('Unable to authenticate, need: ' + response.headers['www-authenticate'], name, response.statusCode, 'EAUTHUNKNOWN')
}
} else {
const msg = parsed.message ? ': ' + parsed.message : ''
er = makeError(
'Registry returned ' + response.statusCode +
' for ' + method +
' on ' + where +
msg,
name,
response.statusCode
)
}
} else if (name && parsed.error === 'not_found') {
er = makeError('404 Not Found: ' + name, name, response.statusCode)
} else if (name && parsed.error === 'User not found') {
Expand All @@ -312,12 +325,12 @@ function requestDone (method, where, cb) {
}.bind(this)
}

function makeError (message, name, code) {
function makeError (message, name, statusCode, code) {
var er = new Error(message)
if (name) er.pkgid = name
if (code) {
er.statusCode = code
er.code = 'E' + code
if (statusCode) {
er.statusCode = statusCode
er.code = code || 'E' + statusCode
}
return er
}
36 changes: 35 additions & 1 deletion test/access.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ var UNSCOPED = {
}

var commands = [
'public', 'restricted', 'grant', 'revoke', 'ls-packages', 'ls-collaborators'
'public', 'restricted', 'grant', 'revoke', 'ls-packages', 'ls-collaborators', '2fa-required', '2fa-not-required'
]

test('access public', function (t) {
Expand All @@ -44,6 +44,40 @@ test('access public', function (t) {
})
})

test('access 2fa-required', function (t) {
server.expect('POST', '/-/package/%40foo%2Fbar/access', function (req, res) {
t.equal(req.method, 'POST', 'requested with POST')
onJsonReq(req, function (json) {
t.deepEqual(json, { publish_requires_tfa: true }, 'request payload ok')
res.statusCode = 200
res.json('ok')
})
})
var params = Object.create(PARAMS)
params.package = '@foo/bar'
client.access('2fa-required', URI, params, function (error, data) {
t.ifError(error, 'no errors')
t.end()
})
})

test('access 2fa-not-required', function (t) {
server.expect('POST', '/-/package/%40foo%2Fbar/access', function (req, res) {
t.equal(req.method, 'POST', 'requested with POST')
onJsonReq(req, function (json) {
t.deepEqual(json, { publish_requires_tfa: false }, 'request payload ok')
res.statusCode = 200
res.json('ok')
})
})
var params = Object.create(PARAMS)
params.package = '@foo/bar'
client.access('2fa-not-required', URI, params, function (error, data) {
t.ifError(error, 'no errors')
t.end()
})
})

test('access restricted', function (t) {
server.expect('POST', '/-/package/%40foo%2Fbar/access', function (req, res) {
t.equal(req.method, 'POST')
Expand Down

0 comments on commit 25901ab

Please sign in to comment.