@deployport/api-services-corelib 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2 @@
1
+ import { Runtime } from "@deployport/specular-runtime";
2
+ export declare function Configure(c?: Runtime.ClientConfig): Runtime.ClientConfig;
@@ -0,0 +1,12 @@
1
+ import { ConfigureSignatureV1 } from "./signer.js";
2
+ import { Runtime } from "@deployport/specular-runtime";
3
+ export function Configure(c) {
4
+ const requestConfigurators = c?.requestConfigurators ?? [];
5
+ requestConfigurators.push(async (sub) => {
6
+ console.warn("configuring request from config", sub.request);
7
+ await ConfigureSignatureV1(sub, c);
8
+ });
9
+ return Runtime.MergeClientConfig(c, {
10
+ requestConfigurators,
11
+ });
12
+ }
@@ -0,0 +1,13 @@
1
+ import { Config, ConfigureSignatureV1 } from "./signer.js";
2
+ import { Runtime } from "@deployport/specular-runtime";
3
+
4
+ export function Configure(c?: Runtime.ClientConfig): Runtime.ClientConfig {
5
+ const requestConfigurators = c?.requestConfigurators ?? [];
6
+ requestConfigurators.push(async (sub) => {
7
+ console.warn("configuring request from config", sub.request);
8
+ await ConfigureSignatureV1(sub, c as Config)
9
+ });
10
+ return Runtime.MergeClientConfig(c, {
11
+ requestConfigurators,
12
+ });
13
+ }
@@ -0,0 +1,22 @@
1
+ import { Runtime } from "@deployport/specular-runtime";
2
+ type Credentials = {
3
+ keyID: string;
4
+ secret: string;
5
+ };
6
+ export type Config = {
7
+ deployport?: {
8
+ region?: string;
9
+ accessKeyID?: string;
10
+ secretAccessKey?: string;
11
+ };
12
+ };
13
+ export declare function ConfigureSignatureV1(submission: Runtime.Submission, config: Config): Promise<void>;
14
+ export declare const getSigningKeyReal: (credentials: Credentials, shortDate: string, region: string, service: string, vendor: string) => Promise<Uint8Array>;
15
+ /**
16
+ * Converts a Uint8Array of binary data to a hexadecimal encoded string.
17
+ *
18
+ * @param bytes The binary data to encode
19
+ */
20
+ export declare function toHex(bytes: Uint8Array): string;
21
+ export declare const iso8601: (time: Date) => string;
22
+ export {};
@@ -0,0 +1,256 @@
1
+ import { Sha256 } from '@aws-crypto/sha256-js';
2
+ const VendorCode = 'dpp';
3
+ export async function ConfigureSignatureV1(submission, config) {
4
+ // const { request } = submission;
5
+ // const { headers } = request;
6
+ // if (!headers["x-specular-signature"]) {
7
+ // headers["x-specular-signature"] = "v1";
8
+ // }
9
+ console.log("Hello from ConfigureSignatureV1 in runtime/signer.ts!", submission);
10
+ const annotations = submission.operation.resource.package.annotations;
11
+ console.log("service annotations", annotations);
12
+ const ServiceSignatureV1 = annotations.find((a) => a.constructor.name === "ServiceSignatureV1");
13
+ if (!ServiceSignatureV1) {
14
+ return;
15
+ }
16
+ console.log("signing using config", config);
17
+ const { deployport } = config;
18
+ if (!deployport) {
19
+ console.warn("missing required deployport configuration for signing", config);
20
+ return;
21
+ }
22
+ const { region, accessKeyID: keyID, secretAccessKey: secret } = deployport;
23
+ if (!region || !keyID || !secret) {
24
+ console.warn("missing required configuration for signing", deployport);
25
+ return;
26
+ }
27
+ const { ServiceName: signingService } = ServiceSignatureV1;
28
+ console.log("annotations", annotations, VendorCode, signingService);
29
+ const { longDate, shortDate } = formatDate(new Date());
30
+ const signingScope = buildSigningScope(VendorCode, region, signingService, shortDate);
31
+ console.log("signingScope", signingScope);
32
+ const credentialPart = buildCredentialPart(keyID, signingScope);
33
+ console.log("credentialPart", credentialPart);
34
+ const authHeaderPrefix = vendorAlgorithm(VendorCode) + " " + credentialPart;
35
+ const bodyDigest = toHex(await hashSHA256(submission.request.body));
36
+ console.log("bodyDigest", bodyDigest);
37
+ const headers = submission.request.headers;
38
+ headers[vendorDateHeaderKey(VendorCode)] = longDate;
39
+ headers[vendorHeaderCanonicalNameKey(VendorCode, "Service")] = signingService;
40
+ headers[vendorHeaderCanonicalNameKey(VendorCode, "Resource")] = submission.operation.resource.packageUniqueName;
41
+ headers[vendorHeaderCanonicalNameKey(VendorCode, "Operation")] = submission.operation.name;
42
+ headers[vendorRegionHeaderKey(VendorCode)] = region;
43
+ headers[vendorHeaderCanonicalNameKey(VendorCode, "Content-Sha256")] = bodyDigest;
44
+ console.log("headers", headers);
45
+ const canonicalHeaders = getCanonicalHeaders(VendorCode, headers);
46
+ const credentials = {
47
+ keyID,
48
+ secret,
49
+ };
50
+ const signingKey = getSigningKey(credentials, region, shortDate, signingService, VendorCode);
51
+ const { request } = submission;
52
+ // const payloadHash = await getPayloadHash(request.body);
53
+ const signature = await getSignature(VendorCode, longDate, signingScope, signingKey, createCanonicalRequest(request, canonicalHeaders, bodyDigest));
54
+ headers["Authorization"] = [authHeaderPrefix, `SignedHeaders=${canonicalHeaders.signedHeaders}`, `Signature=${signature}`];
55
+ console.log("signed headers", headers);
56
+ }
57
+ function createCanonicalRequest(request, canonicalHeaders, payloadHash) {
58
+ const sortedHeaders = canonicalHeaders.names;
59
+ return `${request.method}
60
+ ${getCanonicalPath(request)}
61
+ ${getCanonicalQuery(request)}
62
+ ${sortedHeaders.map((name) => `${name}:${canonicalHeaders.clean[name]}`).join("\n")}
63
+
64
+ ${canonicalHeaders.signedHeaders}
65
+ ${payloadHash}`;
66
+ }
67
+ function getCanonicalQuery({ path }) {
68
+ return ""; // TODO: hardcoded, we don't use query strings
69
+ }
70
+ function getCanonicalPath({ path }) {
71
+ console.log("getCanonicalPath path", path);
72
+ return path;
73
+ // if (this.uriEscapePath) {
74
+ // Non-S3 services, we normalize the path and then double URI encode it.
75
+ // Ref: "Remove Dot Segments" https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.4
76
+ const normalizedPathSegments = [];
77
+ for (const pathSegment of path.split("/")) {
78
+ if (pathSegment?.length === 0)
79
+ continue;
80
+ if (pathSegment === ".")
81
+ continue;
82
+ if (pathSegment === "..") {
83
+ normalizedPathSegments.pop();
84
+ }
85
+ else {
86
+ normalizedPathSegments.push(pathSegment);
87
+ }
88
+ // }
89
+ // Joining by single slashes to remove consecutive slashes.
90
+ const normalizedPath = `${path?.startsWith("/") ? "/" : ""}${normalizedPathSegments.join("/")}${normalizedPathSegments.length > 0 && path?.endsWith("/") ? "/" : ""}`;
91
+ const doubleEncoded = encodeURIComponent(normalizedPath);
92
+ return doubleEncoded.replace(/%2F/g, "/");
93
+ }
94
+ // For S3, we shouldn't normalize the path. For example, object name
95
+ // my-object//example//photo.user should not be normalized to
96
+ // my-object/example/photo.user
97
+ console.log("getCanonicalPath path (curated)", path);
98
+ return path;
99
+ }
100
+ // /**
101
+ // * @private
102
+ // */
103
+ // export const getPayloadHash = async (
104
+ // body: string,
105
+ // ): Promise<string> => {
106
+ // const hashCtor = new Sha256();
107
+ // // new Sha256().update(toUint8Array(body));
108
+ // new Sha256().update(body);
109
+ // return toHex(await hashCtor.digest());
110
+ // };
111
+ async function getSignature(vendor, longDate, credentialScope, keyPromise, canonicalRequest) {
112
+ const stringToSign = await createStringToSign(vendor, longDate, credentialScope, canonicalRequest);
113
+ console.log("stringToSign", stringToSign);
114
+ const hash = new Sha256(await keyPromise);
115
+ hash.update(stringToSign);
116
+ return toHex(await hash.digest());
117
+ }
118
+ async function createStringToSign(vendor, longDate, credentialScope, canonicalRequest) {
119
+ console.log('canonicalRequest', canonicalRequest);
120
+ const hash = new Sha256();
121
+ hash.update(canonicalRequest);
122
+ const hashedRequest = await hash.digest();
123
+ return `${vendorAlgorithm(vendor)}
124
+ ${longDate}
125
+ ${credentialScope}
126
+ ${toHex(hashedRequest)}`;
127
+ }
128
+ const toUtf8 = (input) => new TextDecoder("utf-8").decode(input);
129
+ // export const toUint8Array = (data: string | ArrayBuffer | ArrayBufferView): Uint8Array => {
130
+ // // if (typeof data === "string") {
131
+ // // return fromUtf8(data);
132
+ // // }
133
+ // // if (ArrayBuffer.isView(data)) {
134
+ // // return new Uint8Array(data.buffer, data.byteOffset, data.byteLength / Uint8Array.BYTES_PER_ELEMENT);
135
+ // // }
136
+ // // return new Uint8Array(data);
137
+ // return new Uint8Array(data);
138
+ // };
139
+ async function getSigningKey(credentials, region, shortDate, service, vendor) {
140
+ return getSigningKeyReal(credentials, shortDate, region, service, vendor);
141
+ }
142
+ function vendorKeyType(vendor) {
143
+ return vendor + "1_request";
144
+ }
145
+ export const getSigningKeyReal = async (credentials, shortDate, region, service, vendor) => {
146
+ let key = `${vendor}1${credentials.secret}`;
147
+ for (const signable of [shortDate, region, service, vendorKeyType(vendor)]) {
148
+ key = await hmac(key, signable);
149
+ }
150
+ return key;
151
+ };
152
+ // type signingData = string | Uint8Array;
153
+ const hmac = (secret, data) => {
154
+ const hash = new Sha256(secret);
155
+ hash.update(data);
156
+ return hash.digest();
157
+ };
158
+ function getCanonicalHeaders(vendorCode, headers) {
159
+ const validHeaderNames = [
160
+ "Host",
161
+ vendorDateHeaderKey(vendorCode),
162
+ vendorHeaderCanonicalNameKey(vendorCode, "Service"),
163
+ vendorHeaderCanonicalNameKey(vendorCode, "Resource"),
164
+ vendorHeaderCanonicalNameKey(vendorCode, "Operation"),
165
+ vendorHeaderCanonicalNameKey(vendorCode, "Content-Sha256"),
166
+ vendorHeaderCanonicalNameKey(vendorCode, "Region"),
167
+ ];
168
+ const cleaned = {};
169
+ for (const key in headers) {
170
+ if (!validHeaderNames.includes(key)) {
171
+ continue;
172
+ }
173
+ const val = headers[key];
174
+ if (Array.isArray(val)) {
175
+ cleaned[key] = val.map(v => v.trim());
176
+ }
177
+ else {
178
+ cleaned[key] = val.trim();
179
+ }
180
+ }
181
+ const names = Object.keys(cleaned).sort();
182
+ return {
183
+ names,
184
+ signedHeaders: names.map(n => n.toLowerCase()).join(";"),
185
+ clean: cleaned,
186
+ };
187
+ }
188
+ const SHORT_TO_HEX = {};
189
+ for (let i = 0; i < 256; i++) {
190
+ let encodedByte = i.toString(16).toLowerCase();
191
+ if (encodedByte.length === 1) {
192
+ encodedByte = `0${encodedByte}`;
193
+ }
194
+ SHORT_TO_HEX[i] = encodedByte;
195
+ }
196
+ /**
197
+ * Converts a Uint8Array of binary data to a hexadecimal encoded string.
198
+ *
199
+ * @param bytes The binary data to encode
200
+ */
201
+ export function toHex(bytes) {
202
+ let out = "";
203
+ for (let i = 0; i < bytes.byteLength; i++) {
204
+ out += SHORT_TO_HEX[bytes[i]];
205
+ }
206
+ return out;
207
+ }
208
+ // convert hashSHA256 to ts
209
+ async function hashSHA256(dataStr) {
210
+ const data = new TextEncoder().encode(dataStr);
211
+ const hash = new Sha256();
212
+ hash.update(data);
213
+ return await hash.digest();
214
+ }
215
+ const credentialPartPrefix = "Credential=";
216
+ function buildCredentialPart(keyID, signingScope) {
217
+ return `${credentialPartPrefix}${keyID}/${signingScope}`;
218
+ }
219
+ function buildSigningScope(vendorCode, region, service, shortDate) {
220
+ return [
221
+ shortDate,
222
+ region,
223
+ service,
224
+ vendorRequestCode(vendorCode),
225
+ ].join("/");
226
+ }
227
+ function vendorRequestCode(vendorCode) {
228
+ return vendorCode + "1_request";
229
+ }
230
+ function vendorAlgorithm(vendorCode) {
231
+ return vendorCode.toUpperCase() + "1-HMAC-SHA256";
232
+ }
233
+ // function formatShortTime(dt: Date): string {
234
+ // return dt.toISOString().replace(/[-:]/g, "").slice(0, 15);
235
+ // }
236
+ export const iso8601 = (time) => time.toISOString()
237
+ .replace(/\.\d{3}Z$/, "Z");
238
+ const formatDate = (now) => {
239
+ const longDate = iso8601(now).replace(/[\-:]/g, "");
240
+ return {
241
+ longDate,
242
+ shortDate: longDate.slice(0, 8),
243
+ };
244
+ };
245
+ function vendorDateHeaderKey(vendorCode) {
246
+ return vendorHeaderCanonicalNameKey(vendorCode, "Date");
247
+ }
248
+ function vendorRegionHeaderKey(vendorCode) {
249
+ return vendorHeaderCanonicalNameKey(vendorCode, "Region");
250
+ }
251
+ function vendorHeaderCanonicalNameKey(vendorCode, header) {
252
+ return httpHeaderStandardName("x-" + vendorCode + "-" + header.toLowerCase());
253
+ }
254
+ function httpHeaderStandardName(header) {
255
+ return header.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("-");
256
+ }
@@ -0,0 +1,361 @@
1
+ import { Sha256 } from '@aws-crypto/sha256-js';
2
+ import { Runtime } from "@deployport/specular-runtime";
3
+ import { SourceData } from "@aws-sdk/types";
4
+
5
+ type Credentials = {
6
+ keyID: string;
7
+ secret: string;
8
+ }
9
+
10
+ export type Config = {
11
+ deployport?: {
12
+ region?: string;
13
+ accessKeyID?: string;
14
+ secretAccessKey?: string;
15
+ }
16
+ }
17
+
18
+ const VendorCode = 'dpp';
19
+
20
+ export async function ConfigureSignatureV1(submission: Runtime.Submission, config: Config): Promise<void> {
21
+
22
+ // const { request } = submission;
23
+ // const { headers } = request;
24
+ // if (!headers["x-specular-signature"]) {
25
+ // headers["x-specular-signature"] = "v1";
26
+ // }
27
+ console.log("Hello from ConfigureSignatureV1 in runtime/signer.ts!", submission)
28
+ const annotations = submission.operation.resource.package.annotations;
29
+ console.log("service annotations", annotations);
30
+ const ServiceSignatureV1 = annotations.find((a) => a.constructor.name === "ServiceSignatureV1");
31
+ if (!ServiceSignatureV1) {
32
+ return;
33
+ }
34
+ console.log("signing using config", config)
35
+ const { deployport } = config;
36
+ if (!deployport) {
37
+ console.warn("missing required deployport configuration for signing", config);
38
+ return;
39
+ }
40
+ const { region, accessKeyID: keyID, secretAccessKey: secret } = deployport;
41
+ if (!region || !keyID || !secret) {
42
+ console.warn("missing required configuration for signing", deployport);
43
+ return;
44
+ }
45
+ const { ServiceName: signingService } = ServiceSignatureV1;
46
+ console.log("annotations", annotations, VendorCode, signingService);
47
+ const { longDate, shortDate } = formatDate(new Date());
48
+ const signingScope = buildSigningScope(VendorCode, region, signingService, shortDate);
49
+ console.log("signingScope", signingScope);
50
+
51
+ const credentialPart = buildCredentialPart(keyID, signingScope);
52
+ console.log("credentialPart", credentialPart);
53
+
54
+ const authHeaderPrefix = vendorAlgorithm(VendorCode) + " " + credentialPart
55
+
56
+ const bodyDigest = toHex(await hashSHA256(submission.request.body));
57
+ console.log("bodyDigest", bodyDigest);
58
+ const headers = submission.request.headers;
59
+ headers[vendorDateHeaderKey(VendorCode)] = longDate;
60
+ headers[vendorHeaderCanonicalNameKey(VendorCode, "Service")] = signingService;
61
+ headers[vendorHeaderCanonicalNameKey(VendorCode, "Resource")] = submission.operation.resource.packageUniqueName;
62
+ headers[vendorHeaderCanonicalNameKey(VendorCode, "Operation")] = submission.operation.name;
63
+ headers[vendorRegionHeaderKey(VendorCode)] = region;
64
+ headers[vendorHeaderCanonicalNameKey(VendorCode, "Content-Sha256")] = bodyDigest;
65
+ console.log("headers", headers);
66
+ const canonicalHeaders = getCanonicalHeaders(VendorCode, headers);
67
+
68
+ const credentials: Credentials = {
69
+ keyID,
70
+ secret,
71
+ }
72
+
73
+ const signingKey = getSigningKey(credentials, region, shortDate, signingService, VendorCode);
74
+
75
+ const { request } = submission;
76
+
77
+ // const payloadHash = await getPayloadHash(request.body);
78
+ const signature = await getSignature(
79
+ VendorCode,
80
+ longDate,
81
+ signingScope,
82
+ signingKey,
83
+ createCanonicalRequest(request, canonicalHeaders, bodyDigest)
84
+ );
85
+
86
+ headers["Authorization"] = [authHeaderPrefix, `SignedHeaders=${canonicalHeaders.signedHeaders}`, `Signature=${signature}`] as any;
87
+ console.log("signed headers", headers);
88
+ }
89
+
90
+ function createCanonicalRequest(request: Runtime.HTTPRequest, canonicalHeaders: CanonicalHeaders, payloadHash: string): string {
91
+ const sortedHeaders = canonicalHeaders.names;
92
+ return `${request.method}
93
+ ${getCanonicalPath(request)}
94
+ ${getCanonicalQuery(request)}
95
+ ${sortedHeaders.map((name) => `${name}:${canonicalHeaders.clean[name]}`).join("\n")}
96
+
97
+ ${canonicalHeaders.signedHeaders}
98
+ ${payloadHash}`;
99
+ }
100
+
101
+ function getCanonicalQuery({ path }: Runtime.HTTPRequest): string {
102
+ return ""; // TODO: hardcoded, we don't use query strings
103
+ }
104
+
105
+ function getCanonicalPath({ path }: Runtime.HTTPRequest): string {
106
+ console.log("getCanonicalPath path", path);
107
+ return path;
108
+ // if (this.uriEscapePath) {
109
+ // Non-S3 services, we normalize the path and then double URI encode it.
110
+ // Ref: "Remove Dot Segments" https://datatracker.ietf.org/doc/html/rfc3986#section-5.2.4
111
+ const normalizedPathSegments: string[] = [];
112
+ for (const pathSegment of path.split("/")) {
113
+ if (pathSegment?.length === 0) continue;
114
+ if (pathSegment === ".") continue;
115
+ if (pathSegment === "..") {
116
+ normalizedPathSegments.pop();
117
+ } else {
118
+ normalizedPathSegments.push(pathSegment);
119
+ }
120
+ // }
121
+ // Joining by single slashes to remove consecutive slashes.
122
+ const normalizedPath = `${path?.startsWith("/") ? "/" : ""}${normalizedPathSegments.join("/")}${normalizedPathSegments.length > 0 && path?.endsWith("/") ? "/" : ""
123
+ }`;
124
+
125
+ const doubleEncoded = encodeURIComponent(normalizedPath);
126
+ return doubleEncoded.replace(/%2F/g, "/");
127
+ }
128
+
129
+ // For S3, we shouldn't normalize the path. For example, object name
130
+ // my-object//example//photo.user should not be normalized to
131
+ // my-object/example/photo.user
132
+
133
+ console.log("getCanonicalPath path (curated)", path);
134
+ return path;
135
+ }
136
+
137
+
138
+ // /**
139
+ // * @private
140
+ // */
141
+ // export const getPayloadHash = async (
142
+ // body: string,
143
+ // ): Promise<string> => {
144
+ // const hashCtor = new Sha256();
145
+ // // new Sha256().update(toUint8Array(body));
146
+ // new Sha256().update(body);
147
+ // return toHex(await hashCtor.digest());
148
+ // };
149
+
150
+ async function getSignature(
151
+ vendor: string,
152
+ longDate: string,
153
+ credentialScope: string,
154
+ keyPromise: Promise<Uint8Array>,
155
+ canonicalRequest: string
156
+ ): Promise<string> {
157
+ const stringToSign = await createStringToSign(vendor, longDate, credentialScope, canonicalRequest);
158
+ console.log("stringToSign", stringToSign);
159
+
160
+ const hash = new Sha256(await keyPromise);
161
+ hash.update(stringToSign);
162
+ return toHex(await hash.digest());
163
+ }
164
+
165
+ async function createStringToSign(
166
+ vendor: string,
167
+ longDate: string,
168
+ credentialScope: string,
169
+ canonicalRequest: string
170
+ ): Promise<string> {
171
+ console.log('canonicalRequest', canonicalRequest);
172
+ const hash = new Sha256();
173
+ hash.update(canonicalRequest);
174
+ const hashedRequest = await hash.digest();
175
+
176
+ return `${vendorAlgorithm(vendor)}
177
+ ${longDate}
178
+ ${credentialScope}
179
+ ${toHex(hashedRequest)}`;
180
+ }
181
+
182
+ const toUtf8 = (input: Uint8Array): string => new TextDecoder("utf-8").decode(input);
183
+
184
+ // export const toUint8Array = (data: string | ArrayBuffer | ArrayBufferView): Uint8Array => {
185
+ // // if (typeof data === "string") {
186
+ // // return fromUtf8(data);
187
+ // // }
188
+
189
+ // // if (ArrayBuffer.isView(data)) {
190
+ // // return new Uint8Array(data.buffer, data.byteOffset, data.byteLength / Uint8Array.BYTES_PER_ELEMENT);
191
+ // // }
192
+
193
+ // // return new Uint8Array(data);
194
+ // return new Uint8Array(data);
195
+ // };
196
+
197
+ async function getSigningKey(
198
+ credentials: Credentials,
199
+ region: string,
200
+ shortDate: string,
201
+ service: string,
202
+ vendor: string,
203
+ ): Promise<Uint8Array> {
204
+ return getSigningKeyReal(credentials, shortDate, region, service, vendor);
205
+ }
206
+
207
+ function vendorKeyType(vendor: string): string {
208
+ return vendor + "1_request";
209
+ }
210
+
211
+ export const getSigningKeyReal = async (
212
+ credentials: Credentials,
213
+ shortDate: string,
214
+ region: string,
215
+ service: string,
216
+ vendor: string,
217
+ ): Promise<Uint8Array> => {
218
+ let key: SourceData = `${vendor}1${credentials.secret}`;
219
+ for (const signable of [shortDate, region, service, vendorKeyType(vendor)]) {
220
+ key = await hmac(key, signable);
221
+ }
222
+ return key as Uint8Array;
223
+ };
224
+
225
+ // type signingData = string | Uint8Array;
226
+
227
+ const hmac = (
228
+ secret: SourceData,
229
+ data: SourceData
230
+ ): Promise<Uint8Array> => {
231
+ const hash = new Sha256(secret);
232
+ hash.update(data);
233
+ return hash.digest();
234
+ };
235
+
236
+
237
+ type CanonicalHeaders = {
238
+ /**
239
+ * ordered list of header names
240
+ */
241
+ names: string[]
242
+ clean: Runtime.HTTPHeaders
243
+ signedHeaders: string
244
+ }
245
+
246
+ function getCanonicalHeaders(vendorCode: string, headers: Runtime.HTTPHeaders): CanonicalHeaders {
247
+ const validHeaderNames = [
248
+ "Host",
249
+ vendorDateHeaderKey(vendorCode),
250
+ vendorHeaderCanonicalNameKey(vendorCode, "Service"),
251
+ vendorHeaderCanonicalNameKey(vendorCode, "Resource"),
252
+ vendorHeaderCanonicalNameKey(vendorCode, "Operation"),
253
+ vendorHeaderCanonicalNameKey(vendorCode, "Content-Sha256"),
254
+ vendorHeaderCanonicalNameKey(vendorCode, "Region"),
255
+ ];
256
+ const cleaned: Runtime.HTTPHeaders = {};
257
+ for (const key in headers) {
258
+ if (!validHeaderNames.includes(key)) {
259
+ continue;
260
+ }
261
+ const val = headers[key];
262
+ if (Array.isArray(val)) {
263
+ cleaned[key] = val.map(v => v.trim());
264
+ } else {
265
+ cleaned[key] = val.trim();
266
+ }
267
+ }
268
+ const names = Object.keys(cleaned).sort();
269
+ return {
270
+ names,
271
+ signedHeaders: names.map(n => n.toLowerCase()).join(";"),
272
+ clean: cleaned,
273
+ };
274
+ }
275
+
276
+
277
+ const SHORT_TO_HEX: { [key: number]: string } = {};
278
+ for (let i = 0; i < 256; i++) {
279
+ let encodedByte = i.toString(16).toLowerCase();
280
+ if (encodedByte.length === 1) {
281
+ encodedByte = `0${encodedByte}`;
282
+ }
283
+
284
+ SHORT_TO_HEX[i] = encodedByte;
285
+ }
286
+ /**
287
+ * Converts a Uint8Array of binary data to a hexadecimal encoded string.
288
+ *
289
+ * @param bytes The binary data to encode
290
+ */
291
+ export function toHex(bytes: Uint8Array): string {
292
+ let out = "";
293
+ for (let i = 0; i < bytes.byteLength; i++) {
294
+ out += SHORT_TO_HEX[bytes[i]];
295
+ }
296
+
297
+ return out;
298
+ }
299
+
300
+ // convert hashSHA256 to ts
301
+ async function hashSHA256(dataStr: string): Promise<Uint8Array> {
302
+ const data = new TextEncoder().encode(dataStr);
303
+ const hash = new Sha256();
304
+ hash.update(data);
305
+ return await hash.digest();
306
+ }
307
+
308
+ const credentialPartPrefix = "Credential=";
309
+
310
+ function buildCredentialPart(keyID: string, signingScope: string): string {
311
+ return `${credentialPartPrefix}${keyID}/${signingScope}`;
312
+ }
313
+
314
+ function buildSigningScope(vendorCode: string, region: string, service: string, shortDate: string): string {
315
+ return [
316
+ shortDate,
317
+ region,
318
+ service,
319
+ vendorRequestCode(vendorCode),
320
+ ].join("/");
321
+ }
322
+
323
+ function vendorRequestCode(vendorCode: string): string {
324
+ return vendorCode + "1_request";
325
+ }
326
+
327
+ function vendorAlgorithm(vendorCode: string): string {
328
+ return vendorCode.toUpperCase() + "1-HMAC-SHA256";
329
+ }
330
+
331
+ // function formatShortTime(dt: Date): string {
332
+ // return dt.toISOString().replace(/[-:]/g, "").slice(0, 15);
333
+ // }
334
+
335
+ export const iso8601 = (time: Date): string =>
336
+ time.toISOString()
337
+ .replace(/\.\d{3}Z$/, "Z");
338
+
339
+ const formatDate = (now: Date): { longDate: string; shortDate: string } => {
340
+ const longDate = iso8601(now).replace(/[\-:]/g, "");
341
+ return {
342
+ longDate,
343
+ shortDate: longDate.slice(0, 8),
344
+ };
345
+ };
346
+
347
+ function vendorDateHeaderKey(vendorCode: string): string {
348
+ return vendorHeaderCanonicalNameKey(vendorCode, "Date");
349
+ }
350
+
351
+ function vendorRegionHeaderKey(vendorCode: string): string {
352
+ return vendorHeaderCanonicalNameKey(vendorCode, "Region");
353
+ }
354
+
355
+ function vendorHeaderCanonicalNameKey(vendorCode: string, header: string): string {
356
+ return httpHeaderStandardName("x-" + vendorCode + "-" + header.toLowerCase());
357
+ }
358
+
359
+ function httpHeaderStandardName(header: string): string {
360
+ return header.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("-");
361
+ }
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "@deployport/api-services-corelib",
3
+ "version": "0.1.0",
4
+ "description": "",
5
+ "main": "./specular.js",
6
+ "types": "./specular.d.ts",
7
+ "author": "bithavoc",
8
+ "scripts": {
9
+ "prepublishOnly": "npm run build-configurator",
10
+ "build-configurator": "tsc configurator/index.ts --declaration --module nodenext",
11
+ "clean": "rm -rf node_modules && rm -f **/*.js **/*.d.ts"
12
+ },
13
+ "type": "module",
14
+ "license": "MIT",
15
+ "dependencies": {
16
+ "@aws-crypto/sha256-js": "^5.2.0",
17
+ "@aws-sdk/types": "^3.515.0",
18
+ "@deployport/specular-runtime": "^0.1.5"
19
+ },
20
+ "devDependencies": {
21
+ "@tsconfig/node18": "^18.2.2",
22
+ "@types/node": "^20.8.3"
23
+ }
24
+ }
package/specular.d.ts ADDED
@@ -0,0 +1,23 @@
1
+ import { Runtime, Metadata } from '@deployport/specular-runtime';
2
+ export declare class SignedOperationV1 {
3
+ }
4
+ export declare class ServiceSignatureV1 {
5
+ ServiceName: string;
6
+ }
7
+ export declare const AccessDeniedProblemMeta: Metadata.Struct;
8
+ export interface AccessDeniedProblemProperties {
9
+ message: string;
10
+ }
11
+ export interface AccessDeniedProblem extends AccessDeniedProblemProperties, Runtime.StructInterface {
12
+ }
13
+ export declare class AccessDeniedProblem extends Error {
14
+ }
15
+ export declare const ForbiddenProblemMeta: Metadata.Struct;
16
+ export interface ForbiddenProblemProperties {
17
+ message: string;
18
+ }
19
+ export interface ForbiddenProblem extends ForbiddenProblemProperties, Runtime.StructInterface {
20
+ }
21
+ export declare class ForbiddenProblem extends Error {
22
+ }
23
+ export declare function SpecularPackage(): Metadata.Package;
package/specular.js ADDED
@@ -0,0 +1,36 @@
1
+ // JS gen package
2
+ import { Metadata, } from '@deployport/specular-runtime';
3
+ // SignedOperationV1 entity
4
+ // Marks an operation with digital signature authentication for id4ntity verification
5
+ export class SignedOperationV1 {
6
+ }
7
+ // ServiceSignatureV1 entity
8
+ export class ServiceSignatureV1 {
9
+ ServiceName;
10
+ }
11
+ const _pkg = new Metadata.Package('Deployport/CoreLib');
12
+ export const AccessDeniedProblemMeta = new Metadata.Struct(_pkg, "AccessDeniedProblem");
13
+ new Metadata.Property(AccessDeniedProblemMeta, "message", {
14
+ NonNullable: true,
15
+ SubType: "builtin",
16
+ Builtin: "string"
17
+ });
18
+ ;
19
+ ;
20
+ export class AccessDeniedProblem extends Error {
21
+ }
22
+ AccessDeniedProblemMeta.problemInstantiate = (msg) => new AccessDeniedProblem(msg);
23
+ export const ForbiddenProblemMeta = new Metadata.Struct(_pkg, "ForbiddenProblem");
24
+ new Metadata.Property(ForbiddenProblemMeta, "message", {
25
+ NonNullable: true,
26
+ SubType: "builtin",
27
+ Builtin: "string"
28
+ });
29
+ ;
30
+ ;
31
+ export class ForbiddenProblem extends Error {
32
+ }
33
+ ForbiddenProblemMeta.problemInstantiate = (msg) => new ForbiddenProblem(msg);
34
+ export function SpecularPackage() {
35
+ return _pkg;
36
+ }
package/specular.ts ADDED
@@ -0,0 +1,70 @@
1
+ // JS gen package
2
+ import {
3
+ Runtime,
4
+ Metadata,
5
+ } from '@deployport/specular-runtime';
6
+
7
+ // SignedOperationV1 entity
8
+ // Marks an operation with digital signature authentication for id4ntity verification
9
+ export class SignedOperationV1 {
10
+ }
11
+
12
+ // ServiceSignatureV1 entity
13
+ export class ServiceSignatureV1 {
14
+ public ServiceName: string;
15
+ }
16
+
17
+ const _pkg = new Metadata.Package(
18
+ 'Deployport/CoreLib',
19
+ );
20
+
21
+ export const AccessDeniedProblemMeta = new Metadata.Struct(
22
+ _pkg,
23
+ "AccessDeniedProblem",
24
+ );
25
+ new Metadata.Property(AccessDeniedProblemMeta, "message", {
26
+ NonNullable: true,
27
+ SubType: "builtin",
28
+ Builtin: "string"
29
+ });
30
+ // AccessDeniedProblem entity
31
+ export interface AccessDeniedProblemProperties
32
+ {
33
+ // /**
34
+ // * Returns "Deployport/CoreLib:AccessDeniedProblem"
35
+ // */
36
+ // fqtn: "Deployport/CoreLib:AccessDeniedProblem";
37
+ message : string;
38
+ };
39
+ export interface AccessDeniedProblem extends AccessDeniedProblemProperties, Runtime.StructInterface{};
40
+ export class AccessDeniedProblem
41
+ extends Error
42
+ {}
43
+ AccessDeniedProblemMeta.problemInstantiate = (msg: string) => new AccessDeniedProblem(msg);
44
+ export const ForbiddenProblemMeta = new Metadata.Struct(
45
+ _pkg,
46
+ "ForbiddenProblem",
47
+ );
48
+ new Metadata.Property(ForbiddenProblemMeta, "message", {
49
+ NonNullable: true,
50
+ SubType: "builtin",
51
+ Builtin: "string"
52
+ });
53
+ // ForbiddenProblem entity
54
+ export interface ForbiddenProblemProperties
55
+ {
56
+ // /**
57
+ // * Returns "Deployport/CoreLib:ForbiddenProblem"
58
+ // */
59
+ // fqtn: "Deployport/CoreLib:ForbiddenProblem";
60
+ message : string;
61
+ };
62
+ export interface ForbiddenProblem extends ForbiddenProblemProperties, Runtime.StructInterface{};
63
+ export class ForbiddenProblem
64
+ extends Error
65
+ {}
66
+ ForbiddenProblemMeta.problemInstantiate = (msg: string) => new ForbiddenProblem(msg);
67
+
68
+ export function SpecularPackage() {
69
+ return _pkg;
70
+ }
package/specular.yaml ADDED
@@ -0,0 +1,6 @@
1
+ packages:
2
+ - origin: "file://./../api-services-core-lib//package.specular"
3
+ generate:
4
+ - language: js
5
+ client:
6
+ output: .