@oneuptime/common 7.0.4850 → 7.0.4868
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/Index.ts +3 -0
- package/Models/DatabaseModels/ProjectSCIM.ts +451 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1754304193228-MigrationName.ts +67 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1754315774827-MigrationName.ts +17 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Infrastructure/Queue.ts +1 -1
- package/Server/Middleware/SCIMAuthorization.ts +94 -0
- package/Server/Services/ProjectSCIMService.ts +27 -0
- package/Server/Utils/StartServer.ts +10 -0
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProjectSCIM.js +467 -0
- package/build/dist/Models/DatabaseModels/ProjectSCIM.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1754304193228-MigrationName.js +30 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1754304193228-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1754315774827-MigrationName.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1754315774827-MigrationName.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/Infrastructure/Queue.js +1 -1
- package/build/dist/Server/Middleware/SCIMAuthorization.js +81 -0
- package/build/dist/Server/Middleware/SCIMAuthorization.js.map +1 -0
- package/build/dist/Server/Services/ProjectSCIMService.js +20 -0
- package/build/dist/Server/Services/ProjectSCIMService.js.map +1 -0
- package/build/dist/Server/Utils/StartServer.js +9 -0
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/package.json +1 -1
|
@@ -179,6 +179,7 @@ import ProjectUser from "./ProjectUser";
|
|
|
179
179
|
import OnCallDutyPolicyUserOverride from "./OnCallDutyPolicyUserOverride";
|
|
180
180
|
import MonitorFeed from "./MonitorFeed";
|
|
181
181
|
import MetricType from "./MetricType";
|
|
182
|
+
import ProjectSCIM from "./ProjectSCIM";
|
|
182
183
|
|
|
183
184
|
const AllModelTypes: Array<{
|
|
184
185
|
new (): BaseModel;
|
|
@@ -380,6 +381,8 @@ const AllModelTypes: Array<{
|
|
|
380
381
|
MetricType,
|
|
381
382
|
|
|
382
383
|
OnCallDutyPolicyTimeLog,
|
|
384
|
+
|
|
385
|
+
ProjectSCIM,
|
|
383
386
|
];
|
|
384
387
|
|
|
385
388
|
const modelTypeMap: { [key: string]: { new (): BaseModel } } = {};
|
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
import Project from "./Project";
|
|
2
|
+
import Team from "./Team";
|
|
3
|
+
import User from "./User";
|
|
4
|
+
import BaseModel from "./DatabaseBaseModel/DatabaseBaseModel";
|
|
5
|
+
import Route from "../../Types/API/Route";
|
|
6
|
+
import { PlanType } from "../../Types/Billing/SubscriptionPlan";
|
|
7
|
+
import ColumnAccessControl from "../../Types/Database/AccessControl/ColumnAccessControl";
|
|
8
|
+
import TableAccessControl from "../../Types/Database/AccessControl/TableAccessControl";
|
|
9
|
+
import TableBillingAccessControl from "../../Types/Database/AccessControl/TableBillingAccessControl";
|
|
10
|
+
import ColumnLength from "../../Types/Database/ColumnLength";
|
|
11
|
+
import ColumnType from "../../Types/Database/ColumnType";
|
|
12
|
+
import CrudApiEndpoint from "../../Types/Database/CrudApiEndpoint";
|
|
13
|
+
import TableColumn from "../../Types/Database/TableColumn";
|
|
14
|
+
import TableColumnType from "../../Types/Database/TableColumnType";
|
|
15
|
+
import TableMetadata from "../../Types/Database/TableMetadata";
|
|
16
|
+
import TenantColumn from "../../Types/Database/TenantColumn";
|
|
17
|
+
import UniqueColumnBy from "../../Types/Database/UniqueColumnBy";
|
|
18
|
+
import IconProp from "../../Types/Icon/IconProp";
|
|
19
|
+
import ObjectID from "../../Types/ObjectID";
|
|
20
|
+
import Permission from "../../Types/Permission";
|
|
21
|
+
import {
|
|
22
|
+
Column,
|
|
23
|
+
Entity,
|
|
24
|
+
Index,
|
|
25
|
+
JoinColumn,
|
|
26
|
+
JoinTable,
|
|
27
|
+
ManyToMany,
|
|
28
|
+
ManyToOne,
|
|
29
|
+
} from "typeorm";
|
|
30
|
+
|
|
31
|
+
@TableBillingAccessControl({
|
|
32
|
+
create: PlanType.Scale,
|
|
33
|
+
read: PlanType.Scale,
|
|
34
|
+
update: PlanType.Scale,
|
|
35
|
+
delete: PlanType.Scale,
|
|
36
|
+
})
|
|
37
|
+
@TenantColumn("projectId")
|
|
38
|
+
@TableAccessControl({
|
|
39
|
+
create: [
|
|
40
|
+
Permission.ProjectOwner,
|
|
41
|
+
Permission.ProjectAdmin,
|
|
42
|
+
Permission.CreateProjectSSO,
|
|
43
|
+
],
|
|
44
|
+
read: [
|
|
45
|
+
Permission.ProjectOwner,
|
|
46
|
+
Permission.ProjectAdmin,
|
|
47
|
+
Permission.ProjectMember,
|
|
48
|
+
Permission.ReadProjectSSO,
|
|
49
|
+
],
|
|
50
|
+
delete: [
|
|
51
|
+
Permission.ProjectOwner,
|
|
52
|
+
Permission.ProjectAdmin,
|
|
53
|
+
Permission.DeleteProjectSSO,
|
|
54
|
+
],
|
|
55
|
+
update: [
|
|
56
|
+
Permission.ProjectOwner,
|
|
57
|
+
Permission.ProjectAdmin,
|
|
58
|
+
Permission.EditProjectSSO,
|
|
59
|
+
],
|
|
60
|
+
})
|
|
61
|
+
@CrudApiEndpoint(new Route("/project-scim"))
|
|
62
|
+
@TableMetadata({
|
|
63
|
+
tableName: "ProjectSCIM",
|
|
64
|
+
singularName: "SCIM",
|
|
65
|
+
pluralName: "SCIM",
|
|
66
|
+
icon: IconProp.Lock,
|
|
67
|
+
tableDescription: "Manage SCIM auto-provisioning for your project",
|
|
68
|
+
})
|
|
69
|
+
@Entity({
|
|
70
|
+
name: "ProjectSCIM",
|
|
71
|
+
})
|
|
72
|
+
export default class ProjectSCIM extends BaseModel {
|
|
73
|
+
@ColumnAccessControl({
|
|
74
|
+
create: [
|
|
75
|
+
Permission.ProjectOwner,
|
|
76
|
+
Permission.ProjectAdmin,
|
|
77
|
+
Permission.CreateProjectSSO,
|
|
78
|
+
],
|
|
79
|
+
read: [
|
|
80
|
+
Permission.ProjectOwner,
|
|
81
|
+
Permission.ProjectAdmin,
|
|
82
|
+
Permission.ProjectMember,
|
|
83
|
+
Permission.ReadProjectSSO,
|
|
84
|
+
],
|
|
85
|
+
update: [],
|
|
86
|
+
})
|
|
87
|
+
@TableColumn({
|
|
88
|
+
manyToOneRelationColumn: "projectId",
|
|
89
|
+
type: TableColumnType.Entity,
|
|
90
|
+
modelType: Project,
|
|
91
|
+
title: "Project",
|
|
92
|
+
description: "Relation to Project Resource in which this object belongs",
|
|
93
|
+
})
|
|
94
|
+
@ManyToOne(
|
|
95
|
+
() => {
|
|
96
|
+
return Project;
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
eager: false,
|
|
100
|
+
nullable: true,
|
|
101
|
+
onDelete: "CASCADE",
|
|
102
|
+
orphanedRowAction: "nullify",
|
|
103
|
+
},
|
|
104
|
+
)
|
|
105
|
+
@JoinColumn({ name: "projectId" })
|
|
106
|
+
public project?: Project = undefined;
|
|
107
|
+
|
|
108
|
+
@ColumnAccessControl({
|
|
109
|
+
create: [
|
|
110
|
+
Permission.ProjectOwner,
|
|
111
|
+
Permission.ProjectAdmin,
|
|
112
|
+
Permission.CreateProjectSSO,
|
|
113
|
+
],
|
|
114
|
+
read: [
|
|
115
|
+
Permission.ProjectOwner,
|
|
116
|
+
Permission.ProjectAdmin,
|
|
117
|
+
Permission.ProjectMember,
|
|
118
|
+
Permission.ReadProjectSSO,
|
|
119
|
+
],
|
|
120
|
+
update: [],
|
|
121
|
+
})
|
|
122
|
+
@Index()
|
|
123
|
+
@TableColumn({
|
|
124
|
+
type: TableColumnType.ObjectID,
|
|
125
|
+
required: true,
|
|
126
|
+
canReadOnRelationQuery: true,
|
|
127
|
+
title: "Project ID",
|
|
128
|
+
description: "ID of your OneUptime Project in which this object belongs",
|
|
129
|
+
})
|
|
130
|
+
@Column({
|
|
131
|
+
type: ColumnType.ObjectID,
|
|
132
|
+
nullable: false,
|
|
133
|
+
transformer: ObjectID.getDatabaseTransformer(),
|
|
134
|
+
})
|
|
135
|
+
public projectId?: ObjectID = undefined;
|
|
136
|
+
|
|
137
|
+
@ColumnAccessControl({
|
|
138
|
+
create: [
|
|
139
|
+
Permission.ProjectOwner,
|
|
140
|
+
Permission.ProjectAdmin,
|
|
141
|
+
Permission.CreateProjectSSO,
|
|
142
|
+
],
|
|
143
|
+
read: [
|
|
144
|
+
Permission.ProjectOwner,
|
|
145
|
+
Permission.ProjectAdmin,
|
|
146
|
+
Permission.ProjectMember,
|
|
147
|
+
Permission.ReadProjectSSO,
|
|
148
|
+
],
|
|
149
|
+
update: [
|
|
150
|
+
Permission.ProjectOwner,
|
|
151
|
+
Permission.ProjectAdmin,
|
|
152
|
+
Permission.EditProjectSSO,
|
|
153
|
+
],
|
|
154
|
+
})
|
|
155
|
+
@TableColumn({
|
|
156
|
+
required: true,
|
|
157
|
+
type: TableColumnType.ShortText,
|
|
158
|
+
canReadOnRelationQuery: true,
|
|
159
|
+
title: "Name",
|
|
160
|
+
description: "Any friendly name for this SCIM configuration",
|
|
161
|
+
})
|
|
162
|
+
@Column({
|
|
163
|
+
nullable: false,
|
|
164
|
+
type: ColumnType.ShortText,
|
|
165
|
+
length: ColumnLength.ShortText,
|
|
166
|
+
})
|
|
167
|
+
@UniqueColumnBy("projectId")
|
|
168
|
+
public name?: string = undefined;
|
|
169
|
+
|
|
170
|
+
@ColumnAccessControl({
|
|
171
|
+
create: [
|
|
172
|
+
Permission.ProjectOwner,
|
|
173
|
+
Permission.ProjectAdmin,
|
|
174
|
+
Permission.CreateProjectSSO,
|
|
175
|
+
],
|
|
176
|
+
read: [
|
|
177
|
+
Permission.ProjectOwner,
|
|
178
|
+
Permission.ProjectAdmin,
|
|
179
|
+
Permission.ProjectMember,
|
|
180
|
+
Permission.ReadProjectSSO,
|
|
181
|
+
],
|
|
182
|
+
update: [
|
|
183
|
+
Permission.ProjectOwner,
|
|
184
|
+
Permission.ProjectAdmin,
|
|
185
|
+
Permission.EditProjectSSO,
|
|
186
|
+
],
|
|
187
|
+
})
|
|
188
|
+
@TableColumn({
|
|
189
|
+
required: false,
|
|
190
|
+
type: TableColumnType.LongText,
|
|
191
|
+
title: "Description",
|
|
192
|
+
description: "Friendly description to help you remember",
|
|
193
|
+
})
|
|
194
|
+
@Column({
|
|
195
|
+
nullable: true,
|
|
196
|
+
type: ColumnType.LongText,
|
|
197
|
+
length: ColumnLength.LongText,
|
|
198
|
+
})
|
|
199
|
+
public description?: string = undefined;
|
|
200
|
+
|
|
201
|
+
@ColumnAccessControl({
|
|
202
|
+
create: [
|
|
203
|
+
Permission.ProjectOwner,
|
|
204
|
+
Permission.ProjectAdmin,
|
|
205
|
+
Permission.CreateProjectSSO,
|
|
206
|
+
],
|
|
207
|
+
read: [
|
|
208
|
+
Permission.ProjectOwner,
|
|
209
|
+
Permission.ProjectAdmin,
|
|
210
|
+
Permission.ReadProjectSSO,
|
|
211
|
+
],
|
|
212
|
+
update: [
|
|
213
|
+
Permission.ProjectOwner,
|
|
214
|
+
Permission.ProjectAdmin,
|
|
215
|
+
Permission.EditProjectSSO,
|
|
216
|
+
],
|
|
217
|
+
})
|
|
218
|
+
@TableColumn({
|
|
219
|
+
required: true,
|
|
220
|
+
type: TableColumnType.LongText,
|
|
221
|
+
title: "Bearer Token",
|
|
222
|
+
description: "Bearer token for SCIM authentication. Keep this secure.",
|
|
223
|
+
})
|
|
224
|
+
@Column({
|
|
225
|
+
nullable: false,
|
|
226
|
+
type: ColumnType.LongText,
|
|
227
|
+
length: ColumnLength.LongText,
|
|
228
|
+
})
|
|
229
|
+
public bearerToken?: string = undefined;
|
|
230
|
+
|
|
231
|
+
@ColumnAccessControl({
|
|
232
|
+
create: [
|
|
233
|
+
Permission.ProjectOwner,
|
|
234
|
+
Permission.ProjectAdmin,
|
|
235
|
+
Permission.CreateProjectSSO,
|
|
236
|
+
],
|
|
237
|
+
read: [
|
|
238
|
+
Permission.ProjectOwner,
|
|
239
|
+
Permission.ProjectAdmin,
|
|
240
|
+
Permission.ProjectMember,
|
|
241
|
+
Permission.ReadProjectSSO,
|
|
242
|
+
],
|
|
243
|
+
update: [
|
|
244
|
+
Permission.ProjectOwner,
|
|
245
|
+
Permission.ProjectAdmin,
|
|
246
|
+
Permission.EditProjectSSO,
|
|
247
|
+
],
|
|
248
|
+
})
|
|
249
|
+
@TableColumn({
|
|
250
|
+
required: false,
|
|
251
|
+
type: TableColumnType.EntityArray,
|
|
252
|
+
modelType: Team,
|
|
253
|
+
title: "Default Teams",
|
|
254
|
+
description: "Default teams that new users will be added to via SCIM",
|
|
255
|
+
})
|
|
256
|
+
@ManyToMany(
|
|
257
|
+
() => {
|
|
258
|
+
return Team;
|
|
259
|
+
},
|
|
260
|
+
{ eager: false },
|
|
261
|
+
)
|
|
262
|
+
@JoinTable({
|
|
263
|
+
name: "ProjectScimTeam",
|
|
264
|
+
inverseJoinColumn: {
|
|
265
|
+
name: "teamId",
|
|
266
|
+
referencedColumnName: "_id",
|
|
267
|
+
},
|
|
268
|
+
joinColumn: {
|
|
269
|
+
name: "projectScimId",
|
|
270
|
+
referencedColumnName: "_id",
|
|
271
|
+
},
|
|
272
|
+
})
|
|
273
|
+
public teams?: Array<Team> = undefined;
|
|
274
|
+
|
|
275
|
+
@ColumnAccessControl({
|
|
276
|
+
create: [
|
|
277
|
+
Permission.ProjectOwner,
|
|
278
|
+
Permission.ProjectAdmin,
|
|
279
|
+
Permission.CreateProjectSSO,
|
|
280
|
+
],
|
|
281
|
+
read: [
|
|
282
|
+
Permission.ProjectOwner,
|
|
283
|
+
Permission.ProjectAdmin,
|
|
284
|
+
Permission.ProjectMember,
|
|
285
|
+
Permission.ReadProjectSSO,
|
|
286
|
+
],
|
|
287
|
+
update: [
|
|
288
|
+
Permission.ProjectOwner,
|
|
289
|
+
Permission.ProjectAdmin,
|
|
290
|
+
Permission.EditProjectSSO,
|
|
291
|
+
],
|
|
292
|
+
})
|
|
293
|
+
@TableColumn({
|
|
294
|
+
isDefaultValueColumn: true,
|
|
295
|
+
type: TableColumnType.Boolean,
|
|
296
|
+
title: "Auto Provision Users",
|
|
297
|
+
description: "Automatically create users when they are added via SCIM",
|
|
298
|
+
defaultValue: true,
|
|
299
|
+
})
|
|
300
|
+
@Column({
|
|
301
|
+
type: ColumnType.Boolean,
|
|
302
|
+
default: true,
|
|
303
|
+
})
|
|
304
|
+
public autoProvisionUsers?: boolean = undefined;
|
|
305
|
+
|
|
306
|
+
@ColumnAccessControl({
|
|
307
|
+
create: [
|
|
308
|
+
Permission.ProjectOwner,
|
|
309
|
+
Permission.ProjectAdmin,
|
|
310
|
+
Permission.CreateProjectSSO,
|
|
311
|
+
],
|
|
312
|
+
read: [
|
|
313
|
+
Permission.ProjectOwner,
|
|
314
|
+
Permission.ProjectAdmin,
|
|
315
|
+
Permission.ProjectMember,
|
|
316
|
+
Permission.ReadProjectSSO,
|
|
317
|
+
],
|
|
318
|
+
update: [
|
|
319
|
+
Permission.ProjectOwner,
|
|
320
|
+
Permission.ProjectAdmin,
|
|
321
|
+
Permission.EditProjectSSO,
|
|
322
|
+
],
|
|
323
|
+
})
|
|
324
|
+
@TableColumn({
|
|
325
|
+
isDefaultValueColumn: true,
|
|
326
|
+
type: TableColumnType.Boolean,
|
|
327
|
+
title: "Auto Deprovision Users",
|
|
328
|
+
description: "Automatically remove users when they are removed via SCIM",
|
|
329
|
+
defaultValue: true,
|
|
330
|
+
})
|
|
331
|
+
@Column({
|
|
332
|
+
type: ColumnType.Boolean,
|
|
333
|
+
default: true,
|
|
334
|
+
})
|
|
335
|
+
public autoDeprovisionUsers?: boolean = undefined;
|
|
336
|
+
|
|
337
|
+
@ColumnAccessControl({
|
|
338
|
+
create: [],
|
|
339
|
+
read: [
|
|
340
|
+
Permission.ProjectOwner,
|
|
341
|
+
Permission.ProjectAdmin,
|
|
342
|
+
Permission.ProjectMember,
|
|
343
|
+
Permission.ReadProjectSSO,
|
|
344
|
+
],
|
|
345
|
+
update: [],
|
|
346
|
+
})
|
|
347
|
+
@TableColumn({
|
|
348
|
+
manyToOneRelationColumn: "createdByUserId",
|
|
349
|
+
type: TableColumnType.Entity,
|
|
350
|
+
modelType: User,
|
|
351
|
+
title: "Created by User",
|
|
352
|
+
description:
|
|
353
|
+
"Relation to User who created this object (if this object was created by a User)",
|
|
354
|
+
})
|
|
355
|
+
@ManyToOne(
|
|
356
|
+
() => {
|
|
357
|
+
return User;
|
|
358
|
+
},
|
|
359
|
+
{
|
|
360
|
+
eager: false,
|
|
361
|
+
nullable: true,
|
|
362
|
+
onDelete: "SET NULL",
|
|
363
|
+
orphanedRowAction: "nullify",
|
|
364
|
+
},
|
|
365
|
+
)
|
|
366
|
+
@JoinColumn({ name: "createdByUserId" })
|
|
367
|
+
public createdByUser?: User = undefined;
|
|
368
|
+
|
|
369
|
+
@ColumnAccessControl({
|
|
370
|
+
create: [
|
|
371
|
+
Permission.ProjectOwner,
|
|
372
|
+
Permission.ProjectAdmin,
|
|
373
|
+
Permission.CreateProjectSSO,
|
|
374
|
+
],
|
|
375
|
+
read: [
|
|
376
|
+
Permission.ProjectOwner,
|
|
377
|
+
Permission.ProjectAdmin,
|
|
378
|
+
Permission.ProjectMember,
|
|
379
|
+
Permission.ReadProjectSSO,
|
|
380
|
+
],
|
|
381
|
+
update: [],
|
|
382
|
+
})
|
|
383
|
+
@TableColumn({
|
|
384
|
+
type: TableColumnType.ObjectID,
|
|
385
|
+
title: "Created by User ID",
|
|
386
|
+
description:
|
|
387
|
+
"User ID who created this object (if this object was created by a User)",
|
|
388
|
+
})
|
|
389
|
+
@Column({
|
|
390
|
+
type: ColumnType.ObjectID,
|
|
391
|
+
nullable: true,
|
|
392
|
+
transformer: ObjectID.getDatabaseTransformer(),
|
|
393
|
+
})
|
|
394
|
+
public createdByUserId?: ObjectID = undefined;
|
|
395
|
+
|
|
396
|
+
@ColumnAccessControl({
|
|
397
|
+
create: [],
|
|
398
|
+
read: [
|
|
399
|
+
Permission.ProjectOwner,
|
|
400
|
+
Permission.ProjectAdmin,
|
|
401
|
+
Permission.ProjectMember,
|
|
402
|
+
Permission.ReadProjectSSO,
|
|
403
|
+
],
|
|
404
|
+
update: [],
|
|
405
|
+
})
|
|
406
|
+
@TableColumn({
|
|
407
|
+
manyToOneRelationColumn: "deletedByUserId",
|
|
408
|
+
type: TableColumnType.Entity,
|
|
409
|
+
modelType: User,
|
|
410
|
+
title: "Deleted by User",
|
|
411
|
+
description:
|
|
412
|
+
"Relation to User who deleted this object (if this object was deleted by a User)",
|
|
413
|
+
})
|
|
414
|
+
@ManyToOne(
|
|
415
|
+
() => {
|
|
416
|
+
return User;
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
cascade: false,
|
|
420
|
+
eager: false,
|
|
421
|
+
nullable: true,
|
|
422
|
+
onDelete: "SET NULL",
|
|
423
|
+
orphanedRowAction: "nullify",
|
|
424
|
+
},
|
|
425
|
+
)
|
|
426
|
+
@JoinColumn({ name: "deletedByUserId" })
|
|
427
|
+
public deletedByUser?: User = undefined;
|
|
428
|
+
|
|
429
|
+
@ColumnAccessControl({
|
|
430
|
+
create: [],
|
|
431
|
+
read: [
|
|
432
|
+
Permission.ProjectOwner,
|
|
433
|
+
Permission.ProjectAdmin,
|
|
434
|
+
Permission.ProjectMember,
|
|
435
|
+
Permission.ReadProjectSSO,
|
|
436
|
+
],
|
|
437
|
+
update: [],
|
|
438
|
+
})
|
|
439
|
+
@TableColumn({
|
|
440
|
+
type: TableColumnType.ObjectID,
|
|
441
|
+
title: "Deleted by User ID",
|
|
442
|
+
description:
|
|
443
|
+
"User ID who deleted this object (if this object was deleted by a User)",
|
|
444
|
+
})
|
|
445
|
+
@Column({
|
|
446
|
+
type: ColumnType.ObjectID,
|
|
447
|
+
nullable: true,
|
|
448
|
+
transformer: ObjectID.getDatabaseTransformer(),
|
|
449
|
+
})
|
|
450
|
+
public deletedByUserId?: ObjectID = undefined;
|
|
451
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
export class MigrationName1754304193228 implements MigrationInterface {
|
|
4
|
+
public name = "MigrationName1754304193228";
|
|
5
|
+
|
|
6
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
7
|
+
await queryRunner.query(
|
|
8
|
+
`CREATE TABLE "ProjectSCIM" ("_id" uuid NOT NULL DEFAULT uuid_generate_v4(), "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(), "deletedAt" TIMESTAMP WITH TIME ZONE, "version" integer NOT NULL, "projectId" uuid NOT NULL, "name" character varying(100) NOT NULL, "description" character varying(500), "bearerToken" character varying(500) NOT NULL, "autoProvisionUsers" boolean NOT NULL DEFAULT true, "autoDeprovisionUsers" boolean NOT NULL DEFAULT true, "isEnabled" boolean NOT NULL DEFAULT false, "createdByUserId" uuid, "deletedByUserId" uuid, CONSTRAINT "PK_51e71d70211675a5c918aee4e68" PRIMARY KEY ("_id"))`,
|
|
9
|
+
);
|
|
10
|
+
await queryRunner.query(
|
|
11
|
+
`CREATE INDEX "IDX_f916360335859c26c4d7051239" ON "ProjectSCIM" ("projectId") `,
|
|
12
|
+
);
|
|
13
|
+
await queryRunner.query(
|
|
14
|
+
`CREATE TABLE "ProjectScimTeam" ("projectScimId" uuid NOT NULL, "teamId" uuid NOT NULL, CONSTRAINT "PK_db724b66b4fa8c880ce5ccf820b" PRIMARY KEY ("projectScimId", "teamId"))`,
|
|
15
|
+
);
|
|
16
|
+
await queryRunner.query(
|
|
17
|
+
`CREATE INDEX "IDX_b9a28efd66600267f0e9de0731" ON "ProjectScimTeam" ("projectScimId") `,
|
|
18
|
+
);
|
|
19
|
+
await queryRunner.query(
|
|
20
|
+
`CREATE INDEX "IDX_bb0eda2ef0c773f975e9ad8448" ON "ProjectScimTeam" ("teamId") `,
|
|
21
|
+
);
|
|
22
|
+
await queryRunner.query(
|
|
23
|
+
`ALTER TABLE "ProjectSCIM" ADD CONSTRAINT "FK_f916360335859c26c4d7051239b" FOREIGN KEY ("projectId") REFERENCES "Project"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
|
24
|
+
);
|
|
25
|
+
await queryRunner.query(
|
|
26
|
+
`ALTER TABLE "ProjectSCIM" ADD CONSTRAINT "FK_5d5d587984f156e5215d51daff7" FOREIGN KEY ("createdByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
|
|
27
|
+
);
|
|
28
|
+
await queryRunner.query(
|
|
29
|
+
`ALTER TABLE "ProjectSCIM" ADD CONSTRAINT "FK_9cadda4fc2af268b5670d02bf76" FOREIGN KEY ("deletedByUserId") REFERENCES "User"("_id") ON DELETE SET NULL ON UPDATE NO ACTION`,
|
|
30
|
+
);
|
|
31
|
+
await queryRunner.query(
|
|
32
|
+
`ALTER TABLE "ProjectScimTeam" ADD CONSTRAINT "FK_b9a28efd66600267f0e9de0731b" FOREIGN KEY ("projectScimId") REFERENCES "ProjectSCIM"("_id") ON DELETE CASCADE ON UPDATE CASCADE`,
|
|
33
|
+
);
|
|
34
|
+
await queryRunner.query(
|
|
35
|
+
`ALTER TABLE "ProjectScimTeam" ADD CONSTRAINT "FK_bb0eda2ef0c773f975e9ad8448a" FOREIGN KEY ("teamId") REFERENCES "Team"("_id") ON DELETE CASCADE ON UPDATE CASCADE`,
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
40
|
+
await queryRunner.query(
|
|
41
|
+
`ALTER TABLE "ProjectScimTeam" DROP CONSTRAINT "FK_bb0eda2ef0c773f975e9ad8448a"`,
|
|
42
|
+
);
|
|
43
|
+
await queryRunner.query(
|
|
44
|
+
`ALTER TABLE "ProjectScimTeam" DROP CONSTRAINT "FK_b9a28efd66600267f0e9de0731b"`,
|
|
45
|
+
);
|
|
46
|
+
await queryRunner.query(
|
|
47
|
+
`ALTER TABLE "ProjectSCIM" DROP CONSTRAINT "FK_9cadda4fc2af268b5670d02bf76"`,
|
|
48
|
+
);
|
|
49
|
+
await queryRunner.query(
|
|
50
|
+
`ALTER TABLE "ProjectSCIM" DROP CONSTRAINT "FK_5d5d587984f156e5215d51daff7"`,
|
|
51
|
+
);
|
|
52
|
+
await queryRunner.query(
|
|
53
|
+
`ALTER TABLE "ProjectSCIM" DROP CONSTRAINT "FK_f916360335859c26c4d7051239b"`,
|
|
54
|
+
);
|
|
55
|
+
await queryRunner.query(
|
|
56
|
+
`DROP INDEX "public"."IDX_bb0eda2ef0c773f975e9ad8448"`,
|
|
57
|
+
);
|
|
58
|
+
await queryRunner.query(
|
|
59
|
+
`DROP INDEX "public"."IDX_b9a28efd66600267f0e9de0731"`,
|
|
60
|
+
);
|
|
61
|
+
await queryRunner.query(`DROP TABLE "ProjectScimTeam"`);
|
|
62
|
+
await queryRunner.query(
|
|
63
|
+
`DROP INDEX "public"."IDX_f916360335859c26c4d7051239"`,
|
|
64
|
+
);
|
|
65
|
+
await queryRunner.query(`DROP TABLE "ProjectSCIM"`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
export class MigrationName1754315774827 implements MigrationInterface {
|
|
4
|
+
public name = "MigrationName1754315774827";
|
|
5
|
+
|
|
6
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
7
|
+
await queryRunner.query(
|
|
8
|
+
`ALTER TABLE "ProjectSCIM" DROP COLUMN "isEnabled"`,
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
13
|
+
await queryRunner.query(
|
|
14
|
+
`ALTER TABLE "ProjectSCIM" ADD "isEnabled" boolean NOT NULL DEFAULT false`,
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -146,6 +146,8 @@ import { MigrationName1753343522987 } from "./1753343522987-MigrationName";
|
|
|
146
146
|
import { MigrationName1753377161288 } from "./1753377161288-MigrationName";
|
|
147
147
|
import { AddPerformanceIndexes1753378524062 } from "./1753378524062-AddPerformanceIndexes";
|
|
148
148
|
import { MigrationName1753383711511 } from "./1753383711511-MigrationName";
|
|
149
|
+
import { MigrationName1754304193228 } from "./1754304193228-MigrationName";
|
|
150
|
+
import { MigrationName1754315774827 } from "./1754315774827-MigrationName";
|
|
149
151
|
|
|
150
152
|
export default [
|
|
151
153
|
InitialMigration,
|
|
@@ -296,4 +298,6 @@ export default [
|
|
|
296
298
|
MigrationName1753377161288,
|
|
297
299
|
AddPerformanceIndexes1753378524062,
|
|
298
300
|
MigrationName1753383711511,
|
|
301
|
+
MigrationName1754304193228,
|
|
302
|
+
MigrationName1754315774827,
|
|
299
303
|
];
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import ProjectSCIMService from "../Services/ProjectSCIMService";
|
|
2
|
+
import {
|
|
3
|
+
ExpressRequest,
|
|
4
|
+
ExpressResponse,
|
|
5
|
+
NextFunction,
|
|
6
|
+
OneUptimeRequest,
|
|
7
|
+
} from "../Utils/Express";
|
|
8
|
+
import ObjectID from "../../Types/ObjectID";
|
|
9
|
+
import ProjectSCIM from "../../Models/DatabaseModels/ProjectSCIM";
|
|
10
|
+
import NotAuthorizedException from "../../Types/Exception/NotAuthorizedException";
|
|
11
|
+
import BadRequestException from "../../Types/Exception/BadRequestException";
|
|
12
|
+
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
13
|
+
import logger from "../Utils/Logger";
|
|
14
|
+
|
|
15
|
+
export default class SCIMMiddleware {
|
|
16
|
+
@CaptureSpan()
|
|
17
|
+
public static async isAuthorizedSCIMRequest(
|
|
18
|
+
req: ExpressRequest,
|
|
19
|
+
_res: ExpressResponse,
|
|
20
|
+
next: NextFunction,
|
|
21
|
+
): Promise<void> {
|
|
22
|
+
try {
|
|
23
|
+
const oneuptimeRequest: OneUptimeRequest = req as OneUptimeRequest;
|
|
24
|
+
|
|
25
|
+
// Extract project SCIM ID from URL path
|
|
26
|
+
const projectScimId: string | undefined = req.params["projectScimId"];
|
|
27
|
+
if (!projectScimId) {
|
|
28
|
+
throw new BadRequestException("Project SCIM ID is required");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Extract bearer token from Authorization header
|
|
32
|
+
let bearerToken: string | undefined;
|
|
33
|
+
if (req.headers?.["authorization"]) {
|
|
34
|
+
const authHeader: string = req.headers["authorization"] as string;
|
|
35
|
+
if (authHeader.startsWith("Bearer ")) {
|
|
36
|
+
bearerToken = authHeader.substring(7);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
logger.debug(
|
|
41
|
+
`SCIM Authorization: projectScimId=${projectScimId}, bearerToken=${
|
|
42
|
+
bearerToken
|
|
43
|
+
}`,
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
if (!bearerToken) {
|
|
47
|
+
throw new NotAuthorizedException(
|
|
48
|
+
"Bearer token is required for SCIM authentication",
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Find SCIM configuration by SCIM ID and bearer token
|
|
53
|
+
const scimConfig: ProjectSCIM | null = await ProjectSCIMService.findOneBy(
|
|
54
|
+
{
|
|
55
|
+
query: {
|
|
56
|
+
_id: new ObjectID(projectScimId),
|
|
57
|
+
bearerToken: bearerToken,
|
|
58
|
+
},
|
|
59
|
+
select: {
|
|
60
|
+
_id: true,
|
|
61
|
+
projectId: true,
|
|
62
|
+
autoProvisionUsers: true,
|
|
63
|
+
autoDeprovisionUsers: true,
|
|
64
|
+
teams: {
|
|
65
|
+
_id: true,
|
|
66
|
+
name: true,
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
props: {
|
|
70
|
+
isRoot: true,
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
if (!scimConfig) {
|
|
76
|
+
throw new NotAuthorizedException(
|
|
77
|
+
"Invalid bearer token or SCIM configuration not found",
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Store SCIM configuration and project ID in bearerTokenData for use in handlers
|
|
82
|
+
oneuptimeRequest.bearerTokenData = {
|
|
83
|
+
scimConfig: scimConfig,
|
|
84
|
+
projectId: scimConfig.projectId,
|
|
85
|
+
projectScimId: new ObjectID(projectScimId),
|
|
86
|
+
type: "scim",
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
return next();
|
|
90
|
+
} catch (err) {
|
|
91
|
+
return next(err);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import CreateBy from "../Types/Database/CreateBy";
|
|
2
|
+
import { OnCreate } from "../Types/Database/Hooks";
|
|
3
|
+
import DatabaseService from "./DatabaseService";
|
|
4
|
+
import Model from "../../Models/DatabaseModels/ProjectSCIM";
|
|
5
|
+
import ObjectID from "../../Types/ObjectID";
|
|
6
|
+
|
|
7
|
+
export class Service extends DatabaseService<Model> {
|
|
8
|
+
public constructor() {
|
|
9
|
+
super(Model);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
protected override async onBeforeCreate(
|
|
13
|
+
createBy: CreateBy<Model>,
|
|
14
|
+
): Promise<OnCreate<Model>> {
|
|
15
|
+
if (!createBy.data.bearerToken) {
|
|
16
|
+
// Generate a secure bearer token if not provided
|
|
17
|
+
createBy.data.bearerToken = ObjectID.generate().toString();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
createBy: createBy,
|
|
22
|
+
carryForward: {},
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export default new Service();
|