fix(api/v2): omit project max_permission (null) when not expanded

The project read handler left MaxPermission at its zero value when
expand=permissions was not requested, which serialised as 0
(PermissionRead) instead of being omitted. Force PermissionUnknown so
the field marshals as null, matching the list operation's behaviour and
avoiding a misleading read permission for projects the caller may own.

Assert the null shape in the ReadOne/Normal webtest.
This commit is contained in:
kolaente 2026-06-03 20:22:57 +02:00 committed by kolaente
parent 0a7750ee3d
commit 2f68a3fae4
2 changed files with 9 additions and 1 deletions

View File

@ -126,9 +126,14 @@ func projectsRead(ctx context.Context, in *struct {
}
// ReadOne doesn't act on Expand itself; the caller's max permission comes
// from DoReadOne's CanRead result. Surface it on the model only when asked,
// matching the list operation's expand=permissions behaviour.
// matching the list operation's expand=permissions behaviour. When not
// expanded, force PermissionUnknown so max_permission marshals as null
// instead of defaulting to the zero value (0 = PermissionRead), which would
// be a misleading "read" permission for projects the caller may fully own.
if models.ProjectExpandable(in.Expand) == models.ProjectExpandableRights {
project.MaxPermission = models.Permission(maxPermission)
} else {
project.MaxPermission = models.PermissionUnknown
}
// PreconditionFailed wants the unquoted etag; response header uses RFC 9110 quoted form.
etag := fmt.Sprintf("%d-%d", project.ID, project.Updated.UnixNano())

View File

@ -69,6 +69,9 @@ func TestHumaProject(t *testing.T) {
assert.Contains(t, rec.Body.String(), `"title":"Test1"`)
assert.NotContains(t, rec.Body.String(), `"title":"Test2"`)
assert.Contains(t, rec.Body.String(), `"username":"user1"`)
// Without expand=permissions, max_permission must be null rather than
// defaulting to 0 (which would falsely read as PermissionRead).
assert.Contains(t, rec.Body.String(), `"max_permission":null`)
assert.NotEmpty(t, rec.Result().Header.Get("ETag"))
})
t.Run("Expand permissions", func(t *testing.T) {