Skip to content

Commit

Permalink
PR: Rename and split to permission package
Browse files Browse the repository at this point in the history
  • Loading branch information
shahzadlone committed Feb 7, 2024
1 parent b098d0c commit 5198c91
Show file tree
Hide file tree
Showing 7 changed files with 266 additions and 106 deletions.
2 changes: 1 addition & 1 deletion db/collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -974,7 +974,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.
Expand Down
54 changes: 22 additions & 32 deletions db/collection_acp.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
}
74 changes: 1 addition & 73 deletions db/fetcher/fetcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
}
Expand Down
39 changes: 39 additions & 0 deletions db/fetcher/fetcher_acp.go
Original file line number Diff line number Diff line change
@@ -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
}
105 changes: 105 additions & 0 deletions db/permission/check.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// 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 acp utilizes the sourcehub acp module to bring the functionality to defradb, this package also helps
avoid the leakage of direct sourcehub references through out the code base, and eases in swapping
between local embedded use case and a more global on sourcehub use case.
*/

package permission

import (
"context"

"github.com/sourcenetwork/defradb/acp"
"github.com/sourcenetwork/defradb/client"
"github.com/sourcenetwork/immutable"
)

// CheckDocPermissionedAccessOnCollection handles the check, which tells us if access to the target
// document is valid, with respect to the permission type, and the specified collection.
//
// According to our access logic we have these components to worry about:
// (1) the request is permissioned (has an identity signature),
// (2) the collection is permissioned (has a policy),
// (3) acp module exists (acp is enabled).
//
// Unrestricted Access (Read + Write) to target document if:
// - Either of (2) or (3) is false.
// - Document is public (unregistered), whether signatured request or not, doesn't matter.
//
// Otherwise, check with acp module to verify signature has the appropriate access.
func CheckDocPermissionedAccessOnCollection(
ctx context.Context,
acpModuleOptional immutable.Option[acp.ACPModule],
collection client.Collection,
permission acp.DPIPermission,
docID string,
) (bool, error) {
// If no acp module, then we have unrestricted access.
if !acpModuleOptional.HasValue() {
return true, 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 := IsPermissioned(collection)
if !hasPolicy {
return true, nil
}

acpModule := acpModuleOptional.Value()

// Now that we know acp module exists and the collection is permissioned, before checking access with
// acp module directly we need to make sure that the document is not public, as public documents will
// not be regestered with acp. We give unrestricted access to public documents, so it does not matter
// whether the request has a signature identity or not at this stage of the check.
isNotPublic, err := acpModule.IsDocRegistered(
ctx,
policyID,
resourceName,
docID,
)
if err != nil {
return false, err
}

if !isNotPublic {
// Unrestricted access as it is a public document.
return true, nil
}

// TODO-ACP: Implement signatures
hasSignature := true

// At this point if the request is not signatured, then it has no access, because:
// the collection has a policy on it, the acp module is enabled/available,
// and the document is not public (is regestered with the acp module).
if !hasSignature {
return false, nil
}

// Now actually check using the signature if this identity has access or not.
hasAccess, err := acpModule.CheckDocAccess(
ctx,
permission,
"cosmos1zzg43wdrhmmk89z3pmejwete2kkd4a3vn7w969", // TODO-ACP: Replace with signature identity
policyID,
resourceName,
docID,
)

if err != nil {
return false, err
}

return hasAccess, nil
}
35 changes: 35 additions & 0 deletions db/permission/permission.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// 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 acp utilizes the sourcehub acp module to bring the functionality to defradb, this package also helps
avoid the leakage of direct sourcehub references through out the code base, and eases in swapping
between local embedded use case and a more global on sourcehub use case.
*/

package permission

import (
"github.com/sourcenetwork/defradb/client"
)

// 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(collection client.Collection) (string, string, bool) {
policy := collection.Definition().Description.Policy
if policy.HasValue() &&
policy.Value().ID != "" &&
policy.Value().ResourceName != "" {
return policy.Value().ID, policy.Value().ResourceName, true
}

return "", "", false
}
Loading

0 comments on commit 5198c91

Please sign in to comment.