From 60cc7bf94006455d6a1c959f7c27cbc64b7eb528 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francesco=20Chicchiricc=C3=B2?= Date: Thu, 18 Jun 2020 09:32:27 +0200 Subject: [PATCH 1/4] Making Payara working again --- .travis.yml | 6 +- .../core/rest/cxf/batch/BatchItemRequest.java | 98 ++++++++++++++++--- .../java/ProvisioningContext.java | 79 ++++++++++++++- .../DefaultNotificationJobDelegate.java | 42 +------- .../spring/security/AuthDataAccessor.java | 5 +- .../spring/security/JWTAuthentication.java | 53 ++++++++++ .../security/JWTAuthenticationFilter.java | 38 ++++--- .../security/JWTAuthenticationProvider.java | 14 +-- ...sernamePasswordAuthenticationProvider.java | 4 +- .../spring/security/WebSecurityContext.java | 61 +++++------- .../client/self/SelfKeymasterServiceOps.java | 25 +---- .../api/service/NetworkServiceService.java | 17 ++-- .../service/NetworkServiceServiceImpl.java | 17 ++-- fit/build-tools/pom.xml | 12 ++- .../{glassfish-web.xml => payara-web.xml} | 16 ++- fit/core-reference/pom.xml | 28 ++++-- .../src/main/webapp/WEB-INF/payara-web.xml | 3 - .../src/test/resources/mail.properties | 2 +- .../{glassfish-web.xml => payara-web.xml} | 16 ++- pom.xml | 2 +- 20 files changed, 346 insertions(+), 192 deletions(-) rename fit/console-reference/src/main/webapp/WEB-INF/{glassfish-web.xml => payara-web.xml} (62%) rename fit/enduser-reference/src/main/webapp/WEB-INF/{glassfish-web.xml => payara-web.xml} (62%) diff --git a/.travis.yml b/.travis.yml index 428e276327a..2c109b976d7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -92,9 +92,9 @@ jobs: - stage: fit name: "Integration Tests: Wildfly / H2 / JSON Content-Type" script: mvn -f fit/core-reference/pom.xml -P wildfly-it -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true - #- stage: fit - #name: "Integration Tests: Payara / H2 / JSON Content-Type" - #script: mvn -f fit/core-reference/pom.xml -P payara-it -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true + - stage: fit + name: "Integration Tests: Payara / H2 / JSON Content-Type" + script: mvn -f fit/core-reference/pom.xml -P payara-it -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true - stage: fit name: "Integration Tests: Tomcat / PostgreSQL / JSON Content-Type" script: mvn -f fit/core-reference/pom.xml -P postgres-it -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true diff --git a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/batch/BatchItemRequest.java b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/batch/BatchItemRequest.java index 75002c3654d..c8ce65f920d 100644 --- a/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/batch/BatchItemRequest.java +++ b/core/idrepo/rest-cxf/src/main/java/org/apache/syncope/core/rest/cxf/batch/BatchItemRequest.java @@ -20,20 +20,30 @@ import java.io.ByteArrayInputStream; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.Enumeration; +import java.util.HashSet; import java.util.List; +import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.ws.rs.core.HttpHeaders; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.math.NumberUtils; import org.apache.syncope.common.rest.api.batch.BatchRequestItem; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.http.MediaType; public class BatchItemRequest extends HttpServletRequestWrapper { + private static final Logger LOG = LoggerFactory.getLogger(BatchItemRequest.class); + private final String basePath; private final BatchRequestItem batchItem; @@ -85,6 +95,28 @@ public String getMethod() { return batchItem.getMethod(); } + @Override + public String getServerName() { + try { + return super.getServerName(); + } catch (Exception e) { + LOG.debug("While delegating to wrapped request", e); + return Optional.ofNullable(getHeader(HttpHeaders.HOST)). + map(host -> StringUtils.substringBefore(host, ":")).orElse(null); + } + } + + @Override + public int getServerPort() { + try { + return super.getServerPort(); + } catch (Exception e) { + LOG.debug("While delegating to wrapped request", e); + return Optional.ofNullable(getHeader(HttpHeaders.HOST)). + map(host -> NumberUtils.toInt(StringUtils.substringAfter(host, ":"))).orElse(0); + } + } + @Override public StringBuffer getRequestURL() { return new StringBuffer(basePath).append(getRequestURI()); @@ -121,21 +153,65 @@ public long getContentLengthLong() { @Override public String getHeader(final String name) { - return batchItem.getHeaders().containsKey(name) - ? batchItem.getHeaders().get(name).get(0).toString() - : HttpHeaders.CONTENT_TYPE.equals(name) || HttpHeaders.ACCEPT.equals(name) - ? MediaType.ALL_VALUE - : super.getHeader(name); + try { + return batchItem.getHeaders().containsKey(name) + ? batchItem.getHeaders().get(name).get(0).toString() + : HttpHeaders.CONTENT_TYPE.equals(name) || HttpHeaders.ACCEPT.equals(name) + ? MediaType.ALL_VALUE + : super.getHeader(name); + } catch (Exception e) { + LOG.debug("While delegating to wrapped request", e); + return null; + } } @Override public Enumeration getHeaders(final String name) { - return batchItem.getHeaders().containsKey(name) - ? Collections.enumeration( - batchItem.getHeaders().get(name).stream().map(Object::toString).collect(Collectors.toList())) - : HttpHeaders.CONTENT_TYPE.equals(name) || HttpHeaders.ACCEPT.equals(name) - ? Collections.enumeration(List.of(MediaType.ALL_VALUE)) - : super.getHeaders(name); + try { + return batchItem.getHeaders().containsKey(name) + ? Collections.enumeration(batchItem.getHeaders().get(name).stream(). + map(Object::toString).collect(Collectors.toList())) + : HttpHeaders.CONTENT_TYPE.equals(name) || HttpHeaders.ACCEPT.equals(name) + ? Collections.enumeration(List.of(MediaType.ALL_VALUE)) + : super.getHeaders(name); + } catch (Exception e) { + LOG.debug("While delegating to wrapped request", e); + return Collections.emptyEnumeration(); + } + } + + @Override + public Enumeration getHeaderNames() { + try { + return super.getHeaderNames(); + } catch (Exception e) { + LOG.debug("While delegating to wrapped request", e); + + Set names = new HashSet<>(batchItem.getHeaders().keySet()); + names.add(HttpHeaders.CONTENT_TYPE); + names.add(HttpHeaders.ACCEPT); + return Collections.enumeration(names); + } + } + + @Override + public Object getAttribute(final String name) { + try { + return super.getAttribute(name); + } catch (Exception e) { + LOG.debug("While delegating to wrapped request", e); + return null; + } + } + + @Override + public String getCharacterEncoding() { + try { + return super.getCharacterEncoding(); + } catch (Exception e) { + LOG.debug("While delegating to wrapped request", e); + return StandardCharsets.UTF_8.name(); + } } @Override diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java index bad73638e68..b83c66f50f0 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java @@ -18,13 +18,23 @@ */ package org.apache.syncope.core.provisioning.java; +import java.io.PrintStream; import java.lang.reflect.InvocationTargetException; import java.nio.charset.StandardCharsets; +import java.util.Enumeration; import java.util.Properties; import java.util.concurrent.Executor; import java.util.concurrent.ThreadPoolExecutor; import javax.annotation.Resource; +import javax.mail.MessagingException; +import javax.mail.Session; +import javax.mail.Transport; +import javax.naming.NamingException; import javax.sql.DataSource; +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.common.lib.LogOutputStream; +import org.apache.syncope.common.lib.PropertyUtils; import org.apache.syncope.core.provisioning.api.AnyObjectProvisioningManager; import org.apache.syncope.core.provisioning.api.AuditManager; import org.apache.syncope.core.provisioning.api.ConnIdBundleManager; @@ -41,6 +51,8 @@ import org.apache.syncope.core.provisioning.java.job.SchedulerDBInit; import org.apache.syncope.core.provisioning.java.job.SchedulerShutdown; import org.apache.syncope.core.provisioning.java.propagation.PropagationManagerImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.ApplicationContext; @@ -55,6 +67,7 @@ import org.springframework.core.env.Environment; import org.springframework.core.io.ClassPathResource; import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator; +import org.springframework.jndi.JndiObjectFactoryBean; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.JavaMailSenderImpl; import org.springframework.scheduling.annotation.AsyncConfigurer; @@ -74,6 +87,8 @@ @Configuration public class ProvisioningContext implements EnvironmentAware, AsyncConfigurer { + private static final Logger LOG = LoggerFactory.getLogger(ProvisioningContext.class); + @Resource(name = "MasterDataSource") private DataSource masterDataSource; @@ -188,14 +203,74 @@ public JobManager jobManager() { @ConditionalOnMissingBean @Bean - public JavaMailSender mailSender() { - JavaMailSenderImpl mailSender = new JavaMailSenderImpl(); + public JavaMailSender mailSender() throws IllegalArgumentException, NamingException { + JavaMailSenderImpl mailSender = new JavaMailSenderImpl() { + + @Override + protected Transport connectTransport() throws MessagingException { + // ensure that no auth means no auth + if (StringUtils.isBlank(getUsername())) { + Transport transport = getTransport(getSession()); + transport.connect(getHost(), getPort(), null, null); + return transport; + } + + return super.connectTransport(); + } + }; mailSender.setDefaultEncoding(env.getProperty("smtpEncoding")); mailSender.setHost(env.getProperty("smtpHost")); mailSender.setPort(env.getProperty("smtpPort", Integer.class)); mailSender.setUsername(env.getProperty("smtpUsername")); mailSender.setPassword(env.getProperty("smtpPassword")); mailSender.setProtocol(env.getProperty("smtpProtocol")); + + if (LOG.isDebugEnabled()) { + LOG.debug("[Mail] host:port = {}:{}", mailSender.getHost(), mailSender.getPort()); + LOG.debug("[Mail] protocol = {}", mailSender.getProtocol()); + LOG.debug("[Mail] username = {}", mailSender.getUsername()); + LOG.debug("[Mail] default encoding = {}", mailSender.getDefaultEncoding()); + } + + JndiObjectFactoryBean mailSession = new JndiObjectFactoryBean(); + mailSession.setJndiName("mail/syncopeNotification"); + try { + mailSession.afterPropertiesSet(); + } catch (NamingException e) { + LOG.debug("While looking up JNDI for mail session", e); + } + + Session session = (Session) mailSession.getObject(); + if (session == null) { + Properties javaMailProperties = mailSender.getJavaMailProperties(); + + Properties props = PropertyUtils.read(ProvisioningContext.class, "mail.properties", "conf.directory"); + for (Enumeration e = props.propertyNames(); e.hasMoreElements();) { + String prop = (String) e.nextElement(); + if (prop.startsWith("mail.smtp.")) { + javaMailProperties.setProperty(prop, props.getProperty(prop)); + } + } + + if (StringUtils.isNotBlank(mailSender.getUsername())) { + javaMailProperties.setProperty("mail.smtp.auth", "true"); + } + + if (LOG.isDebugEnabled()) { + mailSender.getJavaMailProperties(). + forEach((key, value) -> LOG.debug("[Mail] property: {} = {}", key, value)); + } + + String mailDebug = props.getProperty("mail.debug", "false"); + if (BooleanUtils.toBoolean(mailDebug)) { + session = mailSender.getSession(); + session.setDebug(true); + session.setDebugOut(new PrintStream(new LogOutputStream(LOG))); + } + } else { + mailSender.setSession(session); + } + return mailSender; } diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/DefaultNotificationJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/DefaultNotificationJobDelegate.java index 6c5ff848115..13a700b60af 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/DefaultNotificationJobDelegate.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/notification/DefaultNotificationJobDelegate.java @@ -18,18 +18,11 @@ */ package org.apache.syncope.core.provisioning.java.job.notification; -import java.io.PrintStream; import java.util.Date; -import java.util.Enumeration; import java.util.List; -import java.util.Properties; import java.util.concurrent.atomic.AtomicReference; -import javax.mail.Session; import javax.mail.internet.MimeMessage; -import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; -import org.apache.syncope.common.lib.LogOutputStream; -import org.apache.syncope.common.lib.PropertyUtils; import org.apache.syncope.common.lib.types.AuditElements; import org.apache.syncope.common.lib.types.TaskType; import org.apache.syncope.common.lib.types.TraceLevel; @@ -42,20 +35,17 @@ import org.apache.syncope.core.provisioning.api.notification.NotificationJobDelegate; import org.apache.syncope.core.provisioning.api.notification.NotificationManager; import org.apache.syncope.core.spring.security.AuthContextUtils; -import org.apache.syncope.core.spring.security.Encryptor; import org.quartz.JobExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.mail.javamail.JavaMailSender; -import org.springframework.mail.javamail.JavaMailSenderImpl; import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; @Component -public class DefaultNotificationJobDelegate implements InitializingBean, NotificationJobDelegate { +public class DefaultNotificationJobDelegate implements NotificationJobDelegate { private static final Logger LOG = LoggerFactory.getLogger(NotificationJobDelegate.class); @@ -80,36 +70,6 @@ public class DefaultNotificationJobDelegate implements InitializingBean, Notific private boolean interrupted; - @Override - public void afterPropertiesSet() throws Exception { - if (mailSender instanceof JavaMailSenderImpl) { - JavaMailSenderImpl javaMailSender = (JavaMailSenderImpl) mailSender; - - Properties javaMailProperties = javaMailSender.getJavaMailProperties(); - - Properties props = PropertyUtils.read(Encryptor.class, "mail.properties", "conf.directory"); - for (Enumeration e = props.propertyNames(); e.hasMoreElements();) { - String prop = (String) e.nextElement(); - if (prop.startsWith("mail.smtp.")) { - javaMailProperties.setProperty(prop, props.getProperty(prop)); - } - } - - if (StringUtils.isNotBlank(javaMailSender.getUsername())) { - javaMailProperties.setProperty("mail.smtp.auth", "true"); - } - - javaMailSender.setJavaMailProperties(javaMailProperties); - - String mailDebug = props.getProperty("mail.debug", "false"); - if (BooleanUtils.toBoolean(mailDebug)) { - Session session = javaMailSender.getSession(); - session.setDebug(true); - session.setDebugOut(new PrintStream(new LogOutputStream(LOG))); - } - } - } - @Override public String currentStatus() { return status.get(); diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java index 4d00c99892b..125b5f61b93 100644 --- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java +++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java @@ -244,7 +244,7 @@ protected boolean authenticate(final User user, final String password) { Optional connObjectKeyValue = mappingManager.getConnObjectKeyValue(user, provision.get()); if (connObjectKeyValue.isEmpty()) { throw new AccountNotFoundException( - "Unable to locate conn object key value for " + userType.getKey()); + "Unable to locate conn object key value for " + userType.getKey()); } connObjectKey = connObjectKeyValue.get(); Uid uid = connFactory.getConnector(resource).authenticate(connObjectKey, password, null); @@ -405,8 +405,7 @@ public Pair> authenticate(final JWTAuthenti if (BooleanUtils.isTrue(user.isMustChangePassword())) { LOG.debug("User {} must change password, resetting authorities", username); - authorities = Set.of( - new SyncopeGrantedAuthority(IdRepoEntitlement.MUST_CHANGE_PASSWORD)); + authorities = Set.of(new SyncopeGrantedAuthority(IdRepoEntitlement.MUST_CHANGE_PASSWORD)); } } diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/JWTAuthentication.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/JWTAuthentication.java index ab9f3d22819..846693cee3b 100644 --- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/JWTAuthentication.java +++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/JWTAuthentication.java @@ -22,7 +22,10 @@ import java.util.HashSet; import java.util.Optional; import java.util.Set; +import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.cxf.rs.security.jose.jwt.JwtClaims; import org.springframework.security.core.Authentication; @@ -92,4 +95,54 @@ public void setAuthenticated(final boolean authenticated) throws IllegalArgument public String getName() { return Optional.ofNullable(username).orElseGet(claims::getSubject); } + + @Override + public int hashCode() { + return new HashCodeBuilder(). + append(claims). + append(details). + append(username). + append(authorities). + append(authenticated). + build(); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final JWTAuthentication other = (JWTAuthentication) obj; + return new EqualsBuilder(). + append(claims, other.claims). + append(details, other.details). + append(username, other.username). + append(authorities, other.authorities). + append(authenticated, other.authenticated). + build(); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(super.toString()).append(": "); + sb.append("Principal: ").append(this.getPrincipal()).append("; "); + sb.append("Authenticated: ").append(this.isAuthenticated()).append("; "); + sb.append("Details: ").append(this.getDetails()).append("; "); + + if (!authorities.isEmpty()) { + sb.append("Granted Authorities: "); + sb.append(authorities.stream().map(SyncopeGrantedAuthority::toString).collect(Collectors.joining(", "))); + } else { + sb.append("Not granted any authorities"); + } + + return sb.toString(); + } } diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/JWTAuthenticationFilter.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/JWTAuthenticationFilter.java index e82f294e24e..e78c4943f63 100644 --- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/JWTAuthenticationFilter.java +++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/JWTAuthenticationFilter.java @@ -20,16 +20,17 @@ import java.io.IOException; import java.util.Optional; +import java.util.Set; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.ws.rs.core.HttpHeaders; +import org.apache.commons.lang3.tuple.Pair; import org.apache.cxf.rs.security.jose.jws.JwsException; import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactConsumer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.core.AuthenticationException; @@ -45,20 +46,26 @@ public class JWTAuthenticationFilter extends BasicAuthenticationFilter { private static final Logger LOG = LoggerFactory.getLogger(JWTAuthenticationFilter.class); - @Autowired - private AuthenticationEntryPoint authenticationEntryPoint; + private final AuthenticationEntryPoint authenticationEntryPoint; - @Autowired - private SyncopeAuthenticationDetailsSource authenticationDetailsSource; + private final SyncopeAuthenticationDetailsSource authenticationDetailsSource; - @Autowired - private AuthDataAccessor dataAccessor; + private final AuthDataAccessor dataAccessor; - @Autowired - private DefaultCredentialChecker credentialChecker; + private final DefaultCredentialChecker credentialChecker; + + public JWTAuthenticationFilter( + final AuthenticationManager authenticationManager, + final AuthenticationEntryPoint authenticationEntryPoint, + final SyncopeAuthenticationDetailsSource authenticationDetailsSource, + final AuthDataAccessor dataAccessor, + final DefaultCredentialChecker credentialChecker) { - public JWTAuthenticationFilter(final AuthenticationManager authenticationManager) { super(authenticationManager); + this.authenticationEntryPoint = authenticationEntryPoint; + this.authenticationDetailsSource = authenticationDetailsSource; + this.dataAccessor = dataAccessor; + this.credentialChecker = credentialChecker; } @Override @@ -87,8 +94,15 @@ protected void doFilterInternal( throw new BadCredentialsException("Invalid signature found in JWT"); } - SecurityContextHolder.getContext().setAuthentication( - new JWTAuthentication(consumer.getJwtClaims(), authenticationDetailsSource.buildDetails(request))); + JWTAuthentication jwtAuthentication = + new JWTAuthentication(consumer.getJwtClaims(), authenticationDetailsSource.buildDetails(request)); + AuthContextUtils.callAsAdmin(jwtAuthentication.getDetails().getDomain(), () -> { + Pair> authenticated = dataAccessor.authenticate(jwtAuthentication); + jwtAuthentication.setUsername(authenticated.getLeft()); + jwtAuthentication.getAuthorities().addAll(authenticated.getRight()); + return null; + }); + SecurityContextHolder.getContext().setAuthentication(jwtAuthentication); chain.doFilter(request, response); } catch (JwsException e) { diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/JWTAuthenticationProvider.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/JWTAuthenticationProvider.java index c8fb6fb9dd5..9580070277f 100644 --- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/JWTAuthenticationProvider.java +++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/JWTAuthenticationProvider.java @@ -18,9 +18,6 @@ */ package org.apache.syncope.core.spring.security; -import java.util.Date; -import java.util.Set; -import org.apache.commons.lang3.tuple.Pair; import org.apache.cxf.rs.security.jose.jwt.JwtClaims; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.AuthenticationProvider; @@ -39,17 +36,10 @@ public class JWTAuthenticationProvider implements AuthenticationProvider { @Override public Authentication authenticate(final Authentication authentication) throws AuthenticationException { - final JWTAuthentication jwtAuthentication = (JWTAuthentication) authentication; - - AuthContextUtils.callAsAdmin(jwtAuthentication.getDetails().getDomain(), () -> { - Pair> authenticated = dataAccessor.authenticate(jwtAuthentication); - jwtAuthentication.setUsername(authenticated.getLeft()); - jwtAuthentication.getAuthorities().addAll(authenticated.getRight()); - return null; - }); + JWTAuthentication jwtAuthentication = (JWTAuthentication) authentication; JwtClaims claims = jwtAuthentication.getClaims(); - Long referenceTime = new Date().getTime(); + Long referenceTime = System.currentTimeMillis(); Long expiryTime = claims.getExpiryTime(); if (expiryTime == null || (expiryTime * 1000L) < referenceTime) { diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/UsernamePasswordAuthenticationProvider.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/UsernamePasswordAuthenticationProvider.java index 9e7f712abce..76313ecaf3a 100644 --- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/UsernamePasswordAuthenticationProvider.java +++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/UsernamePasswordAuthenticationProvider.java @@ -211,7 +211,7 @@ protected Authentication finalizeAuthentication( } @Override - public boolean supports(final Class type) { - return type.equals(UsernamePasswordAuthenticationToken.class); + public boolean supports(final Class authentication) { + return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication); } } diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/WebSecurityContext.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/WebSecurityContext.java index 05dfc94831b..f421dd69143 100644 --- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/WebSecurityContext.java +++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/WebSecurityContext.java @@ -19,7 +19,9 @@ package org.apache.syncope.core.spring.security; import javax.annotation.Resource; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; @@ -29,13 +31,10 @@ import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.security.web.access.intercept.FilterSecurityInterceptor; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import org.springframework.security.web.context.NullSecurityContextRepository; -import org.springframework.security.web.context.SecurityContextPersistenceFilter; -import org.springframework.security.web.context.SecurityContextRepository; import org.springframework.security.web.firewall.DefaultHttpFirewall; import org.springframework.security.web.firewall.HttpFirewall; @@ -46,6 +45,9 @@ public class WebSecurityContext extends WebSecurityConfigurerAdapter { @Resource(name = "anonymousUser") private String anonymousUser; + @Autowired + private ApplicationContext ctx; + public WebSecurityContext() { super(true); SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL); @@ -82,58 +84,39 @@ protected void configure(final AuthenticationManagerBuilder builder) throws Exce } @Bean - public SecurityContextRepository securityContextRepository() { - return new NullSecurityContextRepository(); - } - - @Bean - public SecurityContextPersistenceFilter securityContextPersistenceFilter() { - return new SecurityContextPersistenceFilter(securityContextRepository()); + public AccessDeniedHandler accessDeniedHandler() { + return new SyncopeAccessDeniedHandler(); } - @Bean - public AuthenticationEntryPoint basicAuthenticationEntryPoint() { + @Override + protected void configure(final HttpSecurity http) throws Exception { SyncopeBasicAuthenticationEntryPoint basicAuthenticationEntryPoint = new SyncopeBasicAuthenticationEntryPoint(); basicAuthenticationEntryPoint.setRealmName("Apache Syncope authentication"); - return basicAuthenticationEntryPoint; - } - @Bean - public SyncopeAuthenticationDetailsSource authenticationDetailsSource() { - return new SyncopeAuthenticationDetailsSource(); - } + SyncopeAuthenticationDetailsSource authenticationDetailsSource = new SyncopeAuthenticationDetailsSource(); - @Bean - public AccessDeniedHandler accessDeniedHandler() { - return new SyncopeAccessDeniedHandler(); - } - - @Bean - public JWTAuthenticationFilter jwtAuthenticationFilter() throws Exception { - return new JWTAuthenticationFilter(authenticationManager()); - } + JWTAuthenticationFilter jwtAuthenticationFilter = new JWTAuthenticationFilter( + authenticationManager(), + basicAuthenticationEntryPoint, + authenticationDetailsSource, + ctx.getBean(AuthDataAccessor.class), + ctx.getBean(DefaultCredentialChecker.class)); - @Bean - public MustChangePasswordFilter mustChangePasswordFilter() { - return new MustChangePasswordFilter(); - } - - @Override - protected void configure(final HttpSecurity http) throws Exception { http.authorizeRequests(). antMatchers("/**").permitAll().and(). sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and(). - securityContext().securityContextRepository(securityContextRepository()).and(). + securityContext().securityContextRepository(new NullSecurityContextRepository()).and(). anonymous().principal(anonymousUser).and(). - httpBasic().authenticationEntryPoint(basicAuthenticationEntryPoint()). - authenticationDetailsSource(authenticationDetailsSource()).and(). + httpBasic().authenticationEntryPoint(basicAuthenticationEntryPoint). + authenticationDetailsSource(authenticationDetailsSource).and(). exceptionHandling().accessDeniedHandler(accessDeniedHandler()).and(). - addFilterBefore(jwtAuthenticationFilter(), BasicAuthenticationFilter.class). - addFilterBefore(mustChangePasswordFilter(), FilterSecurityInterceptor.class). + addFilterBefore(jwtAuthenticationFilter, BasicAuthenticationFilter.class). + addFilterBefore(new MustChangePasswordFilter(), FilterSecurityInterceptor.class). headers().disable(). csrf().disable(); } + @ConditionalOnMissingBean @Bean public AuthDataAccessor authDataAccessor() { return new AuthDataAccessor(); diff --git a/ext/self-keymaster/client/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterServiceOps.java b/ext/self-keymaster/client/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterServiceOps.java index 99d3b6449b5..d52abeadcbe 100644 --- a/ext/self-keymaster/client/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterServiceOps.java +++ b/ext/self-keymaster/client/src/main/java/org/apache/syncope/common/keymaster/client/self/SelfKeymasterServiceOps.java @@ -21,7 +21,6 @@ import java.util.List; import java.util.Map; import java.util.concurrent.CompletionStage; -import javax.ws.rs.HttpMethod; import javax.ws.rs.Path; import javax.ws.rs.client.Entity; import javax.ws.rs.core.MediaType; @@ -31,6 +30,7 @@ import org.apache.syncope.common.keymaster.client.api.model.NetworkService; import org.apache.syncope.common.keymaster.client.api.ServiceOps; import org.apache.syncope.ext.self.keymaster.api.service.NetworkServiceService; +import org.apache.syncope.ext.self.keymaster.api.service.NetworkServiceService.Action; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.backoff.BackOffExecution; @@ -38,12 +38,6 @@ public class SelfKeymasterServiceOps extends SelfKeymasterOps implements ServiceOps { - private enum Action { - register, - unregister - - } - private static final Logger LOG = LoggerFactory.getLogger(ServiceOps.class); private final int maxRetries; @@ -129,22 +123,7 @@ private void retry( } private CompletionStage completionStage(final Action action, final NetworkService service) { - CompletionStage completionStage = null; - switch (action) { - case register: - completionStage = rx(path). - post(Entity.entity(service, MediaType.APPLICATION_JSON)); - break; - - case unregister: - completionStage = rx(path). - method(HttpMethod.DELETE, Entity.entity(service, MediaType.APPLICATION_JSON)); - break; - - default: - } - - return completionStage; + return rx(path + "?action=" + action.name()).post(Entity.entity(service, MediaType.APPLICATION_JSON)); } @Override diff --git a/ext/self-keymaster/rest-api/src/main/java/org/apache/syncope/ext/self/keymaster/api/service/NetworkServiceService.java b/ext/self-keymaster/rest-api/src/main/java/org/apache/syncope/ext/self/keymaster/api/service/NetworkServiceService.java index d7d1845ab27..aade9af53a7 100644 --- a/ext/self-keymaster/rest-api/src/main/java/org/apache/syncope/ext/self/keymaster/api/service/NetworkServiceService.java +++ b/ext/self-keymaster/rest-api/src/main/java/org/apache/syncope/ext/self/keymaster/api/service/NetworkServiceService.java @@ -23,12 +23,12 @@ import java.util.concurrent.CompletableFuture; import javax.validation.constraints.NotNull; import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import org.apache.syncope.common.keymaster.client.api.model.NetworkService; @@ -39,6 +39,12 @@ @Path("networkServices") public interface NetworkServiceService extends Serializable { + enum Action { + register, + unregister + + } + @GET @Path("{serviceType}") @Produces({ MediaType.APPLICATION_JSON }) @@ -52,10 +58,7 @@ public interface NetworkServiceService extends Serializable { @POST @Consumes({ MediaType.APPLICATION_JSON }) @Produces({ MediaType.APPLICATION_JSON }) - CompletableFuture register(@NotNull NetworkService networkService); - - @DELETE - @Consumes({ MediaType.APPLICATION_JSON }) - @Produces({ MediaType.APPLICATION_JSON }) - CompletableFuture unregister(@NotNull NetworkService networkService); + CompletableFuture action( + @NotNull NetworkService networkService, + @QueryParam("action") Action action); } diff --git a/ext/self-keymaster/rest-cxf/src/main/java/org/apache/syncope/ext/self/keymaster/cxf/service/NetworkServiceServiceImpl.java b/ext/self-keymaster/rest-cxf/src/main/java/org/apache/syncope/ext/self/keymaster/cxf/service/NetworkServiceServiceImpl.java index 8bf3ed8416a..e04c3507d52 100644 --- a/ext/self-keymaster/rest-cxf/src/main/java/org/apache/syncope/ext/self/keymaster/cxf/service/NetworkServiceServiceImpl.java +++ b/ext/self-keymaster/rest-cxf/src/main/java/org/apache/syncope/ext/self/keymaster/cxf/service/NetworkServiceServiceImpl.java @@ -48,18 +48,13 @@ public NetworkService get(final NetworkService.Type serviceType) { @PreAuthorize("@environment.getProperty('keymaster.username') == authentication.name and not(isAnonymous())") @Override - public CompletableFuture register(final NetworkService networkService) { + public CompletableFuture action(final NetworkService networkService, final Action action) { return CompletableFuture.supplyAsync(() -> { - logic.register(networkService); - return Response.noContent().build(); - }); - } - - @PreAuthorize("@environment.getProperty('keymaster.username') == authentication.name and not(isAnonymous())") - @Override - public CompletableFuture unregister(final NetworkService networkService) { - return CompletableFuture.supplyAsync(() -> { - logic.unregister(networkService); + if (action == Action.unregister) { + logic.unregister(networkService); + } else { + logic.register(networkService); + } return Response.noContent().build(); }); } diff --git a/fit/build-tools/pom.xml b/fit/build-tools/pom.xml index e823618e213..bf3e4316862 100644 --- a/fit/build-tools/pom.xml +++ b/fit/build-tools/pom.xml @@ -371,17 +371,19 @@ under the License. jakarta.faces ${javax.faces.version} - - - com.fasterxml.jackson.dataformat - jackson-dataformat-xml - clean verify cargo:run + + maven-war-plugin + + WEB-INF/lib/bval-*.jar + + + org.codehaus.cargo cargo-maven2-plugin diff --git a/fit/console-reference/src/main/webapp/WEB-INF/glassfish-web.xml b/fit/console-reference/src/main/webapp/WEB-INF/payara-web.xml similarity index 62% rename from fit/console-reference/src/main/webapp/WEB-INF/glassfish-web.xml rename to fit/console-reference/src/main/webapp/WEB-INF/payara-web.xml index 6400bc2d56f..6b646d090bb 100644 --- a/fit/console-reference/src/main/webapp/WEB-INF/glassfish-web.xml +++ b/fit/console-reference/src/main/webapp/WEB-INF/payara-web.xml @@ -16,10 +16,16 @@ software distributed under the License is distributed on an KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - --> - - + + + + - + + + + diff --git a/fit/core-reference/pom.xml b/fit/core-reference/pom.xml index f6c3a1592cb..8ff156fa5db 100644 --- a/fit/core-reference/pom.xml +++ b/fit/core-reference/pom.xml @@ -1519,7 +1519,6 @@ under the License. - payara-it @@ -1541,17 +1540,34 @@ under the License. jakarta.faces ${javax.faces.version} - - - com.fasterxml.jackson.dataformat - jackson-dataformat-xml - clean verify + + org.apache.maven.plugins + maven-antrun-plugin + true + + + adjust-buildtools-for-payara + prepare-package + + + + + + + + run + + + + + org.codehaus.cargo cargo-maven2-plugin diff --git a/fit/core-reference/src/main/webapp/WEB-INF/payara-web.xml b/fit/core-reference/src/main/webapp/WEB-INF/payara-web.xml index a32bda05d28..6b646d090bb 100644 --- a/fit/core-reference/src/main/webapp/WEB-INF/payara-web.xml +++ b/fit/core-reference/src/main/webapp/WEB-INF/payara-web.xml @@ -25,9 +25,6 @@ under the License. jdbc/syncopeMasterDataSource --> - * - * - false diff --git a/fit/core-reference/src/test/resources/mail.properties b/fit/core-reference/src/test/resources/mail.properties index 09300feecea..a7f9b01777f 100644 --- a/fit/core-reference/src/test/resources/mail.properties +++ b/fit/core-reference/src/test/resources/mail.properties @@ -22,7 +22,7 @@ smtpUser= smtpPassword= smtpProtocol=smtp smtpEncoding=UTF-8 -mail.debug=false +mail.debug=true # Add more properties starting with mail.smtp.* from # https://javaee.github.io/javamail/docs/api/com/sun/mail/smtp/package-summary.html#properties diff --git a/fit/enduser-reference/src/main/webapp/WEB-INF/glassfish-web.xml b/fit/enduser-reference/src/main/webapp/WEB-INF/payara-web.xml similarity index 62% rename from fit/enduser-reference/src/main/webapp/WEB-INF/glassfish-web.xml rename to fit/enduser-reference/src/main/webapp/WEB-INF/payara-web.xml index 6400bc2d56f..6b646d090bb 100644 --- a/fit/enduser-reference/src/main/webapp/WEB-INF/glassfish-web.xml +++ b/fit/enduser-reference/src/main/webapp/WEB-INF/payara-web.xml @@ -16,10 +16,16 @@ software distributed under the License is distributed on an KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - --> - - + + + + - + + + + diff --git a/pom.xml b/pom.xml index fdb48faf83d..1eb67bfa01c 100644 --- a/pom.xml +++ b/pom.xml @@ -509,7 +509,7 @@ under the License. 9.0.36 20.0.0.Final - 5.201 + 5.2020.2 2.3.14 12 From 73ecc26fc481ed5af51044efe429ade24ec3e42d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francesco=20Chicchiricc=C3=B2?= Date: Fri, 19 Jun 2020 18:20:49 +0200 Subject: [PATCH 2/4] No debug --- fit/core-reference/src/test/resources/mail.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fit/core-reference/src/test/resources/mail.properties b/fit/core-reference/src/test/resources/mail.properties index a7f9b01777f..09300feecea 100644 --- a/fit/core-reference/src/test/resources/mail.properties +++ b/fit/core-reference/src/test/resources/mail.properties @@ -22,7 +22,7 @@ smtpUser= smtpPassword= smtpProtocol=smtp smtpEncoding=UTF-8 -mail.debug=true +mail.debug=false # Add more properties starting with mail.smtp.* from # https://javaee.github.io/javamail/docs/api/com/sun/mail/smtp/package-summary.html#properties From 721de33eba57c04ba3e94726b76e683a5f6d36b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francesco=20Chicchiricc=C3=B2?= Date: Sat, 20 Jun 2020 06:55:28 +0200 Subject: [PATCH 3/4] Fix YAML --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2c109b976d7..c2a2b812faa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -92,7 +92,7 @@ jobs: - stage: fit name: "Integration Tests: Wildfly / H2 / JSON Content-Type" script: mvn -f fit/core-reference/pom.xml -P wildfly-it -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true - - stage: fit + - stage: fit name: "Integration Tests: Payara / H2 / JSON Content-Type" script: mvn -f fit/core-reference/pom.xml -P payara-it -Dinvoker.streamLogs=true -Dmodernizer.skip=true -Dianal.skip=true -Drat.skip=true -Dcheckstyle.skip=true -Djacoco.skip=true - stage: fit From 7de9079cf02f02c300b2b9a134dcc8d7767393c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francesco=20Chicchiricc=C3=B2?= Date: Sat, 20 Jun 2020 15:38:39 +0200 Subject: [PATCH 4/4] Different strategy, hopefully more Travis CI-friendly --- fit/core-reference/pom.xml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/fit/core-reference/pom.xml b/fit/core-reference/pom.xml index 8ff156fa5db..eb6ff137bd1 100644 --- a/fit/core-reference/pom.xml +++ b/fit/core-reference/pom.xml @@ -1556,9 +1556,11 @@ under the License. prepare-package - + + + todir="${project.build.outputDirectory}/syncope-fit-buildtools/WEB-INF/lib"/> @@ -1598,7 +1600,7 @@ under the License. - ${basedir}/../build-tools/target/syncope-fit-build-tools-${project.version} + ${project.build.outputDirectory}/syncope-fit-buildtools syncope-fit-build-tools