3
0

Fix setting record with default values

This commit is contained in:
Denis Arh
2021-07-30 14:23:45 +02:00
parent 6eeccaffeb
commit 0b8ae1dac7
2 changed files with 156 additions and 0 deletions
+20
View File
@@ -821,6 +821,26 @@ func (svc record) Create(ctx context.Context, new *types.Record) (rec *types.Rec
func (svc record) procCreate(ctx context.Context, invokerID uint64, m *types.Module, new *types.Record) (rve *types.RecordValueErrorSet) {
new.Values.SetUpdatedFlag(true)
new.Values.Walk(func(v *types.RecordValue) error {
f := m.Fields.FindByName(v.Name)
if f == nil {
return nil
}
d := f.DefaultValue.Get("", v.Place)
if d == nil {
// just so that we do not miss any defaults that MIGHT have
// field name set to it
// this is highly unlikely but it does not hurt to try
d = f.DefaultValue.Get(v.Name, v.Place)
}
// Mark as updated ONLY if value set is different from the default one
v.Updated = d == nil || d.Value != v.Value
return nil
})
// Reset values to new record
// to make sure nobody slips in something we do not want
new.ID = nextID()
+136
View File
@@ -297,9 +297,145 @@ func TestRecord_boolFieldPermissionIssueKBR(t *testing.T) {
req.NotNil(recChecked.Values.Get("bool", 0), "should checked again")
req.Equal("1", recChecked.Values.Get("bool", 0).Value, "should checked again")
}
}
func TestRecord_defValueFieldPermissionIssue(t *testing.T) {
var (
req = require.New(t)
ctx = context.Background()
s, err = sqlite3.ConnectInMemoryWithDebug(ctx)
)
t.Log("setting up the test environment")
req.NoError(err)
req.NoError(store.Upgrade(ctx, zap.NewNop(), s))
req.NoError(store.TruncateComposeNamespaces(ctx, s))
req.NoError(store.TruncateComposeModules(ctx, s))
req.NoError(store.TruncateComposeModuleFields(ctx, s))
req.NoError(store.TruncateComposeRecords(ctx, s, nil))
req.NoError(store.TruncateRbacRules(ctx, s))
var (
rbacService = rbac.NewService(
//zap.NewNop(),
logger.MakeDebugLogger(),
nil,
)
ac = &accessControl{rbac: rbacService}
svc = &record{
sanitizer: values.Sanitizer(),
formatter: values.Formatter(),
ac: ac,
store: s,
}
ns = &types.Namespace{ID: nextID()}
mod = &types.Module{ID: nextID(), NamespaceID: ns.ID}
writableField = &types.ModuleField{ID: nextID(), ModuleID: mod.ID, NamespaceID: ns.ID, Name: "writable", Kind: "String", DefaultValue: types.RecordValueSet{{Value: "def-w"}}}
readableField = &types.ModuleField{ID: nextID(), ModuleID: mod.ID, NamespaceID: ns.ID, Name: "readable", Kind: "String", DefaultValue: types.RecordValueSet{{Value: "def-r"}}}
userID = nextID()
authRoleID = nextID()
editorRole = &sysTypes.Role{Name: "editor", ID: nextID()}
recPartial *types.Record
valueExtractor = func(rec *types.Record, ff ...string) (out string) {
for _, f := range ff {
out += "<"
if v := rec.Values.Get(f, 0); v != nil {
out += v.Value
} else {
out += "NULL"
}
out += ">"
}
return
}
)
t.Log("creating namespace, module and fields")
svc.validator = defaultValidator(svc)
req.NoError(store.CreateComposeNamespace(ctx, s, ns))
req.NoError(store.CreateComposeModule(ctx, s, mod))
req.NoError(store.CreateComposeModuleField(ctx, s, writableField, readableField))
t.Log("setting up security")
rbacService.UpdateRoles(
rbac.CommonRole.Make(editorRole.ID, editorRole.Name),
rbac.AuthenticatedRole.Make(authRoleID, "authenticated"),
)
rbacService.Grant(ctx,
// base permissions
rbac.AllowRule(authRoleID, mod.RbacResource(), "record.create"),
rbac.AllowRule(authRoleID, types.RecordRbacResource(0, 0, 0), "read"),
rbac.AllowRule(authRoleID, types.RecordRbacResource(0, 0, 0), "update"),
rbac.AllowRule(authRoleID, types.ModuleFieldRbacResource(0, 0, 0), "record.value.read"),
rbac.AllowRule(authRoleID, types.ModuleFieldRbacResource(0, 0, 0), "record.value.update"),
// expl. deny value updates for editor on readable field (still allowed to write on writable field)
rbac.DenyRule(editorRole.ID, writableField.RbacResource(), "record.value.update"),
)
{
t.Log("creating record with w/o editor role (expecting defaults")
ctx = auth.SetIdentityToContext(ctx, auth.Authenticated(userID, authRoleID))
recPartial, err = svc.Create(ctx, &types.Record{ModuleID: mod.ID, NamespaceID: ns.ID, Values: types.RecordValueSet{}})
req.NoError(errors.Unwrap(err))
req.Equal("<def-w><def-r>", valueExtractor(recPartial, "writable", "readable"))
t.Log("creating record with w/o editor role (must be able to crate & update record and modify both fields)")
recPartial, err = svc.Create(ctx, &types.Record{ModuleID: mod.ID, NamespaceID: ns.ID, Values: types.RecordValueSet{
&types.RecordValue{Name: "writable", Value: "w"},
&types.RecordValue{Name: "readable", Value: "r"},
}})
req.NoError(errors.Unwrap(err))
req.Equal("<w><r>", valueExtractor(recPartial, "writable", "readable"))
t.Log("updating record removing one of the values")
recPartial.Values = types.RecordValueSet{&types.RecordValue{Name: "writable", Value: "w2"}}
recPartial, err = svc.Update(ctx, recPartial)
req.NoError(errors.Unwrap(err))
req.Equal("<w2><NULL>", valueExtractor(recPartial, "writable", "readable"))
}
{
t.Log("creating record with editor role (expecting defaults")
ctx = auth.SetIdentityToContext(ctx, auth.Authenticated(userID, authRoleID, editorRole.ID))
recPartial, err = svc.Create(ctx, &types.Record{ModuleID: mod.ID, NamespaceID: ns.ID, Values: types.RecordValueSet{}})
req.NoError(errors.Unwrap(err))
req.Equal("<def-w><def-r>", valueExtractor(recPartial, "writable", "readable"))
t.Log("creating record with editor role (must be able to crate & update record and modify both fields)")
recPartial, err = svc.Create(ctx, &types.Record{ModuleID: mod.ID, NamespaceID: ns.ID, Values: types.RecordValueSet{
// this is the def. value set
&types.RecordValue{Name: "writable", Value: "def-w"},
&types.RecordValue{Name: "readable", Value: "r"},
}})
req.NoError(errors.Unwrap(err))
req.Equal("<def-w><r>", valueExtractor(recPartial, "writable", "readable"))
}
}
func TestRecord_refAccessControl(t *testing.T) {