Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

profile: update profile to support new npm-profile API #131

Merged
merged 1 commit into from
Dec 21, 2018
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 65 additions & 60 deletions lib/profile.js
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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')
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand All @@ -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) => {
Expand Down Expand Up @@ -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)
}
})
}))
})
}
Expand All @@ -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 ' +
Expand All @@ -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(() => {
Expand Down Expand Up @@ -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')
Expand Down