Skip to content

Commit

Permalink
feat: add asyncSignatureUrl method (#1057)
Browse files Browse the repository at this point in the history
* feat: add asyncSignatureUrl method

* chore: add asyncSignatureUrl warn message

* feat: to resolve conversation

* chore: optimized test case

* chore: optimized test case

* chore: chore: optimized test case

* chore: the tag signatureUrl will be deprecated in the next version

* chore: remove not use function checkBrowserEnv

Co-authored-by: Undefined <peizerao@gmail.com>
  • Loading branch information
taotao7 and PeterRao authored May 9, 2022
1 parent e4d0e1f commit 1a05e80
Show file tree
Hide file tree
Showing 8 changed files with 190 additions and 63 deletions.
78 changes: 78 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ All operation use es7 async/await to implement. All api is async function.
- [.putMeta(name, meta[, options])](#putmetaname-meta-options)
- [.deleteMulti(names[, options])](#deletemultinames-options)
- [.signatureUrl(name[, options])](#signatureurlname-options)
- [.asyncSignatureUrl(name[, options])](#signatureurlname-options)
- [.putACL(name, acl[, options])](#putaclname-acl-options)
- [.getACL(name[, options])](#getaclname-options)
- [.restore(name[, options])](#restorename-options)
Expand Down Expand Up @@ -2636,6 +2637,83 @@ const url = store.signatureUrl('ossdemo.png', {
console.log(url);
```
### .asyncSignatureUrl(name[, options])
Basically the same as signatureUrl, if refreshSTSToken is configured asyncSignatureUrl will refresh stsToken
parameters:
- name {String} object name store on OSS
- [options] {Object} optional parameters
- [expires] {Number} after expires seconds, the url will become invalid, default is `1800`
- [method] {String} the HTTP method, default is 'GET'
- [Content-Type] {String} set the request content type
- [process] {String} image process params, will send with `x-oss-process`
e.g.: `{process: 'image/resize,w_200'}`
- [trafficLimit] {Number} traffic limit, range: `819200`~`838860800`.
- [subResource] {Object} additional signature parameters in url.
- [response] {Object} set the response headers for download
- [content-type] {String} set the response content type
- [content-disposition] {String} set the response content disposition
- [cache-control] {String} set the response cache control
- See more: <https://help.aliyun.com/document_detail/31980.html>
- [callback] {Object} set the callback for the operation
- url {String} set the url for callback
- [host] {String} set the host for callback
- body {String} set the body for callback
- [contentType] {String} set the type for body
- [customValue] {Object} set the custom value for callback,eg. {var1: value1,var2:value2}
Success will return signature url.
example:
- Get signature url for object
```js
const url = await store.asyncSignatureUrl('ossdemo.txt');
console.log(url);
// --------------------------------------------------
const url = await store.asyncSignatureUrl('ossdemo.txt', {
expires: 3600,
method: 'PUT'
});
console.log(url);
// put object with signatureUrl
// -------------------------------------------------
const url = await store.asyncSignatureUrl('ossdemo.txt', {
expires: 3600,
method: 'PUT',
'Content-Type': 'text/plain; charset=UTF-8',
});
console.log(url);
// --------------------------------------------------
const url = await store.asyncSignatureUrl('ossdemo.txt', {
expires: 3600,
response: {
'content-type': 'text/custom',
'content-disposition': 'attachment'
}
});
console.log(url);
// put operation
```
- Get a signature url for a processed image
```js
const url = await store.asyncSignatureUrl('ossdemo.png', {
process: 'image/resize,w_200'
});
console.log(url);
// --------------------------------------------------
const url = await store.asyncSignatureUrl('ossdemo.png', {
expires: 3600,
process: 'image/resize,w_200'
});
console.log(url);
```
### .putACL(name, acl[, options])
Set object's ACL.
Expand Down
1 change: 1 addition & 0 deletions lib/browser/object.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ merge(proto, require('../common/object/getObjectMeta'));
merge(proto, require('../common/object/getObjectUrl'));
merge(proto, require('../common/object/generateObjectUrl'));
merge(proto, require('../common/object/signatureUrl'));
merge(proto, require('../common/object/asyncSignatureUrl'));

proto.putMeta = async function putMeta(name, meta, options) {
const copyResult = await this.copy(name, name, {
Expand Down
45 changes: 45 additions & 0 deletions lib/common/object/asyncSignatureUrl.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
const urlutil = require('url');
const utility = require('utility');
const copy = require('copy-to');
const signHelper = require('../../common/signUtils');
const { isIP } = require('../utils/isIP');
const { setSTSToken } = require('../utils/setSTSToken');
const { isFunction } = require('../utils/isFunction');
const proto = exports;

proto.asyncSignatureUrl = async function asyncSignatureUrl(name, options) {
if (isIP(this.options.endpoint.hostname)) {
throw new Error('can not get the object URL when endpoint is IP');
}
options = options || {};
name = this._objectName(name);
options.method = options.method || 'GET';
const expires = utility.timestamp() + (options.expires || 1800);
const params = {
bucket: this.options.bucket,
object: name
};

const resource = this._getResource(params);

if (this.options.stsToken && isFunction(this.options.refreshSTSToken)) {
await setSTSToken.call(this);
}

if (this.options.stsToken) {
options['security-token'] = this.options.stsToken;
}

const signRes = signHelper._signatureForURL(this.options.accessKeySecret, options, resource, expires);

const url = urlutil.parse(this._getReqUrl(params));
url.query = {
OSSAccessKeyId: this.options.accessKeyId,
Expires: expires,
Signature: signRes.Signature
};

copy(signRes.subResource).to(url.query);

return url.format();
};
2 changes: 1 addition & 1 deletion lib/common/object/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ merge(proto, require('./getAsyncFetch'));
merge(proto, require('./generateObjectUrl'));
merge(proto, require('./getObjectUrl'));
merge(proto, require('./signatureUrl'));

merge(proto, require('./asyncSignatureUrl'));
26 changes: 7 additions & 19 deletions lib/common/object/signatureUrl.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ const utility = require('utility');
const copy = require('copy-to');
const signHelper = require('../../common/signUtils');
const { isIP } = require('../utils/isIP');
const { isFunction } = require('../../common/utils/isFunction');
const { checkCredentials } = require('../utils/setSTSToken');
const { formatObjKey } = require('../utils/formatObjKey');

const proto = exports;

/**
* signatureUrl
* @deprecated will be deprecated in 7.x
* @param {String} name object name
* @param {Object} options options
*/
proto.signatureUrl = function signatureUrl(name, options) {
if (isIP(this.options.endpoint.hostname)) {
throw new Error('can not get the object URL when endpoint is IP');
Expand All @@ -23,22 +27,6 @@ proto.signatureUrl = function signatureUrl(name, options) {

const resource = this._getResource(params);

if (this.options.stsToken && isFunction(this.options.refreshSTSToken)) {
const now = new Date();
if (this.stsTokenFreshTime >= this.options.refreshSTSTokenInterval) {
this.stsTokenFreshTime = now;
this.options.refreshSTSToken().then(r => {
const credentials = formatObjKey(r, 'firstLowerCase');
if (credentials.securityToken) {
credentials.stsToken = credentials.securityToken;
}
checkCredentials(credentials);
Object.assign(this.options, credentials);
});
} else {
this.stsTokenFreshTime = now;
}
}
if (this.options.stsToken) {
options['security-token'] = this.options.stsToken;
}
Expand Down
31 changes: 31 additions & 0 deletions test/browser/browser.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ const timemachine = require('timemachine');

timemachine.reset();

function sleep(time) {
return new Promise(resolve => {
setTimeout(resolve, time);
});
}

const cleanBucket = async store => {
let result = await store.list({
'max-keys': 1000
Expand Down Expand Up @@ -1079,6 +1085,31 @@ describe('browser', () => {
// http://www.aliyun.com/darwin-v4.4.2/ali-sdk/oss/get-meta.js?OSSAccessKeyId=
assert.equal(url.indexOf('http://www.aliyun.com/'), 0);
});

it('signatureUrl will should use refreshSTSToken', async () => {
let flag = false;

store = oss({
region: ossConfig.region,
accessKeyId: ossConfig.accessKeyId,
accessKeySecret: ossConfig.accessKeySecret,
stsToken: ossConfig.stsToken,
refreshSTSToken: () => {
flag = true;
return {
accessKeyId: 'b',
accessKeySecret: 'b',
stsToken: 'b'
};
},
bucket: ossConfig.bucket,
refreshSTSTokenInterval: 1000
});

await sleep(2000);
await store.asyncSignatureUrl('test.txt');
assert(flag);
});
});

describe('multipart', () => {
Expand Down
43 changes: 0 additions & 43 deletions test/node/object.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1048,49 +1048,6 @@ describe('test/object.test.js', () => {
assert.equal(typeof object.res.headers['x-oss-request-id'], 'string');
});

it('should signature use setSTSToken', async () => {
const stsClient = sts(stsConfig);
const policy = {
Statement: [
{
Action: ['oss:*'],
Effect: 'Allow',
Resource: ['acs:oss:*:*:*']
}
],
Version: '1'
};
const response = await stsClient.assumeRole(stsConfig.roleArn, policy);

const tempStore = oss({
bucket: stsConfig.bucket,
accessKeyId: response.credentials.AccessKeyId,
accessKeySecret: response.credentials.AccessKeySecret,
region: config.region,
stsToken: response.credentials.SecurityToken,
refreshSTSToken: async () => {
const r = await stsClient.assumeRole(stsConfig.roleArn, policy);
return {
accessKeyId: r.credentials.AccessKeyId,
accessKeySecret: r.credentials.AccessKeySecret,
stsToken: r.credentials.SecurityToken
};
},
refreshSTSTokenInterval: 2000
});
const content = 'setSTSToken test';
await tempStore.put(name, Buffer.from(content));
const beforeUrl = tempStore.signatureUrl(name);
const urlRes = await urllib.request(beforeUrl);
assert.equal(urlRes.data.toString(), content);
const beforeTime = tempStore.stsTokenFreshTime;
await utils.sleep(ms(5000));
const afterUrl = tempStore.signatureUrl(name);
const afeterRes = await urllib.request(afterUrl);
assert.equal(afeterRes.data.toString(), content);
assert.notEqual(beforeTime, tempStore.stsTokenFreshTime);
});

it('should signature url get object ok', async () => {
try {
const result = await store.get(name);
Expand Down
27 changes: 27 additions & 0 deletions test/node/sts.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ describe('test/sts.test.js', () => {
};
store = new OSS(testRefreshSTSTokenConf);
});

it('should refresh sts token when token is expired', async () => {
try {
store.options.refreshSTSToken = async () => {
Expand All @@ -198,5 +199,31 @@ describe('test/sts.test.js', () => {
assert(false, error);
}
});

it('asyncSignatureUrl will should use refreshSTSToken', async () => {
const { credentials } = await stsClient.assumeRole(stsConfig.roleArn);
let flag = false;

store = new OSS({
region: config.region,
accessKeyId: credentials.AccessKeyId,
accessKeySecret: credentials.AccessKeySecret,
stsToken: credentials.SecurityToken,
refreshSTSToken: () => {
flag = true;
return {
accessKeyId: 'b',
accessKeySecret: 'b',
stsToken: 'b'
};
},
bucket: stsConfig.bucket,
refreshSTSTokenInterval: 1000
});
await utils.sleep(2000);
await store.asyncSignatureUrl('test.txt');

assert(flag);
});
});
});

0 comments on commit 1a05e80

Please sign in to comment.