@oneuptime/common 8.0.5587 → 9.0.5595
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/StatusPage.ts +73 -0
- package/Server/API/StatusPageAPI.ts +79 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1763643080445-MigrationName.ts +23 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +2 -0
- package/Server/Services/StatusPageService.ts +63 -0
- package/Server/Utils/Cookie.ts +48 -0
- package/Types/CookieName.ts +1 -0
- package/Types/Exception/MasterPasswordRequiredException.ts +7 -0
- package/Types/StatusPage/MasterPassword.ts +10 -0
- package/build/dist/Models/DatabaseModels/StatusPage.js +74 -0
- package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +80 -28
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1763643080445-MigrationName.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1763643080445-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +2 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +38 -0
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Utils/Cookie.js +36 -0
- package/build/dist/Server/Utils/Cookie.js.map +1 -1
- package/build/dist/Types/CookieName.js +1 -0
- package/build/dist/Types/CookieName.js.map +1 -1
- package/build/dist/Types/Exception/MasterPasswordRequiredException.js +7 -0
- package/build/dist/Types/Exception/MasterPasswordRequiredException.js.map +1 -0
- package/build/dist/Types/StatusPage/MasterPassword.js +5 -0
- package/build/dist/Types/StatusPage/MasterPassword.js.map +1 -0
- package/package.json +1 -1
|
@@ -30,6 +30,7 @@ import { JSONObject } from "../../Types/JSON";
|
|
|
30
30
|
import ObjectID from "../../Types/ObjectID";
|
|
31
31
|
import Permission from "../../Types/Permission";
|
|
32
32
|
import Timezone from "../../Types/Timezone";
|
|
33
|
+
import HashedString from "../../Types/HashedString";
|
|
33
34
|
import {
|
|
34
35
|
Column,
|
|
35
36
|
Entity,
|
|
@@ -883,6 +884,78 @@ export default class StatusPage extends BaseModel {
|
|
|
883
884
|
})
|
|
884
885
|
public isPublicStatusPage?: boolean = undefined;
|
|
885
886
|
|
|
887
|
+
@ColumnAccessControl({
|
|
888
|
+
create: [
|
|
889
|
+
Permission.ProjectOwner,
|
|
890
|
+
Permission.ProjectAdmin,
|
|
891
|
+
Permission.ProjectMember,
|
|
892
|
+
Permission.CreateProjectStatusPage,
|
|
893
|
+
],
|
|
894
|
+
read: [
|
|
895
|
+
Permission.ProjectOwner,
|
|
896
|
+
Permission.ProjectAdmin,
|
|
897
|
+
Permission.ProjectMember,
|
|
898
|
+
Permission.ReadProjectStatusPage,
|
|
899
|
+
],
|
|
900
|
+
update: [
|
|
901
|
+
Permission.ProjectOwner,
|
|
902
|
+
Permission.ProjectAdmin,
|
|
903
|
+
Permission.ProjectMember,
|
|
904
|
+
Permission.EditProjectStatusPage,
|
|
905
|
+
],
|
|
906
|
+
})
|
|
907
|
+
@TableColumn({
|
|
908
|
+
isDefaultValueColumn: true,
|
|
909
|
+
type: TableColumnType.Boolean,
|
|
910
|
+
title: "Enable Master Password",
|
|
911
|
+
description:
|
|
912
|
+
"Require visitors to enter a master password before viewing a private status page.",
|
|
913
|
+
defaultValue: false,
|
|
914
|
+
})
|
|
915
|
+
@Column({
|
|
916
|
+
type: ColumnType.Boolean,
|
|
917
|
+
default: false,
|
|
918
|
+
})
|
|
919
|
+
public enableMasterPassword?: boolean = undefined;
|
|
920
|
+
|
|
921
|
+
@ColumnAccessControl({
|
|
922
|
+
create: [
|
|
923
|
+
Permission.ProjectOwner,
|
|
924
|
+
Permission.ProjectAdmin,
|
|
925
|
+
Permission.ProjectMember,
|
|
926
|
+
Permission.CreateProjectStatusPage,
|
|
927
|
+
],
|
|
928
|
+
|
|
929
|
+
// This is a hashed column. So, reading the value is does not affect anything.
|
|
930
|
+
read: [
|
|
931
|
+
Permission.ProjectOwner,
|
|
932
|
+
Permission.ProjectAdmin,
|
|
933
|
+
Permission.ProjectMember,
|
|
934
|
+
Permission.ReadProjectStatusPage,
|
|
935
|
+
],
|
|
936
|
+
update: [
|
|
937
|
+
Permission.ProjectOwner,
|
|
938
|
+
Permission.ProjectAdmin,
|
|
939
|
+
Permission.ProjectMember,
|
|
940
|
+
Permission.EditProjectStatusPage,
|
|
941
|
+
],
|
|
942
|
+
})
|
|
943
|
+
@TableColumn({
|
|
944
|
+
title: "Master Password",
|
|
945
|
+
description:
|
|
946
|
+
"Password required to unlock a private status page. This value is stored as a secure hash.",
|
|
947
|
+
hashed: true,
|
|
948
|
+
type: TableColumnType.HashedString,
|
|
949
|
+
placeholder: "Enter a new master password",
|
|
950
|
+
})
|
|
951
|
+
@Column({
|
|
952
|
+
type: ColumnType.HashedString,
|
|
953
|
+
length: ColumnLength.HashedString,
|
|
954
|
+
nullable: true,
|
|
955
|
+
transformer: HashedString.getDatabaseTransformer(),
|
|
956
|
+
})
|
|
957
|
+
public masterPassword?: HashedString = undefined;
|
|
958
|
+
|
|
886
959
|
@ColumnAccessControl({
|
|
887
960
|
create: [
|
|
888
961
|
Permission.ProjectOwner,
|
|
@@ -49,6 +49,7 @@ import JSONFunctions from "../../Types/JSONFunctions";
|
|
|
49
49
|
import ObjectID from "../../Types/ObjectID";
|
|
50
50
|
import Phone from "../../Types/Phone";
|
|
51
51
|
import PositiveNumber from "../../Types/PositiveNumber";
|
|
52
|
+
import HashedString from "../../Types/HashedString";
|
|
52
53
|
import AcmeChallenge from "../../Models/DatabaseModels/AcmeChallenge";
|
|
53
54
|
import Incident from "../../Models/DatabaseModels/Incident";
|
|
54
55
|
import IncidentPublicNote from "../../Models/DatabaseModels/IncidentPublicNote";
|
|
@@ -87,10 +88,13 @@ import EmailTemplateType from "../../Types/Email/EmailTemplateType";
|
|
|
87
88
|
import Hostname from "../../Types/API/Hostname";
|
|
88
89
|
import Protocol from "../../Types/API/Protocol";
|
|
89
90
|
import DatabaseConfig from "../DatabaseConfig";
|
|
91
|
+
import CookieUtil from "../Utils/Cookie";
|
|
92
|
+
import { EncryptionSecret } from "../EnvironmentConfig";
|
|
90
93
|
import { StatusPageApiRoute } from "../../ServiceRoute";
|
|
91
94
|
import ProjectSmtpConfigService from "../Services/ProjectSmtpConfigService";
|
|
92
95
|
import ForbiddenException from "../../Types/Exception/ForbiddenException";
|
|
93
96
|
import SlackUtil from "../Utils/Workspace/Slack/Slack";
|
|
97
|
+
import { MASTER_PASSWORD_INVALID_MESSAGE } from "../../Types/StatusPage/MasterPassword";
|
|
94
98
|
|
|
95
99
|
type ResolveStatusPageIdOrThrowFunction = (
|
|
96
100
|
statusPageIdOrDomain: string,
|
|
@@ -798,6 +802,7 @@ export default class StatusPageAPI extends BaseAPI<
|
|
|
798
802
|
enableMicrosoftTeamsSubscribers: true,
|
|
799
803
|
enableSmsSubscribers: true,
|
|
800
804
|
isPublicStatusPage: true,
|
|
805
|
+
enableMasterPassword: true,
|
|
801
806
|
allowSubscribersToChooseResources: true,
|
|
802
807
|
allowSubscribersToChooseEventTypes: true,
|
|
803
808
|
requireSsoForLogin: true,
|
|
@@ -910,6 +915,80 @@ export default class StatusPageAPI extends BaseAPI<
|
|
|
910
915
|
},
|
|
911
916
|
);
|
|
912
917
|
|
|
918
|
+
this.router.post(
|
|
919
|
+
`${new this.entityType()
|
|
920
|
+
.getCrudApiPath()
|
|
921
|
+
?.toString()}/master-password/:statusPageId`,
|
|
922
|
+
UserMiddleware.getUserMiddleware,
|
|
923
|
+
async (req: ExpressRequest, res: ExpressResponse, next: NextFunction) => {
|
|
924
|
+
try {
|
|
925
|
+
if (!req.params["statusPageId"]) {
|
|
926
|
+
throw new BadDataException("Status Page ID not found");
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
const statusPageId: ObjectID = new ObjectID(
|
|
930
|
+
req.params["statusPageId"] as string,
|
|
931
|
+
);
|
|
932
|
+
|
|
933
|
+
const password: string | undefined =
|
|
934
|
+
req.body && (req.body["password"] as string);
|
|
935
|
+
|
|
936
|
+
if (!password) {
|
|
937
|
+
throw new BadDataException("Master password is required.");
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
const statusPage: StatusPage | null =
|
|
941
|
+
await StatusPageService.findOneById({
|
|
942
|
+
id: statusPageId,
|
|
943
|
+
select: {
|
|
944
|
+
_id: true,
|
|
945
|
+
projectId: true,
|
|
946
|
+
enableMasterPassword: true,
|
|
947
|
+
masterPassword: true,
|
|
948
|
+
isPublicStatusPage: true,
|
|
949
|
+
},
|
|
950
|
+
props: {
|
|
951
|
+
isRoot: true,
|
|
952
|
+
},
|
|
953
|
+
});
|
|
954
|
+
|
|
955
|
+
if (!statusPage) {
|
|
956
|
+
throw new NotFoundException("Status Page not found");
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
if (statusPage.isPublicStatusPage) {
|
|
960
|
+
throw new BadDataException(
|
|
961
|
+
"This status page is already visible to everyone.",
|
|
962
|
+
);
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
if (!statusPage.enableMasterPassword || !statusPage.masterPassword) {
|
|
966
|
+
throw new BadDataException(
|
|
967
|
+
"Master password has not been configured for this status page.",
|
|
968
|
+
);
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
const hashedInput: string = await HashedString.hashValue(
|
|
972
|
+
password,
|
|
973
|
+
EncryptionSecret,
|
|
974
|
+
);
|
|
975
|
+
|
|
976
|
+
if (hashedInput !== statusPage.masterPassword.toString()) {
|
|
977
|
+
throw new BadDataException(MASTER_PASSWORD_INVALID_MESSAGE);
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
CookieUtil.setStatusPageMasterPasswordCookie({
|
|
981
|
+
expressResponse: res,
|
|
982
|
+
statusPageId,
|
|
983
|
+
});
|
|
984
|
+
|
|
985
|
+
return Response.sendEmptySuccessResponse(req, res);
|
|
986
|
+
} catch (err) {
|
|
987
|
+
next(err);
|
|
988
|
+
}
|
|
989
|
+
},
|
|
990
|
+
);
|
|
991
|
+
|
|
913
992
|
this.router.post(
|
|
914
993
|
`${new this.entityType().getCrudApiPath()?.toString()}/sso/:statusPageId`,
|
|
915
994
|
UserMiddleware.getUserMiddleware,
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
export class MigrationName1763643080445 implements MigrationInterface {
|
|
4
|
+
public name = "MigrationName1763643080445";
|
|
5
|
+
|
|
6
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
7
|
+
await queryRunner.query(
|
|
8
|
+
`ALTER TABLE "StatusPage" ADD "enableMasterPassword" boolean NOT NULL DEFAULT false`,
|
|
9
|
+
);
|
|
10
|
+
await queryRunner.query(
|
|
11
|
+
`ALTER TABLE "StatusPage" ADD "masterPassword" character varying(64)`,
|
|
12
|
+
);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
16
|
+
await queryRunner.query(
|
|
17
|
+
`ALTER TABLE "StatusPage" DROP COLUMN "masterPassword"`,
|
|
18
|
+
);
|
|
19
|
+
await queryRunner.query(
|
|
20
|
+
`ALTER TABLE "StatusPage" DROP COLUMN "enableMasterPassword"`,
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -185,6 +185,7 @@ import { MigrationName1762890441920 } from "./1762890441920-MigrationName";
|
|
|
185
185
|
import { MigrationName1763471659817 } from "./1763471659817-MigrationName";
|
|
186
186
|
import { MigrationName1763477560906 } from "./1763477560906-MigrationName";
|
|
187
187
|
import { MigrationName1763480947474 } from "./1763480947474-MigrationName";
|
|
188
|
+
import { MigrationName1763643080445 } from "./1763643080445-MigrationName";
|
|
188
189
|
|
|
189
190
|
export default [
|
|
190
191
|
InitialMigration,
|
|
@@ -374,4 +375,5 @@ export default [
|
|
|
374
375
|
MigrationName1763471659817,
|
|
375
376
|
MigrationName1763477560906,
|
|
376
377
|
MigrationName1763480947474,
|
|
378
|
+
MigrationName1763643080445,
|
|
377
379
|
];
|
|
@@ -47,6 +47,7 @@ import ProjectSMTPConfigService from "./ProjectSmtpConfigService";
|
|
|
47
47
|
import StatusPageResource from "../../Models/DatabaseModels/StatusPageResource";
|
|
48
48
|
import StatusPageResourceService from "./StatusPageResourceService";
|
|
49
49
|
import Dictionary from "../../Types/Dictionary";
|
|
50
|
+
import { JSONObject } from "../../Types/JSON";
|
|
50
51
|
import MonitorGroupResource from "../../Models/DatabaseModels/MonitorGroupResource";
|
|
51
52
|
import MonitorGroupResourceService from "./MonitorGroupResourceService";
|
|
52
53
|
import QueryHelper from "../Types/Database/QueryHelper";
|
|
@@ -61,6 +62,11 @@ import IP from "../../Types/IP/IP";
|
|
|
61
62
|
import NotAuthenticatedException from "../../Types/Exception/NotAuthenticatedException";
|
|
62
63
|
import ForbiddenException from "../../Types/Exception/ForbiddenException";
|
|
63
64
|
import CommonAPI from "../API/CommonAPI";
|
|
65
|
+
import MasterPasswordRequiredException from "../../Types/Exception/MasterPasswordRequiredException";
|
|
66
|
+
import {
|
|
67
|
+
MASTER_PASSWORD_COOKIE_IDENTIFIER,
|
|
68
|
+
MASTER_PASSWORD_REQUIRED_MESSAGE,
|
|
69
|
+
} from "../../Types/StatusPage/MasterPassword";
|
|
64
70
|
|
|
65
71
|
export interface StatusPageReportItem {
|
|
66
72
|
resourceName: string;
|
|
@@ -389,6 +395,8 @@ export class Service extends DatabaseService<StatusPage> {
|
|
|
389
395
|
_id: true,
|
|
390
396
|
isPublicStatusPage: true,
|
|
391
397
|
ipWhitelist: true,
|
|
398
|
+
enableMasterPassword: true,
|
|
399
|
+
masterPassword: true,
|
|
392
400
|
},
|
|
393
401
|
});
|
|
394
402
|
|
|
@@ -462,6 +470,34 @@ export class Service extends DatabaseService<StatusPage> {
|
|
|
462
470
|
}
|
|
463
471
|
}
|
|
464
472
|
|
|
473
|
+
const shouldEnforceMasterPassword: boolean = Boolean(
|
|
474
|
+
statusPage &&
|
|
475
|
+
statusPage.enableMasterPassword &&
|
|
476
|
+
statusPage.masterPassword &&
|
|
477
|
+
!statusPage.isPublicStatusPage,
|
|
478
|
+
);
|
|
479
|
+
|
|
480
|
+
if (shouldEnforceMasterPassword) {
|
|
481
|
+
const hasValidMasterPassword: boolean =
|
|
482
|
+
this.hasValidMasterPasswordCookie({
|
|
483
|
+
req,
|
|
484
|
+
statusPageId,
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
if (hasValidMasterPassword) {
|
|
488
|
+
return {
|
|
489
|
+
hasReadAccess: true,
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
return {
|
|
494
|
+
hasReadAccess: false,
|
|
495
|
+
error: new MasterPasswordRequiredException(
|
|
496
|
+
MASTER_PASSWORD_REQUIRED_MESSAGE,
|
|
497
|
+
),
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
|
|
465
501
|
// if it does not have public access, check if this user has access.
|
|
466
502
|
|
|
467
503
|
const items: Array<StatusPage> = await this.findBy({
|
|
@@ -493,6 +529,33 @@ export class Service extends DatabaseService<StatusPage> {
|
|
|
493
529
|
};
|
|
494
530
|
}
|
|
495
531
|
|
|
532
|
+
private hasValidMasterPasswordCookie(data: {
|
|
533
|
+
req: ExpressRequest;
|
|
534
|
+
statusPageId: ObjectID;
|
|
535
|
+
}): boolean {
|
|
536
|
+
const token: string | undefined = CookieUtil.getCookieFromExpressRequest(
|
|
537
|
+
data.req,
|
|
538
|
+
CookieUtil.getStatusPageMasterPasswordKey(data.statusPageId),
|
|
539
|
+
);
|
|
540
|
+
|
|
541
|
+
if (!token) {
|
|
542
|
+
return false;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
try {
|
|
546
|
+
const payload: JSONObject = JSONWebToken.decodeJsonPayload(token);
|
|
547
|
+
|
|
548
|
+
return (
|
|
549
|
+
payload["statusPageId"] === data.statusPageId.toString() &&
|
|
550
|
+
payload["type"] === MASTER_PASSWORD_COOKIE_IDENTIFIER
|
|
551
|
+
);
|
|
552
|
+
} catch (err) {
|
|
553
|
+
logger.error(err);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
return false;
|
|
557
|
+
}
|
|
558
|
+
|
|
496
559
|
@CaptureSpan()
|
|
497
560
|
public async getMonitorStatusTimelineForStatusPage(data: {
|
|
498
561
|
monitorIds: Array<ObjectID>;
|
package/Server/Utils/Cookie.ts
CHANGED
|
@@ -8,6 +8,10 @@ import StatusPagePrivateUser from "../../Models/DatabaseModels/StatusPagePrivate
|
|
|
8
8
|
import OneUptimeDate from "../../Types/Date";
|
|
9
9
|
import PositiveNumber from "../../Types/PositiveNumber";
|
|
10
10
|
import CookieName from "../../Types/CookieName";
|
|
11
|
+
import {
|
|
12
|
+
MASTER_PASSWORD_COOKIE_IDENTIFIER,
|
|
13
|
+
MASTER_PASSWORD_COOKIE_MAX_AGE_IN_DAYS,
|
|
14
|
+
} from "../../Types/StatusPage/MasterPassword";
|
|
11
15
|
import CaptureSpan from "./Telemetry/CaptureSpan";
|
|
12
16
|
|
|
13
17
|
export default class CookieUtil {
|
|
@@ -233,6 +237,34 @@ export default class CookieUtil {
|
|
|
233
237
|
return token;
|
|
234
238
|
}
|
|
235
239
|
|
|
240
|
+
@CaptureSpan()
|
|
241
|
+
public static setStatusPageMasterPasswordCookie(data: {
|
|
242
|
+
expressResponse: ExpressResponse;
|
|
243
|
+
statusPageId: ObjectID;
|
|
244
|
+
}): void {
|
|
245
|
+
const expiresInDays: PositiveNumber = new PositiveNumber(
|
|
246
|
+
MASTER_PASSWORD_COOKIE_MAX_AGE_IN_DAYS,
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
const token: string = JSONWebToken.signJsonPayload(
|
|
250
|
+
{
|
|
251
|
+
statusPageId: data.statusPageId.toString(),
|
|
252
|
+
type: MASTER_PASSWORD_COOKIE_IDENTIFIER,
|
|
253
|
+
},
|
|
254
|
+
OneUptimeDate.getSecondsInDays(expiresInDays),
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
CookieUtil.setCookie(
|
|
258
|
+
data.expressResponse,
|
|
259
|
+
CookieUtil.getStatusPageMasterPasswordKey(data.statusPageId),
|
|
260
|
+
token,
|
|
261
|
+
{
|
|
262
|
+
maxAge: OneUptimeDate.getMillisecondsInDays(expiresInDays),
|
|
263
|
+
httpOnly: true,
|
|
264
|
+
},
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
236
268
|
@CaptureSpan()
|
|
237
269
|
public static setCookie(
|
|
238
270
|
res: ExpressResponse,
|
|
@@ -280,6 +312,17 @@ export default class CookieUtil {
|
|
|
280
312
|
});
|
|
281
313
|
}
|
|
282
314
|
|
|
315
|
+
@CaptureSpan()
|
|
316
|
+
public static removeStatusPageMasterPasswordCookie(
|
|
317
|
+
res: ExpressResponse,
|
|
318
|
+
statusPageId: ObjectID,
|
|
319
|
+
): void {
|
|
320
|
+
CookieUtil.removeCookie(
|
|
321
|
+
res,
|
|
322
|
+
CookieUtil.getStatusPageMasterPasswordKey(statusPageId),
|
|
323
|
+
);
|
|
324
|
+
}
|
|
325
|
+
|
|
283
326
|
// get all cookies with express request
|
|
284
327
|
@CaptureSpan()
|
|
285
328
|
public static getAllCookies(req: ExpressRequest): Dictionary<string> {
|
|
@@ -304,6 +347,11 @@ export default class CookieUtil {
|
|
|
304
347
|
return `${CookieName.RefreshToken}-${id.toString()}`;
|
|
305
348
|
}
|
|
306
349
|
|
|
350
|
+
@CaptureSpan()
|
|
351
|
+
public static getStatusPageMasterPasswordKey(id: ObjectID): string {
|
|
352
|
+
return `${CookieName.StatusPageMasterPassword}-${id.toString()}`;
|
|
353
|
+
}
|
|
354
|
+
|
|
307
355
|
@CaptureSpan()
|
|
308
356
|
public static getUserSSOKey(id: ObjectID): string {
|
|
309
357
|
return `${this.getSSOKey()}${id.toString()}`;
|
package/Types/CookieName.ts
CHANGED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const MASTER_PASSWORD_REQUIRED_MESSAGE: string =
|
|
2
|
+
"Master password required";
|
|
3
|
+
|
|
4
|
+
export const MASTER_PASSWORD_INVALID_MESSAGE: string =
|
|
5
|
+
"Invalid master password. Please try again.";
|
|
6
|
+
|
|
7
|
+
export const MASTER_PASSWORD_COOKIE_IDENTIFIER: string =
|
|
8
|
+
"status-page-master-password";
|
|
9
|
+
|
|
10
|
+
export const MASTER_PASSWORD_COOKIE_MAX_AGE_IN_DAYS: number = 7;
|
|
@@ -37,6 +37,7 @@ import Recurring from "../../Types/Events/Recurring";
|
|
|
37
37
|
import IconProp from "../../Types/Icon/IconProp";
|
|
38
38
|
import ObjectID from "../../Types/ObjectID";
|
|
39
39
|
import Permission from "../../Types/Permission";
|
|
40
|
+
import HashedString from "../../Types/HashedString";
|
|
40
41
|
import { Column, Entity, Index, JoinColumn, JoinTable, ManyToMany, ManyToOne, } from "typeorm";
|
|
41
42
|
import UptimePrecision from "../../Types/StatusPage/UptimePrecision";
|
|
42
43
|
let StatusPage = class StatusPage extends BaseModel {
|
|
@@ -66,6 +67,8 @@ let StatusPage = class StatusPage extends BaseModel {
|
|
|
66
67
|
this.customCSS = undefined;
|
|
67
68
|
this.customJavaScript = undefined;
|
|
68
69
|
this.isPublicStatusPage = undefined;
|
|
70
|
+
this.enableMasterPassword = undefined;
|
|
71
|
+
this.masterPassword = undefined;
|
|
69
72
|
this.showIncidentLabelsOnStatusPage = undefined;
|
|
70
73
|
this.showScheduledEventLabelsOnStatusPage = undefined;
|
|
71
74
|
// This column is Deprectaed.
|
|
@@ -899,6 +902,77 @@ __decorate([
|
|
|
899
902
|
}),
|
|
900
903
|
__metadata("design:type", Boolean)
|
|
901
904
|
], StatusPage.prototype, "isPublicStatusPage", void 0);
|
|
905
|
+
__decorate([
|
|
906
|
+
ColumnAccessControl({
|
|
907
|
+
create: [
|
|
908
|
+
Permission.ProjectOwner,
|
|
909
|
+
Permission.ProjectAdmin,
|
|
910
|
+
Permission.ProjectMember,
|
|
911
|
+
Permission.CreateProjectStatusPage,
|
|
912
|
+
],
|
|
913
|
+
read: [
|
|
914
|
+
Permission.ProjectOwner,
|
|
915
|
+
Permission.ProjectAdmin,
|
|
916
|
+
Permission.ProjectMember,
|
|
917
|
+
Permission.ReadProjectStatusPage,
|
|
918
|
+
],
|
|
919
|
+
update: [
|
|
920
|
+
Permission.ProjectOwner,
|
|
921
|
+
Permission.ProjectAdmin,
|
|
922
|
+
Permission.ProjectMember,
|
|
923
|
+
Permission.EditProjectStatusPage,
|
|
924
|
+
],
|
|
925
|
+
}),
|
|
926
|
+
TableColumn({
|
|
927
|
+
isDefaultValueColumn: true,
|
|
928
|
+
type: TableColumnType.Boolean,
|
|
929
|
+
title: "Enable Master Password",
|
|
930
|
+
description: "Require visitors to enter a master password before viewing a private status page.",
|
|
931
|
+
defaultValue: false,
|
|
932
|
+
}),
|
|
933
|
+
Column({
|
|
934
|
+
type: ColumnType.Boolean,
|
|
935
|
+
default: false,
|
|
936
|
+
}),
|
|
937
|
+
__metadata("design:type", Boolean)
|
|
938
|
+
], StatusPage.prototype, "enableMasterPassword", void 0);
|
|
939
|
+
__decorate([
|
|
940
|
+
ColumnAccessControl({
|
|
941
|
+
create: [
|
|
942
|
+
Permission.ProjectOwner,
|
|
943
|
+
Permission.ProjectAdmin,
|
|
944
|
+
Permission.ProjectMember,
|
|
945
|
+
Permission.CreateProjectStatusPage,
|
|
946
|
+
],
|
|
947
|
+
// This is a hashed column. So, reading the value is does not affect anything.
|
|
948
|
+
read: [
|
|
949
|
+
Permission.ProjectOwner,
|
|
950
|
+
Permission.ProjectAdmin,
|
|
951
|
+
Permission.ProjectMember,
|
|
952
|
+
Permission.ReadProjectStatusPage,
|
|
953
|
+
],
|
|
954
|
+
update: [
|
|
955
|
+
Permission.ProjectOwner,
|
|
956
|
+
Permission.ProjectAdmin,
|
|
957
|
+
Permission.ProjectMember,
|
|
958
|
+
Permission.EditProjectStatusPage,
|
|
959
|
+
],
|
|
960
|
+
}),
|
|
961
|
+
TableColumn({
|
|
962
|
+
title: "Master Password",
|
|
963
|
+
description: "Password required to unlock a private status page. This value is stored as a secure hash.",
|
|
964
|
+
hashed: true,
|
|
965
|
+
type: TableColumnType.HashedString,
|
|
966
|
+
placeholder: "Enter a new master password",
|
|
967
|
+
}),
|
|
968
|
+
Column({
|
|
969
|
+
type: ColumnType.HashedString,
|
|
970
|
+
length: ColumnLength.HashedString,
|
|
971
|
+
nullable: true,
|
|
972
|
+
transformer: HashedString.getDatabaseTransformer(),
|
|
973
|
+
}),
|
|
974
|
+
__metadata("design:type", HashedString)
|
|
975
|
+
], StatusPage.prototype, "masterPassword", void 0);
|
|
902
976
|
__decorate([
|
|
903
977
|
ColumnAccessControl({
|
|
904
978
|
create: [
|