Lukasz Lenart created WW-5626:
---------------------------------
Summary: Refactor JSON/REST @StrutsParameter enforcement to
per-property authorization
Key: WW-5626
URL: https://issues.apache.org/jira/browse/WW-5626
Project: Struts 2
Issue Type: Improvement
Components: Plugin - JSON, Plugin - REST
Reporter: Lukasz Lenart
Fix For: 7.2.0
Follow-up to WW-5624. The initial fix enforces {{@StrutsParameter}} on
JSON/REST request bodies via post-hoc reflection — {{ContentTypeInterceptor}}
deserializes into a fresh instance, then recursively copies only authorized
properties. This works but has several drawbacks:
* ~250 lines of reflection-based copy logic in {{ContentTypeInterceptor}}
({{copyAuthorizedProperties}}, {{deepCopyAuthorizedCollection}},
{{deepCopyAuthorizedMap}}, {{deepCopyAuthorizedArray}})
* Requires a public no-arg constructor on target types; otherwise body
deserialization is rejected entirely
* Relies on a fragile package-name heuristic ({{isNestedBeanType}}) to
distinguish nested beans from leaf types — may misclassify third-party value
types
h2. Proposed solution
Replace the two-phase copy with per-property authorization performed _during_
deserialization, so unauthorized fields are never set.
* New optional interface {{AuthorizationAwareContentTypeHandler}} extending
{{ContentTypeHandler}}, exposing a property-level authorization callback
* {{ContentTypeInterceptor}} checks {{instanceof}} and uses property-level
filtering when available; falls back to the existing two-phase copy for
handlers that don't implement it (backward compatible)
* Jackson handlers ({{JacksonJsonHandler}}, {{JacksonXmlHandler}}) register a
{{SimpleModule}} with a {{BeanDeserializerModifier}} that wraps each
{{SettableBeanProperty}} with an authorizing decorator, consulting
{{ParameterAuthorizer.isAuthorized(path, target, action)}} before
{{deserializeAndSet()}}
* {{XStreamHandler}} uses an equivalent mechanism (e.g.
{{xstream.omitField()}} pre-pass or a custom {{ReflectionConverter}})
* Authorization context propagated via {{ThreadLocal}}, consistent with the
existing {{ActionContext}} pattern
h2. Cleanup also included
Minor follow-ups from the WW-5624 review:
* Remove redundant {{ModelDriven}} resolution in
{{ParametersInterceptor.isParameterAnnotatedAndAllowlist}} (now handled by
{{StrutsParameterAuthorizer}})
* Replace mock-based REST integration tests with real {{JacksonJsonHandler}}
tests that assert actual property filtering behavior
* Guard the unchecked {{String}} cast on JSON map keys in
{{JSONInterceptor.filterUnauthorizedKeysRecursive}}
h2. Outcome
* Removes ~200 lines of reflection-based copy logic
* Lifts the no-arg constructor requirement
* Authorization happens at the right layer (the deserializer), matching
{{ParametersInterceptor}}'s per-parameter check semantics
--
This message was sent by Atlassian Jira
(v8.20.10#820010)