Files
ⳕⲛτⲉⲅⲥⲉⳏτⲟⲅ 🕵🏻 e5eb01abda WW-5642 fix(rest): authorize @StrutsParameter on record/creator-bound REST body properties (#1774)
* fix(rest): authorize @StrutsParameter on record/creator-bound REST body properties

ParameterAuthorizingModule enforces @StrutsParameter on REST/JSON body
deserialization by wrapping each property's deserializeAndSet/
deserializeSetAndReturn. Jackson never calls either method for
creator-bound properties (Java records, @JsonCreator constructors,
@ConstructorProperties) — it calls SettableBeanProperty#deserialize
directly, which is declared final and bypasses the wrapper entirely.
With struts.parameters.requireAnnotations enabled, any record-typed
field anywhere in a REST action's request body was populated with no
authorization check at all.

Add AuthorizingValueDeserializer, which wraps the property's value
deserializer instead of the property itself, and install it from
AuthorizingSettableBeanProperty#withValueDeserializer — scoped to
CreatorProperty so ordinary setter/field/builder properties, already
authorized via the existing wrapper, aren't checked twice.

* fix(rest): treat redaction-induced construction failures as unauthorized, not fatal

AuthorizingValueDeserializer substitutes null for a rejected creator-bound
property (record component, @JsonCreator/@ConstructorProperties param).
For reference-typed, unvalidated components this is a harmless stand-in
for "not set" -- but two cases turn that substitution into an unhandled
exception that crashes deserialization of the entire request body instead
of just dropping the unauthorized subtree:

- A record/constructor with its own non-null validation (e.g. a compact
  constructor doing Objects.requireNonNull) throws
  ValueInstantiationException when the redacted component reaches it.
- With DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES enabled, Jackson
  itself throws MismatchedInputException when a primitive-typed creator
  component is redacted to null.

Add RedactionAwareDeserializer, wrapping every bean-type deserializer via
a new BeanDeserializerModifier#modifyDeserializer hook. It tracks (via a
new redaction-scope stack in ParameterAuthorizationContext) whether the
object currently under construction had a property redacted by
authorization; if construction then throws, the object is treated as
unauthorized (returns null) instead of propagating the raw exception --
matching the same fail-closed outcome already used when a non-creator
nested property is rejected outright. A guard test confirms genuine,
unrelated validation failures (nothing redacted) still propagate
normally, so real client errors aren't masked.

Also verified (and added regression coverage for) the other gaps raised
in review: static factory-method @JsonCreator, @ConstructorProperties,
top-level records, 3-level nested creator chains, and List/Map creator
params whose elements are further creator-bound or plain-POJO types --
all of these were already handled correctly by the existing
withValueDeserializer interception.

* test(rest): cover array creator param; document redaction edge cases

Addresses the three non-blocking review notes on WW-5642:

- Add testArrayOfRecordsAsCreatorParam_elementsAuthorizedByIndexedPath
  and a WithArray fixture, exercising the type.isArray() branch of
  AuthorizingValueDeserializer#prefixForNested so the collection matrix
  (List/Map/array) is fully covered.
- Document in AuthorizingValueDeserializer that redacting a primitive
  creator component becomes the type default (0/false) when
  FAIL_ON_NULL_FOR_PRIMITIVES is off -- a deliberate choice, the client
  value never lands either way.
- Document in RedactionAwareDeserializer that a redaction co-located with
  an unrelated mapping error is folded into "object dropped" -- a
  deliberate fail-closed trade-off, never exposing a partial object.

---------

Co-authored-by: g0w6y <g0w6y@users.noreply.github.com>
2026-07-17 11:51:47 +02:00
..
2026-06-26 07:18:26 +02:00

Struts 2 REST plugin

The REST Plugin provides high level support for the implementation of RESTful resource based web applications with the Convention Plugin. You will find more details in documentation.

Installation

Just drop this plugin JAR into WEB-INF/lib folder or add it as a Maven dependency.