diff --git a/keyserver/src/cron/cron.js b/keyserver/src/cron/cron.js index 760228cb6..2b83481e3 100644 --- a/keyserver/src/cron/cron.js +++ b/keyserver/src/cron/cron.js @@ -1,93 +1,107 @@ // @flow import cluster from 'cluster'; import schedule from 'node-schedule'; import { backupDB } from './backups.js'; import { createDailyUpdatesThread } from './daily-updates.js'; import { updateAndReloadGeoipDB } from './update-geoip-db.js'; import { deleteOrphanedActivity } from '../deleters/activity-deleters.js'; import { deleteExpiredCookies } from '../deleters/cookie-deleters.js'; import { deleteOrphanedDays } from '../deleters/day-deleters.js'; import { deleteOrphanedEntries } from '../deleters/entry-deleters.js'; import { deleteOrphanedMemberships } from '../deleters/membership-deleters.js'; import { deleteOrphanedMessages } from '../deleters/message-deleters.js'; import { deleteOrphanedNotifs } from '../deleters/notif-deleters.js'; import { deleteOrphanedRevisions } from '../deleters/revision-deleters.js'; import { deleteOrphanedRoles } from '../deleters/role-deleters.js'; import { deleteOrphanedSessions, deleteOldWebSessions, } from '../deleters/session-deleters.js'; import { deleteStaleSIWENonceEntries } from '../deleters/siwe-nonce-deleters.js'; import { deleteInaccessibleThreads } from '../deleters/thread-deleters.js'; import { deleteExpiredUpdates } from '../deleters/update-deleters.js'; import { deleteUnassignedUploads } from '../deleters/upload-deleters.js'; +import { fetchCallUpdateOlmAccount } from '../updaters/olm-account-updater.js'; +import { validateAccountPrekey } from '../utils/olm-utils.js'; if (cluster.isMaster) { schedule.scheduleJob( '30 3 * * *', // every day at 3:30 AM in the keyserver's timezone async () => { try { // Do everything one at a time to reduce load since we're in no hurry, // and since some queries depend on previous ones. await deleteExpiredCookies(); await deleteInaccessibleThreads(); await deleteOrphanedMemberships(); await deleteOrphanedDays(); await deleteOrphanedEntries(); await deleteOrphanedRevisions(); await deleteOrphanedRoles(); await deleteOrphanedMessages(); await deleteOrphanedActivity(); await deleteOrphanedNotifs(); await deleteOrphanedSessions(); await deleteOldWebSessions(); await deleteExpiredUpdates(); await deleteUnassignedUploads(); await deleteStaleSIWENonceEntries(); } catch (e) { console.warn('encountered error while trying to clean database', e); } }, ); schedule.scheduleJob( '0 */4 * * *', // every four hours async () => { try { await backupDB(); } catch (e) { console.warn('encountered error while trying to backup database', e); } }, ); schedule.scheduleJob( '0 3 ? * 0', // every Sunday at 3:00 AM in the keyserver's timezone async () => { try { await updateAndReloadGeoipDB(); } catch (e) { console.warn( 'encountered error while trying to update GeoIP database', e, ); } }, ); schedule.scheduleJob( '0 0 * * *', // every day at midnight in the keyserver's timezone async () => { try { if (process.env.RUN_COMM_TEAM_DEV_SCRIPTS) { // This is a job that the Comm internal team uses await createDailyUpdatesThread(); } } catch (e) { console.warn( 'encountered error while trying to create daily updates thread', e, ); } }, ); + + schedule.scheduleJob( + '0 0 * * *', // every day at midnight in the keyserver's timezone + async () => { + try { + await fetchCallUpdateOlmAccount('content', validateAccountPrekey); + await fetchCallUpdateOlmAccount('notifications', validateAccountPrekey); + } catch (e) { + console.warn('encountered error while trying to validate prekeys', e); + } + }, + ); } diff --git a/keyserver/src/utils/olm-utils.js b/keyserver/src/utils/olm-utils.js index 1bede58fc..9b37d1fc6 100644 --- a/keyserver/src/utils/olm-utils.js +++ b/keyserver/src/utils/olm-utils.js @@ -1,51 +1,81 @@ // @flow import olm from '@commapp/olm'; import type { Account as OlmAccount, Utility as OlmUtility, } from '@commapp/olm'; import uuid from 'uuid'; type PickledOlmAccount = { +picklingKey: string, +pickledAccount: string, }; +const maxPublishedPrekeyAge = 30 * 24 * 60 * 60 * 1000; +const maxOldPrekeyAge = 24 * 60 * 60 * 1000; + async function createPickledOlmAccount(): Promise { await olm.init(); const account = new olm.Account(); account.create(); const picklingKey = uuid.v4(); const pickledAccount = account.pickle(picklingKey); return { picklingKey: picklingKey, pickledAccount: pickledAccount, }; } async function unpickleOlmAccount( pickledOlmAccount: PickledOlmAccount, ): Promise { await olm.init(); const account = new olm.Account(); account.unpickle( pickledOlmAccount.picklingKey, pickledOlmAccount.pickledAccount, ); return account; } let cachedOLMUtility: OlmUtility; function getOlmUtility(): OlmUtility { if (cachedOLMUtility) { return cachedOLMUtility; } cachedOLMUtility = new olm.Utility(); return cachedOLMUtility; } -export { createPickledOlmAccount, getOlmUtility, unpickleOlmAccount }; +async function validateAccountPrekey(account: OlmAccount): Promise { + const currentDate = new Date(); + const lastPrekeyPublishDate = new Date(account.last_prekey_publish_time()); + + const prekeyPublished = !account.unpublished_prekey(); + if ( + prekeyPublished && + currentDate - lastPrekeyPublishDate > maxPublishedPrekeyAge + ) { + // If there is no prekey or the current prekey is older than month + // we need to generate new one. + account.generate_prekey(); + } + + if ( + prekeyPublished && + currentDate - lastPrekeyPublishDate >= maxOldPrekeyAge + ) { + account.forget_old_prekey(); + } +} + +export { + createPickledOlmAccount, + getOlmUtility, + unpickleOlmAccount, + validateAccountPrekey, +};