Skip to content

Commit

Permalink
Merge in master
Browse files Browse the repository at this point in the history
  • Loading branch information
robotdan committed Jan 28, 2025
2 parents 3779dbe + c0b3543 commit 1228d7a
Show file tree
Hide file tree
Showing 12 changed files with 146 additions and 29 deletions.
6 changes: 3 additions & 3 deletions build.savant
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2014-2024, Inversoft Inc., All Rights Reserved
* Copyright (c) 2014-2025, Inversoft Inc., All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -19,7 +19,7 @@ dropWizardVersion = "3.2.6"
easyMockVersion = "5.2.0"
freemarkerVersion = "2.3.32"
fusionAuthJWTVersion = "5.3.2"
javaHTTPVersion = "0.4.0-RC.3"
javaHTTPVersion = "0.4.0-RC.4"
jsonPatchVersion = "1.13.0"
guavaVersion = "32.1.2-jre"
guiceVersion = "6.0.0"
Expand All @@ -29,7 +29,7 @@ logbackVersion = "1.4.14"
slf4jVersion = "2.0.13"
testngVersion = "7.8.0"

project(group: "org.primeframework", name: "prime-mvc", version: "5.0.0-RC.2", licenses: ["ApacheV2_0"]) {
project(group: "org.primeframework", name: "prime-mvc", version: "5.0.0-RC.3", licenses: ["ApacheV2_0"]) {
workflow {
fetch {
// Dependency resolution order:
Expand Down
6 changes: 3 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>org.primeframework</groupId>
<artifactId>prime-mvc</artifactId>
<version>5.0.0-RC.2</version>
<version>5.0.0-RC.3</version>
<packaging>jar</packaging>

<name>FusionAuth App</name>
Expand Down Expand Up @@ -109,7 +109,7 @@
<dependency>
<groupId>io.fusionauth</groupId>
<artifactId>java-http</artifactId>
<version>0.4.0-RC.3</version>
<version>0.4.0-RC.4</version>
<type>jar</type>
<scope>compile</scope>
<optional>false</optional>
Expand Down Expand Up @@ -212,4 +212,4 @@
</plugin>
</plugins>
</build>
</project>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ public String getMessage(String key, Object... values) throws MissingMessageExce
String message = getOptionalMessage(key, values);
if (message == null) {
ActionInvocation actionInvocation = invocationStore.getCurrent();
throw new MissingMessageException("Message could not be found for the URI [" + actionInvocation.actionURI + "] and key [" + key + "]");
String uri = actionInvocation != null ? actionInvocation.actionURI : null;
throw new MissingMessageException("Message could not be found for the URI [" + uri + "] and key [" + key + "]");
}

return message;
Expand All @@ -96,7 +97,8 @@ public String getOptionalMessage(String key, Object... values) {

if (template == null) {
if (!"[ValidationException]".equals(key)) {
logger.debug("Message could not be found for the URI [{}] and key [{}]", actionInvocation.actionURI, key);
String uri = actionInvocation != null ? actionInvocation.actionURI : null;
logger.debug("Message could not be found for the URI [{}] and key [{}]", uri, key);
}

return null;
Expand Down Expand Up @@ -131,12 +133,13 @@ protected Queue<String> determineBundles(String bundle) {
* @return The message or null if it doesn't exist.
*/
protected String findMessage(ActionInvocation actionInvocation, String key) {
String message = findMessage(actionInvocation.actionURI, key);
String actionURI = actionInvocation != null ? actionInvocation.actionURI : "/";
String message = findMessage(actionURI, key);
if (message != null) {
return message;
}

ActionConfiguration config = actionInvocation.configuration;
ActionConfiguration config = actionInvocation != null ? actionInvocation.configuration : null;
if (config == null) {
return null;
}
Expand Down
7 changes: 5 additions & 2 deletions src/test/java/messages/package.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#
# Copyright (c) 2001-2007, Inversoft Inc., All Rights Reserved
# Copyright (c) 2001-2025, Inversoft Inc., All Rights Reserved
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
Expand All @@ -14,4 +14,7 @@
# language governing permissions and limitations under the License.
#
key=Super Package Message
format_key=Super Package Message %s %s %s
format_key=Super Package Message %s %s %s

# Default messages
[blank]=Required
8 changes: 7 additions & 1 deletion src/test/java/org/example/action/SecureAction.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015-2017, Inversoft Inc., All Rights Reserved
* Copyright (c) 2015-2025, Inversoft Inc., All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -15,11 +15,14 @@
*/
package org.example.action;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import io.fusionauth.http.HTTPValues.Methods;
import org.primeframework.mvc.action.annotation.Action;
import org.primeframework.mvc.action.result.annotation.Status;
import org.primeframework.mvc.parameter.annotation.UnknownParameters;
import org.primeframework.mvc.security.annotation.ConstraintOverride;
import org.primeframework.mvc.security.annotation.ConstraintOverrideMethod;

Expand All @@ -34,6 +37,9 @@
@Status(code = "unauthorized", status = 403)
})
public class SecureAction {
@UnknownParameters
public static Map<String, String[]> UnknownParameters = new HashMap<>();

@ConstraintOverrideMethod(httpMethods = {Methods.PATCH})
public List<String> customConstraintsForPatch() {
return List.of("patch-only");
Expand Down
27 changes: 25 additions & 2 deletions src/test/java/org/primeframework/mvc/CSRFTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019-2024, Inversoft Inc., All Rights Reserved
* Copyright (c) 2019-2025, Inversoft Inc., All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -18,21 +18,40 @@
import java.util.Base64;

import com.google.inject.Inject;
import org.example.action.SecureAction;
import org.example.domain.User;
import org.primeframework.mvc.security.CBCCipherProvider;
import org.primeframework.mvc.security.DefaultEncryptor;
import org.primeframework.mvc.security.Encryptor;
import org.primeframework.mvc.security.MockUserLoginSecurityContext;
import org.primeframework.mvc.security.UserLoginSecurityContext;
import org.testng.annotations.Test;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;

/**
* @author Daniel DeGroff
*/
public class CSRFTest extends PrimeBaseTest {
@Inject public UserLoginSecurityContext securityContext;

@Test(enabled = false)
@Test
public void get_CSRFToken() {
MockUserLoginSecurityContext.roles.add("admin");
securityContext.login(new User());

configuration.csrfEnabled = true;
simulator.test("/secure")
.get()
.assertStatusCode(200)
.assertBody("Secure!");

// A GET request won't contain the parameter
// - This is just testing the RequestBuilder that will try and automatically add the CSRF token
assertFalse(SecureAction.UnknownParameters.containsKey(csrfProvider.getParameterName()));
}

@Test
public void post_CSRFOriginFailure() {
MockUserLoginSecurityContext.roles.add("admin");
securityContext.login(new User());
Expand Down Expand Up @@ -122,6 +141,10 @@ public void post_CSRFTokenCompatibility() throws Exception {
.post()
.assertStatusCode(200)
.assertBody("Secure!");

// A POST request will contain the CSRF token
// - This is just testing the RequestBuilder that will try and automatically add the CSRF token
assertTrue(SecureAction.UnknownParameters.containsKey(csrfProvider.getParameterName()));
}

// Add for testing legacy-encrypted CSRF token which is defined as a private class in DefaultEncryptionBasedTokenCSRFProvider
Expand Down
16 changes: 15 additions & 1 deletion src/test/java/org/primeframework/mvc/GlobalTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2001-2024, Inversoft Inc., All Rights Reserved
* Copyright (c) 2001-2025, Inversoft Inc., All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -814,6 +814,15 @@ public void get_metricsErrors() {
assertEquals(meters.get("prime-mvc.[*].errors").getCount(), 1);
}

@Test
public void get_modifyRequest() throws Exception {
// The Test HTTP request consumer will have added an HTTP request header.
simulator.test("/foo")
.get()
.assertStatusCode(200)
.custom(result -> assertEquals(result.request.getHeader("X-Test-HTTP-Request-Consumer"), "true"));
}

@Test
public void get_nested_parameters() throws Exception {
test.simulate(() -> simulator.test("/nested")
Expand Down Expand Up @@ -2273,6 +2282,11 @@ public void post_savedRequest_sameOriginAllowed() throws Exception {

// Redirected to login
.followRedirect(result -> result
// assert the request that was made included the correct Referer header
// - We could make this configurable, but for now, it is sending the full header, simulating a Refer policy of same-origin.
// See RequestResult._followRedirect notes.
.custom(() -> assertEquals(result.request.getHeader("Referer"),
"http://localhost:9080/store/allow-post-purchase"))
.assertStatusCode(200)
.assertHeaderContains("Cache-Control", "no-cache")
.assertBodyContains("Login"))
Expand Down
15 changes: 14 additions & 1 deletion src/test/java/org/primeframework/mvc/PrimeBaseTest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2024, Inversoft Inc., All Rights Reserved
* Copyright (c) 2012-2025, Inversoft Inc., All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -50,6 +50,7 @@
import io.fusionauth.http.server.HTTPRequest;
import io.fusionauth.http.server.HTTPResponse;
import io.fusionauth.http.server.HTTPServerConfiguration;
import org.example.action.SecureAction;
import org.example.action.user.EditAction;
import org.primeframework.mvc.action.ActionInvocation;
import org.primeframework.mvc.action.ExecuteMethodConfiguration;
Expand Down Expand Up @@ -84,6 +85,7 @@
import org.primeframework.mvc.security.UserLoginSecurityContext;
import org.primeframework.mvc.security.VerifierProvider;
import org.primeframework.mvc.security.csrf.CSRFProvider;
import org.primeframework.mvc.test.RequestBuilder.HTTPRequestConsumer;
import org.primeframework.mvc.test.RequestSimulator;
import org.primeframework.mvc.util.ThrowingRunnable;
import org.primeframework.mvc.validation.Validation;
Expand Down Expand Up @@ -210,6 +212,7 @@ public void beforeMethod() {

// Reset
EditAction.getCalled = false;
SecureAction.UnknownParameters.clear();

TestUnhandledExceptionHandler.reset();
}
Expand All @@ -222,6 +225,7 @@ protected void configure() {
super.configure();
install(new TestMVCConfigurationModule());
bind(CORSConfigurationProvider.class).to(TestCORSConfigurationProvider.class).in(Singleton.class);
bind(HTTPRequestConsumer.class).to(TestHTTPRequestConsumer.class);
bind(MessageObserver.class).toInstance(messageObserver);
bind(MetricRegistry.class).toInstance(metricRegistry);
bind(UserLoginSecurityContext.class).to(MockUserLoginSecurityContext.class);
Expand Down Expand Up @@ -341,6 +345,15 @@ protected void configure() {
}
}

public static class TestHTTPRequestConsumer implements HTTPRequestConsumer {
/**
* @param httpRequest the http request
*/
public void accept(HTTPRequest httpRequest) {
httpRequest.setHeader("X-Test-HTTP-Request-Consumer", "true");
}
}

@SuppressWarnings("unused")
public static class TestListener implements ITestListener {
private final static AtomicInteger TestCounter = new AtomicInteger(0);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2001-2024, Inversoft Inc., All Rights Reserved
* Copyright (c) 2001-2025, Inversoft Inc., All Rights Reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -142,6 +142,31 @@ public void missing() {
verify(store);
}

@Test
public void noActionInvocation() {
HTTPContext context = new HTTPContext(Path.of("src/test/java"));
ActionInvocationStore store = createStrictMock(ActionInvocationStore.class);
expect(store.getCurrent()).andReturn(null).times(3);
replay(store);

LocaleProvider localeProvider = createStrictMock(LocaleProvider.class);
expect(localeProvider.get()).andReturn(Locale.US).anyTimes();
replay(localeProvider);

ResourceBundleMessageProvider provider = new ResourceBundleMessageProvider(localeProvider, new WebControl(new ServletContainerResolver(context), configuration), store);
assertEquals(provider.getMessage("[blank]foo.bar"), "Required");

// Really missing
try {
provider.getMessage("[not_found]bar");
fail("Should have failed");
} catch (MissingMessageException e) {
// Expected
}

verify(store);
}

@Test
public void search() {
HTTPContext context = new HTTPContext(Path.of("src/test/java"));
Expand Down
Loading

0 comments on commit 1228d7a

Please sign in to comment.