@axinom/mosaic-id-guard 0.47.0-rc.9 → 0.47.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.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axinom/mosaic-id-guard",
|
|
3
|
-
"version": "0.47.0
|
|
3
|
+
"version": "0.47.0",
|
|
4
4
|
"description": "Authentication and authorization helpers for Axinom Mosaic services",
|
|
5
5
|
"author": "Axinom",
|
|
6
6
|
"license": "PROPRIETARY",
|
|
@@ -30,10 +30,10 @@
|
|
|
30
30
|
"lint": "eslint . --ext .ts,.tsx,.js --color --cache"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@axinom/mosaic-id-utils": "^0.26.0
|
|
34
|
-
"@axinom/mosaic-message-bus": "^0.49.0
|
|
35
|
-
"@axinom/mosaic-service-common": "^0.68.0
|
|
36
|
-
"@axinom/mosaic-transactional-inbox-outbox": "^0.28.0
|
|
33
|
+
"@axinom/mosaic-id-utils": "^0.26.0",
|
|
34
|
+
"@axinom/mosaic-message-bus": "^0.49.0",
|
|
35
|
+
"@axinom/mosaic-service-common": "^0.68.0",
|
|
36
|
+
"@axinom/mosaic-transactional-inbox-outbox": "^0.28.0",
|
|
37
37
|
"amqplib": "^0.10.3",
|
|
38
38
|
"express": "^4.17.1",
|
|
39
39
|
"express-bearer-token": "^3.0.0",
|
|
@@ -69,5 +69,5 @@
|
|
|
69
69
|
"publishConfig": {
|
|
70
70
|
"access": "public"
|
|
71
71
|
},
|
|
72
|
-
"gitHead": "
|
|
72
|
+
"gitHead": "9732fd094fafedb5360d10405cc19fa202d42de6"
|
|
73
73
|
}
|
|
@@ -2,6 +2,7 @@ import { skipMaskTag } from '@axinom/mosaic-service-common';
|
|
|
2
2
|
import {
|
|
3
3
|
MutationPayloadQueryPlugin,
|
|
4
4
|
MutationPlugin,
|
|
5
|
+
Plugin,
|
|
5
6
|
QueryPlugin,
|
|
6
7
|
StandardTypesPlugin,
|
|
7
8
|
SubscriptionPlugin,
|
|
@@ -989,3 +990,342 @@ describe('QueryMutationGuardPlugin', () => {
|
|
|
989
990
|
});
|
|
990
991
|
});
|
|
991
992
|
});
|
|
993
|
+
|
|
994
|
+
describe('When an operation is renamed via a hook plugin', () => {
|
|
995
|
+
const RenameEchoPlugin: Plugin = (builder) => {
|
|
996
|
+
builder.hook('GraphQLObjectType:fields', (fields, _build, { Self }) => {
|
|
997
|
+
if (Self.name === 'Query' && 'echo' in fields) {
|
|
998
|
+
const { echo, ...rest } = fields;
|
|
999
|
+
return { ...rest, renamedEcho: echo };
|
|
1000
|
+
}
|
|
1001
|
+
if (Self.name === 'Mutation' && 'echoMutation' in fields) {
|
|
1002
|
+
const { echoMutation, ...rest } = fields;
|
|
1003
|
+
return { ...rest, renamedEchoMutation: echoMutation };
|
|
1004
|
+
}
|
|
1005
|
+
if (Self.name === 'Subscription' && 'echoSubscription' in fields) {
|
|
1006
|
+
const { echoSubscription, ...rest } = fields;
|
|
1007
|
+
return { ...rest, renamedEchoSubscription: echoSubscription };
|
|
1008
|
+
}
|
|
1009
|
+
return fields;
|
|
1010
|
+
});
|
|
1011
|
+
};
|
|
1012
|
+
|
|
1013
|
+
const makeSchemaWithRenamePlugin = async (
|
|
1014
|
+
spy: any,
|
|
1015
|
+
options = {},
|
|
1016
|
+
): Promise<GraphQLSchema> =>
|
|
1017
|
+
buildSchema(
|
|
1018
|
+
[
|
|
1019
|
+
StandardTypesPlugin,
|
|
1020
|
+
QueryPlugin,
|
|
1021
|
+
MutationPlugin,
|
|
1022
|
+
MutationPayloadQueryPlugin,
|
|
1023
|
+
SubscriptionPlugin,
|
|
1024
|
+
makeExtendSchemaPlugin(() => ({
|
|
1025
|
+
typeDefs: gql`
|
|
1026
|
+
extend type Query {
|
|
1027
|
+
echo(message: String!): String
|
|
1028
|
+
}
|
|
1029
|
+
extend type Mutation {
|
|
1030
|
+
echoMutation(message: String!): String
|
|
1031
|
+
}
|
|
1032
|
+
extend type Subscription {
|
|
1033
|
+
echoSubscription: String
|
|
1034
|
+
}
|
|
1035
|
+
`,
|
|
1036
|
+
resolvers: {
|
|
1037
|
+
Query: {
|
|
1038
|
+
echo: spy,
|
|
1039
|
+
},
|
|
1040
|
+
Mutation: {
|
|
1041
|
+
echoMutation: spy,
|
|
1042
|
+
},
|
|
1043
|
+
Subscription: {
|
|
1044
|
+
echoSubscription: {
|
|
1045
|
+
subscribe: () => pubSub.asyncIterator('echoSubscription'),
|
|
1046
|
+
resolve: () => 'Hello Subscription',
|
|
1047
|
+
},
|
|
1048
|
+
},
|
|
1049
|
+
},
|
|
1050
|
+
})),
|
|
1051
|
+
RenameEchoPlugin,
|
|
1052
|
+
QueryMutationGuardPlugin,
|
|
1053
|
+
],
|
|
1054
|
+
{
|
|
1055
|
+
serviceId: 'test-service',
|
|
1056
|
+
...options,
|
|
1057
|
+
},
|
|
1058
|
+
);
|
|
1059
|
+
|
|
1060
|
+
it('Authorized user can access the renamed query', async () => {
|
|
1061
|
+
// Arrange
|
|
1062
|
+
const spy = makeEchoSpy();
|
|
1063
|
+
const schema = await makeSchemaWithRenamePlugin(spy, {
|
|
1064
|
+
permissionDefinition: {
|
|
1065
|
+
gqlOptions: {
|
|
1066
|
+
anonymousGqlOperations: [],
|
|
1067
|
+
ignoredGqlOperations: [],
|
|
1068
|
+
},
|
|
1069
|
+
permissions: [
|
|
1070
|
+
{
|
|
1071
|
+
key: 'Editor',
|
|
1072
|
+
title: 'Editor',
|
|
1073
|
+
gqlOperations: ['renamedEcho'],
|
|
1074
|
+
},
|
|
1075
|
+
],
|
|
1076
|
+
},
|
|
1077
|
+
});
|
|
1078
|
+
const jwtContext = {
|
|
1079
|
+
subject: {
|
|
1080
|
+
sub: 'test-user',
|
|
1081
|
+
permissions: { 'test-service': ['Editor'] },
|
|
1082
|
+
subjectType: SubjectType.UserAccount,
|
|
1083
|
+
},
|
|
1084
|
+
};
|
|
1085
|
+
|
|
1086
|
+
// Act
|
|
1087
|
+
const result = await graphql(
|
|
1088
|
+
schema,
|
|
1089
|
+
'{ renamedEcho(message: "Hello") }',
|
|
1090
|
+
null,
|
|
1091
|
+
jwtContext,
|
|
1092
|
+
);
|
|
1093
|
+
|
|
1094
|
+
// Assert
|
|
1095
|
+
expect(result.errors).toBeUndefined();
|
|
1096
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
|
1097
|
+
expect(result.data?.renamedEcho).toBe('Hello');
|
|
1098
|
+
});
|
|
1099
|
+
|
|
1100
|
+
it('Unauthorized user cannot access the renamed query', async () => {
|
|
1101
|
+
// Arrange
|
|
1102
|
+
const spy = makeEchoSpy();
|
|
1103
|
+
const schema = await makeSchemaWithRenamePlugin(spy, {
|
|
1104
|
+
permissionDefinition: {
|
|
1105
|
+
gqlOptions: {
|
|
1106
|
+
anonymousGqlOperations: [],
|
|
1107
|
+
ignoredGqlOperations: [],
|
|
1108
|
+
},
|
|
1109
|
+
permissions: [
|
|
1110
|
+
{
|
|
1111
|
+
key: 'Editor',
|
|
1112
|
+
title: 'Editor',
|
|
1113
|
+
gqlOperations: ['renamedEcho'],
|
|
1114
|
+
},
|
|
1115
|
+
],
|
|
1116
|
+
},
|
|
1117
|
+
});
|
|
1118
|
+
const jwtContext = {
|
|
1119
|
+
subject: {
|
|
1120
|
+
sub: 'test-user',
|
|
1121
|
+
permissions: { 'test-service': ['Viewer'] },
|
|
1122
|
+
subjectType: SubjectType.UserAccount,
|
|
1123
|
+
},
|
|
1124
|
+
};
|
|
1125
|
+
|
|
1126
|
+
// Act
|
|
1127
|
+
const result = await graphql(
|
|
1128
|
+
schema,
|
|
1129
|
+
'{ renamedEcho(message: "Hello") }',
|
|
1130
|
+
null,
|
|
1131
|
+
jwtContext,
|
|
1132
|
+
);
|
|
1133
|
+
|
|
1134
|
+
// Assert
|
|
1135
|
+
expect(result.data?.renamedEcho).toBeNull();
|
|
1136
|
+
expect(spy).toHaveBeenCalledTimes(0);
|
|
1137
|
+
expect(result.errors).toHaveLength(1);
|
|
1138
|
+
const error = result.errors![0].originalError as any;
|
|
1139
|
+
expect(error.code).toBe(IdGuardErrors.UserNotAuthorized.code);
|
|
1140
|
+
});
|
|
1141
|
+
|
|
1142
|
+
it('Authorized user can access the renamed mutation', async () => {
|
|
1143
|
+
// Arrange
|
|
1144
|
+
const spy = makeEchoSpy();
|
|
1145
|
+
const schema = await makeSchemaWithRenamePlugin(spy, {
|
|
1146
|
+
permissionDefinition: {
|
|
1147
|
+
gqlOptions: {
|
|
1148
|
+
anonymousGqlOperations: [],
|
|
1149
|
+
ignoredGqlOperations: [],
|
|
1150
|
+
},
|
|
1151
|
+
permissions: [
|
|
1152
|
+
{
|
|
1153
|
+
key: 'Editor',
|
|
1154
|
+
title: 'Editor',
|
|
1155
|
+
gqlOperations: ['renamedEchoMutation'],
|
|
1156
|
+
},
|
|
1157
|
+
],
|
|
1158
|
+
},
|
|
1159
|
+
});
|
|
1160
|
+
const jwtContext = {
|
|
1161
|
+
subject: {
|
|
1162
|
+
sub: 'test-user',
|
|
1163
|
+
permissions: { 'test-service': ['Editor'] },
|
|
1164
|
+
subjectType: SubjectType.UserAccount,
|
|
1165
|
+
},
|
|
1166
|
+
};
|
|
1167
|
+
|
|
1168
|
+
// Act
|
|
1169
|
+
const result = await graphql(
|
|
1170
|
+
schema,
|
|
1171
|
+
'mutation { renamedEchoMutation(message: "Hello") }',
|
|
1172
|
+
null,
|
|
1173
|
+
jwtContext,
|
|
1174
|
+
);
|
|
1175
|
+
|
|
1176
|
+
// Assert
|
|
1177
|
+
expect(result.errors).toBeUndefined();
|
|
1178
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
|
1179
|
+
expect(result.data?.renamedEchoMutation).toBe('Hello');
|
|
1180
|
+
});
|
|
1181
|
+
|
|
1182
|
+
it('Unauthorized user cannot access the renamed mutation', async () => {
|
|
1183
|
+
// Arrange
|
|
1184
|
+
const spy = makeEchoSpy();
|
|
1185
|
+
const schema = await makeSchemaWithRenamePlugin(spy, {
|
|
1186
|
+
permissionDefinition: {
|
|
1187
|
+
gqlOptions: {
|
|
1188
|
+
anonymousGqlOperations: [],
|
|
1189
|
+
ignoredGqlOperations: [],
|
|
1190
|
+
},
|
|
1191
|
+
permissions: [
|
|
1192
|
+
{
|
|
1193
|
+
key: 'Editor',
|
|
1194
|
+
title: 'Editor',
|
|
1195
|
+
gqlOperations: ['renamedEchoMutation'],
|
|
1196
|
+
},
|
|
1197
|
+
],
|
|
1198
|
+
},
|
|
1199
|
+
});
|
|
1200
|
+
const jwtContext = {
|
|
1201
|
+
subject: {
|
|
1202
|
+
sub: 'test-user',
|
|
1203
|
+
permissions: { 'test-service': ['Viewer'] },
|
|
1204
|
+
subjectType: SubjectType.UserAccount,
|
|
1205
|
+
},
|
|
1206
|
+
};
|
|
1207
|
+
|
|
1208
|
+
// Act
|
|
1209
|
+
const result = await graphql(
|
|
1210
|
+
schema,
|
|
1211
|
+
'mutation { renamedEchoMutation(message: "Hello") }',
|
|
1212
|
+
null,
|
|
1213
|
+
jwtContext,
|
|
1214
|
+
);
|
|
1215
|
+
|
|
1216
|
+
// Assert
|
|
1217
|
+
expect(result.data?.renamedEchoMutation).toBeNull();
|
|
1218
|
+
expect(spy).toHaveBeenCalledTimes(0);
|
|
1219
|
+
expect(result.errors).toHaveLength(1);
|
|
1220
|
+
const error = result.errors![0].originalError as any;
|
|
1221
|
+
expect(error.code).toBe(IdGuardErrors.UserNotAuthorized.code);
|
|
1222
|
+
});
|
|
1223
|
+
|
|
1224
|
+
it('Authorized user can access the renamed subscription', async () => {
|
|
1225
|
+
// Arrange
|
|
1226
|
+
const spy = makeEchoSpy();
|
|
1227
|
+
const schema = await makeSchemaWithRenamePlugin(spy, {
|
|
1228
|
+
permissionDefinition: {
|
|
1229
|
+
gqlOptions: {
|
|
1230
|
+
anonymousGqlOperations: [],
|
|
1231
|
+
ignoredGqlOperations: [],
|
|
1232
|
+
},
|
|
1233
|
+
permissions: [
|
|
1234
|
+
{
|
|
1235
|
+
key: 'Editor',
|
|
1236
|
+
title: 'Editor',
|
|
1237
|
+
gqlOperations: ['renamedEchoSubscription'],
|
|
1238
|
+
},
|
|
1239
|
+
],
|
|
1240
|
+
},
|
|
1241
|
+
});
|
|
1242
|
+
const jwtContext = {
|
|
1243
|
+
subject: {
|
|
1244
|
+
sub: 'test-user',
|
|
1245
|
+
permissions: { 'test-service': ['Editor'] },
|
|
1246
|
+
exp: new Date().getTime() / 1000 + 20,
|
|
1247
|
+
subjectType: SubjectType.UserAccount,
|
|
1248
|
+
},
|
|
1249
|
+
};
|
|
1250
|
+
|
|
1251
|
+
const document = parse('subscription { renamedEchoSubscription }');
|
|
1252
|
+
|
|
1253
|
+
// Create subscription
|
|
1254
|
+
const subscription: any = await subscribe({
|
|
1255
|
+
schema,
|
|
1256
|
+
document,
|
|
1257
|
+
rootValue: null,
|
|
1258
|
+
contextValue: jwtContext,
|
|
1259
|
+
});
|
|
1260
|
+
|
|
1261
|
+
const resolveSubscription = subscription.next();
|
|
1262
|
+
|
|
1263
|
+
// Publish trigger to invoke subscription resolver
|
|
1264
|
+
pubSub.publish('echoSubscription', 'message');
|
|
1265
|
+
|
|
1266
|
+
// Await for the subscription resolver to respond
|
|
1267
|
+
const result = await resolveSubscription;
|
|
1268
|
+
|
|
1269
|
+
expect(result.value.errors).toBeFalsy();
|
|
1270
|
+
expect(result.value.data.renamedEchoSubscription).toBe(
|
|
1271
|
+
'Hello Subscription',
|
|
1272
|
+
);
|
|
1273
|
+
|
|
1274
|
+
// Close subscription
|
|
1275
|
+
await subscription.return();
|
|
1276
|
+
});
|
|
1277
|
+
|
|
1278
|
+
it('Unauthorized user cannot access the renamed subscription', async () => {
|
|
1279
|
+
// Arrange
|
|
1280
|
+
const spy = makeEchoSpy();
|
|
1281
|
+
const schema = await makeSchemaWithRenamePlugin(spy, {
|
|
1282
|
+
permissionDefinition: {
|
|
1283
|
+
gqlOptions: {
|
|
1284
|
+
anonymousGqlOperations: [],
|
|
1285
|
+
ignoredGqlOperations: [],
|
|
1286
|
+
},
|
|
1287
|
+
permissions: [
|
|
1288
|
+
{
|
|
1289
|
+
key: 'Editor',
|
|
1290
|
+
title: 'Editor',
|
|
1291
|
+
gqlOperations: ['renamedEchoSubscription'],
|
|
1292
|
+
},
|
|
1293
|
+
],
|
|
1294
|
+
},
|
|
1295
|
+
});
|
|
1296
|
+
const jwtContext = {
|
|
1297
|
+
subject: {
|
|
1298
|
+
sub: 'test-user',
|
|
1299
|
+
permissions: { 'test-service': ['Viewer'] },
|
|
1300
|
+
exp: new Date().getTime() / 1000 + 20,
|
|
1301
|
+
subjectType: SubjectType.UserAccount,
|
|
1302
|
+
},
|
|
1303
|
+
};
|
|
1304
|
+
|
|
1305
|
+
const document = parse('subscription { renamedEchoSubscription }');
|
|
1306
|
+
|
|
1307
|
+
// Create subscription
|
|
1308
|
+
const subscription: any = await subscribe({
|
|
1309
|
+
schema,
|
|
1310
|
+
document,
|
|
1311
|
+
rootValue: null,
|
|
1312
|
+
contextValue: jwtContext,
|
|
1313
|
+
});
|
|
1314
|
+
|
|
1315
|
+
const resolveSubscription = subscription.next();
|
|
1316
|
+
|
|
1317
|
+
// Publish trigger to invoke subscription resolver
|
|
1318
|
+
pubSub.publish('echoSubscription', 'message');
|
|
1319
|
+
|
|
1320
|
+
// Await for the subscription resolver to respond
|
|
1321
|
+
const result = await resolveSubscription;
|
|
1322
|
+
|
|
1323
|
+
expect(result.value.errors).toBeTruthy();
|
|
1324
|
+
expect(result.value.errors[0].message).toBe(
|
|
1325
|
+
'User is not authorized to access the operation.',
|
|
1326
|
+
);
|
|
1327
|
+
|
|
1328
|
+
// Close subscription
|
|
1329
|
+
await subscription.return();
|
|
1330
|
+
});
|
|
1331
|
+
});
|