summaryrefslogtreecommitdiff
path: root/deps/npm/node_modules/npm-profile/index.js
diff options
context:
space:
mode:
Diffstat (limited to 'deps/npm/node_modules/npm-profile/index.js')
-rw-r--r--deps/npm/node_modules/npm-profile/index.js389
1 files changed, 137 insertions, 252 deletions
diff --git a/deps/npm/node_modules/npm-profile/index.js b/deps/npm/node_modules/npm-profile/index.js
index 023ddae40c..b7f753fb4d 100644
--- a/deps/npm/node_modules/npm-profile/index.js
+++ b/deps/npm/node_modules/npm-profile/index.js
@@ -1,8 +1,10 @@
'use strict'
-const fetch = require('make-fetch-happen').defaults({retry: false})
-const validate = require('aproba')
-const url = require('url')
+
+const fetch = require('npm-registry-fetch')
+const {HttpErrorBase} = require('npm-registry-fetch/errors.js')
const os = require('os')
+const pudding = require('figgy-pudding')
+const validate = require('aproba')
exports.adduserCouch = adduserCouch
exports.loginCouch = loginCouch
@@ -16,99 +18,83 @@ exports.listTokens = listTokens
exports.removeToken = removeToken
exports.createToken = createToken
+const ProfileConfig = pudding({
+ creds: {},
+ hostname: {},
+ otp: {}
+})
+
// try loginWeb, catch the "not supported" message and fall back to couch
-function login (opener, prompter, conf) {
+function login (opener, prompter, opts) {
validate('FFO', arguments)
- return loginWeb(opener, conf).catch(er => {
+ opts = ProfileConfig(opts)
+ return loginWeb(opener, opts).catch(er => {
if (er instanceof WebLoginNotSupported) {
process.emit('log', 'verbose', 'web login not supported, trying couch')
- return prompter(conf.creds)
- .then(data => loginCouch(data.username, data.password, conf))
+ return prompter(opts.creds)
+ .then(data => loginCouch(data.username, data.password, opts))
} else {
throw er
}
})
}
-function adduser (opener, prompter, conf) {
+function adduser (opener, prompter, opts) {
validate('FFO', arguments)
- return adduserWeb(opener, conf).catch(er => {
+ opts = ProfileConfig(opts)
+ return adduserWeb(opener, opts).catch(er => {
if (er instanceof WebLoginNotSupported) {
process.emit('log', 'verbose', 'web adduser not supported, trying couch')
- return prompter(conf.creds)
- .then(data => adduserCouch(data.username, data.email, data.password, conf))
+ return prompter(opts.creds)
+ .then(data => adduserCouch(data.username, data.email, data.password, opts))
} else {
throw er
}
})
}
-function adduserWeb (opener, conf) {
+function adduserWeb (opener, opts) {
validate('FO', arguments)
const body = { create: true }
process.emit('log', 'verbose', 'web adduser', 'before first POST')
- return webAuth(opener, conf, body)
+ return webAuth(opener, opts, body)
}
-function loginWeb (opener, conf) {
+function loginWeb (opener, opts) {
validate('FO', arguments)
process.emit('log', 'verbose', 'web login', 'before first POST')
- return webAuth(opener, conf, {})
+ return webAuth(opener, opts, {})
}
-function webAuth (opener, conf, body) {
- if (!conf.opts) conf.opts = {}
- const target = url.resolve(conf.registry, '-/v1/login')
- body.hostname = conf.hostname || os.hostname()
- return fetchJSON({
- target: target,
+function webAuth (opener, opts, body) {
+ opts = ProfileConfig(opts)
+ body.hostname = opts.hostname || os.hostname()
+ const target = '/-/v1/login'
+ return fetch(target, opts.concat({
method: 'POST',
- body: body,
- opts: conf.opts,
- saveResponse: true
- }).then(result => {
- const res = result[0]
- const content = result[1]
+ body
+ })).then(res => {
+ return Promise.all([res, res.json()])
+ }).then(([res, content]) => {
+ const {doneUrl, loginUrl} = content
process.emit('log', 'verbose', 'web auth', 'got response', content)
- const doneUrl = content.doneUrl
- const loginUrl = content.loginUrl
- if (typeof doneUrl !== 'string' ||
- typeof loginUrl !== 'string' ||
- !doneUrl || !loginUrl) {
- throw new WebLoginInvalidResponse('POST', target, res, content)
+ if (
+ typeof doneUrl !== 'string' ||
+ typeof loginUrl !== 'string' ||
+ !doneUrl ||
+ !loginUrl
+ ) {
+ throw new WebLoginInvalidResponse('POST', res, content)
}
+ return content
+ }).then(({doneUrl, loginUrl}) => {
process.emit('log', 'verbose', 'web auth', 'opening url pair')
- const doneConf = {
- target: doneUrl,
- method: 'GET',
- opts: conf.opts,
- saveResponse: true
- }
- return opener(loginUrl).then(() => fetchJSON(doneConf)).then(onDone)
- function onDone (result) {
- const res = result[0]
- const content = result[1]
- if (res.status === 200) {
- if (!content.token) {
- throw new WebLoginInvalidResponse('GET', doneUrl, res, content)
- } else {
- return content
- }
- } else if (res.status === 202) {
- const retry = +res.headers.get('retry-after')
- if (retry > 0) {
- return new Promise(resolve => setTimeout(resolve, 1000 * retry))
- .then(() => fetchJSON(doneConf)).then(onDone)
- } else {
- return fetchJSON(doneConf).then(onDone)
- }
- } else {
- throw new WebLoginInvalidResponse('GET', doneUrl, res, content)
- }
- }
+ return opener(loginUrl).then(
+ () => webAuthCheckLogin(doneUrl, opts.concat({cache: false}))
+ )
}).catch(er => {
if ((er.statusCode >= 400 && er.statusCode <= 499) || er.statusCode === 500) {
- throw new WebLoginNotSupported('POST', target, {
+ throw new WebLoginNotSupported('POST', {
status: er.statusCode,
headers: { raw: () => er.headers }
}, er.body)
@@ -118,10 +104,33 @@ function webAuth (opener, conf, body) {
})
}
-function adduserCouch (username, email, password, conf) {
+function webAuthCheckLogin (doneUrl, opts) {
+ return fetch(doneUrl, opts).then(res => {
+ return Promise.all([res, res.json()])
+ }).then(([res, content]) => {
+ if (res.status === 200) {
+ if (!content.token) {
+ throw new WebLoginInvalidResponse('GET', res, content)
+ } else {
+ return content
+ }
+ } else if (res.status === 202) {
+ const retry = +res.headers.get('retry-after') * 1000
+ if (retry > 0) {
+ return sleep(retry).then(() => webAuthCheckLogin(doneUrl, opts))
+ } else {
+ return webAuthCheckLogin(doneUrl, opts)
+ }
+ } else {
+ throw new WebLoginInvalidResponse('GET', res, content)
+ }
+ })
+}
+
+function adduserCouch (username, email, password, opts) {
validate('SSSO', arguments)
- if (!conf.opts) conf.opts = {}
- const userobj = {
+ opts = ProfileConfig(opts)
+ const body = {
_id: 'org.couchdb.user:' + username,
name: username,
password: password,
@@ -131,23 +140,25 @@ function adduserCouch (username, email, password, conf) {
date: new Date().toISOString()
}
const logObj = {}
- Object.keys(userobj).forEach(k => {
- logObj[k] = k === 'password' ? 'XXXXX' : userobj[k]
+ Object.keys(body).forEach(k => {
+ logObj[k] = k === 'password' ? 'XXXXX' : body[k]
})
process.emit('log', 'verbose', 'adduser', 'before first PUT', logObj)
- const target = url.resolve(conf.registry, '-/user/org.couchdb.user:' + encodeURIComponent(username))
-
- return fetchJSON({target: target, method: 'PUT', body: userobj, opts: conf.opts})
- .then(result => {
- result.username = username
- return result
- })
+ const target = '/-/user/org.couchdb.user:' + encodeURIComponent(username)
+ return fetch.json(target, opts.concat({
+ method: 'PUT',
+ body
+ })).then(result => {
+ result.username = username
+ return result
+ })
}
-function loginCouch (username, password, conf) {
+function loginCouch (username, password, opts) {
validate('SSO', arguments)
- const userobj = {
+ opts = ProfileConfig(opts)
+ const body = {
_id: 'org.couchdb.user:' + username,
name: username,
password: password,
@@ -156,36 +167,38 @@ function loginCouch (username, password, conf) {
date: new Date().toISOString()
}
const logObj = {}
- Object.keys(userobj).forEach(k => {
- logObj[k] = k === 'password' ? 'XXXXX' : userobj[k]
+ Object.keys(body).forEach(k => {
+ logObj[k] = k === 'password' ? 'XXXXX' : body[k]
})
process.emit('log', 'verbose', 'login', 'before first PUT', logObj)
- const target = url.resolve(conf.registry, '-/user/org.couchdb.user:' + encodeURIComponent(username))
- return fetchJSON(Object.assign({method: 'PUT', target: target, body: userobj}, conf)).catch(err => {
+ const target = '-/user/org.couchdb.user:' + encodeURIComponent(username)
+ return fetch.json(target, opts.concat({
+ method: 'PUT',
+ body
+ })).catch(err => {
if (err.code === 'E400') {
err.message = `There is no user with the username "${username}".`
throw err
}
if (err.code !== 'E409') throw err
- return fetchJSON(Object.assign({method: 'GET', target: target + '?write=true'}, conf)).then(result => {
+ return fetch.json(target, opts.concat({
+ query: {write: true}
+ })).then(result => {
Object.keys(result).forEach(function (k) {
- if (!userobj[k] || k === 'roles') {
- userobj[k] = result[k]
+ if (!body[k] || k === 'roles') {
+ body[k] = result[k]
}
})
- const req = {
+ return fetch.json(`${target}/-rev/${body._rev}`, opts.concat({
method: 'PUT',
- target: target + '/-rev/' + userobj._rev,
- body: userobj,
- auth: {
- basic: {
- username: username,
- password: password
- }
+ body,
+ forceAuth: {
+ username,
+ password: Buffer.from(password, 'utf8').toString('base64'),
+ otp: opts.otp
}
- }
- return fetchJSON(Object.assign({}, conf, req))
+ }))
})
}).then(result => {
result.username = username
@@ -193,29 +206,31 @@ function loginCouch (username, password, conf) {
})
}
-function get (conf) {
+function get (opts) {
validate('O', arguments)
- const target = url.resolve(conf.registry, '-/npm/v1/user')
- return fetchJSON(Object.assign({target: target}, conf))
+ return fetch.json('/-/npm/v1/user', opts)
}
-function set (profile, conf) {
+function set (profile, opts) {
validate('OO', arguments)
- const target = url.resolve(conf.registry, '-/npm/v1/user')
Object.keys(profile).forEach(key => {
// profile keys can't be empty strings, but they CAN be null
if (profile[key] === '') profile[key] = null
})
- return fetchJSON(Object.assign({target: target, method: 'POST', body: profile}, conf))
+ return fetch.json('/-/npm/v1/user', ProfileConfig(opts, {
+ method: 'POST',
+ body: profile
+ }))
}
-function listTokens (conf) {
+function listTokens (opts) {
validate('O', arguments)
+ opts = ProfileConfig(opts)
- return untilLastPage(`-/npm/v1/tokens`)
+ return untilLastPage('/-/npm/v1/tokens')
function untilLastPage (href, objects) {
- return fetchJSON(Object.assign({target: url.resolve(conf.registry, href)}, conf)).then(result => {
+ return fetch.json(href, opts).then(result => {
objects = objects ? objects.concat(result.objects) : result.objects
if (result.urls.next) {
return untilLastPage(result.urls.next, objects)
@@ -226,174 +241,44 @@ function listTokens (conf) {
}
}
-function removeToken (tokenKey, conf) {
+function removeToken (tokenKey, opts) {
validate('SO', arguments)
- const target = url.resolve(conf.registry, `-/npm/v1/tokens/token/${tokenKey}`)
- return fetchJSON(Object.assign({target: target, method: 'DELETE'}, conf))
+ const target = `/-/npm/v1/tokens/token/${tokenKey}`
+ return fetch(target, ProfileConfig(opts, {
+ method: 'DELETE',
+ ignoreBody: true
+ })).then(() => null)
}
-function createToken (password, readonly, cidrs, conf) {
+function createToken (password, readonly, cidrs, opts) {
validate('SBAO', arguments)
- const target = url.resolve(conf.registry, '-/npm/v1/tokens')
- const props = {
- password: password,
- readonly: readonly,
- cidr_whitelist: cidrs
- }
- return fetchJSON(Object.assign({target: target, method: 'POST', body: props}, conf))
-}
-
-function FetchError (err, method, target) {
- err.method = method
- err.href = target
- return err
-}
-
-class HttpErrorBase extends Error {
- constructor (method, target, res, body) {
- super()
- this.headers = res.headers.raw()
- this.statusCode = res.status
- this.code = 'E' + res.status
- this.method = method
- this.target = target
- this.body = body
- this.pkgid = packageName(target)
- }
-}
-
-class HttpErrorGeneral extends HttpErrorBase {
- constructor (method, target, res, body) {
- super(method, target, res, body)
- if (body && body.error) {
- this.message = `Registry returned ${this.statusCode} for ${this.method} on ${this.target}: ${body.error}`
- } else {
- this.message = `Registry returned ${this.statusCode} for ${this.method} on ${this.target}`
+ return fetch.json('/-/npm/v1/tokens', ProfileConfig(opts, {
+ method: 'POST',
+ body: {
+ password: password,
+ readonly: readonly,
+ cidr_whitelist: cidrs
}
- Error.captureStackTrace(this, HttpErrorGeneral)
- }
+ }))
}
class WebLoginInvalidResponse extends HttpErrorBase {
- constructor (method, target, res, body) {
- super(method, target, res, body)
+ constructor (method, res, body) {
+ super(method, res, body)
this.message = 'Invalid response from web login endpoint'
Error.captureStackTrace(this, WebLoginInvalidResponse)
}
}
class WebLoginNotSupported extends HttpErrorBase {
- constructor (method, target, res, body) {
- super(method, target, res, body)
+ constructor (method, res, body) {
+ super(method, res, body)
this.message = 'Web login not supported'
this.code = 'ENYI'
Error.captureStackTrace(this, WebLoginNotSupported)
}
}
-class HttpErrorAuthOTP extends HttpErrorBase {
- constructor (method, target, res, body) {
- super(method, target, res, body)
- this.message = 'OTP required for authentication'
- this.code = 'EOTP'
- Error.captureStackTrace(this, HttpErrorAuthOTP)
- }
-}
-
-class HttpErrorAuthIPAddress extends HttpErrorBase {
- constructor (method, target, res, body) {
- super(method, target, res, body)
- this.message = 'Login is not allowed from your IP address'
- this.code = 'EAUTHIP'
- Error.captureStackTrace(this, HttpErrorAuthIPAddress)
- }
-}
-
-class HttpErrorAuthUnknown extends HttpErrorBase {
- constructor (method, target, res, body) {
- super(method, target, res, body)
- this.message = 'Unable to authenticate, need: ' + res.headers.get('www-authenticate')
- this.code = 'EAUTHUNKNOWN'
- Error.captureStackTrace(this, HttpErrorAuthUnknown)
- }
-}
-
-function authHeaders (auth) {
- const headers = {}
- if (!auth) return headers
- if (auth.otp) headers['npm-otp'] = auth.otp
- if (auth.token) {
- headers['Authorization'] = 'Bearer ' + auth.token
- } else if (auth.basic) {
- const basic = auth.basic.username + ':' + auth.basic.password
- headers['Authorization'] = 'Basic ' + Buffer.from(basic).toString('base64')
- }
- return headers
-}
-
-function fetchJSON (conf) {
- const fetchOpts = {
- method: conf.method,
- headers: Object.assign({}, conf.headers || (conf.auth && authHeaders(conf.auth)) || {})
- }
- if (conf.body != null) {
- fetchOpts.headers['Content-Type'] = 'application/json'
- fetchOpts.body = JSON.stringify(conf.body)
- }
- process.emit('log', 'http', 'request', '→', conf.method || 'GET', conf.target)
- return fetch.defaults(conf.opts || {})(conf.target, fetchOpts).catch(err => {
- throw new FetchError(err, conf.method, conf.target)
- }).then(res => {
- if (res.headers.has('npm-notice')) {
- process.emit('warn', 'notice', res.headers.get('npm-notice'))
- }
- if (res.headers.get('content-type') === 'application/json') {
- return res.json().then(content => [res, content])
- } else {
- return res.buffer().then(content => {
- try {
- return [res, JSON.parse(content)]
- } catch (_) {
- return [res, content]
- }
- })
- }
- }).then(result => {
- const res = result[0]
- const content = result[1]
- const retVal = conf.saveResponse ? result : content
- process.emit('log', 'http', res.status, `← ${res.statusText} (${conf.target})`)
- if (res.status === 401 && res.headers.get('www-authenticate')) {
- const auth = res.headers.get('www-authenticate').split(/,\s*/).map(s => s.toLowerCase())
- if (auth.indexOf('ipaddress') !== -1) {
- throw new HttpErrorAuthIPAddress(conf.method, conf.target, res, content)
- } else if (auth.indexOf('otp') !== -1) {
- throw new HttpErrorAuthOTP(conf.method, conf.target, res, content)
- } else {
- throw new HttpErrorAuthUnknown(conf.method, conf.target, res, content)
- }
- } else if (res.status < 200 || res.status >= 300) {
- throw new HttpErrorGeneral(conf.method, conf.target, res, content)
- } else {
- return retVal
- }
- })
-}
-
-function packageName (href) {
- try {
- let basePath = url.parse(href).pathname.substr(1)
- if (!basePath.match(/^-/)) {
- basePath = basePath.split('/')
- var index = basePath.indexOf('_rewrite')
- if (index === -1) {
- index = basePath.length - 1
- } else {
- index++
- }
- return decodeURIComponent(basePath[index])
- }
- } catch (_) {
- // this is ok
- }
+function sleep (ms) {
+ return new Promise((resolve, reject) => setTimeout(resolve, ms))
}