@oneuptime/common 12.0.28 → 12.0.29
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/Models/DatabaseModels/DashboardDomain.ts +43 -0
- package/Models/DatabaseModels/MonitorTemplate.ts +15 -3
- package/Models/DatabaseModels/StatusPageDomain.ts +43 -0
- package/Server/API/DashboardDomainAPI.ts +74 -0
- package/Server/API/MicrosoftTeamsAPI.ts +81 -180
- package/Server/API/StatusPageDomainAPI.ts +74 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1790200000000-AllowNullMonitorNameOnMonitorTemplate.ts +43 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1790300000000-AddCertificateReissueRequestedAtToDomains.ts +40 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Middleware/ProjectAuthorization.ts +6 -1
- package/Server/Services/DashboardDomainService.ts +131 -0
- package/Server/Services/LogPipelineProcessorService.ts +88 -0
- package/Server/Services/MonitorTemplateService.ts +70 -11
- package/Server/Services/StatusPageDomainService.ts +163 -3
- package/Server/Services/StatusPagePrivateUserSessionService.ts +197 -56
- package/Server/Types/Domain.ts +17 -0
- package/Server/Utils/APIKey/AccessPermission.ts +24 -0
- package/Server/Utils/Greenlock/Greenlock.ts +156 -58
- package/Server/Utils/LogPipelineProcessorValidation.ts +112 -0
- package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.ts +1094 -45
- package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.ts +153 -17
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.ts +23 -11
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.ts +0 -170
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Authorization.ts +158 -0
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +23 -11
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +35 -13
- package/Tests/App/Dashboard/MonitorTemplateColumnAndFacet.test.tsx +505 -0
- package/Tests/App/StatusPage/StatusPageLoginCodeBootstrap.test.ts +106 -0
- package/Tests/App/StatusPage/StatusPageLoginCodeUtil.test.ts +84 -0
- package/Tests/App/StatusPage/StatusPageRedirectSafety.test.ts +119 -0
- package/Tests/Models/MonitorTemplateMonitorNameColumn.test.ts +258 -0
- package/Tests/Server/API/CustomDomainReissueSslAPI.test.ts +469 -0
- package/Tests/Server/API/MicrosoftTeamsBotMessagesEndpoint.test.ts +729 -0
- package/Tests/Server/API/MicrosoftTeamsBotTestEndpoint.test.ts +26 -0
- package/Tests/Server/API/MicrosoftTeamsLegacyWebhookRemoval.test.ts +402 -0
- package/Tests/Server/Middleware/ProjectAuthorizationApiKeyMiddleware.test.ts +4 -0
- package/Tests/Server/Services/AddCertificateReissueRequestedAtToDomainsMigration.test.ts +287 -0
- package/Tests/Server/Services/CustomDomainCertificateReissue.test.ts +477 -0
- package/Tests/Server/Services/LogPipelineProcessorSaveValidation.test.ts +249 -0
- package/Tests/Server/Services/MonitorTemplateServiceBulkSync.test.ts +549 -0
- package/Tests/Server/Services/MonitorTemplateServiceNetworkDeviceSync.test.ts +16 -3
- package/Tests/Server/Services/NetworkDeviceAutoImportRuleEngineService.test.ts +57 -0
- package/Tests/Server/Services/SecurityEventLastErrorColumnWidth.test.ts +151 -15
- package/Tests/Server/Services/StatusPagePrivateUserSessionService.test.ts +587 -0
- package/Tests/Server/Types/Database/Permissions/ApiKeyFileAccess.test.ts +286 -0
- package/Tests/Server/Types/Database/Permissions/MonitorTemplateColumnAccess.test.ts +328 -0
- package/Tests/Server/Types/Database/QueryHelperFindWithSameTextAndIsNull.test.ts +83 -0
- package/Tests/Server/Utils/AI/SRE/ConfidenceSignal.test.ts +377 -0
- package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +16 -1
- package/Tests/Server/Utils/Greenlock/CertificateRenewalSchedule.test.ts +250 -0
- package/Tests/Server/Utils/LogPipelineProcessorValidation.test.ts +198 -0
- package/Tests/Server/Utils/SecurityEvent/ConnectorErrorMessage.test.ts +48 -9
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsAuth.test.ts +726 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsClient.test.ts +74 -11
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsErrorHandling.test.ts +1107 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPoller.test.ts +20 -3
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerFailureTaxonomy.test.ts +51 -11
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerHardening.test.ts +1246 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsRequestContract.test.ts +961 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsResponseParsing.test.ts +653 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsActionAuthorization.test.ts +880 -0
- package/Tests/Types/API/HTTPResponse.test.ts +175 -0
- package/Tests/Types/API/Route.test.ts +36 -4
- package/Tests/Types/Database/DatabaseProperty.test.ts +171 -0
- package/Tests/Types/DateLocalShortDateTimeString.test.ts +518 -0
- package/Tests/Types/DateLocalTimeString.test.ts +259 -0
- package/Tests/Types/DateUserPrefers12HourFormat.test.ts +316 -0
- package/Tests/UI/Components/LogsHistogram.test.tsx +41 -2
- package/Tests/UI/Components/LogsViewerEmptyStateTimeRange.test.tsx +212 -0
- package/Tests/UI/Components/TelemetryChartClockFormat.test.tsx +359 -0
- package/Tests/UI/Components/TimeRangePickerClockFormat.test.tsx +422 -0
- package/Tests/UI/Components/TimeRangePickerDropdown.test.tsx +146 -8
- package/Tests/UI/MicrosoftTeams/MicrosoftTeamsMessagingEndpointGuidance.test.tsx +520 -0
- package/Tests/UI/Utils/Navigation.test.ts +82 -0
- package/Tests/Utils/Array.test.ts +100 -0
- package/Tests/Utils/CertificateReissue.test.ts +225 -0
- package/Tests/Utils/Grok/Grok.test.ts +492 -0
- package/Tests/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.test.ts +181 -13
- package/Types/API/Route.ts +20 -5
- package/Types/Date.ts +143 -13
- package/Types/Rum/SessionReplay.ts +62 -1
- package/UI/Components/Date/TimeRangePickerDropdown.tsx +9 -5
- package/UI/Components/LogsViewer/LogsViewer.tsx +12 -7
- package/UI/Components/LogsViewer/components/HistogramTooltip.tsx +18 -17
- package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +21 -28
- package/UI/Components/LogsViewer/components/LogsHistogram.tsx +2 -4
- package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +2 -4
- package/UI/Components/TelemetryViewer/components/TelemetryHistogramTooltip.tsx +17 -16
- package/UI/Utils/Navigation.ts +42 -0
- package/Utils/Array.ts +37 -0
- package/Utils/CertificateReissue.ts +128 -0
- package/Utils/Grok/Grok.ts +461 -0
- package/Utils/Grok/GrokPatterns.ts +118 -0
- package/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.ts +58 -6
- package/build/dist/Models/DatabaseModels/DashboardDomain.js +44 -0
- package/build/dist/Models/DatabaseModels/DashboardDomain.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MonitorTemplate.js +17 -4
- package/build/dist/Models/DatabaseModels/MonitorTemplate.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageDomain.js +44 -0
- package/build/dist/Models/DatabaseModels/StatusPageDomain.js.map +1 -1
- package/build/dist/Server/API/DashboardDomainAPI.js +43 -1
- package/build/dist/Server/API/DashboardDomainAPI.js.map +1 -1
- package/build/dist/Server/API/MicrosoftTeamsAPI.js +74 -118
- package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageDomainAPI.js +43 -1
- package/build/dist/Server/API/StatusPageDomainAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790200000000-AllowNullMonitorNameOnMonitorTemplate.js +34 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790200000000-AllowNullMonitorNameOnMonitorTemplate.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790300000000-AddCertificateReissueRequestedAtToDomains.js +28 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790300000000-AddCertificateReissueRequestedAtToDomains.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/ProjectAuthorization.js +6 -1
- package/build/dist/Server/Middleware/ProjectAuthorization.js.map +1 -1
- package/build/dist/Server/Services/DashboardDomainService.js +104 -0
- package/build/dist/Server/Services/DashboardDomainService.js.map +1 -1
- package/build/dist/Server/Services/LogPipelineProcessorService.js +65 -0
- package/build/dist/Server/Services/LogPipelineProcessorService.js.map +1 -1
- package/build/dist/Server/Services/MonitorTemplateService.js +57 -11
- package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageDomainService.js +132 -3
- package/build/dist/Server/Services/StatusPageDomainService.js.map +1 -1
- package/build/dist/Server/Services/StatusPagePrivateUserSessionService.js +112 -32
- package/build/dist/Server/Services/StatusPagePrivateUserSessionService.js.map +1 -1
- package/build/dist/Server/Types/Domain.js +16 -0
- package/build/dist/Server/Types/Domain.js.map +1 -1
- package/build/dist/Server/Utils/APIKey/AccessPermission.js +24 -0
- package/build/dist/Server/Utils/APIKey/AccessPermission.js.map +1 -1
- package/build/dist/Server/Utils/Greenlock/Greenlock.js +110 -35
- package/build/dist/Server/Utils/Greenlock/Greenlock.js.map +1 -1
- package/build/dist/Server/Utils/LogPipelineProcessorValidation.js +77 -0
- package/build/dist/Server/Utils/LogPipelineProcessorValidation.js.map +1 -0
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js +777 -36
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js +108 -17
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js +17 -6
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js +0 -159
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Authorization.js +128 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Authorization.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +17 -6
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +25 -13
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/API/Route.js +16 -5
- package/build/dist/Types/API/Route.js.map +1 -1
- package/build/dist/Types/Date.js +115 -13
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/Types/Rum/SessionReplay.js +38 -0
- package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
- package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js +9 -5
- package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +12 -7
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/HistogramTooltip.js +15 -14
- package/build/dist/UI/Components/LogsViewer/components/HistogramTooltip.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +17 -22
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +2 -4
- package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +2 -4
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogramTooltip.js +15 -14
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogramTooltip.js.map +1 -1
- package/build/dist/UI/Utils/Navigation.js +30 -0
- package/build/dist/UI/Utils/Navigation.js.map +1 -1
- package/build/dist/Utils/Array.js +25 -0
- package/build/dist/Utils/Array.js.map +1 -1
- package/build/dist/Utils/CertificateReissue.js +97 -0
- package/build/dist/Utils/CertificateReissue.js.map +1 -0
- package/build/dist/Utils/Grok/Grok.js +324 -0
- package/build/dist/Utils/Grok/Grok.js.map +1 -0
- package/build/dist/Utils/Grok/GrokPatterns.js +112 -0
- package/build/dist/Utils/Grok/GrokPatterns.js.map +1 -0
- package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js +63 -3
- package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js.map +1 -1
- package/package.json +1 -1
- package/UI/Utils/JsonWebToken.ts +0 -14
- package/build/dist/UI/Utils/JsonWebToken.js +0 -10
- package/build/dist/UI/Utils/JsonWebToken.js.map +0 -1
|
@@ -0,0 +1,880 @@
|
|
|
1
|
+
import { afterEach, describe, expect, jest, test } from "@jest/globals";
|
|
2
|
+
import type { TurnContext } from "botbuilder";
|
|
3
|
+
import type { SpyInstance } from "jest-mock";
|
|
4
|
+
import { FindOperator } from "typeorm";
|
|
5
|
+
import Alert from "../../../../Models/DatabaseModels/Alert";
|
|
6
|
+
import AlertOwnerTeam from "../../../../Models/DatabaseModels/AlertOwnerTeam";
|
|
7
|
+
import Incident from "../../../../Models/DatabaseModels/Incident";
|
|
8
|
+
import IncidentOwnerTeam from "../../../../Models/DatabaseModels/IncidentOwnerTeam";
|
|
9
|
+
import Label from "../../../../Models/DatabaseModels/Label";
|
|
10
|
+
import AlertOwnerTeamService from "../../../../Server/Services/AlertOwnerTeamService";
|
|
11
|
+
import AlertOwnerUserService from "../../../../Server/Services/AlertOwnerUserService";
|
|
12
|
+
import AlertService from "../../../../Server/Services/AlertService";
|
|
13
|
+
import IncidentOwnerTeamService from "../../../../Server/Services/IncidentOwnerTeamService";
|
|
14
|
+
import IncidentOwnerUserService from "../../../../Server/Services/IncidentOwnerUserService";
|
|
15
|
+
import IncidentService from "../../../../Server/Services/IncidentService";
|
|
16
|
+
import ModelPermission from "../../../../Server/Types/Database/Permissions/Index";
|
|
17
|
+
import MicrosoftTeamsAlertActions from "../../../../Server/Utils/Workspace/MicrosoftTeams/Actions/Alert";
|
|
18
|
+
import MicrosoftTeamsActionAuthorization from "../../../../Server/Utils/Workspace/MicrosoftTeams/Actions/Authorization";
|
|
19
|
+
import MicrosoftTeamsIncidentActions from "../../../../Server/Utils/Workspace/MicrosoftTeams/Actions/Incident";
|
|
20
|
+
import {
|
|
21
|
+
MicrosoftTeamsAlertActionType,
|
|
22
|
+
MicrosoftTeamsIncidentActionType,
|
|
23
|
+
} from "../../../../Server/Utils/Workspace/MicrosoftTeams/Actions/ActionTypes";
|
|
24
|
+
import DatabaseCommonInteractionProps from "../../../../Types/BaseDatabase/DatabaseCommonInteractionProps";
|
|
25
|
+
import PermissionScope from "../../../../Types/Database/AccessControl/PermissionScope";
|
|
26
|
+
import NotAuthorizedException from "../../../../Types/Exception/NotAuthorizedException";
|
|
27
|
+
import ObjectID from "../../../../Types/ObjectID";
|
|
28
|
+
import Permission, {
|
|
29
|
+
UserGlobalAccessPermission,
|
|
30
|
+
UserPermission,
|
|
31
|
+
UserTenantAccessPermission,
|
|
32
|
+
} from "../../../../Types/Permission";
|
|
33
|
+
|
|
34
|
+
type PermissionInput = {
|
|
35
|
+
permission: Permission;
|
|
36
|
+
isBlockPermission?: boolean;
|
|
37
|
+
labelIds?: Array<ObjectID>;
|
|
38
|
+
scope?: PermissionScope;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
type IncidentActionTestCase = {
|
|
42
|
+
actionType: MicrosoftTeamsIncidentActionType;
|
|
43
|
+
mutation: "acknowledgeIncident" | "resolveIncident";
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
type AlertActionTestCase = {
|
|
47
|
+
actionType: MicrosoftTeamsAlertActionType;
|
|
48
|
+
mutation: "acknowledgeAlert" | "resolveAlert";
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
type IncidentMutationSpy = SpyInstance<
|
|
52
|
+
typeof IncidentService.acknowledgeIncident
|
|
53
|
+
>;
|
|
54
|
+
|
|
55
|
+
type AlertMutationSpy =
|
|
56
|
+
| SpyInstance<typeof AlertService.acknowledgeAlert>
|
|
57
|
+
| SpyInstance<typeof AlertService.resolveAlert>;
|
|
58
|
+
|
|
59
|
+
const projectId: ObjectID = ObjectID.generate();
|
|
60
|
+
const userId: ObjectID = ObjectID.generate();
|
|
61
|
+
|
|
62
|
+
function createDatabaseProps(
|
|
63
|
+
permissions: Array<PermissionInput>,
|
|
64
|
+
userTeamIds: Array<ObjectID> = [],
|
|
65
|
+
): DatabaseCommonInteractionProps {
|
|
66
|
+
const userPermissions: Array<UserPermission> = permissions.map(
|
|
67
|
+
(permissionInput: PermissionInput): UserPermission => {
|
|
68
|
+
return {
|
|
69
|
+
_type: "UserPermission",
|
|
70
|
+
permission: permissionInput.permission,
|
|
71
|
+
isBlockPermission: permissionInput.isBlockPermission || false,
|
|
72
|
+
labelIds: permissionInput.labelIds || [],
|
|
73
|
+
scope: permissionInput.scope,
|
|
74
|
+
};
|
|
75
|
+
},
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const tenantAccessPermission: UserTenantAccessPermission = {
|
|
79
|
+
_type: "UserTenantAccessPermission",
|
|
80
|
+
projectId,
|
|
81
|
+
permissions: userPermissions,
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const globalAccessPermission: UserGlobalAccessPermission = {
|
|
85
|
+
_type: "UserGlobalAccessPermission",
|
|
86
|
+
globalPermissions: [
|
|
87
|
+
Permission.Public,
|
|
88
|
+
Permission.User,
|
|
89
|
+
Permission.CurrentUser,
|
|
90
|
+
],
|
|
91
|
+
projectIds: [projectId],
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
userId,
|
|
96
|
+
tenantId: projectId,
|
|
97
|
+
userTeamIds,
|
|
98
|
+
userGlobalAccessPermission: globalAccessPermission,
|
|
99
|
+
userTenantAccessPermission: {
|
|
100
|
+
[projectId.toString()]: tenantAccessPermission,
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function createIncident(incidentId: ObjectID): Incident {
|
|
106
|
+
const incident: Incident = new Incident();
|
|
107
|
+
incident.id = incidentId;
|
|
108
|
+
incident.projectId = projectId;
|
|
109
|
+
incident.labels = [];
|
|
110
|
+
return incident;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function createAlert(alertId: ObjectID): Alert {
|
|
114
|
+
const alert: Alert = new Alert();
|
|
115
|
+
alert.id = alertId;
|
|
116
|
+
alert.projectId = projectId;
|
|
117
|
+
alert.labels = [];
|
|
118
|
+
return alert;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function getRawParameterValues(value: unknown): Array<unknown> {
|
|
122
|
+
const findOperator: FindOperator<unknown> = value as FindOperator<unknown>;
|
|
123
|
+
return Object.values(findOperator.objectLiteralParameters || {});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function createTurnContext(): TurnContext {
|
|
127
|
+
return {
|
|
128
|
+
activity: {},
|
|
129
|
+
deleteActivity: jest.fn(async (): Promise<void> => {}),
|
|
130
|
+
sendActivity: jest.fn(async (): Promise<void> => {}),
|
|
131
|
+
} as unknown as TurnContext;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
afterEach((): void => {
|
|
135
|
+
jest.restoreAllMocks();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
describe("MicrosoftTeamsActionAuthorization", (): void => {
|
|
139
|
+
test("allows an incident update when the user has an incident edit grant", async (): Promise<void> => {
|
|
140
|
+
const incidentId: ObjectID = ObjectID.generate();
|
|
141
|
+
const incident: Incident = createIncident(incidentId);
|
|
142
|
+
const databaseProps: DatabaseCommonInteractionProps = createDatabaseProps([
|
|
143
|
+
{ permission: Permission.EditProjectIncident },
|
|
144
|
+
{ permission: Permission.ReadProjectIncident },
|
|
145
|
+
]);
|
|
146
|
+
const findOneBySpy: SpyInstance<typeof IncidentService.findOneBy> = jest
|
|
147
|
+
.spyOn(IncidentService, "findOneBy")
|
|
148
|
+
.mockResolvedValue(incident);
|
|
149
|
+
|
|
150
|
+
await expect(
|
|
151
|
+
MicrosoftTeamsActionAuthorization.assertCanUpdateIncident({
|
|
152
|
+
incidentId,
|
|
153
|
+
projectId,
|
|
154
|
+
props: databaseProps,
|
|
155
|
+
}),
|
|
156
|
+
).resolves.toBeUndefined();
|
|
157
|
+
|
|
158
|
+
expect(findOneBySpy).toHaveBeenCalledTimes(2);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test("denies an incident update to a viewer with no update grant", async (): Promise<void> => {
|
|
162
|
+
const incidentId: ObjectID = ObjectID.generate();
|
|
163
|
+
const databaseProps: DatabaseCommonInteractionProps = createDatabaseProps([
|
|
164
|
+
{ permission: Permission.Viewer },
|
|
165
|
+
]);
|
|
166
|
+
const findOneBySpy: SpyInstance<typeof IncidentService.findOneBy> = jest
|
|
167
|
+
.spyOn(IncidentService, "findOneBy")
|
|
168
|
+
.mockResolvedValue(createIncident(incidentId));
|
|
169
|
+
|
|
170
|
+
await expect(
|
|
171
|
+
MicrosoftTeamsActionAuthorization.assertCanUpdateIncident({
|
|
172
|
+
incidentId,
|
|
173
|
+
projectId,
|
|
174
|
+
props: databaseProps,
|
|
175
|
+
}),
|
|
176
|
+
).rejects.toBeInstanceOf(NotAuthorizedException);
|
|
177
|
+
|
|
178
|
+
expect(findOneBySpy).toHaveBeenCalledTimes(1);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
test("honors an explicit incident update block even when another update grant exists", async (): Promise<void> => {
|
|
182
|
+
const incidentId: ObjectID = ObjectID.generate();
|
|
183
|
+
const databaseProps: DatabaseCommonInteractionProps = createDatabaseProps([
|
|
184
|
+
{ permission: Permission.ProjectMember },
|
|
185
|
+
{
|
|
186
|
+
permission: Permission.EditProjectIncident,
|
|
187
|
+
isBlockPermission: true,
|
|
188
|
+
},
|
|
189
|
+
]);
|
|
190
|
+
const findOneBySpy: SpyInstance<typeof IncidentService.findOneBy> = jest
|
|
191
|
+
.spyOn(IncidentService, "findOneBy")
|
|
192
|
+
.mockResolvedValue(createIncident(incidentId));
|
|
193
|
+
|
|
194
|
+
await expect(
|
|
195
|
+
MicrosoftTeamsActionAuthorization.assertCanUpdateIncident({
|
|
196
|
+
incidentId,
|
|
197
|
+
projectId,
|
|
198
|
+
props: databaseProps,
|
|
199
|
+
}),
|
|
200
|
+
).rejects.toBeInstanceOf(NotAuthorizedException);
|
|
201
|
+
|
|
202
|
+
expect(findOneBySpy).toHaveBeenCalledTimes(1);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test("allows an incident update when its label matches the user's scoped grant", async (): Promise<void> => {
|
|
206
|
+
const incidentId: ObjectID = ObjectID.generate();
|
|
207
|
+
const permittedLabelId: ObjectID = ObjectID.generate();
|
|
208
|
+
const incident: Incident = createIncident(incidentId);
|
|
209
|
+
incident.labels = [new Label(permittedLabelId)];
|
|
210
|
+
const databaseProps: DatabaseCommonInteractionProps = createDatabaseProps([
|
|
211
|
+
{
|
|
212
|
+
permission: Permission.EditProjectIncident,
|
|
213
|
+
labelIds: [permittedLabelId],
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
permission: Permission.ReadProjectIncident,
|
|
217
|
+
labelIds: [permittedLabelId],
|
|
218
|
+
},
|
|
219
|
+
]);
|
|
220
|
+
const findOneBySpy: SpyInstance<typeof IncidentService.findOneBy> = jest
|
|
221
|
+
.spyOn(IncidentService, "findOneBy")
|
|
222
|
+
.mockResolvedValue(incident);
|
|
223
|
+
|
|
224
|
+
await expect(
|
|
225
|
+
MicrosoftTeamsActionAuthorization.assertCanUpdateIncident({
|
|
226
|
+
incidentId,
|
|
227
|
+
projectId,
|
|
228
|
+
props: databaseProps,
|
|
229
|
+
}),
|
|
230
|
+
).resolves.toBeUndefined();
|
|
231
|
+
|
|
232
|
+
expect(findOneBySpy).toHaveBeenCalledTimes(2);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test("honors an incident update block scoped to the incident's label", async (): Promise<void> => {
|
|
236
|
+
const incidentId: ObjectID = ObjectID.generate();
|
|
237
|
+
const blockedLabelId: ObjectID = ObjectID.generate();
|
|
238
|
+
const incident: Incident = createIncident(incidentId);
|
|
239
|
+
incident.labels = [new Label(blockedLabelId)];
|
|
240
|
+
const databaseProps: DatabaseCommonInteractionProps = createDatabaseProps([
|
|
241
|
+
{ permission: Permission.ProjectMember },
|
|
242
|
+
{
|
|
243
|
+
permission: Permission.EditProjectIncident,
|
|
244
|
+
isBlockPermission: true,
|
|
245
|
+
labelIds: [blockedLabelId],
|
|
246
|
+
},
|
|
247
|
+
]);
|
|
248
|
+
const findOneBySpy: SpyInstance<typeof IncidentService.findOneBy> = jest
|
|
249
|
+
.spyOn(IncidentService, "findOneBy")
|
|
250
|
+
.mockResolvedValue(incident);
|
|
251
|
+
|
|
252
|
+
await expect(
|
|
253
|
+
MicrosoftTeamsActionAuthorization.assertCanUpdateIncident({
|
|
254
|
+
incidentId,
|
|
255
|
+
projectId,
|
|
256
|
+
props: databaseProps,
|
|
257
|
+
}),
|
|
258
|
+
).rejects.toBeInstanceOf(NotAuthorizedException);
|
|
259
|
+
|
|
260
|
+
expect(findOneBySpy).toHaveBeenCalledTimes(1);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
test("rejects an incident when the exact project and resource query is not permitted", async (): Promise<void> => {
|
|
264
|
+
const incidentId: ObjectID = ObjectID.generate();
|
|
265
|
+
const databaseProps: DatabaseCommonInteractionProps = createDatabaseProps([
|
|
266
|
+
{ permission: Permission.EditProjectIncident },
|
|
267
|
+
{ permission: Permission.ReadProjectIncident },
|
|
268
|
+
]);
|
|
269
|
+
const incident: Incident = createIncident(incidentId);
|
|
270
|
+
incident.isPrivate = true;
|
|
271
|
+
const queryPermissionSpy: SpyInstance<
|
|
272
|
+
typeof ModelPermission.checkUpdateQueryPermissions
|
|
273
|
+
> = jest.spyOn(ModelPermission, "checkUpdateQueryPermissions");
|
|
274
|
+
const findOneBySpy: SpyInstance<typeof IncidentService.findOneBy> = jest
|
|
275
|
+
.spyOn(IncidentService, "findOneBy")
|
|
276
|
+
.mockResolvedValueOnce(incident)
|
|
277
|
+
.mockResolvedValueOnce(null);
|
|
278
|
+
|
|
279
|
+
await expect(
|
|
280
|
+
MicrosoftTeamsActionAuthorization.assertCanUpdateIncident({
|
|
281
|
+
incidentId,
|
|
282
|
+
projectId,
|
|
283
|
+
props: databaseProps,
|
|
284
|
+
}),
|
|
285
|
+
).rejects.toBeInstanceOf(NotAuthorizedException);
|
|
286
|
+
|
|
287
|
+
expect(queryPermissionSpy).toHaveBeenCalledWith(
|
|
288
|
+
Incident,
|
|
289
|
+
expect.objectContaining({
|
|
290
|
+
_id: incidentId,
|
|
291
|
+
projectId,
|
|
292
|
+
}),
|
|
293
|
+
expect.objectContaining({
|
|
294
|
+
currentIncidentStateId: expect.any(ObjectID),
|
|
295
|
+
}),
|
|
296
|
+
databaseProps,
|
|
297
|
+
);
|
|
298
|
+
expect(findOneBySpy).toHaveBeenCalledTimes(2);
|
|
299
|
+
expect(findOneBySpy.mock.calls[0]?.[0].query).toEqual({
|
|
300
|
+
_id: incidentId,
|
|
301
|
+
projectId,
|
|
302
|
+
});
|
|
303
|
+
expect(findOneBySpy.mock.calls[1]?.[0].query).toEqual(
|
|
304
|
+
expect.objectContaining({
|
|
305
|
+
isPrivate: expect.anything(),
|
|
306
|
+
}),
|
|
307
|
+
);
|
|
308
|
+
expect(findOneBySpy.mock.calls[1]?.[0].props).toEqual({ isRoot: true });
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
test("allows an alert update when the user has an alert edit grant", async (): Promise<void> => {
|
|
312
|
+
const alertId: ObjectID = ObjectID.generate();
|
|
313
|
+
const alert: Alert = createAlert(alertId);
|
|
314
|
+
const databaseProps: DatabaseCommonInteractionProps = createDatabaseProps([
|
|
315
|
+
{ permission: Permission.EditAlert },
|
|
316
|
+
{ permission: Permission.ReadAlert },
|
|
317
|
+
]);
|
|
318
|
+
const findOneBySpy: SpyInstance<typeof AlertService.findOneBy> = jest
|
|
319
|
+
.spyOn(AlertService, "findOneBy")
|
|
320
|
+
.mockResolvedValue(alert);
|
|
321
|
+
|
|
322
|
+
await expect(
|
|
323
|
+
MicrosoftTeamsActionAuthorization.assertCanUpdateAlert({
|
|
324
|
+
alertId,
|
|
325
|
+
projectId,
|
|
326
|
+
props: databaseProps,
|
|
327
|
+
}),
|
|
328
|
+
).resolves.toBeUndefined();
|
|
329
|
+
|
|
330
|
+
expect(findOneBySpy).toHaveBeenCalledTimes(2);
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
test("denies an alert update to a viewer with no update grant", async (): Promise<void> => {
|
|
334
|
+
const alertId: ObjectID = ObjectID.generate();
|
|
335
|
+
const databaseProps: DatabaseCommonInteractionProps = createDatabaseProps([
|
|
336
|
+
{ permission: Permission.Viewer },
|
|
337
|
+
]);
|
|
338
|
+
const findOneBySpy: SpyInstance<typeof AlertService.findOneBy> = jest
|
|
339
|
+
.spyOn(AlertService, "findOneBy")
|
|
340
|
+
.mockResolvedValue(createAlert(alertId));
|
|
341
|
+
|
|
342
|
+
await expect(
|
|
343
|
+
MicrosoftTeamsActionAuthorization.assertCanUpdateAlert({
|
|
344
|
+
alertId,
|
|
345
|
+
projectId,
|
|
346
|
+
props: databaseProps,
|
|
347
|
+
}),
|
|
348
|
+
).rejects.toBeInstanceOf(NotAuthorizedException);
|
|
349
|
+
|
|
350
|
+
expect(findOneBySpy).toHaveBeenCalledTimes(1);
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
test("honors an explicit alert update block even when another update grant exists", async (): Promise<void> => {
|
|
354
|
+
const alertId: ObjectID = ObjectID.generate();
|
|
355
|
+
const databaseProps: DatabaseCommonInteractionProps = createDatabaseProps([
|
|
356
|
+
{ permission: Permission.ProjectMember },
|
|
357
|
+
{
|
|
358
|
+
permission: Permission.EditAlert,
|
|
359
|
+
isBlockPermission: true,
|
|
360
|
+
},
|
|
361
|
+
]);
|
|
362
|
+
const findOneBySpy: SpyInstance<typeof AlertService.findOneBy> = jest
|
|
363
|
+
.spyOn(AlertService, "findOneBy")
|
|
364
|
+
.mockResolvedValue(createAlert(alertId));
|
|
365
|
+
|
|
366
|
+
await expect(
|
|
367
|
+
MicrosoftTeamsActionAuthorization.assertCanUpdateAlert({
|
|
368
|
+
alertId,
|
|
369
|
+
projectId,
|
|
370
|
+
props: databaseProps,
|
|
371
|
+
}),
|
|
372
|
+
).rejects.toBeInstanceOf(NotAuthorizedException);
|
|
373
|
+
|
|
374
|
+
expect(findOneBySpy).toHaveBeenCalledTimes(1);
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
test("denies an alert update when its label is outside the user's scoped grant", async (): Promise<void> => {
|
|
378
|
+
const alertId: ObjectID = ObjectID.generate();
|
|
379
|
+
const alert: Alert = createAlert(alertId);
|
|
380
|
+
alert.labels = [new Label(ObjectID.generate())];
|
|
381
|
+
const databaseProps: DatabaseCommonInteractionProps = createDatabaseProps([
|
|
382
|
+
{
|
|
383
|
+
permission: Permission.EditAlert,
|
|
384
|
+
labelIds: [ObjectID.generate()],
|
|
385
|
+
},
|
|
386
|
+
]);
|
|
387
|
+
const findOneBySpy: SpyInstance<typeof AlertService.findOneBy> = jest
|
|
388
|
+
.spyOn(AlertService, "findOneBy")
|
|
389
|
+
.mockResolvedValue(alert);
|
|
390
|
+
|
|
391
|
+
await expect(
|
|
392
|
+
MicrosoftTeamsActionAuthorization.assertCanUpdateAlert({
|
|
393
|
+
alertId,
|
|
394
|
+
projectId,
|
|
395
|
+
props: databaseProps,
|
|
396
|
+
}),
|
|
397
|
+
).rejects.toBeInstanceOf(NotAuthorizedException);
|
|
398
|
+
|
|
399
|
+
expect(findOneBySpy).toHaveBeenCalledTimes(1);
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
test("rejects an alert when the exact project and resource query is not permitted", async (): Promise<void> => {
|
|
403
|
+
const alertId: ObjectID = ObjectID.generate();
|
|
404
|
+
const databaseProps: DatabaseCommonInteractionProps = createDatabaseProps([
|
|
405
|
+
{ permission: Permission.EditAlert },
|
|
406
|
+
{ permission: Permission.ReadAlert },
|
|
407
|
+
]);
|
|
408
|
+
const alert: Alert = createAlert(alertId);
|
|
409
|
+
alert.isPrivate = true;
|
|
410
|
+
const queryPermissionSpy: SpyInstance<
|
|
411
|
+
typeof ModelPermission.checkUpdateQueryPermissions
|
|
412
|
+
> = jest.spyOn(ModelPermission, "checkUpdateQueryPermissions");
|
|
413
|
+
const findOneBySpy: SpyInstance<typeof AlertService.findOneBy> = jest
|
|
414
|
+
.spyOn(AlertService, "findOneBy")
|
|
415
|
+
.mockResolvedValueOnce(alert)
|
|
416
|
+
.mockResolvedValueOnce(null);
|
|
417
|
+
|
|
418
|
+
await expect(
|
|
419
|
+
MicrosoftTeamsActionAuthorization.assertCanUpdateAlert({
|
|
420
|
+
alertId,
|
|
421
|
+
projectId,
|
|
422
|
+
props: databaseProps,
|
|
423
|
+
}),
|
|
424
|
+
).rejects.toBeInstanceOf(NotAuthorizedException);
|
|
425
|
+
|
|
426
|
+
expect(queryPermissionSpy).toHaveBeenCalledWith(
|
|
427
|
+
Alert,
|
|
428
|
+
expect.objectContaining({
|
|
429
|
+
_id: alertId,
|
|
430
|
+
projectId,
|
|
431
|
+
}),
|
|
432
|
+
expect.objectContaining({
|
|
433
|
+
currentAlertStateId: expect.any(ObjectID),
|
|
434
|
+
}),
|
|
435
|
+
databaseProps,
|
|
436
|
+
);
|
|
437
|
+
expect(findOneBySpy).toHaveBeenCalledTimes(2);
|
|
438
|
+
expect(findOneBySpy.mock.calls[0]?.[0].query).toEqual({
|
|
439
|
+
_id: alertId,
|
|
440
|
+
projectId,
|
|
441
|
+
});
|
|
442
|
+
expect(findOneBySpy.mock.calls[1]?.[0].query).toEqual(
|
|
443
|
+
expect.objectContaining({
|
|
444
|
+
isPrivate: expect.anything(),
|
|
445
|
+
}),
|
|
446
|
+
);
|
|
447
|
+
expect(findOneBySpy.mock.calls[1]?.[0].props).toEqual({ isRoot: true });
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
test("allows an incident owned by one of the user's accepted teams", async (): Promise<void> => {
|
|
451
|
+
const incidentId: ObjectID = ObjectID.generate();
|
|
452
|
+
const teamId: ObjectID = ObjectID.generate();
|
|
453
|
+
const incident: Incident = createIncident(incidentId);
|
|
454
|
+
incident.isPrivate = true;
|
|
455
|
+
const databaseProps: DatabaseCommonInteractionProps = createDatabaseProps(
|
|
456
|
+
[
|
|
457
|
+
{
|
|
458
|
+
permission: Permission.EditProjectIncident,
|
|
459
|
+
scope: PermissionScope.Owned,
|
|
460
|
+
},
|
|
461
|
+
{ permission: Permission.ReadProjectIncident },
|
|
462
|
+
],
|
|
463
|
+
[teamId],
|
|
464
|
+
);
|
|
465
|
+
const ownerTeam: IncidentOwnerTeam = new IncidentOwnerTeam();
|
|
466
|
+
ownerTeam.incidentId = incidentId;
|
|
467
|
+
ownerTeam.projectId = projectId;
|
|
468
|
+
ownerTeam.teamId = teamId;
|
|
469
|
+
|
|
470
|
+
const ownerUserSpy: SpyInstance<typeof IncidentOwnerUserService.findBy> =
|
|
471
|
+
jest.spyOn(IncidentOwnerUserService, "findBy").mockResolvedValue([]);
|
|
472
|
+
const ownerTeamSpy: SpyInstance<typeof IncidentOwnerTeamService.findBy> =
|
|
473
|
+
jest
|
|
474
|
+
.spyOn(IncidentOwnerTeamService, "findBy")
|
|
475
|
+
.mockResolvedValue([ownerTeam]);
|
|
476
|
+
const findOneBySpy: SpyInstance<typeof IncidentService.findOneBy> = jest
|
|
477
|
+
.spyOn(IncidentService, "findOneBy")
|
|
478
|
+
.mockResolvedValue(incident);
|
|
479
|
+
|
|
480
|
+
await expect(
|
|
481
|
+
MicrosoftTeamsActionAuthorization.assertCanUpdateIncident({
|
|
482
|
+
incidentId,
|
|
483
|
+
projectId,
|
|
484
|
+
props: databaseProps,
|
|
485
|
+
}),
|
|
486
|
+
).resolves.toBeUndefined();
|
|
487
|
+
|
|
488
|
+
expect(ownerUserSpy).toHaveBeenCalledWith(
|
|
489
|
+
expect.objectContaining({
|
|
490
|
+
query: {
|
|
491
|
+
userId,
|
|
492
|
+
projectId,
|
|
493
|
+
},
|
|
494
|
+
}),
|
|
495
|
+
);
|
|
496
|
+
expect(ownerTeamSpy).toHaveBeenCalledTimes(1);
|
|
497
|
+
expect(ownerTeamSpy.mock.calls[0]?.[0].query.projectId).toEqual(projectId);
|
|
498
|
+
expect(
|
|
499
|
+
getRawParameterValues(ownerTeamSpy.mock.calls[0]?.[0].query.teamId),
|
|
500
|
+
).toEqual([[teamId.toString()]]);
|
|
501
|
+
expect(findOneBySpy).toHaveBeenCalledTimes(2);
|
|
502
|
+
expect(findOneBySpy.mock.calls[1]?.[0].query).toEqual(
|
|
503
|
+
expect.objectContaining({
|
|
504
|
+
isPrivate: expect.anything(),
|
|
505
|
+
}),
|
|
506
|
+
);
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
test("rejects an owned-scoped incident when neither the user nor an accepted team owns it", async (): Promise<void> => {
|
|
510
|
+
const incidentId: ObjectID = ObjectID.generate();
|
|
511
|
+
const teamId: ObjectID = ObjectID.generate();
|
|
512
|
+
const incident: Incident = createIncident(incidentId);
|
|
513
|
+
const databaseProps: DatabaseCommonInteractionProps = createDatabaseProps(
|
|
514
|
+
[
|
|
515
|
+
{
|
|
516
|
+
permission: Permission.EditProjectIncident,
|
|
517
|
+
scope: PermissionScope.Owned,
|
|
518
|
+
},
|
|
519
|
+
{ permission: Permission.ReadProjectIncident },
|
|
520
|
+
],
|
|
521
|
+
[teamId],
|
|
522
|
+
);
|
|
523
|
+
|
|
524
|
+
jest.spyOn(IncidentOwnerUserService, "findBy").mockResolvedValue([]);
|
|
525
|
+
const ownerTeamSpy: SpyInstance<typeof IncidentOwnerTeamService.findBy> =
|
|
526
|
+
jest.spyOn(IncidentOwnerTeamService, "findBy").mockResolvedValue([]);
|
|
527
|
+
const findOneBySpy: SpyInstance<typeof IncidentService.findOneBy> = jest
|
|
528
|
+
.spyOn(IncidentService, "findOneBy")
|
|
529
|
+
.mockResolvedValueOnce(incident)
|
|
530
|
+
.mockResolvedValueOnce(null);
|
|
531
|
+
|
|
532
|
+
await expect(
|
|
533
|
+
MicrosoftTeamsActionAuthorization.assertCanUpdateIncident({
|
|
534
|
+
incidentId,
|
|
535
|
+
projectId,
|
|
536
|
+
props: databaseProps,
|
|
537
|
+
}),
|
|
538
|
+
).rejects.toBeInstanceOf(NotAuthorizedException);
|
|
539
|
+
|
|
540
|
+
expect(ownerTeamSpy).toHaveBeenCalledTimes(1);
|
|
541
|
+
expect(
|
|
542
|
+
getRawParameterValues(ownerTeamSpy.mock.calls[0]?.[0].query.teamId),
|
|
543
|
+
).toEqual([[teamId.toString()]]);
|
|
544
|
+
expect(findOneBySpy).toHaveBeenCalledTimes(2);
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
test("allows an alert owned by one of the user's accepted teams", async (): Promise<void> => {
|
|
548
|
+
const alertId: ObjectID = ObjectID.generate();
|
|
549
|
+
const teamId: ObjectID = ObjectID.generate();
|
|
550
|
+
const alert: Alert = createAlert(alertId);
|
|
551
|
+
alert.isPrivate = true;
|
|
552
|
+
const databaseProps: DatabaseCommonInteractionProps = createDatabaseProps(
|
|
553
|
+
[
|
|
554
|
+
{
|
|
555
|
+
permission: Permission.EditAlert,
|
|
556
|
+
scope: PermissionScope.Owned,
|
|
557
|
+
},
|
|
558
|
+
{ permission: Permission.ReadAlert },
|
|
559
|
+
],
|
|
560
|
+
[teamId],
|
|
561
|
+
);
|
|
562
|
+
const ownerTeam: AlertOwnerTeam = new AlertOwnerTeam();
|
|
563
|
+
ownerTeam.alertId = alertId;
|
|
564
|
+
ownerTeam.projectId = projectId;
|
|
565
|
+
ownerTeam.teamId = teamId;
|
|
566
|
+
|
|
567
|
+
const ownerUserSpy: SpyInstance<typeof AlertOwnerUserService.findBy> = jest
|
|
568
|
+
.spyOn(AlertOwnerUserService, "findBy")
|
|
569
|
+
.mockResolvedValue([]);
|
|
570
|
+
const ownerTeamSpy: SpyInstance<typeof AlertOwnerTeamService.findBy> = jest
|
|
571
|
+
.spyOn(AlertOwnerTeamService, "findBy")
|
|
572
|
+
.mockResolvedValue([ownerTeam]);
|
|
573
|
+
const findOneBySpy: SpyInstance<typeof AlertService.findOneBy> = jest
|
|
574
|
+
.spyOn(AlertService, "findOneBy")
|
|
575
|
+
.mockResolvedValue(alert);
|
|
576
|
+
|
|
577
|
+
await expect(
|
|
578
|
+
MicrosoftTeamsActionAuthorization.assertCanUpdateAlert({
|
|
579
|
+
alertId,
|
|
580
|
+
projectId,
|
|
581
|
+
props: databaseProps,
|
|
582
|
+
}),
|
|
583
|
+
).resolves.toBeUndefined();
|
|
584
|
+
|
|
585
|
+
expect(ownerUserSpy).toHaveBeenCalledWith(
|
|
586
|
+
expect.objectContaining({
|
|
587
|
+
query: {
|
|
588
|
+
userId,
|
|
589
|
+
projectId,
|
|
590
|
+
},
|
|
591
|
+
}),
|
|
592
|
+
);
|
|
593
|
+
expect(ownerTeamSpy).toHaveBeenCalledTimes(1);
|
|
594
|
+
expect(ownerTeamSpy.mock.calls[0]?.[0].query.projectId).toEqual(projectId);
|
|
595
|
+
expect(
|
|
596
|
+
getRawParameterValues(ownerTeamSpy.mock.calls[0]?.[0].query.teamId),
|
|
597
|
+
).toEqual([[teamId.toString()]]);
|
|
598
|
+
expect(findOneBySpy).toHaveBeenCalledTimes(2);
|
|
599
|
+
expect(findOneBySpy.mock.calls[1]?.[0].query).toEqual(
|
|
600
|
+
expect.objectContaining({
|
|
601
|
+
isPrivate: expect.anything(),
|
|
602
|
+
}),
|
|
603
|
+
);
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
test("rejects an owned-scoped alert when neither the user nor an accepted team owns it", async (): Promise<void> => {
|
|
607
|
+
const alertId: ObjectID = ObjectID.generate();
|
|
608
|
+
const teamId: ObjectID = ObjectID.generate();
|
|
609
|
+
const alert: Alert = createAlert(alertId);
|
|
610
|
+
const databaseProps: DatabaseCommonInteractionProps = createDatabaseProps(
|
|
611
|
+
[
|
|
612
|
+
{
|
|
613
|
+
permission: Permission.EditAlert,
|
|
614
|
+
scope: PermissionScope.Owned,
|
|
615
|
+
},
|
|
616
|
+
{ permission: Permission.ReadAlert },
|
|
617
|
+
],
|
|
618
|
+
[teamId],
|
|
619
|
+
);
|
|
620
|
+
|
|
621
|
+
jest.spyOn(AlertOwnerUserService, "findBy").mockResolvedValue([]);
|
|
622
|
+
const ownerTeamSpy: SpyInstance<typeof AlertOwnerTeamService.findBy> = jest
|
|
623
|
+
.spyOn(AlertOwnerTeamService, "findBy")
|
|
624
|
+
.mockResolvedValue([]);
|
|
625
|
+
const findOneBySpy: SpyInstance<typeof AlertService.findOneBy> = jest
|
|
626
|
+
.spyOn(AlertService, "findOneBy")
|
|
627
|
+
.mockResolvedValueOnce(alert)
|
|
628
|
+
.mockResolvedValueOnce(null);
|
|
629
|
+
|
|
630
|
+
await expect(
|
|
631
|
+
MicrosoftTeamsActionAuthorization.assertCanUpdateAlert({
|
|
632
|
+
alertId,
|
|
633
|
+
projectId,
|
|
634
|
+
props: databaseProps,
|
|
635
|
+
}),
|
|
636
|
+
).rejects.toBeInstanceOf(NotAuthorizedException);
|
|
637
|
+
|
|
638
|
+
expect(ownerTeamSpy).toHaveBeenCalledTimes(1);
|
|
639
|
+
expect(
|
|
640
|
+
getRawParameterValues(ownerTeamSpy.mock.calls[0]?.[0].query.teamId),
|
|
641
|
+
).toEqual([[teamId.toString()]]);
|
|
642
|
+
expect(findOneBySpy).toHaveBeenCalledTimes(2);
|
|
643
|
+
});
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
describe("MicrosoftTeamsIncidentActions authorization", (): void => {
|
|
647
|
+
const incidentActions: ReadonlyArray<IncidentActionTestCase> = [
|
|
648
|
+
{
|
|
649
|
+
actionType: MicrosoftTeamsIncidentActionType.AckIncident,
|
|
650
|
+
mutation: "acknowledgeIncident",
|
|
651
|
+
},
|
|
652
|
+
{
|
|
653
|
+
actionType: MicrosoftTeamsIncidentActionType.ResolveIncident,
|
|
654
|
+
mutation: "resolveIncident",
|
|
655
|
+
},
|
|
656
|
+
];
|
|
657
|
+
|
|
658
|
+
test.each(incidentActions)(
|
|
659
|
+
"$actionType does not mutate the incident when authorization is denied",
|
|
660
|
+
async ({ actionType, mutation }: IncidentActionTestCase): Promise<void> => {
|
|
661
|
+
const incidentId: ObjectID = ObjectID.generate();
|
|
662
|
+
const databaseProps: DatabaseCommonInteractionProps = createDatabaseProps(
|
|
663
|
+
[{ permission: Permission.Viewer }],
|
|
664
|
+
);
|
|
665
|
+
const turnContext: TurnContext = createTurnContext();
|
|
666
|
+
const authorizationError: NotAuthorizedException =
|
|
667
|
+
new NotAuthorizedException("Not authorized");
|
|
668
|
+
jest
|
|
669
|
+
.spyOn(MicrosoftTeamsActionAuthorization, "assertCanUpdateIncident")
|
|
670
|
+
.mockRejectedValue(authorizationError);
|
|
671
|
+
const mutationSpy: IncidentMutationSpy = jest
|
|
672
|
+
.spyOn(IncidentService, mutation)
|
|
673
|
+
.mockResolvedValue(createIncident(incidentId));
|
|
674
|
+
|
|
675
|
+
await expect(
|
|
676
|
+
MicrosoftTeamsIncidentActions.handleBotIncidentAction({
|
|
677
|
+
actionType,
|
|
678
|
+
actionValue: incidentId.toString(),
|
|
679
|
+
value: {},
|
|
680
|
+
projectId,
|
|
681
|
+
oneUptimeUserId: userId,
|
|
682
|
+
databaseProps,
|
|
683
|
+
turnContext,
|
|
684
|
+
}),
|
|
685
|
+
).rejects.toBe(authorizationError);
|
|
686
|
+
|
|
687
|
+
expect(mutationSpy).not.toHaveBeenCalled();
|
|
688
|
+
},
|
|
689
|
+
);
|
|
690
|
+
|
|
691
|
+
test.each(incidentActions)(
|
|
692
|
+
"$actionType mutates the incident exactly once after authorization succeeds",
|
|
693
|
+
async ({ actionType, mutation }: IncidentActionTestCase): Promise<void> => {
|
|
694
|
+
const incidentId: ObjectID = ObjectID.generate();
|
|
695
|
+
const databaseProps: DatabaseCommonInteractionProps = createDatabaseProps(
|
|
696
|
+
[{ permission: Permission.EditProjectIncident }],
|
|
697
|
+
);
|
|
698
|
+
const turnContext: TurnContext = createTurnContext();
|
|
699
|
+
const authorizationSpy: SpyInstance<
|
|
700
|
+
typeof MicrosoftTeamsActionAuthorization.assertCanUpdateIncident
|
|
701
|
+
> = jest
|
|
702
|
+
.spyOn(MicrosoftTeamsActionAuthorization, "assertCanUpdateIncident")
|
|
703
|
+
.mockResolvedValue();
|
|
704
|
+
const mutationSpy: IncidentMutationSpy = jest
|
|
705
|
+
.spyOn(IncidentService, mutation)
|
|
706
|
+
.mockResolvedValue(createIncident(incidentId));
|
|
707
|
+
|
|
708
|
+
await MicrosoftTeamsIncidentActions.handleBotIncidentAction({
|
|
709
|
+
actionType,
|
|
710
|
+
actionValue: incidentId.toString(),
|
|
711
|
+
value: {},
|
|
712
|
+
projectId,
|
|
713
|
+
oneUptimeUserId: userId,
|
|
714
|
+
databaseProps,
|
|
715
|
+
turnContext,
|
|
716
|
+
});
|
|
717
|
+
|
|
718
|
+
expect(authorizationSpy).toHaveBeenCalledTimes(1);
|
|
719
|
+
expect(authorizationSpy).toHaveBeenCalledWith({
|
|
720
|
+
incidentId,
|
|
721
|
+
projectId,
|
|
722
|
+
props: databaseProps,
|
|
723
|
+
});
|
|
724
|
+
expect(mutationSpy).toHaveBeenCalledTimes(1);
|
|
725
|
+
expect(mutationSpy).toHaveBeenCalledWith(incidentId, userId);
|
|
726
|
+
},
|
|
727
|
+
);
|
|
728
|
+
|
|
729
|
+
test("passes the authenticated user's database props when changing incident state", async (): Promise<void> => {
|
|
730
|
+
const incidentId: ObjectID = ObjectID.generate();
|
|
731
|
+
const incidentStateId: ObjectID = ObjectID.generate();
|
|
732
|
+
const databaseProps: DatabaseCommonInteractionProps = createDatabaseProps([
|
|
733
|
+
{ permission: Permission.EditProjectIncident },
|
|
734
|
+
]);
|
|
735
|
+
const turnContext: TurnContext = createTurnContext();
|
|
736
|
+
const updateSpy: SpyInstance<typeof IncidentService.updateOneById> = jest
|
|
737
|
+
.spyOn(IncidentService, "updateOneById")
|
|
738
|
+
.mockResolvedValue(1);
|
|
739
|
+
|
|
740
|
+
await MicrosoftTeamsIncidentActions.handleBotIncidentAction({
|
|
741
|
+
actionType: MicrosoftTeamsIncidentActionType.SubmitChangeIncidentState,
|
|
742
|
+
actionValue: incidentId.toString(),
|
|
743
|
+
value: {
|
|
744
|
+
incidentState: incidentStateId.toString(),
|
|
745
|
+
},
|
|
746
|
+
projectId,
|
|
747
|
+
oneUptimeUserId: userId,
|
|
748
|
+
databaseProps,
|
|
749
|
+
turnContext,
|
|
750
|
+
});
|
|
751
|
+
|
|
752
|
+
expect(updateSpy).toHaveBeenCalledTimes(1);
|
|
753
|
+
const updateArgs: Parameters<typeof IncidentService.updateOneById>[0] =
|
|
754
|
+
updateSpy.mock.calls[0]![0];
|
|
755
|
+
expect(updateArgs.id).toEqual(incidentId);
|
|
756
|
+
expect(updateArgs.data.currentIncidentStateId).toEqual(incidentStateId);
|
|
757
|
+
expect(updateArgs.props).toBe(databaseProps);
|
|
758
|
+
});
|
|
759
|
+
});
|
|
760
|
+
|
|
761
|
+
describe("MicrosoftTeamsAlertActions authorization", (): void => {
|
|
762
|
+
const alertActions: ReadonlyArray<AlertActionTestCase> = [
|
|
763
|
+
{
|
|
764
|
+
actionType: MicrosoftTeamsAlertActionType.AckAlert,
|
|
765
|
+
mutation: "acknowledgeAlert",
|
|
766
|
+
},
|
|
767
|
+
{
|
|
768
|
+
actionType: MicrosoftTeamsAlertActionType.ResolveAlert,
|
|
769
|
+
mutation: "resolveAlert",
|
|
770
|
+
},
|
|
771
|
+
];
|
|
772
|
+
|
|
773
|
+
test.each(alertActions)(
|
|
774
|
+
"$actionType does not mutate the alert when authorization is denied",
|
|
775
|
+
async ({ actionType, mutation }: AlertActionTestCase): Promise<void> => {
|
|
776
|
+
const alertId: ObjectID = ObjectID.generate();
|
|
777
|
+
const databaseProps: DatabaseCommonInteractionProps = createDatabaseProps(
|
|
778
|
+
[{ permission: Permission.Viewer }],
|
|
779
|
+
);
|
|
780
|
+
const turnContext: TurnContext = createTurnContext();
|
|
781
|
+
const authorizationError: NotAuthorizedException =
|
|
782
|
+
new NotAuthorizedException("Not authorized");
|
|
783
|
+
jest
|
|
784
|
+
.spyOn(MicrosoftTeamsActionAuthorization, "assertCanUpdateAlert")
|
|
785
|
+
.mockRejectedValue(authorizationError);
|
|
786
|
+
const mutationSpy: AlertMutationSpy =
|
|
787
|
+
mutation === "acknowledgeAlert"
|
|
788
|
+
? jest.spyOn(AlertService, mutation).mockResolvedValue()
|
|
789
|
+
: jest
|
|
790
|
+
.spyOn(AlertService, mutation)
|
|
791
|
+
.mockResolvedValue(createAlert(alertId));
|
|
792
|
+
|
|
793
|
+
await expect(
|
|
794
|
+
MicrosoftTeamsAlertActions.handleBotAlertAction({
|
|
795
|
+
actionType,
|
|
796
|
+
actionValue: alertId.toString(),
|
|
797
|
+
value: {},
|
|
798
|
+
projectId,
|
|
799
|
+
oneUptimeUserId: userId,
|
|
800
|
+
databaseProps,
|
|
801
|
+
turnContext,
|
|
802
|
+
}),
|
|
803
|
+
).rejects.toBe(authorizationError);
|
|
804
|
+
|
|
805
|
+
expect(mutationSpy).not.toHaveBeenCalled();
|
|
806
|
+
},
|
|
807
|
+
);
|
|
808
|
+
|
|
809
|
+
test.each(alertActions)(
|
|
810
|
+
"$actionType mutates the alert exactly once after authorization succeeds",
|
|
811
|
+
async ({ actionType, mutation }: AlertActionTestCase): Promise<void> => {
|
|
812
|
+
const alertId: ObjectID = ObjectID.generate();
|
|
813
|
+
const databaseProps: DatabaseCommonInteractionProps = createDatabaseProps(
|
|
814
|
+
[{ permission: Permission.EditAlert }],
|
|
815
|
+
);
|
|
816
|
+
const turnContext: TurnContext = createTurnContext();
|
|
817
|
+
const authorizationSpy: SpyInstance<
|
|
818
|
+
typeof MicrosoftTeamsActionAuthorization.assertCanUpdateAlert
|
|
819
|
+
> = jest
|
|
820
|
+
.spyOn(MicrosoftTeamsActionAuthorization, "assertCanUpdateAlert")
|
|
821
|
+
.mockResolvedValue();
|
|
822
|
+
const mutationSpy: AlertMutationSpy =
|
|
823
|
+
mutation === "acknowledgeAlert"
|
|
824
|
+
? jest.spyOn(AlertService, mutation).mockResolvedValue()
|
|
825
|
+
: jest
|
|
826
|
+
.spyOn(AlertService, mutation)
|
|
827
|
+
.mockResolvedValue(createAlert(alertId));
|
|
828
|
+
|
|
829
|
+
await MicrosoftTeamsAlertActions.handleBotAlertAction({
|
|
830
|
+
actionType,
|
|
831
|
+
actionValue: alertId.toString(),
|
|
832
|
+
value: {},
|
|
833
|
+
projectId,
|
|
834
|
+
oneUptimeUserId: userId,
|
|
835
|
+
databaseProps,
|
|
836
|
+
turnContext,
|
|
837
|
+
});
|
|
838
|
+
|
|
839
|
+
expect(authorizationSpy).toHaveBeenCalledTimes(1);
|
|
840
|
+
expect(authorizationSpy).toHaveBeenCalledWith({
|
|
841
|
+
alertId,
|
|
842
|
+
projectId,
|
|
843
|
+
props: databaseProps,
|
|
844
|
+
});
|
|
845
|
+
expect(mutationSpy).toHaveBeenCalledTimes(1);
|
|
846
|
+
expect(mutationSpy).toHaveBeenCalledWith(alertId, userId);
|
|
847
|
+
},
|
|
848
|
+
);
|
|
849
|
+
|
|
850
|
+
test("passes the authenticated user's database props when changing alert state", async (): Promise<void> => {
|
|
851
|
+
const alertId: ObjectID = ObjectID.generate();
|
|
852
|
+
const alertStateId: ObjectID = ObjectID.generate();
|
|
853
|
+
const databaseProps: DatabaseCommonInteractionProps = createDatabaseProps([
|
|
854
|
+
{ permission: Permission.EditAlert },
|
|
855
|
+
]);
|
|
856
|
+
const turnContext: TurnContext = createTurnContext();
|
|
857
|
+
const updateSpy: SpyInstance<typeof AlertService.updateOneById> = jest
|
|
858
|
+
.spyOn(AlertService, "updateOneById")
|
|
859
|
+
.mockResolvedValue(1);
|
|
860
|
+
|
|
861
|
+
await MicrosoftTeamsAlertActions.handleBotAlertAction({
|
|
862
|
+
actionType: MicrosoftTeamsAlertActionType.SubmitChangeAlertState,
|
|
863
|
+
actionValue: alertId.toString(),
|
|
864
|
+
value: {
|
|
865
|
+
alertState: alertStateId.toString(),
|
|
866
|
+
},
|
|
867
|
+
projectId,
|
|
868
|
+
oneUptimeUserId: userId,
|
|
869
|
+
databaseProps,
|
|
870
|
+
turnContext,
|
|
871
|
+
});
|
|
872
|
+
|
|
873
|
+
expect(updateSpy).toHaveBeenCalledTimes(1);
|
|
874
|
+
const updateArgs: Parameters<typeof AlertService.updateOneById>[0] =
|
|
875
|
+
updateSpy.mock.calls[0]![0];
|
|
876
|
+
expect(updateArgs.id).toEqual(alertId);
|
|
877
|
+
expect(updateArgs.data.currentAlertStateId).toEqual(alertStateId);
|
|
878
|
+
expect(updateArgs.props).toBe(databaseProps);
|
|
879
|
+
});
|
|
880
|
+
});
|