11cb0ef41Sopenharmony_ciconst libnpmaccess = require('libnpmaccess') 21cb0ef41Sopenharmony_ciconst npa = require('npm-package-arg') 31cb0ef41Sopenharmony_ciconst pkgJson = require('@npmcli/package-json') 41cb0ef41Sopenharmony_ciconst localeCompare = require('@isaacs/string-locale-compare')('en') 51cb0ef41Sopenharmony_ci 61cb0ef41Sopenharmony_ciconst otplease = require('../utils/otplease.js') 71cb0ef41Sopenharmony_ciconst getIdentity = require('../utils/get-identity.js') 81cb0ef41Sopenharmony_ciconst BaseCommand = require('../base-command.js') 91cb0ef41Sopenharmony_ci 101cb0ef41Sopenharmony_ciconst commands = [ 111cb0ef41Sopenharmony_ci 'get', 121cb0ef41Sopenharmony_ci 'grant', 131cb0ef41Sopenharmony_ci 'list', 141cb0ef41Sopenharmony_ci 'revoke', 151cb0ef41Sopenharmony_ci 'set', 161cb0ef41Sopenharmony_ci] 171cb0ef41Sopenharmony_ci 181cb0ef41Sopenharmony_ciconst setCommands = [ 191cb0ef41Sopenharmony_ci 'status=public', 201cb0ef41Sopenharmony_ci 'status=private', 211cb0ef41Sopenharmony_ci 'mfa=none', 221cb0ef41Sopenharmony_ci 'mfa=publish', 231cb0ef41Sopenharmony_ci 'mfa=automation', 241cb0ef41Sopenharmony_ci '2fa=none', 251cb0ef41Sopenharmony_ci '2fa=publish', 261cb0ef41Sopenharmony_ci '2fa=automation', 271cb0ef41Sopenharmony_ci] 281cb0ef41Sopenharmony_ci 291cb0ef41Sopenharmony_ciclass Access extends BaseCommand { 301cb0ef41Sopenharmony_ci static description = 'Set access level on published packages' 311cb0ef41Sopenharmony_ci static name = 'access' 321cb0ef41Sopenharmony_ci static params = [ 331cb0ef41Sopenharmony_ci 'json', 341cb0ef41Sopenharmony_ci 'otp', 351cb0ef41Sopenharmony_ci 'registry', 361cb0ef41Sopenharmony_ci ] 371cb0ef41Sopenharmony_ci 381cb0ef41Sopenharmony_ci static usage = [ 391cb0ef41Sopenharmony_ci 'list packages [<user>|<scope>|<scope:team> [<package>]', 401cb0ef41Sopenharmony_ci 'list collaborators [<package> [<user>]]', 411cb0ef41Sopenharmony_ci 'get status [<package>]', 421cb0ef41Sopenharmony_ci 'set status=public|private [<package>]', 431cb0ef41Sopenharmony_ci 'set mfa=none|publish|automation [<package>]', 441cb0ef41Sopenharmony_ci 'grant <read-only|read-write> <scope:team> [<package>]', 451cb0ef41Sopenharmony_ci 'revoke <scope:team> [<package>]', 461cb0ef41Sopenharmony_ci ] 471cb0ef41Sopenharmony_ci 481cb0ef41Sopenharmony_ci static async completion (opts) { 491cb0ef41Sopenharmony_ci const argv = opts.conf.argv.remain 501cb0ef41Sopenharmony_ci if (argv.length === 2) { 511cb0ef41Sopenharmony_ci return commands 521cb0ef41Sopenharmony_ci } 531cb0ef41Sopenharmony_ci 541cb0ef41Sopenharmony_ci if (argv.length === 3) { 551cb0ef41Sopenharmony_ci switch (argv[2]) { 561cb0ef41Sopenharmony_ci case 'grant': 571cb0ef41Sopenharmony_ci return ['read-only', 'read-write'] 581cb0ef41Sopenharmony_ci case 'revoke': 591cb0ef41Sopenharmony_ci return [] 601cb0ef41Sopenharmony_ci case 'list': 611cb0ef41Sopenharmony_ci case 'ls': 621cb0ef41Sopenharmony_ci return ['packages', 'collaborators'] 631cb0ef41Sopenharmony_ci case 'get': 641cb0ef41Sopenharmony_ci return ['status'] 651cb0ef41Sopenharmony_ci case 'set': 661cb0ef41Sopenharmony_ci return setCommands 671cb0ef41Sopenharmony_ci default: 681cb0ef41Sopenharmony_ci throw new Error(argv[2] + ' not recognized') 691cb0ef41Sopenharmony_ci } 701cb0ef41Sopenharmony_ci } 711cb0ef41Sopenharmony_ci } 721cb0ef41Sopenharmony_ci 731cb0ef41Sopenharmony_ci async exec ([cmd, subcmd, ...args]) { 741cb0ef41Sopenharmony_ci if (!cmd) { 751cb0ef41Sopenharmony_ci throw this.usageError() 761cb0ef41Sopenharmony_ci } 771cb0ef41Sopenharmony_ci if (!commands.includes(cmd)) { 781cb0ef41Sopenharmony_ci throw this.usageError(`${cmd} is not a valid access command`) 791cb0ef41Sopenharmony_ci } 801cb0ef41Sopenharmony_ci // All commands take at least one more parameter so we can do this check up front 811cb0ef41Sopenharmony_ci if (!subcmd) { 821cb0ef41Sopenharmony_ci throw this.usageError() 831cb0ef41Sopenharmony_ci } 841cb0ef41Sopenharmony_ci 851cb0ef41Sopenharmony_ci switch (cmd) { 861cb0ef41Sopenharmony_ci case 'grant': 871cb0ef41Sopenharmony_ci if (!['read-only', 'read-write'].includes(subcmd)) { 881cb0ef41Sopenharmony_ci throw this.usageError('grant must be either `read-only` or `read-write`') 891cb0ef41Sopenharmony_ci } 901cb0ef41Sopenharmony_ci if (!args[0]) { 911cb0ef41Sopenharmony_ci throw this.usageError('`<scope:team>` argument is required') 921cb0ef41Sopenharmony_ci } 931cb0ef41Sopenharmony_ci return this.#grant(subcmd, args[0], args[1]) 941cb0ef41Sopenharmony_ci case 'revoke': 951cb0ef41Sopenharmony_ci return this.#revoke(subcmd, args[0]) 961cb0ef41Sopenharmony_ci case 'list': 971cb0ef41Sopenharmony_ci case 'ls': 981cb0ef41Sopenharmony_ci if (subcmd === 'packages') { 991cb0ef41Sopenharmony_ci return this.#listPackages(args[0], args[1]) 1001cb0ef41Sopenharmony_ci } 1011cb0ef41Sopenharmony_ci if (subcmd === 'collaborators') { 1021cb0ef41Sopenharmony_ci return this.#listCollaborators(args[0], args[1]) 1031cb0ef41Sopenharmony_ci } 1041cb0ef41Sopenharmony_ci throw this.usageError(`list ${subcmd} is not a valid access command`) 1051cb0ef41Sopenharmony_ci case 'get': 1061cb0ef41Sopenharmony_ci if (subcmd !== 'status') { 1071cb0ef41Sopenharmony_ci throw this.usageError(`get ${subcmd} is not a valid access command`) 1081cb0ef41Sopenharmony_ci } 1091cb0ef41Sopenharmony_ci return this.#getStatus(args[0]) 1101cb0ef41Sopenharmony_ci case 'set': 1111cb0ef41Sopenharmony_ci if (!setCommands.includes(subcmd)) { 1121cb0ef41Sopenharmony_ci throw this.usageError(`set ${subcmd} is not a valid access command`) 1131cb0ef41Sopenharmony_ci } 1141cb0ef41Sopenharmony_ci return this.#set(subcmd, args[0]) 1151cb0ef41Sopenharmony_ci } 1161cb0ef41Sopenharmony_ci } 1171cb0ef41Sopenharmony_ci 1181cb0ef41Sopenharmony_ci async #grant (permissions, scope, pkg) { 1191cb0ef41Sopenharmony_ci await libnpmaccess.setPermissions(scope, pkg, permissions, this.npm.flatOptions) 1201cb0ef41Sopenharmony_ci } 1211cb0ef41Sopenharmony_ci 1221cb0ef41Sopenharmony_ci async #revoke (scope, pkg) { 1231cb0ef41Sopenharmony_ci await libnpmaccess.removePermissions(scope, pkg, this.npm.flatOptions) 1241cb0ef41Sopenharmony_ci } 1251cb0ef41Sopenharmony_ci 1261cb0ef41Sopenharmony_ci async #listPackages (owner, pkg) { 1271cb0ef41Sopenharmony_ci if (!owner) { 1281cb0ef41Sopenharmony_ci owner = await getIdentity(this.npm, this.npm.flatOptions) 1291cb0ef41Sopenharmony_ci } 1301cb0ef41Sopenharmony_ci const pkgs = await libnpmaccess.getPackages(owner, this.npm.flatOptions) 1311cb0ef41Sopenharmony_ci this.#output(pkgs, pkg) 1321cb0ef41Sopenharmony_ci } 1331cb0ef41Sopenharmony_ci 1341cb0ef41Sopenharmony_ci async #listCollaborators (pkg, user) { 1351cb0ef41Sopenharmony_ci const pkgName = await this.#getPackage(pkg, false) 1361cb0ef41Sopenharmony_ci const collabs = await libnpmaccess.getCollaborators(pkgName, this.npm.flatOptions) 1371cb0ef41Sopenharmony_ci this.#output(collabs, user) 1381cb0ef41Sopenharmony_ci } 1391cb0ef41Sopenharmony_ci 1401cb0ef41Sopenharmony_ci async #getStatus (pkg) { 1411cb0ef41Sopenharmony_ci const pkgName = await this.#getPackage(pkg, false) 1421cb0ef41Sopenharmony_ci const visibility = await libnpmaccess.getVisibility(pkgName, this.npm.flatOptions) 1431cb0ef41Sopenharmony_ci this.#output({ [pkgName]: visibility.public ? 'public' : 'private' }) 1441cb0ef41Sopenharmony_ci } 1451cb0ef41Sopenharmony_ci 1461cb0ef41Sopenharmony_ci async #set (subcmd, pkg) { 1471cb0ef41Sopenharmony_ci const [subkey, subval] = subcmd.split('=') 1481cb0ef41Sopenharmony_ci switch (subkey) { 1491cb0ef41Sopenharmony_ci case 'mfa': 1501cb0ef41Sopenharmony_ci case '2fa': 1511cb0ef41Sopenharmony_ci return this.#setMfa(pkg, subval) 1521cb0ef41Sopenharmony_ci case 'status': 1531cb0ef41Sopenharmony_ci return this.#setStatus(pkg, subval) 1541cb0ef41Sopenharmony_ci } 1551cb0ef41Sopenharmony_ci } 1561cb0ef41Sopenharmony_ci 1571cb0ef41Sopenharmony_ci async #setMfa (pkg, level) { 1581cb0ef41Sopenharmony_ci const pkgName = await this.#getPackage(pkg, false) 1591cb0ef41Sopenharmony_ci await otplease(this.npm, this.npm.flatOptions, (opts) => { 1601cb0ef41Sopenharmony_ci return libnpmaccess.setMfa(pkgName, level, opts) 1611cb0ef41Sopenharmony_ci }) 1621cb0ef41Sopenharmony_ci } 1631cb0ef41Sopenharmony_ci 1641cb0ef41Sopenharmony_ci async #setStatus (pkg, status) { 1651cb0ef41Sopenharmony_ci // only scoped packages can have their access changed 1661cb0ef41Sopenharmony_ci const pkgName = await this.#getPackage(pkg, true) 1671cb0ef41Sopenharmony_ci if (status === 'private') { 1681cb0ef41Sopenharmony_ci status = 'restricted' 1691cb0ef41Sopenharmony_ci } 1701cb0ef41Sopenharmony_ci await otplease(this.npm, this.npm.flatOptions, (opts) => { 1711cb0ef41Sopenharmony_ci return libnpmaccess.setAccess(pkgName, status, opts) 1721cb0ef41Sopenharmony_ci }) 1731cb0ef41Sopenharmony_ci return this.#getStatus(pkgName) 1741cb0ef41Sopenharmony_ci } 1751cb0ef41Sopenharmony_ci 1761cb0ef41Sopenharmony_ci async #getPackage (name, requireScope) { 1771cb0ef41Sopenharmony_ci if (!name) { 1781cb0ef41Sopenharmony_ci try { 1791cb0ef41Sopenharmony_ci const { content } = await pkgJson.normalize(this.npm.prefix) 1801cb0ef41Sopenharmony_ci name = content.name 1811cb0ef41Sopenharmony_ci } catch (err) { 1821cb0ef41Sopenharmony_ci if (err.code === 'ENOENT') { 1831cb0ef41Sopenharmony_ci throw Object.assign(new Error('no package name given and no package.json found'), { 1841cb0ef41Sopenharmony_ci code: 'ENOENT', 1851cb0ef41Sopenharmony_ci }) 1861cb0ef41Sopenharmony_ci } else { 1871cb0ef41Sopenharmony_ci throw err 1881cb0ef41Sopenharmony_ci } 1891cb0ef41Sopenharmony_ci } 1901cb0ef41Sopenharmony_ci } 1911cb0ef41Sopenharmony_ci 1921cb0ef41Sopenharmony_ci const spec = npa(name) 1931cb0ef41Sopenharmony_ci if (requireScope && !spec.scope) { 1941cb0ef41Sopenharmony_ci throw this.usageError('This command is only available for scoped packages.') 1951cb0ef41Sopenharmony_ci } 1961cb0ef41Sopenharmony_ci return name 1971cb0ef41Sopenharmony_ci } 1981cb0ef41Sopenharmony_ci 1991cb0ef41Sopenharmony_ci #output (items, limiter) { 2001cb0ef41Sopenharmony_ci const output = {} 2011cb0ef41Sopenharmony_ci const lookup = { 2021cb0ef41Sopenharmony_ci __proto__: null, 2031cb0ef41Sopenharmony_ci read: 'read-only', 2041cb0ef41Sopenharmony_ci write: 'read-write', 2051cb0ef41Sopenharmony_ci } 2061cb0ef41Sopenharmony_ci for (const item in items) { 2071cb0ef41Sopenharmony_ci const val = items[item] 2081cb0ef41Sopenharmony_ci output[item] = lookup[val] || val 2091cb0ef41Sopenharmony_ci } 2101cb0ef41Sopenharmony_ci if (this.npm.config.get('json')) { 2111cb0ef41Sopenharmony_ci this.npm.output(JSON.stringify(output, null, 2)) 2121cb0ef41Sopenharmony_ci } else { 2131cb0ef41Sopenharmony_ci for (const item of Object.keys(output).sort(localeCompare)) { 2141cb0ef41Sopenharmony_ci if (!limiter || limiter === item) { 2151cb0ef41Sopenharmony_ci this.npm.output(`${item}: ${output[item]}`) 2161cb0ef41Sopenharmony_ci } 2171cb0ef41Sopenharmony_ci } 2181cb0ef41Sopenharmony_ci } 2191cb0ef41Sopenharmony_ci } 2201cb0ef41Sopenharmony_ci} 2211cb0ef41Sopenharmony_ci 2221cb0ef41Sopenharmony_cimodule.exports = Access 223