mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-09-10 14:11:58 +00:00
ACS-7258 Prepare for ACS 7.4.2 Service Pack (#2517)
This commit is contained in:
committed by
GitHub
parent
518058b284
commit
d50e82e74b
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@@ -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: |
|
||||
|
@@ -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
|
||||
|
@@ -436,7 +436,7 @@
|
||||
</run>
|
||||
</image>
|
||||
<image>
|
||||
<name>alfresco/alfresco-activemq:${dependency.activemq.version}-jre11-rockylinux8</name>
|
||||
<name>alfresco/alfresco-activemq:${dependency.activemq.version}-jre17-rockylinux8</name>
|
||||
<run>
|
||||
<ports>
|
||||
<port>${activemq.port1}:${activemq.port1}</port>
|
||||
@@ -507,7 +507,7 @@
|
||||
</run>
|
||||
</image>
|
||||
<image>
|
||||
<name>alfresco/alfresco-activemq:${dependency.activemq.version}-jre11-rockylinux8</name>
|
||||
<name>alfresco/alfresco-activemq:${dependency.activemq.version}-jre17-rockylinux8</name>
|
||||
<run>
|
||||
<ports>
|
||||
<port>${activemq.port1}:${activemq.port1}</port>
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
10
pom.xml
10
pom.xml
@@ -62,11 +62,11 @@
|
||||
<dependency.jackson.version>2.15.0-rc1</dependency.jackson.version>
|
||||
<dependency.cxf.version>3.5.5</dependency.cxf.version>
|
||||
<dependency.opencmis.version>1.0.0</dependency.opencmis.version>
|
||||
<dependency.webscripts.version>8.44</dependency.webscripts.version>
|
||||
<dependency.webscripts.version>8.47</dependency.webscripts.version>
|
||||
<dependency.bouncycastle.version>1.70</dependency.bouncycastle.version>
|
||||
<dependency.mockito-core.version>4.9.0</dependency.mockito-core.version>
|
||||
<dependency.assertj.version>3.24.2</dependency.assertj.version>
|
||||
<dependency.org-json.version>20230227</dependency.org-json.version>
|
||||
<dependency.org-json.version>20231013</dependency.org-json.version>
|
||||
<dependency.commons-dbcp.version>2.9.0</dependency.commons-dbcp.version>
|
||||
<dependency.commons-io.version>2.11.0</dependency.commons-io.version>
|
||||
<dependency.gson.version>2.8.9</dependency.gson.version>
|
||||
@@ -88,7 +88,7 @@
|
||||
<dependency.netty.version>4.1.87.Final</dependency.netty.version> <!-- must be in sync with camels transitive dependencies, e.g.: netty-common -->
|
||||
<dependency.netty.qpid.version>4.1.82.Final</dependency.netty.qpid.version> <!-- must be in sync with camels transitive dependencies: native-unix-common/native-epoll/native-kqueue -->
|
||||
<dependency.netty-tcnative.version>2.0.56.Final</dependency.netty-tcnative.version> <!-- must be in sync with camels transitive dependencies -->
|
||||
<dependency.activemq.version>5.17.4</dependency.activemq.version>
|
||||
<dependency.activemq.version>5.17.6</dependency.activemq.version>
|
||||
<dependency.apache-compress.version>1.22</dependency.apache-compress.version>
|
||||
<dependency.apache.taglibs.version>1.2.5</dependency.apache.taglibs.version>
|
||||
<dependency.awaitility.version>4.2.0</dependency.awaitility.version>
|
||||
@@ -111,8 +111,8 @@
|
||||
<dependency.json-smart.version>2.5.0</dependency.json-smart.version>
|
||||
<dependency.jakarta-rpc-api.version>1.1.4</dependency.jakarta-rpc-api.version>
|
||||
|
||||
<alfresco.googledrive.version>3.4.0</alfresco.googledrive.version>
|
||||
<alfresco.aos-module.version>1.6.0</alfresco.aos-module.version>
|
||||
<alfresco.googledrive.version>3.4.2</alfresco.googledrive.version>
|
||||
<alfresco.aos-module.version>1.6.2</alfresco.aos-module.version>
|
||||
<alfresco.api-explorer.version>7.4.0</alfresco.api-explorer.version> <!-- Also in alfresco-enterprise-share -->
|
||||
|
||||
<alfresco.maven-plugin.version>2.2.0</alfresco.maven-plugin.version>
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -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);
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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<IdentitySer
|
||||
.map(OIDCProviderMetadata::getAuthorizationEndpointURI)
|
||||
.map(URI::toASCIIString)
|
||||
.orElse(null);
|
||||
|
||||
final String issuerUri = Optional.of(metadata)
|
||||
.map(OIDCProviderMetadata::getIssuer)
|
||||
.map(Issuer::getValue)
|
||||
.orElseGet(config::getIssuerUrl);
|
||||
|
||||
return ClientRegistration
|
||||
.withRegistrationId("ids")
|
||||
.authorizationUri(authUri)
|
||||
.tokenUri(metadata.getTokenEndpointURI().toASCIIString())
|
||||
.jwkSetUri(metadata.getJWKSetURI().toASCIIString())
|
||||
.issuerUri(config.getIssuerUrl())
|
||||
.issuerUri(issuerUri)
|
||||
.authorizationGrantType(AuthorizationGrantType.PASSWORD);
|
||||
}
|
||||
|
||||
@@ -551,6 +559,34 @@ public class IdentityServiceFacadeFactoryBean implements FactoryBean<IdentitySer
|
||||
}
|
||||
}
|
||||
|
||||
static class JwtIssuerValidator implements OAuth2TokenValidator<Jwt>
|
||||
{
|
||||
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();
|
||||
|
@@ -1001,8 +1001,10 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean
|
||||
private final Map<String, String> groupsToCreate = new TreeMap<String, String>();
|
||||
private final Map<String, Set<String>> personParentAssocsToCreate = newPersonMap();
|
||||
private final Map<String, Set<String>> personParentAssocsToDelete = newPersonMap();
|
||||
private final List<String> personToRezone = new LinkedList<>();
|
||||
private Map<String, Set<String>> groupParentAssocsToCreate = new TreeMap<String, Set<String>>();
|
||||
private final Map<String, Set<String>> groupParentAssocsToDelete = new TreeMap<String, Set<String>>();
|
||||
private final List<String> groupToRezone = new LinkedList<>();
|
||||
private final Map<String, Set<String>> finalGroupChildAssocs = new TreeMap<String, Set<String>>();
|
||||
private List<String> personsProcessed = new LinkedList<String>();
|
||||
private Set<String> allZonePersons = Collections.emptySet();
|
||||
@@ -1268,7 +1270,18 @@ public class ChainingUserRegistrySynchronizer extends AbstractLifecycleBean
|
||||
parents.add(parent);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void recordParentAssociationAuthoritiesToRezone(String child)
|
||||
{
|
||||
if (child != null)
|
||||
{
|
||||
List<String> toRezone = AuthorityType.getAuthorityType(child) == AuthorityType.USER
|
||||
? this.personToRezone
|
||||
: this.groupToRezone;
|
||||
toRezone.add(child);
|
||||
}
|
||||
}
|
||||
|
||||
private void validateGroupParentAssocsToCreate()
|
||||
{
|
||||
Iterator<Map.Entry<String, Set<String>>> 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<String> 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<String, Set<String>> 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<String> authorities = isPerson ? this.personToRezone : this.groupToRezone;
|
||||
Map<String, Set<String>> 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<String> 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<String> authorityZones, String zoneToCheck)
|
||||
{
|
||||
return authorityName != null && authorityZones != null && zoneToCheck != null && authorityZones.contains(zoneToCheck);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onBootstrap(ApplicationEvent event)
|
||||
{
|
||||
|
@@ -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();
|
||||
}
|
||||
|
||||
}
|
@@ -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
|
@@ -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
|
||||
|
Reference in New Issue
Block a user