WIP: feat/automated-account-deletion #174
7 changed files with 141 additions and 4 deletions
8
flake.lock
generated
8
flake.lock
generated
|
@ -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"
|
||||
},
|
||||
|
|
1
modules/keycloak/auto-delete-accounts/.gitignore
vendored
Normal file
1
modules/keycloak/auto-delete-accounts/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
node_modules
|
|
@ -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));
|
26
modules/keycloak/auto-delete-accounts/package-lock.json
generated
Normal file
26
modules/keycloak/auto-delete-accounts/package-lock.json
generated
Normal 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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
14
modules/keycloak/auto-delete-accounts/package.json
Normal file
14
modules/keycloak/auto-delete-accounts/package.json
Normal 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"
|
||||
}
|
||||
}
|
10
modules/keycloak/automated-account-deletion.nix
Normal file
10
modules/keycloak/automated-account-deletion.nix
Normal 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")
|
||||
''
|
|
@ -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")
|
||||
|
||||
'';
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue