Skip to content

Commit a505bcf

Browse files
authored
Revert "feat: read legacy s3 key from s3 for artifact API requests [3/4]" (#1120)
1 parent 8c8eb18 commit a505bcf

File tree

17 files changed

+192
-418
lines changed

17 files changed

+192
-418
lines changed

integration-tests/testkit/flow.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { gql } from '@app/gql';
22
import { fetch } from '@whatwg-node/fetch';
3+
import { OperationBodyByHashInput } from './../../packages/web/app/src/graphql/index';
34
import type {
45
AnswerOrganizationTransferRequestInput,
56
CreateOrganizationInput,
@@ -9,7 +10,6 @@ import type {
910
DeleteTokensInput,
1011
EnableExternalSchemaCompositionInput,
1112
InviteToOrganizationByEmailInput,
12-
OperationBodyByHashInput,
1313
OperationsStatsSelectorInput,
1414
OrganizationMemberAccessInput,
1515
OrganizationSelectorInput,

integration-tests/tests/api/artifacts-cdn.spec.ts

+11-63
Original file line numberDiff line numberDiff line change
@@ -24,33 +24,29 @@ const s3Client = new S3Client({
2424
forcePathStyle: true,
2525
});
2626

27-
async function deleteS3Object(s3Client: S3Client, bucketName: string, keysToDelete: Array<string>) {
28-
if (keysToDelete.length) {
29-
const deleteObjectsCommand = new DeleteObjectsCommand({
30-
Bucket: bucketName,
31-
Delete: { Objects: keysToDelete.map(key => ({ Key: key })) },
32-
});
33-
34-
await s3Client.send(deleteObjectsCommand);
35-
}
36-
}
37-
3827
async function deleteAllS3BucketObjects(s3Client: S3Client, bucketName: string) {
3928
const listObjectsCommand = new ListObjectsCommand({
4029
Bucket: bucketName,
4130
});
4231
const result = await s3Client.send(listObjectsCommand);
43-
const keysToDelete: Array<string> = [];
32+
const keysToDelete: Array<{ Key: string }> = [];
4433

4534
if (result.Contents) {
4635
for (const item of result.Contents) {
4736
if (item.Key) {
48-
keysToDelete.push(item.Key);
37+
keysToDelete.push({ Key: item.Key });
4938
}
5039
}
5140
}
5241

53-
await deleteS3Object(s3Client, bucketName, keysToDelete);
42+
if (keysToDelete.length) {
43+
const deleteObjectsCommand = new DeleteObjectsCommand({
44+
Bucket: bucketName,
45+
Delete: { Objects: keysToDelete },
46+
});
47+
48+
await s3Client.send(deleteObjectsCommand);
49+
}
5450
}
5551

5652
async function fetchS3ObjectArtifact(
@@ -159,55 +155,6 @@ function runArtifactsCDNTests(
159155
expect(isMatch).toEqual(true);
160156
});
161157

162-
test.concurrent(
163-
'deleting (legacy) cdn access token from s3 revokes artifact cdn access',
164-
async () => {
165-
const { createOrg } = await initSeed().createOwner();
166-
const { createProject } = await createOrg();
167-
const { createToken, target } = await createProject(ProjectType.Single);
168-
const writeToken = await createToken({
169-
targetScopes: [TargetAccessScope.RegistryRead, TargetAccessScope.RegistryWrite],
170-
});
171-
172-
const publishSchemaResult = await writeToken
173-
.publishSchema({
174-
author: 'Kamil',
175-
commit: 'abc123',
176-
sdl: `type Query { ping: String }`,
177-
})
178-
.then(r => r.expectNoGraphQLErrors());
179-
180-
const cdnAccess = await writeToken.createCdnAccess();
181-
const endpointBaseUrl = await getBaseEndpoint();
182-
183-
// First roundtrip
184-
const url = buildEndpointUrl(endpointBaseUrl, target!.id, 'sdl');
185-
let response = await fetch(url, {
186-
method: 'GET',
187-
headers: {
188-
'x-hive-cdn-key': cdnAccess.token,
189-
},
190-
});
191-
expect(response.status).toEqual(200);
192-
expect(await response.text()).toMatchInlineSnapshot(`
193-
"type Query {
194-
ping: String
195-
}"
196-
`);
197-
198-
await deleteS3Object(s3Client, 'artifacts', [`cdn-legacy-keys/${target.id}`]);
199-
200-
// Second roundtrip
201-
response = await fetch(url, {
202-
method: 'GET',
203-
headers: {
204-
'x-hive-cdn-key': cdnAccess.token,
205-
},
206-
});
207-
expect(response.status).toEqual(403);
208-
},
209-
);
210-
211158
test.concurrent('access SDL artifact with valid credentials', async () => {
212159
const { createOrg } = await initSeed().createOwner();
213160
const { createProject } = await createOrg();
@@ -419,6 +366,7 @@ function runArtifactsCDNTests(
419366

420367
expect(response.status).toEqual(200);
421368
const result = await response.json();
369+
console.log(result);
422370
expect(result.data.__schema.types).toContainEqual({
423371
name: 'Query',
424372
fields: [{ name: 'ping' }],

packages/services/api/package.json

-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@
4242
},
4343
"devDependencies": {
4444
"@graphql-hive/core": "0.2.3",
45-
"@hive/cdn-script": "workspace:*",
4645
"@hive/emails": "workspace:*",
4746
"@hive/schema": "workspace:*",
4847
"@hive/storage": "workspace:*",

packages/services/api/src/create.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { createApplication, Scope } from 'graphql-modules';
2-
import { AwsClient } from '@hive/cdn-script/aws';
32
import { activityModule } from './modules/activity';
43
import { adminModule } from './modules/admin';
54
import { alertsModule } from './modules/alerts';
@@ -55,6 +54,7 @@ import {
5554
USAGE_ESTIMATION_SERVICE_CONFIG,
5655
UsageEstimationServiceConfig,
5756
} from './modules/usage-estimation/providers/tokens';
57+
import { AwsClient } from './shared/aws';
5858

5959
const modules = [
6060
sharedModule,

packages/services/cdn-worker/src/artifact-storage-reader.ts renamed to packages/services/api/src/modules/schema/providers/artifact-storage-reader.ts

+18-8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* IMPORTANT NOTE: This file needs to be kept platform-agnostic, don't use any Node.js specific APIs.
33
*/
4-
import { AwsClient } from './aws';
4+
import { AwsClient } from '../../../shared/aws';
55

66
const presignedUrlExpirationSeconds = 60;
77

@@ -17,22 +17,32 @@ export type ArtifactsType = SDLArtifactTypes | 'metadata' | 'services' | 'superg
1717
*/
1818
export class ArtifactStorageReader {
1919
private publicUrl: URL | null;
20+
private awsClient: AwsClient;
21+
private s3Endpoint: string;
2022

2123
constructor(
22-
private s3: {
23-
client: AwsClient;
24+
s3Config: {
25+
accessKeyId: string;
26+
secretAccessKey: string;
2427
endpoint: string;
25-
bucketName: string;
2628
},
29+
private bucketName: string,
2730
/** The public URL in case the public S3 endpoint differs from the internal S3 endpoint. E.g. within a docker network. */
2831
publicUrl: string | null,
2932
) {
3033
this.publicUrl = publicUrl ? new URL(publicUrl) : null;
34+
this.awsClient = new AwsClient({
35+
accessKeyId: s3Config.accessKeyId,
36+
secretAccessKey: s3Config.secretAccessKey,
37+
region: 'auto',
38+
service: 's3',
39+
});
40+
this.s3Endpoint = s3Config.endpoint;
3141
}
3242

3343
private async generatePresignedGetUrl(key: string): Promise<string> {
34-
const signedUrl = await this.s3.client.sign(
35-
[this.s3.endpoint, this.s3.bucketName, key].join('/'),
44+
const signedUrl = await this.awsClient.sign(
45+
this.s3Endpoint + `/` + this.bucketName + '/' + key,
3646
{
3747
method: 'GET',
3848
aws: { signQuery: true },
@@ -66,8 +76,8 @@ export class ArtifactStorageReader {
6676

6777
const key = buildArtifactStorageKey(targetId, artifactType);
6878

69-
const response = await this.s3.client.fetch(
70-
[this.s3.endpoint, this.s3.bucketName, key].join('/'),
79+
const response = await this.awsClient.fetch(
80+
this.s3Endpoint + '/' + this.bucketName + '/' + key,
7181
{
7282
method: 'HEAD',
7383
},

packages/services/api/src/modules/schema/providers/artifact-storage-writer.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Inject } from 'graphql-modules';
2-
import { buildArtifactStorageKey } from '@hive/cdn-script/artifact-storage-reader';
32
import { S3_CONFIG, type S3Config } from '../../shared/providers/s3-config';
3+
import { buildArtifactStorageKey } from './artifact-storage-reader';
44

55
const artifactMeta = {
66
sdl: {

packages/services/api/src/modules/shared/providers/s3-config.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { InjectionToken } from 'graphql-modules';
2-
import type { AwsClient } from '@hive/cdn-script/aws';
2+
import type { AwsClient } from '../../../shared/aws';
33

44
export interface S3Config {
55
client: AwsClient;

packages/services/cdn-worker/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@
1212
"graphql": "^16.0.0"
1313
},
1414
"dependencies": {
15-
"bcryptjs": "2.4.3",
1615
"graphql": "16.6.0",
1716
"toucan-js": "2.7.0",
1817
"zod": "3.20.2"
1918
},
2019
"devDependencies": {
2120
"@cloudflare/workers-types": "4.20221111.1",
21+
"@hive/api": "workspace:*",
2222
"@types/service-worker-mock": "2.0.1",
2323
"@whatwg-node/fetch": "0.6.2",
2424
"@whatwg-node/server": "0.5.7",

packages/services/cdn-worker/src/artifact-handler.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import itty from 'itty-router';
22
import zod from 'zod';
3+
import type { ArtifactsType } from '@hive/api/src/modules/schema/providers/artifact-storage-reader';
34
import { createFetch, type Request } from '@whatwg-node/fetch';
4-
import { type Analytics, createAnalytics } from './analytics';
5-
import { type ArtifactsType } from './artifact-storage-reader';
5+
import type { Analytics } from './analytics';
6+
import { createAnalytics } from './analytics';
67
import { InvalidAuthKeyResponse, MissingAuthKeyResponse } from './errors';
78
import type { KeyValidator } from './key-validation';
89

packages/services/cdn-worker/src/dev.ts

+23-21
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import { createServer } from 'http';
22
import itty from 'itty-router';
33
import { json, withParams } from 'itty-router-extras';
4+
import { ArtifactStorageReader } from '@hive/api/src/modules/schema/providers/artifact-storage-reader';
45
import { createServerAdapter } from '@whatwg-node/server';
56
import { createArtifactRequestHandler } from './artifact-handler';
6-
import { ArtifactStorageReader } from './artifact-storage-reader';
7-
import { AwsClient } from './aws';
87
import './dev-polyfill';
98
import { devStorage } from './dev-polyfill';
109
import { createRequestHandler } from './handler';
@@ -13,38 +12,41 @@ import { createIsKeyValid } from './key-validation';
1312
// eslint-disable-next-line no-process-env
1413
const PORT = process.env.PORT ? parseInt(process.env.PORT, 10) : 4010;
1514

16-
declare let S3_ENDPOINT: string;
17-
declare let S3_ACCESS_KEY_ID: string;
18-
declare let S3_SECRET_ACCESS_KEY: string;
19-
declare let S3_BUCKET_NAME: string;
20-
declare let S3_PUBLIC_URL: string;
21-
22-
const s3 = {
23-
client: new AwsClient({
24-
accessKeyId: S3_ACCESS_KEY_ID,
25-
secretAccessKey: S3_SECRET_ACCESS_KEY,
26-
service: 's3',
27-
}),
28-
bucketName: S3_BUCKET_NAME,
29-
endpoint: S3_ENDPOINT,
30-
};
31-
3215
/**
3316
* KV Storage for the CDN
3417
*/
3518
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
3619
// @ts-ignore
3720
declare let HIVE_DATA: KVNamespace;
3821

22+
/**
23+
* Secret used to sign the CDN keys
24+
*/
25+
declare let KEY_DATA: string;
26+
3927
const handleRequest = createRequestHandler({
4028
getRawStoreValue: value => HIVE_DATA.get(value),
41-
isKeyValid: createIsKeyValid({ s3, waitUntil: null, getCache: () => null }),
29+
isKeyValid: createIsKeyValid({ keyData: KEY_DATA }),
4230
});
4331

44-
const artifactStorageReader = new ArtifactStorageReader(s3, S3_PUBLIC_URL);
32+
declare let S3_ENDPOINT: string;
33+
declare let S3_ACCESS_KEY_ID: string;
34+
declare let S3_SECRET_ACCESS_KEY: string;
35+
declare let S3_BUCKET_NAME: string;
36+
declare let S3_PUBLIC_URL: string;
37+
38+
const artifactStorageReader = new ArtifactStorageReader(
39+
{
40+
accessKeyId: S3_ACCESS_KEY_ID,
41+
secretAccessKey: S3_SECRET_ACCESS_KEY,
42+
endpoint: S3_ENDPOINT,
43+
},
44+
S3_BUCKET_NAME,
45+
S3_PUBLIC_URL,
46+
);
4547

4648
const handleArtifactRequest = createArtifactRequestHandler({
47-
isKeyValid: createIsKeyValid({ s3, waitUntil: null, getCache: () => null }),
49+
isKeyValid: createIsKeyValid({ keyData: KEY_DATA }),
4850
async getArtifactAction(targetId, artifactType, eTag) {
4951
return artifactStorageReader.generateArtifactReadUrl(targetId, artifactType, eTag);
5052
},

0 commit comments

Comments
 (0)