Skip to content

Commit 911b5e4

Browse files
alan-agius4clydin
authored andcommitted
fix(@angular/cli): handle unscoped authentication details in .npmrc files
Unless auth options are scope with the registry url it appears that npm-registry-fetch ignores them, even though they are documented. https://github.com/npm/npm-registry-fetch/blob/8954f61d8d703e5eb7f3d93c9b40488f8b1b62ac/README.md https://github.com/npm/npm-registry-fetch/blob/8954f61d8d703e5eb7f3d93c9b40488f8b1b62ac/auth.js#L45-L91
1 parent 86db1c9 commit 911b5e4

File tree

2 files changed

+64
-40
lines changed

2 files changed

+64
-40
lines changed

packages/angular/cli/src/commands/update/schematic/npm.ts

+33-21
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ const lockfile = require('@yarnpkg/lockfile');
1616
const ini = require('ini');
1717
const pacote = require('pacote');
1818

19-
type PackageManagerOptions = Record<string, unknown>;
19+
interface PackageManagerOptions extends Record<string, unknown> {
20+
forceAuth?: Record<string, unknown>;
21+
}
2022

2123
const npmPackageJsonCache = new Map<string, Promise<Partial<NpmRepositoryPackageJson>>>();
2224
let npmrc: PackageManagerOptions;
@@ -41,8 +43,8 @@ function readOptions(
4143
}
4244

4345
const defaultConfigLocations = [
44-
path.join(globalPrefix, 'etc', baseFilename),
45-
path.join(homedir(), dotFilename),
46+
(!yarn && process.env.NPM_CONFIG_GLOBALCONFIG) || path.join(globalPrefix, 'etc', baseFilename),
47+
(!yarn && process.env.NPM_CONFIG_USERCONFIG) || path.join(homedir(), dotFilename),
4648
];
4749

4850
const projectConfigLocations: string[] = [path.join(cwd, dotFilename)];
@@ -67,47 +69,57 @@ function readOptions(
6769
// See: https://github.com/npm/npm-registry-fetch/blob/ebddbe78a5f67118c1f7af2e02c8a22bcaf9e850/index.js#L99-L126
6870
const rcConfig: PackageManagerOptions = yarn ? lockfile.parse(data) : ini.parse(data);
6971
for (const [key, value] of Object.entries(rcConfig)) {
72+
let substitutedValue = value;
73+
74+
// Substitute any environment variable references.
75+
if (typeof value === 'string') {
76+
substitutedValue = value.replace(/\$\{([^\}]+)\}/, (_, name) => process.env[name] || '');
77+
}
78+
7079
switch (key) {
80+
// Unless auth options are scope with the registry url it appears that npm-registry-fetch ignores them,
81+
// even though they are documented.
82+
// https://github.com/npm/npm-registry-fetch/blob/8954f61d8d703e5eb7f3d93c9b40488f8b1b62ac/README.md
83+
// https://github.com/npm/npm-registry-fetch/blob/8954f61d8d703e5eb7f3d93c9b40488f8b1b62ac/auth.js#L45-L91
84+
case '_authToken':
85+
case 'token':
86+
case 'username':
87+
case 'password':
88+
case '_auth':
89+
case 'auth':
90+
options['forceAuth'] ??= {};
91+
options['forceAuth'][key] = substitutedValue;
92+
break;
7193
case 'noproxy':
7294
case 'no-proxy':
73-
options['noProxy'] = value;
95+
options['noProxy'] = substitutedValue;
7496
break;
7597
case 'maxsockets':
76-
options['maxSockets'] = value;
98+
options['maxSockets'] = substitutedValue;
7799
break;
78100
case 'https-proxy':
79101
case 'proxy':
80-
options['proxy'] = value;
102+
options['proxy'] = substitutedValue;
81103
break;
82104
case 'strict-ssl':
83-
options['strictSSL'] = value;
105+
options['strictSSL'] = substitutedValue;
84106
break;
85107
case 'local-address':
86-
options['localAddress'] = value;
108+
options['localAddress'] = substitutedValue;
87109
break;
88110
case 'cafile':
89-
if (typeof value === 'string') {
90-
const cafile = path.resolve(path.dirname(location), value);
111+
if (typeof substitutedValue === 'string') {
112+
const cafile = path.resolve(path.dirname(location), substitutedValue);
91113
try {
92114
options['ca'] = readFileSync(cafile, 'utf8').replace(/\r?\n/g, '\n');
93115
} catch {}
94116
}
95117
break;
96118
default:
97-
options[key] = value;
119+
options[key] = substitutedValue;
98120
break;
99121
}
100122
}
101-
} else if (showPotentials) {
102-
logger.info(`Trying '${location}'...not found.`);
103-
}
104-
}
105-
106-
// Substitute any environment variable references
107-
for (const key in options) {
108-
const value = options[key];
109-
if (typeof value === 'string') {
110-
options[key] = value.replace(/\$\{([^\}]+)\}/, (_, name) => process.env[name] || '');
111123
}
112124
}
113125

packages/angular/cli/utilities/package-metadata.ts

+31-19
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ export interface PackageMetadata {
5858
'dist-tags'?: unknown;
5959
}
6060

61-
type PackageManagerOptions = Record<string, unknown>;
61+
interface PackageManagerOptions extends Record<string, unknown> {
62+
forceAuth?: Record<string, unknown>;
63+
}
6264

6365
let npmrc: PackageManagerOptions;
6466

@@ -122,47 +124,57 @@ function readOptions(
122124
// See: https://github.com/npm/npm-registry-fetch/blob/ebddbe78a5f67118c1f7af2e02c8a22bcaf9e850/index.js#L99-L126
123125
const rcConfig: PackageManagerOptions = yarn ? lockfile.parse(data) : ini.parse(data);
124126
for (const [key, value] of Object.entries(rcConfig)) {
127+
let substitutedValue = value;
128+
129+
// Substitute any environment variable references.
130+
if (typeof value === 'string') {
131+
substitutedValue = value.replace(/\$\{([^\}]+)\}/, (_, name) => process.env[name] || '');
132+
}
133+
125134
switch (key) {
135+
// Unless auth options are scope with the registry url it appears that npm-registry-fetch ignores them,
136+
// even though they are documented.
137+
// https://github.com/npm/npm-registry-fetch/blob/8954f61d8d703e5eb7f3d93c9b40488f8b1b62ac/README.md
138+
// https://github.com/npm/npm-registry-fetch/blob/8954f61d8d703e5eb7f3d93c9b40488f8b1b62ac/auth.js#L45-L91
139+
case '_authToken':
140+
case 'token':
141+
case 'username':
142+
case 'password':
143+
case '_auth':
144+
case 'auth':
145+
options['forceAuth'] ??= {};
146+
options['forceAuth'][key] = substitutedValue;
147+
break;
126148
case 'noproxy':
127149
case 'no-proxy':
128-
options['noProxy'] = value;
150+
options['noProxy'] = substitutedValue;
129151
break;
130152
case 'maxsockets':
131-
options['maxSockets'] = value;
153+
options['maxSockets'] = substitutedValue;
132154
break;
133155
case 'https-proxy':
134156
case 'proxy':
135-
options['proxy'] = value;
157+
options['proxy'] = substitutedValue;
136158
break;
137159
case 'strict-ssl':
138-
options['strictSSL'] = value;
160+
options['strictSSL'] = substitutedValue;
139161
break;
140162
case 'local-address':
141-
options['localAddress'] = value;
163+
options['localAddress'] = substitutedValue;
142164
break;
143165
case 'cafile':
144-
if (typeof value === 'string') {
145-
const cafile = path.resolve(path.dirname(location), value);
166+
if (typeof substitutedValue === 'string') {
167+
const cafile = path.resolve(path.dirname(location), substitutedValue);
146168
try {
147169
options['ca'] = readFileSync(cafile, 'utf8').replace(/\r?\n/g, '\n');
148170
} catch {}
149171
}
150172
break;
151173
default:
152-
options[key] = value;
174+
options[key] = substitutedValue;
153175
break;
154176
}
155177
}
156-
} else if (showPotentials) {
157-
logger.info(`Trying '${location}'...not found.`);
158-
}
159-
}
160-
161-
// Substitute any environment variable references
162-
for (const key in options) {
163-
const value = options[key];
164-
if (typeof value === 'string') {
165-
options[key] = value.replace(/\$\{([^\}]+)\}/, (_, name) => process.env[name] || '');
166178
}
167179
}
168180

0 commit comments

Comments
 (0)