Skip to content

Commit

Permalink
PutPolicy support custom fields (#429)
Browse files Browse the repository at this point in the history
  • Loading branch information
lihsai0 authored Mar 12, 2024
1 parent 9d4fdc2 commit f60fecd
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 49 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
## CHANGE LOG
## 7.11.1
- 对象存储,上传策略移除严格模式,支持任意策略选项

## 7.11.0
- 对象存储,新增支持归档直读存储
- 对象存储,批量操作、解冻操作支持自动查询 rs 服务域名
Expand Down
12 changes: 12 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1478,6 +1478,7 @@ export declare namespace rs {
expires?: number;
insertOnly?: number;
saveKey?: string;
forceSaveKey?: boolean;
endUser?: string;
returnUrl?: string;
returnBody?: string;
Expand All @@ -1498,8 +1499,19 @@ export declare namespace rs {
detectMime?: number;
deleteAfterDays?: number;
fileType?: number;

// @deprecated
transform?: string;
// @deprecated
transformFallbackMode?: string;
// @deprecated
transformFallbackKey?: string;

[key: string]: string | number | boolean;
}
class PutPolicy {
[k: string]: string | number | boolean | Function;

constructor(options?: PutPolicyOptions);

getFlags(): any;
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "qiniu",
"version": "7.11.0",
"version": "7.11.1",
"description": "Node wrapper for Qiniu Resource (Cloud) Storage API",
"main": "index.js",
"directories": {
Expand Down
124 changes: 76 additions & 48 deletions qiniu/storage/rs.js
Original file line number Diff line number Diff line change
Expand Up @@ -1362,58 +1362,83 @@ function restoreArReq (mac, config, entry, freezeAfterDays, callbackFunc) {
);
}

// 上传策略
// @link https://developer.qiniu.com/kodo/manual/1206/put-policy
function PutPolicy(options) {
// just for compatibility with old sdk versions
function _putPolicyBuildInKeys () {
return ['scope', 'isPrefixalScope', 'insertOnly', 'saveKey', 'forceSaveKey',
'endUser', 'returnUrl', 'returnBody', 'callbackUrl', 'callbackHost',
'callbackBody', 'callbackBodyType', 'callbackFetchKey', 'persistentOps',
'persistentNotifyUrl', 'persistentPipeline', 'fsizeLimit', 'fsizeMin',
'detectMime', 'mimeLimit', 'deleteAfterDays', 'fileType'
];
}

/**
* @typedef PutPolicyOptions
* @extends Object.<string, string | number>
* @property {string} scope
* @property {number} [isPrefixalScope]
* @property {number} [expires]
* @property {number} [insertOnly]
* @property {string} [saveKey]
* @property {string} [forceSaveKey]
* @property {string} [endUser]
* @property {string} [returnUrl]
* @property {string} [returnBody]
* @property {string} [callbackUrl]
* @property {string} [callbackHost]
* @property {string} [callbackBody]
* @property {string} [callbackBodyType]
* @property {number} [callbackFetchKey]
* @property {string} [persistentOps]
* @property {string} [persistentNotifyUrl]
* @property {string} [persistentPipeline]
* @property {number} [fsizeLimit]
* @property {number} [fsizeMin]
* @property {string} [mimeLimit]
* @property {number} [detectMime]
* @property {number} [deleteAfterDays]
* @property {number} [fileType]
* @property {string} [transform] Deprecated
* @property {string} [transformFallbackMode] Deprecated
* @property {string} [transformFallbackKey] Deprecated
*/

/**
* 上传策略
* @link https://developer.qiniu.com/kodo/manual/1206/put-policy
* @param {PutPolicyOptions} options
* @constructor
* @extends Object.<string, string | number>
*/
function PutPolicy (options) {
if (typeof options !== 'object') {
throw new Error('invalid putpolicy options');
}

this.scope = options.scope || null;
this.isPrefixalScope = options.isPrefixalScope || null;
this.expires = options.expires || 3600;
this.insertOnly = options.insertOnly || null;

this.saveKey = options.saveKey || null;
this.forceSaveKey = options.forceSaveKey || null;
this.endUser = options.endUser || null;

this.returnUrl = options.returnUrl || null;
this.returnBody = options.returnBody || null;

this.callbackUrl = options.callbackUrl || null;
this.callbackHost = options.callbackHost || null;
this.callbackBody = options.callbackBody || null;
this.callbackBodyType = options.callbackBodyType || null;
this.callbackFetchKey = options.callbackFetchKey || null;

this.persistentOps = options.persistentOps || null;
this.persistentNotifyUrl = options.persistentNotifyUrl || null;
this.persistentPipeline = options.persistentPipeline || null;

this.fsizeLimit = options.fsizeLimit || null;
this.fsizeMin = options.fsizeMin || null;
this.mimeLimit = options.mimeLimit || null;
Object.keys(options).forEach(k => {
if (k === 'expires') {
return;
}
this[k] = options[k];
});

this.detectMime = options.detectMime || null;
this.deleteAfterDays = options.deleteAfterDays || null;
this.fileType = options.fileType || null;
this.expires = options.expires || 3600;
_putPolicyBuildInKeys().forEach(k => {
if (this[k] === undefined) {
this[k] = this[k] || null;
}
});
}

PutPolicy.prototype.getFlags = function () {
var flags = {};
var attrs = ['scope', 'isPrefixalScope', 'insertOnly', 'saveKey', 'forceSaveKey',
'endUser', 'returnUrl', 'returnBody', 'callbackUrl', 'callbackHost',
'callbackBody', 'callbackBodyType', 'callbackFetchKey', 'persistentOps',
'persistentNotifyUrl', 'persistentPipeline', 'fsizeLimit', 'fsizeMin',
'detectMime', 'mimeLimit', 'deleteAfterDays', 'fileType'
];
const flags = {};

for (var i = attrs.length - 1; i >= 0; i--) {
if (this[attrs[i]] !== null) {
flags[attrs[i]] = this[attrs[i]];
Object.keys(this).forEach(k => {
if (k === 'expires' || this[k] === null) {
return;
}
}
flags[k] = this[k];
});

flags.deadline = this.expires + Math.floor(Date.now() / 1000);

Expand All @@ -1422,10 +1447,13 @@ PutPolicy.prototype.getFlags = function () {

PutPolicy.prototype.uploadToken = function (mac) {
mac = mac || new digest.Mac();
var flags = this.getFlags();
var encodedFlags = util.urlsafeBase64Encode(JSON.stringify(flags));
var encoded = util.hmacSha1(encodedFlags, mac.secretKey);
var encodedSign = util.base64ToUrlSafe(encoded);
var uploadToken = mac.accessKey + ':' + encodedSign + ':' + encodedFlags;
return uploadToken;
const flags = this.getFlags();
const encodedFlags = util.urlsafeBase64Encode(JSON.stringify(flags));
const encoded = util.hmacSha1(encodedFlags, mac.secretKey);
const encodedSign = util.base64ToUrlSafe(encoded);
return [
mac.accessKey,
encodedSign,
encodedFlags
].join(':');
};
77 changes: 77 additions & 0 deletions test/rs.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -933,4 +933,81 @@ describe('test start bucket manager', function () {
);
});
});

describe('test PutPolicy', function () {
it('test build-in options (backward compatibility)', function () {
const buildInProps = {
scope: 'mocked-bucket:some/key',
isPrefixalScope: 1,
insertOnly: 1,
saveKey: 'some/key/specified.mp4',
forceSaveKey: true,
endUser: 'some-user-id',
returnUrl: 'https://mocked.qiniu.com/put-policy/return-url',
returnBody: '{"msg": "mocked"}',
callbackUrl: 'https://mocked.qiniu.com/put-policy/callback-url',
callbackHost: 'mocked.qiniu.com',
callbackBody: '{"msg": "mocked"}',
callbackBodyType: 'application/json',
callbackFetchKey: 1,
persistentOps: 'avthumb/flv|saveas/bW9ja2VkLWJ1Y2tldDpzb21lL2tleS9zcGVjaWZpZWQuZmx2Cg==',
persistentNotifyUrl: 'https://mocked.qiniu.com/put-policy/persistent-notify-url',
persistentPipeline: 'mocked-pipe',
fsizeLimit: 104857600,
fsizeMin: 10485760,
detectMime: 1,
mimeLimit: 'video/*',
deleteAfterDays: 365,
fileType: 1
};
const policy = new qiniu.rs.PutPolicy(buildInProps);
for (const k of Object.keys(buildInProps)) {
should.equal(policy[k], buildInProps[k], `key ${k}, ${policy[k]} not eql ${buildInProps[k]}`);
}
const flags = policy.getFlags();
for (const k of Object.keys(buildInProps)) {
should.equal(flags[k], buildInProps[k], `key ${k}, ${policy[k]} not eql ${buildInProps[k]}`);
}
});

it('test expires option default value', function () {
const putPolicyOptions = {
scope: 'mocked-bucket:some/key'
};
const policy = new qiniu.rs.PutPolicy(putPolicyOptions);

// deviation should be less than 1sec
const deviation = policy.getFlags().deadline - Math.floor(Date.now() / 1000) - 3600;
Math.abs(deviation).should.lessThan(1);
});

it('test expires option', function () {
const expires = 604800;
const putPolicyOptions = {
scope: 'mocked-bucket:some/key',
expires: expires
};
const policy = new qiniu.rs.PutPolicy(putPolicyOptions);

// deviation should be less than 1sec
const deviation = policy.getFlags().deadline - Math.floor(Date.now() / 1000) - expires;
Math.abs(deviation).should.lessThan(1);
});

it('test custom options', function () {
const putPolicyOptions = {
scope: 'mocked-bucket:some/key',
mockedProp: 'mockedProp',
transform: 'some',
transform_fallback_mode: 'bar',
transform_fallback_key: 'foo'
};
const policy = new qiniu.rs.PutPolicy(putPolicyOptions);
const flags = policy.getFlags();

for (const k of Object.keys(putPolicyOptions)) {
should.equal(flags[k], putPolicyOptions[k], `key ${k}, ${policy[k]} not eql ${putPolicyOptions[k]}`);
}
});
});
});

0 comments on commit f60fecd

Please sign in to comment.