diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index cb57a38435..229b542ccf 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -385,18 +385,24 @@ jobs:
pom-dir: tas-email
- test-name: "WebDAV TAS tests"
pom-dir: tas-webdav
- - test-name: "Integration TAS tests"
+ - test-name: "Integration TAS tests (Java 17)"
pom-dir: tas-integration
+ - test-name: "Integration TAS tests (Java 11)"
+ pom-dir: tas-integration
+ jre-version: 11
env:
REQUIRES_LOCAL_IMAGES: true
steps:
- uses: actions/checkout@v3
- uses: Alfresco/alfresco-build-tools/.github/actions/get-build-info@v1.33.0
- uses: Alfresco/alfresco-build-tools/.github/actions/setup-java-build@v1.33.0
+ with:
+ java-version: ${{ matrix.jre-version || '17' }}
- name: "Build"
timeout-minutes: ${{ fromJSON(env.GITHUB_ACTIONS_DEPLOY_TIMEOUT) }}
run: |
bash ./scripts/ci/init.sh
+ export BUILD_OPTIONS="-Ddocker.buildArg.JRE_VERSION=${{ matrix.jre-version }} ${BUILD_OPTIONS}"
bash ./scripts/ci/build.sh
- name: "Set up the environment"
run: |
diff --git a/amps/ags/rm-community/rm-community-repo/.env b/amps/ags/rm-community/rm-community-repo/.env
index 443245722d..7cfb90ba9a 100644
--- a/amps/ags/rm-community/rm-community-repo/.env
+++ b/amps/ags/rm-community/rm-community-repo/.env
@@ -1,3 +1,3 @@
SOLR6_TAG=2.0.7-A5
POSTGRES_TAG=14.4
-ACTIVEMQ_TAG=5.17.1-jre11-rockylinux8
+ACTIVEMQ_TAG=5.17.6-jre17-rockylinux8
diff --git a/amps/ags/rm-community/rm-community-repo/pom.xml b/amps/ags/rm-community/rm-community-repo/pom.xml
index 688c91c843..0c9508020d 100644
--- a/amps/ags/rm-community/rm-community-repo/pom.xml
+++ b/amps/ags/rm-community/rm-community-repo/pom.xml
@@ -436,7 +436,7 @@
- alfresco/alfresco-activemq:${dependency.activemq.version}-jre11-rockylinux8
+ alfresco/alfresco-activemq:${dependency.activemq.version}-jre17-rockylinux8
${activemq.port1}:${activemq.port1}
@@ -507,7 +507,7 @@
- alfresco/alfresco-activemq:${dependency.activemq.version}-jre11-rockylinux8
+ alfresco/alfresco-activemq:${dependency.activemq.version}-jre17-rockylinux8
${activemq.port1}:${activemq.port1}
diff --git a/packaging/docker-alfresco/Dockerfile b/packaging/docker-alfresco/Dockerfile
index c7740d42b4..9b8c0a0999 100644
--- a/packaging/docker-alfresco/Dockerfile
+++ b/packaging/docker-alfresco/Dockerfile
@@ -1,6 +1,8 @@
+ARG JRE_VERSION=17
+
# Fetch image based on Tomcat 9.0, Java 17 and Rocky Linux 8
# More infos about this image: https://github.com/Alfresco/alfresco-docker-base-tomcat
-FROM alfresco/alfresco-base-tomcat:tomcat9-jre17-rockylinux8-202303081618
+FROM alfresco/alfresco-base-tomcat:tomcat9-jre${JRE_VERSION}-rockylinux8-202303081618
# Set default docker_context.
ARG resource_path=target
diff --git a/packaging/tests/environment/.env b/packaging/tests/environment/.env
index 443245722d..7cfb90ba9a 100644
--- a/packaging/tests/environment/.env
+++ b/packaging/tests/environment/.env
@@ -1,3 +1,3 @@
SOLR6_TAG=2.0.7-A5
POSTGRES_TAG=14.4
-ACTIVEMQ_TAG=5.17.1-jre11-rockylinux8
+ACTIVEMQ_TAG=5.17.6-jre17-rockylinux8
diff --git a/pom.xml b/pom.xml
index b8fd140a5e..01d15ef2ff 100644
--- a/pom.xml
+++ b/pom.xml
@@ -62,11 +62,11 @@
2.15.0-rc1
3.5.5
1.0.0
- 8.44
+ 8.47
1.70
4.9.0
3.24.2
- 20230227
+ 20231013
2.9.0
2.11.0
2.8.9
@@ -88,7 +88,7 @@
4.1.87.Final
4.1.82.Final
2.0.56.Final
- 5.17.4
+ 5.17.6
1.22
1.2.5
4.2.0
@@ -111,8 +111,8 @@
2.5.0
1.1.4
- 3.4.0
- 1.6.0
+ 3.4.2
+ 1.6.2
7.4.0
2.2.0
diff --git a/remote-api/src/main/java/org/alfresco/rest/api/NetworkWebScriptGet.java b/remote-api/src/main/java/org/alfresco/rest/api/NetworkWebScriptGet.java
index c1a17aa8ab..71c406b79a 100644
--- a/remote-api/src/main/java/org/alfresco/rest/api/NetworkWebScriptGet.java
+++ b/remote-api/src/main/java/org/alfresco/rest/api/NetworkWebScriptGet.java
@@ -109,11 +109,11 @@ public class NetworkWebScriptGet extends ApiWebScript implements ResponseWriter
}
catch (ApiException | WebScriptException apiException)
{
- renderException(apiException, res, assistant);
+ renderException(apiException, res, req, assistant);
}
catch (RuntimeException runtimeException)
{
- renderException(runtimeException, res, assistant);
+ renderException(runtimeException, res, req, assistant);
}
}
}
diff --git a/remote-api/src/main/java/org/alfresco/rest/api/NetworksWebScriptGet.java b/remote-api/src/main/java/org/alfresco/rest/api/NetworksWebScriptGet.java
index e5f4183060..0c82fb2f0e 100644
--- a/remote-api/src/main/java/org/alfresco/rest/api/NetworksWebScriptGet.java
+++ b/remote-api/src/main/java/org/alfresco/rest/api/NetworksWebScriptGet.java
@@ -118,11 +118,11 @@ public class NetworksWebScriptGet extends ApiWebScript implements RecognizedPara
}
catch (ApiException | WebScriptException apiException)
{
- renderException(apiException, res, assistant);
+ renderException(apiException, res, req, assistant);
}
catch (RuntimeException runtimeException)
{
- renderException(runtimeException, res, assistant);
+ renderException(runtimeException, res, req, assistant);
}
}
}
\ No newline at end of file
diff --git a/remote-api/src/main/java/org/alfresco/rest/api/PublicApiTenantWebScriptServletRuntime.java b/remote-api/src/main/java/org/alfresco/rest/api/PublicApiTenantWebScriptServletRuntime.java
index 8df1321ea1..1334e44d70 100644
--- a/remote-api/src/main/java/org/alfresco/rest/api/PublicApiTenantWebScriptServletRuntime.java
+++ b/remote-api/src/main/java/org/alfresco/rest/api/PublicApiTenantWebScriptServletRuntime.java
@@ -143,7 +143,7 @@ public class PublicApiTenantWebScriptServletRuntime extends TenantWebScriptServl
else
{
try {
- renderException((Exception)exception, response, apiAssistant);
+ renderException((Exception)exception, response, request, apiAssistant);
} catch (IOException e) {
logger.error("Internal error", e);
throw new WebScriptException("Internal error", e);
diff --git a/remote-api/src/main/java/org/alfresco/rest/api/search/SearchApiWebscript.java b/remote-api/src/main/java/org/alfresco/rest/api/search/SearchApiWebscript.java
index c8fe871ddf..8ea59fa1e0 100644
--- a/remote-api/src/main/java/org/alfresco/rest/api/search/SearchApiWebscript.java
+++ b/remote-api/src/main/java/org/alfresco/rest/api/search/SearchApiWebscript.java
@@ -108,7 +108,7 @@ public class SearchApiWebscript extends AbstractWebScript implements RecognizedP
renderJsonResponse(webScriptResponse, toRender, assistant.getJsonHelper());
} catch (Exception exception) {
- renderException(exception,webScriptResponse,assistant);
+ renderException(exception,webScriptResponse,webScriptRequest,assistant);
}
}
diff --git a/remote-api/src/main/java/org/alfresco/rest/api/search/SearchSQLApiWebscript.java b/remote-api/src/main/java/org/alfresco/rest/api/search/SearchSQLApiWebscript.java
index d4b1452031..a783286d3b 100644
--- a/remote-api/src/main/java/org/alfresco/rest/api/search/SearchSQLApiWebscript.java
+++ b/remote-api/src/main/java/org/alfresco/rest/api/search/SearchSQLApiWebscript.java
@@ -102,11 +102,11 @@ public class SearchSQLApiWebscript extends AbstractWebScript implements Recogniz
{
if (exception instanceof QueryParserException)
{
- renderException(exception,res,assistant);
+ renderException(exception,res,webScriptRequest,assistant);
}
else
{
- renderException(new WebScriptException(400, exception.getMessage()), res, assistant);
+ renderException(new WebScriptException(400, exception.getMessage()), res, webScriptRequest, assistant);
}
}
}
diff --git a/remote-api/src/main/java/org/alfresco/rest/framework/tools/ResponseWriter.java b/remote-api/src/main/java/org/alfresco/rest/framework/tools/ResponseWriter.java
index baa4a0282b..cef2358de9 100644
--- a/remote-api/src/main/java/org/alfresco/rest/framework/tools/ResponseWriter.java
+++ b/remote-api/src/main/java/org/alfresco/rest/framework/tools/ResponseWriter.java
@@ -26,37 +26,34 @@
package org.alfresco.rest.framework.tools;
-import com.fasterxml.jackson.core.JsonGenerationException;
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.databind.JsonMappingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import org.alfresco.rest.framework.Api;
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.rest.framework.core.exceptions.DefaultExceptionResolver;
import org.alfresco.rest.framework.core.exceptions.ErrorResponse;
-import org.alfresco.rest.framework.core.exceptions.UnsupportedResourceOperationException;
-import org.alfresco.rest.framework.jacksonextensions.BeanPropertiesFilter;
-import org.alfresco.rest.framework.jacksonextensions.ExecutionResult;
import org.alfresco.rest.framework.jacksonextensions.JacksonHelper;
-import org.alfresco.rest.framework.resource.SerializablePagedCollection;
-import org.alfresco.rest.framework.resource.content.BinaryResource;
import org.alfresco.rest.framework.resource.content.ContentInfo;
import org.alfresco.rest.framework.resource.content.ContentInfoImpl;
-import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
-import org.alfresco.rest.framework.resource.parameters.Params;
import org.alfresco.rest.framework.webscripts.WithResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.simple.JSONObject;
-import org.springframework.beans.BeanUtils;
import org.springframework.extensions.surf.util.I18NUtil;
-import org.springframework.extensions.webscripts.*;
+import org.springframework.extensions.webscripts.Cache;
+import org.springframework.extensions.webscripts.Description;
+import org.springframework.extensions.webscripts.Format;
+import org.springframework.extensions.webscripts.Status;
+import org.springframework.extensions.webscripts.WebScriptRequest;
+import org.springframework.extensions.webscripts.WebScriptResponse;
+import org.springframework.extensions.webscripts.WrappingWebScriptResponse;
import org.springframework.extensions.webscripts.servlet.WebScriptServletResponse;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
+import com.fasterxml.jackson.core.JsonGenerationException;
+import com.fasterxml.jackson.core.JsonGenerator;
+import com.fasterxml.jackson.databind.JsonMappingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
/*
* Writes to the response
@@ -193,24 +190,52 @@ public interface ResponseWriter
default void renderErrorResponse(final ErrorResponse errorResponse, final WebScriptResponse res, final JacksonHelper jsonHelper)
throws IOException
{
+ renderErrorResponse(errorResponse, res, null, jsonHelper);
+ }
- String logId = "";
-
- if (Status.STATUS_INTERNAL_SERVER_ERROR == errorResponse.getStatusCode() || resWriterLogger().isDebugEnabled())
- {
- logId = org.alfresco.util.GUID.generate();
- resWriterLogger().error(logId + " : " + errorResponse.getStackTrace());
- }
-
+ /**
+ * Renders a JSON error response
+ *
+ * @param errorResponse The error
+ * @param res web script response
+ * @param req web script request
+ * @throws IOException
+ */
+ default void renderErrorResponse(final ErrorResponse errorResponse, final WebScriptResponse res, final WebScriptRequest req,
+ final JacksonHelper jsonHelper) throws IOException
+ {
String stackMessage = I18NUtil.getMessage(DefaultExceptionResolver.STACK_MESSAGE_ID);
+ String logId = org.alfresco.util.GUID.generate();
final ErrorResponse errorToWrite = new ErrorResponse(errorResponse.getErrorKey(), errorResponse.getStatusCode(),
- errorResponse.getBriefSummary(), stackMessage, logId, errorResponse.getAdditionalState(), DefaultExceptionResolver.ERROR_URL);
+ errorResponse.getBriefSummary(), stackMessage, logId, errorResponse.getAdditionalState(),
+ DefaultExceptionResolver.ERROR_URL);
+
+ String reqUrl = (req != null) ? req.getURL() : "unknown";
+ String userName = AuthenticationUtil.getFullyAuthenticatedUser() != null ? AuthenticationUtil.getFullyAuthenticatedUser()
+ : "unauthenticated user";
+
+ // If internal server error or class in debug then print the stack trace
+ if (Status.STATUS_INTERNAL_SERVER_ERROR == errorResponse.getStatusCode() || resWriterLogger().isDebugEnabled())
+ {
+ resWriterLogger().error("Exception " + errorToWrite.getLogId() + ". Request " + reqUrl + " executed by " + userName
+ + " returned status code " + errorResponse.getStatusCode() + " with message: "
+ + errorResponse.getBriefSummary() + " - Stack Trace: " + errorResponse.getStackTrace());
+ }
+ else
+ {
+ resWriterLogger().error("Exception " + errorToWrite.getLogId() + ". Request " + reqUrl + " executed by user "
+ + userName + " returned status code " + errorResponse.getStatusCode() + " with message: "
+ + errorResponse.getBriefSummary() + " - Increase logging on " + this.getClass().getName()
+ + " for stack trace.");
+ }
setContentInfoOnResponse(res, DEFAULT_JSON_CONTENT);
- // Status must be set before the response is written by Jackson (which will by default close and commit the response).
- // In a r/w txn, web script buffered responses ensure that it doesn't really matter but for r/o txns this is important.
+ // Status must be set before the response is written by Jackson (which will by default close and commit the
+ // response).
+ // In a r/w txn, web script buffered responses ensure that it doesn't really matter but for r/o txns this is
+ // important.
res.setStatus(errorToWrite.getStatusCode());
jsonHelper.withWriter(res.getOutputStream(), new JacksonHelper.Writer()
@@ -218,7 +243,7 @@ public interface ResponseWriter
@SuppressWarnings("unchecked")
@Override
public void writeContents(JsonGenerator generator, ObjectMapper objectMapper)
- throws JsonGenerationException, JsonMappingException, IOException
+ throws JsonGenerationException, JsonMappingException, IOException
{
JSONObject obj = new JSONObject();
obj.put("error", errorToWrite);
@@ -236,7 +261,21 @@ public interface ResponseWriter
*/
default void renderException(final Exception exception, final WebScriptResponse response, final ApiAssistant assistant) throws IOException
{
- renderErrorResponse(assistant.resolveException(exception), response, assistant.getJsonHelper());
+ renderException(exception, response, null, assistant);
+ }
+
+ /**
+ * Renders an exception to the output stream as Json.
+ *
+ * @param exception
+ * @param response
+ * @param request
+ * @throws IOException
+ */
+ default void renderException(final Exception exception, final WebScriptResponse response, final WebScriptRequest request,
+ final ApiAssistant assistant) throws IOException
+ {
+ renderErrorResponse(assistant.resolveException(exception), response, request, assistant.getJsonHelper());
}
/**
diff --git a/remote-api/src/main/java/org/alfresco/rest/framework/webscripts/AbstractResourceWebScript.java b/remote-api/src/main/java/org/alfresco/rest/framework/webscripts/AbstractResourceWebScript.java
index bb2a547ab9..32c0180c9c 100644
--- a/remote-api/src/main/java/org/alfresco/rest/framework/webscripts/AbstractResourceWebScript.java
+++ b/remote-api/src/main/java/org/alfresco/rest/framework/webscripts/AbstractResourceWebScript.java
@@ -180,15 +180,15 @@ public abstract class AbstractResourceWebScript extends ApiWebScript implements
}
catch (ContentIOException cioe)
{
- handleContentIOException(res, cioe);
+ handleContentIOException(res, req, cioe);
}
catch (AlfrescoRuntimeException | ApiException | WebScriptException xception )
{
- renderException(xception, res, assistant);
+ renderException(xception, res, req, assistant);
}
catch (RuntimeException runtimeException)
{
- renderException(runtimeException, res, assistant);
+ renderException(runtimeException, res, req, assistant);
}
finally
{
@@ -224,17 +224,17 @@ public abstract class AbstractResourceWebScript extends ApiWebScript implements
return toReturn;
}
- private void handleContentIOException(final WebScriptResponse res, ContentIOException exception) throws IOException
+ private void handleContentIOException(final WebScriptResponse res, final WebScriptRequest req, ContentIOException exception) throws IOException
{
// If the Content-Length is not set back to -1 any client will expect to receive binary and will hang until it times out
res.setHeader(HEADER_CONTENT_LENGTH, String.valueOf(-1));
if (exception instanceof ArchivedIOException)
{
- renderException(new ArchivedContentException(exception.getMsgId(), exception), res, assistant);
+ renderException(new ArchivedContentException(exception.getMsgId(), exception), res, req, assistant);
}
else
{
- renderException(exception, res, assistant);
+ renderException(exception, res, req, assistant);
}
}
diff --git a/repository/src/main/java/org/alfresco/repo/security/authentication/identityservice/IdentityServiceFacadeFactoryBean.java b/repository/src/main/java/org/alfresco/repo/security/authentication/identityservice/IdentityServiceFacadeFactoryBean.java
index 856c4c2942..bb2809669d 100644
--- a/repository/src/main/java/org/alfresco/repo/security/authentication/identityservice/IdentityServiceFacadeFactoryBean.java
+++ b/repository/src/main/java/org/alfresco/repo/security/authentication/identityservice/IdentityServiceFacadeFactoryBean.java
@@ -57,6 +57,7 @@ import com.nimbusds.jose.proc.JWSVerificationKeySelector;
import com.nimbusds.jose.proc.SecurityContext;
import com.nimbusds.jose.util.ResourceRetriever;
import com.nimbusds.jwt.proc.ConfigurableJWTProcessor;
+import com.nimbusds.oauth2.sdk.id.Issuer;
import com.nimbusds.openid.connect.sdk.op.OIDCProviderMetadata;
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacade.IdentityServiceFacadeException;
@@ -88,7 +89,9 @@ import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.OAuth2Error;
+import org.springframework.security.oauth2.core.OAuth2ErrorCodes;
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
+import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
import org.springframework.security.oauth2.core.converter.ClaimTypeConverter;
import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
@@ -96,7 +99,6 @@ import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtClaimNames;
import org.springframework.security.oauth2.jwt.JwtClaimValidator;
import org.springframework.security.oauth2.jwt.JwtDecoder;
-import org.springframework.security.oauth2.jwt.JwtIssuerValidator;
import org.springframework.security.oauth2.jwt.JwtTimestampValidator;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.web.client.RestOperations;
@@ -361,12 +363,18 @@ public class IdentityServiceFacadeFactoryBean implements FactoryBean
+ {
+ private final String requiredIssuer;
+
+ public JwtIssuerValidator(String issuer)
+ {
+ this.requiredIssuer = requireNonNull(issuer, "issuer cannot be null");
+ }
+
+ @Override
+ public OAuth2TokenValidatorResult validate(Jwt token)
+ {
+ requireNonNull(token, "token cannot be null");
+ final Object issuer = token.getClaim(JwtClaimNames.ISS);
+ if (issuer != null && requiredIssuer.equals(issuer.toString()))
+ {
+ return OAuth2TokenValidatorResult.success();
+ }
+
+ final OAuth2Error error = new OAuth2Error(
+ OAuth2ErrorCodes.INVALID_TOKEN,
+ String.format("The iss claim is not valid. Expected `%s` but got `%s`.", requiredIssuer, issuer),
+ "https://tools.ietf.org/html/rfc6750#section-3.1");
+ return OAuth2TokenValidatorResult.failure(error);
+ }
+
+ }
+
private static boolean isDefined(String value)
{
return value != null && !value.isBlank();
diff --git a/repository/src/main/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizer.java b/repository/src/main/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizer.java
index 388404d15f..76968fea26 100644
--- a/repository/src/main/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizer.java
+++ b/repository/src/main/java/org/alfresco/repo/security/sync/ChainingUserRegistrySynchronizer.java
@@ -1001,8 +1001,10 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean
private final Map groupsToCreate = new TreeMap();
private final Map> personParentAssocsToCreate = newPersonMap();
private final Map> personParentAssocsToDelete = newPersonMap();
+ private final List personToRezone = new LinkedList<>();
private Map> groupParentAssocsToCreate = new TreeMap>();
private final Map> groupParentAssocsToDelete = new TreeMap>();
+ private final List groupToRezone = new LinkedList<>();
private final Map> finalGroupChildAssocs = new TreeMap>();
private List personsProcessed = new LinkedList();
private Set allZonePersons = Collections.emptySet();
@@ -1268,7 +1270,18 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean
parents.add(parent);
}
}
-
+
+ private void recordParentAssociationAuthoritiesToRezone(String child)
+ {
+ if (child != null)
+ {
+ List toRezone = AuthorityType.getAuthorityType(child) == AuthorityType.USER
+ ? this.personToRezone
+ : this.groupToRezone;
+ toRezone.add(child);
+ }
+ }
+
private void validateGroupParentAssocsToCreate()
{
Iterator>> i = this.groupParentAssocsToCreate.entrySet().iterator();
@@ -1432,36 +1445,55 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean
String child = entry.getKey();
if (!toRetain.contains(child))
{
- if (ChainingUserRegistrySynchronizer.logger.isDebugEnabled())
+ if (!shouldRezone(child))
{
- if (groupList == null)
+ if (ChainingUserRegistrySynchronizer.logger.isDebugEnabled())
{
- groupList = new StringBuilder(1024);
- }
- else
- {
- groupList.setLength(0);
- }
- for (String parent : entry.getValue())
- {
- if (groupList.length() > 0)
+ if (groupList == null)
{
- groupList.append(", ");
+ groupList = new StringBuilder(1024);
}
- groupList.append('\'').append(
- ChainingUserRegistrySynchronizer.this.authorityService.getShortName(parent))
- .append('\'');
+ else
+ {
+ groupList.setLength(0);
+ }
+ for (String parent : entry.getValue())
+ {
+ if (groupList.length() > 0)
+ {
+ groupList.append(", ");
+ }
+ groupList.append('\'').append(
+ ChainingUserRegistrySynchronizer.this.authorityService.getShortName(parent))
+ .append('\'');
+ }
+ ChainingUserRegistrySynchronizer.logger.debug("Ignoring non-existent member '"
+ + ChainingUserRegistrySynchronizer.this.authorityService.getShortName(child)
+ + "' in groups {" + groupList.toString() + "}");
}
- ChainingUserRegistrySynchronizer.logger.debug("Ignoring non-existent member '"
- + ChainingUserRegistrySynchronizer.this.authorityService.getShortName(child)
- + "' in groups {" + groupList.toString() + "}");
+ i.remove();
+ }
+ else {
+ recordParentAssociationAuthoritiesToRezone(child);
}
- i.remove();
}
}
}
+ private boolean shouldRezone(String authorityName)
+ {
+ boolean exists = authorityService.authorityExists(authorityName);
+
+ if (exists)
+ {
+ Set zones = ChainingUserRegistrySynchronizer.this.authorityService.getAuthorityZones(authorityName);
+ return isInZone(authorityName, zones, AuthorityService.ZONE_AUTH_ALFRESCO) && !isInZone(authorityName, zones, zoneId);
+ }
+
+ return false;
+ }
+
private void processGroups(UserRegistry userRegistry, boolean isFullSync, boolean splitTxns)
{
// MNT-12454 fix. If syncDelete is false, there is no need to pull all users and all groups from LDAP during the full synchronization.
@@ -1634,6 +1666,7 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean
public void process(Map.Entry> entry) throws Throwable
{
maintainAssociationCreations(entry.getKey());
+ maintainAssociationCreationsToRezone(entry.getKey());
}
}, splitTxns);
}
@@ -1667,6 +1700,7 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean
{
maintainAssociationDeletions(entry.getKey());
maintainAssociationCreations(entry.getKey());
+ maintainAssociationCreationsToRezone(entry.getKey());
}
}, splitTxns);
}
@@ -1742,6 +1776,25 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean
}
}
}
+
+ private void maintainAssociationCreationsToRezone(String authorityName)
+ {
+ boolean isPerson = AuthorityType.getAuthorityType(authorityName) == AuthorityType.USER;
+
+ List authorities = isPerson ? this.personToRezone : this.groupToRezone;
+ Map> parentAssocsToCreate = isPerson ? this.personParentAssocsToCreate : this.groupParentAssocsToCreate;
+
+ if (authorities != null && !authorities.isEmpty() && parentAssocsToCreate.containsKey(authorityName))
+ {
+ if (ChainingUserRegistrySynchronizer.logger.isDebugEnabled())
+ {
+ ChainingUserRegistrySynchronizer.logger.debug(
+ "Changing '" + ChainingUserRegistrySynchronizer.this.authorityService.getShortName(authorityName)
+ + "' to zone '" + zoneId + "'");
+ }
+ updateAuthorityZones(authorityName, ChainingUserRegistrySynchronizer.this.authorityService.getAuthorityZones(authorityName), zoneSet);
+ }
+ }
} // end of Analyzer class
// Run the first process the Group Analyzer
@@ -1906,6 +1959,7 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean
// create cycles)
groupAnalyzer.maintainAssociationDeletions(personName);
groupAnalyzer.maintainAssociationCreations(personName);
+ groupAnalyzer.maintainAssociationCreationsToRezone(personName);
synchronized (this)
{
@@ -2118,10 +2172,32 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean
zonesToAdd.removeAll(oldZones);
if (!zonesToAdd.isEmpty())
{
+ // Prevents the authority from being added to zones where already is
+ Set currentZones = this.authorityService.getAuthorityZones(authorityName);
+ if (currentZones != null && !currentZones.isEmpty())
+ {
+ zonesToAdd.removeAll(currentZones);
+ }
this.authorityService.addAuthorityToZones(authorityName, zonesToAdd);
}
}
-
+
+ /**
+ * Checks if the supplied authority is part of a certain zone
+ *
+ * @param authorityName
+ * the name of authority to check
+ * @param authorityZones
+ * the zones where authority is
+ * @param zoneToCheck
+ * the zone to check
+ * @return true in case the authority is in supplied zone
+ */
+ private boolean isInZone(String authorityName, Set authorityZones, String zoneToCheck)
+ {
+ return authorityName != null && authorityZones != null && zoneToCheck != null && authorityZones.contains(zoneToCheck);
+ }
+
@Override
protected void onBootstrap(ApplicationEvent event)
{
diff --git a/repository/src/test/java/org/alfresco/repo/security/authentication/identityservice/IdentityServiceFacadeFactoryBeanTest.java b/repository/src/test/java/org/alfresco/repo/security/authentication/identityservice/IdentityServiceFacadeFactoryBeanTest.java
index bf107e68bc..5b07c83db0 100644
--- a/repository/src/test/java/org/alfresco/repo/security/authentication/identityservice/IdentityServiceFacadeFactoryBeanTest.java
+++ b/repository/src/test/java/org/alfresco/repo/security/authentication/identityservice/IdentityServiceFacadeFactoryBeanTest.java
@@ -31,15 +31,20 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.Map;
+import java.util.UUID;
import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacadeFactoryBean.JwtDecoderProvider;
+import org.alfresco.repo.security.authentication.identityservice.IdentityServiceFacadeFactoryBean.JwtIssuerValidator;
import org.junit.Test;
import org.springframework.security.oauth2.client.registration.ClientRegistration.ProviderDetails;
+import org.springframework.security.oauth2.core.OAuth2Error;
+import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
public class IdentityServiceFacadeFactoryBeanTest
{
+ private static final String EXPECTED_ISSUER = "expected-issuer";
@Test
public void shouldCreateJwtDecoderWithoutIDSWhenPublicKeyIsProvided()
{
@@ -62,4 +67,53 @@ public class IdentityServiceFacadeFactoryBeanTest
.containsEntry(USERNAME_CLAIM, "piotrek");
}
+ @Test
+ public void shouldFailWithNotMatchingIssuerURIs()
+ {
+ final JwtIssuerValidator issuerValidator = new JwtIssuerValidator(EXPECTED_ISSUER);
+
+ final OAuth2TokenValidatorResult validationResult = issuerValidator.validate(tokenWithIssuer("different-issuer"));
+ assertThat(validationResult).isNotNull();
+ assertThat(validationResult.hasErrors()).isTrue();
+ assertThat(validationResult.getErrors()).hasSize(1);
+
+ final OAuth2Error error = validationResult.getErrors().iterator().next();
+ assertThat(error).isNotNull();
+ assertThat(error.getDescription()).contains(EXPECTED_ISSUER, "different-issuer");
+ }
+
+ @Test
+ public void shouldFailWithNullIssuerURI()
+ {
+ final JwtIssuerValidator issuerValidator = new JwtIssuerValidator(EXPECTED_ISSUER);
+
+ final OAuth2TokenValidatorResult validationResult = issuerValidator.validate(tokenWithIssuer(null));
+ assertThat(validationResult).isNotNull();
+ assertThat(validationResult.hasErrors()).isTrue();
+ assertThat(validationResult.getErrors()).hasSize(1);
+
+ final OAuth2Error error = validationResult.getErrors().iterator().next();
+ assertThat(error).isNotNull();
+ assertThat(error.getDescription()).contains(EXPECTED_ISSUER, "null");
+ }
+
+ @Test
+ public void shouldSucceedWithMatchingIssuerURI()
+ {
+ final JwtIssuerValidator issuerValidator = new JwtIssuerValidator(EXPECTED_ISSUER);
+
+ final OAuth2TokenValidatorResult validationResult = issuerValidator.validate(tokenWithIssuer(EXPECTED_ISSUER));
+ assertThat(validationResult).isNotNull();
+ assertThat(validationResult.hasErrors()).isFalse();
+ assertThat(validationResult.getErrors()).isEmpty();
+ }
+
+ private Jwt tokenWithIssuer(String issuer)
+ {
+ return Jwt.withTokenValue(UUID.randomUUID().toString())
+ .issuer(issuer)
+ .header("JUST", "FOR TESTING")
+ .build();
+ }
+
}
\ No newline at end of file
diff --git a/scripts/ci/docker-compose/docker-compose-db.yaml b/scripts/ci/docker-compose/docker-compose-db.yaml
index 1e74d3f65d..c6f0e328d9 100644
--- a/scripts/ci/docker-compose/docker-compose-db.yaml
+++ b/scripts/ci/docker-compose/docker-compose-db.yaml
@@ -34,7 +34,7 @@ services:
ports:
- "3307:3306"
activemq:
- image: alfresco/alfresco-activemq:5.17.1-jre11-rockylinux8
+ image: alfresco/alfresco-activemq:5.17.6-jre17-rockylinux8
ports:
- "5672:5672" # AMQP
- "61616:61616" # OpenWire
\ No newline at end of file
diff --git a/scripts/ci/docker-compose/docker-compose.yaml b/scripts/ci/docker-compose/docker-compose.yaml
index dc37e4649f..93f132a039 100644
--- a/scripts/ci/docker-compose/docker-compose.yaml
+++ b/scripts/ci/docker-compose/docker-compose.yaml
@@ -20,7 +20,7 @@ services:
- "5433:5432"
activemq:
profiles: ["default", "with-transform-core-aio", "activemq", "with-mtls-transform-core-aio"]
- image: alfresco/alfresco-activemq:5.17.1-jre11-rockylinux8
+ image: alfresco/alfresco-activemq:5.17.6-jre17-rockylinux8
ports:
- "5672:5672" # AMQP
- "61616:61616" # OpenWire