From be0a68eb94a1db4323a402ed5772ccef5dc73234 Mon Sep 17 00:00:00 2001 From: shreddedbacon Date: Mon, 30 Dec 2024 08:29:32 +1100 Subject: [PATCH 1/2] refactor: use api to store last accessed for user --- .../20241213000000_user_last_accessed.js | 26 ++++++++++++++ services/api/src/models/user.ts | 36 ++++++++++++------- services/api/src/resources/user/sql.ts | 19 ++++++++++ services/api/src/typeDefs.js | 5 ++- 4 files changed, 71 insertions(+), 15 deletions(-) create mode 100644 services/api/database/migrations/20241213000000_user_last_accessed.js diff --git a/services/api/database/migrations/20241213000000_user_last_accessed.js b/services/api/database/migrations/20241213000000_user_last_accessed.js new file mode 100644 index 0000000000..f6c7a9860f --- /dev/null +++ b/services/api/database/migrations/20241213000000_user_last_accessed.js @@ -0,0 +1,26 @@ +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.up = async function(knex) { + organizations = await knex.schema.hasTable('user'); + if (!organizations) { + return knex.schema + .createTable('user', function (table) { + table.specificType('usid', 'CHAR(36)'); + table.datetime('last_accessed'); + table.primary(['usid']); + }) + } + else { + return knex.schema + } +}; + +/** + * @param { import("knex").Knex } knex + * @returns { Promise } + */ +exports.down = async function(knex) { + return knex.schema +}; diff --git a/services/api/src/models/user.ts b/services/api/src/models/user.ts index 5285bde369..9fc18134c6 100644 --- a/services/api/src/models/user.ts +++ b/services/api/src/models/user.ts @@ -8,6 +8,7 @@ import { Group, GroupType, KeycloakLagoonGroup } from './group'; import { Sql } from '../resources/user/sql'; import { getConfigFromEnv } from '../util/config'; import { Helpers as groupHelpers } from '../resources/group/helpers'; +import { logger } from '../loggers/logger'; interface IUserAttributes { comment?: [string]; @@ -20,6 +21,7 @@ export interface User { firstName?: string; lastName?: string; comment?: string; + created?: string; lastAccessed?: string; gitlabId?: string; attributes?: IUserAttributes; @@ -178,23 +180,22 @@ export const User = (clients: { R.pipe( R.pick(['id', 'email', 'username', 'firstName', 'lastName', 'attributes', 'admin', 'owner', 'organizationRole', 'platformRoles']), // @ts-ignore - R.set(commentLens, R.view(attrCommentLens, keycloakUser)) + R.set(commentLens, R.view(attrCommentLens, keycloakUser)), + // set the user created time + R.set(R.lensPath(['created']), new Date(keycloakUser.createdTimestamp).toISOString().slice(0, 19).replace('T', ' ') || null), )(keycloakUser) ); let usersWithGitlabIdFetch = []; for (const user of users) { - // set the lastaccessed attribute - // @TODO: no op last accessed for the time being due to raciness - // @TODO: refactor later - /* - let date = null; - if (user['attributes'] && user['attributes']['last_accessed']) { - date = new Date(user['attributes']['last_accessed']*1000).toISOString() - user.lastAccessed = date + const userdate = await query( + sqlClientPool, + Sql.selectLastAccessed(user.id) + ); + if (userdate.length) { + user.lastAccessed = userdate[0].lastAccessed } - */ usersWithGitlabIdFetch.push({ ...user, gitlabId: await fetchGitlabId(user) @@ -648,8 +649,14 @@ export const User = (clients: { const userLastAccessed = async (userInput: User): Promise => { // set the last accessed as a unix timestamp on the user attributes - // @TODO: no op last accessed for the time being due to raciness - // @TODO: refactor later + try { + await query( + sqlClientPool, + Sql.updateLastAccessed(userInput.id) + ); + } catch (err) { + logger.warn(`Error updating user: ${err.message}`); + } return true }; @@ -754,6 +761,11 @@ export const User = (clients: { sqlClientPool, Sql.deleteFromUserSshKeys(id) ); + // delete from the user table + await query( + sqlClientPool, + Sql.deleteFromUser(id) + ); await keycloakAdminClient.users.del({ id }); } catch (err) { diff --git a/services/api/src/resources/user/sql.ts b/services/api/src/resources/user/sql.ts index 8bf7d58202..9cfdbbaeb3 100644 --- a/services/api/src/resources/user/sql.ts +++ b/services/api/src/resources/user/sql.ts @@ -38,4 +38,23 @@ export const Sql = { .where('sk.key_fingerprint', keyFingerprint) .select('user_ssh_key.usid') .toString(), + updateLastAccessed: (id: string) => + knex('user') + .insert({ + usid: id, + lastAccessed: knex.fn.now(), + }) + .onConflict('usid') + .merge() + .toString(), + selectLastAccessed: (id: string) => + knex('user') + .select('last_accessed') + .where('usid','=',id) + .toString(), + deleteFromUser: (id: string) => + knex('user') + .where('usid', id) + .delete() + .toString(), }; diff --git a/services/api/src/typeDefs.js b/services/api/src/typeDefs.js index 6693c162e7..b3fb60c37c 100644 --- a/services/api/src/typeDefs.js +++ b/services/api/src/typeDefs.js @@ -459,10 +459,9 @@ const typeDefs = gql` # This just returns the group name, id and the role the user has in that group. # This is a neat way to visualize a users specific access without having to get all members of a group groupRoles: [GroupRoleInterface] - # @TODO: no op last accessed for the time being due to raciness - # @TODO: refactor later - # lastAccessed: String platformRoles: [PlatformRole] + created: String + lastAccessed: String } enum PlatformRole { From 33838942ec90e17f751b77cecd458e70f7c7d424 Mon Sep 17 00:00:00 2001 From: Toby Bellwood Date: Mon, 30 Dec 2024 08:55:05 +1100 Subject: [PATCH 2/2] chore: rename and remove user table on knex down --- ...ast_accessed.js => 20241229000000_user_last_accessed.js} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename services/api/database/migrations/{20241213000000_user_last_accessed.js => 20241229000000_user_last_accessed.js} (81%) diff --git a/services/api/database/migrations/20241213000000_user_last_accessed.js b/services/api/database/migrations/20241229000000_user_last_accessed.js similarity index 81% rename from services/api/database/migrations/20241213000000_user_last_accessed.js rename to services/api/database/migrations/20241229000000_user_last_accessed.js index f6c7a9860f..b1d65853ed 100644 --- a/services/api/database/migrations/20241213000000_user_last_accessed.js +++ b/services/api/database/migrations/20241229000000_user_last_accessed.js @@ -3,8 +3,8 @@ * @returns { Promise } */ exports.up = async function(knex) { - organizations = await knex.schema.hasTable('user'); - if (!organizations) { + userTable = await knex.schema.hasTable('user'); + if (!userTable) { return knex.schema .createTable('user', function (table) { table.specificType('usid', 'CHAR(36)'); @@ -22,5 +22,5 @@ exports.up = async function(knex) { * @returns { Promise } */ exports.down = async function(knex) { - return knex.schema + return knex.schema.dropTable('user'); };