feat: add auto-delete-accounts

This commit is contained in:
b12f 2025-02-01 03:44:43 +01:00
parent ec04bfc0a2
commit 6e2b4c3cc6
Signed by: b12f
GPG key ID: 729956E1124F8F26
7 changed files with 141 additions and 4 deletions

8
flake.lock generated
View file

@ -335,11 +335,11 @@
]
},
"locked": {
"lastModified": 1737819581,
"narHash": "sha256-i9rZSxy33BlDpp4JY9SI2zEFo5EhMnS7cAhqHAPRUZA=",
"lastModified": 1738364013,
"narHash": "sha256-GAg9RaSThTW8+nL/rTsb12i4EHWl5uBqtq8Sp2jEoVg=",
"ref": "main",
"rev": "bb9c6f3e3608f0d342ab74d921caddfe4a8bf5d6",
"revCount": 7,
"rev": "1b6c4dda9361bcc108a283e2c38867983474da17",
"revCount": 8,
"type": "git",
"url": "https://git.pub.solar/pub-solar/keycloak-event-listener"
},

View file

@ -0,0 +1 @@
node_modules

View file

@ -0,0 +1,75 @@
#!/usr/bin/env zx
import { add, sub, isEqual, isAfter } from "date-fns";
const realm = argv.realm;
const clientId = argv.clientId;
const server = argv.server;
/*
* You'll have to set KC_CLI_CLIENT_SECRET
*/
let users = JSON.parse(await $`kcadm.sh get users -r ${realm} --server ${server} --client ${clientId} --no-config"`);
// Set a last-login value to today for any accounts that do not have one
const noLastLogin = users.filter(user => !user.attributes?.["last-login"]?.length);
const now = new Date();
const todayString = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${now.getDate()}`;
await Promise.all(noLastLogin.map((user) => {
const attributes = {
"last-login": [todayString],
...user.attributes,
};
$`kcadm.sh update users/${user.id} -s 'attributes=${JSON.stringify(attributes)}' -r ${realm} --server ${server} --client ${clientId} --no-config"`;
}));
users = JSON.parse(await $`kcadm.sh get users -r ${realm} --server ${server} --client ${clientId} --no-config"`);
// Handle non-validated users
const nonValidated = users.filter(user => !user.emailVerified);
await Promise.all(nonValidated.map((user) => {
const lastLogin = new Date(user.attributes?.["last-login"]);
const deletionDate = add(lastLogin, { months: 1 });
if (isEqual(now, sub(deletionDate, { weeks: 1 })) {
// send reminder
}
if (isEqual(now, sub(deletionDate, { days: 1 })) {
// send reminder
}
if (isAfter(now, deletionDate)) {
// delete
}
}).filter(n => !!n));
const validated = users.filter(user => user.emailVerified);
await Promise.all(validated.map((user) => {
const lastLogin = new Date(user.attributes?.["last-login"]);
const deletionDate = add(lastLogin, { years: 2 });
if (isEqual(now, sub(deletionDate, { months: 1 })) {
// Send reminder to validated accounts that have not logged in for 2 years - 1 month
}
if (isEqual(now, sub(deletionDate, { weeks: 1 })) {
// Send reminder to validated accounts that have not logged in for 2 years - 1 week
}
if (isEqual(now, sub(deletionDate, { days: 1 })) {
// Send reminder to validated accounts that have not logged in for 2 years - 1 day
}
if (isEqual(now, deletionDate)) {
// Delete validated that have not logged in for more than 2 years
}
}).filter(n => !!n));

View file

@ -0,0 +1,26 @@
{
"name": "auto-delete-accounts",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "auto-delete-accounts",
"version": "1.0.0",
"license": "AGPL-3.0-or-later",
"dependencies": {
"date-fns": "^4.1.0"
}
},
"node_modules/date-fns": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz",
"integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/kossnocorp"
}
}
}
}

View file

@ -0,0 +1,14 @@
{
"name": "auto-delete-accounts",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "AGPL-3.0-or-later",
"description": "",
"dependencies": {
"date-fns": "^4.1.0"
}
}

View file

@ -0,0 +1,10 @@
{
writeShellScriptBin,
keycloak,
jq
}:
writeShellScriptBin "autodelete-accounts" ''
set -e
USERS=$(${keycloak}/bin/kcadm.sh get users -r test.pub.solar --server http://localhost:8080 --realm master --user admin --password password --no-config")
''

View file

@ -181,6 +181,9 @@ in
puppeteer_succeed('page.waitForNetworkIdle()')
client.screenshot("logged-out")
auth_server.wait_for_file('/tmp/continue', 3600)
auth_server.execute('rm /tmp/continue')
puppeteer_succeed('page.locator("[name=username]").fill("test-user")')
puppeteer_succeed('page.locator("::-p-text(Sign In)").click()')
puppeteer_succeed('page.locator("[name=password]").fill("Password1234")')
@ -204,6 +207,9 @@ in
puppeteer_succeed('page.waitForNetworkIdle()')
client.screenshot("TOTP-signed-in")
auth_server.wait_for_file('/tmp/continue', 600)
auth_server.execute('rm /tmp/continue')
####### Delete TOTP #######
puppeteer_scroll_into_view('[data-testid="otp/credential-list"]')
@ -212,5 +218,10 @@ in
# puppeteer_succeed('page.locator(`[data-testid="otp/credential-list"] button::-p-text(Delete)`).click()')
# client.screenshot("TOTP-deleted")
####### Automated account deletion #######
auth_server.succeed("${pkgs.keycloak}/bin/kcadm.sh get users -r test.pub.solar --server http://localhost:8080 --realm master --user admin --password password --no-config")
'';
}