Skip to content

Commit

Permalink
Merge pull request #3397 from uselagoon/keycloak-optimizations
Browse files Browse the repository at this point in the history
refactor: optimize keycloak interactions
  • Loading branch information
tobybellwood authored Mar 19, 2023
2 parents 6632815 + 5c15242 commit aa7ba96
Show file tree
Hide file tree
Showing 18 changed files with 584 additions and 524 deletions.
83 changes: 74 additions & 9 deletions services/api/src/apolloServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ const {
} = require('./util/auth');
const { sqlClientPool } = require('./clients/sqlClient');
const esClient = require('./clients/esClient');
const redisClient = require('./clients/redisClient');
const { getKeycloakAdminClient } = require('./clients/keycloak-admin');
const { logger } = require('./loggers/logger');
const { userActivityLogger } = require('./loggers/userActivityLogger');
const typeDefs = require('./typeDefs');
const resolvers = require('./resolvers');
const { keycloakGrantManager } = require('./clients/keycloakClient');

const User = require('./models/user');
const Group = require('./models/group');
Expand Down Expand Up @@ -117,9 +117,11 @@ const apolloServer = new ApolloServer({
sqlClientPool,
keycloakAdminClient,
esClient,
redisClient
};

let keycloakGroups = {}
let keycloakUsersGroups = []

return {
keycloakAdminClient,
sqlClientPool,
Expand All @@ -133,7 +135,9 @@ const apolloServer = new ApolloServer({
GroupModel: Group.Group(modelClients),
ProjectModel: ProjectModel.ProjectModel(modelClients),
EnvironmentModel: EnvironmentModel.EnvironmentModel(modelClients)
}
},
keycloakGroups,
keycloakUsersGroups,
};
},
onDisconnect: (websocket, context) => {
Expand Down Expand Up @@ -163,16 +167,69 @@ const apolloServer = new ApolloServer({
sqlClientPool,
keycloakAdminClient,
esClient,
redisClient
};

// get all keycloak groups, do this early to reduce the number of times this is called otherwise
// but doing this early and once is pretty cheap
let allGroups = await Group.Group(modelClients).loadAllGroups();
let keycloakGroups = await Group.Group(modelClients).transformKeycloakGroups(allGroups);

let currentUser = {};
let serviceAccount = {};
// if this is a user request, get the users keycloak groups too, do this one to reduce the number of times it is called elsewhere
let keycloakUsersGroups = []
let groupRoleProjectIds = []
const keycloakGrant = req.kauth ? req.kauth.grant : null
if (keycloakGrant) {
keycloakUsersGroups = await User.User(modelClients).getAllGroupsForUser(keycloakGrant.access_token.content.sub);
serviceAccount = await keycloakGrantManager.obtainFromClientCredentials();
currentUser = await User.User(modelClients).loadUserById(keycloakGrant.access_token.content.sub);
// grab the users project ids and roles in the first request
groupRoleProjectIds = await User.User(modelClients).getAllProjectsIdsForUser(currentUser, keycloakUsersGroups);
}

// do a permission check to see if the user is platform admin/owner, or has permission for `viewAll` on certain resources
// this reduces the number of `viewAll` permission look ups that could potentially occur during subfield resolvers for non admin users
// every `hasPermission` check adds a delay, and if you're a member of a group that has a lot of projects and environments, hasPermissions is costly when we perform
// the viewAll permission check, to then error out and follow through with the standard user permission check, effectively costing 2 hasPermission calls for every request
// this eliminates a huge number of these by making it available in the apollo context
const hasPermission = req.kauth
? keycloakHasPermission(req.kauth.grant, requestCache, modelClients, serviceAccount, currentUser, groupRoleProjectIds)
: legacyHasPermission(req.legacyCredentials)
let projectViewAll = false
let groupViewAll = false
let environmentViewAll = false
let deploytargetViewAll = false
try {
await hasPermission("project","viewAll")
projectViewAll = true
} catch(err) {
// do nothing
}
try {
await hasPermission("group","viewAll")
groupViewAll = true
} catch(err) {
// do nothing
}
try {
await hasPermission("environment","viewAll")
environmentViewAll = true
} catch(err) {
// do nothing
}
try {
await hasPermission("openshift","viewAll")
deploytargetViewAll = true
} catch(err) {
// do nothing
}

return {
keycloakAdminClient,
sqlClientPool,
hasPermission: req.kauth
? keycloakHasPermission(req.kauth.grant, requestCache, modelClients)
: legacyHasPermission(req.legacyCredentials),
keycloakGrant: req.kauth ? req.kauth.grant : null,
hasPermission,
keycloakGrant,
requestCache,
userActivityLogger: (message, meta) => {
let defaultMeta = {
Expand All @@ -193,7 +250,15 @@ const apolloServer = new ApolloServer({
GroupModel: Group.Group(modelClients),
ProjectModel: ProjectModel.ProjectModel(modelClients),
EnvironmentModel: EnvironmentModel.EnvironmentModel(modelClients)
}
},
keycloakGroups,
keycloakUsersGroups,
adminScopes: {
projectViewAll: projectViewAll,
groupViewAll: groupViewAll,
environmentViewAll: environmentViewAll,
deploytargetViewAll: deploytargetViewAll,
},
};
}
},
Expand Down
Loading

0 comments on commit aa7ba96

Please sign in to comment.