()
| 166 | } |
| 167 | |
| 168 | async _update() { |
| 169 | const now = this._t.now(); |
| 170 | const t0 = intervalStart(now, this._interval); |
| 171 | let next = t0 + this._interval; // When this._update() should be called again. |
| 172 | let legacyEnd = now; |
| 173 | // TODO: This is racy. If two instances start up at the same time and there are no existing |
| 174 | // matching publications, each will generate and publish their own paramters. In practice this |
| 175 | // is unlikely to happen, and if it does it can be fixed by restarting both Etherpad instances. |
| 176 | const dbKeys:string[] = await db.findKeys(`${this._dbPrefix}:*`, null) || []; |
| 177 | let currentParams:any = null; |
| 178 | let currentId = null; |
| 179 | const dbWrites:any[] = []; |
| 180 | const allParams = []; |
| 181 | const legacyParams:LegacyParams[] = []; |
| 182 | await Promise.all(dbKeys.map(async (dbKey) => { |
| 183 | const p = await db.get(dbKey); |
| 184 | if (p.algId === 0 && p.algParams === this._legacyStaticSecret) legacyParams.push(p); |
| 185 | if (p.start < legacyEnd) legacyEnd = p.start; |
| 186 | // Check if the params have expired. Params are still useful if a MAC generated by a secret |
| 187 | // derived from the params is still valid, which can be true up to p.end + p.lifetime if |
| 188 | // there was no clock skew. The p.interval factor is added to accommodate clock skew. |
| 189 | // p.interval is null for legacy secrets, so fall back to this._interval. |
| 190 | if (now >= p.end + p.lifetime + (p.interval || this._interval)) { |
| 191 | // This initial keying material (or legacy secret) is expired. |
| 192 | dbWrites.push(db.remove(dbKey)); |
| 193 | dbWrites[dbWrites.length - 1].catch(() => {}); // Prevent unhandled Promise rejections. |
| 194 | return; |
| 195 | } |
| 196 | const t1 = p.interval && intervalStart(now, p.interval) + p.interval; // Start of next intrvl. |
| 197 | const tA = intervalStart(p.start, p.interval); // Start of interval containing p.start. |
| 198 | if (p.interval) next = Math.min(next, t1); |
| 199 | // Determine if these params can be used to generate the current (active) secret. Note that |
| 200 | // p.start is allowed to be in the next interval in case there is clock skew. |
| 201 | if (p.interval && p.interval === this._interval && p.lifetime === this._lifetime && |
| 202 | tA <= t1 && p.end > now && (currentParams == null || p.start > currentParams.start)) { |
| 203 | if (currentParams) allParams.push(currentParams); |
| 204 | currentParams = p; |
| 205 | currentId = dbKey; |
| 206 | } else { |
| 207 | allParams.push(p); |
| 208 | } |
| 209 | })); |
| 210 | if (this._legacyStaticSecret && now < legacyEnd + this._lifetime + this._interval && |
| 211 | !legacyParams.find((p) => p.end + p.lifetime >= legacyEnd + this._lifetime)) { |
| 212 | const d = new Date(legacyEnd).toJSON(); |
| 213 | this._logger.debug(`adding legacy static secret for ${d} with lifetime ${this._lifetime}`); |
| 214 | const p: LegacyParams = { |
| 215 | algId: 0, |
| 216 | algParams: this._legacyStaticSecret, |
| 217 | // The start time is equal to the end time so that this legacy secret does not affect the |
| 218 | // end times of any legacy secrets published by other instances. |
| 219 | start: legacyEnd, |
| 220 | end: legacyEnd, |
| 221 | interval: null, |
| 222 | lifetime: this._lifetime, |
| 223 | }; |
| 224 | allParams.push(p); |
| 225 | dbWrites.push(this._publish(p)); |
no test coverage detected