@contember/engine-s3-plugin 2.0.0-alpha.9 → 2.0.0-beta.2

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.
Files changed (54) hide show
  1. package/dist/development/index.cjs +2 -5
  2. package/dist/development/index.cjs.map +1 -1
  3. package/dist/development/index.js +2 -5
  4. package/dist/development/index.js.map +1 -1
  5. package/dist/development/src/Config.cjs.map +1 -1
  6. package/dist/development/src/Config.js.map +1 -1
  7. package/dist/development/src/S3ConfigProcessor.cjs.map +1 -1
  8. package/dist/development/src/S3ConfigProcessor.js.map +1 -1
  9. package/dist/development/src/S3ObjectAuthorizator.cjs.map +1 -1
  10. package/dist/development/src/S3ObjectAuthorizator.js.map +1 -1
  11. package/dist/development/src/S3SchemaContributor.cjs +21 -11
  12. package/dist/development/src/S3SchemaContributor.cjs.map +1 -1
  13. package/dist/development/src/S3SchemaContributor.js +22 -12
  14. package/dist/development/src/S3SchemaContributor.js.map +1 -1
  15. package/dist/development/src/S3SchemaTypes.cjs.map +1 -1
  16. package/dist/development/src/S3SchemaTypes.js.map +1 -1
  17. package/dist/development/src/S3Service.cjs.map +1 -1
  18. package/dist/development/src/S3Service.js.map +1 -1
  19. package/dist/development/src/S3Signer.cjs.map +1 -1
  20. package/dist/development/src/S3Signer.js.map +1 -1
  21. package/dist/production/index.cjs +2 -5
  22. package/dist/production/index.cjs.map +1 -1
  23. package/dist/production/index.js +2 -5
  24. package/dist/production/index.js.map +1 -1
  25. package/dist/production/src/Config.cjs.map +1 -1
  26. package/dist/production/src/Config.js.map +1 -1
  27. package/dist/production/src/S3ConfigProcessor.cjs.map +1 -1
  28. package/dist/production/src/S3ConfigProcessor.js.map +1 -1
  29. package/dist/production/src/S3ObjectAuthorizator.cjs.map +1 -1
  30. package/dist/production/src/S3ObjectAuthorizator.js.map +1 -1
  31. package/dist/production/src/S3SchemaContributor.cjs +21 -11
  32. package/dist/production/src/S3SchemaContributor.cjs.map +1 -1
  33. package/dist/production/src/S3SchemaContributor.js +22 -12
  34. package/dist/production/src/S3SchemaContributor.js.map +1 -1
  35. package/dist/production/src/S3SchemaTypes.cjs.map +1 -1
  36. package/dist/production/src/S3SchemaTypes.js.map +1 -1
  37. package/dist/production/src/S3Service.cjs.map +1 -1
  38. package/dist/production/src/S3Service.js.map +1 -1
  39. package/dist/production/src/S3Signer.cjs.map +1 -1
  40. package/dist/production/src/S3Signer.js.map +1 -1
  41. package/dist/tests/tsconfig.tsbuildinfo +1 -1
  42. package/dist/types/S3ObjectAuthorizator.d.ts.map +1 -1
  43. package/dist/types/S3SchemaContributor.d.ts +2 -3
  44. package/dist/types/S3SchemaContributor.d.ts.map +1 -1
  45. package/dist/types/index.d.ts +1 -1
  46. package/dist/types/index.d.ts.map +1 -1
  47. package/dist/types/tsconfig.tsbuildinfo +1 -1
  48. package/package.json +11 -11
  49. package/src/S3ObjectAuthorizator.ts +2 -0
  50. package/src/S3SchemaContributor.ts +24 -10
  51. package/src/index.ts +2 -5
  52. package/tests/cases/authorizator.test.ts +8 -8
  53. package/tests/cases/signRequest.test.ts +6 -6
  54. package/tsconfig.settings.json +0 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contember/engine-s3-plugin",
3
- "version": "2.0.0-alpha.9",
3
+ "version": "2.0.0-beta.2",
4
4
  "license": "Apache-2.0",
5
5
  "main": "./dist/production/index.js",
6
6
  "typings": "./dist/types/index.d.ts",
@@ -23,23 +23,23 @@
23
23
  }
24
24
  },
25
25
  "dependencies": {
26
- "@contember/engine-plugins": "2.0.0-alpha.9",
27
- "@contember/graphql-utils": "2.0.0-alpha.9",
28
- "@contember/typesafe": "2.0.0-alpha.9",
26
+ "@contember/engine-plugins": "2.0.0-beta.2",
27
+ "@contember/graphql-utils": "2.0.0-beta.2",
28
+ "@contember/typesafe": "2.0.0-beta.2",
29
29
  "mime-types": "^2.1.35",
30
- "picomatch": "^3.0.1",
31
- "qs": "^6.11.2",
32
- "uuid": "^9.0.1"
30
+ "picomatch": "^4.0.2",
31
+ "qs": "^6.13.0",
32
+ "uuid": "^10.0.0"
33
33
  },
34
34
  "devDependencies": {
35
35
  "@types/mime-types": "^2.1.4",
36
- "@types/qs": "^6.9.10",
37
- "@types/uuid": "^9.0.7",
38
- "graphql": "^16.8.1"
36
+ "@types/qs": "^6.9.16",
37
+ "@types/uuid": "^10.0.0",
38
+ "graphql": "^16.9.0"
39
39
  },
40
40
  "peerDependencies": {
41
41
  "graphql": ">= 14.6.0",
42
42
  "pg": "^8.9.0"
43
43
  },
44
44
  "type": "module"
45
- }
45
+ }
@@ -1,3 +1,5 @@
1
+ // eslint-disable-next-line @typescript-eslint/triple-slash-reference
2
+ /// <reference path="../types/picomatch/index.d.ts" />
1
3
  import pm from 'picomatch'
2
4
  import { ForbiddenError } from '@contember/graphql-utils'
3
5
 
@@ -1,4 +1,5 @@
1
1
  import {
2
+ GraphQLError,
2
3
  GraphQLFieldConfig,
3
4
  GraphQLInt,
4
5
  GraphQLNonNull,
@@ -8,7 +9,7 @@ import {
8
9
  GraphQLString,
9
10
  } from 'graphql'
10
11
  import { S3Service, S3ServiceFactory } from './S3Service'
11
- import { resolveS3Config, S3Config } from './Config'
12
+ import { Project3Config, resolveS3Config, S3Config } from './Config'
12
13
  import { GraphQLSchemaContributor, GraphQLSchemaContributorContext, Providers } from '@contember/engine-plugins'
13
14
  import * as types from './S3SchemaTypes'
14
15
  import { S3Acl, S3GenerateSignedUploadInput, S3SignedRead, S3SignedUpload } from './S3SchemaTypes'
@@ -35,13 +36,16 @@ export type S3SchemaAcl = Record<
35
36
 
36
37
  export class S3SchemaContributor implements GraphQLSchemaContributor {
37
38
  constructor(
38
- private readonly s3Config: S3Config | undefined,
39
39
  private readonly s3Factory: S3ServiceFactory,
40
40
  private readonly providers: Providers,
41
41
  ) {}
42
42
 
43
+ getCacheKey?({ project }: GraphQLSchemaContributorContext): string {
44
+ return project.s3 ? 'yes' : 'no'
45
+ }
46
+
43
47
  createSchema(context: GraphQLSchemaContributorContext): GraphQLSchemaConfig | undefined {
44
- if (!this.s3Config) {
48
+ if (!context.project.s3) {
45
49
  return undefined
46
50
  }
47
51
  const rules = context.identity.projectRoles.flatMap(it =>
@@ -61,11 +65,9 @@ export class S3SchemaContributor implements GraphQLSchemaContributor {
61
65
  return undefined
62
66
  }
63
67
 
64
- const s3Config = resolveS3Config(this.s3Config)
65
68
  const authorizator = new S3ObjectAuthorizator(uploadRules, readRules)
66
- const s3 = this.s3Factory.create(s3Config, this.providers, authorizator)
67
- const uploadMutation = this.createUploadMutation(s3)
68
- const readMutation = this.createReadMutation(s3)
69
+ const uploadMutation = this.createUploadMutation(authorizator)
70
+ const readMutation = this.createReadMutation(authorizator)
69
71
  const mutation = {
70
72
  generateUploadUrl: uploadMutation as GraphQLFieldConfig<any, any, any>,
71
73
  generateReadUrl: readMutation,
@@ -86,7 +88,8 @@ export class S3SchemaContributor implements GraphQLSchemaContributor {
86
88
  }
87
89
  }
88
90
 
89
- private createReadMutation(s3: S3Service): GraphQLFieldConfig<any, any, any> {
91
+ private createReadMutation(authorizator: S3ObjectAuthorizator): GraphQLFieldConfig<any, { project: Project3Config }, any> {
92
+
90
93
  return {
91
94
  type: new GraphQLNonNull(S3SignedRead),
92
95
  args: {
@@ -97,13 +100,18 @@ export class S3SchemaContributor implements GraphQLSchemaContributor {
97
100
  type: GraphQLInt,
98
101
  },
99
102
  },
100
- resolve: async (parent: any, args: { objectKey: string; expiration?: number }) => {
103
+ resolve: async (parent: any, args: { objectKey: string; expiration?: number }, ctx: { project: Project3Config }) => {
104
+ if (!ctx.project.s3) {
105
+ throw new GraphQLError('S3 is not configured for this project')
106
+ }
107
+ const s3Config = resolveS3Config(ctx.project.s3)
108
+ const s3 = this.s3Factory.create(s3Config, this.providers, authorizator)
101
109
  return s3.getSignedReadUrl({ objectKey: args.objectKey, expiration: args.expiration ?? null })
102
110
  },
103
111
  } as GraphQLFieldConfig<any, any, any>
104
112
  }
105
113
 
106
- private createUploadMutation(s3: S3Service): GraphQLFieldConfig<any, any, any> {
114
+ private createUploadMutation(authorizator: S3ObjectAuthorizator): GraphQLFieldConfig<any, any, any> {
107
115
  return {
108
116
  type: new GraphQLNonNull(S3SignedUpload),
109
117
  args: {
@@ -116,7 +124,13 @@ export class S3SchemaContributor implements GraphQLSchemaContributor {
116
124
  resolve: async (
117
125
  parent: any,
118
126
  args: { input?: Partial<S3GenerateSignedUploadInput>; contentType?: string; acl?: S3Acl; expiration?: number; prefix?: string },
127
+ ctx: { project: Project3Config },
119
128
  ) => {
129
+ if (!ctx.project.s3) {
130
+ throw new GraphQLError('S3 is not configured for this project')
131
+ }
132
+ const s3Config = resolveS3Config(ctx.project.s3)
133
+ const s3 = this.s3Factory.create(s3Config, this.providers, authorizator)
120
134
  return s3.getSignedUploadUrl({
121
135
  acl: args.input?.acl ?? args.acl ?? null,
122
136
  contentDisposition: args.input?.contentDisposition ?? null,
package/src/index.ts CHANGED
@@ -15,11 +15,8 @@ export default class S3 implements Plugin<Project3Config> {
15
15
  return new S3ConfigProcessor()
16
16
  }
17
17
 
18
- getSchemaContributor({ project, providers }: SchemaContributorArgs<Project3Config>) {
19
- if (!project.s3) {
20
- return undefined
21
- }
18
+ getSchemaContributor({ providers }: SchemaContributorArgs) {
22
19
  const s3ServiceFactory = new S3ServiceFactory()
23
- return new S3SchemaContributor(project.s3, s3ServiceFactory, providers)
20
+ return new S3SchemaContributor(s3ServiceFactory, providers)
24
21
  }
25
22
  }
@@ -1,22 +1,22 @@
1
- import { test, assert } from 'vitest'
1
+ import { test, expect } from 'bun:test'
2
2
  import { S3ObjectAuthorizator } from '../../src'
3
3
 
4
4
  test('required read pattern', () => {
5
5
  const authorizator = new S3ObjectAuthorizator([], [{ pattern: 'foo/**' }])
6
- assert.doesNotThrow(() => authorizator.verifyReadAccess({ key: 'foo/lorem.jpg' }))
7
- assert.throw(() => authorizator.verifyReadAccess({ key: 'lorem.jpg' }), 'Read access forbidden for object key lorem.jpg')
6
+ expect(() => authorizator.verifyReadAccess({ key: 'foo/lorem.jpg' })).not.toThrow()
7
+ expect(() => authorizator.verifyReadAccess({ key: 'lorem.jpg' })).toThrow('Read access forbidden for object key lorem.jpg')
8
8
  })
9
9
 
10
10
  test('required upload pattern', () => {
11
11
  const authorizator = new S3ObjectAuthorizator([{ pattern: 'foo/**' }], [])
12
- assert.doesNotThrow(() => authorizator.verifyUploadAccess({ key: 'foo/lorem.jpg', size: null }))
13
- assert.throw(() => authorizator.verifyUploadAccess({ key: 'lorem.jpg', size: null }), 'Upload access forbidden for object key lorem.jpg')
12
+ expect(() => authorizator.verifyUploadAccess({ key: 'foo/lorem.jpg', size: null })).not.toThrow()
13
+ expect(() => authorizator.verifyUploadAccess({ key: 'lorem.jpg', size: null })).toThrow('Upload access forbidden for object key lorem.jpg')
14
14
  })
15
15
 
16
16
 
17
17
  test('max upload size', () => {
18
18
  const authorizator = new S3ObjectAuthorizator([{ pattern: '**', maxSize: 1024 }], [])
19
- assert.doesNotThrow(() => authorizator.verifyUploadAccess({ key: 'lorem.jpg', size: 1000 }))
20
- assert.throw(() => authorizator.verifyUploadAccess({ key: 'lorem.jpg', size: null }), 'File size must be provided')
21
- assert.throw(() => authorizator.verifyUploadAccess({ key: 'lorem.jpg', size: 2048 }), 'Uploaded file is too large')
19
+ expect(() => authorizator.verifyUploadAccess({ key: 'lorem.jpg', size: 1000 })).not.toThrow()
20
+ expect(() => authorizator.verifyUploadAccess({ key: 'lorem.jpg', size: null })).toThrow('File size must be provided')
21
+ expect(() => authorizator.verifyUploadAccess({ key: 'lorem.jpg', size: 2048 })).toThrow('Uploaded file is too large')
22
22
  })
@@ -1,4 +1,4 @@
1
- import { test, assert, expect } from 'vitest'
1
+ import { test, expect } from 'bun:test'
2
2
  import { S3ObjectAuthorizator, S3Service } from '../../src'
3
3
 
4
4
  const mocked = new Date('2021-07-02T17:22Z')
@@ -24,31 +24,31 @@ const createS3Service = (bucket: string, prefix = '') => new S3Service(
24
24
  test('sign s3 request', () => {
25
25
  const service = createS3Service('test')
26
26
  const signed = service.getSignedReadUrl({ objectKey: 'foo.jpg', expiration: null })
27
- expect(signed.url).toMatchInlineSnapshot('"https://test.s3.eu-central-1.amazonaws.com/foo.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=test%2F20210702%2Feu-central-1%2Fs3%2Faws4_request&X-Amz-Date=20210702T172200Z&X-Amz-Expires=3600&X-Amz-Signature=00e912fe0383066802fc6c0f93ffb223320ca143992385fe69070bbb87c758dc&X-Amz-SignedHeaders=host"')
27
+ expect(signed.url).toEqual('https://test.s3.eu-central-1.amazonaws.com/foo.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=test%2F20210702%2Feu-central-1%2Fs3%2Faws4_request&X-Amz-Date=20210702T172200Z&X-Amz-Expires=3600&X-Amz-Signature=00e912fe0383066802fc6c0f93ffb223320ca143992385fe69070bbb87c758dc&X-Amz-SignedHeaders=host')
28
28
  })
29
29
 
30
30
  test('sign s3 request - prefix', () => {
31
31
  const service = createS3Service('test', 'lorem')
32
32
  const signed = service.getSignedReadUrl({ objectKey: 'lorem/foo.jpg', expiration: null })
33
- expect(signed.url).toMatchInlineSnapshot('"https://test.s3.eu-central-1.amazonaws.com/lorem/foo.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=test%2F20210702%2Feu-central-1%2Fs3%2Faws4_request&X-Amz-Date=20210702T172200Z&X-Amz-Expires=3600&X-Amz-Signature=f6c69dc306422c2b3242c9d9d42147336817720ffae249af8bd91cc7062e9f6d&X-Amz-SignedHeaders=host"')
33
+ expect(signed.url).toEqual('https://test.s3.eu-central-1.amazonaws.com/lorem/foo.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=test%2F20210702%2Feu-central-1%2Fs3%2Faws4_request&X-Amz-Date=20210702T172200Z&X-Amz-Expires=3600&X-Amz-Signature=f6c69dc306422c2b3242c9d9d42147336817720ffae249af8bd91cc7062e9f6d&X-Amz-SignedHeaders=host')
34
34
  })
35
35
 
36
36
  test('sign upload', () => {
37
37
  const service = createS3Service('test')
38
38
  const signed = service.getSignedUploadUrl({ contentType: 'image/jpeg', acl: 'PUBLIC_READ', expiration: 1800, contentDisposition: null, extension: null, fileName: null, prefix: null, suffix: null, size: null })
39
- expect(signed.url).toMatchInlineSnapshot('"https://test.s3.eu-central-1.amazonaws.com/9fce3907-ff2b-45bb-b4ce-eff5527dd315.jpeg?Cache-Control=immutable&Content-Type=image%2Fjpeg&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=test%2F20210702%2Feu-central-1%2Fs3%2Faws4_request&X-Amz-Date=20210702T172200Z&X-Amz-Expires=1800&X-Amz-Signature=fcd00b967054c19a2ee64b59e31d61e032ea086dcf2f0d8c36a883971c469c99&X-Amz-SignedHeaders=cache-control%3Bhost%3Bx-amz-acl&x-amz-acl=public-read"')
39
+ expect(signed.url).toEqual('https://test.s3.eu-central-1.amazonaws.com/9fce3907-ff2b-45bb-b4ce-eff5527dd315.jpeg?Cache-Control=immutable&Content-Type=image%2Fjpeg&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=test%2F20210702%2Feu-central-1%2Fs3%2Faws4_request&X-Amz-Date=20210702T172200Z&X-Amz-Expires=1800&X-Amz-Signature=fcd00b967054c19a2ee64b59e31d61e032ea086dcf2f0d8c36a883971c469c99&X-Amz-SignedHeaders=cache-control%3Bhost%3Bx-amz-acl&x-amz-acl=public-read')
40
40
  })
41
41
 
42
42
  test('sign upload #2', () => {
43
43
  const service = createS3Service('test')
44
44
  const signed = service.getSignedUploadUrl({ contentType: 'image/jpeg', acl: 'PUBLIC_READ', expiration: 1800, contentDisposition: 'INLINE', extension: 'jpeg', fileName: 'foo-bar.jpeg', prefix: 'foo', suffix: 'bar', size: null })
45
- expect(signed.url).toMatchInlineSnapshot('"https://test.s3.eu-central-1.amazonaws.com/foo/9fce3907-ff2b-45bb-b4ce-eff5527dd315bar.jpeg?Cache-Control=immutable&Content-Type=image%2Fjpeg&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=test%2F20210702%2Feu-central-1%2Fs3%2Faws4_request&X-Amz-Date=20210702T172200Z&X-Amz-Expires=1800&X-Amz-Signature=979a93bca65a98ce6b2bf97d2bf2afe7d5f2fa805f7d596187675416c86a3bd5&X-Amz-SignedHeaders=cache-control%3Bcontent-disposition%3Bhost%3Bx-amz-acl&x-amz-acl=public-read"')
45
+ expect(signed.url).toEqual('https://test.s3.eu-central-1.amazonaws.com/foo/9fce3907-ff2b-45bb-b4ce-eff5527dd315bar.jpeg?Cache-Control=immutable&Content-Type=image%2Fjpeg&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=test%2F20210702%2Feu-central-1%2Fs3%2Faws4_request&X-Amz-Date=20210702T172200Z&X-Amz-Expires=1800&X-Amz-Signature=979a93bca65a98ce6b2bf97d2bf2afe7d5f2fa805f7d596187675416c86a3bd5&X-Amz-SignedHeaders=cache-control%3Bcontent-disposition%3Bhost%3Bx-amz-acl&x-amz-acl=public-read')
46
46
  })
47
47
 
48
48
 
49
49
  test('bucket with dot', () => {
50
50
  const service = createS3Service('test.foo')
51
51
  const signed = service.getSignedReadUrl({ objectKey: 'foo.jpg', expiration: null })
52
- expect(signed.url).toMatchInlineSnapshot('"https://s3.eu-central-1.amazonaws.com/test.foo/foo.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=test%2F20210702%2Feu-central-1%2Fs3%2Faws4_request&X-Amz-Date=20210702T172200Z&X-Amz-Expires=3600&X-Amz-Signature=811a809196e60413e0e54e51d3dd91ab73ad1482b61e5371864d25d43ba642ff&X-Amz-SignedHeaders=host"')
52
+ expect(signed.url).toEqual('https://s3.eu-central-1.amazonaws.com/test.foo/foo.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=test%2F20210702%2Feu-central-1%2Fs3%2Faws4_request&X-Amz-Date=20210702T172200Z&X-Amz-Expires=3600&X-Amz-Signature=811a809196e60413e0e54e51d3dd91ab73ad1482b61e5371864d25d43ba642ff&X-Amz-SignedHeaders=host')
53
53
  })
54
54
 
@@ -1,9 +1,5 @@
1
1
  {
2
2
  "compilerOptions": {
3
- "typeRoots": [
4
- "./types",
5
- "../../node_modules/@types"
6
- ]
7
3
  },
8
4
  "extends": "../../tsconfig.settings.json"
9
5
  }