@oneuptime/common 12.0.11 → 12.0.13
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/DatabaseBaseModel/DatabaseBaseModel.ts +32 -5
- package/Models/DatabaseModels/NetworkDevice.ts +64 -0
- package/Models/DatabaseModels/StatusPage.ts +59 -0
- package/Models/DatabaseModels/StatusPageOidc.ts +2 -0
- package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +19 -2
- package/Server/API/AIChatAPI.ts +55 -56
- package/Server/API/CommonAPI.ts +89 -0
- package/Server/API/MicrosoftTeamsAPI.ts +20 -9
- package/Server/API/SlackAPI.ts +13 -10
- package/Server/API/StatusPageAPI.ts +2197 -2128
- package/Server/API/TelemetryAPI.ts +72 -3
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787500000000-AddEnableSearchEngineIndexingToStatusPage.ts +30 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787600000000-AddNetworkDeviceReachabilityColumns.ts +47 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Middleware/UserAuthorization.ts +11 -2
- package/Server/Services/ApiKeyPermissionService.ts +116 -2
- package/Server/Services/DatabaseService.ts +17 -6
- package/Server/Services/LogAggregationService.ts +34 -0
- package/Server/Services/LogService.ts +21 -0
- package/Server/Services/NetworkSiteService.ts +12 -0
- package/Server/Services/SpanService.ts +21 -0
- package/Server/Services/TraceAggregationService.ts +15 -0
- package/Server/Services/WorkspaceNotificationRuleService.ts +172 -220
- package/Server/Types/AnalyticsDatabase/ModelPermission.ts +9 -0
- package/Server/Types/Database/Permissions/AccessControlPermission.ts +47 -16
- package/Server/Types/Workflow/Components/Conditions/IfElse.ts +9 -0
- package/Server/Types/Workflow/Components/JavaScript.ts +9 -0
- package/Server/Utils/APIKey/AccessPermission.ts +16 -40
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +144 -0
- package/Server/Utils/LogRedaction.ts +576 -0
- package/Server/Utils/Logger.ts +123 -46
- package/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.ts +94 -23
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +12 -0
- package/Server/Utils/Monitor/MonitorResource.ts +29 -9
- package/Server/Utils/Monitor/NetworkInventoryUtil.ts +28 -7
- package/Server/Utils/SessionReplay/SessionReplayGateCacheStore.ts +56 -10
- package/Server/Utils/StartServer.ts +30 -9
- package/Server/Utils/StatusPageSearchEngineIndexing.ts +33 -0
- package/Server/Utils/Telemetry/ResourceEntityFilter.ts +441 -0
- package/Server/Utils/VM/VMRunner.ts +693 -874
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +488 -46
- package/Tests/App/Dashboard/MonitorTypePicker.test.tsx +300 -0
- package/Tests/App/StatusPage/StatusPageIndexPageRobotsMeta.test.ts +130 -0
- package/Tests/Models/DatabaseModels/DatabaseBaseModelToJSONObjectCache.test.ts +180 -0
- package/Tests/Models/StatusPageEnableSearchEngineIndexing.test.ts +157 -0
- package/Tests/Server/API/AIChatCancelAndFeedback.test.ts +23 -1
- package/Tests/Server/API/AIChatRouteAuthorization.test.ts +785 -0
- package/Tests/Server/API/BaseAPIApiKeyAuth.test.ts +21 -8
- package/Tests/Server/API/CommonAPIAuthGuard.test.ts +290 -0
- package/Tests/Server/API/MicrosoftTeamsManifest.test.ts +7 -1
- package/Tests/Server/API/StatusPageOverviewCache.test.ts +474 -0
- package/Tests/Server/API/StatusPageSeoSearchEngineIndexing.test.ts +189 -0
- package/Tests/Server/Infrastructure/InMemoryTTLCache.test.ts +158 -0
- package/Tests/Server/Infrastructure/Postgres/DeviceRoleAndDeclaredLinkParentMigration.test.ts +9 -60
- package/Tests/Server/Middleware/ProjectAuthorizationApiKeyMiddleware.test.ts +24 -15
- package/Tests/Server/Middleware/UserAuthorization.test.ts +79 -0
- package/Tests/Server/Services/AddNetworkDeviceReachabilityColumnsMigration.test.ts +332 -0
- package/Tests/Server/Services/ApiKeyPermissionService.test.ts +347 -0
- package/Tests/Server/Services/DatabaseServiceUpdateDebugLogging.test.ts +282 -0
- package/Tests/Server/Services/NetworkSiteService.test.ts +56 -2
- package/Tests/Server/Services/TelemetryResourceFacetFilters.test.ts +267 -0
- package/Tests/Server/Services/WorkspaceNotificationRuleTestRuleChannels.test.ts +702 -0
- package/Tests/Server/Types/Database/Permissions/AccessControlPermission.test.ts +165 -0
- package/Tests/Server/Types/Workflow/Components/CustomCodeScriptError.test.ts +121 -0
- package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +48 -36
- package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +213 -0
- package/Tests/Server/Utils/LogRedaction.test.ts +415 -0
- package/Tests/Server/Utils/LoggerCredentialLeak.test.ts +245 -0
- package/Tests/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.test.ts +374 -4
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorJavascriptExpression.test.ts +92 -0
- package/Tests/Server/Utils/Monitor/MonitorResourceIngestedStepSelection.test.ts +455 -0
- package/Tests/Server/Utils/Monitor/NetworkDeviceReachabilityRoundTrip.test.ts +369 -0
- package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +86 -8
- package/Tests/Server/Utils/PrivacyFilterUtil.test.ts +64 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayGateCacheStore.test.ts +207 -0
- package/Tests/Server/Utils/StartServerBodyVerify.test.ts +335 -0
- package/Tests/Server/Utils/StatusPageSearchEngineIndexing.test.ts +60 -0
- package/Tests/Server/Utils/Telemetry/ResourceEntityFilter.test.ts +435 -0
- package/Tests/Server/Utils/VM/VMRunnerIsolation.test.ts +385 -0
- package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +3 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelSend.test.ts +607 -62
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsInstalledTeamIdSpace.test.ts +778 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsTeamInstalls.test.ts +44 -10
- package/Tests/Types/API/HostnameFromAuthority.test.ts +164 -0
- package/Tests/Types/Database/AccessControl/ColumnAccessControlCache.test.ts +344 -0
- package/Tests/Types/Database/DatabasePropertyFindOperator.test.ts +518 -0
- package/Tests/Types/Database/TableColumnMetadataCache.test.ts +286 -0
- package/Tests/Types/Monitor/MonitorStep.test.ts +100 -0
- package/Tests/Types/Monitor/MonitorTypeKeywords.test.ts +360 -0
- package/Tests/Types/StatusPage/SearchEngineIndexing.test.ts +86 -0
- package/Tests/Types/Telemetry/ResourceEntityFacet.test.ts +254 -0
- package/Tests/UI/Components/CardSelect.test.tsx +1021 -0
- package/Tests/UI/Components/MasterPage.test.tsx +36 -1
- package/Tests/Utils/Monitor/MonitorMetricType.test.ts +66 -1
- package/Tests/Utils/Monitor/NetworkTopologyUtil.test.ts +115 -6
- package/Tests/Utils/NetworkDevice/DeviceReachabilityUtil.test.ts +610 -0
- package/Tests/Utils/NetworkSite/SiteStatusRollupUtil.test.ts +114 -45
- package/Types/API/Hostname.ts +79 -0
- package/Types/Database/AccessControl/ColumnAccessControl.ts +33 -11
- package/Types/Database/AccessControl/ColumnBillingAccessControl.ts +33 -11
- package/Types/Database/DatabaseProperty.ts +38 -0
- package/Types/Database/TableColumn.ts +34 -7
- package/Types/Events/Recurring.ts +16 -1
- package/Types/Monitor/CustomCodeMonitor/CustomCodeMonitorResponse.ts +14 -2
- package/Types/Monitor/MonitorStep.ts +13 -1
- package/Types/Monitor/MonitorType.ts +345 -40
- package/Types/Monitor/SSLMonitor/SslMonitorResponse.ts +43 -0
- package/Types/StatusPage/SearchEngineIndexing.ts +61 -0
- package/Types/Telemetry/ResourceEntityFacet.ts +211 -0
- package/UI/Components/CardSelect/CardSelect.tsx +714 -105
- package/UI/Components/EmptyState/EmptyState.tsx +8 -1
- package/UI/Components/Footer/Footer.tsx +32 -6
- package/UI/Components/Forms/Fields/FormField.tsx +3 -0
- package/UI/Components/Forms/Types/Field.ts +8 -0
- package/UI/Components/Loader/PageLoader.tsx +29 -8
- package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +26 -4
- package/UI/Components/Markdown.tsx/LazyMarkdownViewer.tsx +17 -1
- package/UI/Components/MasterPage/MasterPage.tsx +24 -11
- package/Utils/Monitor/MonitorMetricType.ts +16 -1
- package/Utils/Monitor/NetworkTopologyUtil.ts +60 -8
- package/Utils/NetworkDevice/DeviceReachabilityUtil.ts +262 -0
- package/Utils/NetworkSite/SiteStatusRollupUtil.ts +41 -24
- package/Utils/Telemetry/CrossSignalScope.ts +75 -4
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js +20 -4
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDevice.js +66 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPage.js +60 -0
- package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageOidc.js +2 -0
- package/build/dist/Models/DatabaseModels/StatusPageOidc.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
- package/build/dist/Server/API/AIChatAPI.js +48 -35
- package/build/dist/Server/API/AIChatAPI.js.map +1 -1
- package/build/dist/Server/API/CommonAPI.js +59 -0
- package/build/dist/Server/API/CommonAPI.js.map +1 -1
- package/build/dist/Server/API/MicrosoftTeamsAPI.js +20 -6
- package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/SlackAPI.js +13 -10
- package/build/dist/Server/API/SlackAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +745 -673
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/TelemetryAPI.js +34 -3
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787500000000-AddEnableSearchEngineIndexingToStatusPage.js +23 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787500000000-AddEnableSearchEngineIndexingToStatusPage.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787600000000-AddNetworkDeviceReachabilityColumns.js +34 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787600000000-AddNetworkDeviceReachabilityColumns.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/UserAuthorization.js +6 -2
- package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
- package/build/dist/Server/Services/ApiKeyPermissionService.js +90 -1
- package/build/dist/Server/Services/ApiKeyPermissionService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +15 -6
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/LogAggregationService.js +2 -0
- package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
- package/build/dist/Server/Services/LogService.js +16 -0
- package/build/dist/Server/Services/LogService.js.map +1 -1
- package/build/dist/Server/Services/NetworkSiteService.js +12 -0
- package/build/dist/Server/Services/NetworkSiteService.js.map +1 -1
- package/build/dist/Server/Services/SpanService.js +16 -0
- package/build/dist/Server/Services/SpanService.js.map +1 -1
- package/build/dist/Server/Services/TraceAggregationService.js +2 -0
- package/build/dist/Server/Services/TraceAggregationService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +137 -164
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js +9 -0
- package/build/dist/Server/Types/AnalyticsDatabase/ModelPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/AccessControlPermission.js +21 -13
- package/build/dist/Server/Types/Database/Permissions/AccessControlPermission.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js +8 -0
- package/build/dist/Server/Types/Workflow/Components/Conditions/IfElse.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/JavaScript.js +8 -0
- package/build/dist/Server/Types/Workflow/Components/JavaScript.js.map +1 -1
- package/build/dist/Server/Utils/APIKey/AccessPermission.js +9 -32
- package/build/dist/Server/Utils/APIKey/AccessPermission.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +87 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
- package/build/dist/Server/Utils/LogRedaction.js +449 -0
- package/build/dist/Server/Utils/LogRedaction.js.map +1 -0
- package/build/dist/Server/Utils/Logger.js +98 -45
- package/build/dist/Server/Utils/Logger.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.js +65 -13
- package/build/dist/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +12 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +20 -3
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +27 -7
- package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -1
- package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCacheStore.js +29 -10
- package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCacheStore.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js +10 -7
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/StatusPageSearchEngineIndexing.js +24 -0
- package/build/dist/Server/Utils/StatusPageSearchEngineIndexing.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js +311 -0
- package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js.map +1 -0
- package/build/dist/Server/Utils/VM/VMRunner.js +402 -525
- package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +366 -37
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/API/Hostname.js +63 -0
- package/build/dist/Types/API/Hostname.js.map +1 -1
- package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js +20 -6
- package/build/dist/Types/Database/AccessControl/ColumnAccessControl.js.map +1 -1
- package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js +20 -6
- package/build/dist/Types/Database/AccessControl/ColumnBillingAccessControl.js.map +1 -1
- package/build/dist/Types/Database/DatabaseProperty.js +38 -0
- package/build/dist/Types/Database/DatabaseProperty.js.map +1 -1
- package/build/dist/Types/Database/TableColumn.js +24 -6
- package/build/dist/Types/Database/TableColumn.js.map +1 -1
- package/build/dist/Types/Events/Recurring.js +13 -1
- package/build/dist/Types/Events/Recurring.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStep.js +9 -1
- package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorType.js +330 -31
- package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
- package/build/dist/Types/StatusPage/SearchEngineIndexing.js +55 -0
- package/build/dist/Types/StatusPage/SearchEngineIndexing.js.map +1 -0
- package/build/dist/Types/Telemetry/ResourceEntityFacet.js +156 -0
- package/build/dist/Types/Telemetry/ResourceEntityFacet.js.map +1 -0
- package/build/dist/UI/Components/CardSelect/CardSelect.js +360 -40
- package/build/dist/UI/Components/CardSelect/CardSelect.js.map +1 -1
- package/build/dist/UI/Components/EmptyState/EmptyState.js +1 -1
- package/build/dist/UI/Components/EmptyState/EmptyState.js.map +1 -1
- package/build/dist/UI/Components/Footer/Footer.js +7 -4
- package/build/dist/UI/Components/Footer/Footer.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Loader/PageLoader.js +15 -5
- package/build/dist/UI/Components/Loader/PageLoader.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +16 -3
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
- package/build/dist/UI/Components/Markdown.tsx/LazyMarkdownViewer.js +13 -1
- package/build/dist/UI/Components/Markdown.tsx/LazyMarkdownViewer.js.map +1 -1
- package/build/dist/UI/Components/MasterPage/MasterPage.js +10 -2
- package/build/dist/UI/Components/MasterPage/MasterPage.js.map +1 -1
- package/build/dist/Utils/Monitor/MonitorMetricType.js +13 -1
- package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +34 -7
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
- package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js +177 -0
- package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js.map +1 -0
- package/build/dist/Utils/NetworkSite/SiteStatusRollupUtil.js +23 -27
- package/build/dist/Utils/NetworkSite/SiteStatusRollupUtil.js.map +1 -1
- package/build/dist/Utils/Telemetry/CrossSignalScope.js +44 -4
- package/build/dist/Utils/Telemetry/CrossSignalScope.js.map +1 -1
- package/package.json +1 -1
|
@@ -58,6 +58,32 @@ export type DbTypes =
|
|
|
58
58
|
| JSONArray
|
|
59
59
|
| Buffer;
|
|
60
60
|
|
|
61
|
+
/*
|
|
62
|
+
* Metadata-only vanilla instances used by toJSONObject to read table-column
|
|
63
|
+
* metadata. Frozen and never exported, so they can never be mutated or escape
|
|
64
|
+
* this file. _fromJSON must never use this cache — it mutates and returns the
|
|
65
|
+
* instance it constructs.
|
|
66
|
+
*/
|
|
67
|
+
const vanillaModelCache: WeakMap<
|
|
68
|
+
{ new (): DatabaseBaseModel },
|
|
69
|
+
DatabaseBaseModel
|
|
70
|
+
> = new WeakMap();
|
|
71
|
+
|
|
72
|
+
function getVanillaModel(modelType: {
|
|
73
|
+
new (): DatabaseBaseModel;
|
|
74
|
+
}): DatabaseBaseModel {
|
|
75
|
+
let vanillaModel: DatabaseBaseModel | undefined =
|
|
76
|
+
vanillaModelCache.get(modelType);
|
|
77
|
+
|
|
78
|
+
if (!vanillaModel) {
|
|
79
|
+
vanillaModel = new modelType();
|
|
80
|
+
Object.freeze(vanillaModel);
|
|
81
|
+
vanillaModelCache.set(modelType, vanillaModel);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return vanillaModel;
|
|
85
|
+
}
|
|
86
|
+
|
|
61
87
|
export default class DatabaseBaseModel extends BaseEntity {
|
|
62
88
|
@TableColumn({
|
|
63
89
|
title: "ID",
|
|
@@ -252,8 +278,7 @@ export default class DatabaseBaseModel extends BaseEntity {
|
|
|
252
278
|
}
|
|
253
279
|
|
|
254
280
|
public getTableColumnMetadata(columnName: string): TableColumnMetadata {
|
|
255
|
-
|
|
256
|
-
return dictionary[columnName] as TableColumnMetadata;
|
|
281
|
+
return getTableColumn(this, columnName);
|
|
257
282
|
}
|
|
258
283
|
|
|
259
284
|
public hasColumn(columnName: string): boolean {
|
|
@@ -275,8 +300,10 @@ export default class DatabaseBaseModel extends BaseEntity {
|
|
|
275
300
|
}
|
|
276
301
|
|
|
277
302
|
public getColumnAccessControlForAllColumns(): Dictionary<ColumnAccessControl> {
|
|
278
|
-
|
|
279
|
-
|
|
303
|
+
// Copy: the helper's dictionary is cached per class and must stay pristine.
|
|
304
|
+
const dictionary: Dictionary<ColumnAccessControl> = {
|
|
305
|
+
...getColumnAccessControlForAllColumns(this),
|
|
306
|
+
};
|
|
280
307
|
|
|
281
308
|
const defaultColumns: Array<string> = [
|
|
282
309
|
"_id",
|
|
@@ -613,7 +640,7 @@ export default class DatabaseBaseModel extends BaseEntity {
|
|
|
613
640
|
): JSONObject {
|
|
614
641
|
const json: JSONObject = {};
|
|
615
642
|
|
|
616
|
-
const vanillaModel: DatabaseBaseModel =
|
|
643
|
+
const vanillaModel: DatabaseBaseModel = getVanillaModel(modelType);
|
|
617
644
|
|
|
618
645
|
for (const key of vanillaModel.getTableColumns().columns) {
|
|
619
646
|
if ((model as any)[key] === undefined) {
|
|
@@ -1942,6 +1942,70 @@ export default class NetworkDevice extends BaseModel {
|
|
|
1942
1942
|
})
|
|
1943
1943
|
public lastSeenAt?: Date = undefined;
|
|
1944
1944
|
|
|
1945
|
+
@ColumnAccessControl({
|
|
1946
|
+
create: [],
|
|
1947
|
+
read: [
|
|
1948
|
+
Permission.ProjectOwner,
|
|
1949
|
+
Permission.ProjectAdmin,
|
|
1950
|
+
Permission.ProjectMember,
|
|
1951
|
+
Permission.Viewer,
|
|
1952
|
+
Permission.SettingsAdmin,
|
|
1953
|
+
Permission.SettingsMember,
|
|
1954
|
+
Permission.SettingsViewer,
|
|
1955
|
+
Permission.ReadNetworkDevice,
|
|
1956
|
+
],
|
|
1957
|
+
update: [
|
|
1958
|
+
Permission.ProjectOwner,
|
|
1959
|
+
Permission.ProjectAdmin,
|
|
1960
|
+
Permission.EditNetworkDevice,
|
|
1961
|
+
],
|
|
1962
|
+
})
|
|
1963
|
+
@TableColumn({
|
|
1964
|
+
required: false,
|
|
1965
|
+
type: TableColumnType.Date,
|
|
1966
|
+
canReadOnRelationQuery: true,
|
|
1967
|
+
title: "Last Polled At",
|
|
1968
|
+
description:
|
|
1969
|
+
'When the assigned probe last ATTEMPTED an SNMP walk of this device, whether or not the device answered. Paired with lastSeenAt (which only moves on a successful walk) this is what separates "the device did not answer" from "we have not asked recently". Managed by the probe.',
|
|
1970
|
+
})
|
|
1971
|
+
@Column({
|
|
1972
|
+
nullable: true,
|
|
1973
|
+
type: ColumnType.Date,
|
|
1974
|
+
})
|
|
1975
|
+
public lastPolledAt?: Date = undefined;
|
|
1976
|
+
|
|
1977
|
+
@ColumnAccessControl({
|
|
1978
|
+
create: [],
|
|
1979
|
+
read: [
|
|
1980
|
+
Permission.ProjectOwner,
|
|
1981
|
+
Permission.ProjectAdmin,
|
|
1982
|
+
Permission.ProjectMember,
|
|
1983
|
+
Permission.Viewer,
|
|
1984
|
+
Permission.SettingsAdmin,
|
|
1985
|
+
Permission.SettingsMember,
|
|
1986
|
+
Permission.SettingsViewer,
|
|
1987
|
+
Permission.ReadNetworkDevice,
|
|
1988
|
+
],
|
|
1989
|
+
update: [
|
|
1990
|
+
Permission.ProjectOwner,
|
|
1991
|
+
Permission.ProjectAdmin,
|
|
1992
|
+
Permission.EditNetworkDevice,
|
|
1993
|
+
],
|
|
1994
|
+
})
|
|
1995
|
+
@TableColumn({
|
|
1996
|
+
required: false,
|
|
1997
|
+
type: TableColumnType.Boolean,
|
|
1998
|
+
canReadOnRelationQuery: true,
|
|
1999
|
+
title: "Is Reachable",
|
|
2000
|
+
description:
|
|
2001
|
+
"Whether the most recent SNMP walk reached this device. NULL means it has never been polled. This — not the age of lastSeenAt — is what the device list, the topology graph and the site rollup read, so a device whose last poll succeeded is never shown as down just because the probe is behind schedule. Managed by the probe.",
|
|
2002
|
+
})
|
|
2003
|
+
@Column({
|
|
2004
|
+
nullable: true,
|
|
2005
|
+
type: ColumnType.Boolean,
|
|
2006
|
+
})
|
|
2007
|
+
public isReachable?: boolean = undefined;
|
|
2008
|
+
|
|
1945
2009
|
@ColumnAccessControl({
|
|
1946
2010
|
create: [],
|
|
1947
2011
|
read: [
|
|
@@ -317,6 +317,65 @@ export default class StatusPage extends BaseModel {
|
|
|
317
317
|
})
|
|
318
318
|
public pageDescription?: string = undefined;
|
|
319
319
|
|
|
320
|
+
/*
|
|
321
|
+
* Search engine indexing opt-out. Indexing stays on unless the owner turns
|
|
322
|
+
* it off, so this defaults to true and every pre-existing status page keeps
|
|
323
|
+
* being indexed exactly as it is today.
|
|
324
|
+
*
|
|
325
|
+
* Turning it off is served two ways, both of which a crawler honours before
|
|
326
|
+
* it decides to index: a <meta name="robots" content="noindex, nofollow">
|
|
327
|
+
* in the server-rendered index.ejs (so it is there before the JS bundle
|
|
328
|
+
* loads) and an X-Robots-Tag response header (so it also covers the RSS
|
|
329
|
+
* feed and llms.txt, which are not HTML).
|
|
330
|
+
*
|
|
331
|
+
* Deliberately NOT wired into robots.txt: Disallow blocks the crawl, and a
|
|
332
|
+
* crawler that never fetches the page never sees the noindex, so the URL can
|
|
333
|
+
* still surface in results. Letting crawlers in and telling them not to
|
|
334
|
+
* index is what actually removes the page.
|
|
335
|
+
*/
|
|
336
|
+
@ColumnAccessControl({
|
|
337
|
+
create: [
|
|
338
|
+
Permission.ProjectOwner,
|
|
339
|
+
Permission.ProjectAdmin,
|
|
340
|
+
Permission.ProjectMember,
|
|
341
|
+
Permission.StatusPageAdmin,
|
|
342
|
+
Permission.StatusPageMember,
|
|
343
|
+
Permission.CreateProjectStatusPage,
|
|
344
|
+
],
|
|
345
|
+
read: [
|
|
346
|
+
Permission.ProjectOwner,
|
|
347
|
+
Permission.ProjectAdmin,
|
|
348
|
+
Permission.ProjectMember,
|
|
349
|
+
Permission.Viewer,
|
|
350
|
+
Permission.StatusPageAdmin,
|
|
351
|
+
Permission.StatusPageMember,
|
|
352
|
+
Permission.StatusPageViewer,
|
|
353
|
+
Permission.ReadProjectStatusPage,
|
|
354
|
+
],
|
|
355
|
+
update: [
|
|
356
|
+
Permission.ProjectOwner,
|
|
357
|
+
Permission.ProjectAdmin,
|
|
358
|
+
Permission.ProjectMember,
|
|
359
|
+
Permission.StatusPageAdmin,
|
|
360
|
+
Permission.StatusPageMember,
|
|
361
|
+
Permission.EditProjectStatusPage,
|
|
362
|
+
],
|
|
363
|
+
})
|
|
364
|
+
@TableColumn({
|
|
365
|
+
isDefaultValueColumn: true,
|
|
366
|
+
type: TableColumnType.Boolean,
|
|
367
|
+
title: "Enable Search Engine Indexing",
|
|
368
|
+
description:
|
|
369
|
+
"Should search engines like Google and Bing be allowed to index this status page? Turn this off to keep the page reachable by link but out of search results.",
|
|
370
|
+
defaultValue: true,
|
|
371
|
+
})
|
|
372
|
+
@Column({
|
|
373
|
+
type: ColumnType.Boolean,
|
|
374
|
+
default: true,
|
|
375
|
+
nullable: false,
|
|
376
|
+
})
|
|
377
|
+
public enableSearchEngineIndexing?: boolean = undefined;
|
|
378
|
+
|
|
320
379
|
@ColumnAccessControl({
|
|
321
380
|
create: [
|
|
322
381
|
Permission.ProjectOwner,
|
|
@@ -14,6 +14,7 @@ import CanAccessIfCanReadOn from "../../Types/Database/CanAccessIfCanReadOn";
|
|
|
14
14
|
import ColumnLength from "../../Types/Database/ColumnLength";
|
|
15
15
|
import ColumnType from "../../Types/Database/ColumnType";
|
|
16
16
|
import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
|
|
17
|
+
import EnableDocumentation from "../../Types/Database/EnableDocumentation";
|
|
17
18
|
import TableColumn from "../../Types/Database/TableColumn";
|
|
18
19
|
import TableColumnType from "../../Types/Database/TableColumnType";
|
|
19
20
|
import TableMetadata from "../../Types/Database/TableMetadata";
|
|
@@ -24,6 +25,7 @@ import ObjectID from "../../Types/ObjectID";
|
|
|
24
25
|
import Permission from "../../Types/Permission";
|
|
25
26
|
import { Column, Entity, Index, JoinColumn, ManyToOne } from "typeorm";
|
|
26
27
|
|
|
28
|
+
@EnableDocumentation()
|
|
27
29
|
@CanAccessIfCanReadOn("statusPage")
|
|
28
30
|
@TableEditionAccessControl({
|
|
29
31
|
requiresEnterprise: true,
|
|
@@ -45,9 +45,26 @@ export interface MicrosoftTeamsChat {
|
|
|
45
45
|
* roster member of. Listing a team is therefore NOT evidence that we can post
|
|
46
46
|
* to it — this record is, which is why channel sends check it before calling
|
|
47
47
|
* the Bot Framework.
|
|
48
|
+
*
|
|
49
|
+
* A Teams team has TWO ids and they are not interchangeable. Bot activities
|
|
50
|
+
* carry the thread id (channelData.team.id, "19:...@thread.tacv2"); Graph — and
|
|
51
|
+
* therefore every team picker, notification rule and channel URL in OneUptime —
|
|
52
|
+
* uses the AAD group id (a GUID, channelData.team.aadGroupId). Records used to
|
|
53
|
+
* be keyed by the thread id and looked up by the group id, so the lookup could
|
|
54
|
+
* never hit. Both are stored explicitly now so neither side has to guess which
|
|
55
|
+
* one it holds.
|
|
48
56
|
*/
|
|
49
57
|
export interface MicrosoftTeamsInstalledTeam {
|
|
50
|
-
|
|
58
|
+
/*
|
|
59
|
+
* Map key. The Graph team id when we could resolve it, otherwise the Teams
|
|
60
|
+
* thread id — a record written before this shipped, or one whose group id
|
|
61
|
+
* could not be resolved, keeps the thread id here so uninstall still matches.
|
|
62
|
+
* Read it through MicrosoftTeamsUtil.indexInstalledTeamsByGraphTeamId rather
|
|
63
|
+
* than assuming which id space it is in.
|
|
64
|
+
*/
|
|
65
|
+
id: string;
|
|
66
|
+
graphTeamId?: string | undefined; // AAD group id — what Graph and notification rules use. Absent on unresolved records.
|
|
67
|
+
teamsThreadId?: string | undefined; // channelData.team.id ("19:...@thread.tacv2") — what bot activities carry.
|
|
51
68
|
name?: string | undefined;
|
|
52
69
|
serviceUrl?: string | undefined; // Bot Framework service URL captured on install. Required for GCC/DoD.
|
|
53
70
|
addedAt?: string | undefined;
|
|
@@ -79,7 +96,7 @@ export interface MicrosoftTeamsMiscData extends MiscData {
|
|
|
79
96
|
availableTeams?: Record<string, MicrosoftTeamsTeam>; // keyed by team id. Every team Graph can see in the tenant.
|
|
80
97
|
appAccessTokenExpiresAt?: string;
|
|
81
98
|
availableChats?: Record<string, MicrosoftTeamsChat>; // keyed by chat id. Chats the OneUptime app has been added to.
|
|
82
|
-
installedTeams?: Record<string, MicrosoftTeamsInstalledTeam>; // keyed by
|
|
99
|
+
installedTeams?: Record<string, MicrosoftTeamsInstalledTeam>; // keyed by MicrosoftTeamsInstalledTeam.id — see that type, the key is not always a Graph team id.
|
|
83
100
|
}
|
|
84
101
|
|
|
85
102
|
export type WorkspaceMiscData = SlackMiscData | MicrosoftTeamsMiscData;
|
package/Server/API/AIChatAPI.ts
CHANGED
|
@@ -9,7 +9,6 @@ import Express, {
|
|
|
9
9
|
import Response from "../Utils/Response";
|
|
10
10
|
import BadDataException from "../../Types/Exception/BadDataException";
|
|
11
11
|
import PaymentRequiredException from "../../Types/Exception/PaymentRequiredException";
|
|
12
|
-
import NotAuthorizedException from "../../Types/Exception/NotAuthorizedException";
|
|
13
12
|
import DatabaseCommonInteractionProps from "../../Types/BaseDatabase/DatabaseCommonInteractionProps";
|
|
14
13
|
import ObjectID from "../../Types/ObjectID";
|
|
15
14
|
import OneUptimeDate from "../../Types/Date";
|
|
@@ -89,18 +88,16 @@ router.post(
|
|
|
89
88
|
const props: DatabaseCommonInteractionProps =
|
|
90
89
|
await CommonAPI.getDatabaseCommonInteractionProps(req);
|
|
91
90
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
const projectId: ObjectID = props.tenantId;
|
|
103
|
-
const userId: ObjectID = props.userId;
|
|
91
|
+
/*
|
|
92
|
+
* Membership, not just a session. getUserMiddleware admits an
|
|
93
|
+
* unauthenticated request as "public" and reads the tenant id straight
|
|
94
|
+
* out of the caller-supplied `tenantid` header, so the pair of checks
|
|
95
|
+
* this replaces proved only that SOMEBODY was logged in and had named
|
|
96
|
+
* SOME project - never that they belonged to the project they named.
|
|
97
|
+
*/
|
|
98
|
+
const projectId: ObjectID =
|
|
99
|
+
CommonAPI.assertAuthenticatedProjectMember(props);
|
|
100
|
+
const userId: ObjectID = props.userId!;
|
|
104
101
|
|
|
105
102
|
const content: string = (req.body["content"] as string) || "";
|
|
106
103
|
|
|
@@ -502,17 +499,30 @@ router.post(
|
|
|
502
499
|
const props: DatabaseCommonInteractionProps =
|
|
503
500
|
await CommonAPI.getDatabaseCommonInteractionProps(req);
|
|
504
501
|
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
502
|
+
/*
|
|
503
|
+
* Membership, not just a session - see the note on send-message. This
|
|
504
|
+
* route is where that distinction was load-bearing: it took the project
|
|
505
|
+
* id straight from the header and handed it to root-privileged reads,
|
|
506
|
+
* so any logged-in user could name ANY project's UUID and be told which
|
|
507
|
+
* LLM providers that project has - their names, descriptions, types and
|
|
508
|
+
* models (GHSA-hm7m-9qjj-xj5x).
|
|
509
|
+
*/
|
|
510
|
+
const projectId: ObjectID =
|
|
511
|
+
CommonAPI.assertAuthenticatedProjectMember(props);
|
|
514
512
|
|
|
515
|
-
|
|
513
|
+
/*
|
|
514
|
+
* Membership is still not read authorisation. The very same rows served
|
|
515
|
+
* by a plain GET /llm-provider are gated there by LlmProvider's own read
|
|
516
|
+
* list, and a member whose teams grant none of those permissions has no
|
|
517
|
+
* business reading them through the chat picker instead. Asking the
|
|
518
|
+
* model for that list keeps the two paths from drifting apart.
|
|
519
|
+
*/
|
|
520
|
+
CommonAPI.assertPermittedInProject({
|
|
521
|
+
databaseProps: props,
|
|
522
|
+
allowedPermissions: new LlmProvider().getReadPermissions(),
|
|
523
|
+
errorMessage:
|
|
524
|
+
"You do not have permission to read this project's AI providers.",
|
|
525
|
+
});
|
|
516
526
|
|
|
517
527
|
const [providers, defaultProvider]: [
|
|
518
528
|
Array<LlmProvider>,
|
|
@@ -561,18 +571,16 @@ router.post(
|
|
|
561
571
|
const props: DatabaseCommonInteractionProps =
|
|
562
572
|
await CommonAPI.getDatabaseCommonInteractionProps(req);
|
|
563
573
|
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
const projectId: ObjectID = props.tenantId;
|
|
575
|
-
const userId: ObjectID = props.userId;
|
|
574
|
+
/*
|
|
575
|
+
* Membership, not just a session. getUserMiddleware admits an
|
|
576
|
+
* unauthenticated request as "public" and reads the tenant id straight
|
|
577
|
+
* out of the caller-supplied `tenantid` header, so the pair of checks
|
|
578
|
+
* this replaces proved only that SOMEBODY was logged in and had named
|
|
579
|
+
* SOME project - never that they belonged to the project they named.
|
|
580
|
+
*/
|
|
581
|
+
const projectId: ObjectID =
|
|
582
|
+
CommonAPI.assertAuthenticatedProjectMember(props);
|
|
583
|
+
const userId: ObjectID = props.userId!;
|
|
576
584
|
|
|
577
585
|
const conversationIdString: string = req.body["conversationId"] as string;
|
|
578
586
|
const assistantMessageIdString: string = req.body[
|
|
@@ -743,18 +751,16 @@ router.post(
|
|
|
743
751
|
const props: DatabaseCommonInteractionProps =
|
|
744
752
|
await CommonAPI.getDatabaseCommonInteractionProps(req);
|
|
745
753
|
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
const projectId: ObjectID = props.tenantId;
|
|
757
|
-
const userId: ObjectID = props.userId;
|
|
754
|
+
/*
|
|
755
|
+
* Membership, not just a session. getUserMiddleware admits an
|
|
756
|
+
* unauthenticated request as "public" and reads the tenant id straight
|
|
757
|
+
* out of the caller-supplied `tenantid` header, so the pair of checks
|
|
758
|
+
* this replaces proved only that SOMEBODY was logged in and had named
|
|
759
|
+
* SOME project - never that they belonged to the project they named.
|
|
760
|
+
*/
|
|
761
|
+
const projectId: ObjectID =
|
|
762
|
+
CommonAPI.assertAuthenticatedProjectMember(props);
|
|
763
|
+
const userId: ObjectID = props.userId!;
|
|
758
764
|
|
|
759
765
|
const conversationIdString: string = req.body["conversationId"] as string;
|
|
760
766
|
|
|
@@ -913,15 +919,8 @@ router.post(
|
|
|
913
919
|
const props: DatabaseCommonInteractionProps =
|
|
914
920
|
await CommonAPI.getDatabaseCommonInteractionProps(req);
|
|
915
921
|
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
"AI chat requires a logged-in user session.",
|
|
919
|
-
);
|
|
920
|
-
}
|
|
921
|
-
|
|
922
|
-
if (!props.tenantId) {
|
|
923
|
-
throw new BadDataException("Project ID is required (tenantid header).");
|
|
924
|
-
}
|
|
922
|
+
// Membership, not just a session - see the note on send-message.
|
|
923
|
+
CommonAPI.assertAuthenticatedProjectMember(props);
|
|
925
924
|
|
|
926
925
|
const messageIdString: string = req.body["messageId"] as string;
|
|
927
926
|
|
package/Server/API/CommonAPI.ts
CHANGED
|
@@ -9,6 +9,14 @@ import SpanUtil from "../Utils/Telemetry/SpanUtil";
|
|
|
9
9
|
import ObjectID from "../../Types/ObjectID";
|
|
10
10
|
import BadDataException from "../../Types/Exception/BadDataException";
|
|
11
11
|
import NotAuthorizedException from "../../Types/Exception/NotAuthorizedException";
|
|
12
|
+
import Permission, {
|
|
13
|
+
PermissionHelper,
|
|
14
|
+
PermissionProps,
|
|
15
|
+
UserPermission,
|
|
16
|
+
} from "../../Types/Permission";
|
|
17
|
+
import DatabaseCommonInteractionPropsUtil, {
|
|
18
|
+
PermissionType,
|
|
19
|
+
} from "../../Types/BaseDatabase/DatabaseCommonInteractionPropsUtil";
|
|
12
20
|
|
|
13
21
|
export default class CommonAPI {
|
|
14
22
|
/*
|
|
@@ -132,6 +140,87 @@ export default class CommonAPI {
|
|
|
132
140
|
}
|
|
133
141
|
}
|
|
134
142
|
|
|
143
|
+
/*
|
|
144
|
+
* Membership is not read authorisation.
|
|
145
|
+
*
|
|
146
|
+
* assertAuthenticatedProjectMember proves the caller belongs to the project
|
|
147
|
+
* they named in the `tenantid` header; it says nothing about WHICH of that
|
|
148
|
+
* project's data they may see. A custom route that then reads with
|
|
149
|
+
* `isRoot: true` has skipped the permission check the equivalent CRUD read
|
|
150
|
+
* would have run, so the route has to make that check itself - otherwise a
|
|
151
|
+
* member whose teams grant nothing but, say, ReadProjectIncident still reads
|
|
152
|
+
* everything else the route happens to touch.
|
|
153
|
+
*
|
|
154
|
+
* The permissions consulted are the caller's grants in props.tenantId, so
|
|
155
|
+
* this must be called after assertAuthenticatedProjectMember has confirmed
|
|
156
|
+
* that tenant is one the caller actually belongs to.
|
|
157
|
+
*
|
|
158
|
+
* `allowedPermissions` is normally the model's own declared list, e.g.
|
|
159
|
+
* `new LlmProvider().getReadPermissions()`, so the custom route stays in
|
|
160
|
+
* step with the CRUD endpoint for the same data instead of inventing a
|
|
161
|
+
* second, quietly diverging rule.
|
|
162
|
+
*
|
|
163
|
+
* Permissions that are never assigned INSIDE a project are stripped from
|
|
164
|
+
* that list first. This matters: getUserPermissions merges the caller's
|
|
165
|
+
* global permissions in, and every caller - including an anonymous one -
|
|
166
|
+
* carries Permission.Public, which a model's read list very often contains
|
|
167
|
+
* (LlmProvider has it so the shared global providers stay readable). Left
|
|
168
|
+
* in, it would make this guard pass for anyone. What remains is the
|
|
169
|
+
* tenant-assignable subset, which is exactly what a team can grant.
|
|
170
|
+
*
|
|
171
|
+
* Stripping can empty the list, and an empty list denies everyone rather
|
|
172
|
+
* than admitting them - a model whose read access is purely public has no
|
|
173
|
+
* business behind this guard, and failing closed is the safe direction.
|
|
174
|
+
*
|
|
175
|
+
* Master admins bypass, matching every other permission gate in the API.
|
|
176
|
+
*/
|
|
177
|
+
public static assertPermittedInProject(data: {
|
|
178
|
+
databaseProps: DatabaseCommonInteractionProps;
|
|
179
|
+
allowedPermissions: Array<Permission>;
|
|
180
|
+
errorMessage?: string | undefined;
|
|
181
|
+
}): void {
|
|
182
|
+
if (data.databaseProps.isMasterAdmin) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const tenantAssignablePermissions: Array<Permission> =
|
|
187
|
+
PermissionHelper.getTenantPermissionProps().map(
|
|
188
|
+
(permissionProps: PermissionProps) => {
|
|
189
|
+
return permissionProps.permission;
|
|
190
|
+
},
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
const requiredPermissions: Array<Permission> =
|
|
194
|
+
data.allowedPermissions.filter((permission: Permission) => {
|
|
195
|
+
return tenantAssignablePermissions.includes(permission);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
/*
|
|
199
|
+
* Allow-only: getUserPermissions discriminates grants from denials by
|
|
200
|
+
* isBlockPermission, so a team's explicit BLOCK row for one of these
|
|
201
|
+
* permissions must never be counted as a grant of it.
|
|
202
|
+
*/
|
|
203
|
+
const grantedPermissions: Array<Permission> =
|
|
204
|
+
DatabaseCommonInteractionPropsUtil.getUserPermissions(
|
|
205
|
+
data.databaseProps,
|
|
206
|
+
PermissionType.Allow,
|
|
207
|
+
).map((userPermission: UserPermission) => {
|
|
208
|
+
return userPermission.permission;
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
if (
|
|
212
|
+
!PermissionHelper.doesPermissionsIntersect(
|
|
213
|
+
grantedPermissions,
|
|
214
|
+
requiredPermissions,
|
|
215
|
+
)
|
|
216
|
+
) {
|
|
217
|
+
throw new NotAuthorizedException(
|
|
218
|
+
data.errorMessage ||
|
|
219
|
+
"You do not have permission to access this project's data.",
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
135
224
|
@CaptureSpan()
|
|
136
225
|
public static async getDatabaseCommonInteractionProps(
|
|
137
226
|
req: ExpressRequest,
|
|
@@ -177,6 +177,16 @@ export default class MicrosoftTeamsAPI {
|
|
|
177
177
|
type: "Application",
|
|
178
178
|
name: "ChatMember.Read.Chat",
|
|
179
179
|
},
|
|
180
|
+
/*
|
|
181
|
+
* Lets OneUptime confirm, per team, that the installed OneUptime app
|
|
182
|
+
* is the package built from THIS deployment before telling an admin
|
|
183
|
+
* their app is missing. Without it the installed-apps read falls back
|
|
184
|
+
* to "unknown" and the send is handed to Microsoft to reject.
|
|
185
|
+
*/
|
|
186
|
+
{
|
|
187
|
+
type: "Application",
|
|
188
|
+
name: "TeamsAppInstallation.Read.Group",
|
|
189
|
+
},
|
|
180
190
|
],
|
|
181
191
|
},
|
|
182
192
|
},
|
|
@@ -386,12 +396,13 @@ export default class MicrosoftTeamsAPI {
|
|
|
386
396
|
"https://graph.microsoft.com/User.Read https://graph.microsoft.com/Team.ReadBasic.All https://graph.microsoft.com/Channel.ReadBasic.All https://graph.microsoft.com/ChannelMessage.Send",
|
|
387
397
|
};
|
|
388
398
|
|
|
399
|
+
/*
|
|
400
|
+
* The token request body holds the app client secret and the
|
|
401
|
+
* authorization code, and the response holds the user access and
|
|
402
|
+
* refresh tokens -- neither is logged.
|
|
403
|
+
*/
|
|
389
404
|
logger.debug(
|
|
390
|
-
"Microsoft Teams
|
|
391
|
-
getLogAttributesFromRequest(req as any),
|
|
392
|
-
);
|
|
393
|
-
logger.debug(
|
|
394
|
-
tokenRequestBody,
|
|
405
|
+
"Exchanging Microsoft Teams authorization code for an access token.",
|
|
395
406
|
getLogAttributesFromRequest(req as any),
|
|
396
407
|
);
|
|
397
408
|
|
|
@@ -420,10 +431,9 @@ export default class MicrosoftTeamsAPI {
|
|
|
420
431
|
|
|
421
432
|
const tokenData: JSONObject = tokenResponse.data;
|
|
422
433
|
logger.debug(
|
|
423
|
-
"Microsoft Teams
|
|
434
|
+
"Microsoft Teams token exchange completed.",
|
|
424
435
|
getLogAttributesFromRequest(req as any),
|
|
425
436
|
);
|
|
426
|
-
logger.debug(tokenData, getLogAttributesFromRequest(req as any));
|
|
427
437
|
|
|
428
438
|
if (!tokenData["access_token"]) {
|
|
429
439
|
return Response.sendErrorResponse(
|
|
@@ -725,11 +735,12 @@ export default class MicrosoftTeamsAPI {
|
|
|
725
735
|
Date.now() + Math.max(0, (expiresInSec - 60) * 1000),
|
|
726
736
|
).toISOString();
|
|
727
737
|
|
|
738
|
+
// tokenData carries the app access token; only its expiry is logged.
|
|
728
739
|
logger.debug(
|
|
729
|
-
"
|
|
740
|
+
"Microsoft Graph app token acquired via admin consent. expiresAt: " +
|
|
741
|
+
expiresAtIso,
|
|
730
742
|
getLogAttributesFromRequest(req as any),
|
|
731
743
|
);
|
|
732
|
-
logger.debug(tokenData, getLogAttributesFromRequest(req as any));
|
|
733
744
|
|
|
734
745
|
// Get available teams from user auth token
|
|
735
746
|
const userId: string = stateParts[1]!;
|
package/Server/API/SlackAPI.ts
CHANGED
|
@@ -164,11 +164,15 @@ export default class SlackAPI {
|
|
|
164
164
|
redirect_uri: redirectUri.toString(),
|
|
165
165
|
};
|
|
166
166
|
|
|
167
|
+
/*
|
|
168
|
+
* Neither the token request nor the token response is logged: the
|
|
169
|
+
* request carries the app client secret and the authorization code,
|
|
170
|
+
* and the response carries the bot and user access tokens.
|
|
171
|
+
*/
|
|
167
172
|
logger.debug(
|
|
168
|
-
"Slack
|
|
173
|
+
"Exchanging Slack authorization code for an access token.",
|
|
169
174
|
getLogAttributesFromRequest(req as any),
|
|
170
175
|
);
|
|
171
|
-
logger.debug(requestBody, getLogAttributesFromRequest(req as any));
|
|
172
176
|
|
|
173
177
|
// send the request to slack api to get the access token https://slack.com/api/oauth.v2.access
|
|
174
178
|
|
|
@@ -188,10 +192,9 @@ export default class SlackAPI {
|
|
|
188
192
|
const responseBody: JSONObject = response.data;
|
|
189
193
|
|
|
190
194
|
logger.debug(
|
|
191
|
-
"Slack
|
|
195
|
+
"Slack token exchange completed. ok: " + String(responseBody["ok"]),
|
|
192
196
|
getLogAttributesFromRequest(req as any),
|
|
193
197
|
);
|
|
194
|
-
logger.debug(responseBody, getLogAttributesFromRequest(req as any));
|
|
195
198
|
|
|
196
199
|
let slackTeamId: string | undefined = undefined;
|
|
197
200
|
let slackBotAccessToken: string | undefined = undefined;
|
|
@@ -384,11 +387,11 @@ export default class SlackAPI {
|
|
|
384
387
|
redirect_uri: redirectUri.toString(),
|
|
385
388
|
};
|
|
386
389
|
|
|
390
|
+
// Same as above: the body holds the client secret and the auth code.
|
|
387
391
|
logger.debug(
|
|
388
|
-
"Slack
|
|
392
|
+
"Exchanging Slack authorization code for a user token.",
|
|
389
393
|
getLogAttributesFromRequest(req as any),
|
|
390
394
|
);
|
|
391
|
-
logger.debug(requestBody, getLogAttributesFromRequest(req as any));
|
|
392
395
|
|
|
393
396
|
// send the request to slack api to get the access token https://slack.com/api/oauth.v2.access
|
|
394
397
|
|
|
@@ -408,10 +411,10 @@ export default class SlackAPI {
|
|
|
408
411
|
const responseBody: JSONObject = response.data;
|
|
409
412
|
|
|
410
413
|
logger.debug(
|
|
411
|
-
"Slack
|
|
414
|
+
"Slack user token exchange completed. ok: " +
|
|
415
|
+
String(responseBody["ok"]),
|
|
412
416
|
getLogAttributesFromRequest(req as any),
|
|
413
417
|
);
|
|
414
|
-
logger.debug(responseBody, getLogAttributesFromRequest(req as any));
|
|
415
418
|
|
|
416
419
|
if (
|
|
417
420
|
responseBody["id_token"] &&
|
|
@@ -425,11 +428,11 @@ export default class SlackAPI {
|
|
|
425
428
|
"base64",
|
|
426
429
|
).toString("utf8"),
|
|
427
430
|
);
|
|
431
|
+
// The decoded ID token is identity material; only the fact is logged.
|
|
428
432
|
logger.debug(
|
|
429
|
-
"Decoded ID
|
|
433
|
+
"Decoded Slack ID token.",
|
|
430
434
|
getLogAttributesFromRequest(req as any),
|
|
431
435
|
);
|
|
432
|
-
logger.debug(decodedIdToken, getLogAttributesFromRequest(req as any));
|
|
433
436
|
responseBody["id_token"] = decodedIdToken;
|
|
434
437
|
}
|
|
435
438
|
|