Skip to content

Commit 5c06a33

Browse files
authored
fix: clean up owner command and otplease (#4528)
1 parent 55ab38c commit 5c06a33

File tree

2 files changed

+64
-124
lines changed

2 files changed

+64
-124
lines changed

lib/commands/owner.js

Lines changed: 56 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -59,60 +59,39 @@ class Owner extends BaseCommand {
5959
}
6060

6161
async exec ([action, ...args]) {
62-
const opts = {
63-
...this.npm.flatOptions,
64-
}
6562
switch (action) {
6663
case 'ls':
6764
case 'list':
68-
return this.ls(args[0], opts)
65+
return this.ls(args[0])
6966
case 'add':
70-
return this.add(args[0], args[1], opts)
67+
return this.changeOwners(args[0], args[1], 'add')
7168
case 'rm':
7269
case 'remove':
73-
return this.rm(args[0], args[1], opts)
70+
return this.changeOwners(args[0], args[1], 'rm')
7471
default:
7572
throw this.usageError()
7673
}
7774
}
7875

79-
async ls (pkg, opts) {
80-
if (!pkg) {
81-
if (this.npm.config.get('global')) {
82-
throw this.usageError()
83-
}
84-
85-
const pkgName = await readLocalPkgName(this.npm.prefix)
86-
if (!pkgName) {
87-
throw this.usageError()
88-
}
89-
90-
pkg = pkgName
91-
}
92-
76+
async ls (pkg) {
77+
pkg = await this.getPkg(pkg)
9378
const spec = npa(pkg)
9479

9580
try {
96-
const packumentOpts = { ...opts, fullMetadata: true }
81+
const packumentOpts = { ...this.npm.flatOptions, fullMetadata: true }
9782
const { maintainers } = await pacote.packument(spec, packumentOpts)
9883
if (!maintainers || !maintainers.length) {
9984
this.npm.output('no admin found')
10085
} else {
101-
this.npm.output(maintainers.map(o => `${o.name} <${o.email}>`).join('\n'))
86+
this.npm.output(maintainers.map(m => `${m.name} <${m.email}>`).join('\n'))
10287
}
103-
104-
return maintainers
10588
} catch (err) {
10689
log.error('owner ls', "Couldn't get owner data", pkg)
10790
throw err
10891
}
10992
}
11093

111-
async add (user, pkg, opts) {
112-
if (!user) {
113-
throw this.usageError()
114-
}
115-
94+
async getPkg (pkg) {
11695
if (!pkg) {
11796
if (this.npm.config.get('global')) {
11897
throw this.usageError()
@@ -122,44 +101,25 @@ class Owner extends BaseCommand {
122101
throw this.usageError()
123102
}
124103

125-
pkg = pkgName
104+
return pkgName
126105
}
127-
log.verbose('owner add', '%s to %s', user, pkg)
128-
129-
const spec = npa(pkg)
130-
return this.putOwners(spec, user, opts,
131-
(newOwner, owners) => this.validateAddOwner(newOwner, owners))
106+
return pkg
132107
}
133108

134-
async rm (user, pkg, opts) {
109+
async changeOwners (user, pkg, addOrRm) {
135110
if (!user) {
136111
throw this.usageError()
137112
}
138113

139-
if (!pkg) {
140-
if (this.npm.config.get('global')) {
141-
throw this.usageError()
142-
}
143-
const pkgName = await readLocalPkgName(this.npm.prefix)
144-
if (!pkgName) {
145-
throw this.usageError()
146-
}
147-
148-
pkg = pkgName
149-
}
150-
log.verbose('owner rm', '%s from %s', user, pkg)
114+
pkg = await this.getPkg(pkg)
115+
log.verbose(`owner ${addOrRm}`, '%s to %s', user, pkg)
151116

152117
const spec = npa(pkg)
153-
return this.putOwners(spec, user, opts,
154-
(rmOwner, owners) => this.validateRmOwner(rmOwner, owners))
155-
}
156-
157-
async putOwners (spec, user, opts, validation) {
158118
const uri = `/-/user/org.couchdb.user:${encodeURIComponent(user)}`
159-
let u = ''
119+
let u
160120

161121
try {
162-
u = await npmFetch.json(uri, opts)
122+
u = await npmFetch.json(uri, this.npm.flatOptions)
163123
} catch (err) {
164124
log.error('owner mutate', `Error getting user data for ${user}`)
165125
throw err
@@ -177,36 +137,60 @@ class Owner extends BaseCommand {
177137
// normalize user data
178138
u = { name: u.name, email: u.email }
179139

180-
const data = await pacote.packument(spec, { ...opts, fullMetadata: true })
140+
const data = await pacote.packument(spec, { ...this.npm.flatOptions, fullMetadata: true })
181141

182-
// save the number of maintainers before validation for comparison
183-
const before = data.maintainers ? data.maintainers.length : 0
142+
const owners = data.maintainers || []
143+
let maintainers
144+
if (addOrRm === 'add') {
145+
const existing = owners.find(o => o.name === u.name)
146+
if (existing) {
147+
log.info(
148+
'owner add',
149+
`Already a package owner: ${existing.name} <${existing.email}>`
150+
)
151+
return
152+
}
153+
maintainers = [
154+
...owners,
155+
u,
156+
]
157+
} else {
158+
maintainers = owners.filter(o => o.name !== u.name)
184159

185-
const m = validation(u, data.maintainers)
186-
if (!m) {
187-
return
188-
} // invalid owners
160+
if (maintainers.length === owners.length) {
161+
log.info('owner rm', 'Not a package owner: ' + u.name)
162+
return false
163+
}
189164

190-
const body = {
191-
_id: data._id,
192-
_rev: data._rev,
193-
maintainers: m,
165+
if (!maintainers.length) {
166+
throw Object.assign(
167+
new Error(
168+
'Cannot remove all owners of a package. Add someone else first.'
169+
),
170+
{ code: 'EOWNERRM' }
171+
)
172+
}
194173
}
174+
195175
const dataPath = `/${spec.escapedName}/-rev/${encodeURIComponent(data._rev)}`
196-
const res = await otplease(opts, opts => {
176+
const res = await otplease(this.npm.flatOptions, opts => {
197177
return npmFetch.json(dataPath, {
198178
...opts,
199179
method: 'PUT',
200-
body,
180+
body: {
181+
_id: data._id,
182+
_rev: data._rev,
183+
maintainers,
184+
},
201185
spec,
202186
})
203187
})
204188

205189
if (!res.error) {
206-
if (m.length < before) {
207-
this.npm.output(`- ${user} (${spec.name})`)
208-
} else {
190+
if (addOrRm === 'add') {
209191
this.npm.output(`+ ${user} (${spec.name})`)
192+
} else {
193+
this.npm.output(`- ${user} (${spec.name})`)
210194
}
211195
} else {
212196
throw Object.assign(
@@ -216,47 +200,6 @@ class Owner extends BaseCommand {
216200
}
217201
return res
218202
}
219-
220-
validateAddOwner (newOwner, owners) {
221-
owners = owners || []
222-
for (const o of owners) {
223-
if (o.name === newOwner.name) {
224-
log.info(
225-
'owner add',
226-
'Already a package owner: ' + o.name + ' <' + o.email + '>'
227-
)
228-
return false
229-
}
230-
}
231-
return [
232-
...owners,
233-
newOwner,
234-
]
235-
}
236-
237-
validateRmOwner (rmOwner, owners) {
238-
let found = false
239-
const m = owners.filter(function (o) {
240-
var match = (o.name === rmOwner.name)
241-
found = found || match
242-
return !match
243-
})
244-
245-
if (!found) {
246-
log.info('owner rm', 'Not a package owner: ' + rmOwner.name)
247-
return false
248-
}
249-
250-
if (!m.length) {
251-
throw Object.assign(
252-
new Error(
253-
'Cannot remove all owners of a package. Add someone else first.'
254-
),
255-
{ code: 'EOWNERRM' }
256-
)
257-
}
258-
259-
return m
260-
}
261203
}
204+
262205
module.exports = Owner

lib/utils/otplease.js

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,17 @@
1-
const prompt = 'This operation requires a one-time password.\nEnter OTP:'
21
const readUserInfo = require('./read-user-info.js')
32

4-
const isOtpError = err =>
5-
err.code === 'EOTP' || (err.code === 'E401' && /one-time pass/.test(err.body))
6-
73
module.exports = otplease
8-
function otplease (opts, fn) {
9-
opts = { prompt, ...opts }
10-
return Promise.resolve().then(() => fn(opts)).catch(err => {
11-
if (!isOtpError(err)) {
4+
async function otplease (opts, fn) {
5+
try {
6+
await fn(opts)
7+
} catch (err) {
8+
if (err.code !== 'EOTP' && (err.code !== 'E401' || !/one-time pass/.test(err.body))) {
129
throw err
1310
} else if (!process.stdin.isTTY || !process.stdout.isTTY) {
1411
throw err
1512
} else {
16-
return readUserInfo.otp(opts.prompt)
17-
.then(otp => fn({ ...opts, otp }))
13+
const otp = await readUserInfo.otp('This operation requires a one-time password.\nEnter OTP:')
14+
return fn({ ...opts, otp })
1815
}
19-
})
16+
}
2017
}

0 commit comments

Comments
 (0)