@oneuptime/common 12.0.17 → 12.0.19
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/EnterpriseLicense.ts +23 -0
- package/Models/DatabaseModels/Index.ts +4 -2
- package/Models/DatabaseModels/Monitor.ts +39 -9
- package/Models/DatabaseModels/UserMicrosoftTeams.ts +344 -0
- package/Models/DatabaseModels/UserNotificationRule.ts +144 -8
- package/Models/DatabaseModels/UserNotificationSetting.ts +34 -0
- package/Models/DatabaseModels/UserOnCallLogTimeline.ts +95 -0
- package/Models/DatabaseModels/UserSlack.ts +341 -0
- package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +9 -0
- package/Server/API/UserMicrosoftTeamsAPI.ts +127 -0
- package/Server/API/UserSlackAPI.ts +125 -0
- package/Server/EnvironmentConfig.ts +12 -107
- package/Server/Infrastructure/Postgres/SchemaMigrations/1788700000000-RemoveMarketingConversionUploadState.ts +33 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1788800000000-DropMarketingConversionAddEnterpriseLicenseEmail.ts +51 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1788900000000-RedactStoredMonitorIngestSecrets.ts +117 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789000000000-AddUserSlackAndMicrosoftTeams.ts +197 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +8 -0
- package/Server/Infrastructure/Queue.ts +7 -0
- package/Server/Services/BillingService.ts +8 -0
- package/Server/Services/EnterpriseLicenseService.ts +60 -0
- package/Server/Services/Index.ts +6 -2
- package/Server/Services/LlmLogService.ts +25 -9
- package/Server/Services/OnCallReadinessService.ts +86 -2
- package/Server/Services/ProjectService.ts +67 -0
- package/Server/Services/UserMicrosoftTeamsService.ts +208 -0
- package/Server/Services/UserNotificationMethodAdminService.ts +167 -5
- package/Server/Services/UserNotificationRuleAdminService.ts +40 -6
- package/Server/Services/UserNotificationRuleService.ts +589 -11
- package/Server/Services/UserNotificationSettingService.ts +138 -0
- package/Server/Services/UserService.ts +28 -0
- package/Server/Services/UserSlackService.ts +218 -0
- package/Server/Services/WorkspaceProjectAuthTokenService.ts +67 -1
- package/Server/Services/WorkspaceUserAuthTokenService.ts +73 -0
- package/Server/Services/WorkspaceUserNotificationService.ts +190 -0
- package/Server/Utils/Marketing/MarketingEventUtil.ts +145 -0
- package/Server/Utils/Marketing/MarketingEventWebhook.ts +114 -0
- package/Server/Utils/Monitor/MonitorLogUtil.ts +14 -1
- package/Server/Utils/Monitor/MonitorPayloadRedaction.ts +321 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +191 -4
- package/Tests/App/Dashboard/AdminNotificationRulesPage.test.tsx +22 -14
- package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +22 -11
- package/Tests/App/Dashboard/OnCallPreventionGuards.test.tsx +37 -1
- package/Tests/App/Dashboard/OnCallRulesTable.test.tsx +18 -10
- package/Tests/App/Dashboard/PayAsYouGoNotices.test.tsx +17 -0
- package/Tests/Models/DatabaseModels/DomainRoleTierCoverage.test.ts +27 -1
- package/Tests/Models/DatabaseModels/MonitorSecretKeyColumnAccessControl.test.ts +214 -0
- package/Tests/Server/API/OnCallReadinessAPI.test.ts +145 -26
- package/Tests/Server/API/UserNotificationMethodAdminAPI.test.ts +2 -2
- package/Tests/Server/Services/AIServiceDailyBudget.test.ts +11 -1
- package/Tests/Server/Services/AdminRuleEditGuards.test.ts +22 -8
- package/Tests/Server/Services/BillingService.test.ts +20 -0
- package/Tests/Server/Services/DeliverNotificationForRuleExtraction.test.ts +18 -2
- package/Tests/Server/Services/EpisodeRuleSeverityRepair.test.ts +5 -1
- package/Tests/Server/Services/LlmLogServiceTokenAggregateParams.test.ts +640 -0
- package/Tests/Server/Services/NotificationChannelEventCoverage.test.ts +246 -22
- package/Tests/Server/Services/NotificationDeletionImpact.test.ts +136 -8
- package/Tests/Server/Services/OnCallNotificationFallback.test.ts +191 -8
- package/Tests/Server/Services/OnCallReadinessService.test.ts +343 -18
- package/Tests/Server/Services/SeverityRuleBackfill.test.ts +5 -1
- package/Tests/Server/Services/UserMicrosoftTeamsService.test.ts +343 -0
- package/Tests/Server/Services/UserNotificationMethodAdminService.test.ts +209 -8
- package/Tests/Server/Services/UserNotificationRuleAdminGuards.test.ts +22 -6
- package/Tests/Server/Services/UserNotificationRuleDefaultCreation.test.ts +12 -6
- package/Tests/Server/Services/UserNotificationRuleExecuteItem.test.ts +37 -3
- package/Tests/Server/Services/UserNotificationRuleWorkspaceDelivery.test.ts +669 -0
- package/Tests/Server/Services/UserNotificationSettingWorkspaceChannels.test.ts +410 -0
- package/Tests/Server/Services/UserSlackService.test.ts +407 -0
- package/Tests/Server/Services/WorkspaceProjectAuthTokenDisconnectCascade.test.ts +224 -0
- package/Tests/Server/Services/WorkspaceUserAuthTokenNotificationMethodCascade.test.ts +189 -0
- package/Tests/Server/Services/WorkspaceUserNotificationService.test.ts +496 -0
- package/Tests/Server/Types/Database/Permissions/AdminNotificationRuleAccess.test.ts +30 -17
- package/Tests/Server/Types/Database/Permissions/CreateOwnershipScoping.test.ts +8 -3
- package/Tests/Server/Types/Database/Permissions/OwnerOnlyColumns.test.ts +72 -3
- package/Tests/Server/Types/Database/Permissions/UserNotificationRuleScoping.test.ts +22 -18
- package/Tests/Server/Types/Database/Permissions/WorkspaceMethodStampedColumnCreate.test.ts +178 -0
- package/Tests/Server/Utils/Marketing/MarketingEventUtil.test.ts +259 -0
- package/Tests/Server/Utils/Marketing/MarketingEventWebhook.test.ts +257 -0
- package/Tests/Server/Utils/Monitor/MonitorLogUtilRedaction.test.ts +300 -0
- package/Tests/Server/Utils/Monitor/MonitorPayloadRedaction.test.ts +381 -0
- package/Tests/Server/Utils/Runbook/RunbookExecutePermission.test.ts +228 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayErasureTombstone.test.ts +189 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayUsage.test.ts +205 -0
- package/Tests/Server/Utils/Telemetry/AppMetrics.test.ts +275 -0
- package/Tests/Server/Utils/Telemetry/TelemetryContext.test.ts +279 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsDirectMessage.test.ts +454 -0
- package/Tests/Types/Kubernetes/KubernetesObjectParsers.test.ts +794 -0
- package/Tests/Types/Kubernetes/KubernetesRightSizing.test.ts +53 -0
- package/Tests/Types/Measurement/MeasurementAggregationType.test.ts +100 -0
- package/Tests/Types/Monitor/MonitorStepConfigHelpers.test.ts +114 -0
- package/Tests/Types/Workspace/WorkspaceType.test.ts +53 -0
- package/Tests/UI/Utils/NotificationMethodUtil.test.ts +137 -9
- package/Tests/UI/Utils/PermissionGate.test.ts +105 -0
- package/Tests/Utils/Dashboard/Components/DashboardComponentDefaults.test.ts +257 -0
- package/Types/Billing/PayAsYouGoPricing.ts +1 -1
- package/Types/Marketing/MarketingEvent.ts +97 -0
- package/UI/Utils/NotificationMethodUtil.ts +41 -1
- package/UI/Utils/PermissionGate.ts +56 -0
- package/build/dist/Models/DatabaseModels/EnterpriseLicense.js +24 -0
- package/build/dist/Models/DatabaseModels/EnterpriseLicense.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +4 -2
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Monitor.js +39 -9
- package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserMicrosoftTeams.js +363 -0
- package/build/dist/Models/DatabaseModels/UserMicrosoftTeams.js.map +1 -0
- package/build/dist/Models/DatabaseModels/UserNotificationRule.js +145 -8
- package/build/dist/Models/DatabaseModels/UserNotificationRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserNotificationSetting.js +38 -0
- package/build/dist/Models/DatabaseModels/UserNotificationSetting.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +96 -0
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserSlack.js +361 -0
- package/build/dist/Models/DatabaseModels/UserSlack.js.map +1 -0
- package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
- package/build/dist/Server/API/UserMicrosoftTeamsAPI.js +82 -0
- package/build/dist/Server/API/UserMicrosoftTeamsAPI.js.map +1 -0
- package/build/dist/Server/API/UserSlackAPI.js +81 -0
- package/build/dist/Server/API/UserSlackAPI.js.map +1 -0
- package/build/dist/Server/EnvironmentConfig.js +10 -70
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788700000000-RemoveMarketingConversionUploadState.js +26 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788700000000-RemoveMarketingConversionUploadState.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788800000000-DropMarketingConversionAddEnterpriseLicenseEmail.js +37 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788800000000-DropMarketingConversionAddEnterpriseLicenseEmail.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788900000000-RedactStoredMonitorIngestSecrets.js +106 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788900000000-RedactStoredMonitorIngestSecrets.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789000000000-AddUserSlackAndMicrosoftTeams.js +78 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789000000000-AddUserSlackAndMicrosoftTeams.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +8 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/Queue.js +7 -0
- package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
- package/build/dist/Server/Services/BillingService.js +6 -0
- package/build/dist/Server/Services/BillingService.js.map +1 -1
- package/build/dist/Server/Services/EnterpriseLicenseService.js +65 -0
- package/build/dist/Server/Services/EnterpriseLicenseService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +6 -2
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/LlmLogService.js +25 -9
- package/build/dist/Server/Services/LlmLogService.js.map +1 -1
- package/build/dist/Server/Services/OnCallReadinessService.js +74 -2
- package/build/dist/Server/Services/OnCallReadinessService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +56 -0
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/UserMicrosoftTeamsService.js +183 -0
- package/build/dist/Server/Services/UserMicrosoftTeamsService.js.map +1 -0
- package/build/dist/Server/Services/UserNotificationMethodAdminService.js +138 -2
- package/build/dist/Server/Services/UserNotificationMethodAdminService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleAdminService.js +32 -4
- package/build/dist/Server/Services/UserNotificationRuleAdminService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleService.js +499 -67
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationSettingService.js +118 -0
- package/build/dist/Server/Services/UserNotificationSettingService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +26 -1
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Services/UserSlackService.js +195 -0
- package/build/dist/Server/Services/UserSlackService.js.map +1 -0
- package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js +64 -1
- package/build/dist/Server/Services/WorkspaceProjectAuthTokenService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceUserAuthTokenService.js +70 -0
- package/build/dist/Server/Services/WorkspaceUserAuthTokenService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceUserNotificationService.js +129 -0
- package/build/dist/Server/Services/WorkspaceUserNotificationService.js.map +1 -0
- package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js +94 -0
- package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js.map +1 -0
- package/build/dist/Server/Utils/Marketing/MarketingEventWebhook.js +93 -0
- package/build/dist/Server/Utils/Marketing/MarketingEventWebhook.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorLogUtil.js +12 -1
- package/build/dist/Server/Utils/Monitor/MonitorLogUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorPayloadRedaction.js +233 -0
- package/build/dist/Server/Utils/Monitor/MonitorPayloadRedaction.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +141 -4
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/Billing/PayAsYouGoPricing.js +1 -1
- package/build/dist/Types/Billing/PayAsYouGoPricing.js.map +1 -1
- package/build/dist/Types/Marketing/MarketingEvent.js +43 -0
- package/build/dist/Types/Marketing/MarketingEvent.js.map +1 -0
- package/build/dist/UI/Utils/NotificationMethodUtil.js +33 -1
- package/build/dist/UI/Utils/NotificationMethodUtil.js.map +1 -1
- package/build/dist/UI/Utils/PermissionGate.js +32 -0
- package/build/dist/UI/Utils/PermissionGate.js.map +1 -1
- package/package.json +1 -1
- package/Models/DatabaseModels/MarketingConversion.ts +0 -410
- package/Server/Services/MarketingConversionService.ts +0 -10
- package/Server/Utils/Marketing/ConversionUploadProvider.ts +0 -215
- package/Server/Utils/Marketing/ConversionUploadProviders.ts +0 -22
- package/Server/Utils/Marketing/Providers/GoogleAds.ts +0 -333
- package/Server/Utils/Marketing/Providers/LinkedIn.ts +0 -185
- package/Server/Utils/Marketing/Providers/Meta.ts +0 -182
- package/Server/Utils/Marketing/Providers/MicrosoftAds.ts +0 -223
- package/Server/Utils/Marketing/Providers/Reddit.ts +0 -208
- package/Tests/Server/Utils/Marketing/AdUploadableConversionTypes.test.ts +0 -315
- package/Tests/Server/Utils/Marketing/ConversionUploadProvider.test.ts +0 -300
- package/Tests/Server/Utils/Marketing/GoogleAds.test.ts +0 -592
- package/Tests/Server/Utils/Marketing/LinkedIn.test.ts +0 -282
- package/Tests/Server/Utils/Marketing/Meta.test.ts +0 -304
- package/Tests/Server/Utils/Marketing/MicrosoftAds.test.ts +0 -263
- package/Tests/Server/Utils/Marketing/Reddit.test.ts +0 -259
- package/Types/Marketing/MarketingConversion.ts +0 -53
- package/build/dist/Models/DatabaseModels/MarketingConversion.js +0 -445
- package/build/dist/Models/DatabaseModels/MarketingConversion.js.map +0 -1
- package/build/dist/Server/Services/MarketingConversionService.js +0 -9
- package/build/dist/Server/Services/MarketingConversionService.js.map +0 -1
- package/build/dist/Server/Utils/Marketing/ConversionUploadProvider.js +0 -93
- package/build/dist/Server/Utils/Marketing/ConversionUploadProvider.js.map +0 -1
- package/build/dist/Server/Utils/Marketing/ConversionUploadProviders.js +0 -20
- package/build/dist/Server/Utils/Marketing/ConversionUploadProviders.js.map +0 -1
- package/build/dist/Server/Utils/Marketing/Providers/GoogleAds.js +0 -221
- package/build/dist/Server/Utils/Marketing/Providers/GoogleAds.js.map +0 -1
- package/build/dist/Server/Utils/Marketing/Providers/LinkedIn.js +0 -137
- package/build/dist/Server/Utils/Marketing/Providers/LinkedIn.js.map +0 -1
- package/build/dist/Server/Utils/Marketing/Providers/Meta.js +0 -133
- package/build/dist/Server/Utils/Marketing/Providers/Meta.js.map +0 -1
- package/build/dist/Server/Utils/Marketing/Providers/MicrosoftAds.js +0 -143
- package/build/dist/Server/Utils/Marketing/Providers/MicrosoftAds.js.map +0 -1
- package/build/dist/Server/Utils/Marketing/Providers/Reddit.js +0 -145
- package/build/dist/Server/Utils/Marketing/Providers/Reddit.js.map +0 -1
- package/build/dist/Types/Marketing/MarketingConversion.js +0 -52
- package/build/dist/Types/Marketing/MarketingConversion.js.map +0 -1
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import UserCall from "../../../../../Models/DatabaseModels/UserCall";
|
|
2
2
|
import UserEmail from "../../../../../Models/DatabaseModels/UserEmail";
|
|
3
|
+
import UserMicrosoftTeams from "../../../../../Models/DatabaseModels/UserMicrosoftTeams";
|
|
3
4
|
import UserNotificationRule from "../../../../../Models/DatabaseModels/UserNotificationRule";
|
|
4
5
|
import UserPush from "../../../../../Models/DatabaseModels/UserPush";
|
|
6
|
+
import UserSlack from "../../../../../Models/DatabaseModels/UserSlack";
|
|
5
7
|
import UserSMS from "../../../../../Models/DatabaseModels/UserSMS";
|
|
6
8
|
import UserTelegram from "../../../../../Models/DatabaseModels/UserTelegram";
|
|
7
9
|
import UserWebhook from "../../../../../Models/DatabaseModels/UserWebhook";
|
|
@@ -38,12 +40,13 @@ import { describe, expect, test } from "@jest/globals";
|
|
|
38
40
|
* decide it: a member with no matching rule was silently unpageable, and the
|
|
39
41
|
* only person able to notice was the person who was not being notified.
|
|
40
42
|
*
|
|
41
|
-
* PHASE 3 SHIPPED THAT CAPABILITY ON THE RULE TABLE AND ONLY THERE. The
|
|
43
|
+
* PHASE 3 SHIPPED THAT CAPABILITY ON THE RULE TABLE AND ONLY THERE. The nine
|
|
42
44
|
* notification-method models - UserEmail, UserSMS, UserCall, UserPush,
|
|
43
|
-
* UserWhatsApp, UserTelegram,
|
|
44
|
-
* verb, so an administrator reading one of THEM
|
|
45
|
-
* other member. That split is not a leftover; it
|
|
46
|
-
* design because the alternative was tried and
|
|
45
|
+
* UserWhatsApp, UserTelegram, UserSlack, UserMicrosoftTeams, UserWebhook -
|
|
46
|
+
* stay CurrentUser-only on every verb, so an administrator reading one of THEM
|
|
47
|
+
* is refused exactly like any other member. That split is not a leftover; it
|
|
48
|
+
* is the design, and it is the design because the alternative was tried and
|
|
49
|
+
* did not hold.
|
|
47
50
|
*
|
|
48
51
|
* WHY THE METHOD TABLES COULD NOT BE WIDENED. Widening a read on a model that
|
|
49
52
|
* scopes rows by owner is a privilege change rather than a convenience change,
|
|
@@ -77,7 +80,7 @@ import { describe, expect, test } from "@jest/globals";
|
|
|
77
80
|
*
|
|
78
81
|
* - the rule table lifts for an administrator and not for a plain member, on
|
|
79
82
|
* all four verbs;
|
|
80
|
-
* - the
|
|
83
|
+
* - the nine method models lift for NOBODY on ANY verb, and the read case is
|
|
81
84
|
* asserted in both directions because it is the one that shipped widened
|
|
82
85
|
* once already;
|
|
83
86
|
* - the rule's method FK id columns are admin-readable while the method
|
|
@@ -147,9 +150,9 @@ const NEW_GRANULAR_PERMISSIONS: Array<Permission> = [
|
|
|
147
150
|
];
|
|
148
151
|
|
|
149
152
|
/*
|
|
150
|
-
* The
|
|
151
|
-
* runs against all
|
|
152
|
-
*
|
|
153
|
+
* The nine notification methods a rule can point at. Every method assertion
|
|
154
|
+
* runs against all nine rather than against a representative one: they are
|
|
155
|
+
* nine copies of one decorator shape, and a widening applied to one of them is
|
|
153
156
|
* exactly the kind of thing that survives review.
|
|
154
157
|
*/
|
|
155
158
|
const METHOD_MODELS: Array<[string, ModelConstructor]> = [
|
|
@@ -159,6 +162,8 @@ const METHOD_MODELS: Array<[string, ModelConstructor]> = [
|
|
|
159
162
|
["UserPush", UserPush],
|
|
160
163
|
["UserWhatsApp", UserWhatsApp],
|
|
161
164
|
["UserTelegram", UserTelegram],
|
|
165
|
+
["UserSlack", UserSlack],
|
|
166
|
+
["UserMicrosoftTeams", UserMicrosoftTeams],
|
|
162
167
|
["UserWebhook", UserWebhook],
|
|
163
168
|
];
|
|
164
169
|
|
|
@@ -478,10 +483,10 @@ describe("the rule table - an administrator's row scope lifts, a member's does n
|
|
|
478
483
|
});
|
|
479
484
|
});
|
|
480
485
|
|
|
481
|
-
describe("the
|
|
486
|
+
describe("the nine methods - an administrator may not look either", () => {
|
|
482
487
|
/*
|
|
483
488
|
* THE CORE DECISION OF THIS PHASE AS IT FINALLY SHIPPED, AND THE ONE MOST
|
|
484
|
-
* LIKELY TO BE "TIDIED UP" LATER, because
|
|
489
|
+
* LIKELY TO BE "TIDIED UP" LATER, because nine models that differ from their
|
|
485
490
|
* sibling in all four lists look like an oversight rather than a decision.
|
|
486
491
|
*
|
|
487
492
|
* The read half of these lists was widened once and then reverted. The reason
|
|
@@ -1243,7 +1248,7 @@ describe("the credentials behind a notification method, guarded by the table sco
|
|
|
1243
1248
|
* column-level decision to verify per column and per query shape, because
|
|
1244
1249
|
* there is no query on these models that reaches another member's ROW - so
|
|
1245
1250
|
* every column on that row, credential or label, is out of reach for the same
|
|
1246
|
-
* single reason. The
|
|
1251
|
+
* single reason. The columns are still enumerated rather than reduced to
|
|
1247
1252
|
* one representative, because they are what a reader comes here looking for
|
|
1248
1253
|
* and because "the row is unreachable" is a claim worth checking against the
|
|
1249
1254
|
* things that would actually matter if it were false.
|
|
@@ -1251,8 +1256,10 @@ describe("the credentials behind a notification method, guarded by the table sco
|
|
|
1251
1256
|
* The columns: the webhook URL and its signing secret (both bearer
|
|
1252
1257
|
* credentials - anyone holding them can impersonate OneUptime to the member's
|
|
1253
1258
|
* endpoint), the push device token, the Telegram chat id an unverified bot
|
|
1254
|
-
* message can be addressed to,
|
|
1255
|
-
*
|
|
1259
|
+
* message can be addressed to, the verification code that turns possession
|
|
1260
|
+
* of that chat id into a verified method, and the Slack member id and
|
|
1261
|
+
* Microsoft Teams user id the project's workspace bots deliver direct
|
|
1262
|
+
* messages to.
|
|
1256
1263
|
*/
|
|
1257
1264
|
|
|
1258
1265
|
/*
|
|
@@ -1266,6 +1273,8 @@ describe("the credentials behind a notification method, guarded by the table sco
|
|
|
1266
1273
|
["UserPush", "deviceToken", UserPush],
|
|
1267
1274
|
["UserTelegram", "telegramChatId", UserTelegram],
|
|
1268
1275
|
["UserTelegram", "verificationCode", UserTelegram],
|
|
1276
|
+
["UserSlack", "slackUserId", UserSlack],
|
|
1277
|
+
["UserMicrosoftTeams", "microsoftTeamsUserId", UserMicrosoftTeams],
|
|
1269
1278
|
];
|
|
1270
1279
|
|
|
1271
1280
|
test.each(CREDENTIAL_COLUMNS)(
|
|
@@ -1375,8 +1384,8 @@ describe("the rule relation - an administrator reads the FK, never the method be
|
|
|
1375
1384
|
*
|
|
1376
1385
|
* Neither check can see the query, so neither can tell whose row this is.
|
|
1377
1386
|
* That leaves the outer column list as the only place the decision can be
|
|
1378
|
-
* made, and it is why the
|
|
1379
|
-
* UserNotificationRule are `read: [Permission.CurrentUser]` while the
|
|
1387
|
+
* made, and it is why the nine method RELATION columns on
|
|
1388
|
+
* UserNotificationRule are `read: [Permission.CurrentUser]` while the nine
|
|
1380
1389
|
* FK ID columns beside them are admin-readable. The pairing is the design in
|
|
1381
1390
|
* one line: an administrator may learn WHICH method a rule points at, and may
|
|
1382
1391
|
* re-point it at a different one, without ever learning what the method IS.
|
|
@@ -1397,6 +1406,8 @@ describe("the rule relation - an administrator reads the FK, never the method be
|
|
|
1397
1406
|
"userPushId",
|
|
1398
1407
|
"userWhatsAppId",
|
|
1399
1408
|
"userTelegramId",
|
|
1409
|
+
"userSlackId",
|
|
1410
|
+
"userMicrosoftTeamsId",
|
|
1400
1411
|
"userWebhookId",
|
|
1401
1412
|
];
|
|
1402
1413
|
|
|
@@ -1412,6 +1423,8 @@ describe("the rule relation - an administrator reads the FK, never the method be
|
|
|
1412
1423
|
["userPush", "deviceToken"],
|
|
1413
1424
|
["userWhatsApp", "phone"],
|
|
1414
1425
|
["userTelegram", "telegramChatId"],
|
|
1426
|
+
["userSlack", "slackUserId"],
|
|
1427
|
+
["userMicrosoftTeams", "microsoftTeamsUserId"],
|
|
1415
1428
|
["userWebhook", "webhookUrl"],
|
|
1416
1429
|
];
|
|
1417
1430
|
|
|
@@ -1420,7 +1433,7 @@ describe("the rule relation - an administrator reads the FK, never the method be
|
|
|
1420
1433
|
async (grant: Permission): Promise<void> => {
|
|
1421
1434
|
/*
|
|
1422
1435
|
* The capability half, and it has to pass or the refusal below has taken
|
|
1423
|
-
* the feature with it. Selecting all
|
|
1436
|
+
* the feature with it. Selecting all nine ids at once rather than one
|
|
1424
1437
|
* representative, because the admin surface renders the whole row and a
|
|
1425
1438
|
* single narrowed column would break the page while six tests still
|
|
1426
1439
|
* passed.
|
|
@@ -4,12 +4,14 @@ import User from "../../../../../Models/DatabaseModels/User";
|
|
|
4
4
|
import UserCall from "../../../../../Models/DatabaseModels/UserCall";
|
|
5
5
|
import UserEmail from "../../../../../Models/DatabaseModels/UserEmail";
|
|
6
6
|
import UserIncomingCallNumber from "../../../../../Models/DatabaseModels/UserIncomingCallNumber";
|
|
7
|
+
import UserMicrosoftTeams from "../../../../../Models/DatabaseModels/UserMicrosoftTeams";
|
|
7
8
|
import UserNotificationRule from "../../../../../Models/DatabaseModels/UserNotificationRule";
|
|
8
9
|
import UserNotificationSetting from "../../../../../Models/DatabaseModels/UserNotificationSetting";
|
|
9
10
|
import UserOnCallLog from "../../../../../Models/DatabaseModels/UserOnCallLog";
|
|
10
11
|
import UserPush from "../../../../../Models/DatabaseModels/UserPush";
|
|
11
12
|
import UserSMS from "../../../../../Models/DatabaseModels/UserSMS";
|
|
12
13
|
import UserSession from "../../../../../Models/DatabaseModels/UserSession";
|
|
14
|
+
import UserSlack from "../../../../../Models/DatabaseModels/UserSlack";
|
|
13
15
|
import UserTelegram from "../../../../../Models/DatabaseModels/UserTelegram";
|
|
14
16
|
import UserTotpAuth from "../../../../../Models/DatabaseModels/UserTotpAuth";
|
|
15
17
|
import UserWebAuthn from "../../../../../Models/DatabaseModels/UserWebAuthn";
|
|
@@ -42,8 +44,8 @@ import { describe, expect, test } from "@jest/globals";
|
|
|
42
44
|
* allow-list intersection over column names. So on any model whose create list
|
|
43
45
|
* is CurrentUser-only, a caller could write a row owned by somebody else.
|
|
44
46
|
*
|
|
45
|
-
*
|
|
46
|
-
* resource: notification rules, all
|
|
47
|
+
* Sixteen models have that shape, and every one of them is a "my own X"
|
|
48
|
+
* resource: notification rules, all nine notification methods behind them,
|
|
47
49
|
* sessions, TOTP and WebAuthn credentials, workspace tokens. On the
|
|
48
50
|
* notification models the ownership column is not bookkeeping — it decides
|
|
49
51
|
* whose on-call pages select the row, while the address actually messaged is
|
|
@@ -124,7 +126,8 @@ type ModelConstructor = { new (): BaseModel };
|
|
|
124
126
|
* Every model whose create list is exactly [Permission.CurrentUser]. These are
|
|
125
127
|
* the ones the fix protects, and the ones an attacker could previously write on
|
|
126
128
|
* another user's behalf. Enumerated rather than discovered so that adding a
|
|
127
|
-
*
|
|
129
|
+
* seventeenth model with this shape is a deliberate act that shows up in
|
|
130
|
+
* review.
|
|
128
131
|
*/
|
|
129
132
|
const CURRENT_USER_ONLY_MODELS: Array<[string, ModelConstructor]> = [
|
|
130
133
|
["UserNotificationRule", UserNotificationRule],
|
|
@@ -135,6 +138,8 @@ const CURRENT_USER_ONLY_MODELS: Array<[string, ModelConstructor]> = [
|
|
|
135
138
|
["UserPush", UserPush],
|
|
136
139
|
["UserWhatsApp", UserWhatsApp],
|
|
137
140
|
["UserTelegram", UserTelegram],
|
|
141
|
+
["UserSlack", UserSlack],
|
|
142
|
+
["UserMicrosoftTeams", UserMicrosoftTeams],
|
|
138
143
|
["UserNotificationSetting", UserNotificationSetting],
|
|
139
144
|
["UserIncomingCallNumber", UserIncomingCallNumber],
|
|
140
145
|
["UserSession", UserSession],
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import UserCall from "../../../../../Models/DatabaseModels/UserCall";
|
|
2
2
|
import UserEmail from "../../../../../Models/DatabaseModels/UserEmail";
|
|
3
|
+
import UserMicrosoftTeams from "../../../../../Models/DatabaseModels/UserMicrosoftTeams";
|
|
3
4
|
import UserPush from "../../../../../Models/DatabaseModels/UserPush";
|
|
5
|
+
import UserSlack from "../../../../../Models/DatabaseModels/UserSlack";
|
|
4
6
|
import UserSMS from "../../../../../Models/DatabaseModels/UserSMS";
|
|
5
7
|
import UserTelegram from "../../../../../Models/DatabaseModels/UserTelegram";
|
|
6
8
|
import UserWebhook from "../../../../../Models/DatabaseModels/UserWebhook";
|
|
@@ -34,7 +36,7 @@ import UserType from "../../../../../Types/UserType";
|
|
|
34
36
|
* administrators - which is the feature, and is correct - and the moment it
|
|
35
37
|
* does, an admin's read of that table is no longer confined to their own rows.
|
|
36
38
|
* Everything reachable from those rows is then reachable by an admin, and the
|
|
37
|
-
*
|
|
39
|
+
* nine notification-method models hang off them by ManyToOne relation.
|
|
38
40
|
*
|
|
39
41
|
* There are FOUR routes into a marked column, and closing three of them is the
|
|
40
42
|
* same as closing none. Every route below is driven through the real permission
|
|
@@ -49,7 +51,7 @@ import UserType from "../../../../../Types/UserType";
|
|
|
49
51
|
* Route 3 sort: { webhookUrl: "ASC" } injected post-gate
|
|
50
52
|
* Route 4 query: { webhookUrl: "https://..." } as a value oracle
|
|
51
53
|
*
|
|
52
|
-
* HOW THE ADMIN CASE IS SET UP, AND WHY IT IS NOT THEATRE. Today the
|
|
54
|
+
* HOW THE ADMIN CASE IS SET UP, AND WHY IT IS NOT THEATRE. Today the nine
|
|
53
55
|
* method models list [CurrentUser] and nothing else, so TenantPermission stamps
|
|
54
56
|
* every non-root caller's own userId onto their query and an administrator is
|
|
55
57
|
* indistinguishable from a plain member - the admin case is currently
|
|
@@ -68,7 +70,7 @@ const otherUserId: ObjectID = ObjectID.generate();
|
|
|
68
70
|
type ModelTypeUnderTest = { new (): BaseModel };
|
|
69
71
|
|
|
70
72
|
/*
|
|
71
|
-
* The
|
|
73
|
+
* The nine notification-method models with the post-Phase-3 table read. Only
|
|
72
74
|
* the table access control is restated; every column, its @OwnerOnlyColumn
|
|
73
75
|
* mark, the tenant column and @CurrentUserCanAccessRecordBy("userId") are
|
|
74
76
|
* inherited from the production model.
|
|
@@ -103,6 +105,12 @@ class AdminReadableUserWhatsApp extends UserWhatsApp {}
|
|
|
103
105
|
@TableAccessControl(ADMIN_READABLE_TABLE_ACCESS)
|
|
104
106
|
class AdminReadableUserTelegram extends UserTelegram {}
|
|
105
107
|
|
|
108
|
+
@TableAccessControl(ADMIN_READABLE_TABLE_ACCESS)
|
|
109
|
+
class AdminReadableUserSlack extends UserSlack {}
|
|
110
|
+
|
|
111
|
+
@TableAccessControl(ADMIN_READABLE_TABLE_ACCESS)
|
|
112
|
+
class AdminReadableUserMicrosoftTeams extends UserMicrosoftTeams {}
|
|
113
|
+
|
|
106
114
|
@TableAccessControl(ADMIN_READABLE_TABLE_ACCESS)
|
|
107
115
|
class AdminReadableUserWebhook extends UserWebhook {}
|
|
108
116
|
|
|
@@ -218,6 +226,32 @@ class AdminReadableRuleModel extends BaseModel {
|
|
|
218
226
|
})
|
|
219
227
|
public userTelegram?: UserTelegram = undefined;
|
|
220
228
|
|
|
229
|
+
@ColumnAccessControl({
|
|
230
|
+
create: [Permission.CurrentUser],
|
|
231
|
+
read: [Permission.CurrentUser, Permission.ProjectAdmin],
|
|
232
|
+
update: [],
|
|
233
|
+
})
|
|
234
|
+
@TableColumn({
|
|
235
|
+
type: TableColumnType.Entity,
|
|
236
|
+
modelType: UserSlack,
|
|
237
|
+
manyToOneRelationColumn: "userSlackId",
|
|
238
|
+
title: "User Slack",
|
|
239
|
+
})
|
|
240
|
+
public userSlack?: UserSlack = undefined;
|
|
241
|
+
|
|
242
|
+
@ColumnAccessControl({
|
|
243
|
+
create: [Permission.CurrentUser],
|
|
244
|
+
read: [Permission.CurrentUser, Permission.ProjectAdmin],
|
|
245
|
+
update: [],
|
|
246
|
+
})
|
|
247
|
+
@TableColumn({
|
|
248
|
+
type: TableColumnType.Entity,
|
|
249
|
+
modelType: UserMicrosoftTeams,
|
|
250
|
+
manyToOneRelationColumn: "userMicrosoftTeamsId",
|
|
251
|
+
title: "User Microsoft Teams",
|
|
252
|
+
})
|
|
253
|
+
public userMicrosoftTeams?: UserMicrosoftTeams = undefined;
|
|
254
|
+
|
|
221
255
|
@ColumnAccessControl({
|
|
222
256
|
create: [Permission.CurrentUser],
|
|
223
257
|
read: [Permission.CurrentUser, Permission.ProjectAdmin],
|
|
@@ -359,6 +393,41 @@ const METHOD_MODELS: Array<MethodModelUnderTest> = [
|
|
|
359
393
|
labelColumns: ["userId", "projectId", "isVerified"],
|
|
360
394
|
unmarkedSortColumn: "isVerified",
|
|
361
395
|
},
|
|
396
|
+
{
|
|
397
|
+
name: "UserSlack",
|
|
398
|
+
modelType: UserSlack,
|
|
399
|
+
adminReadableModelType: AdminReadableUserSlack,
|
|
400
|
+
relationColumnName: "userSlack",
|
|
401
|
+
/*
|
|
402
|
+
* No verificationCode here: a Slack row is born verified off the user's own
|
|
403
|
+
* OAuth workspace link, so the two marked columns are the member id the bot
|
|
404
|
+
* delivers to and the display name that identifies the person in an outside
|
|
405
|
+
* workspace. Both are owner-readable, so the owner-only mark is the ONLY
|
|
406
|
+
* thing between them and any admin in the project.
|
|
407
|
+
*/
|
|
408
|
+
ownerOnlyColumns: ["slackUserId", "slackUserName"],
|
|
409
|
+
ownerOnlyColumnsSelectableByOwner: ["slackUserId", "slackUserName"],
|
|
410
|
+
ownerOnlyColumnReachableThroughRelation: "slackUserId",
|
|
411
|
+
labelColumnReachableThroughRelation: "projectId",
|
|
412
|
+
labelColumns: ["userId", "projectId", "isVerified"],
|
|
413
|
+
unmarkedSortColumn: "isVerified",
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
name: "UserMicrosoftTeams",
|
|
417
|
+
modelType: UserMicrosoftTeams,
|
|
418
|
+
adminReadableModelType: AdminReadableUserMicrosoftTeams,
|
|
419
|
+
relationColumnName: "userMicrosoftTeams",
|
|
420
|
+
// Same shape as UserSlack: created verified, no verificationCode column.
|
|
421
|
+
ownerOnlyColumns: ["microsoftTeamsUserId", "microsoftTeamsUserName"],
|
|
422
|
+
ownerOnlyColumnsSelectableByOwner: [
|
|
423
|
+
"microsoftTeamsUserId",
|
|
424
|
+
"microsoftTeamsUserName",
|
|
425
|
+
],
|
|
426
|
+
ownerOnlyColumnReachableThroughRelation: "microsoftTeamsUserId",
|
|
427
|
+
labelColumnReachableThroughRelation: "projectId",
|
|
428
|
+
labelColumns: ["userId", "projectId", "isVerified"],
|
|
429
|
+
unmarkedSortColumn: "isVerified",
|
|
430
|
+
},
|
|
362
431
|
{
|
|
363
432
|
name: "UserWebhook",
|
|
364
433
|
modelType: UserWebhook,
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import TeamMember from "../../../../../Models/DatabaseModels/TeamMember";
|
|
2
2
|
import UserCall from "../../../../../Models/DatabaseModels/UserCall";
|
|
3
3
|
import UserEmail from "../../../../../Models/DatabaseModels/UserEmail";
|
|
4
|
+
import UserMicrosoftTeams from "../../../../../Models/DatabaseModels/UserMicrosoftTeams";
|
|
4
5
|
import UserNotificationRule from "../../../../../Models/DatabaseModels/UserNotificationRule";
|
|
5
6
|
import UserPush from "../../../../../Models/DatabaseModels/UserPush";
|
|
7
|
+
import UserSlack from "../../../../../Models/DatabaseModels/UserSlack";
|
|
6
8
|
import UserSMS from "../../../../../Models/DatabaseModels/UserSMS";
|
|
7
9
|
import UserTelegram from "../../../../../Models/DatabaseModels/UserTelegram";
|
|
8
10
|
import UserWebhook from "../../../../../Models/DatabaseModels/UserWebhook";
|
|
@@ -27,8 +29,8 @@ import Permission from "../../../../../Types/Permission";
|
|
|
27
29
|
* WHAT THIS FILE PINS
|
|
28
30
|
*
|
|
29
31
|
* On-call notification configuration - the rules themselves plus every channel
|
|
30
|
-
* they can fire through (email, SMS, call, push, WhatsApp, Telegram,
|
|
31
|
-
* is row-scoped to its owner by one mechanism:
|
|
32
|
+
* they can fire through (email, SMS, call, push, WhatsApp, Telegram, Slack,
|
|
33
|
+
* Microsoft Teams, webhook) - is row-scoped to its owner by one mechanism:
|
|
32
34
|
*
|
|
33
35
|
* @TableAccessControl({ ... [Permission.CurrentUser] ... })
|
|
34
36
|
* @CurrentUserCanAccessRecordBy("userId")
|
|
@@ -39,7 +41,7 @@ import Permission from "../../../../../Types/Permission";
|
|
|
39
41
|
* addCurrentUserScopeToQuery() rewrites the query to `userId = me`, and throws
|
|
40
42
|
* NotAuthorizedException outright if the caller had explicitly named somebody
|
|
41
43
|
* else. The FIRST group below is the standing proof that this still holds for a
|
|
42
|
-
* plain member on all
|
|
44
|
+
* plain member on all ten models.
|
|
43
45
|
*
|
|
44
46
|
* GAP D in Internal/Roadmap/OnCallNotificationReadiness.md was the consequence
|
|
45
47
|
* for everybody else: a Project ADMIN was treated identically to a plain member
|
|
@@ -55,12 +57,12 @@ import Permission from "../../../../../Types/Permission";
|
|
|
55
57
|
* a member's rules, repair them, delete them, and provision one for a
|
|
56
58
|
* member who has none.
|
|
57
59
|
*
|
|
58
|
-
* - The
|
|
60
|
+
* - The NINE METHOD MODELS widen on NOTHING. All four lists are
|
|
59
61
|
* [CurrentUser], so an administrator is force-scoped onto their own rows
|
|
60
62
|
* there and is rejected outright for naming somebody else, on every verb -
|
|
61
63
|
* indistinguishable from a plain member.
|
|
62
64
|
*
|
|
63
|
-
* The read half of those
|
|
65
|
+
* The read half of those models WAS widened at one point, and reverting it is
|
|
64
66
|
* the most load-bearing fact in this file. The table scope is not one gate
|
|
65
67
|
* among several on these models; it is the only one. Every column on them
|
|
66
68
|
* declares `read: [Permission.CurrentUser]`, and that list is intersected by
|
|
@@ -97,7 +99,7 @@ import Permission from "../../../../../Types/Permission";
|
|
|
97
99
|
* list. It is force-scoped for a member and unscoped for an admin. The
|
|
98
100
|
* ownership decorator was therefore never the thing scoping admins out -
|
|
99
101
|
* the absent permission entry was, which is why the rule table's fix is an
|
|
100
|
-
* entry in a list rather than a new gate, and why the
|
|
102
|
+
* entry in a list rather than a new gate, and why the nine methods stay
|
|
101
103
|
* scoped purely by having no such entry.
|
|
102
104
|
*
|
|
103
105
|
* - AdminReadableNotificationRuleModel below isolates the same shape on a
|
|
@@ -113,7 +115,7 @@ import Permission from "../../../../../Types/Permission";
|
|
|
113
115
|
* production models, but a combination of verbs that none of them use.
|
|
114
116
|
*
|
|
115
117
|
* It is deliberately not a copy of any shipping model - not of the rule table,
|
|
116
|
-
* whose four lists are all widened, and not of the
|
|
118
|
+
* whose four lists are all widened, and not of the nine methods, whose four
|
|
117
119
|
* lists are all closed. Its job is to pin the mechanism - the scope lifts for
|
|
118
120
|
* the operations whose list names a real permission and holds for the ones
|
|
119
121
|
* whose list does not - so that the production assertions above it are testing
|
|
@@ -154,8 +156,8 @@ class AdminReadableNotificationRuleModel extends BaseModel {
|
|
|
154
156
|
type ModelTypeUnderTest = { new (): BaseModel };
|
|
155
157
|
|
|
156
158
|
/*
|
|
157
|
-
* The
|
|
158
|
-
* decorator shape, so every method assertion runs against all
|
|
159
|
+
* The nine channels a rule can fire through. They are nine copies of one
|
|
160
|
+
* decorator shape, so every method assertion runs against all nine rather than
|
|
159
161
|
* against a representative one - a widening applied to one of them is exactly
|
|
160
162
|
* the kind of thing that survives review.
|
|
161
163
|
*/
|
|
@@ -166,16 +168,18 @@ const NOTIFICATION_METHOD_MODELS: Array<ModelTypeUnderTest> = [
|
|
|
166
168
|
UserPush,
|
|
167
169
|
UserWhatsApp,
|
|
168
170
|
UserTelegram,
|
|
171
|
+
UserSlack,
|
|
172
|
+
UserMicrosoftTeams,
|
|
169
173
|
UserWebhook,
|
|
170
174
|
];
|
|
171
175
|
|
|
172
176
|
/*
|
|
173
177
|
* Every model that makes up a user's on-call notification configuration.
|
|
174
178
|
*
|
|
175
|
-
* All
|
|
179
|
+
* All ten behave identically for a plain member, which is what the first
|
|
176
180
|
* group asserts over this list. They diverge only in what an ADMINISTRATOR
|
|
177
181
|
* sees, so the admin-facing groups below name UserNotificationRule separately
|
|
178
|
-
* and iterate NOTIFICATION_METHOD_MODELS for the
|
|
182
|
+
* and iterate NOTIFICATION_METHOD_MODELS for the nine that stay closed.
|
|
179
183
|
*/
|
|
180
184
|
const NOTIFICATION_CONFIG_MODELS: Array<ModelTypeUnderTest> = [
|
|
181
185
|
UserNotificationRule,
|
|
@@ -194,7 +198,7 @@ const ROW_SCOPED_REQUEST_TYPES: Array<DatabaseRequestType> = [
|
|
|
194
198
|
* the model equals itself; naming them independently means a change to a
|
|
195
199
|
* shipping list has to be re-stated deliberately in a test.
|
|
196
200
|
*
|
|
197
|
-
* On the
|
|
201
|
+
* On the nine method models these names appear only inside a `not.toContain`.
|
|
198
202
|
*/
|
|
199
203
|
const ADMIN_READ_GRANTS: Array<Permission> = [
|
|
200
204
|
Permission.ProjectOwner,
|
|
@@ -554,10 +558,10 @@ describe("GAP D closed - the rule table opens to a ProjectAdmin on every verb",
|
|
|
554
558
|
});
|
|
555
559
|
});
|
|
556
560
|
|
|
557
|
-
describe("GAP D closed on the rule table alone - the
|
|
561
|
+
describe("GAP D closed on the rule table alone - the nine methods open to nobody", () => {
|
|
558
562
|
/*
|
|
559
563
|
* THE BOUNDARY OF THE PHASE, AND THE PART MOST LIKELY TO BE "TIDIED UP"
|
|
560
|
-
* LATER, because
|
|
564
|
+
* LATER, because nine models that differ from their sibling in all four
|
|
561
565
|
* lists look like an oversight rather than a decision.
|
|
562
566
|
*
|
|
563
567
|
* Two independent reasons keep them closed, and both have to be understood or
|
|
@@ -736,7 +740,7 @@ describe("TeamMember proves the mechanism - a listed admin permission lifts the
|
|
|
736
740
|
*
|
|
737
741
|
* That is the whole of what the rule table gained, on a model that has
|
|
738
742
|
* always shipped it - which is why the fix there was an entry in a list
|
|
739
|
-
* rather than a new gate, and equally why the
|
|
743
|
+
* rather than a new gate, and equally why the nine method models stay
|
|
740
744
|
* closed by simply having no such entry. Nothing guards them beyond the
|
|
741
745
|
* absence, so this group stays here as the independent statement of what
|
|
742
746
|
* the absence is doing.
|
|
@@ -842,14 +846,14 @@ describe("TeamMember proves the mechanism - a listed admin permission lifts the
|
|
|
842
846
|
|
|
843
847
|
describe("the lift mechanism in isolation, on a throwaway model", () => {
|
|
844
848
|
/*
|
|
845
|
-
* The production groups above assert what UserNotificationRule and the
|
|
849
|
+
* The production groups above assert what UserNotificationRule and the nine
|
|
846
850
|
* methods DO; this group asserts what the framework does with any user-scoped
|
|
847
851
|
* model, on a class whose lists match neither of them - all four widened on
|
|
848
852
|
* the rule table, all four closed on the methods, two-of-four here.
|
|
849
853
|
*
|
|
850
854
|
* That mismatch is the point. If a future change to TenantPermission alters
|
|
851
855
|
* the rule, this group fails on its own terms and says so, instead of the
|
|
852
|
-
* failure arriving disguised as
|
|
856
|
+
* failure arriving disguised as ten models having drifted.
|
|
853
857
|
*/
|
|
854
858
|
|
|
855
859
|
it("lifts the ownership scope for a ProjectAdmin on read and update", async () => {
|
|
@@ -989,7 +993,7 @@ describe("notification config model decorators", () => {
|
|
|
989
993
|
* while "restoring the admin feature" - and re-adding it silently removes
|
|
990
994
|
* the row scope that every column on the model depends on. Neither
|
|
991
995
|
* ADMIN_READ_GRANTS nor ADMIN_WRITE_GRANTS belongs in any list on these
|
|
992
|
-
*
|
|
996
|
+
* nine; there is no verb on a notification method that an administrator
|
|
993
997
|
* may perform on somebody else's row.
|
|
994
998
|
*/
|
|
995
999
|
const model: BaseModel = new modelType();
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import DatabaseRequestType from "../../../../../Server/Types/BaseDatabase/DatabaseRequestType";
|
|
2
|
+
import ColumnPermissions from "../../../../../Server/Types/Database/Permissions/ColumnPermission";
|
|
3
|
+
import UserSlack from "../../../../../Models/DatabaseModels/UserSlack";
|
|
4
|
+
import UserMicrosoftTeams from "../../../../../Models/DatabaseModels/UserMicrosoftTeams";
|
|
5
|
+
import DatabaseCommonInteractionProps from "../../../../../Types/BaseDatabase/DatabaseCommonInteractionProps";
|
|
6
|
+
import ObjectID from "../../../../../Types/ObjectID";
|
|
7
|
+
import Permission, {
|
|
8
|
+
UserTenantAccessPermission,
|
|
9
|
+
} from "../../../../../Types/Permission";
|
|
10
|
+
import { describe, expect, it } from "@jest/globals";
|
|
11
|
+
|
|
12
|
+
/*
|
|
13
|
+
* REGRESSION PIN for the defect that made the whole feature dead on arrival.
|
|
14
|
+
*
|
|
15
|
+
* UserSlackService / UserMicrosoftTeamsService stamp the workspace identifier,
|
|
16
|
+
* the display name and isVerified onto createBy.data inside onBeforeCreate.
|
|
17
|
+
* DatabaseService.create runs that hook FIRST and only then calls
|
|
18
|
+
* ModelPermission.checkCreatePermissions on the STAMPED data — so the column
|
|
19
|
+
* ACLs are evaluated against values the SERVER wrote, with the CALLER's
|
|
20
|
+
* permissions. When those three columns carried `create: []`, every non-root
|
|
21
|
+
* create was refused with "User is not allowed to create on slackUserId
|
|
22
|
+
* column of Slack Account": no user could ever add Slack or Microsoft Teams
|
|
23
|
+
* as a notification method through the API or the dashboard, and no test
|
|
24
|
+
* noticed because the service suites call the hook directly rather than
|
|
25
|
+
* DatabaseService.create.
|
|
26
|
+
*
|
|
27
|
+
* The columns therefore carry `create: [Permission.CurrentUser]` — the same
|
|
28
|
+
* shape UserWebAuthn uses for its server-stamped isVerified — and the thing
|
|
29
|
+
* that keeps the values out of the CLIENT's hands is the hook itself, which
|
|
30
|
+
* refuses any non-root payload carrying them BEFORE it stamps. These tests
|
|
31
|
+
* pin both halves: the stamped shape passes the column check, and the check
|
|
32
|
+
* itself is really being evaluated (the harness guard).
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
const projectId: ObjectID = ObjectID.generate();
|
|
36
|
+
const userId: ObjectID = ObjectID.generate();
|
|
37
|
+
|
|
38
|
+
function makeProps(
|
|
39
|
+
permissions: Array<Permission>,
|
|
40
|
+
): DatabaseCommonInteractionProps {
|
|
41
|
+
const tenantPermission: UserTenantAccessPermission = {
|
|
42
|
+
projectId,
|
|
43
|
+
_type: "UserTenantAccessPermission",
|
|
44
|
+
permissions: permissions.map((permission: Permission) => {
|
|
45
|
+
return {
|
|
46
|
+
_type: "UserPermission",
|
|
47
|
+
permission: permission,
|
|
48
|
+
labelIds: [],
|
|
49
|
+
isBlockPermission: false,
|
|
50
|
+
};
|
|
51
|
+
}),
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
userId,
|
|
56
|
+
tenantId: projectId,
|
|
57
|
+
userTenantAccessPermission: {
|
|
58
|
+
[projectId.toString()]: tenantPermission,
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/* Exactly what UserSlackService.onBeforeCreate leaves on createBy.data. */
|
|
64
|
+
function stampedUserSlack(): UserSlack {
|
|
65
|
+
const model: UserSlack = new UserSlack();
|
|
66
|
+
model.projectId = projectId;
|
|
67
|
+
model.userId = userId;
|
|
68
|
+
model.slackUserId = "U0123ABCD";
|
|
69
|
+
model.slackUserName = "alice";
|
|
70
|
+
model.isVerified = true;
|
|
71
|
+
return model;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* Exactly what UserMicrosoftTeamsService.onBeforeCreate leaves behind. */
|
|
75
|
+
function stampedUserMicrosoftTeams(): UserMicrosoftTeams {
|
|
76
|
+
const model: UserMicrosoftTeams = new UserMicrosoftTeams();
|
|
77
|
+
model.projectId = projectId;
|
|
78
|
+
model.userId = userId;
|
|
79
|
+
model.microsoftTeamsUserId = "e6f1c1f7-aad0-4b6c-9c11-2f5b7c8d9e0f";
|
|
80
|
+
model.microsoftTeamsUserName = "Alice Example";
|
|
81
|
+
model.isVerified = true;
|
|
82
|
+
return model;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
describe("server-stamped workspace method columns survive the create column check", () => {
|
|
86
|
+
/*
|
|
87
|
+
* Harness guard: a caller with NO permissions at all must still be refused,
|
|
88
|
+
* or every "does not throw" below would pass because the permission set was
|
|
89
|
+
* mis-built rather than because the columns are actually creatable.
|
|
90
|
+
* CurrentUser is auto-granted to authenticated callers by
|
|
91
|
+
* getUserPermissions, so the guard uses a column no list opens: userId's
|
|
92
|
+
* create list contains CurrentUser, but a props object with no userId is
|
|
93
|
+
* unauthenticated and gets nothing.
|
|
94
|
+
*/
|
|
95
|
+
it("harness guard: an unauthenticated caller is still refused", () => {
|
|
96
|
+
const props: DatabaseCommonInteractionProps = makeProps([]);
|
|
97
|
+
delete (props as { userId?: ObjectID }).userId;
|
|
98
|
+
|
|
99
|
+
expect(() => {
|
|
100
|
+
ColumnPermissions.checkDataColumnPermissions(
|
|
101
|
+
UserSlack,
|
|
102
|
+
stampedUserSlack(),
|
|
103
|
+
props,
|
|
104
|
+
DatabaseRequestType.Create,
|
|
105
|
+
);
|
|
106
|
+
}).toThrow();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("a stamped UserSlack passes for a plain authenticated member", () => {
|
|
110
|
+
expect(() => {
|
|
111
|
+
ColumnPermissions.checkDataColumnPermissions(
|
|
112
|
+
UserSlack,
|
|
113
|
+
stampedUserSlack(),
|
|
114
|
+
makeProps([Permission.CurrentUser]),
|
|
115
|
+
DatabaseRequestType.Create,
|
|
116
|
+
);
|
|
117
|
+
}).not.toThrow();
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it("a stamped UserMicrosoftTeams passes for a plain authenticated member", () => {
|
|
121
|
+
expect(() => {
|
|
122
|
+
ColumnPermissions.checkDataColumnPermissions(
|
|
123
|
+
UserMicrosoftTeams,
|
|
124
|
+
stampedUserMicrosoftTeams(),
|
|
125
|
+
makeProps([Permission.CurrentUser]),
|
|
126
|
+
DatabaseRequestType.Create,
|
|
127
|
+
);
|
|
128
|
+
}).not.toThrow();
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
/*
|
|
132
|
+
* The other half of the design: opening the column ACL did NOT hand the
|
|
133
|
+
* value to the client — the SERVICE hook refuses any non-root payload that
|
|
134
|
+
* arrives carrying these columns, before anything is stamped. That refusal
|
|
135
|
+
* is pinned in UserSlackService.test.ts / UserMicrosoftTeamsService.test.ts
|
|
136
|
+
* ("cannot be set directly" / "cannot be set to true"); this file exists so
|
|
137
|
+
* that the two halves cannot drift apart unnoticed: if someone reverts the
|
|
138
|
+
* ACL to create: [], the two "passes" tests above fail; if someone removes
|
|
139
|
+
* the hook guards, the service suites fail.
|
|
140
|
+
*/
|
|
141
|
+
it("update stays closed: even the owner cannot rewrite the stamped identifier", () => {
|
|
142
|
+
expect(() => {
|
|
143
|
+
ColumnPermissions.checkDataColumnPermissions(
|
|
144
|
+
UserSlack,
|
|
145
|
+
{ slackUserId: "U-ATTACKER" } as UserSlack,
|
|
146
|
+
makeProps([Permission.CurrentUser]),
|
|
147
|
+
DatabaseRequestType.Update,
|
|
148
|
+
);
|
|
149
|
+
}).toThrow();
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it("update stays closed on the Teams identifier too", () => {
|
|
153
|
+
expect(() => {
|
|
154
|
+
ColumnPermissions.checkDataColumnPermissions(
|
|
155
|
+
UserMicrosoftTeams,
|
|
156
|
+
{ microsoftTeamsUserId: "spoofed" } as UserMicrosoftTeams,
|
|
157
|
+
makeProps([Permission.CurrentUser]),
|
|
158
|
+
DatabaseRequestType.Update,
|
|
159
|
+
);
|
|
160
|
+
}).toThrow();
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it("update stays closed on isVerified for both models", () => {
|
|
164
|
+
for (const [modelType, data] of [
|
|
165
|
+
[UserSlack, { isVerified: true } as UserSlack],
|
|
166
|
+
[UserMicrosoftTeams, { isVerified: true } as UserMicrosoftTeams],
|
|
167
|
+
] as Array<[{ new (): UserSlack | UserMicrosoftTeams }, UserSlack]>) {
|
|
168
|
+
expect(() => {
|
|
169
|
+
ColumnPermissions.checkDataColumnPermissions(
|
|
170
|
+
modelType,
|
|
171
|
+
data,
|
|
172
|
+
makeProps([Permission.CurrentUser]),
|
|
173
|
+
DatabaseRequestType.Update,
|
|
174
|
+
);
|
|
175
|
+
}).toThrow();
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
});
|