@oneuptime/common 12.0.14 → 12.0.15
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/GlobalOidc.ts +20 -0
- package/Models/DatabaseModels/GlobalSso.ts +20 -0
- package/Models/DatabaseModels/MarketingConversion.ts +19 -9
- package/Models/DatabaseModels/NetworkDeviceLinkRule.ts +46 -0
- package/Models/DatabaseModels/StatusPage.ts +7 -4
- package/Server/API/StatusPageAPI.ts +29 -4
- package/Server/EnvironmentConfig.ts +7 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787800000000-AddScopeToNetworkDeviceLinkRule.ts +19 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787900000000-AddRestrictToAttachedProjectsToGlobalSso.ts +40 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Middleware/SlackAuthorization.ts +43 -6
- package/Server/Middleware/UserAuthorization.ts +236 -26
- package/Server/Middleware/WhatsAppAuthorization.ts +43 -3
- package/Server/Services/GlobalOidcProjectService.ts +126 -1
- package/Server/Services/GlobalOidcService.ts +132 -0
- package/Server/Services/GlobalSsoProjectService.ts +126 -1
- package/Server/Services/GlobalSsoService.ts +132 -0
- package/Server/Services/UserEmailService.ts +4 -4
- package/Server/Utils/CronTab.ts +73 -0
- package/Server/Utils/GlobalSsoAuthorization.ts +175 -0
- package/Server/Utils/Marketing/ConversionUploadProvider.ts +35 -5
- package/Server/Utils/Marketing/Providers/GoogleAds.ts +1 -1
- package/Server/Utils/Marketing/Providers/LinkedIn.ts +1 -1
- package/Server/Utils/Marketing/Providers/Meta.ts +1 -1
- package/Server/Utils/Marketing/Providers/MicrosoftAds.ts +1 -1
- package/Server/Utils/Marketing/Providers/Reddit.ts +1 -1
- package/Server/Utils/Monitor/Criteria/APIRequestCriteria.ts +48 -33
- package/Server/Utils/Monitor/Criteria/DnsMonitorCriteria.ts +35 -30
- package/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.ts +33 -28
- package/Server/Utils/Monitor/Criteria/EvaluateOverTime.ts +475 -47
- package/Server/Utils/Monitor/Criteria/ExternalStatusPageMonitorCriteria.ts +35 -30
- package/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.ts +34 -28
- package/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.ts +33 -28
- package/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.ts +69 -40
- package/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.ts +35 -29
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +8 -0
- package/Server/Utils/Monitor/MonitorResource.ts +161 -21
- package/Server/Utils/StatusPageContentSecurityPolicy.ts +56 -0
- package/Server/Utils/StatusPageCustomizationAccess.ts +117 -0
- package/Tests/Server/API/StatusPageCustomizationOrigin.test.ts +348 -0
- package/Tests/Server/Infrastructure/Postgres/FixTotpOtpUrlAlgorithmMigration.test.ts +9 -59
- package/Tests/Server/Infrastructure/Postgres/SchemaMigrationsOrdering.test.ts +227 -0
- package/Tests/Server/Middleware/SlackAuthorization.test.ts +149 -0
- package/Tests/Server/Middleware/UserAuthorization.test.ts +27 -13
- package/Tests/Server/Middleware/UserAuthorizationGlobalSsoGovernance.test.ts +1455 -0
- package/Tests/Server/Middleware/UserAuthorizationSSOProvider.test.ts +91 -2
- package/Tests/Server/Middleware/WhatsAppAuthorization.test.ts +233 -0
- package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +21 -8
- package/Tests/Server/Services/GlobalSsoProviderAuthorization.test.ts +1242 -0
- package/Tests/Server/Services/MonitorResourceProbeAgreementHydration.test.ts +459 -0
- package/Tests/Server/Services/MonitorResourceProbeAgreementReuse.test.ts +10 -0
- package/Tests/Server/Types/AnalyticsDatabase/AggregateUtilBucketTimestamp.test.ts +416 -0
- package/Tests/Server/Types/Database/QueryHelperManyToManyAndEmptyValues.test.ts +551 -0
- package/Tests/Server/Types/Database/SelectUtil.test.ts +348 -0
- package/Tests/Server/Utils/CronTab.test.ts +116 -0
- package/Tests/Server/Utils/GlobalSSOToken.test.ts +581 -0
- package/Tests/Server/Utils/GlobalSsoAuthorization.test.ts +1401 -0
- package/Tests/Server/Utils/Marketing/AdUploadableConversionTypes.test.ts +254 -0
- package/Tests/Server/Utils/Marketing/ConversionUploadProvider.test.ts +1 -1
- package/Tests/Server/Utils/Monitor/Criteria/APIRequestCriteriaEvaluateOverTime.test.ts +508 -0
- package/Tests/Server/Utils/Monitor/Criteria/APIRequestCriteriaPortTimings.test.ts +30 -9
- package/Tests/Server/Utils/Monitor/Criteria/EvaluateOverTime.test.ts +987 -0
- package/Tests/Server/Utils/Monitor/Criteria/ExternalStatusPageMonitorCriteria.test.ts +916 -0
- package/Tests/Server/Utils/Monitor/Criteria/ProfileMonitorCriteria.test.ts +382 -0
- package/Tests/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.test.ts +1219 -0
- package/Tests/Server/Utils/Monitor/MonitorResourceIngestedStepSelection.test.ts +86 -0
- package/Tests/Server/Utils/StatusPageContentSecurityPolicy.test.ts +136 -0
- package/Tests/Server/Utils/StatusPageCustomizationAccess.test.ts +223 -0
- package/Tests/Types/Monitor/MonitorStepConfigHelpers.test.ts +165 -0
- package/Tests/Types/NetworkDevice/NetworkDeviceLinkRuleScope.test.ts +215 -0
- package/Tests/UI/Components/Forms/FormSubmitAnalytics.test.tsx +206 -0
- package/Tests/UI/Utils/AIChatExport/ConversationMarkdown.test.ts +774 -0
- package/Tests/UI/Utils/AIChatExport/MarkdownBlocks.test.ts +791 -0
- package/Tests/UI/Utils/AIChatExport/MarkdownSafety.test.ts +544 -0
- package/Tests/Utils/Analytics.test.ts +187 -0
- package/Tests/Utils/Monitor/MonitorMetricType.test.ts +80 -0
- package/Tests/Utils/Monitor/NetworkDeviceLinkRuleUtil.test.ts +1291 -0
- package/Tests/Utils/NetworkDiscovery/UnclaimedScanDiagnosis.test.ts +214 -0
- package/Types/Marketing/MarketingConversion.ts +12 -0
- package/Types/Monitor/CriteriaFilter.ts +9 -0
- package/Types/NetworkDevice/NetworkDeviceLinkRuleScope.ts +51 -0
- package/UI/Components/FormModal/BasicFormModal.tsx +12 -0
- package/UI/Components/Forms/BasicForm.tsx +13 -1
- package/UI/Components/Forms/BasicModelForm.tsx +2 -1
- package/UI/Components/Forms/ModelForm.tsx +10 -1
- package/UI/Components/Forms/Utils/FormAnalyticsName.ts +39 -0
- package/UI/Components/ModelFormModal/ModelFormModal.tsx +6 -1
- package/Utils/Analytics.ts +55 -2
- package/Utils/Monitor/MonitorMetricType.ts +40 -0
- package/Utils/Monitor/NetworkDeviceLinkRuleUtil.ts +587 -37
- package/Utils/NetworkDiscovery/UnclaimedScanDiagnosis.ts +115 -0
- package/build/dist/Models/DatabaseModels/GlobalOidc.js +21 -0
- package/build/dist/Models/DatabaseModels/GlobalOidc.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalSso.js +21 -0
- package/build/dist/Models/DatabaseModels/GlobalSso.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MarketingConversion.js +20 -10
- package/build/dist/Models/DatabaseModels/MarketingConversion.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceLinkRule.js +47 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceLinkRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPage.js +4 -4
- package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +25 -4
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +6 -0
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787800000000-AddScopeToNetworkDeviceLinkRule.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787800000000-AddScopeToNetworkDeviceLinkRule.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787900000000-AddRestrictToAttachedProjectsToGlobalSso.js +29 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787900000000-AddRestrictToAttachedProjectsToGlobalSso.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/SlackAuthorization.js +28 -4
- package/build/dist/Server/Middleware/SlackAuthorization.js.map +1 -1
- package/build/dist/Server/Middleware/UserAuthorization.js +197 -22
- package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
- package/build/dist/Server/Middleware/WhatsAppAuthorization.js +32 -2
- package/build/dist/Server/Middleware/WhatsAppAuthorization.js.map +1 -1
- package/build/dist/Server/Services/GlobalOidcProjectService.js +105 -0
- package/build/dist/Server/Services/GlobalOidcProjectService.js.map +1 -1
- package/build/dist/Server/Services/GlobalOidcService.js +127 -0
- package/build/dist/Server/Services/GlobalOidcService.js.map +1 -1
- package/build/dist/Server/Services/GlobalSsoProjectService.js +105 -0
- package/build/dist/Server/Services/GlobalSsoProjectService.js.map +1 -1
- package/build/dist/Server/Services/GlobalSsoService.js +127 -0
- package/build/dist/Server/Services/GlobalSsoService.js.map +1 -1
- package/build/dist/Server/Services/UserEmailService.js +4 -4
- package/build/dist/Server/Utils/CronTab.js +66 -0
- package/build/dist/Server/Utils/CronTab.js.map +1 -1
- package/build/dist/Server/Utils/GlobalSsoAuthorization.js +112 -0
- package/build/dist/Server/Utils/GlobalSsoAuthorization.js.map +1 -0
- package/build/dist/Server/Utils/Marketing/ConversionUploadProvider.js +17 -1
- package/build/dist/Server/Utils/Marketing/ConversionUploadProvider.js.map +1 -1
- package/build/dist/Server/Utils/Marketing/Providers/GoogleAds.js +1 -1
- package/build/dist/Server/Utils/Marketing/Providers/GoogleAds.js.map +1 -1
- package/build/dist/Server/Utils/Marketing/Providers/LinkedIn.js +1 -1
- package/build/dist/Server/Utils/Marketing/Providers/LinkedIn.js.map +1 -1
- package/build/dist/Server/Utils/Marketing/Providers/Meta.js +1 -1
- package/build/dist/Server/Utils/Marketing/Providers/Meta.js.map +1 -1
- package/build/dist/Server/Utils/Marketing/Providers/MicrosoftAds.js +1 -1
- package/build/dist/Server/Utils/Marketing/Providers/MicrosoftAds.js.map +1 -1
- package/build/dist/Server/Utils/Marketing/Providers/Reddit.js +1 -1
- package/build/dist/Server/Utils/Marketing/Providers/Reddit.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js +37 -35
- package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/DnsMonitorCriteria.js +21 -25
- package/build/dist/Server/Utils/Monitor/Criteria/DnsMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.js +20 -22
- package/build/dist/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js +370 -42
- package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/ExternalStatusPageMonitorCriteria.js +21 -25
- package/build/dist/Server/Utils/Monitor/Criteria/ExternalStatusPageMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.js +21 -25
- package/build/dist/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.js +21 -25
- package/build/dist/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.js +64 -52
- package/build/dist/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js +25 -29
- package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +8 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +123 -23
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/StatusPageContentSecurityPolicy.js +39 -0
- package/build/dist/Server/Utils/StatusPageContentSecurityPolicy.js.map +1 -0
- package/build/dist/Server/Utils/StatusPageCustomizationAccess.js +85 -0
- package/build/dist/Server/Utils/StatusPageCustomizationAccess.js.map +1 -0
- package/build/dist/Types/Marketing/MarketingConversion.js +10 -0
- package/build/dist/Types/Marketing/MarketingConversion.js.map +1 -1
- package/build/dist/Types/Monitor/CriteriaFilter.js +1 -0
- package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
- package/build/dist/Types/NetworkDevice/NetworkDeviceLinkRuleScope.js +46 -0
- package/build/dist/Types/NetworkDevice/NetworkDeviceLinkRuleScope.js.map +1 -0
- package/build/dist/UI/Components/FormModal/BasicFormModal.js +2 -1
- package/build/dist/UI/Components/FormModal/BasicFormModal.js.map +1 -1
- package/build/dist/UI/Components/Forms/BasicForm.js +10 -1
- package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
- package/build/dist/UI/Components/Forms/BasicModelForm.js +2 -1
- package/build/dist/UI/Components/Forms/BasicModelForm.js.map +1 -1
- package/build/dist/UI/Components/Forms/ModelForm.js +4 -1
- package/build/dist/UI/Components/Forms/ModelForm.js.map +1 -1
- package/build/dist/UI/Components/Forms/Utils/FormAnalyticsName.js +33 -0
- package/build/dist/UI/Components/Forms/Utils/FormAnalyticsName.js.map +1 -0
- package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js +2 -1
- package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js.map +1 -1
- package/build/dist/Utils/Analytics.js +50 -2
- package/build/dist/Utils/Analytics.js.map +1 -1
- package/build/dist/Utils/Monitor/MonitorMetricType.js +37 -0
- package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
- package/build/dist/Utils/Monitor/NetworkDeviceLinkRuleUtil.js +318 -37
- package/build/dist/Utils/Monitor/NetworkDeviceLinkRuleUtil.js.map +1 -1
- package/build/dist/Utils/NetworkDiscovery/UnclaimedScanDiagnosis.js +87 -0
- package/build/dist/Utils/NetworkDiscovery/UnclaimedScanDiagnosis.js.map +1 -0
- package/package.json +1 -1
|
@@ -235,6 +235,26 @@ export default class GlobalOIDC extends BaseModel {
|
|
|
235
235
|
})
|
|
236
236
|
public disableSignUpWithSso?: boolean = undefined;
|
|
237
237
|
|
|
238
|
+
@ColumnAccessControl({
|
|
239
|
+
create: [],
|
|
240
|
+
read: [],
|
|
241
|
+
update: [],
|
|
242
|
+
})
|
|
243
|
+
@TableColumn({
|
|
244
|
+
isDefaultValueColumn: true,
|
|
245
|
+
type: TableColumnType.Boolean,
|
|
246
|
+
title: "Restrict to Attached Projects",
|
|
247
|
+
description:
|
|
248
|
+
"When enabled, a login through this provider only satisfies SSO enforcement for the projects attached below. Leave this off (the default) and the provider satisfies SSO enforcement for every project the user is a member of, which is how attachments have always behaved - they are the provisioning allow-list, not an access boundary. Turning this on narrows access for people already signed in.",
|
|
249
|
+
defaultValue: false,
|
|
250
|
+
example: true,
|
|
251
|
+
})
|
|
252
|
+
@Column({
|
|
253
|
+
type: ColumnType.Boolean,
|
|
254
|
+
default: false,
|
|
255
|
+
})
|
|
256
|
+
public restrictToAttachedProjects?: boolean = undefined;
|
|
257
|
+
|
|
238
258
|
@ColumnAccessControl({
|
|
239
259
|
create: [],
|
|
240
260
|
read: [],
|
|
@@ -196,6 +196,26 @@ export default class GlobalSSO extends BaseModel {
|
|
|
196
196
|
})
|
|
197
197
|
public disableSignUpWithSso?: boolean = undefined;
|
|
198
198
|
|
|
199
|
+
@ColumnAccessControl({
|
|
200
|
+
create: [],
|
|
201
|
+
read: [],
|
|
202
|
+
update: [],
|
|
203
|
+
})
|
|
204
|
+
@TableColumn({
|
|
205
|
+
isDefaultValueColumn: true,
|
|
206
|
+
type: TableColumnType.Boolean,
|
|
207
|
+
title: "Restrict to Attached Projects",
|
|
208
|
+
description:
|
|
209
|
+
"When enabled, a login through this provider only satisfies SSO enforcement for the projects attached below. Leave this off (the default) and the provider satisfies SSO enforcement for every project the user is a member of, which is how attachments have always behaved - they are the provisioning allow-list, not an access boundary. Turning this on narrows access for people already signed in.",
|
|
210
|
+
defaultValue: false,
|
|
211
|
+
example: true,
|
|
212
|
+
})
|
|
213
|
+
@Column({
|
|
214
|
+
type: ColumnType.Boolean,
|
|
215
|
+
default: false,
|
|
216
|
+
})
|
|
217
|
+
public restrictToAttachedProjects?: boolean = undefined;
|
|
218
|
+
|
|
199
219
|
@ColumnAccessControl({
|
|
200
220
|
create: [],
|
|
201
221
|
read: [],
|
|
@@ -14,11 +14,13 @@ import ObjectID from "../../Types/ObjectID";
|
|
|
14
14
|
import { Column, Entity, Index } from "typeorm";
|
|
15
15
|
|
|
16
16
|
/*
|
|
17
|
-
* Internal table (no API access) recording
|
|
18
|
-
* signups and paid subscriptions
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
17
|
+
* Internal table (no API access) recording server-confirmed conversions —
|
|
18
|
+
* signups, booked meetings and paid subscriptions, along with any ad click IDs
|
|
19
|
+
* the converting visitor carried — and the status of uploading the
|
|
20
|
+
* ad-uploadable ones to ad platforms (Google Ads offline click conversions,
|
|
21
|
+
* Meta Conversions API). Browser analytics never write this table: rows are
|
|
22
|
+
* written by the signup/subscription flows and the Cal.com webhook, and read
|
|
23
|
+
* by the MarketingConversions worker job.
|
|
22
24
|
*/
|
|
23
25
|
@TableAccessControl({
|
|
24
26
|
create: [],
|
|
@@ -33,11 +35,18 @@ import { Column, Entity, Index } from "typeorm";
|
|
|
33
35
|
pluralName: "Marketing Conversions",
|
|
34
36
|
icon: IconProp.ChartBar,
|
|
35
37
|
tableDescription:
|
|
36
|
-
"
|
|
38
|
+
"Server-confirmed conversions (signups, meetings booked, paid subscriptions) and their upload status to ad platforms for offline conversion tracking.",
|
|
37
39
|
})
|
|
38
40
|
@Entity({
|
|
39
41
|
name: "MarketingConversion",
|
|
40
42
|
})
|
|
43
|
+
/*
|
|
44
|
+
* One conversion of each type per user/project. Postgres treats NULLs as
|
|
45
|
+
* distinct, so these indexes only constrain rows that actually carry a user or
|
|
46
|
+
* project id — conversion types with neither (a booked meeting from an
|
|
47
|
+
* anonymous visitor) are de-duplicated by their deterministic primary key
|
|
48
|
+
* instead.
|
|
49
|
+
*/
|
|
41
50
|
@Index("uq_marketing_conversion_type_user", ["conversionType", "userId"], {
|
|
42
51
|
unique: true,
|
|
43
52
|
})
|
|
@@ -58,7 +67,7 @@ export default class MarketingConversion extends BaseModel {
|
|
|
58
67
|
type: TableColumnType.ShortText,
|
|
59
68
|
required: true,
|
|
60
69
|
title: "Conversion Type",
|
|
61
|
-
description: "SignUp or PaidSubscription.",
|
|
70
|
+
description: "SignUp, MeetingBooked or PaidSubscription.",
|
|
62
71
|
})
|
|
63
72
|
@Column({
|
|
64
73
|
type: ColumnType.ShortText,
|
|
@@ -77,7 +86,8 @@ export default class MarketingConversion extends BaseModel {
|
|
|
77
86
|
type: TableColumnType.ObjectID,
|
|
78
87
|
required: false,
|
|
79
88
|
title: "User ID",
|
|
80
|
-
description:
|
|
89
|
+
description:
|
|
90
|
+
"User this conversion belongs to (SignUp conversions). Null when the conversion has no OneUptime user yet, as with a booked meeting.",
|
|
81
91
|
})
|
|
82
92
|
@Column({
|
|
83
93
|
type: ColumnType.ObjectID,
|
|
@@ -170,7 +180,7 @@ export default class MarketingConversion extends BaseModel {
|
|
|
170
180
|
required: false,
|
|
171
181
|
title: "Conversion Value (USD Cents)",
|
|
172
182
|
description:
|
|
173
|
-
"Conversion value in USD cents (monthly recurring revenue for paid subscriptions). Null when unknown (custom pricing) or not applicable (signups).",
|
|
183
|
+
"Conversion value in USD cents (monthly recurring revenue for paid subscriptions). Null when unknown (custom pricing) or not applicable (signups, booked meetings).",
|
|
174
184
|
})
|
|
175
185
|
@Column({
|
|
176
186
|
type: ColumnType.Number,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import Label from "./Label";
|
|
2
2
|
import Project from "./Project";
|
|
3
3
|
import User from "./User";
|
|
4
|
+
import NetworkDeviceLinkRuleScope from "../../Types/NetworkDevice/NetworkDeviceLinkRuleScope";
|
|
4
5
|
import BaseModel from "./DatabaseBaseModel/DatabaseBaseModel";
|
|
5
6
|
import Route from "../../Types/API/Route";
|
|
6
7
|
import ColumnAccessControl from "../../Types/Database/AccessControl/ColumnAccessControl";
|
|
@@ -42,6 +43,15 @@ import {
|
|
|
42
43
|
* cabling. Both cases are reported rather than silently producing an empty
|
|
43
44
|
* map — see NetworkDeviceLinkRuleUtil.
|
|
44
45
|
*
|
|
46
|
+
* "Exactly one" needs a universe, and `scope` names it. By default the
|
|
47
|
+
* question is asked across the whole project, which is what every rule
|
|
48
|
+
* written before that column existed means. Setting it to Site asks the same
|
|
49
|
+
* question once per site instead, so one rule draws the same router-to-switch
|
|
50
|
+
* star in every building rather than reporting fourteen candidate routers and
|
|
51
|
+
* drawing nothing anywhere. A site-scoped rule looks only inside each device's
|
|
52
|
+
* OWN site — it never reaches up to a parent site — and skips devices that
|
|
53
|
+
* have no site at all, reporting how many it walked past.
|
|
54
|
+
*
|
|
45
55
|
* Rules are evaluated when the topology is built, not materialised into
|
|
46
56
|
* NetworkDeviceLink rows: relabelling a device then shows up on the next
|
|
47
57
|
* refresh, and there is nothing to garbage-collect when a rule is deleted.
|
|
@@ -342,6 +352,42 @@ export default class NetworkDeviceLinkRule extends BaseModel {
|
|
|
342
352
|
})
|
|
343
353
|
public parentDeviceLabels?: Array<Label> = undefined;
|
|
344
354
|
|
|
355
|
+
@ColumnAccessControl({
|
|
356
|
+
create: [
|
|
357
|
+
Permission.ProjectOwner,
|
|
358
|
+
Permission.ProjectAdmin,
|
|
359
|
+
Permission.CreateNetworkDeviceLinkRule,
|
|
360
|
+
],
|
|
361
|
+
read: [
|
|
362
|
+
Permission.ProjectOwner,
|
|
363
|
+
Permission.ProjectAdmin,
|
|
364
|
+
Permission.ProjectMember,
|
|
365
|
+
Permission.Viewer,
|
|
366
|
+
Permission.ReadNetworkDeviceLinkRule,
|
|
367
|
+
],
|
|
368
|
+
update: [
|
|
369
|
+
Permission.ProjectOwner,
|
|
370
|
+
Permission.ProjectAdmin,
|
|
371
|
+
Permission.EditNetworkDeviceLinkRule,
|
|
372
|
+
],
|
|
373
|
+
})
|
|
374
|
+
@TableColumn({
|
|
375
|
+
required: false,
|
|
376
|
+
type: TableColumnType.ShortText,
|
|
377
|
+
canReadOnRelationQuery: true,
|
|
378
|
+
title: "Parent Scope",
|
|
379
|
+
description:
|
|
380
|
+
"How wide the 'exactly one parent device' question is asked. Project (the default) looks for one parent across the whole project. Site asks once per site, so the same rule can draw an uplink in every building. Rules created before this existed are Project.",
|
|
381
|
+
example: "Project",
|
|
382
|
+
})
|
|
383
|
+
@Column({
|
|
384
|
+
nullable: true,
|
|
385
|
+
type: ColumnType.ShortText,
|
|
386
|
+
length: ColumnLength.ShortText,
|
|
387
|
+
default: NetworkDeviceLinkRuleScope.Project,
|
|
388
|
+
})
|
|
389
|
+
public scope?: string = undefined;
|
|
390
|
+
|
|
345
391
|
@ColumnAccessControl({
|
|
346
392
|
create: [
|
|
347
393
|
Permission.ProjectOwner,
|
|
@@ -928,7 +928,8 @@ export default class StatusPage extends BaseModel {
|
|
|
928
928
|
required: false,
|
|
929
929
|
type: TableColumnType.HTML,
|
|
930
930
|
title: "Header HTML",
|
|
931
|
-
description:
|
|
931
|
+
description:
|
|
932
|
+
"Status Page Custom HTML Header. Served only from a verified custom domain.",
|
|
932
933
|
})
|
|
933
934
|
@Column({
|
|
934
935
|
nullable: true,
|
|
@@ -973,7 +974,8 @@ export default class StatusPage extends BaseModel {
|
|
|
973
974
|
required: false,
|
|
974
975
|
type: TableColumnType.HTML,
|
|
975
976
|
title: "Footer HTML",
|
|
976
|
-
description:
|
|
977
|
+
description:
|
|
978
|
+
"Status Page Custom HTML Footer. Served only from a verified custom domain.",
|
|
977
979
|
})
|
|
978
980
|
@Column({
|
|
979
981
|
nullable: true,
|
|
@@ -1018,7 +1020,8 @@ export default class StatusPage extends BaseModel {
|
|
|
1018
1020
|
required: false,
|
|
1019
1021
|
type: TableColumnType.CSS,
|
|
1020
1022
|
title: "CSS",
|
|
1021
|
-
description:
|
|
1023
|
+
description:
|
|
1024
|
+
"Status Page Custom CSS. Served only from a verified custom domain.",
|
|
1022
1025
|
})
|
|
1023
1026
|
@Column({
|
|
1024
1027
|
nullable: true,
|
|
@@ -1064,7 +1067,7 @@ export default class StatusPage extends BaseModel {
|
|
|
1064
1067
|
type: TableColumnType.JavaScript,
|
|
1065
1068
|
title: "JavaScript",
|
|
1066
1069
|
description:
|
|
1067
|
-
"Status Page Custom JavaScript. This runs when the status page is loaded.",
|
|
1070
|
+
"Status Page Custom JavaScript. This runs when the status page is loaded from a verified custom domain.",
|
|
1068
1071
|
})
|
|
1069
1072
|
@Column({
|
|
1070
1073
|
nullable: true,
|
|
@@ -117,6 +117,7 @@ import StatusPageSubscriberNotificationTemplate from "../../Models/DatabaseModel
|
|
|
117
117
|
import StatusPageSubscriberNotificationTemplateService, {
|
|
118
118
|
Service as StatusPageSubscriberNotificationTemplateServiceClass,
|
|
119
119
|
} from "../Services/StatusPageSubscriberNotificationTemplateService";
|
|
120
|
+
import { canServeStatusPageCustomizations } from "../Utils/StatusPageCustomizationAccess";
|
|
120
121
|
|
|
121
122
|
type EscapeXmlFunction = (text: string) => string;
|
|
122
123
|
|
|
@@ -860,6 +861,12 @@ export default class StatusPageAPI extends BaseAPI<
|
|
|
860
861
|
ObjectID.validateUUID(statusPageIdParam);
|
|
861
862
|
const objectId: ObjectID = new ObjectID(statusPageIdParam);
|
|
862
863
|
|
|
864
|
+
const allowStatusPageCustomizations: boolean =
|
|
865
|
+
await canServeStatusPageCustomizations({
|
|
866
|
+
req,
|
|
867
|
+
statusPageId: objectId,
|
|
868
|
+
});
|
|
869
|
+
|
|
863
870
|
const select: Select<StatusPage> = {
|
|
864
871
|
_id: true,
|
|
865
872
|
slug: true,
|
|
@@ -870,11 +877,7 @@ export default class StatusPageAPI extends BaseAPI<
|
|
|
870
877
|
pageTitle: true,
|
|
871
878
|
pageDescription: true,
|
|
872
879
|
copyrightText: true,
|
|
873
|
-
customCSS: true,
|
|
874
|
-
customJavaScript: true,
|
|
875
880
|
hidePoweredByOneUptimeBranding: true,
|
|
876
|
-
headerHTML: true,
|
|
877
|
-
footerHTML: true,
|
|
878
881
|
enableEmailSubscribers: true,
|
|
879
882
|
enableSlackSubscribers: true,
|
|
880
883
|
enableMicrosoftTeamsSubscribers: true,
|
|
@@ -911,6 +914,13 @@ export default class StatusPageAPI extends BaseAPI<
|
|
|
911
914
|
enabledLanguages: true,
|
|
912
915
|
};
|
|
913
916
|
|
|
917
|
+
if (allowStatusPageCustomizations) {
|
|
918
|
+
select.customCSS = true;
|
|
919
|
+
select.customJavaScript = true;
|
|
920
|
+
select.headerHTML = true;
|
|
921
|
+
select.footerHTML = true;
|
|
922
|
+
}
|
|
923
|
+
|
|
914
924
|
const hasEnabledSSO: PositiveNumber =
|
|
915
925
|
await StatusPageSsoService.countBy({
|
|
916
926
|
query: {
|
|
@@ -934,6 +944,20 @@ export default class StatusPageAPI extends BaseAPI<
|
|
|
934
944
|
throw new BadDataException("Status Page not found");
|
|
935
945
|
}
|
|
936
946
|
|
|
947
|
+
if (!allowStatusPageCustomizations) {
|
|
948
|
+
/*
|
|
949
|
+
* The conditional select above keeps these values out of the
|
|
950
|
+
* normal database result. Explicit redaction is the second half
|
|
951
|
+
* of the boundary: it prevents a future eager-load, hook, cache,
|
|
952
|
+
* or over-populated service result from serializing tenant code
|
|
953
|
+
* onto the authenticated application's origin.
|
|
954
|
+
*/
|
|
955
|
+
delete item.customCSS;
|
|
956
|
+
delete item.customJavaScript;
|
|
957
|
+
delete item.headerHTML;
|
|
958
|
+
delete item.footerHTML;
|
|
959
|
+
}
|
|
960
|
+
|
|
937
961
|
const footerLinks: Array<StatusPageFooterLink> =
|
|
938
962
|
await StatusPageFooterLinkService.findBy({
|
|
939
963
|
query: {
|
|
@@ -987,6 +1011,7 @@ export default class StatusPageAPI extends BaseAPI<
|
|
|
987
1011
|
StatusPageHeaderLink,
|
|
988
1012
|
),
|
|
989
1013
|
hasEnabledSSO: hasEnabledSSO.toNumber(),
|
|
1014
|
+
allowStatusPageCustomizations,
|
|
990
1015
|
};
|
|
991
1016
|
|
|
992
1017
|
return Response.sendJsonObjectResponse(req, res, response);
|
|
@@ -368,6 +368,13 @@ export const SubscriptionPlans: Array<SubscriptionPlan> =
|
|
|
368
368
|
export const AnalyticsKey: string = process.env["ANALYTICS_KEY"] || "";
|
|
369
369
|
export const AnalyticsHost: string = process.env["ANALYTICS_HOST"] || "";
|
|
370
370
|
|
|
371
|
+
/*
|
|
372
|
+
* Shared secret Cal.com signs booking webhooks with (App/API/CalWebhook.ts).
|
|
373
|
+
* Server-only — must never be added to FRONTEND_ENV_ALLOW_LIST. Empty means
|
|
374
|
+
* the webhook endpoint is disabled and answers 503.
|
|
375
|
+
*/
|
|
376
|
+
export const CalWebhookSecret: string = process.env["CAL_WEBHOOK_SECRET"] || "";
|
|
377
|
+
|
|
371
378
|
/*
|
|
372
379
|
* Google Ads offline conversion uploads (MarketingConversions worker job).
|
|
373
380
|
* Server-only secrets — must never be added to FRONTEND_ENV_ALLOW_LIST.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
export class AddScopeToNetworkDeviceLinkRule1787800000000
|
|
4
|
+
implements MigrationInterface
|
|
5
|
+
{
|
|
6
|
+
public name: string = "AddScopeToNetworkDeviceLinkRule1787800000000";
|
|
7
|
+
|
|
8
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
9
|
+
await queryRunner.query(
|
|
10
|
+
`ALTER TABLE "NetworkDeviceLinkRule" ADD "scope" character varying(100) DEFAULT 'Project'`,
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
15
|
+
await queryRunner.query(
|
|
16
|
+
`ALTER TABLE "NetworkDeviceLinkRule" DROP COLUMN "scope"`,
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Adds the opt-in that decides whether a Global SSO / Global OIDC provider's
|
|
5
|
+
* project attachments act as an ACCESS boundary as well as a provisioning one.
|
|
6
|
+
*
|
|
7
|
+
* Defaults to false, which is the behaviour every existing installation
|
|
8
|
+
* already has: a global login satisfies SSO enforcement for every project the
|
|
9
|
+
* user is a member of. Attachments were introduced as the provisioning
|
|
10
|
+
* allow-list - "a SAML assertion can only ever provision a user into projects
|
|
11
|
+
* that a master admin has explicitly attached here" - and the login routers
|
|
12
|
+
* grant a session on membership of ANY project, so silently reinterpreting
|
|
13
|
+
* them as an access boundary would lock existing users out of projects they
|
|
14
|
+
* legitimately reach, with nothing in the product to recover with.
|
|
15
|
+
*
|
|
16
|
+
* An admin who wants the narrower behaviour turns it on per provider.
|
|
17
|
+
*/
|
|
18
|
+
export class AddRestrictToAttachedProjectsToGlobalSso1787900000000
|
|
19
|
+
implements MigrationInterface
|
|
20
|
+
{
|
|
21
|
+
public name = "AddRestrictToAttachedProjectsToGlobalSso1787900000000";
|
|
22
|
+
|
|
23
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
24
|
+
await queryRunner.query(
|
|
25
|
+
`ALTER TABLE "GlobalSSO" ADD "restrictToAttachedProjects" boolean NOT NULL DEFAULT false`,
|
|
26
|
+
);
|
|
27
|
+
await queryRunner.query(
|
|
28
|
+
`ALTER TABLE "GlobalOIDC" ADD "restrictToAttachedProjects" boolean NOT NULL DEFAULT false`,
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
33
|
+
await queryRunner.query(
|
|
34
|
+
`ALTER TABLE "GlobalOIDC" DROP COLUMN "restrictToAttachedProjects"`,
|
|
35
|
+
);
|
|
36
|
+
await queryRunner.query(
|
|
37
|
+
`ALTER TABLE "GlobalSSO" DROP COLUMN "restrictToAttachedProjects"`,
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -531,6 +531,8 @@ import { AddDeviceRoleAndDeclaredLinkParent1787400000000 } from "./1787400000000
|
|
|
531
531
|
import { AddEnableSearchEngineIndexingToStatusPage1787500000000 } from "./1787500000000-AddEnableSearchEngineIndexingToStatusPage";
|
|
532
532
|
import { AddNetworkDeviceReachabilityColumns1787600000000 } from "./1787600000000-AddNetworkDeviceReachabilityColumns";
|
|
533
533
|
import { FixTotpOtpUrlAlgorithm1787700000000 } from "./1787700000000-FixTotpOtpUrlAlgorithm";
|
|
534
|
+
import { AddScopeToNetworkDeviceLinkRule1787800000000 } from "./1787800000000-AddScopeToNetworkDeviceLinkRule";
|
|
535
|
+
import { AddRestrictToAttachedProjectsToGlobalSso1787900000000 } from "./1787900000000-AddRestrictToAttachedProjectsToGlobalSso";
|
|
534
536
|
import { MigrationName1787142779538 } from "./1787142779538-MigrationName";
|
|
535
537
|
import { MigrationName1787156982416 } from "./1787156982416-MigrationName";
|
|
536
538
|
|
|
@@ -1070,4 +1072,6 @@ export default [
|
|
|
1070
1072
|
AddEnableSearchEngineIndexingToStatusPage1787500000000,
|
|
1071
1073
|
AddNetworkDeviceReachabilityColumns1787600000000,
|
|
1072
1074
|
FixTotpOtpUrlAlgorithm1787700000000,
|
|
1075
|
+
AddScopeToNetworkDeviceLinkRule1787800000000,
|
|
1076
|
+
AddRestrictToAttachedProjectsToGlobalSso1787900000000,
|
|
1073
1077
|
];
|
|
@@ -10,6 +10,18 @@ import crypto from "crypto";
|
|
|
10
10
|
import logger, { getLogAttributesFromRequest } from "../Utils/Logger";
|
|
11
11
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
12
12
|
|
|
13
|
+
/*
|
|
14
|
+
* Slack sends the digest as "v0=" followed by the 64 hex characters of a
|
|
15
|
+
* SHA-256 HMAC. Same reasoning as WhatsAppAuthorization: the header is
|
|
16
|
+
* attacker-controlled on an endpoint whose only authentication IS the
|
|
17
|
+
* signature, and crypto.timingSafeEqual throws RangeError on a length
|
|
18
|
+
* mismatch instead of returning false, so the shape is validated before the
|
|
19
|
+
* comparison. A missing header is covered by the same check - it used to
|
|
20
|
+
* reach Buffer.from(undefined) and throw a TypeError.
|
|
21
|
+
*/
|
|
22
|
+
const SIGNATURE_PREFIX: string = "v0=";
|
|
23
|
+
const HEX_DIGEST_REGEX: RegExp = /^[a-f0-9]{64}$/i;
|
|
24
|
+
|
|
13
25
|
export default class SlackAuthorization {
|
|
14
26
|
@CaptureSpan()
|
|
15
27
|
public static async isAuthorizedSlackRequest(
|
|
@@ -39,7 +51,9 @@ export default class SlackAuthorization {
|
|
|
39
51
|
// validate slack signing secret
|
|
40
52
|
const slackSigningSecret: string = SlackAppSigningSecret.toString();
|
|
41
53
|
|
|
42
|
-
const slackSignature: string = req.headers[
|
|
54
|
+
const slackSignature: string | undefined = req.headers[
|
|
55
|
+
"x-slack-signature"
|
|
56
|
+
] as string | undefined;
|
|
43
57
|
const timestamp: string = req.headers[
|
|
44
58
|
"x-slack-request-timestamp"
|
|
45
59
|
] as string;
|
|
@@ -57,19 +71,42 @@ export default class SlackAuthorization {
|
|
|
57
71
|
logger.debug(`requestBody: `, getLogAttributesFromRequest(req));
|
|
58
72
|
logger.debug(requestBody, getLogAttributesFromRequest(req));
|
|
59
73
|
|
|
74
|
+
const providedDigest: string =
|
|
75
|
+
slackSignature && slackSignature.startsWith(SIGNATURE_PREFIX)
|
|
76
|
+
? slackSignature.slice(SIGNATURE_PREFIX.length)
|
|
77
|
+
: "";
|
|
78
|
+
|
|
79
|
+
if (!HEX_DIGEST_REGEX.test(providedDigest)) {
|
|
80
|
+
logger.error(
|
|
81
|
+
"Slack request has a missing or malformed X-Slack-Signature header.",
|
|
82
|
+
getLogAttributesFromRequest(req),
|
|
83
|
+
);
|
|
84
|
+
return Response.sendErrorResponse(
|
|
85
|
+
req,
|
|
86
|
+
res,
|
|
87
|
+
new BadDataException("Slack Signature Verification Failed."),
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
60
91
|
const baseString: string = `v0:${timestamp}:${requestBody}`;
|
|
61
|
-
const
|
|
92
|
+
const expectedDigest: string = crypto
|
|
93
|
+
.createHmac("sha256", slackSigningSecret)
|
|
94
|
+
.update(baseString)
|
|
95
|
+
.digest("hex");
|
|
62
96
|
|
|
63
97
|
logger.debug(
|
|
64
|
-
`Generated signature: ${
|
|
98
|
+
`Generated signature: ${SIGNATURE_PREFIX}${expectedDigest}`,
|
|
65
99
|
getLogAttributesFromRequest(req),
|
|
66
100
|
);
|
|
67
101
|
|
|
68
|
-
|
|
102
|
+
/*
|
|
103
|
+
* Both sides are decoded from 64 validated hex characters, so both
|
|
104
|
+
* buffers are 32 bytes and timingSafeEqual cannot throw here.
|
|
105
|
+
*/
|
|
69
106
|
if (
|
|
70
107
|
!crypto.timingSafeEqual(
|
|
71
|
-
Buffer.from(
|
|
72
|
-
Buffer.from(
|
|
108
|
+
Buffer.from(expectedDigest, "hex") as Uint8Array,
|
|
109
|
+
Buffer.from(providedDigest, "hex") as Uint8Array,
|
|
73
110
|
)
|
|
74
111
|
) {
|
|
75
112
|
logger.error(
|