diff options
Diffstat (limited to 'deps/npm/node_modules/npm-profile/index.js')
-rw-r--r-- | deps/npm/node_modules/npm-profile/index.js | 187 |
1 files changed, 163 insertions, 24 deletions
diff --git a/deps/npm/node_modules/npm-profile/index.js b/deps/npm/node_modules/npm-profile/index.js index 611200c5f8..f99b631e63 100644 --- a/deps/npm/node_modules/npm-profile/index.js +++ b/deps/npm/node_modules/npm-profile/index.js @@ -2,16 +2,123 @@ const fetch = require('make-fetch-happen').defaults({retry: false}) const validate = require('aproba') const url = require('url') +const os = require('os') -exports.adduser = adduser +exports.adduserCouch = adduserCouch +exports.loginCouch = loginCouch +exports.adduserWeb = adduserWeb +exports.loginWeb = loginWeb exports.login = login +exports.adduser = adduser exports.get = get exports.set = set exports.listTokens = listTokens exports.removeToken = removeToken exports.createToken = createToken -function adduser (username, email, password, conf) { +// try loginWeb, catch the "not supported" message and fall back to couch +function login (opener, prompter, conf) { + validate('FFO', arguments) + return loginWeb(opener, conf).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)) + } else { + throw er + } + }) +} + +function adduser (opener, prompter, conf) { + validate('FFO', arguments) + return adduserWeb(opener, conf).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)) + } else { + throw er + } + }) +} + +function adduserWeb (opener, conf) { + validate('FO', arguments) + const body = { create: true } + process.emit('log', 'verbose', 'web adduser', 'before first POST') + return webAuth(opener, conf, body) +} + +function loginWeb (opener, conf) { + validate('FO', arguments) + process.emit('log', 'verbose', 'web login', 'before first POST') + return webAuth(opener, conf, {}) +} + +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, + method: 'POST', + body: body, + opts: conf.opts, + saveResponse: true + }).then(result => { + const res = result[0] + const content = result[1] + 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) + } + 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) + } + } + }).catch(er => { + if (er.statusCode >= 400 && er.statusCode <= 499) { + throw new WebLoginNotSupported('POST', target, { + status: er.statusCode, + headers: { raw: () => er.headers } + }, er.body) + } else { + throw er + } + }) +} + +function adduserCouch (username, email, password, conf) { validate('SSSO', arguments) if (!conf.opts) conf.opts = {} const userobj = { @@ -32,9 +139,13 @@ function adduser (username, email, password, conf) { 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 + }) } -function login (username, password, conf) { +function loginCouch (username, password, conf) { validate('SSO', arguments) const userobj = { _id: 'org.couchdb.user:' + username, @@ -52,7 +163,10 @@ function login (username, password, conf) { 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 => { - if (err.code === 'E400') err.message = `There is no user with the username "${username}".` + 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 => { Object.keys(result).forEach(function (k) { @@ -73,6 +187,9 @@ function login (username, password, conf) { } return fetchJSON(Object.assign({}, conf, req)) }) + }).then(result => { + result.username = username + return result }) } @@ -145,37 +262,59 @@ class HttpErrorBase extends Error { } } -class General extends HttpErrorBase { +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}` + } + Error.captureStackTrace(this, HttpErrorGeneral) + } +} + +class WebLoginInvalidResponse extends HttpErrorBase { constructor (method, target, res, body) { super(method, target, res, body) - this.message = `Registry returned ${this.statusCode} for ${this.method} on ${this.href}` + this.message = 'Invalid response from web login endpoint' + Error.captureStackTrace(this, WebLoginInvalidResponse) } } -class AuthOTP extends HttpErrorBase { +class WebLoginNotSupported extends HttpErrorBase { + constructor (method, target, res, body) { + super(method, target, 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, AuthOTP) + Error.captureStackTrace(this, HttpErrorAuthOTP) } } -class AuthIPAddress extends HttpErrorBase { - constructor (res, body) { +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, AuthIPAddress) + Error.captureStackTrace(this, HttpErrorAuthIPAddress) } } -class AuthUnknown extends HttpErrorBase { +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 = 'EAUTHIP' - Error.captureStackTrace(this, AuthIPAddress) + Error.captureStackTrace(this, HttpErrorAuthUnknown) } } @@ -201,10 +340,13 @@ function fetchJSON (conf) { fetchOpts.headers['Content-Type'] = 'application/json' fetchOpts.body = JSON.stringify(conf.body) } - process.emit('log', 'http', 'request', '→',conf.method || 'GET', conf.target) + 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 { @@ -219,24 +361,21 @@ function fetchJSON (conf) { }).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 AuthIPAddress(conf.method, conf.target, res, content) + throw new HttpErrorAuthIPAddress(conf.method, conf.target, res, content) } else if (auth.indexOf('otp') !== -1) { - throw new AuthOTP(conf.method, conf.target, res, content) + throw new HttpErrorAuthOTP(conf.method, conf.target, res, content) } else { - throw new AuthUnknown(conf.method, conf.target, res, content) + throw new HttpErrorAuthUnknown(conf.method, conf.target, res, content) } } else if (res.status < 200 || res.status >= 300) { - if (typeof content === 'object' && content.error) { - return content - } else { - throw new General(conf.method, conf.target, res, content) - } + throw new HttpErrorGeneral(conf.method, conf.target, res, content) } else { - return content + return retVal } }) } @@ -257,4 +396,4 @@ function packageName (href) { } catch (_) { // this is ok } -}
\ No newline at end of file +} |