-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
218 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import { OperateObjectParams } from '..' | ||
import { NosBaseClient } from '../client' | ||
import { createHmac } from 'crypto' | ||
|
||
export interface CreateTokenParams extends OperateObjectParams { | ||
/** | ||
* 过期时间,单位毫秒。(NOS 只要求秒级,但是为了减少转化过程,只需要传入毫秒即可) | ||
*/ | ||
expires: number | ||
/** | ||
* 对象的最小上传大小 | ||
*/ | ||
objectSizeMin?: number | ||
/** | ||
* 对象的最大上传大小 | ||
*/ | ||
objectSizeMax?: number | ||
/** | ||
* 对象类型限制,可以传入 `'image/png;image/jpg'` 字符串或者 `['image/png', 'image/jpg']` 数组 | ||
*/ | ||
mimeLimit?: string | string[] | ||
/** | ||
* 是否允许覆盖上传,默认允许 | ||
*/ | ||
overwrite?: boolean | ||
} | ||
|
||
export interface Token { | ||
/** | ||
* 上传凭证 | ||
*/ | ||
putPolicy: string | ||
/** | ||
* 签名 | ||
*/ | ||
sign: string | ||
} | ||
|
||
export class NosClientAuthExt extends NosBaseClient { | ||
/** | ||
* 创建上传凭证,返回 Token 对象,里面有 `putPolicy` 和 `sign` 字段,前端可以通过此构建上传 Token | ||
* @param params | ||
*/ | ||
createToken(params: CreateTokenParams): Token { | ||
const putPolicyObject = { | ||
Bucket: params.bucket || this.options.defaultBucket, | ||
Object: params.objectKey, | ||
Expires: Math.floor(params.expires / 1000), | ||
ObjectSizeMin: params.objectSizeMin, | ||
ObjectSizeMax: params.objectSizeMax, | ||
MimeLimit: params.mimeLimit, | ||
OverWrite: params.overwrite | ||
} | ||
|
||
if (putPolicyObject.MimeLimit && typeof putPolicyObject.MimeLimit !== 'string') { | ||
putPolicyObject.MimeLimit = putPolicyObject.MimeLimit.join(';') | ||
} | ||
|
||
const putPolicyString = JSON.stringify(putPolicyObject) | ||
const putPolicy = Buffer.from(putPolicyString, 'utf8').toString('base64') | ||
|
||
const ciper = createHmac('sha256', this.options.accessSecret) | ||
ciper.update(putPolicy, 'utf8') | ||
const sign = ciper.digest('base64') | ||
|
||
return { | ||
putPolicy, | ||
sign | ||
} | ||
} | ||
|
||
/** | ||
* 创建上传凭证字符串,对 `createToken` 的简单包装,直接返回 accessKey:sign:putPolicy 字符串 | ||
* @param params | ||
*/ | ||
createTokenString(params: CreateTokenParams): string { | ||
const { putPolicy, sign } = this.createToken(params) | ||
return `${this.options.accessKey}:${sign}:${putPolicy}` | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
import { NosClient, Token } from '../../src' | ||
import { cleanClient, newClient, randomObjectKey, uploadUseToken } from '../helpers/client' | ||
|
||
let client: NosClient | ||
let bucket: string | ||
|
||
beforeAll(async () => { | ||
client = await newClient() | ||
bucket = client.options.defaultBucket as string | ||
}) | ||
|
||
afterAll(async () => { | ||
await cleanClient(client) | ||
}) | ||
|
||
function joinToken(token: Token) { | ||
return `${client.options.accessKey}:${token.sign}:${token.putPolicy}` | ||
} | ||
|
||
describe('createToken', () => { | ||
it('should create token success', async () => { | ||
const objectKey = randomObjectKey() | ||
const token = client.createToken({ | ||
objectKey, | ||
expires: Date.now() + 3600 * 1000, | ||
}) | ||
|
||
const ret = await uploadUseToken(`${client.options.accessKey}:${token.sign}:${token.putPolicy}`, bucket, objectKey, 'xxxxxx') | ||
expect(ret).toBeTrue() | ||
|
||
await expect(client.isObjectExist({objectKey})).resolves.toBeTrue() | ||
}) | ||
|
||
it('should limit size', async () => { | ||
const objectKey = randomObjectKey() | ||
const minSize = 4 | ||
const maxSize = 16 | ||
const token = client.createToken({ | ||
objectKey, | ||
expires: Date.now() + 3600 * 1000, | ||
objectSizeMin: minSize, | ||
objectSizeMax: maxSize, | ||
}) | ||
|
||
await expect(uploadUseToken(joinToken(token), bucket, objectKey, 'x'.repeat(3))).resolves.toBeFalse() | ||
await expect(uploadUseToken(joinToken(token), bucket, objectKey, 'x'.repeat(10))).resolves.toBeTrue() | ||
await expect(uploadUseToken(joinToken(token), bucket, objectKey, 'x'.repeat(20))).resolves.toBeFalse() | ||
}) | ||
|
||
it('should mime limit works', async () => { | ||
const objectKey = randomObjectKey('.jpg') | ||
const token = client.createToken({ | ||
objectKey, | ||
expires: Date.now() + 3600 * 1000, | ||
mimeLimit: ['image/jpg', 'image.png'] | ||
}) | ||
|
||
await expect(uploadUseToken(joinToken(token), bucket, objectKey, 'x')).resolves.toBeFalse() | ||
await expect(uploadUseToken(joinToken(token), bucket, objectKey, 'x', { | ||
'content-type': 'image/jpg' | ||
})).resolves.toBeTrue() | ||
await expect(uploadUseToken(joinToken(token), bucket, objectKey, 'x', { | ||
'content-type': 'text/plain' | ||
})).resolves.toBeFalse() | ||
}) | ||
|
||
it('should overwrite=true works', async () => { | ||
const objectKey = randomObjectKey() | ||
const token = client.createToken({ | ||
objectKey, | ||
expires: Date.now() + 3600 * 1000, | ||
overwrite: true | ||
}) | ||
|
||
for (let i = 0; i < 2; i++) { | ||
await expect(uploadUseToken(joinToken(token), bucket, objectKey, 'x')).resolves.toBeTrue() | ||
} | ||
}) | ||
|
||
it('should overwrite=false works', async () => { | ||
const objectKey = randomObjectKey() | ||
const token = client.createToken({ | ||
objectKey, | ||
expires: Date.now() + 3600 * 1000, | ||
overwrite: false | ||
}) | ||
|
||
await expect(uploadUseToken(joinToken(token), bucket, objectKey, 'x')).resolves.toBeTrue() | ||
await expect(uploadUseToken(joinToken(token), bucket, objectKey, 'x')).resolves.toBeFalse() | ||
}) | ||
}) | ||
|
||
describe('createTokenString', () => { | ||
it('should equal with createToken', () => { | ||
const objectKey = randomObjectKey() | ||
const expires = Date.now() | ||
const token = client.createToken({ | ||
objectKey, | ||
expires | ||
}) | ||
|
||
const tokenString = client.createTokenString({objectKey, expires}) | ||
|
||
expect(tokenString).toEqual(`${client.options.accessKey}:${token.sign}:${token.putPolicy}`) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters