jdaugherty opened a new issue, #16550:
URL: https://github.com/apache/grails-core/issues/16550

   ### Steps to Reproduce
   
   In a Grails 8.1.x application (verified at b2b20a89e4 with Spring Boot 
4.1.1), `grails-app/conf/application.yml`:
   
   ```yaml
   grails:
       cors:
           enabled: true
           allowedOrigins:
               - https://grails.github.io
               - "${CORS_ALLOWED_ORIGIN:https://start.grails.org}";
   ```
   
   Start the application and look at the `GrailsCorsConfiguration` bean, which 
is `@ConfigurationProperties(prefix = 'grails.cors')`. Any 
`@ConfigurationProperties` bean with a `List<String>` property fed from a YAML 
list behaves the same.
   
   ### Expected Behaviour
   
   ```groovy
   allowedOrigins == ['https://grails.github.io', 'https://start.grails.org']
   ```
   
   or the value of `CORS_ALLOWED_ORIGIN` when it is set: the placeholder is 
resolved, as it is for a scalar property and as a Spring Boot application 
resolves it from the same YAML.
   
   ### Actual Behaviour
   
   ```groovy
   allowedOrigins == ['https://grails.github.io', 
'${CORS_ALLOWED_ORIGIN:https://start.grails.org}']
   ```
   
   The placeholder is bound as written, so the CORS filter never allows the 
origin. Found while porting the Forge web application to Grails, where the 
allowed origins are configured from environment variables this way.
   
   ### Cause
   
   `org.grails.config.NavigableMapPropertySource` presents a YAML list of 
scalars as one property whose value is the `List`. Spring Boot's `Binder` 
resolves placeholders only in a `String` value: 
`PropertySourcesPlaceholdersResolver.resolvePlaceholders` returns any other 
value untouched, and `IndexedElementsBinder` calls it on the whole-collection 
value before converting it, so the elements are copied as they are. Spring 
Boot's own YAML loader never produces a whole-list property; it presents 
`allowedOrigins[0]`, `allowedOrigins[1]`, … and the binder binds, and resolves, 
each element on its own.
   
   Since 12c05b5ce2, 379baff716 and b56b3f5226 (8.0.x and later) 
`NavigableMapPropertySource` presents a list *of objects* under those indexed 
names, so such a list binds correctly. A list of scalars, for which 
`isObjectList` is false, is still presented whole. `YamlPropertySourceLoader` 
is the same on 7.0.x, where no list is presented that way.
   
   Grails' own `Config` is not affected: 
`PropertySourcesConfig.processAndEvaluate` resolves the placeholders of list 
elements itself, but the Boot binder reads the property source, not the 
`Config`.
   
   ### Workarounds
   
   - Declare the list as one comma separated scalar; the binder resolves the 
string and splits it:
     ```yaml
     allowedOrigins: 
"https://grails.github.io,${CORS_ALLOWED_ORIGIN:https://start.grails.org}";
     ```
   - A `BeanPostProcessor` that passes the bound values through 
`Environment.resolvePlaceholders`.
   
   ### Suggested Fix
   
   Present a list of scalars under indexed names in 
`NavigableMapPropertySource` as well, as a list of objects already is, so that 
Spring Boot binds and resolves each element. `getNavigableProperty` and the 
Grails `Config` would keep returning the `List`.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to