@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.
- package/dist/development/index.cjs +2 -5
- package/dist/development/index.cjs.map +1 -1
- package/dist/development/index.js +2 -5
- package/dist/development/index.js.map +1 -1
- package/dist/development/src/Config.cjs.map +1 -1
- package/dist/development/src/Config.js.map +1 -1
- package/dist/development/src/S3ConfigProcessor.cjs.map +1 -1
- package/dist/development/src/S3ConfigProcessor.js.map +1 -1
- package/dist/development/src/S3ObjectAuthorizator.cjs.map +1 -1
- package/dist/development/src/S3ObjectAuthorizator.js.map +1 -1
- package/dist/development/src/S3SchemaContributor.cjs +21 -11
- package/dist/development/src/S3SchemaContributor.cjs.map +1 -1
- package/dist/development/src/S3SchemaContributor.js +22 -12
- package/dist/development/src/S3SchemaContributor.js.map +1 -1
- package/dist/development/src/S3SchemaTypes.cjs.map +1 -1
- package/dist/development/src/S3SchemaTypes.js.map +1 -1
- package/dist/development/src/S3Service.cjs.map +1 -1
- package/dist/development/src/S3Service.js.map +1 -1
- package/dist/development/src/S3Signer.cjs.map +1 -1
- package/dist/development/src/S3Signer.js.map +1 -1
- package/dist/production/index.cjs +2 -5
- package/dist/production/index.cjs.map +1 -1
- package/dist/production/index.js +2 -5
- package/dist/production/index.js.map +1 -1
- package/dist/production/src/Config.cjs.map +1 -1
- package/dist/production/src/Config.js.map +1 -1
- package/dist/production/src/S3ConfigProcessor.cjs.map +1 -1
- package/dist/production/src/S3ConfigProcessor.js.map +1 -1
- package/dist/production/src/S3ObjectAuthorizator.cjs.map +1 -1
- package/dist/production/src/S3ObjectAuthorizator.js.map +1 -1
- package/dist/production/src/S3SchemaContributor.cjs +21 -11
- package/dist/production/src/S3SchemaContributor.cjs.map +1 -1
- package/dist/production/src/S3SchemaContributor.js +22 -12
- package/dist/production/src/S3SchemaContributor.js.map +1 -1
- package/dist/production/src/S3SchemaTypes.cjs.map +1 -1
- package/dist/production/src/S3SchemaTypes.js.map +1 -1
- package/dist/production/src/S3Service.cjs.map +1 -1
- package/dist/production/src/S3Service.js.map +1 -1
- package/dist/production/src/S3Signer.cjs.map +1 -1
- package/dist/production/src/S3Signer.js.map +1 -1
- package/dist/tests/tsconfig.tsbuildinfo +1 -1
- package/dist/types/S3ObjectAuthorizator.d.ts.map +1 -1
- package/dist/types/S3SchemaContributor.d.ts +2 -3
- package/dist/types/S3SchemaContributor.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +11 -11
- package/src/S3ObjectAuthorizator.ts +2 -0
- package/src/S3SchemaContributor.ts +24 -10
- package/src/index.ts +2 -5
- package/tests/cases/authorizator.test.ts +8 -8
- package/tests/cases/signRequest.test.ts +6 -6
- 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-
|
|
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-
|
|
27
|
-
"@contember/graphql-utils": "2.0.0-
|
|
28
|
-
"@contember/typesafe": "2.0.0-
|
|
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": "^
|
|
31
|
-
"qs": "^6.
|
|
32
|
-
"uuid": "^
|
|
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.
|
|
37
|
-
"@types/uuid": "^
|
|
38
|
-
"graphql": "^16.
|
|
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,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 (!
|
|
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
|
|
67
|
-
const
|
|
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(
|
|
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(
|
|
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({
|
|
19
|
-
if (!project.s3) {
|
|
20
|
-
return undefined
|
|
21
|
-
}
|
|
18
|
+
getSchemaContributor({ providers }: SchemaContributorArgs) {
|
|
22
19
|
const s3ServiceFactory = new S3ServiceFactory()
|
|
23
|
-
return new S3SchemaContributor(
|
|
20
|
+
return new S3SchemaContributor(s3ServiceFactory, providers)
|
|
24
21
|
}
|
|
25
22
|
}
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import { test,
|
|
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
|
-
|
|
7
|
-
|
|
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
|
-
|
|
13
|
-
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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,
|
|
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).
|
|
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).
|
|
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).
|
|
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).
|
|
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).
|
|
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
|
|