diff --git a/client/collection.go b/client/collection.go index 4c65521bc7..5402ea3fa6 100644 --- a/client/collection.go +++ b/client/collection.go @@ -185,20 +185,6 @@ type Collection interface { GetIndexes(ctx context.Context) ([]IndexDescription, error) } -// IsPermissioned returns true if the collection has a policy, otherwise returns false. -// -// This tells us if access control is enabled for this collection or not. -func IsPermissioned(c Collection) (string, string, bool) { - policy := c.Definition().Description.Policy - if policy.HasValue() && - policy.Value().ID != "" && - policy.Value().ResourceName != "" { - return policy.Value().ID, policy.Value().ResourceName, true - } - - return "", "", false -} - // DocIDResult wraps the result of an attempt at a DocID retrieval operation. type DocIDResult struct { // If a DocID was successfully retrieved, this will be that DocID. diff --git a/db/collection.go b/db/collection.go index 80c1807915..d3e61b5745 100644 --- a/db/collection.go +++ b/db/collection.go @@ -1035,7 +1035,7 @@ func (c *collection) create(ctx context.Context, txn datastore.Txn, doc *client. return err } - return c.tryRegisterDocWithACP(ctx, doc) + return c.registerDocCreation(ctx, doc.ID().String()) } // Update an existing document with the new values. diff --git a/db/collection_acp.go b/db/collection_acp.go index c6ec796c14..98fd9d5ba1 100644 --- a/db/collection_acp.go +++ b/db/collection_acp.go @@ -13,39 +13,29 @@ package db import ( "context" - "github.com/sourcenetwork/defradb/client" + "github.com/sourcenetwork/defradb/acp" + "github.com/sourcenetwork/defradb/db/permission" ) -// tryRegisterDocWithACP handles the registeration of the document with acp module, -// according to our registration logic based on weather (1) the request is permissioned, -// (2) the collection is permissioned (has a policy), (3) acp module exists. -// -// Note: we only register the document with ACP if all (1) (2) and (3) are true. -// In all other cases, nothing is registered with ACP. -// -// Moreover 8 states, upon document creation: -// - (SignatureRequest, PermissionedCollection, ModuleExists) => Register with ACP -// - (SignatureRequest, PermissionedCollection, !ModuleExists) => Normal/Public - Don't Register with ACP -// - (SignatureRequest, !PermissionedCollection, ModuleExists) => Normal/Public - Don't Register with ACP -// - (SignatureRequest, !PermissionedCollection, !ModuleExists) => Normal/Public - Don't Register with ACP -// - (!SignatureRequest, PermissionedCollection, ModuleExists) => Normal/Public - Don't Register with ACP -// - (!SignatureRequest, !PermissionedCollection, ModuleExists) => Normal/Public - Don't Register with ACP -// - (!SignatureRequest, PermissionedCollection, !ModuleExists) => Normal/Public - Don't Register with ACP -// - (!SignatureRequest, !PermissionedCollection, !ModuleExists) => Normal/Public - Don't Register with ACP -func (c *collection) tryRegisterDocWithACP(ctx context.Context, doc *client.Document) error { - // Check if acp module exists. - if c.db.ACPModule().HasValue() { - // Check if collection has policy. - if policyID, resourceName, hasPolicy := client.IsPermissioned(c); hasPolicy { - return c.db.ACPModule().Value().RegisterDocCreation( - ctx, - "cosmos1zzg43wdrhmmk89z3pmejwete2kkd4a3vn7w969", // TODO-ACP: Replace with signature identity - policyID, - resourceName, - doc.ID().String(), - ) - } - } +func (c *collection) registerDocCreation(ctx context.Context, docID string) error { + return permission.RegisterDocCreationOnCollection( + ctx, + c.db.ACPModule(), + c, + docID, + ) +} - return nil +func (c *collection) checkDocPermissionedAccess( + ctx context.Context, + dpiPermission acp.DPIPermission, + docID string, +) (bool, error) { + return permission.CheckDocPermissionedAccessOnCollection( + ctx, + c.db.ACPModule(), + c, + dpiPermission, + docID, + ) } diff --git a/db/fetcher/fetcher.go b/db/fetcher/fetcher.go index f95824c79f..a72b441887 100644 --- a/db/fetcher/fetcher.go +++ b/db/fetcher/fetcher.go @@ -576,78 +576,6 @@ func (df *DocumentFetcher) FetchNext(ctx context.Context) (EncodedDocument, Exec return encdoc, resultExecInfo, err } -// runDocumentReadPermissionCheck handles the checking (while fetching) if the document has read access -// or not, according to our access logic based on weather (1) the request is permissioned, -// (2) the collection is permissioned (has a policy), (3) acp module exists. -// -// Note: we only need to make a call to the acp module if (2) and (3) are true, where if (1) is true -// we then check passes only if the document has proper access, otherwise if (1) is false then -// the check passes only if the document is public (is not registered with acp module at all). -// -// Moreover 8 states, upon checking access: -// (SignatureRequest, PermissionedCollection, ModuleExists) => Must pass ACP check, unless public (not registered) -// (!SignatureRequest, PermissionedCollection, ModuleExists) => Only public (No access if registered with ACP) -// (SignatureRequest, PermissionedCollection, !ModuleExists) => No check needed -// (SignatureRequest, !PermissionedCollection, ModuleExists) => No check needed -// (SignatureRequest, !PermissionedCollection, !ModuleExists) => No check needed -// (!SignatureRequest, !PermissionedCollection, ModuleExists) => No check needed -// (!SignatureRequest, PermissionedCollection, !ModuleExists) => No check needed -// (!SignatureRequest, !PermissionedCollection, !ModuleExists) => No check needed -func (df *DocumentFetcher) runDocumentReadPermissionCheck(ctx context.Context) error { - // If no acp module, then we have unrestricted access. - if !df.acp.HasValue() { - df.passedPermissionCheck = true - return nil - } - - // Even if acp module exists, but there is no policy on the collection (unpermissioned collection) - // then we still have unrestricted access. - policyID, resourceName, hasPolicy := client.IsPermissioned(df.col) - if !hasPolicy { - df.passedPermissionCheck = true - return nil - } - - // TODO-ACP: Implement signatures - hasSignature := true - - // Now that we know acp module exists and the collection is permissioned, handle based on signature. - if hasSignature { - hasAccess, err := df.acp.Value().CheckDocAccess( - ctx, - acp.ReadPermission, - "cosmos1zzg43wdrhmmk89z3pmejwete2kkd4a3vn7w969", // TODO-ACP: Replace with signature identity - policyID, - resourceName, - df.kv.Key.DocID, - ) - if err != nil { - df.passedPermissionCheck = false - return err - } - df.passedPermissionCheck = hasAccess - return nil - } - - // If does not have signature, we need to make sure we don't operate on registered documents. - // In this case actor only has access to the public (unregistered) documents. - isRegistered, err := df.acp.Value().IsDocRegistered( - ctx, - policyID, - resourceName, - df.kv.Key.DocID, - ) - if err != nil { - df.passedPermissionCheck = false - return err - } - - // Check passes if document is NOT registered. If it is registered then the - // document must not be accessed. - df.passedPermissionCheck = !isRegistered - return nil -} - func (df *DocumentFetcher) fetchNext(ctx context.Context) (EncodedDocument, ExecInfo, error) { if df.kvEnd { return nil, ExecInfo{}, nil @@ -687,7 +615,7 @@ func (df *DocumentFetcher) fetchNext(ctx context.Context) (EncodedDocument, Exec // Check if can access document with current permissions/signature. if !df.passedPermissionCheck { - if err := df.runDocumentReadPermissionCheck(ctx); err != nil { + if err := df.runDocReadPermissionCheck(ctx); err != nil { return nil, ExecInfo{}, err } } diff --git a/db/fetcher/fetcher_acp.go b/db/fetcher/fetcher_acp.go new file mode 100644 index 0000000000..ea72f75684 --- /dev/null +++ b/db/fetcher/fetcher_acp.go @@ -0,0 +1,39 @@ +// Copyright 2024 Democratized Data Foundation +// +// Use of this software is governed by the Business Source License +// included in the file licenses/BSL.txt. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0, included in the file +// licenses/APL.txt. + +package fetcher + +import ( + "context" + + "github.com/sourcenetwork/defradb/acp" + "github.com/sourcenetwork/defradb/db/permission" +) + +// runDocReadPermissionCheck handles the checking (while fetching) if the document has read access +// or not, according to our access logic based on weather (1) the request is permissioned, +// (2) the collection is permissioned (has a policy), (3) acp module exists. +func (df *DocumentFetcher) runDocReadPermissionCheck(ctx context.Context) error { + hasPermission, err := permission.CheckDocPermissionedAccessOnCollection( + ctx, + df.acp, + df.col, + acp.ReadPermission, + df.kv.Key.DocID, + ) + + if err != nil { + df.passedPermissionCheck = false + return err + } + + df.passedPermissionCheck = hasPermission + return nil +}