Skip to content

Commit d50aaec

Browse files
committed
PR(MAIN): Gate & Test - Patch Collection
1 parent 837c48f commit d50aaec

5 files changed

Lines changed: 111 additions & 2 deletions

File tree

acp/types/types.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ const (
9090
AdminAACRelationDeletePerm
9191
AdminSchemaAddPerm
9292
AdminSchemaPatchPerm
93+
AdminPatchCollectionPerm
9394
)
9495

9596
// List of all valid resource interface permissions for admin access control, the order of
@@ -112,6 +113,7 @@ var RequiredResourcePermissionsForAdmin = []string{
112113
"aac-relation-delete",
113114
"schema-add",
114115
"schema-patch",
116+
"patch-collection",
115117
}
116118

117119
const InternalAdminACPObjectToGate = "NodeObject"
@@ -160,6 +162,8 @@ resources:
160162
expr: owner + admin
161163
schema-patch:
162164
expr: owner + admin
165+
patch-collection:
166+
expr: owner + admin
163167
164168
relations:
165169
owner:

internal/db/store.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,11 @@ func (db *DB) PatchCollection(
211211
ctx, span := tracer.Start(ctx)
212212
defer span.End()
213213

214+
err := db.checkAdminAccess(ctx, acpTypes.AdminPatchCollectionPerm)
215+
if err != nil {
216+
return err
217+
}
218+
214219
ctx, txn, err := ensureContextTxn(ctx, db, false)
215220
if err != nil {
216221
return err
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
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_aac
12+
13+
import (
14+
"testing"
15+
16+
testUtils "github.com/sourcenetwork/defradb/tests/integration"
17+
"github.com/sourcenetwork/immutable"
18+
)
19+
20+
func TestAAC_GatesPatchCollection_AllowIfAuthorizedElseError(t *testing.T) {
21+
test := testUtils.TestCase{
22+
Description: "admin acp correctly gates patch collection operation, allow if authorized, otherwise error",
23+
Actions: []any{
24+
// Setup that we can do before aac is enabled.
25+
testUtils.SchemaUpdate{
26+
Schema: `
27+
type Users {}
28+
`,
29+
},
30+
testUtils.SchemaPatch{
31+
Patch: `
32+
[
33+
{ "op": "add", "path": "/Users/Fields/-", "value": {"Name": "name", "Kind": "String"} }
34+
]
35+
`,
36+
SetAsDefaultVersion: immutable.Some(false),
37+
},
38+
39+
// Starting with ACC, so only authorized user(s) can perform operations.
40+
testUtils.Close{},
41+
testUtils.Start{
42+
Identity: testUtils.ClientIdentity(1),
43+
EnableAAC: true,
44+
},
45+
46+
// We haven't authorized non-identities. So, this should error.
47+
testUtils.PatchCollection{
48+
Patch: `
49+
[
50+
{
51+
"op": "copy",
52+
"from": "/bafkreia2jn5ecrhtvy4fravk6pm3wqiny46m7mqymvjkgat7xiqupgqoai/Name",
53+
"path": "/bafkreialnju2rez4t3quvpobf3463eai3lo64vdrdhdmunz7yy7sv3f5ce/Name"
54+
}
55+
]
56+
`,
57+
ExpectedError: "not authorized to perform operation",
58+
},
59+
60+
// Wrong user/identity will also not be authorized.
61+
testUtils.PatchCollection{
62+
Identity: testUtils.ClientIdentity(2),
63+
Patch: `
64+
[
65+
{
66+
"op": "copy",
67+
"from": "/bafkreia2jn5ecrhtvy4fravk6pm3wqiny46m7mqymvjkgat7xiqupgqoai/Name",
68+
"path": "/bafkreialnju2rez4t3quvpobf3463eai3lo64vdrdhdmunz7yy7sv3f5ce/Name"
69+
}
70+
]
71+
`,
72+
ExpectedError: "not authorized to perform operation",
73+
},
74+
75+
// This should work as the identity is authorized.
76+
testUtils.PatchCollection{
77+
Identity: testUtils.ClientIdentity(1),
78+
Patch: `
79+
[
80+
{
81+
"op": "copy",
82+
"from": "/bafkreia2jn5ecrhtvy4fravk6pm3wqiny46m7mqymvjkgat7xiqupgqoai/Name",
83+
"path": "/bafkreialnju2rez4t3quvpobf3463eai3lo64vdrdhdmunz7yy7sv3f5ce/Name"
84+
}
85+
]
86+
`,
87+
},
88+
},
89+
}
90+
91+
testUtils.ExecuteTestCase(t, test)
92+
}

tests/integration/test_case.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,11 @@ type PatchCollection struct {
256256
// If a value is not provided the patch will be applied to all nodes.
257257
NodeID immutable.Option[int]
258258

259+
// The identity of this request. Optional.
260+
//
261+
// If admin acp is enabled, identity will be used to check if this operation can be performed.
262+
Identity immutable.Option[Identity]
263+
259264
// The Patch to apply to the collection version.
260265
Patch string
261266

tests/integration/utils.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1136,9 +1136,12 @@ func patchCollection(
11361136
s *state,
11371137
action PatchCollection,
11381138
) {
1139-
_, nodes := getNodesWithIDs(action.NodeID, s.nodes)
1140-
for _, node := range nodes {
1139+
nodeIDs, nodes := getNodesWithIDs(action.NodeID, s.nodes)
1140+
for index, node := range nodes {
1141+
nodeID := nodeIDs[index]
1142+
s.ctx = getContextWithIdentity(s.ctx, s, action.Identity, nodeID)
11411143
err := node.PatchCollection(s.ctx, action.Patch)
1144+
resetStateContext(s)
11421145
expectedErrorRaised := AssertError(s.t, s.testCase.Description, err, action.ExpectedError)
11431146

11441147
assertExpectedErrorRaised(s.t, s.testCase.Description, action.ExpectedError, expectedErrorRaised)

0 commit comments

Comments
 (0)