codeconsole opened a new pull request, #16291:
URL: https://github.com/apache/grails-core/pull/16291
Grails 8 makes an unconstrained persistent property nullable by default
(documented in `upgrading80x.adoc`, "GORM Properties Are Nullable by Default").
The `spring-boot-starter-security` `User` template was never updated for that
flip, so its `blank: false` constraints no longer make the credentials required
— a `User` with a null username and a null password validates clean, and the
scaffolded user admin will save one.
Every other domain template in the forge already declares `nullable: false`,
which is what makes this look like a miss rather than a choice:
| Template | Constraint |
|---|---|
| `role.rocker.raw` | `authority unique: true, nullable: false` |
| `userClassic.rocker.raw` | `username unique: true, nullable: false` /
`password nullable: false` |
| `userRole.rocker.raw` | `user nullable: false` / `role nullable: false,
unique: 'user'` |
| `user.rocker.raw` | `username blank: false, unique: true` ← missing |
```groovy
static constraints = {
username nullable: false, blank: false, unique: true
password nullable: false, blank: false, password: true
}
```
## The generated spec failed out of the box
Creating an app with this feature produced a red build on the very first
`./gradlew test`:
```
UserSpec > username and password are required FAILED
Condition not satisfied:
!new User(username: '', password: '').validate()
|| |
|example.User : (unsaved) true
false
```
The assertion exercised neither constraint it appeared to test. The domain
map constructor binds through data binding with `convertEmptyStringsToNull`, so
`username: ''` arrives as `null` — the `blank` constraint never sees an empty
string — and nullable-by-default then let the nulls through.
The spec now covers the two constraints as separate features, assigning the
blank values directly since that is the only way a blank string actually
reaches the property:
```groovy
void 'a user whose username and password are blank is rejected'() {
given: 'the values are assigned directly, since the map constructor
binds an empty string to null'
User user = new User()
user.username = ''
user.password = ''
expect:
!user.validate()
and:
with(user.errors) {
getFieldError('username').code == 'blank'
getFieldError('password').code == 'blank'
}
}
```
`SpringBootStarterSecuritySpec` asserted on the old constraint string and is
updated to match.
## Verification
Generated a real app (`create-app --data mongodb --features
spring-boot-starter-security --jdk 25`) and ran it, rather than only asserting
on template text:
- `:grails-forge-core:test` — 343 tests, 0 failures
- `grails-forge` `codeStyle` — clean
- Generated app's `UserSpec` — 4/4 pass
- Reverting `nullable: false` and re-running makes the nullable feature
fail, so the new spec is not vacuous. The blank feature passes either way —
`blank: false` was always correct; the nullable gap was the whole defect.
The app was also exercised end to end on embedded MongoDB: `/` anonymous
200, `/user` anonymous redirects to login, form login succeeds, `/user` as
`ROLE_ADMIN` 200, `/user` as `ROLE_USER` 403.
## Not addressed here
`user.rocker.raw` declares `static mapping = { table 'users' }`, a Hibernate
directive, but the template is also generated for the `mongodb` data
implementation where the equivalent is `collection`. It is inert today — the
app boots and persists fine — so I left it out of this change rather than widen
the scope.
--
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]