Skip to content

Commit 9cd062b

Browse files
committed
feat: support credentials providers
1 parent c514d23 commit 9cd062b

File tree

6 files changed

+409
-31
lines changed

6 files changed

+409
-31
lines changed

.github/workflows/node.js.yml

+7-1
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@ jobs:
1515
runs-on: ubuntu-latest
1616

1717
strategy:
18+
fail-fast: false
1819
matrix:
19-
node-version: [8.x, 10.x, 12.x, 14.x, 16.x]
20+
node-version: [8.x, 10.x, 12.x, 14.x, 16.x, 18.x, 20.x, 22.x]
2021
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
2122

2223
steps:
@@ -27,3 +28,8 @@ jobs:
2728
node-version: ${{ matrix.node-version }}
2829
- run: npm install
2930
- run: npm run ci
31+
32+
- name: Upload Coverage Report
33+
uses: codecov/codecov-action@v4
34+
with:
35+
token: ${{ secrets.CODECOV_TOKEN }} # required

lib/roa.js

+51-10
Original file line numberDiff line numberDiff line change
@@ -114,15 +114,30 @@ class ROAClient {
114114
throw new Error(`"config.endpoint" must starts with 'https://' or 'http://'.`);
115115
}
116116
assert(config.apiVersion, 'must pass "config.apiVersion"');
117-
assert(config.accessKeyId, 'must pass "config.accessKeyId"');
118-
assert(config.accessKeySecret, 'must pass "config.accessKeySecret"');
117+
if (config.credentialsProvider) {
118+
if (typeof config.credentialsProvider.getCredentials !== 'function') {
119+
throw new Error(`must pass "config.credentialsProvider" with function "getCredentials()"`);
120+
}
121+
this.credentialsProvider = config.credentialsProvider;
122+
} else {
123+
assert(config.accessKeyId, 'must pass "config.accessKeyId"');
124+
assert(config.accessKeySecret, 'must pass "config.accessKeySecret"');
125+
this.accessKeyId = config.accessKeyId;
126+
this.accessKeySecret = config.accessKeySecret;
127+
this.securityToken = config.securityToken;
128+
this.credentialsProvider = {
129+
getCredentials: async () => {
130+
return {
131+
accessKeyId: config.accessKeyId,
132+
accessKeySecret: config.accessKeySecret,
133+
securityToken: config.securityToken,
134+
};
135+
}
136+
};
137+
}
119138

120139
this.endpoint = config.endpoint;
121-
122140
this.apiVersion = config.apiVersion;
123-
this.accessKeyId = config.accessKeyId;
124-
this.accessKeySecret = config.accessKeySecret;
125-
this.securityToken = config.securityToken;
126141
this.host = url.parse(this.endpoint).hostname;
127142
this.opts = config.opts;
128143
var httpModule = this.endpoint.startsWith('https://') ? require('https') : require('http');
@@ -163,9 +178,31 @@ class ROAClient {
163178
}
164179

165180
async request(method, uriPattern, query = {}, body = '', headers = {}, opts = {}) {
181+
const credentials = await this.credentialsProvider.getCredentials();
182+
183+
const now = new Date();
184+
var defaultHeaders = {
185+
accept: 'application/json',
186+
date: now.toGMTString(),
187+
host: this.host,
188+
'x-acs-signature-nonce': kitx.makeNonce(),
189+
'x-acs-version': this.apiVersion,
190+
'user-agent': helper.DEFAULT_UA,
191+
'x-sdk-client': helper.DEFAULT_CLIENT
192+
};
193+
if (credentials && credentials.accessKeyId && credentials.accessKeySecret) {
194+
defaultHeaders['x-acs-signature-method'] = 'HMAC-SHA1';
195+
defaultHeaders['x-acs-signature-version'] = '1.0';
196+
if (credentials.securityToken) {
197+
defaultHeaders['x-acs-accesskey-id'] = credentials.accessKeyId;
198+
defaultHeaders['x-acs-security-token'] = credentials.securityToken;
199+
}
200+
}
201+
202+
var mixHeaders = Object.assign(defaultHeaders, keyLowerify(headers));
203+
166204
var postBody = null;
167205

168-
var mixHeaders = Object.assign(this.buildHeaders(), keyLowerify(headers));
169206
postBody = Buffer.from(body, 'utf8');
170207
mixHeaders['content-md5'] = kitx.md5(postBody, 'base64');
171208
mixHeaders['content-length'] = postBody.length;
@@ -175,9 +212,13 @@ class ROAClient {
175212
url += `?${querystring.stringify(query)}`;
176213
}
177214

178-
const stringToSign = buildStringToSign(method, uriPattern, mixHeaders, query);
179-
debug('stringToSign: %s', stringToSign);
180-
mixHeaders['authorization'] = this.buildAuthorization(stringToSign);
215+
if (credentials && credentials.accessKeyId && credentials.accessKeySecret) {
216+
const stringToSign = buildStringToSign(method, uriPattern, mixHeaders, query);
217+
debug('stringToSign: %s', stringToSign);
218+
const utf8Buff = Buffer.from(stringToSign, 'utf8');
219+
const signature = kitx.sha1(utf8Buff, credentials.accessKeySecret, 'base64');
220+
mixHeaders['authorization'] = `acs ${credentials.accessKeyId}:${signature}`;
221+
}
181222

182223
const options = Object.assign({
183224
method,

lib/rpc.js

+51-17
Original file line numberDiff line numberDiff line change
@@ -111,19 +111,36 @@ class RPCClient {
111111
throw new Error(`"config.endpoint" must starts with 'https://' or 'http://'.`);
112112
}
113113
assert(config.apiVersion, 'must pass "config.apiVersion"');
114-
assert(config.accessKeyId, 'must pass "config.accessKeyId"');
115-
var accessKeySecret = config.secretAccessKey || config.accessKeySecret;
116-
assert(accessKeySecret, 'must pass "config.accessKeySecret"');
114+
if (config.credentialsProvider) {
115+
if (typeof config.credentialsProvider.getCredentials !== 'function') {
116+
throw new Error(`must pass "config.credentialsProvider" with function "getCredentials()"`);
117+
}
118+
this.credentialsProvider = config.credentialsProvider;
119+
} else {
120+
assert(config.accessKeyId, 'must pass "config.accessKeyId"');
121+
var accessKeySecret = config.secretAccessKey || config.accessKeySecret;
122+
assert(accessKeySecret, 'must pass "config.accessKeySecret"');
123+
this.accessKeyId = config.accessKeyId;
124+
this.accessKeySecret = accessKeySecret;
125+
this.securityToken = config.securityToken;
126+
this.credentialsProvider = {
127+
getCredentials: async () => {
128+
return {
129+
accessKeyId: config.accessKeyId,
130+
accessKeySecret: accessKeySecret,
131+
securityToken: config.securityToken,
132+
};
133+
}
134+
};
135+
}
136+
117137

118138
if (config.endpoint.endsWith('/')) {
119139
config.endpoint = config.endpoint.slice(0, -1);
120140
}
121141

122142
this.endpoint = config.endpoint;
123143
this.apiVersion = config.apiVersion;
124-
this.accessKeyId = config.accessKeyId;
125-
this.accessKeySecret = accessKeySecret;
126-
this.securityToken = config.securityToken;
127144
this.verbose = verbose === true;
128145
// 非 codes 里的值,将抛出异常
129146
this.codes = new Set([200, '200', 'OK', 'Success', 'success']);
@@ -145,6 +162,7 @@ class RPCClient {
145162
}
146163

147164
async request(action, params = {}, opts = {}) {
165+
const credentials = await this.credentialsProvider.getCredentials();
148166
// 1. compose params and opts
149167
opts = Object.assign({
150168
headers: {
@@ -164,20 +182,36 @@ class RPCClient {
164182
if (opts.formatParams !== false) {
165183
params = formatParams(params);
166184
}
167-
const defaults = this._buildParams();
168-
params = Object.assign({Action: action}, defaults, params);
185+
const defaultParams = {
186+
Format: 'JSON',
187+
Timestamp: timestamp(),
188+
Version: this.apiVersion,
189+
};
190+
if (credentials && credentials.accessKeyId && credentials.accessKeySecret) {
191+
defaultParams.SignatureMethod = 'HMAC-SHA1';
192+
defaultParams.SignatureVersion = '1.0';
193+
defaultParams.SignatureNonce = kitx.makeNonce();
194+
defaultParams.AccessKeyId = credentials.accessKeyId;
195+
if (credentials.securityToken) {
196+
defaultParams.SecurityToken = credentials.securityToken;
197+
}
198+
}
199+
params = Object.assign({ Action: action }, defaultParams, params);
169200

170-
// 2. caculate signature
171201
const method = (opts.method || 'GET').toUpperCase();
172202
const normalized = normalize(params);
173-
const canonicalized = canonicalize(normalized);
174-
// 2.1 get string to sign
175-
const stringToSign = `${method}&${encode('/')}&${encode(canonicalized)}`;
176-
// 2.2 get signature
177-
const key = this.accessKeySecret + '&';
178-
const signature = kitx.sha1(stringToSign, key, 'base64');
179-
// add signature
180-
normalized.push(['Signature', encode(signature)]);
203+
// 2. caculate signature
204+
if (credentials && credentials.accessKeyId && credentials.accessKeySecret) {
205+
const canonicalized = canonicalize(normalized);
206+
// 2.1 get string to sign
207+
const stringToSign = `${method}&${encode('/')}&${encode(canonicalized)}`;
208+
// 2.2 get signature
209+
const key = credentials.accessKeySecret + '&';
210+
const signature = kitx.sha1(stringToSign, key, 'base64');
211+
// add signature
212+
normalized.push(['Signature', encode(signature)]);
213+
}
214+
181215
// 3. generate final url
182216
const url = opts.method === 'POST' ? `${this.endpoint}/` : `${this.endpoint}/?${canonicalize(normalized)}`;
183217
// 4. send request

package.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"test": "mocha -R spec test/*.test.js",
1010
"test-cov": "nyc -r=html -r=text -r=lcov mocha -t 3000 -R spec test/*.test.js",
1111
"test-integration": "mocha -R spec test/*.integration.js",
12-
"ci": "npm run lint && npm run test-cov && codecov"
12+
"ci": "npm run lint && npm run test-cov"
1313
},
1414
"keywords": [
1515
"Aliyun",
@@ -33,7 +33,6 @@
3333
"index.js"
3434
],
3535
"devDependencies": {
36-
"codecov": "^3.0.4",
3736
"eslint": "^6.6.0",
3837
"expect.js": "^0.3.1",
3938
"mocha": "^4",

0 commit comments

Comments
 (0)