@oneuptime/common 7.0.3129 → 7.0.3140
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/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel.ts +1 -1
- package/Models/AnalyticsModels/Index.ts +28 -1
- package/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.ts +4 -0
- package/Models/DatabaseModels/Incident.ts +5 -0
- package/Models/DatabaseModels/Index.ts +35 -1
- package/Server/Middleware/ProjectAuthorization.ts +5 -3
- package/Server/Middleware/UserAuthorization.ts +32 -5
- package/Server/Services/AccessTokenService.ts +10 -154
- package/Server/Services/AnalyticsDatabaseService.ts +2 -2
- package/Server/Services/DatabaseService.ts +77 -4
- package/Server/Services/StatusPageService.ts +1 -1
- package/Server/Types/Permission/PermissionNamespace.ts +6 -0
- package/Server/Utils/APIKey/AccessPermission.ts +111 -0
- package/Server/Utils/Cookie.ts +15 -1
- package/Server/Utils/Monitor/MonitorResource.ts +57 -23
- package/Server/Utils/Realtime.ts +288 -79
- package/Server/Utils/UserPermission/UserPermission.ts +81 -0
- package/Tests/Server/Middleware/ProjectAuthorization.test.ts +3 -3
- package/Tests/Server/Middleware/UserAuthorization.test.ts +14 -10
- package/Tests/Server/Services/ScheduledMaintenanceService.test.ts +4 -2
- package/Tests/Server/Utils/Cookie.test.ts +1 -1
- package/Types/Database/TableMetadata.ts +5 -0
- package/Types/Realtime/EnableRealtimeEventsOn.ts +5 -0
- package/Types/Realtime/EventName.ts +5 -0
- package/Types/Realtime/ListenToModelEventJSON.ts +10 -0
- package/Types/Realtime/ModelEventType.ts +7 -0
- package/UI/Components/HeaderAlert/HeaderModelAlert.tsx +1 -1
- package/UI/Components/ModelList/ModelList.tsx +1 -1
- package/UI/Components/ModelTable/BaseModelTable.tsx +1 -1
- package/UI/Utils/Realtime.ts +28 -19
- package/Utils/Realtime.ts +10 -29
- package/build/dist/Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/Index.js +14 -0
- package/build/dist/Models/AnalyticsModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Incident.js +5 -0
- package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +16 -1
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Server/Middleware/ProjectAuthorization.js +3 -3
- package/build/dist/Server/Middleware/ProjectAuthorization.js.map +1 -1
- package/build/dist/Server/Middleware/UserAuthorization.js +13 -5
- package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
- package/build/dist/Server/Services/AccessTokenService.js +6 -108
- package/build/dist/Server/Services/AccessTokenService.js.map +1 -1
- package/build/dist/Server/Services/AnalyticsDatabaseService.js +2 -2
- package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +43 -4
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +1 -1
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Types/Permission/PermissionNamespace.js +7 -0
- package/build/dist/Server/Types/Permission/PermissionNamespace.js.map +1 -0
- package/build/dist/Server/Utils/APIKey/AccessPermission.js +78 -0
- package/build/dist/Server/Utils/APIKey/AccessPermission.js.map +1 -0
- package/build/dist/Server/Utils/Cookie.js +11 -1
- package/build/dist/Server/Utils/Cookie.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +58 -32
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Realtime.js +181 -51
- package/build/dist/Server/Utils/Realtime.js.map +1 -1
- package/build/dist/Server/Utils/UserPermission/UserPermission.js +47 -0
- package/build/dist/Server/Utils/UserPermission/UserPermission.js.map +1 -0
- package/build/dist/Tests/Server/Middleware/ProjectAuthorization.test.js +3 -3
- package/build/dist/Tests/Server/Middleware/ProjectAuthorization.test.js.map +1 -1
- package/build/dist/Tests/Server/Middleware/UserAuthorization.test.js +9 -8
- package/build/dist/Tests/Server/Middleware/UserAuthorization.test.js.map +1 -1
- package/build/dist/Tests/Server/Services/ScheduledMaintenanceService.test.js +2 -2
- package/build/dist/Tests/Server/Services/ScheduledMaintenanceService.test.js.map +1 -1
- package/build/dist/Tests/Server/Utils/Cookie.test.js +1 -1
- package/build/dist/Tests/Server/Utils/Cookie.test.js.map +1 -1
- package/build/dist/Types/Database/TableMetadata.js +3 -0
- package/build/dist/Types/Database/TableMetadata.js.map +1 -1
- package/build/dist/Types/Realtime/EnableRealtimeEventsOn.js +2 -0
- package/build/dist/Types/Realtime/EnableRealtimeEventsOn.js.map +1 -0
- package/build/dist/Types/Realtime/EventName.js +6 -0
- package/build/dist/Types/Realtime/EventName.js.map +1 -0
- package/build/dist/Types/Realtime/ListenToModelEventJSON.js +2 -0
- package/build/dist/Types/Realtime/ListenToModelEventJSON.js.map +1 -0
- package/build/dist/Types/Realtime/ModelEventType.js +8 -0
- package/build/dist/Types/Realtime/ModelEventType.js.map +1 -0
- package/build/dist/UI/Utils/Realtime.js +17 -11
- package/build/dist/UI/Utils/Realtime.js.map +1 -1
- package/build/dist/Utils/Realtime.js +6 -12
- package/build/dist/Utils/Realtime.js.map +1 -1
- package/package.json +2 -2
- package/Models/Permissions/Permission.ts +0 -0
- package/build/dist/Models/Permissions/Permission.js +0 -2
- package/build/dist/Models/Permissions/Permission.js.map +0 -1
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import APIKeyPermission from "../../../Models/DatabaseModels/ApiKeyPermission";
|
|
2
|
+
import Label from "../../../Models/DatabaseModels/Label";
|
|
3
|
+
import LIMIT_MAX from "../../../Types/Database/LimitMax";
|
|
4
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
5
|
+
import Permission, {
|
|
6
|
+
UserGlobalAccessPermission,
|
|
7
|
+
UserPermission,
|
|
8
|
+
UserTenantAccessPermission,
|
|
9
|
+
} from "../../../Types/Permission";
|
|
10
|
+
import ApiKeyPermissionService from "../../Services/ApiKeyPermissionService";
|
|
11
|
+
import UserPermissionUtil from "../UserPermission/UserPermission";
|
|
12
|
+
|
|
13
|
+
export default class APIKeyAccessPermission {
|
|
14
|
+
public static async getDefaultApiGlobalPermission(
|
|
15
|
+
projectId: ObjectID,
|
|
16
|
+
): Promise<UserGlobalAccessPermission> {
|
|
17
|
+
return {
|
|
18
|
+
projectIds: [projectId],
|
|
19
|
+
globalPermissions: [
|
|
20
|
+
Permission.Public,
|
|
21
|
+
Permission.User,
|
|
22
|
+
Permission.CurrentUser,
|
|
23
|
+
],
|
|
24
|
+
_type: "UserGlobalAccessPermission",
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
public static async getMasterKeyApiGlobalPermission(
|
|
29
|
+
projectId: ObjectID,
|
|
30
|
+
): Promise<UserGlobalAccessPermission> {
|
|
31
|
+
return {
|
|
32
|
+
projectIds: [projectId],
|
|
33
|
+
globalPermissions: [
|
|
34
|
+
Permission.Public,
|
|
35
|
+
Permission.User,
|
|
36
|
+
Permission.CurrentUser,
|
|
37
|
+
Permission.ProjectOwner,
|
|
38
|
+
],
|
|
39
|
+
_type: "UserGlobalAccessPermission",
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public static async getMasterApiTenantAccessPermission(
|
|
44
|
+
projectId: ObjectID,
|
|
45
|
+
): Promise<UserTenantAccessPermission> {
|
|
46
|
+
const userPermissions: Array<UserPermission> = [];
|
|
47
|
+
|
|
48
|
+
userPermissions.push({
|
|
49
|
+
permission: Permission.ProjectOwner,
|
|
50
|
+
labelIds: [],
|
|
51
|
+
_type: "UserPermission",
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const permission: UserTenantAccessPermission =
|
|
55
|
+
UserPermissionUtil.getDefaultUserTenantAccessPermission(projectId);
|
|
56
|
+
|
|
57
|
+
permission.permissions = permission.permissions.concat(userPermissions);
|
|
58
|
+
|
|
59
|
+
return permission;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public static async getApiTenantAccessPermission(
|
|
63
|
+
projectId: ObjectID,
|
|
64
|
+
apiKeyId: ObjectID,
|
|
65
|
+
): Promise<UserTenantAccessPermission> {
|
|
66
|
+
// get team permissions.
|
|
67
|
+
const apiKeyPermission: Array<APIKeyPermission> =
|
|
68
|
+
await ApiKeyPermissionService.findBy({
|
|
69
|
+
query: {
|
|
70
|
+
apiKeyId: apiKeyId,
|
|
71
|
+
},
|
|
72
|
+
select: {
|
|
73
|
+
permission: true,
|
|
74
|
+
labels: {
|
|
75
|
+
_id: true,
|
|
76
|
+
},
|
|
77
|
+
isBlockPermission: true,
|
|
78
|
+
},
|
|
79
|
+
|
|
80
|
+
limit: LIMIT_MAX,
|
|
81
|
+
skip: 0,
|
|
82
|
+
props: {
|
|
83
|
+
isRoot: true,
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const userPermissions: Array<UserPermission> = [];
|
|
88
|
+
|
|
89
|
+
for (const apiPermission of apiKeyPermission) {
|
|
90
|
+
if (!apiPermission.labels) {
|
|
91
|
+
apiPermission.labels = [];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
userPermissions.push({
|
|
95
|
+
permission: apiPermission.permission!,
|
|
96
|
+
labelIds: apiPermission.labels.map((label: Label) => {
|
|
97
|
+
return label.id!;
|
|
98
|
+
}),
|
|
99
|
+
isBlockPermission: apiPermission.isBlockPermission,
|
|
100
|
+
_type: "UserPermission",
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const permission: UserTenantAccessPermission =
|
|
105
|
+
UserPermissionUtil.getDefaultUserTenantAccessPermission(projectId);
|
|
106
|
+
|
|
107
|
+
permission.permissions = permission.permissions.concat(userPermissions);
|
|
108
|
+
|
|
109
|
+
return permission;
|
|
110
|
+
}
|
|
111
|
+
}
|
package/Server/Utils/Cookie.ts
CHANGED
|
@@ -11,6 +11,20 @@ import CookieName from "Common/Types/CookieName";
|
|
|
11
11
|
export default class CookieUtil {
|
|
12
12
|
// set cookie with express response
|
|
13
13
|
|
|
14
|
+
public static getCookiesFromCookieString(
|
|
15
|
+
cookieString: string,
|
|
16
|
+
): Dictionary<string> {
|
|
17
|
+
const cookies: Dictionary<string> = {};
|
|
18
|
+
cookieString.split(";").forEach((cookie: string) => {
|
|
19
|
+
const parts: string[] = cookie.split("=");
|
|
20
|
+
const key: string = (parts[0] as string).trim() as string;
|
|
21
|
+
const value: string = parts[1] as string;
|
|
22
|
+
cookies[key] = value;
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
return cookies;
|
|
26
|
+
}
|
|
27
|
+
|
|
14
28
|
public static setSSOCookie(data: {
|
|
15
29
|
user: User;
|
|
16
30
|
projectId: ObjectID;
|
|
@@ -141,7 +155,7 @@ export default class CookieUtil {
|
|
|
141
155
|
|
|
142
156
|
// get cookie with express request
|
|
143
157
|
|
|
144
|
-
public static
|
|
158
|
+
public static getCookieFromExpressRequest(
|
|
145
159
|
req: ExpressRequest,
|
|
146
160
|
name: string,
|
|
147
161
|
): string | undefined {
|
|
@@ -237,11 +237,16 @@ export default class MonitorResourceUtil {
|
|
|
237
237
|
`${dataToProcess.monitorId.toString()} - Saving monitor metrics`,
|
|
238
238
|
);
|
|
239
239
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
240
|
+
try {
|
|
241
|
+
await this.saveMonitorMetrics({
|
|
242
|
+
monitorId: monitor.id!,
|
|
243
|
+
projectId: monitor.projectId!,
|
|
244
|
+
dataToProcess: dataToProcess,
|
|
245
|
+
});
|
|
246
|
+
} catch (err) {
|
|
247
|
+
logger.error("Unable to save metrics");
|
|
248
|
+
logger.error(err);
|
|
249
|
+
}
|
|
245
250
|
|
|
246
251
|
logger.debug(
|
|
247
252
|
`${dataToProcess.monitorId.toString()} - Monitor metrics saved`,
|
|
@@ -751,42 +756,71 @@ export default class MonitorResourceUtil {
|
|
|
751
756
|
}): Promise<void> {
|
|
752
757
|
// criteria filters are met, now process the actions.
|
|
753
758
|
|
|
759
|
+
const lastMonitorStatusTimeline: MonitorStatusTimeline | null =
|
|
760
|
+
await MonitorStatusTimelineService.findOneBy({
|
|
761
|
+
query: {
|
|
762
|
+
monitorId: input.monitor.id!,
|
|
763
|
+
projectId: input.monitor.projectId!,
|
|
764
|
+
},
|
|
765
|
+
select: {
|
|
766
|
+
_id: true,
|
|
767
|
+
monitorStatusId: true,
|
|
768
|
+
},
|
|
769
|
+
sort: {
|
|
770
|
+
startsAt: SortOrder.Descending,
|
|
771
|
+
},
|
|
772
|
+
props: {
|
|
773
|
+
isRoot: true,
|
|
774
|
+
},
|
|
775
|
+
});
|
|
776
|
+
|
|
777
|
+
let shouldUpdateStatus: boolean = false;
|
|
778
|
+
|
|
779
|
+
if (!lastMonitorStatusTimeline) {
|
|
780
|
+
// if monitor does not have any status timeline, then create one.
|
|
781
|
+
shouldUpdateStatus = true;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
if (
|
|
785
|
+
input.criteriaInstance.data?.changeMonitorStatus &&
|
|
786
|
+
input.criteriaInstance.data?.monitorStatusId &&
|
|
787
|
+
input.criteriaInstance.data?.monitorStatusId.toString() !==
|
|
788
|
+
lastMonitorStatusTimeline?.id?.toString()
|
|
789
|
+
) {
|
|
790
|
+
// if monitor status is changed, then create a new status timeline.
|
|
791
|
+
shouldUpdateStatus = true;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
// check if the current status is same as the last status.
|
|
795
|
+
|
|
754
796
|
if (
|
|
755
797
|
input.criteriaInstance.data?.changeMonitorStatus &&
|
|
756
798
|
input.criteriaInstance.data?.monitorStatusId &&
|
|
757
799
|
input.criteriaInstance.data?.monitorStatusId.toString() !==
|
|
758
800
|
input.monitor.currentMonitorStatusId?.toString()
|
|
759
801
|
) {
|
|
802
|
+
// if monitor status is changed, then create a new status timeline.
|
|
803
|
+
shouldUpdateStatus = true;
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
if (shouldUpdateStatus) {
|
|
760
807
|
logger.debug(
|
|
761
|
-
`${input.monitor.id?.toString()} - Change monitor status to ${input.criteriaInstance.data?.monitorStatusId
|
|
808
|
+
`${input.monitor.id?.toString()} - Change monitor status to ${input.criteriaInstance.data?.monitorStatusId?.toString()}`,
|
|
762
809
|
);
|
|
763
810
|
// change monitor status
|
|
764
811
|
|
|
765
812
|
const monitorStatusId: ObjectID | undefined =
|
|
766
813
|
input.criteriaInstance.data?.monitorStatusId;
|
|
767
814
|
|
|
815
|
+
if (!monitorStatusId) {
|
|
816
|
+
throw new BadDataException("Monitor status is not defined.");
|
|
817
|
+
}
|
|
818
|
+
|
|
768
819
|
//change monitor status.
|
|
769
820
|
|
|
770
821
|
// get last status of this monitor.
|
|
771
822
|
|
|
772
823
|
// get last monitor status timeline.
|
|
773
|
-
const lastMonitorStatusTimeline: MonitorStatusTimeline | null =
|
|
774
|
-
await MonitorStatusTimelineService.findOneBy({
|
|
775
|
-
query: {
|
|
776
|
-
monitorId: input.monitor.id!,
|
|
777
|
-
projectId: input.monitor.projectId!,
|
|
778
|
-
},
|
|
779
|
-
select: {
|
|
780
|
-
_id: true,
|
|
781
|
-
monitorStatusId: true,
|
|
782
|
-
},
|
|
783
|
-
sort: {
|
|
784
|
-
startsAt: SortOrder.Descending,
|
|
785
|
-
},
|
|
786
|
-
props: {
|
|
787
|
-
isRoot: true,
|
|
788
|
-
},
|
|
789
|
-
});
|
|
790
824
|
|
|
791
825
|
if (
|
|
792
826
|
lastMonitorStatusTimeline &&
|
package/Server/Utils/Realtime.ts
CHANGED
|
@@ -1,78 +1,78 @@
|
|
|
1
1
|
import IO, { Socket, SocketServer } from "../Infrastructure/SocketIO";
|
|
2
2
|
import logger from "./Logger";
|
|
3
|
-
import AnalyticsBaseModel
|
|
4
|
-
|
|
5
|
-
} from "Common/Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel";
|
|
6
|
-
import BaseModel, {
|
|
7
|
-
DatabaseBaseModelType,
|
|
8
|
-
} from "Common/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
|
|
3
|
+
import AnalyticsBaseModel from "Common/Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel";
|
|
4
|
+
import BaseModel from "Common/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
|
|
9
5
|
import DatabaseType from "Common/Types/BaseDatabase/DatabaseType";
|
|
10
6
|
import BadDataException from "Common/Types/Exception/BadDataException";
|
|
11
7
|
import { JSONObject } from "Common/Types/JSON";
|
|
12
|
-
import JSONFunctions from "Common/Types/JSONFunctions";
|
|
13
8
|
import ObjectID from "Common/Types/ObjectID";
|
|
14
|
-
import RealtimeUtil
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
9
|
+
import RealtimeUtil from "Common/Utils/Realtime";
|
|
10
|
+
import JSONWebTokenData from "../../Types/JsonWebTokenData";
|
|
11
|
+
import JSONWebToken from "./JsonWebToken";
|
|
12
|
+
import Permission, {
|
|
13
|
+
UserGlobalAccessPermission,
|
|
14
|
+
UserTenantAccessPermission,
|
|
15
|
+
} from "../../Types/Permission";
|
|
16
|
+
import { getModelTypeByName } from "../../Models/DatabaseModels/Index";
|
|
17
|
+
import { getModelTypeByName as getAnalyticsModelTypeByname } from "../../Models/AnalyticsModels/Index";
|
|
18
|
+
import DatabaseRequestType from "../Types/BaseDatabase/DatabaseRequestType";
|
|
19
|
+
import ModelPermission from "../../Types/BaseDatabase/ModelPermission";
|
|
20
|
+
import ModelEventType from "../../Types/Realtime/ModelEventType";
|
|
21
|
+
import ListenToModelEventJSON from "../../Types/Realtime/ListenToModelEventJSON";
|
|
22
|
+
import EventName from "../../Types/Realtime/EventName";
|
|
23
|
+
import CookieUtil from "./Cookie";
|
|
24
|
+
import Dictionary from "../../Types/Dictionary";
|
|
25
|
+
import UserPermissionUtil from "./UserPermission/UserPermission";
|
|
19
26
|
|
|
20
27
|
export default abstract class Realtime {
|
|
21
28
|
private static socketServer: SocketServer | null = null;
|
|
22
29
|
|
|
23
30
|
public static isInitialized(): boolean {
|
|
24
|
-
|
|
31
|
+
logger.debug("Checking if socket server is initialized");
|
|
32
|
+
const isInitialized: boolean = this.socketServer !== null;
|
|
33
|
+
logger.debug(`Socket server is initialized: ${isInitialized}`);
|
|
34
|
+
return isInitialized;
|
|
25
35
|
}
|
|
26
36
|
|
|
27
37
|
public static async init(): Promise<SocketServer | null> {
|
|
28
38
|
if (!this.socketServer) {
|
|
39
|
+
logger.debug("Initializing socket server");
|
|
29
40
|
this.socketServer = IO.getSocketServer();
|
|
30
41
|
logger.debug("Realtime socket server initialized");
|
|
31
|
-
}
|
|
32
42
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
throw new BadDataException("select is not an object");
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
await Realtime.listenToModelEvent(socket, {
|
|
67
|
-
eventType: data["eventType"] as ModelEventType,
|
|
68
|
-
modelType: data["modelType"] as DatabaseType,
|
|
69
|
-
modelName: data["modelName"] as string,
|
|
70
|
-
query: JSONFunctions.deserialize(data["query"] as JSONObject),
|
|
71
|
-
tenantId: data["tenantId"] as string,
|
|
72
|
-
select: JSONFunctions.deserialize(data["select"] as JSONObject),
|
|
43
|
+
this.socketServer!.on("connection", (socket: Socket) => {
|
|
44
|
+
logger.debug("New socket connection established");
|
|
45
|
+
|
|
46
|
+
socket.on(EventName.ListenToModalEvent, async (data: JSONObject) => {
|
|
47
|
+
logger.debug("Received ListenToModalEvent with data:");
|
|
48
|
+
logger.debug(data);
|
|
49
|
+
|
|
50
|
+
if (typeof data["eventType"] !== "string") {
|
|
51
|
+
logger.error("eventType is not a string");
|
|
52
|
+
throw new BadDataException("eventType is not a string");
|
|
53
|
+
}
|
|
54
|
+
if (typeof data["modelType"] !== "string") {
|
|
55
|
+
logger.error("modelType is not a string");
|
|
56
|
+
throw new BadDataException("modelType is not a string");
|
|
57
|
+
}
|
|
58
|
+
if (typeof data["modelName"] !== "string") {
|
|
59
|
+
logger.error("modelName is not a string");
|
|
60
|
+
throw new BadDataException("modelName is not a string");
|
|
61
|
+
}
|
|
62
|
+
if (typeof data["tenantId"] !== "string") {
|
|
63
|
+
logger.error("tenantId is not a string");
|
|
64
|
+
throw new BadDataException("tenantId is not a string");
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
await Realtime.listenToModelEvent(socket, {
|
|
68
|
+
eventType: data["eventType"] as ModelEventType,
|
|
69
|
+
modelType: data["modelType"] as DatabaseType,
|
|
70
|
+
modelName: data["modelName"] as string,
|
|
71
|
+
tenantId: data["tenantId"] as string,
|
|
72
|
+
});
|
|
73
73
|
});
|
|
74
74
|
});
|
|
75
|
-
}
|
|
75
|
+
}
|
|
76
76
|
|
|
77
77
|
return this.socketServer;
|
|
78
78
|
}
|
|
@@ -81,66 +81,275 @@ export default abstract class Realtime {
|
|
|
81
81
|
socket: Socket,
|
|
82
82
|
data: ListenToModelEventJSON,
|
|
83
83
|
): Promise<void> {
|
|
84
|
+
logger.debug("Listening to model event with data:");
|
|
85
|
+
logger.debug(data);
|
|
86
|
+
|
|
84
87
|
if (!this.socketServer) {
|
|
88
|
+
logger.debug("Socket server not initialized, initializing now");
|
|
85
89
|
await this.init();
|
|
86
90
|
}
|
|
87
91
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
92
|
+
// before joining room check the user token and check if the user has access to this tenant
|
|
93
|
+
// and to this model and to this event type
|
|
94
|
+
|
|
95
|
+
logger.debug("Extracting user access token from socket");
|
|
96
|
+
const userAccessToken: string | undefined =
|
|
97
|
+
this.getAccessTokenFromSocket(socket);
|
|
98
|
+
|
|
99
|
+
if (!userAccessToken) {
|
|
100
|
+
logger.debug(
|
|
101
|
+
"User access token not found in socket, aborting joining room",
|
|
102
|
+
);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
logger.debug("Decoding user access token");
|
|
107
|
+
const userAuthorizationData: JSONWebTokenData =
|
|
108
|
+
JSONWebToken.decode(userAccessToken);
|
|
109
|
+
|
|
110
|
+
if (!userAuthorizationData) {
|
|
111
|
+
logger.debug(
|
|
112
|
+
"User authorization data not found in socket, aborting joining room",
|
|
113
|
+
);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (!userAuthorizationData.userId) {
|
|
118
|
+
logger.debug("User ID not found in socket, aborting joining room");
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
logger.debug("Checking user access permissions");
|
|
123
|
+
let hasAccess: boolean = false;
|
|
124
|
+
|
|
125
|
+
if (userAuthorizationData.isMasterAdmin) {
|
|
126
|
+
logger.debug("User is a master admin, granting access");
|
|
127
|
+
hasAccess = true;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
logger.debug("Fetching user global access permissions");
|
|
131
|
+
const userGlobalAccessPermission: UserGlobalAccessPermission | null =
|
|
132
|
+
await UserPermissionUtil.getUserGlobalAccessPermissionFromCache(
|
|
133
|
+
userAuthorizationData.userId,
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
// check if the user has access to this tenant
|
|
137
|
+
if (userGlobalAccessPermission && !hasAccess) {
|
|
138
|
+
logger.debug("Checking if user has access to the tenant");
|
|
139
|
+
const hasAccessToProjectId: boolean =
|
|
140
|
+
userGlobalAccessPermission.projectIds.some((projectId: ObjectID) => {
|
|
141
|
+
return projectId.toString() === data.tenantId.toString();
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
if (!hasAccessToProjectId) {
|
|
145
|
+
logger.debug(
|
|
146
|
+
"User does not have access to this tenant, aborting joining room",
|
|
147
|
+
);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
logger.debug("User has access to the tenant, checking model access");
|
|
152
|
+
const userId: ObjectID = new ObjectID(
|
|
153
|
+
userAuthorizationData.userId.toString(),
|
|
154
|
+
);
|
|
155
|
+
const projectId: ObjectID = new ObjectID(data.tenantId.toString());
|
|
156
|
+
|
|
157
|
+
// if it has the access to the tenant, check if it has access to the model
|
|
158
|
+
const userTenantAccessPermission: UserTenantAccessPermission | null =
|
|
159
|
+
await UserPermissionUtil.getUserTenantAccessPermissionFromCache(
|
|
160
|
+
userId,
|
|
161
|
+
projectId,
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
let databaseRequestType: DatabaseRequestType = DatabaseRequestType.Read;
|
|
165
|
+
|
|
166
|
+
if (data.eventType === ModelEventType.Create) {
|
|
167
|
+
logger.debug("Event type is Create, setting request type to Create");
|
|
168
|
+
databaseRequestType = DatabaseRequestType.Create;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (data.eventType === ModelEventType.Update) {
|
|
172
|
+
logger.debug("Event type is Update, setting request type to Update");
|
|
173
|
+
databaseRequestType = DatabaseRequestType.Update;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (data.eventType === ModelEventType.Delete) {
|
|
177
|
+
logger.debug("Event type is Delete, setting request type to Delete");
|
|
178
|
+
databaseRequestType = DatabaseRequestType.Delete;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// check if the user has access to this model
|
|
182
|
+
if (
|
|
183
|
+
userTenantAccessPermission &&
|
|
184
|
+
this.hasPermissionsByModelName(
|
|
185
|
+
userTenantAccessPermission,
|
|
186
|
+
data.modelName,
|
|
187
|
+
databaseRequestType,
|
|
188
|
+
)
|
|
189
|
+
) {
|
|
190
|
+
logger.debug("User has access to the model, granting access");
|
|
191
|
+
hasAccess = true;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (!hasAccess) {
|
|
196
|
+
logger.debug(
|
|
197
|
+
"User does not have access to this tenant, aborting joining room",
|
|
198
|
+
);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (data.modelId) {
|
|
203
|
+
const modelRoomId: string = RealtimeUtil.getRoomId(
|
|
204
|
+
data.tenantId,
|
|
205
|
+
data.modelName,
|
|
206
|
+
ModelEventType.Create,
|
|
207
|
+
data.modelId,
|
|
208
|
+
);
|
|
93
209
|
|
|
94
|
-
|
|
95
|
-
|
|
210
|
+
logger.debug(`Joining room with ID: ${modelRoomId}`);
|
|
211
|
+
// join the room.
|
|
212
|
+
await socket.join(modelRoomId);
|
|
213
|
+
} else {
|
|
214
|
+
const roomId: string = RealtimeUtil.getRoomId(
|
|
215
|
+
data.tenantId,
|
|
216
|
+
data.modelName,
|
|
217
|
+
data.eventType,
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
logger.debug(`Joining room with ID: ${roomId}`);
|
|
221
|
+
// join the room.
|
|
222
|
+
await socket.join(roomId);
|
|
223
|
+
}
|
|
96
224
|
}
|
|
97
225
|
|
|
98
226
|
public static async stopListeningToModelEvent(
|
|
99
227
|
socket: Socket,
|
|
100
228
|
data: ListenToModelEventJSON,
|
|
101
229
|
): Promise<void> {
|
|
230
|
+
logger.debug("Stopping listening to model event with data:");
|
|
231
|
+
logger.debug(data);
|
|
232
|
+
|
|
102
233
|
if (!this.socketServer) {
|
|
234
|
+
logger.debug("Socket server not initialized, initializing now");
|
|
103
235
|
await this.init();
|
|
104
236
|
}
|
|
105
237
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
238
|
+
const roomId: string = RealtimeUtil.getRoomId(
|
|
239
|
+
data.tenantId,
|
|
240
|
+
data.modelName,
|
|
241
|
+
data.eventType,
|
|
242
|
+
data.modelId,
|
|
109
243
|
);
|
|
244
|
+
|
|
245
|
+
logger.debug(`Leaving room with ID: ${roomId}`);
|
|
246
|
+
// leave this room.
|
|
247
|
+
await socket.leave(roomId);
|
|
110
248
|
}
|
|
111
249
|
|
|
112
250
|
public static async emitModelEvent(data: {
|
|
113
251
|
tenantId: string | ObjectID;
|
|
114
252
|
eventType: ModelEventType;
|
|
115
|
-
|
|
253
|
+
modelId: ObjectID;
|
|
116
254
|
modelType: { new (): BaseModel | AnalyticsBaseModel };
|
|
117
255
|
}): Promise<void> {
|
|
256
|
+
logger.debug("Emitting model event with data:");
|
|
257
|
+
logger.debug(`Tenant ID: ${data.tenantId}`);
|
|
258
|
+
logger.debug(`Event Type: ${data.eventType}`);
|
|
259
|
+
logger.debug(`Model ID: ${data.modelId}`);
|
|
260
|
+
|
|
118
261
|
if (!this.socketServer) {
|
|
262
|
+
logger.debug("Socket server not initialized, initializing now");
|
|
119
263
|
await this.init();
|
|
120
264
|
}
|
|
121
265
|
|
|
122
|
-
|
|
266
|
+
const jsonObject: JSONObject = {
|
|
267
|
+
modelId: data.modelId.toString(),
|
|
268
|
+
};
|
|
123
269
|
|
|
124
|
-
|
|
125
|
-
jsonObject = BaseModel.toJSON(
|
|
126
|
-
data.model,
|
|
127
|
-
data.modelType as DatabaseBaseModelType,
|
|
128
|
-
);
|
|
129
|
-
}
|
|
270
|
+
const model: BaseModel | AnalyticsBaseModel = new data.modelType();
|
|
130
271
|
|
|
131
|
-
if (
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
data.modelType as AnalyticsBaseModelType,
|
|
135
|
-
);
|
|
272
|
+
if (!model.tableName) {
|
|
273
|
+
logger.warn("Model does not have a tableName, aborting emit");
|
|
274
|
+
return;
|
|
136
275
|
}
|
|
137
276
|
|
|
138
277
|
const roomId: string = RealtimeUtil.getRoomId(
|
|
139
278
|
data.tenantId,
|
|
140
|
-
|
|
279
|
+
model.tableName!,
|
|
141
280
|
data.eventType,
|
|
142
281
|
);
|
|
143
282
|
|
|
283
|
+
const modelRoomId: string = RealtimeUtil.getRoomId(
|
|
284
|
+
data.tenantId,
|
|
285
|
+
model.tableName!,
|
|
286
|
+
ModelEventType.Create,
|
|
287
|
+
data.modelId,
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
logger.debug(`Emitting event to room with ID: ${roomId}`);
|
|
291
|
+
logger.debug(jsonObject);
|
|
292
|
+
|
|
144
293
|
this.socketServer!.to(roomId).emit(roomId, jsonObject);
|
|
294
|
+
this.socketServer!.to(modelRoomId).emit(modelRoomId, jsonObject);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
public static hasPermissionsByModelName(
|
|
298
|
+
userProjectPermissions: UserTenantAccessPermission | Array<Permission>,
|
|
299
|
+
modelName: string,
|
|
300
|
+
requestType: DatabaseRequestType,
|
|
301
|
+
): boolean {
|
|
302
|
+
let modelPermissions: Array<Permission> = [];
|
|
303
|
+
|
|
304
|
+
let modelType:
|
|
305
|
+
| { new (): BaseModel }
|
|
306
|
+
| { new (): AnalyticsBaseModel }
|
|
307
|
+
| null = getModelTypeByName(modelName);
|
|
308
|
+
|
|
309
|
+
if (!modelType) {
|
|
310
|
+
// check if it is an analytics model
|
|
311
|
+
modelType = getAnalyticsModelTypeByname(modelName);
|
|
312
|
+
|
|
313
|
+
if (!modelType) {
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (requestType === DatabaseRequestType.Create) {
|
|
319
|
+
modelPermissions = new modelType().getCreatePermissions();
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (requestType === DatabaseRequestType.Read) {
|
|
323
|
+
modelPermissions = new modelType().getReadPermissions();
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (requestType === DatabaseRequestType.Update) {
|
|
327
|
+
modelPermissions = new modelType().getUpdatePermissions();
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (requestType === DatabaseRequestType.Delete) {
|
|
331
|
+
modelPermissions = new modelType().getDeletePermissions();
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
return ModelPermission.hasPermissions(
|
|
335
|
+
userProjectPermissions,
|
|
336
|
+
modelPermissions,
|
|
337
|
+
);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
public static getAccessTokenFromSocket(socket: Socket): string | undefined {
|
|
341
|
+
let accessToken: string | undefined = undefined;
|
|
342
|
+
|
|
343
|
+
if (socket.handshake.headers.cookie) {
|
|
344
|
+
const cookies: Dictionary<string> = CookieUtil.getCookiesFromCookieString(
|
|
345
|
+
socket.handshake.headers.cookie,
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
if (cookies[CookieUtil.getUserTokenKey()]) {
|
|
349
|
+
accessToken = cookies[CookieUtil.getUserTokenKey()];
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return accessToken;
|
|
145
354
|
}
|
|
146
355
|
}
|