diff --git a/lib/profile.js b/lib/profile.js index d821973a07763..7ce9cb5cce5df 100644 --- a/lib/profile.js +++ b/lib/profile.js @@ -1,18 +1,23 @@ 'use strict' -const profile = require('libnpm/profile') -const npm = require('./npm.js') + +const BB = require('bluebird') + +const ansistyles = require('ansistyles') +const figgyPudding = require('figgy-pudding') +const inspect = require('util').inspect const log = require('npmlog') +const npm = require('./npm.js') +const npmConfig = require('./config/figgy-config.js') +const otplease = require('./utils/otplease.js') const output = require('./utils/output.js') +const profile = require('libnpm/profile') +const pulseTillDone = require('./utils/pulse-till-done.js') +const qrcodeTerminal = require('qrcode-terminal') +const queryString = require('query-string') const qw = require('qw') -const Table = require('cli-table3') -const ansistyles = require('ansistyles') -const Bluebird = require('bluebird') const readUserInfo = require('./utils/read-user-info.js') -const qrcodeTerminal = require('qrcode-terminal') +const Table = require('cli-table3') const url = require('url') -const queryString = require('query-string') -const pulseTillDone = require('./utils/pulse-till-done.js') -const inspect = require('util').inspect module.exports = profileCmd @@ -48,6 +53,13 @@ function withCb (prom, cb) { prom.then((value) => cb(null, value), cb) } +const ProfileOpts = figgyPudding({ + json: {}, + otp: {}, + parseable: {}, + registry: {} +}) + function profileCmd (args, cb) { if (args.length === 0) return cb(new Error(profileCmd.usage)) log.gauge.show('profile') @@ -75,36 +87,13 @@ function profileCmd (args, cb) { } } -function config () { - const conf = { - json: npm.config.get('json'), - parseable: npm.config.get('parseable'), - registry: npm.config.get('registry'), - otp: npm.config.get('otp') - } - const creds = npm.config.getCredentialsByURI(conf.registry) - if (creds.token) { - conf.auth = {token: creds.token} - } else if (creds.username) { - conf.auth = {basic: {username: creds.username, password: creds.password}} - } else if (creds.auth) { - const auth = Buffer.from(creds.auth, 'base64').toString().split(':', 2) - conf.auth = {basic: {username: auth[0], password: auth[1]}} - } else { - conf.auth = {} - } - - if (conf.otp) conf.auth.otp = conf.otp - return conf -} - const knownProfileKeys = qw` name email ${'two-factor auth'} fullname homepage freenode twitter github created updated` function get (args) { const tfa = 'two-factor auth' - const conf = config() + const conf = ProfileOpts(npmConfig()) return pulseTillDone.withPromise(profile.get(conf)).then((info) => { if (!info.cidr_whitelist) delete info.cidr_whitelist if (conf.json) { @@ -150,7 +139,7 @@ const writableProfileKeys = qw` email password fullname homepage freenode twitter github` function set (args) { - const conf = config() + let conf = ProfileOpts(npmConfig()) const prop = (args[0] || '').toLowerCase().trim() let value = args.length > 1 ? args.slice(1).join(' ') : null if (prop !== 'password' && value === null) { @@ -164,7 +153,7 @@ function set (args) { if (writableProfileKeys.indexOf(prop) === -1) { return Promise.reject(Error(`"${prop}" is not a property we can set. Valid properties are: ` + writableProfileKeys.join(', '))) } - return Bluebird.try(() => { + return BB.try(() => { if (prop === 'password') { return readUserInfo.password('Current password: ').then((current) => { return readPasswords().then((newpassword) => { @@ -193,23 +182,18 @@ function set (args) { const newUser = {} writableProfileKeys.forEach((k) => { newUser[k] = user[k] }) newUser[prop] = value - return profile.set(newUser, conf).catch((err) => { - if (err.code !== 'EOTP') throw err - return readUserInfo.otp().then((otp) => { - conf.auth.otp = otp - return profile.set(newUser, conf) + return otplease(conf, conf => profile.set(newUser, conf)) + .then((result) => { + if (conf.json) { + output(JSON.stringify({[prop]: result[prop]}, null, 2)) + } else if (conf.parseable) { + output(prop + '\t' + result[prop]) + } else if (result[prop] != null) { + output('Set', prop, 'to', result[prop]) + } else { + output('Set', prop) + } }) - }).then((result) => { - if (conf.json) { - output(JSON.stringify({[prop]: result[prop]}, null, 2)) - } else if (conf.parseable) { - output(prop + '\t' + result[prop]) - } else if (result[prop] != null) { - output('Set', prop, 'to', result[prop]) - } else { - output('Set', prop) - } - }) })) }) } @@ -225,7 +209,7 @@ function enable2fa (args) { ' auth-only - Require two-factor authentication only when logging in\n' + ' auth-and-writes - Require two-factor authentication when logging in AND when publishing')) } - const conf = config() + const conf = ProfileOpts(npmConfig()) if (conf.json || conf.parseable) { return Promise.reject(new Error( 'Enabling two-factor authentication is an interactive operation and ' + @@ -238,15 +222,18 @@ function enable2fa (args) { } } - return Bluebird.try(() => { + return BB.try(() => { // if they're using legacy auth currently then we have to update them to a // bearer token before continuing. - if (conf.auth.basic) { + const auth = getAuth(conf) + if (auth.basic) { log.info('profile', 'Updating authentication to bearer token') - return profile.login(conf.auth.basic.username, conf.auth.basic.password, conf).then((result) => { + return profile.createToken( + auth.basic.password, false, [], conf + ).then((result) => { if (!result.token) throw new Error('Your registry ' + conf.registry + 'does not seem to support bearer tokens. Bearer tokens are required for two-factor authentication') npm.config.setCredentialsByURI(conf.registry, {token: result.token}) - return Bluebird.fromNode((cb) => npm.config.save('user', cb)) + return BB.fromNode((cb) => npm.config.save('user', cb)) }) } }).then(() => { @@ -295,18 +282,36 @@ function enable2fa (args) { }) } +function getAuth (conf) { + const creds = npm.config.getCredentialsByURI(conf.registry) + let auth + if (creds.token) { + auth = {token: creds.token} + } else if (creds.username) { + auth = {basic: {username: creds.username, password: creds.password}} + } else if (creds.auth) { + const basic = Buffer.from(creds.auth, 'base64').toString().split(':', 2) + auth = {basic: {username: basic[0], password: basic[1]}} + } else { + auth = {} + } + + if (conf.otp) auth.otp = conf.otp + return auth +} + function disable2fa (args) { - const conf = config() + let conf = ProfileOpts(npmConfig()) return pulseTillDone.withPromise(profile.get(conf)).then((info) => { if (!info.tfa || info.tfa.pending) { output('Two factor authentication not enabled.') return } return readUserInfo.password().then((password) => { - return Bluebird.try(() => { - if (conf.auth.otp) return + return BB.try(() => { + if (conf.otp) return return readUserInfo.otp('Enter one-time password from your authenticator: ').then((otp) => { - conf.auth.otp = otp + conf = conf.concat({otp}) }) }).then(() => { log.info('profile', 'disabling tfa')