Skip to content
This repository has been archived by the owner on Nov 2, 2024. It is now read-only.

Commit

Permalink
🚑 Temporary performance fix for keycloak mandate sync
Browse files Browse the repository at this point in the history
  • Loading branch information
danieladugyan committed Jan 2, 2024
1 parent c55c7e5 commit 8aab6aa
Showing 1 changed file with 65 additions and 39 deletions.
104 changes: 65 additions & 39 deletions backend/services/core/src/keycloak.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import KcAdminClient from '@keycloak/keycloak-admin-client';
import { Knex } from 'knex';
import { throttle } from 'lodash';
import { createLogger } from './shared';

const logger = createLogger('keycloak-admin');

const userEmails = new Map<string, string>();

const {
KEYCLOAK_ADMIN_USERNAME,
KEYCLOAK_ADMIN_PASSWORD,
KEYCLOAK_ENDPOINT,
} = process.env;
const { KEYCLOAK_ADMIN_USERNAME, KEYCLOAK_ADMIN_PASSWORD, KEYCLOAK_ENDPOINT } = process.env;

/**
* turns dsek.sexm.kok.mastare into ['dsek', 'dsek.sexm', 'dsek.sexm.kok', 'dsek.sexm.kok.mastare']
Expand All @@ -19,7 +16,8 @@ const {
*/
export function getRoleNames(id: string): string[] {
const parts = id.split('.');
return [...Array(parts.length).keys()].map((i) => parts.slice(0, i + 1).join('.'));
return [...Array(parts.length).keys()].map((i) =>
parts.slice(0, i + 1).join('.'));
}

class KeycloakAdmin {
Expand All @@ -30,6 +28,7 @@ class KeycloakAdmin {
baseUrl: KEYCLOAK_ENDPOINT,
realmName: 'master',
});
this.auth = throttle(this.auth.bind(this), 50 * 1000) as () => Promise<void>;
}

async auth() {
Expand Down Expand Up @@ -63,7 +62,7 @@ class KeycloakAdmin {
*/
async checkIfGroupExists(positionId: string): Promise<boolean> {
if (process.env.KEYCLOAK_ENABLED !== 'true') return false;
return !!await this.getGroupId(positionId);
return !!(await this.getGroupId(positionId));
}

/**
Expand All @@ -75,7 +74,9 @@ class KeycloakAdmin {
if (process.env.KEYCLOAK_ENABLED !== 'true') return;
await this.auth();
const groupId = await this.getGroupId(positionId);
if (groupId) { await this.client.users.addToGroup({ id: userId, groupId }); }
if (groupId) {
await this.client.users.addToGroup({ id: userId, groupId });
}
}

/**
Expand All @@ -93,8 +94,9 @@ class KeycloakAdmin {
}
}

async getUserData(keycloakIds: string[]):
Promise<{ keycloakId: string, email: string, studentId: string }[]> {
async getUserData(
keycloakIds: string[],
): Promise<{ keycloakId: string; email: string; studentId: string }[]> {
if (process.env.KEYCLOAK_ENABLED !== 'true') return [];
await this.auth();

Expand Down Expand Up @@ -134,48 +136,72 @@ class KeycloakAdmin {
return userEmails.get(keycloakId);
}

async updateKeycloakMandates(
knex: Knex,
): Promise<boolean> {
async updateKeycloakMandates(knex: Knex): Promise<boolean> {
let success = true;
logger.info('Updating keycloak mandates');

const today = new Date();
const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000).toISOString()
const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000)
.toISOString()
.substring(0, 10);

const expiredMandates = await knex('mandates').join('keycloak', 'mandates.member_id', '=', 'keycloak.member_id').where('end_date', '<', yesterday).where({ in_keycloak: true })
const expiredMandates = await knex('mandates')
.join('keycloak', 'mandates.member_id', '=', 'keycloak.member_id')
.where('end_date', '<', yesterday)
.where({ in_keycloak: true })
.select('keycloak_id', 'position_id', 'mandates.id');
logger.info(`Found ${expiredMandates.length} expired mandates.`);

const mandatesToAdd = await knex<{ keycloak_id: string, position_id: string }>('mandates').join('keycloak', 'mandates.member_id', '=', 'keycloak.member_id').where('end_date', '>', today)
const mandatesToAdd = await knex<{
keycloak_id: string;
position_id: string;
}>('mandates')
.join('keycloak', 'mandates.member_id', '=', 'keycloak.member_id')
.where('start_date', '<=', today)
.where('end_date', '>=', today)
.where({ in_keycloak: false })
.select('keycloak.keycloak_id', 'mandates.position_id', 'mandates.id');
logger.info(`Found ${mandatesToAdd.length} mandates to add.`);

logger.info('Updating keycloak...');
await Promise.all(mandatesToAdd.map((mandate) => this
.createMandate(mandate.keycloak_id, mandate.position_id)
.then(async () => {
await knex('mandates').where({ id: mandate.id }).update({ in_keycloak: true });
logger.info(`Created mandate ${mandate.keycloak_id}->${mandate.position_id}`);
})
.catch((e) => {
logger.error(`Failed to create mandate ${mandate.keycloak_id}->${mandate.position_id}`);
logger.error(e);
success = false;
})));

await Promise.all(expiredMandates.map((mandate) => this
.deleteMandate(mandate.keycloak_id, mandate.position_id)
.then(async () => {
await knex('mandates').where({ id: mandate.id }).update({ in_keycloak: false });
logger.info(`Deleted mandate ${mandate.keycloak_id}->${mandate.position_id}`);
})
.catch((e) => {
logger.error(`Failed to delete mandate ${mandate.keycloak_id}->${mandate.position_id}`);
logger.error(e);
success = false;
})));
expiredMandates.forEach(async (mandate) => {
await this.deleteMandate(mandate.keycloak_id, mandate.position_id)
.then(async () => {
await knex('mandates')
.where({ id: mandate.id })
.update({ in_keycloak: false });
logger.info(
`Deleted mandate ${mandate.keycloak_id}->${mandate.position_id}`,
);
})
.catch((e) => {
logger.error(
`Failed to delete mandate ${mandate.keycloak_id}->${mandate.position_id}`,
);
logger.error(e);
success = false;
});
});

mandatesToAdd.forEach(async (mandate) => {
await this.createMandate(mandate.keycloak_id, mandate.position_id)
.then(async () => {
await knex('mandates')
.where({ id: mandate.id })
.update({ in_keycloak: true });
logger.info(
`Created mandate ${mandate.keycloak_id}->${mandate.position_id}`,
);
})
.catch((e) => {
logger.error(
`Failed to create mandate ${mandate.keycloak_id}->${mandate.position_id}`,
);
logger.error(e);
success = false;
});
});

logger.info('Done updating mandates');
return success;
}
Expand Down

0 comments on commit 8aab6aa

Please sign in to comment.