diff --git a/pom.xml b/pom.xml
index bf68978..f04d203 100644
--- a/pom.xml
+++ b/pom.xml
@@ -204,6 +204,21 @@
test
+
+ de.acosix.alfresco.utility
+ de.acosix.alfresco.utility.repo
+ ${acosix.utility.version}
+ provided
+
+
+
+ de.acosix.alfresco.utility
+ de.acosix.alfresco.utility.repo
+ ${acosix.utility.version}
+ installable
+ test
+
+
org.orderofthebee.support-tools
support-tools-repo
diff --git a/repository/src/main/config/module-context.xml b/repository/src/main/config/module-context.xml
index f5d03ed..7155952 100644
--- a/repository/src/main/config/module-context.xml
+++ b/repository/src/main/config/module-context.xml
@@ -75,6 +75,11 @@
+
+
+
+
+
diff --git a/repository/src/main/globalConfig/webscripts/extensions/config/acosix-keycloak-extension.xml b/repository/src/main/globalConfig/webscripts/extensions/config/acosix-keycloak-extension.xml
new file mode 100644
index 0000000..1415b78
--- /dev/null
+++ b/repository/src/main/globalConfig/webscripts/extensions/config/acosix-keycloak-extension.xml
@@ -0,0 +1,18 @@
+
+
+
+
+ ${moduleId} - Web Script Extension
+ ${project.name}
+ ${noSnapshotVersion}
+ true
+
+
+
+ org.alfresco
+ de.acosix.keycloak.customisations
+
+
+
+
+
\ No newline at end of file
diff --git a/repository/src/main/java/de/acosix/alfresco/keycloak/repo/authentication/KeycloakAuthenticationComponent.java b/repository/src/main/java/de/acosix/alfresco/keycloak/repo/authentication/KeycloakAuthenticationComponent.java
index 04f1616..914bb66 100644
--- a/repository/src/main/java/de/acosix/alfresco/keycloak/repo/authentication/KeycloakAuthenticationComponent.java
+++ b/repository/src/main/java/de/acosix/alfresco/keycloak/repo/authentication/KeycloakAuthenticationComponent.java
@@ -256,10 +256,6 @@ public class KeycloakAuthenticationComponent extends AbstractAuthenticationCompo
public RefreshableAccessTokenHolder checkAndRefreshTicketToken(final RefreshableAccessTokenHolder ticketToken)
throws AuthenticationException
{
- if (this.failExpiredTicketTokens && ticketToken.isExpired())
- {
- throw new AuthenticationException("Keycloak access token has expired - authentication ticket is no longer valid");
- }
RefreshableAccessTokenHolder result = null;
if (ticketToken.canRefresh() && ticketToken.shouldRefresh(this.deployment.getTokenMinimumTimeToLive()))
@@ -289,6 +285,10 @@ public class KeycloakAuthenticationComponent extends AbstractAuthenticationCompo
throw new AuthenticationException("Failed to refresh Keycloak authentication", ioex);
}
}
+ else if (this.failExpiredTicketTokens && ticketToken.isExpired())
+ {
+ throw new AuthenticationException("Keycloak access token has expired - authentication ticket is no longer valid");
+ }
if (result != null || !ticketToken.isExpired())
{
diff --git a/repository/src/main/java/de/acosix/alfresco/keycloak/repo/roles/NoOpRoleServiceImpl.java b/repository/src/main/java/de/acosix/alfresco/keycloak/repo/roles/NoOpRoleServiceImpl.java
index dca056c..e355dfe 100644
--- a/repository/src/main/java/de/acosix/alfresco/keycloak/repo/roles/NoOpRoleServiceImpl.java
+++ b/repository/src/main/java/de/acosix/alfresco/keycloak/repo/roles/NoOpRoleServiceImpl.java
@@ -27,6 +27,16 @@ import java.util.List;
public class NoOpRoleServiceImpl implements RoleService
{
+ /**
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ public List listRoles()
+ {
+ return Collections.emptyList();
+ }
+
/**
*
* {@inheritDoc}
@@ -37,6 +47,16 @@ public class NoOpRoleServiceImpl implements RoleService
return Collections.emptyList();
}
+ /**
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ public List listRoles(final boolean realmOnly)
+ {
+ return Collections.emptyList();
+ }
+
/**
*
* {@inheritDoc}
@@ -47,6 +67,16 @@ public class NoOpRoleServiceImpl implements RoleService
return Collections.emptyList();
}
+ /**
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ public List listRoles(final String resourceName)
+ {
+ return Collections.emptyList();
+ }
+
/**
*
* {@inheritDoc}
diff --git a/repository/src/main/java/de/acosix/alfresco/keycloak/repo/roles/RoleService.java b/repository/src/main/java/de/acosix/alfresco/keycloak/repo/roles/RoleService.java
index cd43b7b..a7547b1 100644
--- a/repository/src/main/java/de/acosix/alfresco/keycloak/repo/roles/RoleService.java
+++ b/repository/src/main/java/de/acosix/alfresco/keycloak/repo/roles/RoleService.java
@@ -25,6 +25,13 @@ import java.util.List;
public interface RoleService
{
+ /**
+ * Retrieves roles in the main realm and/or resource scopes (as far as possible based on configuration).
+ *
+ * @return the list of roles
+ */
+ List listRoles();
+
/**
* Finds matching roles in the main realm and/or resource scopes (as far as possible based on configuration).
*
@@ -35,6 +42,17 @@ public interface RoleService
*/
List findRoles(String shortNameFilter);
+ /**
+ * Retrieves roles in the main realm and/or resource scopes (as far as possible based on configuration).
+ *
+ * @param realmOnly
+ * {@code true} if the list operation should only consider the main realm, or {@code false} if both realm and resource
+ * scopes are allowed to be listed
+ *
+ * @return the list of roles
+ */
+ List listRoles(boolean realmOnly);
+
/**
* Finds matching roles in the main realm and/or resource scopes (as far as possible based on configuration).
*
@@ -48,6 +66,16 @@ public interface RoleService
*/
List findRoles(String shortNameFilter, boolean realmOnly);
+ /**
+ * Retrieves roles in a specific resource scope (as far as possible based on configuration).
+ *
+ * @param resourceName
+ * the name of the resource for which to retrieve roles
+ *
+ * @return the list of roles
+ */
+ List listRoles(String resourceName);
+
/**
* Finds matching roles in a specific resource scope (as far as possible based on configuration).
*
diff --git a/repository/src/main/java/de/acosix/alfresco/keycloak/repo/roles/RoleServiceImpl.java b/repository/src/main/java/de/acosix/alfresco/keycloak/repo/roles/RoleServiceImpl.java
index 51b8c99..26f081f 100644
--- a/repository/src/main/java/de/acosix/alfresco/keycloak/repo/roles/RoleServiceImpl.java
+++ b/repository/src/main/java/de/acosix/alfresco/keycloak/repo/roles/RoleServiceImpl.java
@@ -215,6 +215,16 @@ public class RoleServiceImpl implements InitializingBean, RoleService
this.hiddenMappedRoles = hiddenMappedRoles;
}
+ /**
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ public List listRoles()
+ {
+ return this.doFindRoles(null, false);
+ }
+
/**
*
* {@inheritDoc}
@@ -222,7 +232,18 @@ public class RoleServiceImpl implements InitializingBean, RoleService
@Override
public List findRoles(final String shortNameFilter)
{
- return this.findRoles(shortNameFilter, false);
+ ParameterCheck.mandatoryString("shortNameFilter", shortNameFilter);
+ return this.doFindRoles(shortNameFilter, false);
+ }
+
+ /**
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ public List listRoles(final boolean realmOnly)
+ {
+ return this.doFindRoles(null, realmOnly);
}
/**
@@ -231,6 +252,35 @@ public class RoleServiceImpl implements InitializingBean, RoleService
*/
@Override
public List findRoles(final String shortNameFilter, final boolean realmOnly)
+ {
+ ParameterCheck.mandatoryString("shortNameFilter", shortNameFilter);
+ return this.doFindRoles(shortNameFilter, realmOnly);
+ }
+
+ /**
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ public List listRoles(final String resourceName)
+ {
+ ParameterCheck.mandatory("resourceName", resourceName);
+ return this.doFindRoles(resourceName, null);
+ }
+
+ /**
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ public List findRoles(final String resourceName, final String shortNameFilter)
+ {
+ ParameterCheck.mandatory("resourceName", resourceName);
+ ParameterCheck.mandatoryString("shortNameFilter", shortNameFilter);
+ return this.doFindRoles(resourceName, shortNameFilter);
+ }
+
+ protected List doFindRoles(final String shortNameFilter, final boolean realmOnly)
{
final List roles;
@@ -254,7 +304,7 @@ public class RoleServiceImpl implements InitializingBean, RoleService
if (!realmOnly && this.processResourceRoles)
{
this.resourceRoleNameMapper.keySet().stream().forEach(resourceName -> {
- final List resourceRoles = this.findRoles(resourceName, shortNameFilter);
+ final List resourceRoles = this.doFindRoles(resourceName, shortNameFilter);
roles.addAll(resourceRoles);
});
}
@@ -275,15 +325,8 @@ public class RoleServiceImpl implements InitializingBean, RoleService
return roles;
}
- /**
- *
- * {@inheritDoc}
- */
- @Override
- public List findRoles(final String resourceName, final String shortNameFilter)
+ protected List doFindRoles(final String resourceName, final String shortNameFilter)
{
- ParameterCheck.mandatory("resourceName", resourceName);
-
List roles;
if (this.enabled && !this.processResourceRoles)
diff --git a/repository/src/main/java/de/acosix/alfresco/keycloak/repo/roles/ScriptRoleService.java b/repository/src/main/java/de/acosix/alfresco/keycloak/repo/roles/ScriptRoleService.java
new file mode 100644
index 0000000..e9d0bc6
--- /dev/null
+++ b/repository/src/main/java/de/acosix/alfresco/keycloak/repo/roles/ScriptRoleService.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2019 - 2020 Acosix GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package de.acosix.alfresco.keycloak.repo.roles;
+
+import java.util.List;
+
+import org.alfresco.repo.jscript.BaseScopableProcessorExtension;
+import org.alfresco.util.PropertyCheck;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.Scriptable;
+import org.springframework.beans.factory.InitializingBean;
+
+/**
+ * This service exposes mapped Keycloak roles to scripts running within the Repository-tier script processor, e.g. web script controllers.
+ *
+ * @author Axel Faust
+ */
+public class ScriptRoleService extends BaseScopableProcessorExtension implements InitializingBean
+{
+
+ protected RoleService roleService;
+
+ /**
+ *
+ * {@inheritDoc}
+ */
+ @Override
+ public void afterPropertiesSet()
+ {
+ PropertyCheck.mandatory(this, "roleService", this.roleService);
+ }
+
+ /**
+ * @param roleService
+ * the roleService to set
+ */
+ public void setRoleService(final RoleService roleService)
+ {
+ this.roleService = roleService;
+ }
+
+ /**
+ * Retrieves roles in the main realm and/or resource scopes (as far as possible based on configuration).
+ *
+ * @return the list of roles
+ */
+ public Scriptable listRoles()
+ {
+ final List roles = this.roleService.listRoles();
+ final Scriptable roleArray = this.makeRoleArray(roles);
+ return roleArray;
+ }
+
+ /**
+ * Finds matching roles in the main realm and/or resource scopes (as far as possible based on configuration).
+ *
+ * @param shortNameFilter
+ * name pattern on which to filter groups - the filter will be applied on both the original Keycloak and the mapped Alfresco
+ * role name, and a match in either will result the role to be considered a match
+ * @return the list of matching roles
+ */
+ public Scriptable findRoles(final String shortNameFilter)
+ {
+ final List roles = this.roleService.findRoles(shortNameFilter);
+ final Scriptable roleArray = this.makeRoleArray(roles);
+ return roleArray;
+ }
+
+ /**
+ * Retrieves roles in the main realm and/or resource scopes (as far as possible based on configuration).
+ *
+ * @param realmOnly
+ * {@code true} if the list operation should only consider the main realm, or {@code false} if both realm and resource
+ * scopes are allowed to be listed
+ *
+ * @return the list of roles
+ */
+ public Scriptable listRoles(final boolean realmOnly)
+ {
+ final List roles = this.roleService.listRoles(realmOnly);
+ final Scriptable roleArray = this.makeRoleArray(roles);
+ return roleArray;
+ }
+
+ /**
+ * Finds matching roles in the main realm and/or resource scopes (as far as possible based on configuration).
+ *
+ * @param shortNameFilter
+ * name pattern on which to filter groups - the filter will be applied on both the original Keycloak and the mapped Alfresco
+ * role name, and a match in either will result the role to be considered a match
+ * @param realmOnly
+ * {@code true} if the search operation should only consider the main realm, or {@code false} if both realm and resource
+ * scopes are allowed to be searched
+ * @return the list of matching roles
+ */
+ public Scriptable findRoles(final String shortNameFilter, final boolean realmOnly)
+ {
+ final List roles = this.roleService.findRoles(shortNameFilter, realmOnly);
+ final Scriptable roleArray = this.makeRoleArray(roles);
+ return roleArray;
+ }
+
+ /**
+ * Retrieves roles in a specific resource scope (as far as possible based on configuration).
+ *
+ * @param resourceName
+ * the name of the resource for which to retrieve roles
+ *
+ * @return the list of roles
+ */
+ public Scriptable listRoles(final String resourceName)
+ {
+ final List roles = this.roleService.listRoles(resourceName);
+ final Scriptable roleArray = this.makeRoleArray(roles);
+ return roleArray;
+ }
+
+ /**
+ * Finds matching roles in a specific resource scope (as far as possible based on configuration).
+ *
+ * @param resourceName
+ * the name of the resource for which to retrieve roles
+ * @param shortNameFilter
+ * name pattern on which to filter groups - the filter will be applied on both the original Keycloak and the mapped Alfresco
+ * role name, and a match in either will result the role to be considered a match
+ * @return the list of matching roles
+ */
+ public Scriptable findRoles(final String resourceName, final String shortNameFilter)
+ {
+ final List roles = this.roleService.findRoles(resourceName, shortNameFilter);
+ final Scriptable roleArray = this.makeRoleArray(roles);
+ return roleArray;
+ }
+
+ protected Scriptable makeRoleArray(final List roles)
+ {
+ final Scriptable sitesArray = Context.getCurrentContext().newArray(this.getScope(), roles.toArray(new Object[0]));
+ return sitesArray;
+ }
+}
diff --git a/repository/src/main/java/de/acosix/alfresco/keycloak/repo/web/scripts/RolesGet.java b/repository/src/main/java/de/acosix/alfresco/keycloak/repo/web/scripts/RolesGet.java
index e449407..4b9819f 100644
--- a/repository/src/main/java/de/acosix/alfresco/keycloak/repo/web/scripts/RolesGet.java
+++ b/repository/src/main/java/de/acosix/alfresco/keycloak/repo/web/scripts/RolesGet.java
@@ -103,7 +103,9 @@ public class RolesGet extends DeclarativeWebScript implements InitializingBean
maxItems = Integer.parseInt(maxItemsParam);
}
- final List roles = this.roleService.findRoles(shortNameFilterParam);
+ final List roles = shortNameFilterParam != null && !shortNameFilterParam.trim().isEmpty()
+ ? this.roleService.findRoles(shortNameFilterParam)
+ : this.roleService.listRoles();
if (roles.isEmpty())
{
diff --git a/repository/src/main/webscripts/de/acosix/keycloak/customisations/slingshot/documentlibrary/permissions.get.js b/repository/src/main/webscripts/de/acosix/keycloak/customisations/slingshot/documentlibrary/permissions.get.js
new file mode 100644
index 0000000..cbae554
--- /dev/null
+++ b/repository/src/main/webscripts/de/acosix/keycloak/customisations/slingshot/documentlibrary/permissions.get.js
@@ -0,0 +1,57 @@
+/* global keycloakRoles: false */
+
+var keycloakRolesHash;
+
+function process(permissions)
+{
+ var idx, permissionObj, authority, keycloakRolesArr, jdx, role;
+
+ for (idx = 0; idx < permissions.length; idx++)
+ {
+ permissionObj = permissions[idx];
+ authority = permissionObj.authority.name;
+
+ if (authority && /^ROLE_.+$/.test(authority))
+ {
+ // lazy init
+ if (!keycloakRolesHash)
+ {
+ keycloakRolesArr = keycloakRoles.listRoles();
+ keycloakRolesHash = {};
+ for (jdx = 0; jdx < keycloakRolesArr.length; jdx++)
+ {
+ keycloakRolesHash[keycloakRolesArr[jdx].name] = keycloakRolesArr[jdx];
+ }
+ }
+
+ // only process if role mapped from Keycloak
+ if (keycloakRolesHash.hasOwnProperty(authority))
+ {
+ role = keycloakRolesHash[authority];
+ if (role)
+ {
+ // enhance permissionObj.authority to at least add displayName
+ // may/will still look like a user in UI which only differentiates groups / users
+ permissionObj.authority = {
+ name : authority,
+ fullName : authority,
+ shortName : authority.substring(5),
+ displayName : role.description || role.keycloakName
+ };
+ }
+ }
+ }
+ }
+}
+
+function main()
+{
+ var permissions = model.data;
+
+ process(permissions.direct);
+ process(permissions.inherited);
+
+ model.data = permissions;
+}
+
+main();
diff --git a/repository/src/main/webscripts/de/acosix/keycloak/customisations/slingshot/documentlibrary/permissions.post.json.js b/repository/src/main/webscripts/de/acosix/keycloak/customisations/slingshot/documentlibrary/permissions.post.json.js
new file mode 100644
index 0000000..6250f21
--- /dev/null
+++ b/repository/src/main/webscripts/de/acosix/keycloak/customisations/slingshot/documentlibrary/permissions.post.json.js
@@ -0,0 +1,44 @@
+/* global keycloakRoles: false */
+function main()
+{
+ var nodeRef, node, permissions, idx, permissionObj, add, authority, permission, keycloakRolesHash, keycloakRolesArr, jdx;
+
+ nodeRef = url.templateArgs.store_type + '://' + url.templateArgs.store_id + '/' + url.templateArgs.id;
+ // normally not a fan of Alfresco utils object, but needed here for consistency with base script (there via parse-args.lib.js import)
+ node = utils.resolveNodeReference(nodeRef);
+
+ permissions = json.getJSONArray('permissions');
+ for (idx = 0; idx < permissions.length(); idx++)
+ {
+ permissionObj = permissions.getJSONObject(idx);
+ add = !permissionObj.has('remove') || !permissionObj.getBoolean('remove');
+
+ if (add)
+ {
+ authority = permissionObj.getString('authority');
+ permission = permissionObj.getString('role');
+
+ if (/^ROLE_.+$/.test(authority))
+ {
+ // lazy init
+ if (!keycloakRolesHash)
+ {
+ keycloakRolesArr = keycloakRoles.listRoles();
+ keycloakRolesHash = {};
+ for (jdx = 0; jdx < keycloakRolesArr.length; jdx++)
+ {
+ keycloakRolesHash[keycloakRolesArr[jdx].name] = true;
+ }
+ }
+
+ // only process if role mapped from Keycloak
+ if (keycloakRolesHash.hasOwnProperty(authority))
+ {
+ node.setPermission(permission, authority);
+ }
+ }
+ }
+ }
+}
+
+main();
diff --git a/share/pom.xml b/share/pom.xml
index 150b686..c1ba75c 100644
--- a/share/pom.xml
+++ b/share/pom.xml
@@ -88,6 +88,12 @@
installable
+
+ de.acosix.alfresco.utility
+ de.acosix.alfresco.utility.repo
+ installable
+
+
de.acosix.alfresco.utility
de.acosix.alfresco.utility.core.share
diff --git a/share/src/main/java/de/acosix/alfresco/keycloak/share/web/KeycloakAuthenticationFilter.java b/share/src/main/java/de/acosix/alfresco/keycloak/share/web/KeycloakAuthenticationFilter.java
index 909df8c..ff0e2b3 100644
--- a/share/src/main/java/de/acosix/alfresco/keycloak/share/web/KeycloakAuthenticationFilter.java
+++ b/share/src/main/java/de/acosix/alfresco/keycloak/share/web/KeycloakAuthenticationFilter.java
@@ -392,6 +392,10 @@ public class KeycloakAuthenticationFilter implements DependencyInjectedFilter, I
this.continueFilterChain(context, request, response, chain);
}
+ else if (res.isCommitted())
+ {
+ LOGGER.debug("Response has already been committed by skip condition-check - not processing it any further");
+ }
else
{
this.processKeycloakAuthenticationAndActions(context, req, res, chain);
@@ -819,34 +823,34 @@ public class KeycloakAuthenticationFilter implements DependencyInjectedFilter, I
if (!this.externalAuthEnabled || !this.filterEnabled)
{
- LOGGER.debug("Skipping doFilter as filter and/or external authentication are not enabled");
+ LOGGER.debug("Skipping processKeycloakAuthenticationAndActions as filter and/or external authentication are not enabled");
skip = true;
}
else if (this.keycloakDeployment == null)
{
- LOGGER.debug("Skipping doFilter as Keycloak adapter was not properly initialised");
+ LOGGER.debug("Skipping processKeycloakAuthenticationAndActions as Keycloak adapter was not properly initialised");
skip = true;
}
else if (servletRequestUri.matches(KEYCLOAK_ACTION_URL_PATTERN))
{
- LOGGER.debug("Explicitly not skipping doFilter as Keycloak action URL is being called");
+ LOGGER.debug("Explicitly not skipping processKeycloakAuthenticationAndActions as Keycloak action URL is being called");
}
else if (req.getParameter("state") != null && req.getParameter("code") != null && this.hasStateCookie(req))
{
LOGGER.debug(
- "Explicitly not skipping doFilter as state and code query parameters of OAuth2 redirect as well as state cookie are present");
+ "Explicitly not skipping processKeycloakAuthenticationAndActions as state and code query parameters of OAuth2 redirect as well as state cookie are present");
}
else if (authHeader != null && authHeader.toLowerCase(Locale.ENGLISH).startsWith("bearer "))
{
- LOGGER.debug("Explicitly not skipping doFilter as Bearer authorization header is present");
+ LOGGER.debug("Explicitly not skipping processKeycloakAuthenticationAndActions as Bearer authorization header is present");
}
else if (authHeader != null && authHeader.toLowerCase(Locale.ENGLISH).startsWith("basic "))
{
- LOGGER.debug("Explicitly not skipping doFilter as Basic authorization header is present");
+ LOGGER.debug("Explicitly not skipping processKeycloakAuthenticationAndActions as Basic authorization header is present");
}
else if (authHeader != null)
{
- LOGGER.debug("Skipping doFilter as non-OIDC / non-Basic authorization header is present");
+ LOGGER.debug("Skipping processKeycloakAuthenticationAndActions as non-OIDC / non-Basic authorization header is present");
skip = true;
}
else if (currentSession != null && AuthenticationUtil.isAuthenticated(req))
@@ -858,7 +862,11 @@ public class KeycloakAuthenticationFilter implements DependencyInjectedFilter, I
}
else
{
- LOGGER.debug("Skipping doFilter as non-Keycloak-authenticated session is already established");
+ // TODO Validate via custom /touch to check if session is still valid
+ // custom => handle potential 302 instead of 401 response from Keycloak-enabled backend
+ // custom => deal with redirect host being unknown (similar to our auth-server-url vs. directAuthHost case)
+ LOGGER.debug(
+ "Skipping processKeycloakAuthenticationAndActions as non-Keycloak-authenticated session is already established");
skip = true;
}
}
@@ -868,26 +876,26 @@ public class KeycloakAuthenticationFilter implements DependencyInjectedFilter, I
final String noauth = proxyMatcher.group(2);
if (noauth != null && !noauth.trim().isEmpty())
{
- LOGGER.debug("Skipping doFilter as proxy servlet to noauth endpoint {} is being called");
+ LOGGER.debug("Skipping processKeycloakAuthenticationAndActions as proxy servlet to noauth endpoint {} is being called");
skip = true;
}
else if (!endpoint.equals(this.primaryEndpoint)
&& (this.secondaryEndpoints == null || !this.secondaryEndpoints.contains(endpoint)))
{
LOGGER.debug(
- "Skipping doFilter on proxy servlet call as endpoint {} has not been configured as a primary / secondary endpoint to handle");
+ "Skipping processKeycloakAuthenticationAndActions on proxy servlet call as endpoint {} has not been configured as a primary / secondary endpoint to handle");
skip = true;
}
}
else if (PAGE_SERVLET_PATH.equals(servletPath) && (LOGIN_PATH_INFORMATION.equals(pathInfo)
|| (pathInfo == null && LOGIN_PAGE_TYPE_PARAMETER_VALUE.equals(req.getParameter(PAGE_TYPE_PARAMETER_NAME)))))
{
- LOGGER.debug("Skipping doFilter as login page was explicitly requested");
+ LOGGER.debug("Skipping processKeycloakAuthenticationAndActions as login page was explicitly requested");
skip = true;
}
else if (this.isNoAuthPage(req))
{
- LOGGER.debug("Skipping doFilter as requested page does not require authentication");
+ LOGGER.debug("Skipping processKeycloakAuthenticationAndActions as requested page does not require authentication");
skip = true;
}
@@ -928,7 +936,7 @@ public class KeycloakAuthenticationFilter implements DependencyInjectedFilter, I
boolean skip = false;
if (currentSession != null)
{
- LOGGER.debug("Skipping doFilter as Keycloak-authentication session is still valid");
+ LOGGER.debug("Skipping processKeycloakAuthenticationAndActions as Keycloak-authentication session is still valid");
skip = true;
if (keycloakAccount instanceof OidcKeycloakAccount)
diff --git a/share/src/main/site-webscripts/de/acosix/keycloak/customisations/components/manage-permissions/manage-permissions.get.html.ftl b/share/src/main/site-webscripts/de/acosix/keycloak/customisations/components/manage-permissions/manage-permissions.get.html.ftl
new file mode 100644
index 0000000..77bf5f9
--- /dev/null
+++ b/share/src/main/site-webscripts/de/acosix/keycloak/customisations/components/manage-permissions/manage-permissions.get.html.ftl
@@ -0,0 +1,3 @@
+<@markup id="keycloak-js" target="js" action="after">
+ <@script src="${url.context}/res/acosix/keycloak/components/manage-permissions/manage-permissions.js" group="manage-permissions"/>
+@>
\ No newline at end of file
diff --git a/share/src/main/site-webscripts/de/acosix/keycloak/customisations/components/people-finder/authority-query.get.js b/share/src/main/site-webscripts/de/acosix/keycloak/customisations/components/people-finder/authority-query.get.js
new file mode 100644
index 0000000..9822cfe
--- /dev/null
+++ b/share/src/main/site-webscripts/de/acosix/keycloak/customisations/components/people-finder/authority-query.get.js
@@ -0,0 +1,45 @@
+function main()
+{
+ var requestedAuthorityType, filter, maxResults, url, response, responseObj, authorities, idx;
+
+ requestedAuthorityType = args.authorityType ? String(args.authorityType).toLowerCase() : 'all';
+ filter = args.filter ? String(args.filter) : null;
+ maxResults = args.maxResults ? parseInt(String(args.maxResults)) : 0;
+
+ if (requestedAuthorityType === 'all')
+ {
+ url = '/acosix/api/keycloak/roles';
+ if (maxResults > 0)
+ {
+ url += '?maxItems=' + maxResults;
+ }
+ if (filter)
+ {
+ url += url.indexOf('?') === -1 ? '?' : '&';
+ url += 'shortNameFilter=' + encodeURIComponent(filter);
+ }
+ response = remote.call(url);
+ if (response.status.code === 200)
+ {
+ responseObj = JSON.parse(response.text);
+ authorities = model.authorities;
+
+ for (idx = 0; idx < responseObj.data.length; idx++)
+ {
+ // UI likely cannot handle authorityType ROLE, which would be semantically correct
+ authorities.push({
+ authorityType : 'GROUP',
+ shortName : responseObj.data[idx].shortName,
+ fullName : responseObj.data[idx].fullName,
+ displayName : responseObj.data[idx].displayName,
+ description : responseObj.data[idx].fullName,
+ metadata : {}
+ });
+ }
+
+ model.authorities = authorities;
+ }
+ }
+}
+
+main();
diff --git a/share/src/main/webapp/.gitkeep b/share/src/main/webapp/.gitkeep
deleted file mode 100644
index e69de29..0000000
diff --git a/share/src/main/webapp/acosix/keycloak/components/manage-permissions/manage-permissions.js b/share/src/main/webapp/acosix/keycloak/components/manage-permissions/manage-permissions.js
new file mode 100644
index 0000000..b7bcc34
--- /dev/null
+++ b/share/src/main/webapp/acosix/keycloak/components/manage-permissions/manage-permissions.js
@@ -0,0 +1,35 @@
+(function()
+{
+ var Dom = YAHOO.util.Dom;
+
+ if (Alfresco.component.ManagePermissions)
+ {
+ Alfresco.component.ManagePermissions.prototype.fnRenderCellAuthorityIcon = function Acosix_Keycloak_ManagePermissions_fnRenderCellAuthorityIcon()
+ {
+ return function Acosix_Keycloak_ManagePermissions_renderCellAuthorityIcon(elCell, oRecord, oColumn)
+ {
+ var authority, isGroupLike, iconUrl;
+
+ Dom.setStyle(elCell, 'width', oColumn.width + 'px');
+ Dom.setStyle(elCell.parentNode, 'width', oColumn.width + 'px');
+
+ authority = oRecord.getData('authority');
+ // main modification - treat ROLE just like a group, because any number of users can belong to a role
+ isGroupLike = /^(GROUP|ROLE)_.*/.test(authority.name);
+ // end main modification
+ iconUrl = Alfresco.constants.URL_RESCONTEXT + 'components/images/' + (isGroupLike ? 'group' : 'no-user-photo') + '-64.png';
+
+ if (authority.avatar && authority.avatar.length !== 0)
+ {
+ iconUrl = Alfresco.constants.PROXY_URI + authority.avatar + '?c=queue&ph=true';
+ }
+ else if (authority.iconUrl)
+ {
+ // As passed-back from the Authority Finder component
+ iconUrl = authority.iconUrl;
+ }
+ elCell.innerHTML = '
';
+ };
+ };
+ }
+}());
diff --git a/share/src/test/docker/alfresco/web-extension/share-config-custom.xml b/share/src/test/docker/alfresco/web-extension/share-config-custom.xml
index 4570606..68c43a2 100644
--- a/share/src/test/docker/alfresco/web-extension/share-config-custom.xml
+++ b/share/src/test/docker/alfresco/web-extension/share-config-custom.xml
@@ -21,18 +21,10 @@
alfrescoCookie
Alfresco Connector
- Connects to an Alfresco instance using cookie-based authentication
+ Connects to an Alfresco instance using cookie-based authentication and awareness of OIDC bearer tokens
de.acosix.alfresco.keycloak.share.remote.BearerTokenAwareSlingshotAlfrescoConnector
-
- alfrescoHeader
- Alfresco Connector
- Connects to an Alfresco instance using header and cookie-based authentication
- de.acosix.alfresco.keycloak.share.remote.BearerTokenAwareSlingshotAlfrescoConnector
- SsoUserHeader
-
-
alfresco
Alfresco - user access
@@ -47,7 +39,7 @@
alfresco-feed
Alfresco Feed
Alfresco Feed - supports basic HTTP authentication via the EndPointProxyServlet
- alfrescoHeader
+ alfrescoCookie
http://repository:8080/alfresco/wcs
true
user
@@ -58,10 +50,8 @@
alfresco-api
alfresco
Alfresco Public API - user access
- Access to Alfresco Repository Public API that require user authentication.
- This makes use of the authentication that is provided by parent 'alfresco' endpoint.
-
- alfrescoHeader
+ Access to Alfresco Repository Public API that require user authentication. This makes use of the authentication that is provided by parent 'alfresco' endpoint.
+ alfrescoCookie
http://repository:8080/alfresco/api
user
true
diff --git a/share/src/test/docker/repository-it.xml b/share/src/test/docker/repository-it.xml
index 1d203a3..62e2e4c 100644
--- a/share/src/test/docker/repository-it.xml
+++ b/share/src/test/docker/repository-it.xml
@@ -70,6 +70,8 @@
de.acosix.alfresco.utility:de.acosix.alfresco.utility.core.repo.quartz2:*
${project.groupId}:de.acosix.alfresco.keycloak.repo.deps:*
de.acosix.alfresco.utility:de.acosix.alfresco.utility.core.repo:jar:installable:*
+
+ de.acosix.alfresco.utility:de.acosix.alfresco.utility.repo:jar:installable:*
${project.groupId}:de.acosix.alfresco.keycloak.repo:jar:installable:*
test