From 237a7ed4c4784fcdfb0871951bbee5c49d041a6f Mon Sep 17 00:00:00 2001 From: guFalcon Date: Mon, 15 Jul 2024 15:51:24 +0200 Subject: [PATCH] fix updates because of change to Keycloak 25 --- README.md | 16 ++++++++++------ middlewares/keycloak-middleware.js | 24 +++++++++++++----------- utils.js | 8 ++++++-- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index f975c00..ff9d607 100644 --- a/README.md +++ b/README.md @@ -323,12 +323,16 @@ Then you can run the REST-examples in the `http` directory. ## Keycloak Setup In order for this setup to work correctly, you'll have to have a Keycloak-client (private with secret) and standard-authorization-flow enabled (should be enabled anyway). You need the following user-attributes of type `string` with mapper to the token. - -| ATTRIBUTE-NAME | TYPE | DESCRIPTION | -| ------------------------------------------------------------------------------------------------------------------ | ------ | ----------------------------------------------------------------------------------------------------- | -| Client -> Client Scopes -> ...-dedicated -> Add Mapper (User Attribute)
`config` | string | Holds several preference-values like dark-mode or not or the preferred font, fontsize or line-height. | -| Client -> Client Scopes -> ...-dedicated -> Add Mapper (User Attribute)
`lastVisitedUrl` | string | Holds the last-visited page of the current user. | -| Client -> Client Scopes -> ...-dedicated -> Add Mapper (User Attribute)
map the field `LDAP_ENTRY_DN` to `ldap` | string | Holds the users LDAP information (class, teacher, etc.). | +You also need to add the user-attributes to the user-profile first (`Realm settings` -> `User profile (Attribute group = none, not user-metadata)`) in order to allow for the addition of data to your users. Be sure to set those to `allow edit and view for User and Admin` so that the application is able to change the values. +The application uses the following endpoints of the Keycloak-API to do that: +- `GET {{keycloakUrl}}/realms/{{realm}}/account` +- `POST {{keycloakUrl}}/realms/{{realm}}/account` +The user-metadata field `LDAP_ENTRY_DN` will be automatically present because of the LDAP mapper in your Keycloak instance. It will be readable from the `access-token`. So there are no additional setup-steps requried. + +| ATTRIBUTE-NAME | TYPE | DESCRIPTION | +| ------------------------------------------------------------------------------------------- | ------ | ----------------------------------------------------------------------------------------------------- | +| Client -> Client Scopes -> ...-dedicated -> Add Mapper (User Attribute)
`config` | string | Holds several preference-values like dark-mode or not or the preferred font, fontsize or line-height. | +| Client -> Client Scopes -> ...-dedicated -> Add Mapper (User Attribute)
`lastVisitedUrl` | string | Holds the last-visited page of the current user. | ## MD-File Conversion This is done using [marked](https://www.npmjs.com/package/marked) which is installed on the web-server (via `package.json`). With the help of this you can link to any MD-file and show it in the context of your site. diff --git a/middlewares/keycloak-middleware.js b/middlewares/keycloak-middleware.js index 11a3077..fd4eb4e 100644 --- a/middlewares/keycloak-middleware.js +++ b/middlewares/keycloak-middleware.js @@ -156,6 +156,7 @@ export function getLdapGroups(req) { return r; } const ldap = req.user.ldap; + // console.log("LDAP-String", ldap) // Regular expression to match "OU=..." const regex = /OU=[^,]*/gi; @@ -170,7 +171,7 @@ export function getLdapGroups(req) { r[value] = true; }); } - + // console.log("LDAP-Groups", r); return r; } @@ -194,13 +195,14 @@ export async function getUserAttributes(req, getAll = false) { return response.json(); }) .then((data) => { - for(let key in data) { - data[key] = data[key][0]; + // console.log("data of user attributes", data); + for(let key in data.attributes) { + data.attributes[key] = data.attributes[key][0]; } if(getAll) { - // Remove fields from the object that are not needed. - let { userProfileMetadata, id, username, emailVerified, ...d} = data; - return d; + // Remove fields from the object that are not needed. + let { userProfileMetadata, id, username, emailVerified, ...d} = data; + return d; } return data; }) @@ -222,12 +224,12 @@ export async function setUserAttribute(req, attributeName, attributeValue) { // Fetch current user attributes const currentAttributes = await getUserAttributes(req, true); + // console.log("current attributes", currentAttributes); - // Merge current and new attributes at the root level - let mergedAttributes = { ...currentAttributes, [attributeName]: attributeValue }; - mergedAttributes = { - attributes: mergedAttributes - } + // Merge current and new attributes + const mas = { ...currentAttributes.attributes, [attributeName]: attributeValue }; + const mergedAttributes = { ...currentAttributes, attributes: mas }; + // console.log("merged attributes before saving", mergedAttributes); const result = fetch(url, { method: "POST", diff --git a/utils.js b/utils.js index 46660eb..ccb20b7 100644 --- a/utils.js +++ b/utils.js @@ -96,13 +96,17 @@ async function hasRoles(req, clientRoles, all, override) { let clientAccess = null; const attributes = await getUserAttributes(req); const ccr = await getClientRoles(req, clientRoles); + // console.log("Client roles", ccr); + // console.log("Request user rolesCalculated", req.user.rolesCalculated); if ( req.user.rolesCalculated !== undefined && req.user.rolesCalculated !== null ) { - if (attributes && attributes.config) { - const a = JSON.parse(attributes.config); + // console.log("attributes", attributes); + if (attributes && attributes.attributes && attributes.attributes.config) { + const a = JSON.parse(attributes.attributes.config); const r = JSON.parse(req.user.rolesCalculated); + // console.log("Roles Calculated", r); const cr = await getClientRoles(req, clientRoles); if (cr) { for (const role of cr) {