Files
outline/server/scripts/reset-encrypted-data.ts
Tom Moor 92db179230 chore: Add script to reset encrypted data (#10238)
* chore: Add script to reset encrypted data

* Update server/models/WebhookSubscription.ts

Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>

* Update server/scripts/reset-encrypted-data.ts

Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>

* Add confirmation

---------

Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com>
2025-09-23 08:34:46 -04:00

118 lines
3.0 KiB
TypeScript

import "./bootstrap";
import * as readline from "readline";
import { Transaction } from "sequelize";
import {
OAuthClient,
User,
UserAuthentication,
WebhookSubscription,
} from "@server/models";
import { sequelize } from "@server/storage/database";
// Helper function to prompt user for input
function askQuestion(question: string): Promise<string> {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
return new Promise((resolve) => {
rl.question(question, (answer) => {
rl.close();
resolve(answer.trim().toLowerCase());
});
});
}
// Helper function to pause and wait for user confirmation
async function waitForConfirmation(message: string): Promise<boolean> {
const answer = await askQuestion(`${message} (y/N): `);
return answer === "y" || answer === "yes";
}
export default async function main() {
console.log("🔐 Reset Encrypted Data Script");
console.log("This script will:");
console.log("- Delete all user authentication tokens");
console.log("- Rotate webhook signing secrets");
console.log("- Rotate OAuth client secrets");
console.log("- Rotate JWT secrets for all users (logging them out)");
console.log("");
const shouldContinue = await waitForConfirmation(
"⚠️ This will log out all users and invalidate tokens. Continue?"
);
if (!shouldContinue) {
console.log("❌ Operation cancelled.");
process.exit(0);
}
await sequelize.transaction(async (transaction) => {
await UserAuthentication.destroy({
where: {},
transaction,
});
const webhooks = await WebhookSubscription.findAll({
lock: Transaction.LOCK.UPDATE,
transaction,
});
for (const webhook of webhooks) {
try {
webhook.rotateSecret();
await webhook.save({ transaction });
} catch (err) {
console.error(
`Failed to rotate webhook signing secret for webhook ${webhook.id}:`,
err
);
continue;
}
}
const oauthClients = await OAuthClient.findAll({
lock: Transaction.LOCK.UPDATE,
transaction,
});
for (const client of oauthClients) {
try {
client.rotateClientSecret();
await client.save({ transaction });
} catch (err) {
console.error(
`Failed to rotate OAuth client secret for client ${client.id}:`,
err
);
continue;
}
}
const users = await User.findAll({
lock: Transaction.LOCK.UPDATE,
transaction,
});
for (const user of users) {
try {
await user.rotateJwtSecret({ transaction });
} catch (err) {
console.error(`Failed to rotate JWT secret for user ${user.id}:`, err);
continue;
}
}
console.log(`Reset encrypted data, logged out ${users.length} users`);
});
process.exit(0);
}
// In the test suite we import the script rather than run via node CLI
if (process.env.NODE_ENV !== "test") {
void main();
}