Skip to content

Commit 1f58832

Browse files
committed
PR(MAIN): Gate & Test - Collection Doc Operations
1 parent ede1639 commit 1f58832

14 files changed

Lines changed: 912 additions & 0 deletions

acp/types/types.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ const (
9797
NodeIndexListPerm
9898
NodeIndexCreatePerm
9999
NodeIndexDropPerm
100+
NodeDocReadPerm
101+
NodeDocUpdatePerm
102+
NodeDocDeletePerm
100103
NodeGetIdentityPerm
101104
NodeVerifySignaturePerm
102105
)
@@ -128,6 +131,9 @@ var RequiredResourcePermissionsForNode = []string{
128131
"index-list",
129132
"index-create",
130133
"index-drop",
134+
"doc-read",
135+
"doc-update",
136+
"doc-delete",
131137
"node-get-identity",
132138
"verify-signature",
133139
}
@@ -192,6 +198,12 @@ resources:
192198
expr: owner + admin
193199
index-drop:
194200
expr: owner + admin
201+
doc-read:
202+
expr: owner + admin
203+
doc-update:
204+
expr: owner + admin
205+
doc-delete:
206+
expr: owner + admin
195207
node-get-identity:
196208
expr: owner + admin
197209
verify-signature:

internal/db/collection.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,10 @@ func (c *collection) GetAllDocIDs(
253253
ctx, span := tracer.Start(ctx)
254254
defer span.End()
255255

256+
if err := c.db.checkNodeAccess(ctx, acpTypes.NodeDocReadPerm); err != nil {
257+
return nil, err
258+
}
259+
256260
ctx, _, err := ensureContextTxn(ctx, c.db, true)
257261
if err != nil {
258262
return nil, err
@@ -388,6 +392,10 @@ func (c *collection) Create(
388392
ctx, span := tracer.Start(ctx)
389393
defer span.End()
390394

395+
if err := c.db.checkNodeAccess(ctx, acpTypes.NodeDocUpdatePerm); err != nil {
396+
return err
397+
}
398+
391399
ctx, txn, err := ensureContextTxn(ctx, c.db, false)
392400
if err != nil {
393401
return err
@@ -412,6 +420,10 @@ func (c *collection) CreateMany(
412420
ctx, span := tracer.Start(ctx)
413421
defer span.End()
414422

423+
if err := c.db.checkNodeAccess(ctx, acpTypes.NodeDocUpdatePerm); err != nil {
424+
return err
425+
}
426+
415427
ctx, txn, err := ensureContextTxn(ctx, c.db, false)
416428
if err != nil {
417429
return err
@@ -532,6 +544,10 @@ func (c *collection) Update(
532544
ctx, span := tracer.Start(ctx)
533545
defer span.End()
534546

547+
if err := c.db.checkNodeAccess(ctx, acpTypes.NodeDocUpdatePerm); err != nil {
548+
return err
549+
}
550+
535551
ctx, txn, err := ensureContextTxn(ctx, c.db, false)
536552
if err != nil {
537553
return err
@@ -606,6 +622,10 @@ func (c *collection) Save(
606622
ctx, span := tracer.Start(ctx)
607623
defer span.End()
608624

625+
if err := c.db.checkNodeAccess(ctx, acpTypes.NodeDocUpdatePerm); err != nil {
626+
return err
627+
}
628+
609629
ctx, txn, err := ensureContextTxn(ctx, c.db, false)
610630
if err != nil {
611631
return err
@@ -951,6 +971,10 @@ func (c *collection) Delete(
951971
ctx, span := tracer.Start(ctx)
952972
defer span.End()
953973

974+
if err := c.db.checkNodeAccess(ctx, acpTypes.NodeDocDeletePerm); err != nil {
975+
return false, err
976+
}
977+
954978
ctx, txn, err := ensureContextTxn(ctx, c.db, false)
955979
if err != nil {
956980
return false, err
@@ -982,6 +1006,10 @@ func (c *collection) Exists(
9821006
ctx, span := tracer.Start(ctx)
9831007
defer span.End()
9841008

1009+
if err := c.db.checkNodeAccess(ctx, acpTypes.NodeDocReadPerm); err != nil {
1010+
return false, err
1011+
}
1012+
9851013
ctx, txn, err := ensureContextTxn(ctx, c.db, false)
9861014
if err != nil {
9871015
return false, err

internal/db/collection_delete.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ func (c *collection) DeleteWithFilter(
3333
ctx, span := tracer.Start(ctx)
3434
defer span.End()
3535

36+
if err := c.db.checkNodeAccess(ctx, acpTypes.NodeDocDeletePerm); err != nil {
37+
return nil, err
38+
}
39+
3640
ctx, txn, err := ensureContextTxn(ctx, c.db, false)
3741
if err != nil {
3842
return nil, err

internal/db/collection_get.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616
"github.com/sourcenetwork/immutable"
1717

1818
"github.com/sourcenetwork/defradb/acp/identity"
19+
acpTypes "github.com/sourcenetwork/defradb/acp/types"
1920
"github.com/sourcenetwork/defradb/client"
2021
"github.com/sourcenetwork/defradb/internal/datastore"
2122
"github.com/sourcenetwork/defradb/internal/db/fetcher"
@@ -31,6 +32,10 @@ func (c *collection) Get(
3132
ctx, span := tracer.Start(ctx)
3233
defer span.End()
3334

35+
if err := c.db.checkNodeAccess(ctx, acpTypes.NodeDocReadPerm); err != nil {
36+
return nil, err
37+
}
38+
3439
// create txn
3540
ctx, txn, err := ensureContextTxn(ctx, c.db, true)
3641
if err != nil {

internal/db/collection_update.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"github.com/valyala/fastjson"
1818

1919
"github.com/sourcenetwork/defradb/acp/identity"
20+
acpTypes "github.com/sourcenetwork/defradb/acp/types"
2021
"github.com/sourcenetwork/defradb/client"
2122
"github.com/sourcenetwork/defradb/client/request"
2223
"github.com/sourcenetwork/defradb/internal/planner"
@@ -33,6 +34,10 @@ func (c *collection) UpdateWithFilter(
3334
ctx, span := tracer.Start(ctx)
3435
defer span.End()
3536

37+
if err := c.db.checkNodeAccess(ctx, acpTypes.NodeDocUpdatePerm); err != nil {
38+
return nil, err
39+
}
40+
3641
ctx, txn, err := ensureContextTxn(ctx, c.db, false)
3742
if err != nil {
3843
return nil, err
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Copyright 2025 Democratized Data Foundation
2+
//
3+
// Use of this software is governed by the Business Source License
4+
// included in the file licenses/BSL.txt.
5+
//
6+
// As of the Change Date specified in that file, in accordance with
7+
// the Business Source License, use of this software will be governed
8+
// by the Apache License, Version 2.0, included in the file
9+
// licenses/APL.txt.
10+
11+
package test_acp_nac
12+
13+
import (
14+
"testing"
15+
16+
testUtils "github.com/sourcenetwork/defradb/tests/integration"
17+
)
18+
19+
func TestNAC_GatesDocCreate_AllowIfAuthorizedElseError(t *testing.T) {
20+
test := testUtils.TestCase{
21+
Description: "node acp correctly gates doc create operation, allow if authorized, otherwise error",
22+
Actions: []any{
23+
// Starting with NAC, so only authorized user(s) can perform operations from here on out.
24+
testUtils.Close{},
25+
testUtils.Start{
26+
Identity: testUtils.ClientIdentity(1),
27+
EnableNAC: true,
28+
},
29+
// Note: Doing setup steps after starting with nac enabled, otherwise the in-memory tests
30+
// will loose setup state when the restart happens (i.e. the restart that started nac).
31+
testUtils.SchemaUpdate{
32+
Identity: testUtils.ClientIdentity(1),
33+
Schema: `type User { name: String }`,
34+
},
35+
36+
// We haven't authorized non-identities. So, this should error.
37+
testUtils.CreateDoc{
38+
Identity: testUtils.NoIdentity(),
39+
CollectionID: 0,
40+
Doc: `{ "name": "Shahzad" }`,
41+
ExpectedError: "not authorized to perform operation",
42+
},
43+
testUtils.Request{ // Should not be created
44+
Identity: testUtils.ClientIdentity(1),
45+
Request: `query{ User { name } }`,
46+
Results: map[string]any{"User": []map[string]any{}},
47+
},
48+
49+
// Wrong user/identity will also not be authorized.
50+
testUtils.CreateDoc{
51+
Identity: testUtils.ClientIdentity(2),
52+
CollectionID: 0,
53+
Doc: `{ "name": "Shahzad" }`,
54+
ExpectedError: "not authorized to perform operation",
55+
},
56+
testUtils.Request{ // Should not be created
57+
Identity: testUtils.ClientIdentity(1),
58+
Request: `query{ User { name } }`,
59+
Results: map[string]any{"User": []map[string]any{}},
60+
},
61+
62+
// This should work as the identity is authorized.
63+
testUtils.CreateDoc{
64+
Identity: testUtils.ClientIdentity(1),
65+
CollectionID: 0,
66+
Doc: `{ "name": "Shahzad" }`,
67+
},
68+
testUtils.Request{ // Should now be created
69+
Identity: testUtils.ClientIdentity(1),
70+
Request: `query{ User { name } }`,
71+
Results: map[string]any{
72+
"User": []map[string]any{{"name": "Shahzad"}},
73+
},
74+
},
75+
},
76+
}
77+
78+
testUtils.ExecuteTestCase(t, test)
79+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Copyright 2025 Democratized Data Foundation
2+
//
3+
// Use of this software is governed by the Business Source License
4+
// included in the file licenses/BSL.txt.
5+
//
6+
// As of the Change Date specified in that file, in accordance with
7+
// the Business Source License, use of this software will be governed
8+
// by the Apache License, Version 2.0, included in the file
9+
// licenses/APL.txt.
10+
11+
package test_acp_nac
12+
13+
import (
14+
"testing"
15+
16+
testUtils "github.com/sourcenetwork/defradb/tests/integration"
17+
)
18+
19+
func TestNAC_GatesDocDelete_AllowIfAuthorizedElseError(t *testing.T) {
20+
test := testUtils.TestCase{
21+
Description: "admin acp correctly gates doc delete operation, allow if authorized, otherwise error",
22+
Actions: []any{
23+
// Starting with NAC, so only authorized user(s) can perform operations from here on out.
24+
testUtils.Close{},
25+
testUtils.Start{
26+
Identity: testUtils.ClientIdentity(1),
27+
EnableNAC: true,
28+
},
29+
// Note: Doing setup steps after starting with nac enabled, otherwise the in-memory tests
30+
// will loose setup state when the restart happens (i.e. the restart that started nac).
31+
testUtils.SchemaUpdate{
32+
Identity: testUtils.ClientIdentity(1),
33+
Schema: `
34+
type User {
35+
name: String
36+
age: Int
37+
}`,
38+
},
39+
testUtils.CreateDoc{
40+
Identity: testUtils.ClientIdentity(1),
41+
CollectionID: 0,
42+
Doc: `{
43+
"name": "Shahzad",
44+
"age": 48
45+
}`,
46+
},
47+
48+
// We haven't authorized non-identities. So, this should error.
49+
testUtils.DeleteDoc{
50+
Identity: testUtils.NoIdentity(),
51+
CollectionID: 0,
52+
DocID: 0,
53+
ExpectedError: "not authorized to perform operation",
54+
},
55+
testUtils.Request{ // Should not be deleted.
56+
Identity: testUtils.ClientIdentity(1),
57+
Request: `query{ User { name } }`,
58+
Results: map[string]any{
59+
"User": []map[string]any{{"name": "Shahzad"}},
60+
},
61+
},
62+
63+
// Wrong user/identity will also not be authorized.
64+
testUtils.DeleteDoc{
65+
Identity: testUtils.ClientIdentity(2),
66+
CollectionID: 0,
67+
DocID: 0,
68+
ExpectedError: "not authorized to perform operation",
69+
},
70+
testUtils.Request{ // Should not be deleted
71+
Identity: testUtils.ClientIdentity(1),
72+
Request: `query{ User { name } }`,
73+
Results: map[string]any{
74+
"User": []map[string]any{{"name": "Shahzad"}},
75+
},
76+
},
77+
78+
// This should work as the identity is authorized.
79+
testUtils.DeleteDoc{
80+
Identity: testUtils.ClientIdentity(1),
81+
CollectionID: 0,
82+
DocID: 0,
83+
},
84+
testUtils.Request{ // Should now be deleted
85+
Identity: testUtils.ClientIdentity(1),
86+
Request: `query{ User { name } }`,
87+
Results: map[string]any{
88+
"User": []map[string]any{},
89+
},
90+
},
91+
},
92+
}
93+
94+
testUtils.ExecuteTestCase(t, test)
95+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Copyright 2025 Democratized Data Foundation
2+
//
3+
// Use of this software is governed by the Business Source License
4+
// included in the file licenses/BSL.txt.
5+
//
6+
// As of the Change Date specified in that file, in accordance with
7+
// the Business Source License, use of this software will be governed
8+
// by the Apache License, Version 2.0, included in the file
9+
// licenses/APL.txt.
10+
11+
package test_acp_nac
12+
13+
import (
14+
"testing"
15+
16+
testUtils "github.com/sourcenetwork/defradb/tests/integration"
17+
)
18+
19+
func TestNAC_GatesDocRead_AllowIfAuthorizedElseError(t *testing.T) {
20+
test := testUtils.TestCase{
21+
Description: "node acp correctly gates doc read operation, allow if authorized, otherwise error",
22+
Actions: []any{
23+
// Starting with NAC, so only authorized user(s) can perform operations from here on out.
24+
testUtils.Close{},
25+
testUtils.Start{
26+
Identity: testUtils.ClientIdentity(1),
27+
EnableNAC: true,
28+
},
29+
// Note: Doing setup steps after starting with nac enabled, otherwise the in-memory tests
30+
// will loose setup state when the restart happens (i.e. the restart that started nac).
31+
testUtils.SchemaUpdate{
32+
Identity: testUtils.ClientIdentity(1),
33+
Schema: `type User { name: String }`,
34+
},
35+
testUtils.CreateDoc{
36+
Identity: testUtils.ClientIdentity(1),
37+
CollectionID: 0,
38+
Doc: `{ "name": "Shahzad" }`,
39+
},
40+
41+
// This should work as the identity is authorized.
42+
testUtils.Request{
43+
Identity: testUtils.ClientIdentity(1),
44+
Request: `query{ User { name } }`,
45+
Results: map[string]any{
46+
"User": []map[string]any{{"name": "Shahzad"}},
47+
},
48+
},
49+
},
50+
}
51+
52+
testUtils.ExecuteTestCase(t, test)
53+
}

0 commit comments

Comments
 (0)