Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WW-5382 Fix stale injections in Dispatcher #826

Merged
merged 8 commits into from
Jan 6, 2024
Merged

Conversation

kusalk
Copy link
Member

@kusalk kusalk commented Dec 30, 2023

WW-5382

I understand most applications do not reload configuration after startup - but for applications that do, this is a critical bug as it means stale configuration persists after a reload. This extends to the SecurityMemberAccess policy which is now container injected.

@kusalk
Copy link
Member Author

kusalk commented Dec 30, 2023

Need to test this further in a staging environment and will improve test coverage to prevent similar regressions from reoccurring

@kusalk kusalk force-pushed the WW-5382-stale-config branch from bda8f01 to b9e3c5e Compare January 1, 2024 15:28
@kusalk kusalk force-pushed the WW-5382-stale-config branch from b9e3c5e to fa5b46c Compare January 1, 2024 15:57
@kusalk kusalk force-pushed the WW-5382-stale-config branch from fbd32f3 to 2c5087e Compare January 1, 2024 16:35
@kusalk kusalk force-pushed the WW-5382-stale-config branch from 2c5087e to 946737c Compare January 1, 2024 16:57
@@ -51,10 +51,6 @@ public void setUp() throws Exception {
@After
public void tearDown() throws Exception {
XWorkTestCaseHelper.tearDown(configurationManager);
configurationManager = null;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not necessary

@@ -325,12 +325,8 @@ public Class<? extends Configuration> type() {
}

protected ActionContext setContext(Container cont) {
ActionContext context = ActionContext.getContext();
if (context == null) {
Copy link
Member Author

@kusalk kusalk Jan 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is only called twice, and it is always null on the first call, and never null on the second call. I can't see why we wouldn't want to replace the bootstrap context once the final context has been constructed. Although it is also unclear if the ActionContext is even used after this point - i.e. for loading the PackageProviders. The ActionContext is cleared once the PackageProviders are loaded.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be honest I would love to re-organise the whole proces. I assume the ActionContext is only needed when serving an action, it should be created just before and destroyed just after.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah that would be ideal - if that were the case we'd be able to remove the need for the bootstrap context too it looks like

@@ -192,6 +194,11 @@ public class Dispatcher {
* Store ConfigurationManager instance, set on init.
*/
protected ConfigurationManager configurationManager;
private ObjectFactory objectFactory;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should save some computation by injecting these beans once when the Dispatcher is initialised instead of constantly retrieving them from the container

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe they should be moved into constructor?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the Dispatcher itself is not container-managed, we can't use constructor injection for it

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't have to be, just using container.inject(Dispatcher.class) will create a new instance and injects all the dependencies. Anyway this would be a bit out of scope for this PR.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep would require a major refactor, I've got some ideas but not a high priority right now, as long as everything works

@@ -988,18 +1031,7 @@ protected boolean isMultipartRequest(HttpServletRequest request) {
* @return a multi part request object
*/
protected MultiPartRequest getMultiPartRequest() {
MultiPartRequest mpr = null;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remnants from before the bean aliasing functionality existed I presume?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm... good question, no idea :)

}
}
if (injectedContainer != ContainerHolder.get()) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the core change of this PR - we compare the retrieved container instance against the one we last injected this with, and if it has changed, reinject

public static void tearDown() throws Exception {
Dispatcher.setInstance(null);
public static void tearDown(Dispatcher dispatcher) {
if (dispatcher != null && dispatcher.getConfigurationManager() != null) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved this here for better reuse

/**
* Test case for Dispatcher.
*/
public class DispatcherTest extends StrutsInternalTestCase {
public class DispatcherTest extends StrutsJUnit4InternalTestCase {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Upgraded to JUnit4 and replaced older mocks with Mockito

}

@Test
public void dispatcherReinjectedAfterReload() {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New test for reinjection functionality

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Changed line-endings to LF for this file)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same deal with line-endings

Copy link

sonarqubecloud bot commented Jan 2, 2024

Quality Gate Passed Quality Gate passed

The SonarCloud Quality Gate passed, but some issues were introduced.

8 New issues
0 Security Hotspots
87.3% Coverage on New Code
0.0% Duplication on New Code

See analysis details on SonarCloud

@kusalk
Copy link
Member Author

kusalk commented Jan 2, 2024

I've now tested this fix with the Confluence DC test suite and did some additional manual sanity testing and all seems to be as expected now. Following a ConfigurationManager#reload, all constants, including the exclusion list and allowlist, are now refreshed as per the latest ConfigurationProviders.

@kusalk kusalk marked this pull request as ready for review January 2, 2024 07:28
@kusalk kusalk requested a review from lukaszlenart January 2, 2024 07:34
Copy link
Member

@lukaszlenart lukaszlenart left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks ok, left a few comments

@@ -325,12 +325,8 @@ public Class<? extends Configuration> type() {
}

protected ActionContext setContext(Container cont) {
ActionContext context = ActionContext.getContext();
if (context == null) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be honest I would love to re-organise the whole proces. I assume the ActionContext is only needed when serving an action, it should be created just before and destroyed just after.

@@ -192,6 +194,11 @@ public class Dispatcher {
* Store ConfigurationManager instance, set on init.
*/
protected ConfigurationManager configurationManager;
private ObjectFactory objectFactory;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe they should be moved into constructor?

@@ -988,18 +1031,7 @@ protected boolean isMultipartRequest(HttpServletRequest request) {
* @return a multi part request object
*/
protected MultiPartRequest getMultiPartRequest() {
MultiPartRequest mpr = null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm... good question, no idea :)

@kusalk kusalk merged commit 3b07899 into master Jan 6, 2024
9 checks passed
@kusalk kusalk deleted the WW-5382-stale-config branch January 6, 2024 15:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants