diff --git a/compose/service/record.go b/compose/service/record.go index b9c6ae012..cca29ceeb 100644 --- a/compose/service/record.go +++ b/compose/service/record.go @@ -625,7 +625,14 @@ func RecordValueMerger(ctx context.Context, ac recordValueAccessController, m *t rve := &types.RecordValueErrorSet{} _ = vv.Walk(func(v *types.RecordValue) error { - if v.IsUpdated() && !ac.CanUpdateRecordValue(ctx, m.Fields.FindByName(v.Name)) { + f := m.Fields.FindByName(v.Name) + + // when f is nil, the module field was deleted so we shouldn't do any AC + if f == nil { + return nil + } + + if v.IsUpdated() && !ac.CanUpdateRecordValue(ctx, f) { rve.Push(types.RecordValueError{Kind: "updateDenied", Meta: map[string]interface{}{"field": v.Name, "value": v.Value}}) } diff --git a/tests/compose/record_test.go b/tests/compose/record_test.go index 32707edc6..25f1ed0fd 100644 --- a/tests/compose/record_test.go +++ b/tests/compose/record_test.go @@ -343,6 +343,49 @@ func TestRecordUpdate(t *testing.T) { h.a.NotNil(r) } +func TestRecordUpdate_missingField(t *testing.T) { + h := newHelper(t) + h.clearRecords() + + module := h.repoMakeRecordModuleWithFields("record testing module", + &types.ModuleField{Name: "f1", Kind: "String"}, + &types.ModuleField{Name: "f2", Kind: "String"}, + ) + record := h.makeRecord(module, + &types.RecordValue{Name: "f1", Value: "f1.v1"}, + &types.RecordValue{Name: "f2", Value: "f2.v1"}, + ) + _ = record + h.allow(types.ModuleRBACResource.AppendWildcard(), "update") + h.allow(types.ModuleRBACResource.AppendWildcard(), "record.update") + + api := h.apiInit() + + // delete f2 + api. + Post(fmt.Sprintf("/namespace/%d/module/%d", module.NamespaceID, module.ID)). + JSON(fmt.Sprintf(`{"name": "%s", "handle": "%s", "fields": [{ "name": "f1", "kind": "String" }]}`, module.Name, module.Handle)). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertNoErrors). + End() + + // update f1 + api. + Post(fmt.Sprintf("/namespace/%d/module/%d/record/%d", module.NamespaceID, module.ID, record.ID)). + JSON(fmt.Sprintf(`{"values": [{"name": "f1", "value": "f1.v1 (edited)"}]}`)). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertNoErrors). + End() + + r := h.lookupRecordByID(module, record.ID) + h.a.NotNil(r) + h.a.Len(r.Values, 1) + h.a.Equal("f1", r.Values[0].Name) + h.a.Equal("f1.v1 (edited)", r.Values[0].Value) +} + func TestRecordUpdate_refUnchanged(t *testing.T) { h := newHelper(t) h.clearRecords()