diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml new file mode 100644 index 0000000..99b2d28 --- /dev/null +++ b/.github/workflows/maven.yml @@ -0,0 +1,17 @@ +# This workflow will build a Java project with Maven +# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven + +name: Java CI with Maven + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + call-workflow: + uses: 42BV/42-github-workflows/.github/workflows/maven-test.yml@main + with: + java-version: 21 + secrets: inherit diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..8658ccc --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,24 @@ +name: Publish package to the Maven Central Repository + +on: + workflow_dispatch: + inputs: + release-version: + required: false + description: Release-version (not required) + next-version: + required: false + description: Next development-version. (not required) + java-version: + required: true + default: '21' + description: Java-version to use for the deployment. + +jobs: + call-workflow: + uses: 42BV/42-github-workflows/.github/workflows/maven-release.yml@main + secrets: inherit + with: + release-version: ${{ github.event.inputs.release-version }} + next-version: ${{ github.event.inputs.next-version }} + java-version: ${{ github.event.inputs.java-version }} diff --git a/README.md b/README.md index 463c08a..24ae76d 100644 --- a/README.md +++ b/README.md @@ -17,16 +17,16 @@ Add the Maven dependency: nl.42.restzilla restzilla - 2.0.0 + 5.0.0 ``` Required dependencies: -* Spring MVC (5.1+) -* Spring Data JPA (2.1+) -* Jackson (2.9+) -* Java (1.8+) +* Spring MVC (7+) +* Spring Data JPA (4+) +* Jackson (2.21+) +* Java (21+) Annotate your Spring Configuration with `@EnableRest`: diff --git a/owasp-suppressions.xml b/owasp-suppressions.xml new file mode 100644 index 0000000..442d77a --- /dev/null +++ b/owasp-suppressions.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 90f718f..8863f33 100644 --- a/pom.xml +++ b/pom.xml @@ -36,12 +36,20 @@ ${maven.build.timestamp} UTF-8 - 17 + 21 - 3.0.5 + 4.0.6 5.0.0 3.18.0 + + + 12.2.2 + 0.8.14 + 0.9.0 + 3.2.8 + 3.4.0 + 3.12.0 @@ -74,6 +82,11 @@ commons-lang3 ${commons-lang.version} + + com.google.guava + guava + 33.6.0-jre + @@ -154,102 +167,123 @@ - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.10.1 - - ${java.version} - ${java.version} - ${project.build.sourceEncoding} - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.22.1 - - - - org.apache.maven.plugins - maven-deploy-plugin - 3.0.0 - - - org.apache.maven.plugins - maven-release-plugin - 2.5.3 - - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.13 - true + org.apache.maven.plugins + maven-compiler-plugin + 3.15.0 - sonatype-nexus-staging - https://oss.sonatype.org/ - false + ${java.version} + ${java.version} + ${java.version} + + + org.projectlombok + lombok + + org.apache.maven.plugins - maven-javadoc-plugin - 3.4.1 - - - attach-javadocs - - jar - - - + maven-surefire-plugin + 3.5.5 - org.apache.maven.plugins - maven-source-plugin - 3.2.1 + org.owasp + dependency-check-maven + ${dependency-check-maven.version} + + 0 + true + true + true + false + owasp-suppressions.xml + + + + org.jacoco + jacoco-maven-plugin + ${jacoco-maven-plugin.version} - attach-sources - jar-no-fork + prepare-agent - - - - org.apache.maven.plugins - maven-gpg-plugin - 3.0.1 - - sign-artifacts - verify + report + test - sign + report - - org.owasp - dependency-check-maven - 8.1.2 - - true - true - true - true - false - owasp-suppressions.xml - - + + + release + + + + org.apache.maven.plugins + maven-javadoc-plugin + ${maven-javadoc-plugin.version} + + + attach-javadocs + + jar + + + + + + + org.apache.maven.plugins + maven-source-plugin + ${maven-source-plugin.version} + + + attach-sources + + jar-no-fork + + + + + + + org.apache.maven.plugins + maven-gpg-plugin + ${maven-gpg-plugin.version} + + + sign-artifacts + verify + + sign + + + + + + + org.sonatype.central + central-publishing-maven-plugin + ${central-publishing-maven-plugin.version} + true + + central + + + + + + + diff --git a/src/main/java/nl/_42/restzilla/web/security/SpelSecurityProvider.java b/src/main/java/nl/_42/restzilla/web/security/SpelSecurityProvider.java index ea13f5d..a9325af 100644 --- a/src/main/java/nl/_42/restzilla/web/security/SpelSecurityProvider.java +++ b/src/main/java/nl/_42/restzilla/web/security/SpelSecurityProvider.java @@ -13,11 +13,14 @@ import org.springframework.expression.ExpressionParser; import org.springframework.security.access.expression.ExpressionUtils; import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.authorization.AuthorizationResult; import org.springframework.security.core.Authentication; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.FilterInvocation; -import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler; +import org.springframework.security.web.access.expression.DefaultHttpSecurityExpressionHandler; +import org.springframework.security.web.access.expression.WebExpressionAuthorizationManager; +import org.springframework.security.web.access.intercept.RequestAuthorizationContext; /** * Implementation that evaluates SPEL expressions. @@ -26,11 +29,6 @@ * @since Sep 8, 2015 */ public class SpelSecurityProvider implements SecurityProvider { - - /** - * Web security expression handler. - */ - private DefaultWebSecurityExpressionHandler handler = new DefaultWebSecurityExpressionHandler(); /** * {@inheritDoc} @@ -39,13 +37,13 @@ public class SpelSecurityProvider implements SecurityProvider { public boolean isAuthorized(String[] expressions, HttpServletRequest request) { boolean authorized = true; if (expressions.length > 0) { + RequestAuthorizationContext context = new RequestAuthorizationContext(request); Authentication authentication = getAuthentication(request); - FilterInvocation invocation = new FilterInvocation(request.getServletPath(), request.getMethod()); - EvaluationContext context = handler.createEvaluationContext(authentication, invocation); for (String expression : expressions) { if (StringUtils.isNotBlank(expression)) { - ExpressionParser parser = handler.getExpressionParser(); - if (!ExpressionUtils.evaluateAsBoolean(parser.parseExpression(expression), context)) { + WebExpressionAuthorizationManager manager = new WebExpressionAuthorizationManager(expression); + AuthorizationResult result = manager.authorize(() -> authentication, context); + if (!result.isGranted()) { return false; } } @@ -56,8 +54,8 @@ public boolean isAuthorized(String[] expressions, HttpServletRequest request) { private Authentication getAuthentication(HttpServletRequest request) { Principal principal = request.getUserPrincipal(); - if (principal instanceof Authentication) { - return (Authentication) principal; + if (principal instanceof Authentication authentication) { + return authentication; } else { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication == null) { @@ -70,14 +68,5 @@ private Authentication getAuthentication(HttpServletRequest request) { private AnonymousAuthenticationToken annonymous() { return new AnonymousAuthenticationToken("anonymousUser", "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); } - - /** - * Configure the default web security expression handler. - * @param handler the handler to set - */ - @Autowired(required = false) - public void setHandler(DefaultWebSecurityExpressionHandler handler) { - this.handler = handler; - } } diff --git a/src/test/java/nl/_42/restzilla/ApplicationConfig.java b/src/test/java/nl/_42/restzilla/ApplicationConfig.java index 00881f9..4bc3a56 100644 --- a/src/test/java/nl/_42/restzilla/ApplicationConfig.java +++ b/src/test/java/nl/_42/restzilla/ApplicationConfig.java @@ -8,7 +8,6 @@ import io.beanmapper.config.BeanMapperBuilder; import nl._42.restzilla.config.EnableRest; import nl._42.restzilla.web.GlobalExceptionHandler; -import org.hibernate.cfg.ImprovedNamingStrategy; import org.hibernate.dialect.HSQLDialect; import org.hibernate.jpa.HibernatePersistenceProvider; import org.springframework.beans.factory.annotation.Autowired; @@ -77,7 +76,6 @@ public LocalContainerEntityManagerFactoryBean entityManagerFactory() { entityManagerFactoryBean.setJpaVendorAdapter(jpaVendorAdapter); Map jpaProperties = new HashMap(); - jpaProperties.put("hibernate.ejb.naming_strategy", ImprovedNamingStrategy.class.getName()); jpaProperties.put("hibernate.dialect", hibernateDialect); jpaProperties.put("hibernate.hbm2ddl.auto", "create-drop"); jpaProperties.put("hibernate.jdbc.use_get_generated_keys", true); @@ -120,11 +118,6 @@ private MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter( return converter; } - @Bean - public Validator validator() { - return new CustomValidatorBean(); - } - @Bean public Cache cache() { return new ConcurrentMapCache("test"); diff --git a/src/test/java/nl/_42/restzilla/web/GlobalExceptionHandler.java b/src/test/java/nl/_42/restzilla/web/GlobalExceptionHandler.java index c8ad414..12c90a3 100644 --- a/src/test/java/nl/_42/restzilla/web/GlobalExceptionHandler.java +++ b/src/test/java/nl/_42/restzilla/web/GlobalExceptionHandler.java @@ -8,10 +8,12 @@ import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.servlet.ModelAndView; +import org.springframework.web.servlet.NoHandlerFoundException; import org.springframework.web.servlet.view.json.MappingJackson2JsonView; import static org.springframework.http.HttpStatus.FORBIDDEN; import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; +import static org.springframework.http.HttpStatus.NOT_FOUND; import static org.springframework.http.HttpStatus.UNPROCESSABLE_ENTITY; @ControllerAdvice @@ -19,6 +21,12 @@ public class GlobalExceptionHandler { private final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class); + @ExceptionHandler(NoHandlerFoundException.class) + public ModelAndView handleNoHandlerFound(HttpServletResponse response, NoHandlerFoundException ex) { + logger.error("Handler not found", ex); + return error(response, NOT_FOUND); + } + @ExceptionHandler(SecurityException.class) public ModelAndView handleSecurityException(HttpServletResponse response, SecurityException ex) { logger.error("Security exception", ex);