Skip to content

Commit c0f504b

Browse files
stocaarojjarvisp
andauthored
fix: Custom type permissions issues from both authenticated and groups (#589)
Co-authored-by: James Jarvis <jjarvisp@amazon.com>
1 parent 92327ff commit c0f504b

File tree

8 files changed

+156
-11
lines changed

8 files changed

+156
-11
lines changed

.changeset/tidy-worlds-sort.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@aws-amplify/data-schema': patch
3+
---
4+
5+
Fix custom type permissions issues from both authenticated and group authorization

packages/data-schema/__tests__/CustomOperations.test.ts

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1021,6 +1021,45 @@ describe('CustomOperation transform', () => {
10211021
});
10221022
});
10231023

1024+
test('defineFunction for an async operation that has authorization rules for both group and authenticated', () => {
1025+
const fn1 = defineFunctionStub({});
1026+
const s = a.schema({
1027+
getPostDetails: a
1028+
.query()
1029+
.arguments({})
1030+
.handler([
1031+
a.handler.function(fn1).async(),
1032+
])
1033+
.authorization((allow) => [allow.authenticated(), allow.group('TestGroup')]),
1034+
});
1035+
1036+
const { schema, lambdaFunctions } = s.transform();
1037+
expect(schema).toMatchSnapshot();
1038+
});
1039+
1040+
test('defineFunction for two async operations that have authorization rules for either group or authenticated', () => {
1041+
const fn1 = defineFunctionStub({});
1042+
const s = a.schema({
1043+
getPostDetailsA: a
1044+
.query()
1045+
.arguments({})
1046+
.handler([
1047+
a.handler.function(fn1).async(),
1048+
])
1049+
.authorization((allow) => allow.group('TestGroup')),
1050+
getPostDetailsB: a
1051+
.query()
1052+
.arguments({})
1053+
.handler([
1054+
a.handler.function(fn1).async(),
1055+
])
1056+
.authorization((allow) => allow.authenticated()),
1057+
});
1058+
1059+
const { schema, lambdaFunctions } = s.transform();
1060+
expect(schema).toMatchSnapshot();
1061+
});
1062+
10241063
test('invalid', () => {
10251064
const invalidFnDef = {};
10261065

@@ -1841,7 +1880,7 @@ describe('custom operations + custom type auth inheritance', () => {
18411880
expect(result).toMatchSnapshot();
18421881
expect(result).toEqual(
18431882
expect.stringContaining(
1844-
'type QueryReturn @aws_api_key @aws_cognito_user_pools @aws_iam @aws_cognito_user_pools(cognito_groups: ["admin", "superAdmin"])',
1883+
'type QueryReturn @aws_api_key @aws_cognito_user_pools @aws_iam',
18451884
),
18461885
);
18471886
});

packages/data-schema/__tests__/CustomType.test.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { expectTypeTestsToPassAsync } from 'jest-tsd';
22
import { a } from '../src/index';
3+
import { defineFunctionStub } from './utils';
34

45
// evaluates type defs in corresponding test-d.ts file
56
it('should not produce static type errors', async () => {
@@ -258,4 +259,48 @@ describe('CustomType transform', () => {
258259

259260
expect(result).toMatchSnapshot();
260261
});
262+
263+
test('Implicit CustomType authorized by both group and authorized though its custom operation', () => {
264+
const fn1 = defineFunctionStub({});
265+
const s = a
266+
.schema({
267+
getPostDetailsA: a
268+
.query()
269+
.arguments({content: a.customType({content: a.string()})})
270+
.returns(a.customType({}))
271+
.handler([
272+
a.handler.function(fn1),
273+
]).authorization((allow) => [allow.authenticated(), allow.group("TestGroup")]),
274+
});
275+
276+
const result = s.transform().schema;
277+
278+
expect(result).toMatchSnapshot();
279+
});
280+
281+
test('Explicit CustomType authorized by both group and authorized though different custom operations', () => {
282+
const fn1 = defineFunctionStub({});
283+
const s = a
284+
.schema({
285+
testCustomType: a.customType({ content: a.string() }),
286+
getPostDetailsA: a
287+
.query()
288+
.arguments({})
289+
.returns(a.ref("testCustomType"))
290+
.handler([
291+
a.handler.function(fn1),
292+
]).authorization((allow) => [allow.authenticated()]),
293+
getPostDetailsB: a
294+
.query()
295+
.arguments({})
296+
.returns(a.ref("testCustomType"))
297+
.handler([
298+
a.handler.function(fn1),
299+
]).authorization((allow) => [allow.group("TestGroup")])
300+
});
301+
302+
const result = s.transform().schema;
303+
304+
expect(result).toMatchSnapshot();
305+
});
261306
});

packages/data-schema/__tests__/ModelType.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ describe('model auth rules', () => {
277277

278278
expect(graphql).toEqual(
279279
expect.stringContaining(
280-
'type CustomType @aws_api_key @aws_cognito_user_pools @aws_iam @aws_cognito_user_pools(cognito_groups: ["Admin", "User"])',
280+
'type CustomType @aws_api_key @aws_cognito_user_pools @aws_iam',
281281
),
282282
);
283283
});

packages/data-schema/__tests__/__snapshots__/CustomOperations.test.ts.snap

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -754,7 +754,7 @@ exports[`CustomOperation transform dynamo schema handlers a.handler.custom a.han
754754
}
755755
756756
type Query {
757-
customQuery: String @aws_cognito_user_pools @aws_cognito_user_pools(cognito_groups: ["groupA", "groupB"])
757+
customQuery: String @aws_cognito_user_pools
758758
}"
759759
`;
760760

@@ -802,6 +802,30 @@ type Query {
802802
}"
803803
`;
804804

805+
exports[`CustomOperation transform dynamo schema handlers a.handler.function defineFunction for an async operation that has authorization rules for both group and authenticated 1`] = `
806+
"type EventInvocationResponse @aws_cognito_user_pools
807+
{
808+
success: Boolean!
809+
}
810+
811+
type Query {
812+
getPostDetails: EventInvocationResponse @function(name: "FnGetPostDetails", invocationType: Event) @auth(rules: [{allow: private},
813+
{allow: groups, groups: ["TestGroup"]}])
814+
}"
815+
`;
816+
817+
exports[`CustomOperation transform dynamo schema handlers a.handler.function defineFunction for two async operations that have authorization rules for either group or authenticated 1`] = `
818+
"type EventInvocationResponse @aws_cognito_user_pools
819+
{
820+
success: Boolean!
821+
}
822+
823+
type Query {
824+
getPostDetailsA: EventInvocationResponse @function(name: "FnGetPostDetailsA", invocationType: Event) @auth(rules: [{allow: groups, groups: ["TestGroup"]}])
825+
getPostDetailsB: EventInvocationResponse @function(name: "FnGetPostDetailsB", invocationType: Event) @auth(rules: [{allow: private}])
826+
}"
827+
`;
828+
805829
exports[`CustomOperation transform dynamo schema handlers a.handler.function defineFunction sync - async 1`] = `
806830
"type EventInvocationResponse @aws_cognito_user_pools
807831
{
@@ -970,7 +994,7 @@ type Query {
970994
`;
971995

972996
exports[`custom operations + custom type auth inheritance op returns top-level custom type with all supported auth modes 1`] = `
973-
"type QueryReturn @aws_api_key @aws_cognito_user_pools @aws_iam @aws_cognito_user_pools(cognito_groups: ["admin", "superAdmin"])
997+
"type QueryReturn @aws_api_key @aws_cognito_user_pools @aws_iam
974998
{
975999
fieldA: String
9761000
fieldB: Int

packages/data-schema/__tests__/__snapshots__/CustomType.test.ts.snap

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,18 @@ type Location
3939
}"
4040
`;
4141

42+
exports[`CustomType transform Explicit CustomType authorized by both group and authorized though different custom operations 1`] = `
43+
"type testCustomType @aws_cognito_user_pools
44+
{
45+
content: String
46+
}
47+
48+
type Query {
49+
getPostDetailsA: testCustomType @function(name: "FnGetPostDetailsA") @auth(rules: [{allow: private}])
50+
getPostDetailsB: testCustomType @function(name: "FnGetPostDetailsB") @auth(rules: [{allow: groups, groups: ["TestGroup"]}])
51+
}"
52+
`;
53+
4254
exports[`CustomType transform Explicit CustomType nests explicit CustomType 1`] = `
4355
"type Post @model @auth(rules: [{allow: public, provider: apiKey}])
4456
{
@@ -134,6 +146,22 @@ type PostLocation
134146
}"
135147
`;
136148

149+
exports[`CustomType transform Implicit CustomType authorized by both group and authorized though its custom operation 1`] = `
150+
"type GetPostDetailsAReturnType @aws_cognito_user_pools
151+
{
152+
153+
}
154+
155+
input GetPostDetailsAContentInput {
156+
content: String
157+
}
158+
159+
type Query {
160+
getPostDetailsA(content: GetPostDetailsAContentInput): GetPostDetailsAReturnType @function(name: "FnGetPostDetailsA") @auth(rules: [{allow: private},
161+
{allow: groups, groups: ["TestGroup"]}])
162+
}"
163+
`;
164+
137165
exports[`CustomType transform Implicit CustomType nests explicit CustomType 1`] = `
138166
"type Post @model @auth(rules: [{allow: public, provider: apiKey}])
139167
{

packages/data-schema/__tests__/__snapshots__/ModelType.test.ts.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ exports[`model auth rules can create a static Admins group rule 1`] = `
137137
`;
138138

139139
exports[`model auth rules can deduplicate authorization prevent errors from auth defined by multiple custom operations 1`] = `
140-
"type CustomType @aws_api_key @aws_cognito_user_pools @aws_iam @aws_cognito_user_pools(cognito_groups: ["Admin", "User"])
140+
"type CustomType @aws_api_key @aws_cognito_user_pools @aws_iam
141141
{
142142
id: String!
143143
name: String!

packages/data-schema/src/SchemaProcessor.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -897,6 +897,7 @@ function mapToNativeAppSyncAuthDirectives(
897897
const rules = new Set<string>();
898898

899899
const groupProvider: Map<string, Set<string>> = new Map();
900+
const generalProviderUsed = new Set<string>();
900901

901902
for (const entry of authorization) {
902903
const rule = accessData(entry);
@@ -911,17 +912,20 @@ function mapToNativeAppSyncAuthDirectives(
911912
};
912913
rule.groups.forEach((group) => groupProvider.get(provider)?.add(group));
913914
} else {
915+
generalProviderUsed.add(provider);
914916
rules.add(provider);
915917
}
916918
}
917919

918920
groupProvider.forEach((groups, provider) => {
919-
rules.add(
920-
`${provider}(cognito_groups: [${Array.from(groups)
921-
.map((group) => `"${group}"`)
922-
.join(', ')}])`,
923-
);
924-
// example: (cognito_groups: ["Bloggers", "Readers"])
921+
if(!generalProviderUsed.has(provider)) {
922+
rules.add(
923+
`${provider}(cognito_groups: [${Array.from(groups).reduce((acc, group) =>
924+
acc == "" ? `"${group}"` : `${acc}, "${group}"`
925+
, "")}])`
926+
);
927+
// example: (cognito_groups: ["Bloggers", "Readers"])
928+
}
925929
})
926930

927931
const authString = [...rules].join(' ');

0 commit comments

Comments
 (0)