天翼云删除图片
This commit is contained in:
parent
fecb70c440
commit
26a17de6f1
|
|
@ -1,4 +1,7 @@
|
|||
import { CTYunZone } from '../../types/CTYun';
|
||||
/// <reference types="node" />
|
||||
/// <reference types="node" />
|
||||
import { BinaryToTextEncoding } from 'crypto';
|
||||
import { CTYunZone, ReqOptionProps } from '../../types/CTYun';
|
||||
export declare class CTYunInstance {
|
||||
private accessKey;
|
||||
private secretKey;
|
||||
|
|
@ -18,4 +21,17 @@ export declare class CTYunInstance {
|
|||
private base64ToUrlSafe;
|
||||
private hmacSha1;
|
||||
private urlSafeBase64Encode;
|
||||
removeFile(bucket: string, zone: CTYunZone, key: string): Promise<void>;
|
||||
getAuthorization(reqOptions: ReqOptionProps, zone: CTYunZone): string;
|
||||
signatureFn(reqOptions: ReqOptionProps, zone: CTYunZone): string | Buffer;
|
||||
stringToSign(reqOptions: ReqOptionProps, zone: CTYunZone): string;
|
||||
signedHeaders(headers: Record<string, string>): string;
|
||||
canonicalString(path: string, method: string, headers: Record<string, string>): string;
|
||||
canonicalHeaders(headers: Record<string, string>): string;
|
||||
canonicalHeaderValues(values: string): string;
|
||||
getSigningKey(date: string, zone: CTYunZone): string | Buffer;
|
||||
hmacSha256(key: string | Buffer, content: string | Buffer, digest?: BinaryToTextEncoding, fn?: string): string | Buffer;
|
||||
each(object: any, iterFunction: (key: any, item: any) => any): void;
|
||||
arrayEach(array: any, iterFunction: (item: any, key: any) => any): void;
|
||||
isSignableHeader(key: string): boolean;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// import AWS from 'aws-sdk';
|
||||
import crypto from 'crypto';
|
||||
import { OakNetworkException, } from 'oak-domain/lib/types/Exception';
|
||||
const CTYun_ENDPOINT_LIST = {
|
||||
hazz: {
|
||||
ul: 'oos-hazz.ctyunapi.cn',
|
||||
|
|
@ -41,6 +42,10 @@ const CTYun_ENDPOINT_LIST = {
|
|||
ul: 'oos-sh2.ctyunapi.cn',
|
||||
},
|
||||
};
|
||||
const serviceName = 's3';
|
||||
const v4Identifier = 'aws4_request';
|
||||
const expiresHeader = "presigned-expires";
|
||||
const unsignableHeaders = ["authorization", "x-ctyun-data-location", "content-length", "user-agent", expiresHeader, "expect", "x-amzn-trace-id"];
|
||||
export class CTYunInstance {
|
||||
accessKey;
|
||||
secretKey;
|
||||
|
|
@ -94,33 +99,6 @@ export class CTYunInstance {
|
|||
signature
|
||||
};
|
||||
}
|
||||
// getToken(zone: CTYunZone, bucket: string, actions?: Action[]) {
|
||||
// const config = {
|
||||
// accessKeyId: this.accessKey,
|
||||
// secretAccessKey: this.secretKey,
|
||||
// endpoint: `http://${CTYun_ENDPOINT_LIST[zone].ul}`,
|
||||
// region: "ctyun",
|
||||
// }
|
||||
// const stsClient = new AWS.STS(config);
|
||||
// const actions2 = actions ? actions.map((ele) => `s3:${ele}`) : ['s3:*'];
|
||||
// const params = {
|
||||
// Policy: `{"Version":"2012-10-17","Statement":{"Effect":"Allow","A
|
||||
// ction":${actions2},"Resource":["arn:aws:s3:::${bucket}","arn:aws:s
|
||||
// 3:::${bucket}/*"]}}`,
|
||||
// RoleArn: "arn:aws:iam:::role/oak",
|
||||
// RoleSessionName: "oak",
|
||||
// DurationSeconds: 900, // 过期时间
|
||||
// }
|
||||
// stsClient.assumeRole(params, (err, data) => {
|
||||
// if (err) {
|
||||
// throw err;
|
||||
// }
|
||||
// else {
|
||||
// console.log('success', data);
|
||||
// return data;
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
base64ToUrlSafe(v) {
|
||||
return v.replace(/\//g, '_').replace(/\+/g, '-');
|
||||
}
|
||||
|
|
@ -133,4 +111,146 @@ export class CTYunInstance {
|
|||
const encoded = Buffer.from(jsonFlags).toString('base64');
|
||||
return this.base64ToUrlSafe(encoded);
|
||||
}
|
||||
// 当初就不应该封装天翼云,文档不全,找技术要签名代码还得自己去看源码,恶心
|
||||
// 下面的代码是根据天翼云生成签名源码改动得来 oos-js-sdk-6.2.js
|
||||
async removeFile(bucket, zone, key) {
|
||||
const path = `/${bucket}/${key}`;
|
||||
const host = `${CTYun_ENDPOINT_LIST[zone].ul}`;
|
||||
const url = `https://${host}${path}`;
|
||||
const date = new Date().toISOString().replace(/\.\d{3}Z$/, "Z").replace(/[:\-]|\.\d{3}/g, "");
|
||||
const headers = {
|
||||
"Content-Type": "application/octet-stream; charset=UTF-8",
|
||||
"Host": "oos-hbwh.ctyunapi.cn",
|
||||
"X-Amz-Content-Sha256": "UNSIGNED-PAYLOAD",
|
||||
"X-Amz-User-Agent": "aws-sdk-js/2.296.0 callback"
|
||||
};
|
||||
headers["X-Amz-Date"] = date;
|
||||
const reqOptions = {
|
||||
path,
|
||||
host,
|
||||
date,
|
||||
headers,
|
||||
method: "DELETE",
|
||||
};
|
||||
const authorization = this.getAuthorization(reqOptions, zone);
|
||||
headers['Authorization'] = authorization;
|
||||
try {
|
||||
await fetch(url, {
|
||||
method: 'DELETE',
|
||||
headers,
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
throw new OakNetworkException();
|
||||
}
|
||||
}
|
||||
getAuthorization(reqOptions, zone) {
|
||||
const { headers, date } = reqOptions;
|
||||
const parts = [];
|
||||
const credString = [date.substring(0, 8), zone, serviceName, v4Identifier].join("/");
|
||||
parts.push("AWS4-HMAC-SHA256" + " Credential=" + this.accessKey + "/" + credString);
|
||||
parts.push("SignedHeaders=" + this.signedHeaders(headers));
|
||||
parts.push("Signature=" + this.signatureFn(reqOptions, zone));
|
||||
return parts.join(", ");
|
||||
}
|
||||
signatureFn(reqOptions, zone) {
|
||||
const { date } = reqOptions;
|
||||
var signingKey = this.getSigningKey(date.substring(0, 8), zone);
|
||||
return this.hmacSha256(signingKey, this.stringToSign(reqOptions, zone), "hex");
|
||||
}
|
||||
stringToSign(reqOptions, zone) {
|
||||
const { date, path, method, headers } = reqOptions;
|
||||
var parts = [];
|
||||
parts.push("AWS4-HMAC-SHA256");
|
||||
parts.push(date);
|
||||
parts.push([date.substring(0, 8), zone, serviceName, v4Identifier].join("/"));
|
||||
const canonicalStr = this.canonicalString(path, method, headers);
|
||||
const buffer = Buffer.from(canonicalStr);
|
||||
const encodeStr = crypto.createHash('sha256').update(buffer).digest('hex');
|
||||
parts.push(encodeStr);
|
||||
return parts.join("\n");
|
||||
}
|
||||
signedHeaders(headers) {
|
||||
const keys = [];
|
||||
this.each(headers, (key) => {
|
||||
key = key.toLowerCase();
|
||||
if (this.isSignableHeader(key))
|
||||
keys.push(key);
|
||||
});
|
||||
return keys.sort().join(";");
|
||||
}
|
||||
canonicalString(path, method, headers) {
|
||||
const parts = [];
|
||||
parts.push(method);
|
||||
parts.push(path);
|
||||
parts.push("");
|
||||
parts.push(this.canonicalHeaders(headers) + "\n");
|
||||
parts.push(this.signedHeaders(headers));
|
||||
parts.push("UNSIGNED-PAYLOAD");
|
||||
return parts.join("\n");
|
||||
}
|
||||
canonicalHeaders(headers) {
|
||||
const headerarr = [];
|
||||
this.each(headers, function (key, item) {
|
||||
headerarr.push([key, item]);
|
||||
});
|
||||
headerarr.sort(function (a, b) {
|
||||
return a[0].toLowerCase() < b[0].toLowerCase() ? -1 : 1;
|
||||
});
|
||||
const parts = [];
|
||||
this.arrayEach(headerarr, (item) => {
|
||||
let key = item[0].toLowerCase();
|
||||
if (this.isSignableHeader(key)) {
|
||||
var value = item[1];
|
||||
if (typeof value === "undefined" || value === null || typeof value.toString !== "function") {
|
||||
throw new Error("Header " + key + " contains invalid value");
|
||||
}
|
||||
parts.push(key + ":" + this.canonicalHeaderValues(value.toString()));
|
||||
}
|
||||
});
|
||||
return parts.join("\n");
|
||||
}
|
||||
canonicalHeaderValues(values) {
|
||||
return values.replace(/\s+/g, " ").replace(/^\s+|\s+$/g, "");
|
||||
}
|
||||
getSigningKey(date, zone) {
|
||||
const kDate = this.hmacSha256("AWS4" + this.secretKey, date);
|
||||
const kRegion = this.hmacSha256(kDate, zone);
|
||||
const kService = this.hmacSha256(kRegion, serviceName);
|
||||
var signingKey = this.hmacSha256(kService, v4Identifier);
|
||||
return signingKey;
|
||||
}
|
||||
hmacSha256(key, content, digest, fn) {
|
||||
if (!fn)
|
||||
fn = "sha256";
|
||||
if (typeof content === "string")
|
||||
content = Buffer.from(content);
|
||||
if (!digest) {
|
||||
return crypto.createHmac(fn, key).update(content).digest();
|
||||
}
|
||||
return crypto.createHmac(fn, key).update(content).digest(digest);
|
||||
}
|
||||
each(object, iterFunction) {
|
||||
for (let key in object) {
|
||||
if (Object.prototype.hasOwnProperty.call(object, key)) {
|
||||
const ret = iterFunction.call(this, key, object[key]);
|
||||
if (Object.keys(ret).length === 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
arrayEach(array, iterFunction) {
|
||||
for (let idx in array) {
|
||||
if (Object.prototype.hasOwnProperty.call(array, idx)) {
|
||||
const ret = iterFunction.call(this, array[idx], parseInt(idx, 10));
|
||||
if (Object.keys(ret).length === 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
isSignableHeader(key) {
|
||||
if (key.toLowerCase().indexOf("x-amz-") === 0)
|
||||
return true;
|
||||
return unsignableHeaders.indexOf(key) < 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,4 +3,11 @@
|
|||
* https://www.ctyun.cn/document/10306929/10136179
|
||||
*/
|
||||
export type Action = '*' | 'PutObject' | 'GetObject' | 'DeleteObject' | 'ListBucket';
|
||||
export type ReqOptionProps = {
|
||||
path: string;
|
||||
host: string;
|
||||
date: string;
|
||||
headers: any;
|
||||
method: 'DELETE' | "GET" | "POST" | "PUT";
|
||||
};
|
||||
export type CTYunZone = 'hazz' | 'lnsy' | 'sccd' | 'xjwlmq' | 'gslz' | 'sdqd' | 'gzgy' | 'hbwh' | 'xzls' | 'ahwh' | 'gdsz' | 'jssz' | 'sh2';
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
import { CTYunZone } from '../../types/CTYun';
|
||||
/// <reference types="node" />
|
||||
/// <reference types="node" />
|
||||
import { BinaryToTextEncoding } from 'crypto';
|
||||
import { CTYunZone, ReqOptionProps } from '../../types/CTYun';
|
||||
export declare class CTYunInstance {
|
||||
private accessKey;
|
||||
private secretKey;
|
||||
|
|
@ -18,4 +21,17 @@ export declare class CTYunInstance {
|
|||
private base64ToUrlSafe;
|
||||
private hmacSha1;
|
||||
private urlSafeBase64Encode;
|
||||
removeFile(bucket: string, zone: CTYunZone, key: string): Promise<void>;
|
||||
getAuthorization(reqOptions: ReqOptionProps, zone: CTYunZone): string;
|
||||
signatureFn(reqOptions: ReqOptionProps, zone: CTYunZone): string | Buffer;
|
||||
stringToSign(reqOptions: ReqOptionProps, zone: CTYunZone): string;
|
||||
signedHeaders(headers: Record<string, string>): string;
|
||||
canonicalString(path: string, method: string, headers: Record<string, string>): string;
|
||||
canonicalHeaders(headers: Record<string, string>): string;
|
||||
canonicalHeaderValues(values: string): string;
|
||||
getSigningKey(date: string, zone: CTYunZone): string | Buffer;
|
||||
hmacSha256(key: string | Buffer, content: string | Buffer, digest?: BinaryToTextEncoding, fn?: string): string | Buffer;
|
||||
each(object: any, iterFunction: (key: any, item: any) => any): void;
|
||||
arrayEach(array: any, iterFunction: (item: any, key: any) => any): void;
|
||||
isSignableHeader(key: string): boolean;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ exports.CTYunInstance = void 0;
|
|||
const tslib_1 = require("tslib");
|
||||
// import AWS from 'aws-sdk';
|
||||
const crypto_1 = tslib_1.__importDefault(require("crypto"));
|
||||
const Exception_1 = require("oak-domain/lib/types/Exception");
|
||||
const CTYun_ENDPOINT_LIST = {
|
||||
hazz: {
|
||||
ul: 'oos-hazz.ctyunapi.cn',
|
||||
|
|
@ -45,6 +46,10 @@ const CTYun_ENDPOINT_LIST = {
|
|||
ul: 'oos-sh2.ctyunapi.cn',
|
||||
},
|
||||
};
|
||||
const serviceName = 's3';
|
||||
const v4Identifier = 'aws4_request';
|
||||
const expiresHeader = "presigned-expires";
|
||||
const unsignableHeaders = ["authorization", "x-ctyun-data-location", "content-length", "user-agent", expiresHeader, "expect", "x-amzn-trace-id"];
|
||||
class CTYunInstance {
|
||||
accessKey;
|
||||
secretKey;
|
||||
|
|
@ -98,33 +103,6 @@ class CTYunInstance {
|
|||
signature
|
||||
};
|
||||
}
|
||||
// getToken(zone: CTYunZone, bucket: string, actions?: Action[]) {
|
||||
// const config = {
|
||||
// accessKeyId: this.accessKey,
|
||||
// secretAccessKey: this.secretKey,
|
||||
// endpoint: `http://${CTYun_ENDPOINT_LIST[zone].ul}`,
|
||||
// region: "ctyun",
|
||||
// }
|
||||
// const stsClient = new AWS.STS(config);
|
||||
// const actions2 = actions ? actions.map((ele) => `s3:${ele}`) : ['s3:*'];
|
||||
// const params = {
|
||||
// Policy: `{"Version":"2012-10-17","Statement":{"Effect":"Allow","A
|
||||
// ction":${actions2},"Resource":["arn:aws:s3:::${bucket}","arn:aws:s
|
||||
// 3:::${bucket}/*"]}}`,
|
||||
// RoleArn: "arn:aws:iam:::role/oak",
|
||||
// RoleSessionName: "oak",
|
||||
// DurationSeconds: 900, // 过期时间
|
||||
// }
|
||||
// stsClient.assumeRole(params, (err, data) => {
|
||||
// if (err) {
|
||||
// throw err;
|
||||
// }
|
||||
// else {
|
||||
// console.log('success', data);
|
||||
// return data;
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
base64ToUrlSafe(v) {
|
||||
return v.replace(/\//g, '_').replace(/\+/g, '-');
|
||||
}
|
||||
|
|
@ -137,5 +115,147 @@ class CTYunInstance {
|
|||
const encoded = Buffer.from(jsonFlags).toString('base64');
|
||||
return this.base64ToUrlSafe(encoded);
|
||||
}
|
||||
// 当初就不应该封装天翼云,文档不全,找技术要签名代码还得自己去看源码,恶心
|
||||
// 下面的代码是根据天翼云生成签名源码改动得来 oos-js-sdk-6.2.js
|
||||
async removeFile(bucket, zone, key) {
|
||||
const path = `/${bucket}/${key}`;
|
||||
const host = `${CTYun_ENDPOINT_LIST[zone].ul}`;
|
||||
const url = `https://${host}${path}`;
|
||||
const date = new Date().toISOString().replace(/\.\d{3}Z$/, "Z").replace(/[:\-]|\.\d{3}/g, "");
|
||||
const headers = {
|
||||
"Content-Type": "application/octet-stream; charset=UTF-8",
|
||||
"Host": "oos-hbwh.ctyunapi.cn",
|
||||
"X-Amz-Content-Sha256": "UNSIGNED-PAYLOAD",
|
||||
"X-Amz-User-Agent": "aws-sdk-js/2.296.0 callback"
|
||||
};
|
||||
headers["X-Amz-Date"] = date;
|
||||
const reqOptions = {
|
||||
path,
|
||||
host,
|
||||
date,
|
||||
headers,
|
||||
method: "DELETE",
|
||||
};
|
||||
const authorization = this.getAuthorization(reqOptions, zone);
|
||||
headers['Authorization'] = authorization;
|
||||
try {
|
||||
await fetch(url, {
|
||||
method: 'DELETE',
|
||||
headers,
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
throw new Exception_1.OakNetworkException();
|
||||
}
|
||||
}
|
||||
getAuthorization(reqOptions, zone) {
|
||||
const { headers, date } = reqOptions;
|
||||
const parts = [];
|
||||
const credString = [date.substring(0, 8), zone, serviceName, v4Identifier].join("/");
|
||||
parts.push("AWS4-HMAC-SHA256" + " Credential=" + this.accessKey + "/" + credString);
|
||||
parts.push("SignedHeaders=" + this.signedHeaders(headers));
|
||||
parts.push("Signature=" + this.signatureFn(reqOptions, zone));
|
||||
return parts.join(", ");
|
||||
}
|
||||
signatureFn(reqOptions, zone) {
|
||||
const { date } = reqOptions;
|
||||
var signingKey = this.getSigningKey(date.substring(0, 8), zone);
|
||||
return this.hmacSha256(signingKey, this.stringToSign(reqOptions, zone), "hex");
|
||||
}
|
||||
stringToSign(reqOptions, zone) {
|
||||
const { date, path, method, headers } = reqOptions;
|
||||
var parts = [];
|
||||
parts.push("AWS4-HMAC-SHA256");
|
||||
parts.push(date);
|
||||
parts.push([date.substring(0, 8), zone, serviceName, v4Identifier].join("/"));
|
||||
const canonicalStr = this.canonicalString(path, method, headers);
|
||||
const buffer = Buffer.from(canonicalStr);
|
||||
const encodeStr = crypto_1.default.createHash('sha256').update(buffer).digest('hex');
|
||||
parts.push(encodeStr);
|
||||
return parts.join("\n");
|
||||
}
|
||||
signedHeaders(headers) {
|
||||
const keys = [];
|
||||
this.each(headers, (key) => {
|
||||
key = key.toLowerCase();
|
||||
if (this.isSignableHeader(key))
|
||||
keys.push(key);
|
||||
});
|
||||
return keys.sort().join(";");
|
||||
}
|
||||
canonicalString(path, method, headers) {
|
||||
const parts = [];
|
||||
parts.push(method);
|
||||
parts.push(path);
|
||||
parts.push("");
|
||||
parts.push(this.canonicalHeaders(headers) + "\n");
|
||||
parts.push(this.signedHeaders(headers));
|
||||
parts.push("UNSIGNED-PAYLOAD");
|
||||
return parts.join("\n");
|
||||
}
|
||||
canonicalHeaders(headers) {
|
||||
const headerarr = [];
|
||||
this.each(headers, function (key, item) {
|
||||
headerarr.push([key, item]);
|
||||
});
|
||||
headerarr.sort(function (a, b) {
|
||||
return a[0].toLowerCase() < b[0].toLowerCase() ? -1 : 1;
|
||||
});
|
||||
const parts = [];
|
||||
this.arrayEach(headerarr, (item) => {
|
||||
let key = item[0].toLowerCase();
|
||||
if (this.isSignableHeader(key)) {
|
||||
var value = item[1];
|
||||
if (typeof value === "undefined" || value === null || typeof value.toString !== "function") {
|
||||
throw new Error("Header " + key + " contains invalid value");
|
||||
}
|
||||
parts.push(key + ":" + this.canonicalHeaderValues(value.toString()));
|
||||
}
|
||||
});
|
||||
return parts.join("\n");
|
||||
}
|
||||
canonicalHeaderValues(values) {
|
||||
return values.replace(/\s+/g, " ").replace(/^\s+|\s+$/g, "");
|
||||
}
|
||||
getSigningKey(date, zone) {
|
||||
const kDate = this.hmacSha256("AWS4" + this.secretKey, date);
|
||||
const kRegion = this.hmacSha256(kDate, zone);
|
||||
const kService = this.hmacSha256(kRegion, serviceName);
|
||||
var signingKey = this.hmacSha256(kService, v4Identifier);
|
||||
return signingKey;
|
||||
}
|
||||
hmacSha256(key, content, digest, fn) {
|
||||
if (!fn)
|
||||
fn = "sha256";
|
||||
if (typeof content === "string")
|
||||
content = Buffer.from(content);
|
||||
if (!digest) {
|
||||
return crypto_1.default.createHmac(fn, key).update(content).digest();
|
||||
}
|
||||
return crypto_1.default.createHmac(fn, key).update(content).digest(digest);
|
||||
}
|
||||
each(object, iterFunction) {
|
||||
for (let key in object) {
|
||||
if (Object.prototype.hasOwnProperty.call(object, key)) {
|
||||
const ret = iterFunction.call(this, key, object[key]);
|
||||
if (Object.keys(ret).length === 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
arrayEach(array, iterFunction) {
|
||||
for (let idx in array) {
|
||||
if (Object.prototype.hasOwnProperty.call(array, idx)) {
|
||||
const ret = iterFunction.call(this, array[idx], parseInt(idx, 10));
|
||||
if (Object.keys(ret).length === 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
isSignableHeader(key) {
|
||||
if (key.toLowerCase().indexOf("x-amz-") === 0)
|
||||
return true;
|
||||
return unsignableHeaders.indexOf(key) < 0;
|
||||
}
|
||||
}
|
||||
exports.CTYunInstance = CTYunInstance;
|
||||
|
|
|
|||
|
|
@ -3,4 +3,11 @@
|
|||
* https://www.ctyun.cn/document/10306929/10136179
|
||||
*/
|
||||
export type Action = '*' | 'PutObject' | 'GetObject' | 'DeleteObject' | 'ListBucket';
|
||||
export type ReqOptionProps = {
|
||||
path: string;
|
||||
host: string;
|
||||
date: string;
|
||||
headers: any;
|
||||
method: 'DELETE' | "GET" | "POST" | "PUT";
|
||||
};
|
||||
export type CTYunZone = 'hazz' | 'lnsy' | 'sccd' | 'xjwlmq' | 'gslz' | 'sdqd' | 'gzgy' | 'hbwh' | 'xzls' | 'ahwh' | 'gdsz' | 'jssz' | 'sh2';
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
// import AWS from 'aws-sdk';
|
||||
import crypto from 'crypto';
|
||||
import { Action, CTYunZone } from '../../types/CTYun';
|
||||
|
||||
import crypto, { BinaryToTextEncoding } from 'crypto';
|
||||
import { Action, CTYunZone, ReqOptionProps } from '../../types/CTYun';
|
||||
import {
|
||||
OakExternalException,
|
||||
OakNetworkException,
|
||||
} from 'oak-domain/lib/types/Exception';
|
||||
const CTYun_ENDPOINT_LIST = {
|
||||
hazz: {
|
||||
ul: 'oos-hazz.ctyunapi.cn',
|
||||
|
|
@ -44,6 +47,12 @@ const CTYun_ENDPOINT_LIST = {
|
|||
},
|
||||
};
|
||||
|
||||
|
||||
const serviceName = 's3';
|
||||
const v4Identifier = 'aws4_request';
|
||||
const expiresHeader = "presigned-expires";
|
||||
const unsignableHeaders = ["authorization", "x-ctyun-data-location", "content-length", "user-agent", expiresHeader, "expect", "x-amzn-trace-id"];
|
||||
|
||||
export class CTYunInstance {
|
||||
private accessKey: string;
|
||||
private secretKey: string;
|
||||
|
|
@ -100,34 +109,6 @@ export class CTYunInstance {
|
|||
};
|
||||
}
|
||||
|
||||
// getToken(zone: CTYunZone, bucket: string, actions?: Action[]) {
|
||||
// const config = {
|
||||
// accessKeyId: this.accessKey,
|
||||
// secretAccessKey: this.secretKey,
|
||||
// endpoint: `http://${CTYun_ENDPOINT_LIST[zone].ul}`,
|
||||
// region: "ctyun",
|
||||
// }
|
||||
// const stsClient = new AWS.STS(config);
|
||||
// const actions2 = actions ? actions.map((ele) => `s3:${ele}`) : ['s3:*'];
|
||||
// const params = {
|
||||
// Policy: `{"Version":"2012-10-17","Statement":{"Effect":"Allow","A
|
||||
// ction":${actions2},"Resource":["arn:aws:s3:::${bucket}","arn:aws:s
|
||||
// 3:::${bucket}/*"]}}`,
|
||||
// RoleArn: "arn:aws:iam:::role/oak",
|
||||
// RoleSessionName: "oak",
|
||||
// DurationSeconds: 900, // 过期时间
|
||||
// }
|
||||
// stsClient.assumeRole(params, (err, data) => {
|
||||
// if (err) {
|
||||
// throw err;
|
||||
// }
|
||||
// else {
|
||||
// console.log('success', data);
|
||||
// return data;
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
|
||||
private base64ToUrlSafe(v: string) {
|
||||
return v.replace(/\//g, '_').replace(/\+/g, '-');
|
||||
}
|
||||
|
|
@ -142,4 +123,153 @@ export class CTYunInstance {
|
|||
const encoded = Buffer.from(jsonFlags).toString('base64');
|
||||
return this.base64ToUrlSafe(encoded);
|
||||
}
|
||||
|
||||
|
||||
// 当初就不应该封装天翼云,文档不全,找技术要签名代码还得自己去看源码,恶心
|
||||
// 下面的代码是根据天翼云生成签名源码改动得来 oos-js-sdk-6.2.js
|
||||
async removeFile(bucket: string, zone: CTYunZone, key: string) {
|
||||
const path = `/${bucket}/${key}`;
|
||||
const host = `${CTYun_ENDPOINT_LIST[zone].ul}`;
|
||||
const url = `https://${host}${path}`;
|
||||
const date = new Date().toISOString().replace(/\.\d{3}Z$/, "Z").replace(/[:\-]|\.\d{3}/g, "");
|
||||
const headers: Record<string, string> = {
|
||||
"Content-Type": "application/octet-stream; charset=UTF-8",
|
||||
"Host": "oos-hbwh.ctyunapi.cn",
|
||||
"X-Amz-Content-Sha256": "UNSIGNED-PAYLOAD",
|
||||
"X-Amz-User-Agent": "aws-sdk-js/2.296.0 callback"
|
||||
}
|
||||
headers["X-Amz-Date"] = date;
|
||||
const reqOptions: ReqOptionProps = {
|
||||
path,
|
||||
host,
|
||||
date,
|
||||
headers,
|
||||
method: "DELETE",
|
||||
}
|
||||
|
||||
const authorization = this.getAuthorization(reqOptions, zone);
|
||||
headers['Authorization'] = authorization;
|
||||
try {
|
||||
await fetch(url, {
|
||||
method: 'DELETE',
|
||||
headers,
|
||||
})
|
||||
} catch (err) {
|
||||
throw new OakNetworkException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
getAuthorization(reqOptions: ReqOptionProps, zone: CTYunZone) {
|
||||
const { headers, date } = reqOptions;
|
||||
const parts = [];
|
||||
const credString = [date.substring(0, 8), zone, serviceName, v4Identifier].join("/");
|
||||
parts.push("AWS4-HMAC-SHA256" + " Credential=" + this.accessKey + "/" + credString);
|
||||
parts.push("SignedHeaders=" + this.signedHeaders(headers));
|
||||
parts.push("Signature=" + this.signatureFn(reqOptions, zone));
|
||||
return parts.join(", ");
|
||||
}
|
||||
|
||||
signatureFn(reqOptions: ReqOptionProps, zone: CTYunZone) {
|
||||
const { date } = reqOptions;
|
||||
var signingKey = this.getSigningKey(date.substring(0, 8), zone);
|
||||
return this.hmacSha256(signingKey, this.stringToSign(reqOptions, zone), "hex");
|
||||
}
|
||||
|
||||
stringToSign(reqOptions: ReqOptionProps, zone: CTYunZone) {
|
||||
const { date, path, method, headers } = reqOptions;
|
||||
var parts = [];
|
||||
parts.push("AWS4-HMAC-SHA256");
|
||||
parts.push(date);
|
||||
parts.push([date.substring(0, 8), zone, serviceName, v4Identifier].join("/"));
|
||||
const canonicalStr = this.canonicalString(path, method, headers);
|
||||
const buffer = Buffer.from(canonicalStr);
|
||||
const encodeStr = crypto.createHash('sha256').update(buffer).digest('hex');
|
||||
parts.push(encodeStr);
|
||||
return parts.join("\n");
|
||||
}
|
||||
|
||||
signedHeaders(headers: Record<string, string>) {
|
||||
const keys: string[] = [];
|
||||
this.each(headers, (key) => {
|
||||
key = key.toLowerCase();
|
||||
if (this.isSignableHeader(key)) keys.push(key);
|
||||
});
|
||||
return keys.sort().join(";");
|
||||
}
|
||||
|
||||
canonicalString(path: string, method: string, headers: Record<string, string>) {
|
||||
const parts = [];
|
||||
parts.push(method);
|
||||
parts.push(path);
|
||||
parts.push("");
|
||||
parts.push(this.canonicalHeaders(headers) + "\n");
|
||||
parts.push(this.signedHeaders(headers));
|
||||
parts.push("UNSIGNED-PAYLOAD");
|
||||
return parts.join("\n");
|
||||
}
|
||||
canonicalHeaders(headers: Record<string, string>) {
|
||||
const headerarr:string[][] = [];
|
||||
this.each(headers, function (key, item) {
|
||||
headerarr.push([key, item]);
|
||||
});
|
||||
headerarr.sort(function (a, b) {
|
||||
return a[0].toLowerCase() < b[0].toLowerCase() ? -1 : 1;
|
||||
});
|
||||
const parts: string[] = [];
|
||||
this.arrayEach(headerarr, (item) => {
|
||||
let key = item[0].toLowerCase();
|
||||
if (this.isSignableHeader(key)) {
|
||||
var value = item[1];
|
||||
if (typeof value === "undefined" || value === null || typeof value.toString !== "function") {
|
||||
throw new Error("Header " + key + " contains invalid value");
|
||||
}
|
||||
parts.push(key + ":" + this.canonicalHeaderValues(value.toString()));
|
||||
}
|
||||
});
|
||||
return parts.join("\n");
|
||||
}
|
||||
canonicalHeaderValues(values: string) {
|
||||
return values.replace(/\s+/g, " ").replace(/^\s+|\s+$/g, "");
|
||||
}
|
||||
|
||||
getSigningKey(date: string, zone: CTYunZone) {
|
||||
const kDate = this.hmacSha256("AWS4" + this.secretKey, date);
|
||||
const kRegion = this.hmacSha256(kDate, zone);
|
||||
const kService = this.hmacSha256(kRegion, serviceName);
|
||||
var signingKey = this.hmacSha256(kService, v4Identifier);
|
||||
return signingKey;
|
||||
}
|
||||
|
||||
hmacSha256(key: string | Buffer, content: string | Buffer, digest?: BinaryToTextEncoding, fn?: string) {
|
||||
if (!fn) fn = "sha256";
|
||||
if (typeof content === "string") content = Buffer.from(content);
|
||||
if (!digest) {
|
||||
return crypto.createHmac(fn, key).update(content).digest();
|
||||
}
|
||||
return crypto.createHmac(fn, key).update(content).digest(digest);
|
||||
}
|
||||
|
||||
each(object: any, iterFunction: (key: any, item: any) => any) {
|
||||
for (let key in object) {
|
||||
if (Object.prototype.hasOwnProperty.call(object, key)) {
|
||||
const ret = iterFunction.call(this, key, object[key]);
|
||||
if (Object.keys(ret).length === 0) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
arrayEach(array: any, iterFunction: (item: any, key: any) => any) {
|
||||
for (let idx in array) {
|
||||
if (Object.prototype.hasOwnProperty.call(array, idx)) {
|
||||
const ret = iterFunction.call(this, array[idx], parseInt(idx, 10));
|
||||
if (Object.keys(ret).length === 0) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isSignableHeader(key: string) {
|
||||
if (key.toLowerCase().indexOf("x-amz-") === 0) return true;
|
||||
return unsignableHeaders.indexOf(key) < 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,4 +5,12 @@
|
|||
*/
|
||||
export type Action = '*' | 'PutObject' | 'GetObject' | 'DeleteObject' | 'ListBucket';
|
||||
|
||||
export type ReqOptionProps = {
|
||||
path: string;
|
||||
host: string;
|
||||
date: string;
|
||||
headers: any
|
||||
method: 'DELETE' | "GET" | "POST" | "PUT",
|
||||
}
|
||||
|
||||
export type CTYunZone = 'hazz' | 'lnsy' | 'sccd' | 'xjwlmq' | 'gslz' | 'sdqd' | 'gzgy' | 'hbwh' | 'xzls' | 'ahwh' | 'gdsz' | 'jssz' | 'sh2';
|
||||
|
|
|
|||
Loading…
Reference in New Issue