From 4464362c7eef31d75a665b90326cef494d47e01e Mon Sep 17 00:00:00 2001 From: hyj1991 Date: Sun, 28 Apr 2019 13:01:35 +0800 Subject: [PATCH] feat: support authorization (#259) --- lib/cnpm_config.js | 37 +++++++++++++++++++++++++++++++++++++ lib/get.js | 10 ++++++++++ test/fixtures/auth/.cnpmrc | 4 ++++ test/get.test.js | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 83 insertions(+) create mode 100644 lib/cnpm_config.js create mode 100644 test/fixtures/auth/.cnpmrc diff --git a/lib/cnpm_config.js b/lib/cnpm_config.js new file mode 100644 index 00000000..084b4bfe --- /dev/null +++ b/lib/cnpm_config.js @@ -0,0 +1,37 @@ +'use strict'; +const path = require('path'); +const fs = require('fs'); +const config = {}; + +function createConfigs() { + let root; + if (process.platform === 'win32') { + root = process.env.USERPROFILE || process.env.APPDATA || process.env.TMP || process.env.TEMP; + } else { + root = process.env.HOME || process.env.TMPDIR || '/tmp'; + } + const userConfig = path.join(root, '.cnpmrc'); + if (!fs.existsSync(userConfig)) return; + const userConfigContent = fs.readFileSync(userConfig).toString(); + const configs = typeof userConfigContent === 'string' && userConfigContent.split('\n'); + configs.reduce((pre, next) => { + if (typeof next === 'string') { + const map = next.split('='); + const key = map[0]; + let value = map[1]; + if (value === 'true') value = true; + if (value === 'false') value = false; + pre[key] = value; + } + return pre; + }, config); + +} + +createConfigs(); + +module.exports = { + get(key) { + return config[key]; + }, +}; diff --git a/lib/get.js b/lib/get.js index 1a2bbc5e..fa33cf15 100644 --- a/lib/get.js +++ b/lib/get.js @@ -6,6 +6,8 @@ const destroy = require('destroy'); const HttpAgent = require('agentkeepalive'); const HttpsAgent = require('agentkeepalive').HttpsAgent; const utils = require('./utils'); +const cnpmConfig = require('./cnpm_config'); +const urlParser = require('url'); module.exports = get; @@ -41,6 +43,14 @@ function* get(url, options, globalOptions) { debug('enableProxy: %s', options.proxy); } } + // need auth + const registryUrl = cnpmConfig.get('registry'); + const registryUri = registryUrl && registryUrl.replace(urlParser.parse(registryUrl).protocol, '') || ''; + const authed = registryUri && url.indexOf(registryUri) !== -1 || false; + if (authed || cnpmConfig.get(registryUri + ':always-auth') || cnpmConfig.get('always-auth')) { + const authToken = (`${cnpmConfig.get(registryUri + ':username')}:${Buffer.from(cnpmConfig.get(registryUri + ':_password'), 'base64').toString()}`); + options.headers.Authorization = `Basic ${Buffer.from(authToken).toString('base64')}`; + } const retry = options.retry || options.retry === 0 ? options.retry : MAX_RETRY; options.retry = undefined; debug('GET %s with headers: %j', url, options.headers); diff --git a/test/fixtures/auth/.cnpmrc b/test/fixtures/auth/.cnpmrc new file mode 100644 index 00000000..808c606c --- /dev/null +++ b/test/fixtures/auth/.cnpmrc @@ -0,0 +1,4 @@ +registry=https://registry-mock.org/ +//registry-mock.org/:always-auth=true +//registry-mock.org/:_password="bW9jaw==" +//registry-mock.org/:username=hyj19911120 \ No newline at end of file diff --git a/test/get.test.js b/test/get.test.js index c5578922..45e6050c 100644 --- a/test/get.test.js +++ b/test/get.test.js @@ -1,7 +1,20 @@ 'use strict'; +const fs = require('fs'); const assert = require('assert'); +const mm = require('mm'); +const path = require('path'); +const mockCnpmrc = path.join(__dirname, './fixtures/auth/'); +if (!fs.existsSync(mockCnpmrc + '.cnpmrc')) { + fs.writeFileSync(mockCnpmrc + '.cnpmrc', 'registry=https://registry-mock.org/\n//registry-mock.org/:always-auth=true\n//registry-mock.org/:_password="bW9jaw=="\n//registry-mock.org/:username=hyj19911120'); +} +mm(process.env, 'HOME', mockCnpmrc); +mm(process.env, 'USERPROFILE', mockCnpmrc); +// clean cnpm_config.js cache +delete require.cache[require.resolve('../lib/get')]; +delete require.cache[require.resolve('../lib/cnpm_config')]; const get = require('../lib/get'); +mm.restore(); describe('test/get.test.js', () => { it('should retry on JSON parse error', function* () { @@ -18,4 +31,23 @@ describe('test/get.test.js', () => { assert(err.res.requestUrls.length === 5); } }); + + it('should set auth info into header', function* () { + const logger = { + warn(msg) { + assert(msg.indexOf('[npminstall:get] retry GET') === 0); + }, + }; + const options = { dataType: 'json' }; + assert(fs.existsSync(mockCnpmrc + '.cnpmrc')); + try { + yield get('https://registry-mock.org/mock', options, { console: logger }); + assert(false, 'should not run this'); + } catch (err) { + const headers = options.headers; + assert(headers.Authorization); + assert(err.name === 'RequestError'); + assert(err.res.requestUrls.length === 5); + } + }); });