@oneuptime/common 12.0.28 → 12.0.29
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/DashboardDomain.ts +43 -0
- package/Models/DatabaseModels/MonitorTemplate.ts +15 -3
- package/Models/DatabaseModels/StatusPageDomain.ts +43 -0
- package/Server/API/DashboardDomainAPI.ts +74 -0
- package/Server/API/MicrosoftTeamsAPI.ts +81 -180
- package/Server/API/StatusPageDomainAPI.ts +74 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1790200000000-AllowNullMonitorNameOnMonitorTemplate.ts +43 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1790300000000-AddCertificateReissueRequestedAtToDomains.ts +40 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Middleware/ProjectAuthorization.ts +6 -1
- package/Server/Services/DashboardDomainService.ts +131 -0
- package/Server/Services/LogPipelineProcessorService.ts +88 -0
- package/Server/Services/MonitorTemplateService.ts +70 -11
- package/Server/Services/StatusPageDomainService.ts +163 -3
- package/Server/Services/StatusPagePrivateUserSessionService.ts +197 -56
- package/Server/Types/Domain.ts +17 -0
- package/Server/Utils/APIKey/AccessPermission.ts +24 -0
- package/Server/Utils/Greenlock/Greenlock.ts +156 -58
- package/Server/Utils/LogPipelineProcessorValidation.ts +112 -0
- package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.ts +1094 -45
- package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.ts +153 -17
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.ts +23 -11
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.ts +0 -170
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Authorization.ts +158 -0
- package/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.ts +23 -11
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +35 -13
- package/Tests/App/Dashboard/MonitorTemplateColumnAndFacet.test.tsx +505 -0
- package/Tests/App/StatusPage/StatusPageLoginCodeBootstrap.test.ts +106 -0
- package/Tests/App/StatusPage/StatusPageLoginCodeUtil.test.ts +84 -0
- package/Tests/App/StatusPage/StatusPageRedirectSafety.test.ts +119 -0
- package/Tests/Models/MonitorTemplateMonitorNameColumn.test.ts +258 -0
- package/Tests/Server/API/CustomDomainReissueSslAPI.test.ts +469 -0
- package/Tests/Server/API/MicrosoftTeamsBotMessagesEndpoint.test.ts +729 -0
- package/Tests/Server/API/MicrosoftTeamsBotTestEndpoint.test.ts +26 -0
- package/Tests/Server/API/MicrosoftTeamsLegacyWebhookRemoval.test.ts +402 -0
- package/Tests/Server/Middleware/ProjectAuthorizationApiKeyMiddleware.test.ts +4 -0
- package/Tests/Server/Services/AddCertificateReissueRequestedAtToDomainsMigration.test.ts +287 -0
- package/Tests/Server/Services/CustomDomainCertificateReissue.test.ts +477 -0
- package/Tests/Server/Services/LogPipelineProcessorSaveValidation.test.ts +249 -0
- package/Tests/Server/Services/MonitorTemplateServiceBulkSync.test.ts +549 -0
- package/Tests/Server/Services/MonitorTemplateServiceNetworkDeviceSync.test.ts +16 -3
- package/Tests/Server/Services/NetworkDeviceAutoImportRuleEngineService.test.ts +57 -0
- package/Tests/Server/Services/SecurityEventLastErrorColumnWidth.test.ts +151 -15
- package/Tests/Server/Services/StatusPagePrivateUserSessionService.test.ts +587 -0
- package/Tests/Server/Types/Database/Permissions/ApiKeyFileAccess.test.ts +286 -0
- package/Tests/Server/Types/Database/Permissions/MonitorTemplateColumnAccess.test.ts +328 -0
- package/Tests/Server/Types/Database/QueryHelperFindWithSameTextAndIsNull.test.ts +83 -0
- package/Tests/Server/Utils/AI/SRE/ConfidenceSignal.test.ts +377 -0
- package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +16 -1
- package/Tests/Server/Utils/Greenlock/CertificateRenewalSchedule.test.ts +250 -0
- package/Tests/Server/Utils/LogPipelineProcessorValidation.test.ts +198 -0
- package/Tests/Server/Utils/SecurityEvent/ConnectorErrorMessage.test.ts +48 -9
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsAuth.test.ts +726 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsClient.test.ts +74 -11
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsErrorHandling.test.ts +1107 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPoller.test.ts +20 -3
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerFailureTaxonomy.test.ts +51 -11
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerHardening.test.ts +1246 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsRequestContract.test.ts +961 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsResponseParsing.test.ts +653 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsActionAuthorization.test.ts +880 -0
- package/Tests/Types/API/HTTPResponse.test.ts +175 -0
- package/Tests/Types/API/Route.test.ts +36 -4
- package/Tests/Types/Database/DatabaseProperty.test.ts +171 -0
- package/Tests/Types/DateLocalShortDateTimeString.test.ts +518 -0
- package/Tests/Types/DateLocalTimeString.test.ts +259 -0
- package/Tests/Types/DateUserPrefers12HourFormat.test.ts +316 -0
- package/Tests/UI/Components/LogsHistogram.test.tsx +41 -2
- package/Tests/UI/Components/LogsViewerEmptyStateTimeRange.test.tsx +212 -0
- package/Tests/UI/Components/TelemetryChartClockFormat.test.tsx +359 -0
- package/Tests/UI/Components/TimeRangePickerClockFormat.test.tsx +422 -0
- package/Tests/UI/Components/TimeRangePickerDropdown.test.tsx +146 -8
- package/Tests/UI/MicrosoftTeams/MicrosoftTeamsMessagingEndpointGuidance.test.tsx +520 -0
- package/Tests/UI/Utils/Navigation.test.ts +82 -0
- package/Tests/Utils/Array.test.ts +100 -0
- package/Tests/Utils/CertificateReissue.test.ts +225 -0
- package/Tests/Utils/Grok/Grok.test.ts +492 -0
- package/Tests/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.test.ts +181 -13
- package/Types/API/Route.ts +20 -5
- package/Types/Date.ts +143 -13
- package/Types/Rum/SessionReplay.ts +62 -1
- package/UI/Components/Date/TimeRangePickerDropdown.tsx +9 -5
- package/UI/Components/LogsViewer/LogsViewer.tsx +12 -7
- package/UI/Components/LogsViewer/components/HistogramTooltip.tsx +18 -17
- package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +21 -28
- package/UI/Components/LogsViewer/components/LogsHistogram.tsx +2 -4
- package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +2 -4
- package/UI/Components/TelemetryViewer/components/TelemetryHistogramTooltip.tsx +17 -16
- package/UI/Utils/Navigation.ts +42 -0
- package/Utils/Array.ts +37 -0
- package/Utils/CertificateReissue.ts +128 -0
- package/Utils/Grok/Grok.ts +461 -0
- package/Utils/Grok/GrokPatterns.ts +118 -0
- package/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.ts +58 -6
- package/build/dist/Models/DatabaseModels/DashboardDomain.js +44 -0
- package/build/dist/Models/DatabaseModels/DashboardDomain.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MonitorTemplate.js +17 -4
- package/build/dist/Models/DatabaseModels/MonitorTemplate.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageDomain.js +44 -0
- package/build/dist/Models/DatabaseModels/StatusPageDomain.js.map +1 -1
- package/build/dist/Server/API/DashboardDomainAPI.js +43 -1
- package/build/dist/Server/API/DashboardDomainAPI.js.map +1 -1
- package/build/dist/Server/API/MicrosoftTeamsAPI.js +74 -118
- package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageDomainAPI.js +43 -1
- package/build/dist/Server/API/StatusPageDomainAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790200000000-AllowNullMonitorNameOnMonitorTemplate.js +34 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790200000000-AllowNullMonitorNameOnMonitorTemplate.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790300000000-AddCertificateReissueRequestedAtToDomains.js +28 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790300000000-AddCertificateReissueRequestedAtToDomains.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/ProjectAuthorization.js +6 -1
- package/build/dist/Server/Middleware/ProjectAuthorization.js.map +1 -1
- package/build/dist/Server/Services/DashboardDomainService.js +104 -0
- package/build/dist/Server/Services/DashboardDomainService.js.map +1 -1
- package/build/dist/Server/Services/LogPipelineProcessorService.js +65 -0
- package/build/dist/Server/Services/LogPipelineProcessorService.js.map +1 -1
- package/build/dist/Server/Services/MonitorTemplateService.js +57 -11
- package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageDomainService.js +132 -3
- package/build/dist/Server/Services/StatusPageDomainService.js.map +1 -1
- package/build/dist/Server/Services/StatusPagePrivateUserSessionService.js +112 -32
- package/build/dist/Server/Services/StatusPagePrivateUserSessionService.js.map +1 -1
- package/build/dist/Server/Types/Domain.js +16 -0
- package/build/dist/Server/Types/Domain.js.map +1 -1
- package/build/dist/Server/Utils/APIKey/AccessPermission.js +24 -0
- package/build/dist/Server/Utils/APIKey/AccessPermission.js.map +1 -1
- package/build/dist/Server/Utils/Greenlock/Greenlock.js +110 -35
- package/build/dist/Server/Utils/Greenlock/Greenlock.js.map +1 -1
- package/build/dist/Server/Utils/LogPipelineProcessorValidation.js +77 -0
- package/build/dist/Server/Utils/LogPipelineProcessorValidation.js.map +1 -0
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js +777 -36
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js +108 -17
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js +17 -6
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Alert.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js +0 -159
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Auth.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Authorization.js +128 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Authorization.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js +17 -6
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/Actions/Incident.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +25 -13
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/API/Route.js +16 -5
- package/build/dist/Types/API/Route.js.map +1 -1
- package/build/dist/Types/Date.js +115 -13
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/Types/Rum/SessionReplay.js +38 -0
- package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
- package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js +9 -5
- package/build/dist/UI/Components/Date/TimeRangePickerDropdown.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +12 -7
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/HistogramTooltip.js +15 -14
- package/build/dist/UI/Components/LogsViewer/components/HistogramTooltip.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +17 -22
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +2 -4
- package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +2 -4
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogramTooltip.js +15 -14
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogramTooltip.js.map +1 -1
- package/build/dist/UI/Utils/Navigation.js +30 -0
- package/build/dist/UI/Utils/Navigation.js.map +1 -1
- package/build/dist/Utils/Array.js +25 -0
- package/build/dist/Utils/Array.js.map +1 -1
- package/build/dist/Utils/CertificateReissue.js +97 -0
- package/build/dist/Utils/CertificateReissue.js.map +1 -0
- package/build/dist/Utils/Grok/Grok.js +324 -0
- package/build/dist/Utils/Grok/Grok.js.map +1 -0
- package/build/dist/Utils/Grok/GrokPatterns.js +112 -0
- package/build/dist/Utils/Grok/GrokPatterns.js.map +1 -0
- package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js +63 -3
- package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js.map +1 -1
- package/package.json +1 -1
- package/UI/Utils/JsonWebToken.ts +0 -14
- package/build/dist/UI/Utils/JsonWebToken.js +0 -10
- package/build/dist/UI/Utils/JsonWebToken.js.map +0 -1
|
@@ -9,6 +9,7 @@ import Text from "../../Types/Text";
|
|
|
9
9
|
import logger from "../Utils/Logger";
|
|
10
10
|
import Exception from "../../Types/Exception/Exception";
|
|
11
11
|
import BadDataException from "../../Types/Exception/BadDataException";
|
|
12
|
+
import { JsonContains, MoreThan, UpdateResult } from "typeorm";
|
|
12
13
|
|
|
13
14
|
export interface SessionMetadata {
|
|
14
15
|
session: Model;
|
|
@@ -52,8 +53,24 @@ export interface RevokeSessionOptions {
|
|
|
52
53
|
reason?: string | undefined;
|
|
53
54
|
}
|
|
54
55
|
|
|
56
|
+
export interface ExchangeLoginCodeOptions {
|
|
57
|
+
statusPageId: ObjectID;
|
|
58
|
+
ipAddress?: string | undefined;
|
|
59
|
+
userAgent?: string | undefined;
|
|
60
|
+
deviceName?: string | undefined;
|
|
61
|
+
deviceType?: string | undefined;
|
|
62
|
+
deviceOS?: string | undefined;
|
|
63
|
+
deviceBrowser?: string | undefined;
|
|
64
|
+
additionalInfo?: JSONObject | undefined;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export const STATUS_PAGE_LOGIN_CODE_TTL_MINUTES: number = 5;
|
|
68
|
+
|
|
55
69
|
export class Service extends DatabaseService<Model> {
|
|
56
70
|
private static readonly DEFAULT_REFRESH_TOKEN_TTL_DAYS: number = 30;
|
|
71
|
+
private static readonly LOGIN_CODE_PURPOSE_KEY: string =
|
|
72
|
+
"oneuptimeStatusPageSessionPurpose";
|
|
73
|
+
private static readonly LOGIN_CODE_PURPOSE_VALUE: string = "login-code";
|
|
57
74
|
private static readonly SHORT_TEXT_LIMIT: number = 100;
|
|
58
75
|
|
|
59
76
|
public constructor() {
|
|
@@ -91,6 +108,31 @@ export class Service extends DatabaseService<Model> {
|
|
|
91
108
|
}
|
|
92
109
|
}
|
|
93
110
|
|
|
111
|
+
public async createLoginCodeSession(
|
|
112
|
+
options: Omit<
|
|
113
|
+
CreateSessionOptions,
|
|
114
|
+
"refreshToken" | "refreshTokenExpiresAt"
|
|
115
|
+
>,
|
|
116
|
+
): Promise<SessionMetadata> {
|
|
117
|
+
return await this.createSession({
|
|
118
|
+
...options,
|
|
119
|
+
additionalInfo: {
|
|
120
|
+
...(options.additionalInfo || {}),
|
|
121
|
+
[Service.LOGIN_CODE_PURPOSE_KEY]: Service.LOGIN_CODE_PURPOSE_VALUE,
|
|
122
|
+
} as JSONObject,
|
|
123
|
+
refreshTokenExpiresAt: OneUptimeDate.getSomeMinutesAfter(
|
|
124
|
+
STATUS_PAGE_LOGIN_CODE_TTL_MINUTES,
|
|
125
|
+
),
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
public isLoginCodeSession(session: Model): boolean {
|
|
130
|
+
return (
|
|
131
|
+
session.additionalInfo?.[Service.LOGIN_CODE_PURPOSE_KEY] ===
|
|
132
|
+
Service.LOGIN_CODE_PURPOSE_VALUE
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
94
136
|
public async findActiveSessionByRefreshToken(
|
|
95
137
|
refreshToken: string,
|
|
96
138
|
): Promise<Model | null> {
|
|
@@ -146,79 +188,111 @@ export class Service extends DatabaseService<Model> {
|
|
|
146
188
|
const refreshTokenExpiresAt: Date =
|
|
147
189
|
options.refreshTokenExpiresAt || Service.getRefreshTokenExpiry();
|
|
148
190
|
|
|
149
|
-
const updatePayload: Partial<Model> = {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
};
|
|
191
|
+
const updatePayload: Partial<Model> = this.buildRenewalUpdatePayload({
|
|
192
|
+
options,
|
|
193
|
+
refreshToken,
|
|
194
|
+
refreshTokenExpiresAt,
|
|
195
|
+
});
|
|
155
196
|
|
|
156
|
-
const
|
|
157
|
-
options.
|
|
158
|
-
|
|
159
|
-
|
|
197
|
+
const updatedSession: Model | null = await this.updateOneByIdAndFetch({
|
|
198
|
+
id: options.session.id!,
|
|
199
|
+
data: updatePayload as any,
|
|
200
|
+
props: {
|
|
201
|
+
isRoot: true,
|
|
202
|
+
},
|
|
203
|
+
});
|
|
160
204
|
|
|
161
|
-
if (
|
|
162
|
-
|
|
205
|
+
if (!updatedSession) {
|
|
206
|
+
throw new BadDataException("Unable to renew status page user session");
|
|
163
207
|
}
|
|
164
208
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
209
|
+
return {
|
|
210
|
+
session: updatedSession,
|
|
211
|
+
refreshToken,
|
|
212
|
+
refreshTokenExpiresAt,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
168
215
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
216
|
+
/**
|
|
217
|
+
* Atomically consume the short-lived login code and replace it with the
|
|
218
|
+
* long-lived refresh credential that is delivered only as an HttpOnly
|
|
219
|
+
* cookie. The refresh-token column is unique and included in the UPDATE
|
|
220
|
+
* predicate, so concurrent redemption attempts cannot both succeed.
|
|
221
|
+
*/
|
|
222
|
+
public async exchangeLoginCode(
|
|
223
|
+
loginCode: string,
|
|
224
|
+
options: ExchangeLoginCodeOptions,
|
|
225
|
+
): Promise<SessionMetadata | null> {
|
|
226
|
+
if (!ObjectID.isValidUUID(loginCode)) {
|
|
227
|
+
return null;
|
|
175
228
|
}
|
|
176
229
|
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
230
|
+
const session: Model | null =
|
|
231
|
+
await this.findActiveSessionByRefreshToken(loginCode);
|
|
232
|
+
|
|
233
|
+
if (
|
|
234
|
+
!session?.id ||
|
|
235
|
+
!session.statusPageId ||
|
|
236
|
+
!this.isLoginCodeSession(session) ||
|
|
237
|
+
session.statusPageId.toString() !== options.statusPageId.toString()
|
|
238
|
+
) {
|
|
239
|
+
return null;
|
|
183
240
|
}
|
|
184
241
|
|
|
185
|
-
const
|
|
186
|
-
|
|
187
|
-
|
|
242
|
+
const refreshToken: string = Service.generateRefreshToken();
|
|
243
|
+
const refreshTokenExpiresAt: Date = Service.getRefreshTokenExpiry();
|
|
244
|
+
const renewalOptions: RenewSessionOptions = {
|
|
245
|
+
session,
|
|
246
|
+
ipAddress: options.ipAddress,
|
|
247
|
+
userAgent: options.userAgent,
|
|
248
|
+
deviceName: options.deviceName,
|
|
249
|
+
deviceType: options.deviceType,
|
|
250
|
+
deviceOS: options.deviceOS,
|
|
251
|
+
deviceBrowser: options.deviceBrowser,
|
|
252
|
+
additionalInfo: options.additionalInfo,
|
|
253
|
+
};
|
|
254
|
+
const updatePayload: Partial<Model> = this.buildRenewalUpdatePayload({
|
|
255
|
+
options: renewalOptions,
|
|
256
|
+
refreshToken,
|
|
257
|
+
refreshTokenExpiresAt,
|
|
258
|
+
});
|
|
259
|
+
const refreshTokenHash: string = await HashedString.hashValue(
|
|
260
|
+
refreshToken,
|
|
261
|
+
EncryptionSecret,
|
|
262
|
+
);
|
|
263
|
+
updatePayload.refreshToken = new HashedString(refreshTokenHash, true);
|
|
264
|
+
const additionalInfo: JSONObject = {
|
|
265
|
+
...(updatePayload.additionalInfo || {}),
|
|
266
|
+
} as JSONObject;
|
|
267
|
+
delete additionalInfo[Service.LOGIN_CODE_PURPOSE_KEY];
|
|
268
|
+
updatePayload.additionalInfo = additionalInfo;
|
|
269
|
+
const loginCodeHash: string = await HashedString.hashValue(
|
|
270
|
+
loginCode,
|
|
271
|
+
EncryptionSecret,
|
|
188
272
|
);
|
|
189
|
-
if (deviceOS) {
|
|
190
|
-
updatePayload.deviceOS = deviceOS;
|
|
191
|
-
}
|
|
192
273
|
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
|
|
274
|
+
const updateResult: UpdateResult = await this.getRepository().update(
|
|
275
|
+
{
|
|
276
|
+
_id: session._id!,
|
|
277
|
+
statusPageId: options.statusPageId,
|
|
278
|
+
refreshToken: new HashedString(loginCodeHash, true),
|
|
279
|
+
refreshTokenExpiresAt: MoreThan(OneUptimeDate.getCurrentDate()),
|
|
280
|
+
isRevoked: false,
|
|
281
|
+
additionalInfo: JsonContains({
|
|
282
|
+
[Service.LOGIN_CODE_PURPOSE_KEY]: Service.LOGIN_CODE_PURPOSE_VALUE,
|
|
283
|
+
}),
|
|
284
|
+
} as any,
|
|
285
|
+
updatePayload as any,
|
|
196
286
|
);
|
|
197
|
-
if (deviceBrowser) {
|
|
198
|
-
updatePayload.deviceBrowser = deviceBrowser;
|
|
199
|
-
}
|
|
200
287
|
|
|
201
|
-
if (
|
|
202
|
-
|
|
203
|
-
...(options.session.additionalInfo || {}),
|
|
204
|
-
...(options.additionalInfo || {}),
|
|
205
|
-
} as JSONObject;
|
|
288
|
+
if (updateResult.affected !== 1) {
|
|
289
|
+
return null;
|
|
206
290
|
}
|
|
207
291
|
|
|
208
|
-
|
|
209
|
-
id: options.session.id!,
|
|
210
|
-
data: updatePayload as any,
|
|
211
|
-
props: {
|
|
212
|
-
isRoot: true,
|
|
213
|
-
},
|
|
214
|
-
});
|
|
215
|
-
|
|
216
|
-
if (!updatedSession) {
|
|
217
|
-
throw new BadDataException("Unable to renew status page user session");
|
|
218
|
-
}
|
|
292
|
+
Object.assign(session, updatePayload);
|
|
219
293
|
|
|
220
294
|
return {
|
|
221
|
-
session
|
|
295
|
+
session,
|
|
222
296
|
refreshToken,
|
|
223
297
|
refreshTokenExpiresAt,
|
|
224
298
|
};
|
|
@@ -354,6 +428,73 @@ export class Service extends DatabaseService<Model> {
|
|
|
354
428
|
return session;
|
|
355
429
|
}
|
|
356
430
|
|
|
431
|
+
private buildRenewalUpdatePayload(data: {
|
|
432
|
+
options: RenewSessionOptions;
|
|
433
|
+
refreshToken: string;
|
|
434
|
+
refreshTokenExpiresAt: Date;
|
|
435
|
+
}): Partial<Model> {
|
|
436
|
+
const updatePayload: Partial<Model> = {
|
|
437
|
+
refreshToken: HashedString.fromString(data.refreshToken),
|
|
438
|
+
refreshTokenExpiresAt: data.refreshTokenExpiresAt,
|
|
439
|
+
lastActiveAt: OneUptimeDate.getCurrentDate(),
|
|
440
|
+
isRevoked: false,
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
const ipAddress: string | undefined = Text.truncate(
|
|
444
|
+
data.options.ipAddress,
|
|
445
|
+
Service.SHORT_TEXT_LIMIT,
|
|
446
|
+
);
|
|
447
|
+
|
|
448
|
+
if (ipAddress) {
|
|
449
|
+
updatePayload.ipAddress = ipAddress;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
if (data.options.userAgent) {
|
|
453
|
+
updatePayload.userAgent = data.options.userAgent;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const deviceName: string | undefined = Text.truncate(
|
|
457
|
+
data.options.deviceName,
|
|
458
|
+
Service.SHORT_TEXT_LIMIT,
|
|
459
|
+
);
|
|
460
|
+
if (deviceName) {
|
|
461
|
+
updatePayload.deviceName = deviceName;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
const deviceType: string | undefined = Text.truncate(
|
|
465
|
+
data.options.deviceType,
|
|
466
|
+
Service.SHORT_TEXT_LIMIT,
|
|
467
|
+
);
|
|
468
|
+
if (deviceType) {
|
|
469
|
+
updatePayload.deviceType = deviceType;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
const deviceOS: string | undefined = Text.truncate(
|
|
473
|
+
data.options.deviceOS,
|
|
474
|
+
Service.SHORT_TEXT_LIMIT,
|
|
475
|
+
);
|
|
476
|
+
if (deviceOS) {
|
|
477
|
+
updatePayload.deviceOS = deviceOS;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const deviceBrowser: string | undefined = Text.truncate(
|
|
481
|
+
data.options.deviceBrowser,
|
|
482
|
+
Service.SHORT_TEXT_LIMIT,
|
|
483
|
+
);
|
|
484
|
+
if (deviceBrowser) {
|
|
485
|
+
updatePayload.deviceBrowser = deviceBrowser;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
if (data.options.additionalInfo || data.options.session.additionalInfo) {
|
|
489
|
+
updatePayload.additionalInfo = {
|
|
490
|
+
...(data.options.session.additionalInfo || {}),
|
|
491
|
+
...(data.options.additionalInfo || {}),
|
|
492
|
+
} as JSONObject;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
return updatePayload;
|
|
496
|
+
}
|
|
497
|
+
|
|
357
498
|
private static generateRefreshToken(): string {
|
|
358
499
|
return ObjectID.generate().toString();
|
|
359
500
|
}
|
package/Server/Types/Domain.ts
CHANGED
|
@@ -11,6 +11,13 @@ import URL from "../../Types/API/URL";
|
|
|
11
11
|
import API from "../../Utils/API";
|
|
12
12
|
import DataSourceEgressGuard from "../Utils/DataSource/EgressGuard";
|
|
13
13
|
|
|
14
|
+
/*
|
|
15
|
+
* A status page that is up answers the verification path well inside this.
|
|
16
|
+
* It is a ceiling on how long one unreachable domain may hold up the rest of a
|
|
17
|
+
* certificate run, not a latency target.
|
|
18
|
+
*/
|
|
19
|
+
export const DOMAIN_VERIFICATION_TIMEOUT_IN_MS: number = 10000;
|
|
20
|
+
|
|
14
21
|
export default class Domain extends DomainCommon {
|
|
15
22
|
/*
|
|
16
23
|
* GET a domain-verification URL built from a tenant-supplied domain.
|
|
@@ -26,6 +33,15 @@ export default class Domain extends DomainCommon {
|
|
|
26
33
|
*
|
|
27
34
|
* Private ranges stay reachable on self-hosted installs, where a status page
|
|
28
35
|
* domain legitimately resolves inside the corporate network.
|
|
36
|
+
*
|
|
37
|
+
* The call is bounded. A customer domain that has been pointed at an address
|
|
38
|
+
* which silently drops packets - a decommissioned load balancer, a firewall
|
|
39
|
+
* that blackholes instead of refusing - never answers and never resets, so an
|
|
40
|
+
* unbounded request sits until the kernel gives up on the TCP connect, which
|
|
41
|
+
* is a little over two minutes on Linux. The certificate crons walk every
|
|
42
|
+
* status page domain in turn, so each such domain would burn that timeout out
|
|
43
|
+
* of the run's budget and push the domains behind it into the next run - the
|
|
44
|
+
* renewals that matter get starved by the domains that will never answer.
|
|
29
45
|
*/
|
|
30
46
|
@CaptureSpan()
|
|
31
47
|
public static async getForDomainVerification(data: {
|
|
@@ -41,6 +57,7 @@ export default class Domain extends DomainCommon {
|
|
|
41
57
|
url: URL.fromString(data.url),
|
|
42
58
|
options: {
|
|
43
59
|
doNotFollowRedirects: true,
|
|
60
|
+
timeout: DOMAIN_VERIFICATION_TIMEOUT_IN_MS,
|
|
44
61
|
httpAgent,
|
|
45
62
|
httpsAgent,
|
|
46
63
|
},
|
|
@@ -10,6 +10,28 @@ import ApiKeyPermissionService, {
|
|
|
10
10
|
import CaptureSpan from "../Telemetry/CaptureSpan";
|
|
11
11
|
import UserPermissionUtil from "../UserPermission/UserPermission";
|
|
12
12
|
|
|
13
|
+
/*
|
|
14
|
+
* Permission.AuthenticatedRequest is a marker, not a capability. It says "some
|
|
15
|
+
* authenticated principal made this call" - its own definition names API keys
|
|
16
|
+
* first - and unlike every other entry in these lists it has no PermissionProps,
|
|
17
|
+
* so it never appears in the admin picker and nobody can grant or revoke it per
|
|
18
|
+
* key. File and FileModel are the only models that name it, which is what bounds
|
|
19
|
+
* what including it here can widen.
|
|
20
|
+
*
|
|
21
|
+
* It is in the list because of TenantPermission.isAccessGrantedOnlyByCurrentUser.
|
|
22
|
+
* That predicate asks whether the ONLY thing letting a caller through a model's
|
|
23
|
+
* gate is the auto-granted CurrentUser - i.e. whether they are here as nothing
|
|
24
|
+
* more than some logged-in user - and when it says yes the caller is held to
|
|
25
|
+
* rows they own, which an API key can never satisfy because it has no userId.
|
|
26
|
+
* Without AuthenticatedRequest an API key intersects File's create list down to
|
|
27
|
+
* CurrentUser alone and answers yes to that question, which is simply the wrong
|
|
28
|
+
* description of a key: it is not a user acting on their own rows.
|
|
29
|
+
*
|
|
30
|
+
* On File as it stands today the wrong answer is harmless - File declares no
|
|
31
|
+
* user column, so the ownership branch returns before it can bite. The grant is
|
|
32
|
+
* here so that stays a fact about File's schema rather than the only thing
|
|
33
|
+
* holding API-key file access up.
|
|
34
|
+
*/
|
|
13
35
|
export default class APIKeyAccessPermission {
|
|
14
36
|
@CaptureSpan()
|
|
15
37
|
public static async getDefaultApiGlobalPermission(
|
|
@@ -21,6 +43,7 @@ export default class APIKeyAccessPermission {
|
|
|
21
43
|
Permission.Public,
|
|
22
44
|
Permission.User,
|
|
23
45
|
Permission.CurrentUser,
|
|
46
|
+
Permission.AuthenticatedRequest,
|
|
24
47
|
],
|
|
25
48
|
_type: "UserGlobalAccessPermission",
|
|
26
49
|
};
|
|
@@ -36,6 +59,7 @@ export default class APIKeyAccessPermission {
|
|
|
36
59
|
Permission.Public,
|
|
37
60
|
Permission.User,
|
|
38
61
|
Permission.CurrentUser,
|
|
62
|
+
Permission.AuthenticatedRequest,
|
|
39
63
|
Permission.ProjectOwner,
|
|
40
64
|
],
|
|
41
65
|
_type: "UserGlobalAccessPermission",
|
|
@@ -16,11 +16,64 @@ import ServerException from "../../../Types/Exception/ServerException";
|
|
|
16
16
|
import Text from "../../../Types/Text";
|
|
17
17
|
import AcmeCertificate from "../../../Models/DatabaseModels/AcmeCertificate";
|
|
18
18
|
import AcmeChallenge from "../../../Models/DatabaseModels/AcmeChallenge";
|
|
19
|
+
import ArrayUtil from "../../../Utils/Array";
|
|
19
20
|
import acme from "acme-client";
|
|
20
21
|
import { Challenge } from "acme-client/types/rfc8555";
|
|
21
22
|
import CaptureSpan from "../Telemetry/CaptureSpan";
|
|
22
23
|
|
|
23
24
|
export default class GreenlockUtil {
|
|
25
|
+
/*
|
|
26
|
+
* How early a certificate is renewed, as a range rather than a single value.
|
|
27
|
+
*
|
|
28
|
+
* Let's Encrypt issues for 90 days, so every domain ordered on the same day
|
|
29
|
+
* expires on the same day. Renewing them all at one fixed lead time keeps
|
|
30
|
+
* that batch together for every cycle that follows, and a single bad day -
|
|
31
|
+
* an upstream outage, a rate limit, a run that does not finish - then expires
|
|
32
|
+
* the whole batch at once instead of one domain.
|
|
33
|
+
*
|
|
34
|
+
* Spreading the lead time across a range pulls the batch apart: domains that
|
|
35
|
+
* expire on the same day come due days apart, and having been renewed days
|
|
36
|
+
* apart they expire days apart next cycle too. The offset is derived from the
|
|
37
|
+
* domain name so it is stable - a domain must not drift in and out of
|
|
38
|
+
* eligibility between two runs, which would leave it renewed by neither.
|
|
39
|
+
*/
|
|
40
|
+
public static readonly RENEW_LEAD_TIME_MAX_IN_DAYS: number = 40;
|
|
41
|
+
public static readonly RENEW_LEAD_TIME_MIN_IN_DAYS: number = 25;
|
|
42
|
+
|
|
43
|
+
/*
|
|
44
|
+
* A run renews at most this many domains, at most this many at a time.
|
|
45
|
+
*
|
|
46
|
+
* Both bound how much of a backlog is dispatched at once. Let's Encrypt
|
|
47
|
+
* allows 300 new orders per account per three hours, and the reactive
|
|
48
|
+
* provisioning sweep spends from the same allowance, so a run that tried to
|
|
49
|
+
* clear a large backlog in one pass would spend the account's budget and get
|
|
50
|
+
* the renewals behind it refused. At the schedule this job runs on the cap
|
|
51
|
+
* still clears far more per day than one sequential daily pass ever did,
|
|
52
|
+
* while leaving room under the limit.
|
|
53
|
+
*/
|
|
54
|
+
public static readonly RENEW_MAX_PER_RUN: number = 10;
|
|
55
|
+
public static readonly RENEW_CONCURRENCY: number = 5;
|
|
56
|
+
|
|
57
|
+
/*
|
|
58
|
+
* Stable per-domain lead time, somewhere in the range above. Same domain,
|
|
59
|
+
* same answer, on every run and every replica.
|
|
60
|
+
*/
|
|
61
|
+
public static getRenewalLeadTimeInDays(domain: string): number {
|
|
62
|
+
const spanInDays: number =
|
|
63
|
+
GreenlockUtil.RENEW_LEAD_TIME_MAX_IN_DAYS -
|
|
64
|
+
GreenlockUtil.RENEW_LEAD_TIME_MIN_IN_DAYS;
|
|
65
|
+
|
|
66
|
+
let hash: number = 0;
|
|
67
|
+
|
|
68
|
+
for (let i: number = 0; i < domain.length; i++) {
|
|
69
|
+
hash = (hash * 31 + domain.charCodeAt(i)) % 1000003;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
GreenlockUtil.RENEW_LEAD_TIME_MIN_IN_DAYS + (hash % (spanInDays + 1))
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
24
77
|
@CaptureSpan()
|
|
25
78
|
public static async renewAllCertsWhichAreExpiringSoon(data: {
|
|
26
79
|
validateCname: (domain: string) => Promise<boolean>;
|
|
@@ -29,15 +82,17 @@ export default class GreenlockUtil {
|
|
|
29
82
|
try {
|
|
30
83
|
logger.debug("Renewing all certificates");
|
|
31
84
|
|
|
32
|
-
|
|
33
|
-
|
|
85
|
+
/*
|
|
86
|
+
* Read the widest window any domain's lead time can make it due in, then
|
|
87
|
+
* keep only the domains whose own lead time has actually been reached.
|
|
88
|
+
*/
|
|
34
89
|
const certificates: AcmeCertificate[] =
|
|
35
90
|
await AcmeCertificateService.findBy({
|
|
36
91
|
query: {
|
|
37
92
|
expiresAt: QueryHelper.lessThanEqualTo(
|
|
38
93
|
OneUptimeDate.addRemoveDays(
|
|
39
94
|
OneUptimeDate.getCurrentDate(),
|
|
40
|
-
|
|
95
|
+
GreenlockUtil.RENEW_LEAD_TIME_MAX_IN_DAYS,
|
|
41
96
|
),
|
|
42
97
|
),
|
|
43
98
|
},
|
|
@@ -45,6 +100,7 @@ export default class GreenlockUtil {
|
|
|
45
100
|
skip: 0,
|
|
46
101
|
select: {
|
|
47
102
|
domain: true,
|
|
103
|
+
expiresAt: true,
|
|
48
104
|
},
|
|
49
105
|
sort: {
|
|
50
106
|
expiresAt: SortOrder.Ascending,
|
|
@@ -54,76 +110,118 @@ export default class GreenlockUtil {
|
|
|
54
110
|
},
|
|
55
111
|
});
|
|
56
112
|
|
|
113
|
+
const now: Date = OneUptimeDate.getCurrentDate();
|
|
114
|
+
|
|
115
|
+
const dueCertificates: AcmeCertificate[] = certificates.filter(
|
|
116
|
+
(certificate: AcmeCertificate) => {
|
|
117
|
+
if (!certificate.domain || !certificate.expiresAt) {
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const renewAt: Date = OneUptimeDate.addRemoveDays(
|
|
122
|
+
certificate.expiresAt,
|
|
123
|
+
-GreenlockUtil.getRenewalLeadTimeInDays(certificate.domain),
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
return !OneUptimeDate.isAfter(renewAt, now);
|
|
127
|
+
},
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
/*
|
|
131
|
+
* Still sorted by expiry, so a run that cannot take the whole backlog
|
|
132
|
+
* spends itself on the domains closest to expiring and leaves the rest -
|
|
133
|
+
* which by construction still have weeks of lead time - to the next run.
|
|
134
|
+
*/
|
|
135
|
+
const batch: AcmeCertificate[] = dueCertificates.slice(
|
|
136
|
+
0,
|
|
137
|
+
GreenlockUtil.RENEW_MAX_PER_RUN,
|
|
138
|
+
);
|
|
139
|
+
|
|
57
140
|
logger.debug(
|
|
58
|
-
`Found ${
|
|
59
|
-
{
|
|
141
|
+
`Found ${dueCertificates.length} certificates due for renewal, renewing ${batch.length} in this run`,
|
|
142
|
+
{
|
|
143
|
+
dueCount: dueCertificates.length,
|
|
144
|
+
batchCount: batch.length,
|
|
145
|
+
},
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
await ArrayUtil.forEachWithConcurrency(
|
|
149
|
+
batch,
|
|
150
|
+
GreenlockUtil.RENEW_CONCURRENCY,
|
|
151
|
+
async (certificate: AcmeCertificate): Promise<void> => {
|
|
152
|
+
await GreenlockUtil.renewCertForDomain({
|
|
153
|
+
domain: certificate.domain as string,
|
|
154
|
+
validateCname: data.validateCname,
|
|
155
|
+
notifyDomainRemoved: data.notifyDomainRemoved,
|
|
156
|
+
});
|
|
157
|
+
},
|
|
60
158
|
);
|
|
159
|
+
} catch (e) {
|
|
160
|
+
logger.error("Error renewing all certificates");
|
|
161
|
+
logger.error(e);
|
|
61
162
|
|
|
62
|
-
|
|
163
|
+
throw e;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
63
166
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
167
|
+
/*
|
|
168
|
+
* Renew one domain. Never throws: one domain that cannot be renewed - a CNAME
|
|
169
|
+
* that no longer points here, a challenge the CA would not accept - must not
|
|
170
|
+
* take down the rest of the run with it.
|
|
171
|
+
*/
|
|
172
|
+
private static async renewCertForDomain(data: {
|
|
173
|
+
domain: string;
|
|
174
|
+
validateCname: (domain: string) => Promise<boolean>;
|
|
175
|
+
notifyDomainRemoved: (domain: string) => Promise<void>;
|
|
176
|
+
}): Promise<void> {
|
|
177
|
+
const { domain } = data;
|
|
178
|
+
|
|
179
|
+
const certLogAttributes: LogAttributes = {
|
|
180
|
+
domain: domain,
|
|
181
|
+
};
|
|
68
182
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
183
|
+
logger.debug(
|
|
184
|
+
`Renewing certificate for domain: ${domain}`,
|
|
185
|
+
certLogAttributes,
|
|
186
|
+
);
|
|
72
187
|
|
|
188
|
+
try {
|
|
189
|
+
//validate cname
|
|
190
|
+
const isValidCname: boolean = await data.validateCname(domain);
|
|
191
|
+
|
|
192
|
+
if (!isValidCname) {
|
|
73
193
|
logger.debug(
|
|
74
|
-
`
|
|
194
|
+
`CNAME is not valid for domain: ${domain}`,
|
|
75
195
|
certLogAttributes,
|
|
76
196
|
);
|
|
77
197
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
certificate.domain,
|
|
82
|
-
);
|
|
83
|
-
|
|
84
|
-
if (!isValidCname) {
|
|
85
|
-
logger.debug(
|
|
86
|
-
`CNAME is not valid for domain: ${certificate.domain}`,
|
|
87
|
-
certLogAttributes,
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
// if cname is not valid then remove the domain
|
|
91
|
-
await GreenlockUtil.removeDomain(certificate.domain);
|
|
92
|
-
await data.notifyDomainRemoved(certificate.domain);
|
|
198
|
+
// if cname is not valid then remove the domain
|
|
199
|
+
await GreenlockUtil.removeDomain(domain);
|
|
200
|
+
await data.notifyDomainRemoved(domain);
|
|
93
201
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
`CNAME is valid for domain: ${certificate.domain}`,
|
|
101
|
-
certLogAttributes,
|
|
102
|
-
);
|
|
202
|
+
logger.error(
|
|
203
|
+
`Cname is not valid for domain: ${domain}`,
|
|
204
|
+
certLogAttributes,
|
|
205
|
+
);
|
|
206
|
+
} else {
|
|
207
|
+
logger.debug(`CNAME is valid for domain: ${domain}`, certLogAttributes);
|
|
103
208
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
209
|
+
await GreenlockUtil.orderCert({
|
|
210
|
+
domain: domain,
|
|
211
|
+
validateCname: data.validateCname,
|
|
212
|
+
});
|
|
108
213
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
} catch (e) {
|
|
115
|
-
logger.error(
|
|
116
|
-
`Error renewing certificate for domain: ${certificate.domain}`,
|
|
117
|
-
certLogAttributes,
|
|
118
|
-
);
|
|
119
|
-
logger.error(e, certLogAttributes);
|
|
120
|
-
}
|
|
214
|
+
logger.debug(
|
|
215
|
+
`Certificate renewed for domain: ${domain}`,
|
|
216
|
+
certLogAttributes,
|
|
217
|
+
);
|
|
121
218
|
}
|
|
122
219
|
} catch (e) {
|
|
123
|
-
logger.error(
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
220
|
+
logger.error(
|
|
221
|
+
`Error renewing certificate for domain: ${domain}`,
|
|
222
|
+
certLogAttributes,
|
|
223
|
+
);
|
|
224
|
+
logger.error(e, certLogAttributes);
|
|
127
225
|
}
|
|
128
226
|
}
|
|
129
227
|
|