@stamhoofd/backend 2.105.0 → 2.106.1
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 +10 -10
- package/src/crons.ts +39 -5
- package/src/endpoints/global/members/GetMembersEndpoint.test.ts +953 -47
- package/src/endpoints/global/members/GetMembersEndpoint.ts +1 -1
- package/src/endpoints/global/members/PatchOrganizationMembersEndpoint.test.ts +142 -0
- package/src/endpoints/global/registration/GetRegistrationsEndpoint.ts +1 -1
- package/src/endpoints/global/registration/RegisterMembersEndpoint.test.ts +163 -8
- package/src/endpoints/global/registration/RegisterMembersEndpoint.ts +2 -0
- package/src/endpoints/organization/dashboard/billing/GetPackagesEndpoint.test.ts +108 -0
- package/src/endpoints/organization/dashboard/billing/GetPackagesEndpoint.ts +40 -0
- package/src/endpoints/organization/dashboard/webshops/PatchWebshopOrdersEndpoint.ts +8 -1
- package/src/endpoints/organization/webshops/PlaceOrderEndpoint.ts +8 -1
- package/src/helpers/AdminPermissionChecker.ts +30 -6
- package/src/helpers/AuthenticatedStructures.ts +2 -2
- package/src/helpers/MemberUserSyncer.test.ts +400 -1
- package/src/helpers/MemberUserSyncer.ts +15 -10
- package/src/helpers/ServiceFeeHelper.ts +63 -0
- package/src/helpers/StripeHelper.ts +7 -4
- package/src/helpers/StripePayoutChecker.ts +1 -1
- package/src/seeds/0000000001-development-user.ts +2 -2
- package/src/seeds/0000000004-single-organization.ts +60 -0
- package/src/seeds/1754560914-groups-prices.test.ts +3023 -0
- package/src/seeds/1754560914-groups-prices.ts +408 -0
- package/src/seeds/{1722344162-sync-member-users.ts → 1761665607-sync-member-users.ts} +1 -1
- package/src/sql-filters/members.ts +1 -1
- package/tests/init/initAdmin.ts +19 -5
- package/tests/init/initPermissionRole.ts +14 -4
- package/tests/init/initPlatformRecordCategory.ts +8 -0
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { Endpoint, Request } from '@simonbackx/simple-endpoints';
|
|
2
|
-
import { EventFactory, GroupFactory, MemberFactory, OrganizationFactory, RegistrationFactory, RegistrationPeriod, RegistrationPeriodFactory, Token, UserFactory } from '@stamhoofd/models';
|
|
3
|
-
import { AccessRight, EventMeta, GroupType, LimitedFilteredRequest, NamedObject, PermissionLevel, PermissionRoleDetailed, Permissions, PermissionsResourceType, ResourcePermissions } from '@stamhoofd/structures';
|
|
2
|
+
import { EventFactory, GroupFactory, MemberFactory, OrganizationFactory, RecordCategoryFactory, RegistrationFactory, RegistrationPeriod, RegistrationPeriodFactory, Token, UserFactory } from '@stamhoofd/models';
|
|
3
|
+
import { AccessRight, EventMeta, GroupType, LimitedFilteredRequest, NamedObject, PermissionLevel, PermissionRoleDetailed, Permissions, PermissionsResourceType, RecordAnswer, RecordTextAnswer, RecordType, ResourcePermissions } from '@stamhoofd/structures';
|
|
4
4
|
import { STExpect, TestUtils } from '@stamhoofd/test-utils';
|
|
5
5
|
import { GetMembersEndpoint } from './GetMembersEndpoint';
|
|
6
6
|
import { testServer } from '../../../../tests/helpers/TestServer';
|
|
7
|
+
import { initPlatformRecordCategory } from '../../../../tests/init/initPlatformRecordCategory';
|
|
7
8
|
|
|
8
9
|
const baseUrl = `/members`;
|
|
9
10
|
const endpoint = new GetMembersEndpoint();
|
|
@@ -884,14 +885,572 @@ describe('Endpoint.GetMembersEndpoint', () => {
|
|
|
884
885
|
STExpect.errorWithCode('permission_denied'),
|
|
885
886
|
);
|
|
886
887
|
});
|
|
888
|
+
|
|
889
|
+
test('Not allowed: A user cannot filter on record answer if no permission for that category', async () => {
|
|
890
|
+
// Same test, but without giving the user permissions to read the group
|
|
891
|
+
// Setup
|
|
892
|
+
const role = PermissionRoleDetailed.create({
|
|
893
|
+
name: 'Test Role',
|
|
894
|
+
accessRights: [],
|
|
895
|
+
});
|
|
896
|
+
|
|
897
|
+
const resources = new Map();
|
|
898
|
+
|
|
899
|
+
const organization = await new OrganizationFactory({ period, roles: [role] })
|
|
900
|
+
.create();
|
|
901
|
+
|
|
902
|
+
const recordCategory = await new RecordCategoryFactory({
|
|
903
|
+
records: [
|
|
904
|
+
{
|
|
905
|
+
type: RecordType.Text,
|
|
906
|
+
},
|
|
907
|
+
],
|
|
908
|
+
}).create();
|
|
909
|
+
|
|
910
|
+
await initPlatformRecordCategory({ recordCategory });
|
|
911
|
+
const record = recordCategory.records[0];
|
|
912
|
+
|
|
913
|
+
const user = await new UserFactory({
|
|
914
|
+
organization,
|
|
915
|
+
permissions: Permissions.create({
|
|
916
|
+
level: PermissionLevel.None,
|
|
917
|
+
roles: [
|
|
918
|
+
role,
|
|
919
|
+
],
|
|
920
|
+
resources,
|
|
921
|
+
}),
|
|
922
|
+
})
|
|
923
|
+
.create();
|
|
924
|
+
|
|
925
|
+
const token = await Token.createToken(user);
|
|
926
|
+
const member1 = await new MemberFactory({ }).create();
|
|
927
|
+
const member2 = await new MemberFactory({ }).create();
|
|
928
|
+
|
|
929
|
+
// Make sure member1 has answered the question
|
|
930
|
+
const answer = RecordTextAnswer.create({ settings: record });
|
|
931
|
+
answer.value = 'This has been answered';
|
|
932
|
+
member1.details.recordAnswers.set(record.id, answer);
|
|
933
|
+
await member1.save();
|
|
934
|
+
|
|
935
|
+
const group = await new GroupFactory({ organization, period }).create();
|
|
936
|
+
|
|
937
|
+
// The user can read members registered for default group, but not events for default group
|
|
938
|
+
resources.set(
|
|
939
|
+
PermissionsResourceType.Groups, new Map([[
|
|
940
|
+
group.id,
|
|
941
|
+
ResourcePermissions.create({
|
|
942
|
+
level: PermissionLevel.Read,
|
|
943
|
+
accessRights: [],
|
|
944
|
+
}),
|
|
945
|
+
]]),
|
|
946
|
+
);
|
|
947
|
+
|
|
948
|
+
await user.save();
|
|
949
|
+
|
|
950
|
+
await new RegistrationFactory({ member: member1, group }).create();
|
|
951
|
+
await new RegistrationFactory({ member: member2, group }).create();
|
|
952
|
+
|
|
953
|
+
// Try to request members for this group
|
|
954
|
+
const request = Request.get({
|
|
955
|
+
path: baseUrl,
|
|
956
|
+
host: organization.getApiHost(),
|
|
957
|
+
query: new LimitedFilteredRequest({
|
|
958
|
+
filter: {
|
|
959
|
+
registrations: {
|
|
960
|
+
$elemMatch: {
|
|
961
|
+
groupId: group.id,
|
|
962
|
+
},
|
|
963
|
+
},
|
|
964
|
+
details: {
|
|
965
|
+
recordAnswers: {
|
|
966
|
+
[record.id]: {
|
|
967
|
+
value: {
|
|
968
|
+
$contains: 'has been',
|
|
969
|
+
},
|
|
970
|
+
},
|
|
971
|
+
},
|
|
972
|
+
},
|
|
973
|
+
},
|
|
974
|
+
limit: 10,
|
|
975
|
+
}),
|
|
976
|
+
headers: {
|
|
977
|
+
authorization: 'Bearer ' + token.accessToken,
|
|
978
|
+
},
|
|
979
|
+
});
|
|
980
|
+
|
|
981
|
+
await expect(testServer.test(endpoint, request)).rejects.toThrow(
|
|
982
|
+
STExpect.errorWithCode('permission_denied'),
|
|
983
|
+
);
|
|
984
|
+
});
|
|
985
|
+
|
|
986
|
+
test('Allowed: A user can filter on record answer if permission for that category', async () => {
|
|
987
|
+
const resources = new Map();
|
|
988
|
+
|
|
989
|
+
const organization = await new OrganizationFactory({ period, roles: [] })
|
|
990
|
+
.create();
|
|
991
|
+
|
|
992
|
+
const recordCategory = await new RecordCategoryFactory({
|
|
993
|
+
records: [
|
|
994
|
+
{
|
|
995
|
+
type: RecordType.Text,
|
|
996
|
+
},
|
|
997
|
+
],
|
|
998
|
+
}).create();
|
|
999
|
+
|
|
1000
|
+
await initPlatformRecordCategory({ recordCategory });
|
|
1001
|
+
const record = recordCategory.records[0];
|
|
1002
|
+
|
|
1003
|
+
const user = await new UserFactory({
|
|
1004
|
+
organization,
|
|
1005
|
+
permissions: Permissions.create({
|
|
1006
|
+
level: PermissionLevel.None,
|
|
1007
|
+
roles: [],
|
|
1008
|
+
resources,
|
|
1009
|
+
}),
|
|
1010
|
+
})
|
|
1011
|
+
.create();
|
|
1012
|
+
|
|
1013
|
+
const token = await Token.createToken(user);
|
|
1014
|
+
const member1 = await new MemberFactory({ }).create();
|
|
1015
|
+
const member2 = await new MemberFactory({ }).create();
|
|
1016
|
+
|
|
1017
|
+
// Make sure member1 has answered the question
|
|
1018
|
+
const answer = RecordTextAnswer.create({ settings: record });
|
|
1019
|
+
answer.value = 'This has been answered';
|
|
1020
|
+
member1.details.recordAnswers.set(record.id, answer);
|
|
1021
|
+
await member1.save();
|
|
1022
|
+
|
|
1023
|
+
const group = await new GroupFactory({ organization, period }).create();
|
|
1024
|
+
|
|
1025
|
+
// The user can read members registered for default group, but not events for default group
|
|
1026
|
+
resources.set(
|
|
1027
|
+
PermissionsResourceType.Groups, new Map([[
|
|
1028
|
+
group.id,
|
|
1029
|
+
ResourcePermissions.create({
|
|
1030
|
+
level: PermissionLevel.Read,
|
|
1031
|
+
accessRights: [],
|
|
1032
|
+
}),
|
|
1033
|
+
]]),
|
|
1034
|
+
);
|
|
1035
|
+
|
|
1036
|
+
resources.set(
|
|
1037
|
+
PermissionsResourceType.RecordCategories, new Map([[
|
|
1038
|
+
recordCategory.id,
|
|
1039
|
+
ResourcePermissions.create({
|
|
1040
|
+
level: PermissionLevel.Read,
|
|
1041
|
+
accessRights: [],
|
|
1042
|
+
}),
|
|
1043
|
+
]]),
|
|
1044
|
+
);
|
|
1045
|
+
|
|
1046
|
+
await user.save();
|
|
1047
|
+
|
|
1048
|
+
await new RegistrationFactory({ member: member1, group }).create();
|
|
1049
|
+
await new RegistrationFactory({ member: member2, group }).create();
|
|
1050
|
+
|
|
1051
|
+
// Try to request members for this group
|
|
1052
|
+
const request = Request.get({
|
|
1053
|
+
path: baseUrl,
|
|
1054
|
+
host: organization.getApiHost(),
|
|
1055
|
+
query: new LimitedFilteredRequest({
|
|
1056
|
+
filter: {
|
|
1057
|
+
registrations: {
|
|
1058
|
+
$elemMatch: {
|
|
1059
|
+
groupId: group.id,
|
|
1060
|
+
},
|
|
1061
|
+
},
|
|
1062
|
+
details: {
|
|
1063
|
+
recordAnswers: {
|
|
1064
|
+
[record.id]: {
|
|
1065
|
+
value: {
|
|
1066
|
+
$contains: 'has been',
|
|
1067
|
+
},
|
|
1068
|
+
},
|
|
1069
|
+
},
|
|
1070
|
+
},
|
|
1071
|
+
},
|
|
1072
|
+
limit: 10,
|
|
1073
|
+
}),
|
|
1074
|
+
headers: {
|
|
1075
|
+
authorization: 'Bearer ' + token.accessToken,
|
|
1076
|
+
},
|
|
1077
|
+
});
|
|
1078
|
+
|
|
1079
|
+
// Response only includes members registered in a membership group, not the event group
|
|
1080
|
+
const response = await testServer.test(endpoint, request);
|
|
1081
|
+
expect(response.status).toBe(200);
|
|
1082
|
+
expect(response.body.results.members).toHaveLength(1);
|
|
1083
|
+
// Check ids are matching without depending on ordering using jest extended
|
|
1084
|
+
expect(response.body.results.members).toIncludeSameMembers([
|
|
1085
|
+
expect.objectContaining({ id: member1.id }),
|
|
1086
|
+
]);
|
|
1087
|
+
});
|
|
1088
|
+
|
|
1089
|
+
test('Allowed: A platform admin can filter on record answer if permission for all members', async () => {
|
|
1090
|
+
const resources = new Map();
|
|
1091
|
+
|
|
1092
|
+
const organization = await new OrganizationFactory({ period, roles: [] })
|
|
1093
|
+
.create();
|
|
1094
|
+
|
|
1095
|
+
const recordCategory = await new RecordCategoryFactory({
|
|
1096
|
+
records: [
|
|
1097
|
+
{
|
|
1098
|
+
type: RecordType.Text,
|
|
1099
|
+
},
|
|
1100
|
+
],
|
|
1101
|
+
}).create();
|
|
1102
|
+
|
|
1103
|
+
await initPlatformRecordCategory({ recordCategory });
|
|
1104
|
+
const record = recordCategory.records[0];
|
|
1105
|
+
|
|
1106
|
+
const user = await new UserFactory({
|
|
1107
|
+
globalPermissions: Permissions.create({
|
|
1108
|
+
level: PermissionLevel.None,
|
|
1109
|
+
roles: [],
|
|
1110
|
+
resources,
|
|
1111
|
+
}),
|
|
1112
|
+
})
|
|
1113
|
+
.create();
|
|
1114
|
+
|
|
1115
|
+
const token = await Token.createToken(user);
|
|
1116
|
+
const member1 = await new MemberFactory({ }).create();
|
|
1117
|
+
const member2 = await new MemberFactory({ }).create();
|
|
1118
|
+
|
|
1119
|
+
// Make sure member1 has answered the question
|
|
1120
|
+
const answer = RecordTextAnswer.create({ settings: record });
|
|
1121
|
+
answer.value = 'This has been answered';
|
|
1122
|
+
member1.details.recordAnswers.set(record.id, answer);
|
|
1123
|
+
await member1.save();
|
|
1124
|
+
|
|
1125
|
+
const group = await new GroupFactory({ organization, period }).create();
|
|
1126
|
+
|
|
1127
|
+
resources.set(
|
|
1128
|
+
PermissionsResourceType.OrganizationTags, new Map([[
|
|
1129
|
+
'',
|
|
1130
|
+
ResourcePermissions.create({
|
|
1131
|
+
level: PermissionLevel.Full,
|
|
1132
|
+
accessRights: [],
|
|
1133
|
+
}),
|
|
1134
|
+
]]),
|
|
1135
|
+
);
|
|
1136
|
+
|
|
1137
|
+
await user.save();
|
|
1138
|
+
|
|
1139
|
+
await new RegistrationFactory({ member: member1, group }).create();
|
|
1140
|
+
await new RegistrationFactory({ member: member2, group }).create();
|
|
1141
|
+
|
|
1142
|
+
// Try to request members for this group
|
|
1143
|
+
const request = Request.get({
|
|
1144
|
+
path: baseUrl,
|
|
1145
|
+
host: organization.getApiHost(),
|
|
1146
|
+
query: new LimitedFilteredRequest({
|
|
1147
|
+
filter: {
|
|
1148
|
+
registrations: {
|
|
1149
|
+
$elemMatch: {
|
|
1150
|
+
groupId: group.id,
|
|
1151
|
+
},
|
|
1152
|
+
},
|
|
1153
|
+
details: {
|
|
1154
|
+
recordAnswers: {
|
|
1155
|
+
[record.id]: {
|
|
1156
|
+
value: {
|
|
1157
|
+
$contains: 'has been',
|
|
1158
|
+
},
|
|
1159
|
+
},
|
|
1160
|
+
},
|
|
1161
|
+
},
|
|
1162
|
+
},
|
|
1163
|
+
limit: 10,
|
|
1164
|
+
}),
|
|
1165
|
+
headers: {
|
|
1166
|
+
authorization: 'Bearer ' + token.accessToken,
|
|
1167
|
+
},
|
|
1168
|
+
});
|
|
1169
|
+
|
|
1170
|
+
// Response only includes members registered in a membership group, not the event group
|
|
1171
|
+
const response = await testServer.test(endpoint, request);
|
|
1172
|
+
expect(response.status).toBe(200);
|
|
1173
|
+
expect(response.body.results.members).toHaveLength(1);
|
|
1174
|
+
// Check ids are matching without depending on ordering using jest extended
|
|
1175
|
+
expect(response.body.results.members).toIncludeSameMembers([
|
|
1176
|
+
expect.objectContaining({ id: member1.id }),
|
|
1177
|
+
]);
|
|
1178
|
+
});
|
|
1179
|
+
|
|
1180
|
+
test('Not allowed: A platform admin cannot filter on record answer if no permission for all members', async () => {
|
|
1181
|
+
const resources = new Map();
|
|
1182
|
+
|
|
1183
|
+
const organization = await new OrganizationFactory({ period, roles: [] })
|
|
1184
|
+
.create();
|
|
1185
|
+
|
|
1186
|
+
const recordCategory = await new RecordCategoryFactory({
|
|
1187
|
+
records: [
|
|
1188
|
+
{
|
|
1189
|
+
type: RecordType.Text,
|
|
1190
|
+
},
|
|
1191
|
+
],
|
|
1192
|
+
}).create();
|
|
1193
|
+
|
|
1194
|
+
await initPlatformRecordCategory({ recordCategory });
|
|
1195
|
+
const record = recordCategory.records[0];
|
|
1196
|
+
const group = await new GroupFactory({ organization, period }).create();
|
|
1197
|
+
|
|
1198
|
+
const user = await new UserFactory({
|
|
1199
|
+
organization,
|
|
1200
|
+
permissions: Permissions.create({
|
|
1201
|
+
level: PermissionLevel.None,
|
|
1202
|
+
resources: new Map([[
|
|
1203
|
+
PermissionsResourceType.Groups, new Map([[
|
|
1204
|
+
'',
|
|
1205
|
+
ResourcePermissions.create({
|
|
1206
|
+
level: PermissionLevel.Full,
|
|
1207
|
+
accessRights: [],
|
|
1208
|
+
}),
|
|
1209
|
+
]]),
|
|
1210
|
+
]]),
|
|
1211
|
+
}),
|
|
1212
|
+
globalPermissions: Permissions.create({
|
|
1213
|
+
level: PermissionLevel.None,
|
|
1214
|
+
roles: [],
|
|
1215
|
+
resources,
|
|
1216
|
+
}),
|
|
1217
|
+
})
|
|
1218
|
+
.create();
|
|
1219
|
+
|
|
1220
|
+
const token = await Token.createToken(user);
|
|
1221
|
+
const member1 = await new MemberFactory({ }).create();
|
|
1222
|
+
const member2 = await new MemberFactory({ }).create();
|
|
1223
|
+
|
|
1224
|
+
// Make sure member1 has answered the question
|
|
1225
|
+
const answer = RecordTextAnswer.create({ settings: record });
|
|
1226
|
+
answer.value = 'This has been answered';
|
|
1227
|
+
member1.details.recordAnswers.set(record.id, answer);
|
|
1228
|
+
await member1.save();
|
|
1229
|
+
|
|
1230
|
+
resources.set(
|
|
1231
|
+
PermissionsResourceType.OrganizationTags, new Map([[
|
|
1232
|
+
'tagtest',
|
|
1233
|
+
ResourcePermissions.create({
|
|
1234
|
+
level: PermissionLevel.Full,
|
|
1235
|
+
accessRights: [],
|
|
1236
|
+
}),
|
|
1237
|
+
]]),
|
|
1238
|
+
);
|
|
1239
|
+
|
|
1240
|
+
await user.save();
|
|
1241
|
+
|
|
1242
|
+
await new RegistrationFactory({ member: member1, group }).create();
|
|
1243
|
+
await new RegistrationFactory({ member: member2, group }).create();
|
|
1244
|
+
|
|
1245
|
+
// Try to request members for this group
|
|
1246
|
+
const request = Request.get({
|
|
1247
|
+
path: baseUrl,
|
|
1248
|
+
host: organization.getApiHost(),
|
|
1249
|
+
query: new LimitedFilteredRequest({
|
|
1250
|
+
filter: {
|
|
1251
|
+
registrations: {
|
|
1252
|
+
$elemMatch: {
|
|
1253
|
+
groupId: group.id,
|
|
1254
|
+
},
|
|
1255
|
+
},
|
|
1256
|
+
details: {
|
|
1257
|
+
recordAnswers: {
|
|
1258
|
+
[record.id]: {
|
|
1259
|
+
value: {
|
|
1260
|
+
$contains: 'has been',
|
|
1261
|
+
},
|
|
1262
|
+
},
|
|
1263
|
+
},
|
|
1264
|
+
},
|
|
1265
|
+
},
|
|
1266
|
+
limit: 10,
|
|
1267
|
+
}),
|
|
1268
|
+
headers: {
|
|
1269
|
+
authorization: 'Bearer ' + token.accessToken,
|
|
1270
|
+
},
|
|
1271
|
+
});
|
|
1272
|
+
|
|
1273
|
+
await expect(testServer.test(endpoint, request)).rejects.toThrow(
|
|
1274
|
+
STExpect.errorWithCode('permission_denied'),
|
|
1275
|
+
);
|
|
1276
|
+
});
|
|
1277
|
+
});
|
|
1278
|
+
|
|
1279
|
+
describe('Default filtering', () => {
|
|
1280
|
+
test('A user without read permissions for all groups will only see members of membership groups', async () => {
|
|
1281
|
+
// Same test, but without giving the user permissions to read the group
|
|
1282
|
+
// Setup
|
|
1283
|
+
const role = PermissionRoleDetailed.create({
|
|
1284
|
+
name: 'Test Role',
|
|
1285
|
+
accessRights: [],
|
|
1286
|
+
});
|
|
1287
|
+
|
|
1288
|
+
const resources = new Map();
|
|
1289
|
+
|
|
1290
|
+
const organization = await new OrganizationFactory({ period, roles: [role] })
|
|
1291
|
+
.create();
|
|
1292
|
+
|
|
1293
|
+
const user = await new UserFactory({
|
|
1294
|
+
organization,
|
|
1295
|
+
permissions: Permissions.create({
|
|
1296
|
+
level: PermissionLevel.None,
|
|
1297
|
+
roles: [
|
|
1298
|
+
role,
|
|
1299
|
+
],
|
|
1300
|
+
resources,
|
|
1301
|
+
}),
|
|
1302
|
+
})
|
|
1303
|
+
.create();
|
|
1304
|
+
|
|
1305
|
+
const token = await Token.createToken(user);
|
|
1306
|
+
const member1 = await new MemberFactory({ }).create();
|
|
1307
|
+
const member2 = await new MemberFactory({ }).create();
|
|
1308
|
+
const member3 = await new MemberFactory({ }).create();
|
|
1309
|
+
const member4 = await new MemberFactory({ }).create();
|
|
1310
|
+
const group = await new GroupFactory({ organization, period }).create();
|
|
1311
|
+
const group2 = await new GroupFactory({ organization, period }).create();
|
|
1312
|
+
const group3 = await new GroupFactory({ organization, period }).create();
|
|
1313
|
+
|
|
1314
|
+
const group4 = await new GroupFactory({ organization, period, type: GroupType.EventRegistration }).create();
|
|
1315
|
+
|
|
1316
|
+
// Give permission for 2 / 3 groups + one event group
|
|
1317
|
+
resources.set(
|
|
1318
|
+
PermissionsResourceType.Groups, new Map([[
|
|
1319
|
+
group.id,
|
|
1320
|
+
ResourcePermissions.create({
|
|
1321
|
+
level: PermissionLevel.Read,
|
|
1322
|
+
}),
|
|
1323
|
+
], [
|
|
1324
|
+
group2.id,
|
|
1325
|
+
ResourcePermissions.create({
|
|
1326
|
+
level: PermissionLevel.Write,
|
|
1327
|
+
}),
|
|
1328
|
+
], [
|
|
1329
|
+
group4.id,
|
|
1330
|
+
ResourcePermissions.create({
|
|
1331
|
+
level: PermissionLevel.Read,
|
|
1332
|
+
}),
|
|
1333
|
+
]]),
|
|
1334
|
+
);
|
|
1335
|
+
|
|
1336
|
+
await user.save();
|
|
1337
|
+
|
|
1338
|
+
await new RegistrationFactory({ member: member1, group }).create();
|
|
1339
|
+
await new RegistrationFactory({ member: member2, group: group2 }).create();
|
|
1340
|
+
await new RegistrationFactory({ member: member3, group: group3 }).create();
|
|
1341
|
+
await new RegistrationFactory({ member: member4, group: group4 }).create();
|
|
1342
|
+
|
|
1343
|
+
// Try to request all members
|
|
1344
|
+
const request = Request.get({
|
|
1345
|
+
path: baseUrl,
|
|
1346
|
+
host: organization.getApiHost(),
|
|
1347
|
+
query: new LimitedFilteredRequest({
|
|
1348
|
+
limit: 10,
|
|
1349
|
+
}),
|
|
1350
|
+
headers: {
|
|
1351
|
+
authorization: 'Bearer ' + token.accessToken,
|
|
1352
|
+
},
|
|
1353
|
+
});
|
|
1354
|
+
|
|
1355
|
+
// Response only includes members registered in a membership group, not the event group
|
|
1356
|
+
const response = await testServer.test(endpoint, request);
|
|
1357
|
+
expect(response.status).toBe(200);
|
|
1358
|
+
expect(response.body.results.members).toHaveLength(2);
|
|
1359
|
+
// Check ids are matching without depending on ordering using jest extended
|
|
1360
|
+
expect(response.body.results.members).toIncludeSameMembers([
|
|
1361
|
+
expect.objectContaining({ id: member1.id }),
|
|
1362
|
+
expect.objectContaining({ id: member2.id }),
|
|
1363
|
+
]);
|
|
1364
|
+
});
|
|
1365
|
+
|
|
1366
|
+
test('A user with read permissions for all groups will also see members of event groups', async () => {
|
|
1367
|
+
// Same test, but without giving the user permissions to read the group
|
|
1368
|
+
// Setup
|
|
1369
|
+
const role = PermissionRoleDetailed.create({
|
|
1370
|
+
name: 'Test Role',
|
|
1371
|
+
accessRights: [],
|
|
1372
|
+
});
|
|
1373
|
+
|
|
1374
|
+
const resources = new Map();
|
|
1375
|
+
|
|
1376
|
+
const organization = await new OrganizationFactory({ period, roles: [role] })
|
|
1377
|
+
.create();
|
|
1378
|
+
|
|
1379
|
+
const user = await new UserFactory({
|
|
1380
|
+
organization,
|
|
1381
|
+
permissions: Permissions.create({
|
|
1382
|
+
level: PermissionLevel.None,
|
|
1383
|
+
roles: [
|
|
1384
|
+
role,
|
|
1385
|
+
],
|
|
1386
|
+
resources,
|
|
1387
|
+
}),
|
|
1388
|
+
})
|
|
1389
|
+
.create();
|
|
1390
|
+
|
|
1391
|
+
const token = await Token.createToken(user);
|
|
1392
|
+
const member1 = await new MemberFactory({ }).create();
|
|
1393
|
+
const member2 = await new MemberFactory({ }).create();
|
|
1394
|
+
const member3 = await new MemberFactory({ }).create();
|
|
1395
|
+
const member4 = await new MemberFactory({ }).create();
|
|
1396
|
+
const group = await new GroupFactory({ organization, period }).create();
|
|
1397
|
+
const group2 = await new GroupFactory({ organization, period }).create();
|
|
1398
|
+
const group3 = await new GroupFactory({ organization, period }).create();
|
|
1399
|
+
|
|
1400
|
+
const group4 = await new GroupFactory({ organization, period, type: GroupType.EventRegistration }).create();
|
|
1401
|
+
|
|
1402
|
+
// Give permission to all groups
|
|
1403
|
+
resources.set(
|
|
1404
|
+
PermissionsResourceType.Groups, new Map([[
|
|
1405
|
+
'',
|
|
1406
|
+
ResourcePermissions.create({
|
|
1407
|
+
level: PermissionLevel.Read,
|
|
1408
|
+
}),
|
|
1409
|
+
]]),
|
|
1410
|
+
);
|
|
1411
|
+
|
|
1412
|
+
await user.save();
|
|
1413
|
+
|
|
1414
|
+
await new RegistrationFactory({ member: member1, group }).create();
|
|
1415
|
+
await new RegistrationFactory({ member: member2, group: group2 }).create();
|
|
1416
|
+
await new RegistrationFactory({ member: member3, group: group3 }).create();
|
|
1417
|
+
await new RegistrationFactory({ member: member4, group: group4 }).create();
|
|
1418
|
+
|
|
1419
|
+
// Try to request all members
|
|
1420
|
+
const request = Request.get({
|
|
1421
|
+
path: baseUrl,
|
|
1422
|
+
host: organization.getApiHost(),
|
|
1423
|
+
query: new LimitedFilteredRequest({
|
|
1424
|
+
limit: 10,
|
|
1425
|
+
}),
|
|
1426
|
+
headers: {
|
|
1427
|
+
authorization: 'Bearer ' + token.accessToken,
|
|
1428
|
+
},
|
|
1429
|
+
});
|
|
1430
|
+
|
|
1431
|
+
// Response only includes members registered in a membership group, not the event group
|
|
1432
|
+
const response = await testServer.test(endpoint, request);
|
|
1433
|
+
expect(response.status).toBe(200);
|
|
1434
|
+
expect(response.body.results.members).toHaveLength(4);
|
|
1435
|
+
// Check ids are matching without depending on ordering using jest extended
|
|
1436
|
+
expect(response.body.results.members).toIncludeSameMembers([
|
|
1437
|
+
expect.objectContaining({ id: member1.id }),
|
|
1438
|
+
expect.objectContaining({ id: member2.id }),
|
|
1439
|
+
expect.objectContaining({ id: member3.id }),
|
|
1440
|
+
expect.objectContaining({ id: member4.id }),
|
|
1441
|
+
]);
|
|
1442
|
+
});
|
|
887
1443
|
});
|
|
888
1444
|
|
|
889
|
-
describe('
|
|
890
|
-
test('A user
|
|
891
|
-
|
|
892
|
-
|
|
1445
|
+
describe('Record answer filtering', () => {
|
|
1446
|
+
test('[REGRESSION] A user with minimal access can also view platform record answers in platform scope', async () => {
|
|
1447
|
+
/**
|
|
1448
|
+
* When fetching members via the admin api, without organization scope, we need to calculate which records to return and which not.
|
|
1449
|
+
* This test makes sure we check all registrations of the member to know whether we can return a platform record cateogry answer.
|
|
1450
|
+
*/
|
|
893
1451
|
const role = PermissionRoleDetailed.create({
|
|
894
|
-
name: '
|
|
1452
|
+
name: 'Stamhoofd verantwoordelijke',
|
|
1453
|
+
level: PermissionLevel.None,
|
|
895
1454
|
accessRights: [],
|
|
896
1455
|
});
|
|
897
1456
|
|
|
@@ -900,6 +1459,28 @@ describe('Endpoint.GetMembersEndpoint', () => {
|
|
|
900
1459
|
const organization = await new OrganizationFactory({ period, roles: [role] })
|
|
901
1460
|
.create();
|
|
902
1461
|
|
|
1462
|
+
const recordCategory = await new RecordCategoryFactory({
|
|
1463
|
+
records: [
|
|
1464
|
+
{
|
|
1465
|
+
type: RecordType.Text,
|
|
1466
|
+
},
|
|
1467
|
+
],
|
|
1468
|
+
}).create();
|
|
1469
|
+
|
|
1470
|
+
// Add a record category the admin does not have access to
|
|
1471
|
+
const controlRecordCategory = await new RecordCategoryFactory({
|
|
1472
|
+
records: [
|
|
1473
|
+
{
|
|
1474
|
+
type: RecordType.Text,
|
|
1475
|
+
},
|
|
1476
|
+
],
|
|
1477
|
+
}).create();
|
|
1478
|
+
|
|
1479
|
+
await initPlatformRecordCategory({ recordCategory });
|
|
1480
|
+
await initPlatformRecordCategory({ recordCategory: controlRecordCategory });
|
|
1481
|
+
const record = recordCategory.records[0];
|
|
1482
|
+
const controlRecord = controlRecordCategory.records[0];
|
|
1483
|
+
|
|
903
1484
|
const user = await new UserFactory({
|
|
904
1485
|
organization,
|
|
905
1486
|
permissions: Permissions.create({
|
|
@@ -914,29 +1495,32 @@ describe('Endpoint.GetMembersEndpoint', () => {
|
|
|
914
1495
|
|
|
915
1496
|
const token = await Token.createToken(user);
|
|
916
1497
|
const member1 = await new MemberFactory({ }).create();
|
|
917
|
-
const member2 = await new MemberFactory({ }).create();
|
|
918
|
-
const member3 = await new MemberFactory({ }).create();
|
|
919
|
-
const member4 = await new MemberFactory({ }).create();
|
|
920
1498
|
const group = await new GroupFactory({ organization, period }).create();
|
|
921
|
-
const group2 = await new GroupFactory({ organization, period }).create();
|
|
922
|
-
const group3 = await new GroupFactory({ organization, period }).create();
|
|
923
1499
|
|
|
924
|
-
|
|
1500
|
+
// Make sure member1 has answered the question
|
|
1501
|
+
const answer = RecordTextAnswer.create({ settings: record });
|
|
1502
|
+
answer.value = 'This has been answered';
|
|
1503
|
+
member1.details.recordAnswers.set(record.id, answer);
|
|
925
1504
|
|
|
926
|
-
|
|
1505
|
+
const controlAnswer = RecordTextAnswer.create({ settings: controlRecord });
|
|
1506
|
+
controlAnswer.value = 'This should be invisible';
|
|
1507
|
+
member1.details.recordAnswers.set(controlRecord.id, controlAnswer);
|
|
1508
|
+
await member1.save();
|
|
1509
|
+
|
|
1510
|
+
// Give read permission to the group
|
|
927
1511
|
resources.set(
|
|
928
1512
|
PermissionsResourceType.Groups, new Map([[
|
|
929
1513
|
group.id,
|
|
930
1514
|
ResourcePermissions.create({
|
|
931
1515
|
level: PermissionLevel.Read,
|
|
932
1516
|
}),
|
|
933
|
-
],
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
1517
|
+
]]),
|
|
1518
|
+
);
|
|
1519
|
+
|
|
1520
|
+
// Give permission to read the record category
|
|
1521
|
+
resources.set(
|
|
1522
|
+
PermissionsResourceType.RecordCategories, new Map([[
|
|
1523
|
+
recordCategory.id,
|
|
940
1524
|
ResourcePermissions.create({
|
|
941
1525
|
level: PermissionLevel.Read,
|
|
942
1526
|
}),
|
|
@@ -945,17 +1529,21 @@ describe('Endpoint.GetMembersEndpoint', () => {
|
|
|
945
1529
|
|
|
946
1530
|
await user.save();
|
|
947
1531
|
|
|
1532
|
+
// Register the memebr for group
|
|
948
1533
|
await new RegistrationFactory({ member: member1, group }).create();
|
|
949
|
-
await new RegistrationFactory({ member: member2, group: group2 }).create();
|
|
950
|
-
await new RegistrationFactory({ member: member3, group: group3 }).create();
|
|
951
|
-
await new RegistrationFactory({ member: member4, group: group4 }).create();
|
|
952
1534
|
|
|
953
1535
|
// Try to request all members
|
|
954
1536
|
const request = Request.get({
|
|
955
1537
|
path: baseUrl,
|
|
956
|
-
host: organization.getApiHost(),
|
|
957
1538
|
query: new LimitedFilteredRequest({
|
|
958
1539
|
limit: 10,
|
|
1540
|
+
filter: {
|
|
1541
|
+
registrations: {
|
|
1542
|
+
$elemMatch: {
|
|
1543
|
+
groupId: group.id,
|
|
1544
|
+
},
|
|
1545
|
+
},
|
|
1546
|
+
},
|
|
959
1547
|
}),
|
|
960
1548
|
headers: {
|
|
961
1549
|
authorization: 'Bearer ' + token.accessToken,
|
|
@@ -965,19 +1553,33 @@ describe('Endpoint.GetMembersEndpoint', () => {
|
|
|
965
1553
|
// Response only includes members registered in a membership group, not the event group
|
|
966
1554
|
const response = await testServer.test(endpoint, request);
|
|
967
1555
|
expect(response.status).toBe(200);
|
|
968
|
-
expect(response.body.results.members).toHaveLength(
|
|
1556
|
+
expect(response.body.results.members).toHaveLength(1);
|
|
969
1557
|
// Check ids are matching without depending on ordering using jest extended
|
|
970
1558
|
expect(response.body.results.members).toIncludeSameMembers([
|
|
971
1559
|
expect.objectContaining({ id: member1.id }),
|
|
972
|
-
expect.objectContaining({ id: member2.id }),
|
|
973
1560
|
]);
|
|
1561
|
+
|
|
1562
|
+
const returnedMember = response.body.results.members[0];
|
|
1563
|
+
|
|
1564
|
+
// Check only one record answer returned
|
|
1565
|
+
expect(returnedMember.details.recordAnswers.size).toEqual(1);
|
|
1566
|
+
|
|
1567
|
+
expect(returnedMember.details.recordAnswers.get(record.id)).toMatchObject({
|
|
1568
|
+
value: 'This has been answered',
|
|
1569
|
+
settings: expect.objectContaining({
|
|
1570
|
+
id: record.id,
|
|
1571
|
+
}),
|
|
1572
|
+
});
|
|
974
1573
|
});
|
|
975
1574
|
|
|
976
|
-
test('A user with
|
|
977
|
-
|
|
978
|
-
|
|
1575
|
+
test('[REGRESSION] A user with full access to a single organization can also view platform record answers in platform scope', async () => {
|
|
1576
|
+
/**
|
|
1577
|
+
* When fetching members via the admin api, without organization scope, we need to calculate which records to return and which not.
|
|
1578
|
+
* This test makes sure we check all registrations of the member to know whether we can return a platform record cateogry answer.
|
|
1579
|
+
*/
|
|
979
1580
|
const role = PermissionRoleDetailed.create({
|
|
980
|
-
name: '
|
|
1581
|
+
name: 'Stamhoofd verantwoordelijke',
|
|
1582
|
+
level: PermissionLevel.Full,
|
|
981
1583
|
accessRights: [],
|
|
982
1584
|
});
|
|
983
1585
|
|
|
@@ -986,6 +1588,28 @@ describe('Endpoint.GetMembersEndpoint', () => {
|
|
|
986
1588
|
const organization = await new OrganizationFactory({ period, roles: [role] })
|
|
987
1589
|
.create();
|
|
988
1590
|
|
|
1591
|
+
const recordCategory = await new RecordCategoryFactory({
|
|
1592
|
+
records: [
|
|
1593
|
+
{
|
|
1594
|
+
type: RecordType.Text,
|
|
1595
|
+
},
|
|
1596
|
+
],
|
|
1597
|
+
}).create();
|
|
1598
|
+
|
|
1599
|
+
// Add a record category the admin does not have access to
|
|
1600
|
+
const controlRecordCategory = await new RecordCategoryFactory({
|
|
1601
|
+
records: [
|
|
1602
|
+
{
|
|
1603
|
+
type: RecordType.Text,
|
|
1604
|
+
},
|
|
1605
|
+
],
|
|
1606
|
+
}).create();
|
|
1607
|
+
|
|
1608
|
+
await initPlatformRecordCategory({ recordCategory });
|
|
1609
|
+
await initPlatformRecordCategory({ recordCategory: controlRecordCategory });
|
|
1610
|
+
const record = recordCategory.records[0];
|
|
1611
|
+
const controlRecord = controlRecordCategory.records[0];
|
|
1612
|
+
|
|
989
1613
|
const user = await new UserFactory({
|
|
990
1614
|
organization,
|
|
991
1615
|
permissions: Permissions.create({
|
|
@@ -1000,38 +1624,188 @@ describe('Endpoint.GetMembersEndpoint', () => {
|
|
|
1000
1624
|
|
|
1001
1625
|
const token = await Token.createToken(user);
|
|
1002
1626
|
const member1 = await new MemberFactory({ }).create();
|
|
1003
|
-
const member2 = await new MemberFactory({ }).create();
|
|
1004
|
-
const member3 = await new MemberFactory({ }).create();
|
|
1005
|
-
const member4 = await new MemberFactory({ }).create();
|
|
1006
1627
|
const group = await new GroupFactory({ organization, period }).create();
|
|
1007
|
-
const group2 = await new GroupFactory({ organization, period }).create();
|
|
1008
|
-
const group3 = await new GroupFactory({ organization, period }).create();
|
|
1009
1628
|
|
|
1010
|
-
|
|
1629
|
+
// Make sure member1 has answered the question
|
|
1630
|
+
const answer = RecordTextAnswer.create({ settings: record });
|
|
1631
|
+
answer.value = 'This has been answered';
|
|
1632
|
+
member1.details.recordAnswers.set(record.id, answer);
|
|
1011
1633
|
|
|
1012
|
-
|
|
1634
|
+
const controlAnswer = RecordTextAnswer.create({ settings: controlRecord });
|
|
1635
|
+
controlAnswer.value = 'This should be invisible';
|
|
1636
|
+
member1.details.recordAnswers.set(controlRecord.id, controlAnswer);
|
|
1637
|
+
await member1.save();
|
|
1638
|
+
|
|
1639
|
+
// Register the memebr for group
|
|
1640
|
+
await new RegistrationFactory({ member: member1, group }).create();
|
|
1641
|
+
|
|
1642
|
+
// Try to request all members
|
|
1643
|
+
const request = Request.get({
|
|
1644
|
+
path: baseUrl,
|
|
1645
|
+
query: new LimitedFilteredRequest({
|
|
1646
|
+
limit: 10,
|
|
1647
|
+
filter: {
|
|
1648
|
+
registrations: {
|
|
1649
|
+
$elemMatch: {
|
|
1650
|
+
groupId: group.id,
|
|
1651
|
+
},
|
|
1652
|
+
},
|
|
1653
|
+
},
|
|
1654
|
+
}),
|
|
1655
|
+
headers: {
|
|
1656
|
+
authorization: 'Bearer ' + token.accessToken,
|
|
1657
|
+
},
|
|
1658
|
+
});
|
|
1659
|
+
|
|
1660
|
+
// Response only includes members registered in a membership group, not the event group
|
|
1661
|
+
const response = await testServer.test(endpoint, request);
|
|
1662
|
+
expect(response.status).toBe(200);
|
|
1663
|
+
expect(response.body.results.members).toHaveLength(1);
|
|
1664
|
+
// Check ids are matching without depending on ordering using jest extended
|
|
1665
|
+
expect(response.body.results.members).toIncludeSameMembers([
|
|
1666
|
+
expect.objectContaining({ id: member1.id }),
|
|
1667
|
+
]);
|
|
1668
|
+
|
|
1669
|
+
const returnedMember = response.body.results.members[0];
|
|
1670
|
+
|
|
1671
|
+
// Check only one record answer returned
|
|
1672
|
+
expect(returnedMember.details.recordAnswers.size).toEqual(2);
|
|
1673
|
+
});
|
|
1674
|
+
|
|
1675
|
+
test('[REGRESSION] A user with full access to a single organization cannot view platform record answers in platform scope of member of different organization', async () => {
|
|
1676
|
+
/**
|
|
1677
|
+
* Case:
|
|
1678
|
+
* - organization1 gives read access to the member, not the record category
|
|
1679
|
+
* - organization2 gives read access to the member and record category
|
|
1680
|
+
*
|
|
1681
|
+
* member is registered at organization1, and at organization2 (but in a group the user does not have access to)
|
|
1682
|
+
* -> cannot see answer
|
|
1683
|
+
*/
|
|
1684
|
+
const resources = new Map();
|
|
1685
|
+
const resources2 = new Map();
|
|
1686
|
+
|
|
1687
|
+
const organization = await new OrganizationFactory({ period, roles: [] }).create();
|
|
1688
|
+
const organization2 = await new OrganizationFactory({ period, roles: [] }).create();
|
|
1689
|
+
|
|
1690
|
+
const recordCategory = await new RecordCategoryFactory({
|
|
1691
|
+
records: [
|
|
1692
|
+
{
|
|
1693
|
+
type: RecordType.Text,
|
|
1694
|
+
},
|
|
1695
|
+
],
|
|
1696
|
+
}).create();
|
|
1697
|
+
|
|
1698
|
+
// Add a record category the admin does not have access to
|
|
1699
|
+
const controlRecordCategory = await new RecordCategoryFactory({
|
|
1700
|
+
records: [
|
|
1701
|
+
{
|
|
1702
|
+
type: RecordType.Text,
|
|
1703
|
+
},
|
|
1704
|
+
],
|
|
1705
|
+
}).create();
|
|
1706
|
+
|
|
1707
|
+
await initPlatformRecordCategory({ recordCategory });
|
|
1708
|
+
await initPlatformRecordCategory({ recordCategory: controlRecordCategory });
|
|
1709
|
+
const record = recordCategory.records[0];
|
|
1710
|
+
const controlRecord = controlRecordCategory.records[0];
|
|
1711
|
+
|
|
1712
|
+
const user = await new UserFactory({
|
|
1713
|
+
organization,
|
|
1714
|
+
permissions: Permissions.create({
|
|
1715
|
+
level: PermissionLevel.None,
|
|
1716
|
+
roles: [],
|
|
1717
|
+
resources,
|
|
1718
|
+
}),
|
|
1719
|
+
})
|
|
1720
|
+
.create();
|
|
1721
|
+
|
|
1722
|
+
const token = await Token.createToken(user);
|
|
1723
|
+
const member1 = await new MemberFactory({ }).create();
|
|
1724
|
+
const controlMember = await new MemberFactory({ }).create();
|
|
1725
|
+
const group = await new GroupFactory({ organization, period }).create();
|
|
1726
|
+
const unauthorizedGroup = await new GroupFactory({ organization, period }).create();
|
|
1727
|
+
const controlGroup = await new GroupFactory({ organization: organization2, period }).create();
|
|
1728
|
+
|
|
1729
|
+
// Make sure member1 has answered the question
|
|
1730
|
+
const answer = RecordTextAnswer.create({ settings: record });
|
|
1731
|
+
answer.value = 'This has been answered';
|
|
1732
|
+
member1.details.recordAnswers.set(record.id, answer);
|
|
1733
|
+
|
|
1734
|
+
const controlAnswer = RecordTextAnswer.create({ settings: controlRecord });
|
|
1735
|
+
controlAnswer.value = 'This should be invisible';
|
|
1736
|
+
member1.details.recordAnswers.set(controlRecord.id, controlAnswer);
|
|
1737
|
+
await member1.save();
|
|
1738
|
+
|
|
1739
|
+
// Make sure member1 has answered the question
|
|
1740
|
+
const controlMemberAnswer = RecordTextAnswer.create({ settings: record });
|
|
1741
|
+
controlMemberAnswer.value = 'This has been answered control';
|
|
1742
|
+
controlMember.details.recordAnswers.set(record.id, controlMemberAnswer);
|
|
1743
|
+
|
|
1744
|
+
const controlMemberControlAnswer = RecordTextAnswer.create({ settings: controlRecord });
|
|
1745
|
+
controlMemberControlAnswer.value = 'This should be invisible';
|
|
1746
|
+
controlMember.details.recordAnswers.set(controlRecord.id, controlMemberControlAnswer);
|
|
1747
|
+
await controlMember.save();
|
|
1748
|
+
|
|
1749
|
+
// Give read permission to the group
|
|
1013
1750
|
resources.set(
|
|
1014
1751
|
PermissionsResourceType.Groups, new Map([[
|
|
1015
|
-
|
|
1752
|
+
group.id,
|
|
1753
|
+
ResourcePermissions.create({
|
|
1754
|
+
level: PermissionLevel.Read,
|
|
1755
|
+
}),
|
|
1756
|
+
]]),
|
|
1757
|
+
);
|
|
1758
|
+
|
|
1759
|
+
// Do not give permission to read the record category
|
|
1760
|
+
|
|
1761
|
+
// Give read permission to the control group
|
|
1762
|
+
resources2.set(
|
|
1763
|
+
PermissionsResourceType.Groups, new Map([[
|
|
1764
|
+
controlGroup.id,
|
|
1765
|
+
ResourcePermissions.create({
|
|
1766
|
+
level: PermissionLevel.Read,
|
|
1767
|
+
}),
|
|
1768
|
+
]]),
|
|
1769
|
+
);
|
|
1770
|
+
|
|
1771
|
+
// Give permission to read the record category
|
|
1772
|
+
resources2.set(
|
|
1773
|
+
PermissionsResourceType.RecordCategories, new Map([[
|
|
1774
|
+
recordCategory.id,
|
|
1016
1775
|
ResourcePermissions.create({
|
|
1017
1776
|
level: PermissionLevel.Read,
|
|
1018
1777
|
}),
|
|
1019
1778
|
]]),
|
|
1020
1779
|
);
|
|
1021
1780
|
|
|
1781
|
+
// Add permission for organization2
|
|
1782
|
+
user.permissions!.organizationPermissions.set(organization2.id, Permissions.create({
|
|
1783
|
+
level: PermissionLevel.None,
|
|
1784
|
+
resources: resources2,
|
|
1785
|
+
}));
|
|
1022
1786
|
await user.save();
|
|
1023
1787
|
|
|
1788
|
+
// Register the memebr for group
|
|
1024
1789
|
await new RegistrationFactory({ member: member1, group }).create();
|
|
1025
|
-
await new RegistrationFactory({ member:
|
|
1026
|
-
await new RegistrationFactory({ member:
|
|
1027
|
-
await new RegistrationFactory({ member: member4, group: group4 }).create();
|
|
1790
|
+
await new RegistrationFactory({ member: member1, group: unauthorizedGroup }).create();
|
|
1791
|
+
await new RegistrationFactory({ member: controlMember, group: controlGroup }).create();
|
|
1028
1792
|
|
|
1029
1793
|
// Try to request all members
|
|
1030
1794
|
const request = Request.get({
|
|
1031
1795
|
path: baseUrl,
|
|
1032
|
-
host: organization.getApiHost(),
|
|
1033
1796
|
query: new LimitedFilteredRequest({
|
|
1034
1797
|
limit: 10,
|
|
1798
|
+
filter: {
|
|
1799
|
+
registrations: {
|
|
1800
|
+
$elemMatch: {
|
|
1801
|
+
groupId: {
|
|
1802
|
+
$in: [
|
|
1803
|
+
group.id, controlGroup.id,
|
|
1804
|
+
],
|
|
1805
|
+
},
|
|
1806
|
+
},
|
|
1807
|
+
},
|
|
1808
|
+
},
|
|
1035
1809
|
}),
|
|
1036
1810
|
headers: {
|
|
1037
1811
|
authorization: 'Bearer ' + token.accessToken,
|
|
@@ -1041,13 +1815,145 @@ describe('Endpoint.GetMembersEndpoint', () => {
|
|
|
1041
1815
|
// Response only includes members registered in a membership group, not the event group
|
|
1042
1816
|
const response = await testServer.test(endpoint, request);
|
|
1043
1817
|
expect(response.status).toBe(200);
|
|
1044
|
-
expect(response.body.results.members).toHaveLength(
|
|
1818
|
+
expect(response.body.results.members).toHaveLength(2);
|
|
1045
1819
|
// Check ids are matching without depending on ordering using jest extended
|
|
1046
1820
|
expect(response.body.results.members).toIncludeSameMembers([
|
|
1047
1821
|
expect.objectContaining({ id: member1.id }),
|
|
1048
|
-
expect.objectContaining({ id:
|
|
1049
|
-
|
|
1050
|
-
|
|
1822
|
+
expect.objectContaining({ id: controlMember.id }),
|
|
1823
|
+
]);
|
|
1824
|
+
|
|
1825
|
+
const returnedMember = response.body.results.members.find(m => m.id === member1.id)!;
|
|
1826
|
+
const returnedControlMember = response.body.results.members.find(m => m.id === controlMember.id)!;
|
|
1827
|
+
|
|
1828
|
+
expect(returnedMember).toBeDefined();
|
|
1829
|
+
expect(returnedControlMember).toBeDefined();
|
|
1830
|
+
|
|
1831
|
+
// Check only one record answer returned
|
|
1832
|
+
expect(returnedMember.details.recordAnswers.size).toEqual(0);
|
|
1833
|
+
expect(returnedControlMember.details.recordAnswers.size).toEqual(1);
|
|
1834
|
+
|
|
1835
|
+
expect(returnedControlMember.details.recordAnswers.get(record.id)).toMatchObject({
|
|
1836
|
+
value: 'This has been answered control',
|
|
1837
|
+
settings: expect.objectContaining({
|
|
1838
|
+
id: record.id,
|
|
1839
|
+
}),
|
|
1840
|
+
});
|
|
1841
|
+
});
|
|
1842
|
+
});
|
|
1843
|
+
|
|
1844
|
+
// Returned registrations in the members
|
|
1845
|
+
describe('Filtering registrations', () => {
|
|
1846
|
+
test('[REGRESSION] Deactivated registrations are returned when having access to that group', async () => {
|
|
1847
|
+
/**
|
|
1848
|
+
* Note: a deactivated registration doesn't give an admin access to a member, so we need an
|
|
1849
|
+
* extra registration so the admin does have acess to the member and can fetch it.
|
|
1850
|
+
* Next, we add two deactivated registrations: one for a group we have access to, one without. We should only see the one with access of course.
|
|
1851
|
+
*/
|
|
1852
|
+
|
|
1853
|
+
const role = PermissionRoleDetailed.create({
|
|
1854
|
+
name: 'Stamhoofd verantwoordelijke',
|
|
1855
|
+
level: PermissionLevel.None,
|
|
1856
|
+
accessRights: [],
|
|
1857
|
+
});
|
|
1858
|
+
|
|
1859
|
+
const resources = new Map();
|
|
1860
|
+
|
|
1861
|
+
const organization = await new OrganizationFactory({ period, roles: [role] })
|
|
1862
|
+
.create();
|
|
1863
|
+
const member = await new MemberFactory({ }).create();
|
|
1864
|
+
|
|
1865
|
+
// Group we have access for, but with an active registration
|
|
1866
|
+
const accessGroup = await new GroupFactory({
|
|
1867
|
+
organization,
|
|
1868
|
+
period,
|
|
1869
|
+
}).create();
|
|
1870
|
+
|
|
1871
|
+
// Deactivated, with access
|
|
1872
|
+
const deactivatedGroup = await new GroupFactory({
|
|
1873
|
+
organization,
|
|
1874
|
+
period,
|
|
1875
|
+
}).create();
|
|
1876
|
+
|
|
1877
|
+
// Deactivated, without access
|
|
1878
|
+
const deactivatedControlGroup = await new GroupFactory({
|
|
1879
|
+
organization,
|
|
1880
|
+
period,
|
|
1881
|
+
}).create();
|
|
1882
|
+
|
|
1883
|
+
// Create 3 registrations with each group
|
|
1884
|
+
const accessRegistration = await new RegistrationFactory({
|
|
1885
|
+
group: accessGroup,
|
|
1886
|
+
member,
|
|
1887
|
+
}).create();
|
|
1888
|
+
|
|
1889
|
+
const deactivatedRegistration = await new RegistrationFactory({
|
|
1890
|
+
group: deactivatedGroup,
|
|
1891
|
+
member,
|
|
1892
|
+
deactivatedAt: new Date(),
|
|
1893
|
+
}).create();
|
|
1894
|
+
|
|
1895
|
+
// deactivatedControlRegistration
|
|
1896
|
+
await new RegistrationFactory({
|
|
1897
|
+
group: deactivatedControlGroup,
|
|
1898
|
+
member,
|
|
1899
|
+
deactivatedAt: new Date(),
|
|
1900
|
+
}).create();
|
|
1901
|
+
|
|
1902
|
+
// Give read permission to the group
|
|
1903
|
+
resources.set(
|
|
1904
|
+
PermissionsResourceType.Groups, new Map([[
|
|
1905
|
+
accessGroup.id,
|
|
1906
|
+
ResourcePermissions.create({
|
|
1907
|
+
level: PermissionLevel.Read,
|
|
1908
|
+
}),
|
|
1909
|
+
], [
|
|
1910
|
+
deactivatedGroup.id, // not for the control group
|
|
1911
|
+
ResourcePermissions.create({
|
|
1912
|
+
level: PermissionLevel.Read,
|
|
1913
|
+
}),
|
|
1914
|
+
]]),
|
|
1915
|
+
);
|
|
1916
|
+
const user = await new UserFactory({
|
|
1917
|
+
organization,
|
|
1918
|
+
permissions: Permissions.create({
|
|
1919
|
+
level: PermissionLevel.None,
|
|
1920
|
+
roles: [
|
|
1921
|
+
role,
|
|
1922
|
+
],
|
|
1923
|
+
resources,
|
|
1924
|
+
}),
|
|
1925
|
+
})
|
|
1926
|
+
.create();
|
|
1927
|
+
|
|
1928
|
+
const token = await Token.createToken(user);
|
|
1929
|
+
|
|
1930
|
+
// Try to request all members at organization
|
|
1931
|
+
const request = Request.get({
|
|
1932
|
+
path: baseUrl,
|
|
1933
|
+
host: organization.getApiHost(),
|
|
1934
|
+
query: new LimitedFilteredRequest({
|
|
1935
|
+
limit: 10,
|
|
1936
|
+
}),
|
|
1937
|
+
headers: {
|
|
1938
|
+
authorization: 'Bearer ' + token.accessToken,
|
|
1939
|
+
},
|
|
1940
|
+
});
|
|
1941
|
+
|
|
1942
|
+
const response = await testServer.test(endpoint, request);
|
|
1943
|
+
expect(response.status).toBe(200);
|
|
1944
|
+
expect(response.body.results.members).toHaveLength(1);
|
|
1945
|
+
expect(response.body.results.members).toIncludeSameMembers([
|
|
1946
|
+
expect.objectContaining({ id: member.id }),
|
|
1947
|
+
]);
|
|
1948
|
+
|
|
1949
|
+
const returnedMember = response.body.results.members[0];
|
|
1950
|
+
|
|
1951
|
+
// Check only one record answer returned
|
|
1952
|
+
expect(returnedMember.registrations.length).toEqual(2);
|
|
1953
|
+
|
|
1954
|
+
expect(returnedMember.registrations).toIncludeSameMembers([
|
|
1955
|
+
expect.objectContaining({ id: accessRegistration.id, deactivatedAt: null }),
|
|
1956
|
+
expect.objectContaining({ id: deactivatedRegistration.id, deactivatedAt: deactivatedRegistration.deactivatedAt }),
|
|
1051
1957
|
]);
|
|
1052
1958
|
});
|
|
1053
1959
|
});
|