From 47e1a4acebce1727e1c8cd557e4c12495ac6a8a4 Mon Sep 17 00:00:00 2001 From: Tobie Morgan Hitchcock Date: Tue, 1 May 2018 12:11:32 +0100 Subject: [PATCH] Fix a bug with virtual records in scoped queries When querying a virtual document, it would check that the scope had permission to view the record, even though the record did not actually belong to a table. Not it checks to see if the record is a virtual in-memory record, and does not perform any permissions checks if this is the case. --- db/check.go | 18 ++++ db/perms.go | 9 ++ db/scope_test.go | 250 ++++++++++++++++++++++++++++++++++++++++++++++ db/select_test.go | 101 ++++++++++++++++++- 4 files changed, 377 insertions(+), 1 deletion(-) create mode 100644 db/scope_test.go diff --git a/db/check.go b/db/check.go index 52da42d2..9c0f4343 100644 --- a/db/check.go +++ b/db/check.go @@ -52,6 +52,15 @@ func (d *document) grant(ctx context.Context, met method) (ok bool, err error) { var val interface{} + // If this is a document loaded from + // a subquery or data param, and not + // from the KV store, then there is + // no need to check permissions. + + if d.key == nil { + return false, nil + } + // If we are authenticated using DB, NS, // or KV permissions level, then we can // ignore all permissions checks, but we @@ -123,6 +132,15 @@ func (d *document) allow(ctx context.Context, met method) (ok bool, err error) { var val interface{} + // If this is a document loaded from + // a subquery or data param, and not + // from the KV store, then there is + // no need to check permissions. + + if d.key == nil { + return true, nil + } + // If we are authenticated using DB, NS, // or KV permissions level, then we can // ignore all permissions checks, but we diff --git a/db/perms.go b/db/perms.go index f5d6322a..cd70344b 100644 --- a/db/perms.go +++ b/db/perms.go @@ -24,6 +24,15 @@ import ( func (d *document) perms(ctx context.Context, doc *data.Doc) (err error) { + // If this is a document loaded from + // a subquery or data param, and not + // from the KV store, then there is + // no need to check permissions. + + if d.key == nil { + return nil + } + // Get the field definitions so we can // check if the permissions allow us // to view each field. diff --git a/db/scope_test.go b/db/scope_test.go new file mode 100644 index 00000000..793f5e29 --- /dev/null +++ b/db/scope_test.go @@ -0,0 +1,250 @@ +// Copyright © 2016 Abcum Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package db + +import ( + "testing" + + "github.com/abcum/surreal/sql" + "github.com/abcum/surreal/util/data" + . "github.com/smartystreets/goconvey/convey" +) + +func TestScope(t *testing.T) { + + Convey("Select records from an array of strings", t, func() { + + setupDB() + + func() { + + txt := ` + USE NS test DB test; + DEFINE NAMESPACE test; + DEFINE DATABASE test; + ` + + res, err := Execute(setupKV(), txt, nil) + So(err, ShouldBeNil) + So(res, ShouldHaveLength, 3) + + }() + + func() { + + txt := ` + USE NS test DB test; + SELECT * FROM [ + "one", + "two", + "tre", + ]; + ` + + res, err := Execute(setupSC(), txt, nil) + So(err, ShouldBeNil) + So(res, ShouldHaveLength, 2) + So(res[1].Result, ShouldHaveLength, 3) + So(data.Consume(res[1].Result[0]).Data(), ShouldResemble, "one") + So(data.Consume(res[1].Result[1]).Data(), ShouldResemble, "two") + So(data.Consume(res[1].Result[2]).Data(), ShouldResemble, "tre") + + }() + + }) + + Convey("Select records from an array of objects with an id key", t, func() { + + setupDB() + + func() { + + txt := ` + USE NS test DB test; + DEFINE NAMESPACE test; + DEFINE DATABASE test; + ` + + res, err := Execute(setupKV(), txt, nil) + So(err, ShouldBeNil) + So(res, ShouldHaveLength, 3) + + }() + + func() { + + txt := ` + USE NS test DB test; + SELECT * FROM [ + { id: "one" }, + { id: "two" }, + { id: "tre" }, + ]; + ` + + res, err := Execute(setupSC(), txt, nil) + So(err, ShouldBeNil) + So(res, ShouldHaveLength, 2) + So(res[1].Result, ShouldHaveLength, 3) + So(data.Consume(res[1].Result[0]).Data(), ShouldResemble, map[string]interface{}{"id": "one"}) + So(data.Consume(res[1].Result[1]).Data(), ShouldResemble, map[string]interface{}{"id": "two"}) + So(data.Consume(res[1].Result[2]).Data(), ShouldResemble, map[string]interface{}{"id": "tre"}) + + }() + + }) + + Convey("Select records from an array of objects with no id key", t, func() { + + setupDB() + + func() { + + txt := ` + USE NS test DB test; + DEFINE NAMESPACE test; + DEFINE DATABASE test; + ` + + res, err := Execute(setupKV(), txt, nil) + So(err, ShouldBeNil) + So(res, ShouldHaveLength, 3) + + }() + + func() { + + txt := ` + USE NS test DB test; + SELECT * FROM [ + { test: "one" }, + { test: "two" }, + { test: "tre" }, + ]; + ` + + res, err := Execute(setupSC(), txt, nil) + So(err, ShouldBeNil) + So(res, ShouldHaveLength, 2) + So(res[1].Result, ShouldHaveLength, 3) + So(data.Consume(res[1].Result[0]).Data(), ShouldResemble, map[string]interface{}{"test": "one"}) + So(data.Consume(res[1].Result[1]).Data(), ShouldResemble, map[string]interface{}{"test": "two"}) + So(data.Consume(res[1].Result[2]).Data(), ShouldResemble, map[string]interface{}{"test": "tre"}) + + }() + + }) + + Convey("Select records from an array of virtual record things with no permissions", t, func() { + + setupDB() + + func() { + + txt := ` + USE NS test DB test; + DEFINE NAMESPACE test; + DEFINE DATABASE test; + CREATE test:one, test:two, test:tre; + ` + + res, err := Execute(setupKV(), txt, nil) + So(err, ShouldBeNil) + So(res, ShouldHaveLength, 4) + + }() + + func() { + + txt := ` + USE NS test DB test; + SELECT * FROM array( + thing("test", "one"), + thing("test", "two"), + thing("test", "tre") + ); + ` + + res, err := Execute(setupSC(), txt, nil) + So(err, ShouldBeNil) + So(res, ShouldHaveLength, 2) + So(res[1].Result, ShouldHaveLength, 0) + + }() + + }) + + Convey("Select records from an array of virtual record things with full permissions", t, func() { + + setupDB() + + func() { + + txt := ` + USE NS test DB test; + DEFINE NAMESPACE test; + DEFINE DATABASE test; + DEFINE TABLE test PERMISSIONS FULL; + CREATE test:one, test:two, test:tre; + ` + + res, err := Execute(setupKV(), txt, nil) + So(err, ShouldBeNil) + So(res, ShouldHaveLength, 5) + + }() + + func() { + + txt := ` + USE NS test DB test; + SELECT * FROM array( + thing("test", "one"), + thing("test", "two"), + thing("test", "tre") + ); + ` + + res, err := Execute(setupSC(), txt, nil) + So(err, ShouldBeNil) + So(res, ShouldHaveLength, 2) + So(res[1].Result, ShouldHaveLength, 3) + So(data.Consume(res[1].Result[0]).Data(), ShouldResemble, map[string]interface{}{ + "id": sql.NewThing("test", "one"), + "meta": map[string]interface{}{ + "tb": "test", + "id": "one", + }, + }) + So(data.Consume(res[1].Result[1]).Data(), ShouldResemble, map[string]interface{}{ + "id": sql.NewThing("test", "two"), + "meta": map[string]interface{}{ + "tb": "test", + "id": "two", + }, + }) + So(data.Consume(res[1].Result[2]).Data(), ShouldResemble, map[string]interface{}{ + "id": sql.NewThing("test", "tre"), + "meta": map[string]interface{}{ + "tb": "test", + "id": "tre", + }, + }) + + }() + + }) + +} diff --git a/db/select_test.go b/db/select_test.go index dad0a099..471caee2 100644 --- a/db/select_test.go +++ b/db/select_test.go @@ -92,7 +92,7 @@ func TestSelect(t *testing.T) { }) - Convey("Select records from multiple things", t, func() { + Convey("Select records from multiple tables", t, func() { setupDB() @@ -136,6 +136,105 @@ func TestSelect(t *testing.T) { }) + Convey("Select records from an array of strings", t, func() { + + setupDB() + + txt := ` + USE NS test DB test; + SELECT * FROM ["one", "two", "tre"]; + ` + + res, err := Execute(setupKV(), txt, nil) + So(err, ShouldBeNil) + So(res, ShouldHaveLength, 2) + So(res[1].Result, ShouldHaveLength, 3) + So(data.Consume(res[1].Result[0]).Data(), ShouldResemble, "one") + So(data.Consume(res[1].Result[1]).Data(), ShouldResemble, "two") + So(data.Consume(res[1].Result[2]).Data(), ShouldResemble, "tre") + + }) + + Convey("Select records from an array of objects with an id key", t, func() { + + setupDB() + + txt := ` + USE NS test DB test; + SELECT * FROM [{ id: "one" }, { id: "two" }, { id: "tre" }]; + ` + + res, err := Execute(setupKV(), txt, nil) + So(err, ShouldBeNil) + So(res, ShouldHaveLength, 2) + So(res[1].Result, ShouldHaveLength, 3) + So(data.Consume(res[1].Result[0]).Data(), ShouldResemble, map[string]interface{}{"id": "one"}) + So(data.Consume(res[1].Result[1]).Data(), ShouldResemble, map[string]interface{}{"id": "two"}) + So(data.Consume(res[1].Result[2]).Data(), ShouldResemble, map[string]interface{}{"id": "tre"}) + + }) + + Convey("Select records from an array of objects with no id key", t, func() { + + setupDB() + + txt := ` + USE NS test DB test; + SELECT * FROM [{ test: "one" }, { test: "two" }, { test: "tre" }]; + ` + + res, err := Execute(setupKV(), txt, nil) + So(err, ShouldBeNil) + So(res, ShouldHaveLength, 2) + So(res[1].Result, ShouldHaveLength, 3) + So(data.Consume(res[1].Result[0]).Data(), ShouldResemble, map[string]interface{}{"test": "one"}) + So(data.Consume(res[1].Result[1]).Data(), ShouldResemble, map[string]interface{}{"test": "two"}) + So(data.Consume(res[1].Result[2]).Data(), ShouldResemble, map[string]interface{}{"test": "tre"}) + + }) + + Convey("Select records from an array of virtual record things", t, func() { + + setupDB() + + txt := ` + USE NS test DB test; + CREATE test:one, test:two, test:tre; + SELECT * FROM array( + thing("test", "one"), + thing("test", "two"), + thing("test", "tre") + ); + ` + + res, err := Execute(setupKV(), txt, nil) + So(err, ShouldBeNil) + So(res, ShouldHaveLength, 3) + So(res[1].Result, ShouldHaveLength, 3) + So(data.Consume(res[1].Result[0]).Data(), ShouldResemble, map[string]interface{}{ + "id": sql.NewThing("test", "one"), + "meta": map[string]interface{}{ + "tb": "test", + "id": "one", + }, + }) + So(data.Consume(res[1].Result[1]).Data(), ShouldResemble, map[string]interface{}{ + "id": sql.NewThing("test", "two"), + "meta": map[string]interface{}{ + "tb": "test", + "id": "two", + }, + }) + So(data.Consume(res[1].Result[2]).Data(), ShouldResemble, map[string]interface{}{ + "id": sql.NewThing("test", "tre"), + "meta": map[string]interface{}{ + "tb": "test", + "id": "tre", + }, + }) + + }) + Convey("Select records with an embedded record field", t, func() { setupDB()