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)

Reply via email to