Skip to content

Commit

Permalink
PR(MAIN): Implement local & sourcehub acp doc sharing
Browse files Browse the repository at this point in the history
  • Loading branch information
shahzadlone committed Aug 12, 2024
1 parent 6f69e61 commit 568a7c8
Show file tree
Hide file tree
Showing 15 changed files with 666 additions and 32 deletions.
16 changes: 16 additions & 0 deletions acp/acp.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,22 @@ type ACP interface {
docID string,
) (bool, error)

// AddDocActorRelationship creates a relationship between document and the target actor.
//
// If failure occurs, the result will return an error. Upon success the boolean value will
// be true if the relationship already existed (no-op), and false if a new relationship was made.
//
// Note: The request actor must either be the owner or manager of the document.
AddDocActorRelationship(
ctx context.Context,
policyID string,
resourceName string,
docID string,
relation string,
requestActor identity.Identity,
targetActor string,
) (bool, error)

// SupportsP2P returns true if the implementation supports ACP across a peer network.
SupportsP2P() bool
}
31 changes: 31 additions & 0 deletions acp/acp_local.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,3 +236,34 @@ func (l *ACPLocal) VerifyAccessRequest(

return resp.Valid, nil
}

func (l *ACPLocal) AddActorRelationship(
ctx context.Context,
policyID string,
resourceName string,
objectID string,
relation string,
requester identity.Identity,
targetActor string,
creationTime *protoTypes.Timestamp,
) (bool, error) {
principal, err := auth.NewDIDPrincipal(requester.DID)
if err != nil {
return false, newErrInvalidActorID(err, requester.DID)
}

ctx = auth.InjectPrincipal(ctx, principal)

setRelationshipRequest := types.SetRelationshipRequest{
PolicyId: policyID,
Relationship: types.NewActorRelationship(resourceName, objectID, relation, targetActor),
CreationTime: creationTime,
}

setRelationshipResponse, err := l.engine.SetRelationship(ctx, &setRelationshipRequest)
if err != nil {
return false, err
}

return setRelationshipResponse.RecordExisted, nil
}
50 changes: 50 additions & 0 deletions acp/acp_source_hub.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,3 +261,53 @@ func (a *acpSourceHub) VerifyAccessRequest(
func (a *acpSourceHub) Close() error {
return nil
}

func (a *acpSourceHub) AddActorRelationship(
ctx context.Context,
policyID string,
resourceName string,
objectID string,
relation string,
requester identity.Identity,
targetActor string,
creationTime *protoTypes.Timestamp,
) (bool, error) {
msgSet := sourcehub.MsgSet{}
cmdMapper := msgSet.WithBearerPolicyCmd(&acptypes.MsgBearerPolicyCmd{
Creator: a.signer.GetAccAddress(),
BearerToken: requester.BearerToken,
PolicyId: policyID,
Cmd: acptypes.NewSetRelationshipCmd(
acptypes.NewActorRelationship(
resourceName,
objectID,
relation,
targetActor,
),
),
CreationTime: creationTime,
})
tx, err := a.txBuilder.Build(ctx, a.signer, &msgSet)
if err != nil {
return false, err
}
resp, err := a.client.BroadcastTx(ctx, tx)
if err != nil {
return false, err
}

result, err := a.client.AwaitTx(ctx, resp.TxHash)
if err != nil {
return false, err
}
if result.Error() != nil {
return false, result.Error()
}

cmdResult, err := cmdMapper.Map(result.TxPayload())
if err != nil {
return false, err
}

return cmdResult.GetResult().GetSetRelationshipResult().RecordExisted, nil
}
70 changes: 58 additions & 12 deletions acp/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ import (
)

const (
errInitializationOfACPFailed = "initialization of acp failed"
errStartingACPInEmptyPath = "starting acp in an empty path"
errFailedToAddPolicyWithACP = "failed to add policy with acp"
errFailedToRegisterDocWithACP = "failed to register document with acp"
errFailedToCheckIfDocIsRegisteredWithACP = "failed to check if doc is registered with acp"
errFailedToVerifyDocAccessWithACP = "failed to verify doc access with acp"
errInitializationOfACPFailed = "initialization of acp failed"
errStartingACPInEmptyPath = "starting acp in an empty path"
errFailedToAddPolicyWithACP = "failed to add policy with acp"
errFailedToRegisterDocWithACP = "failed to register document with acp"
errFailedToCheckIfDocIsRegisteredWithACP = "failed to check if doc is registered with acp"
errFailedToVerifyDocAccessWithACP = "failed to verify doc access with acp"
errFailedToAddDocActorRelationshipWithACP = "failed to add document actor relationship with acp"
errMissingRequiredArgToAddDocActorRelationship = "missing a required argument needed to add doc actor relationship"

errObjectDidNotRegister = "no-op while registering object (already exists or error) with acp"
errNoPolicyArgs = "missing policy arguments, must have both id and resource"
Expand All @@ -40,12 +42,13 @@ const (
)

var (
ErrInitializationOfACPFailed = errors.New(errInitializationOfACPFailed)
ErrFailedToAddPolicyWithACP = errors.New(errFailedToAddPolicyWithACP)
ErrFailedToRegisterDocWithACP = errors.New(errFailedToRegisterDocWithACP)
ErrFailedToCheckIfDocIsRegisteredWithACP = errors.New(errFailedToCheckIfDocIsRegisteredWithACP)
ErrFailedToVerifyDocAccessWithACP = errors.New(errFailedToVerifyDocAccessWithACP)
ErrPolicyDoesNotExistWithACP = errors.New(errPolicyDoesNotExistWithACP)
ErrInitializationOfACPFailed = errors.New(errInitializationOfACPFailed)
ErrFailedToAddPolicyWithACP = errors.New(errFailedToAddPolicyWithACP)
ErrFailedToRegisterDocWithACP = errors.New(errFailedToRegisterDocWithACP)
ErrFailedToCheckIfDocIsRegisteredWithACP = errors.New(errFailedToCheckIfDocIsRegisteredWithACP)
ErrFailedToVerifyDocAccessWithACP = errors.New(errFailedToVerifyDocAccessWithACP)
ErrFailedToAddDocActorRelationshipWithACP = errors.New(errFailedToAddDocActorRelationshipWithACP)
ErrPolicyDoesNotExistWithACP = errors.New(errPolicyDoesNotExistWithACP)

ErrResourceDoesNotExistOnTargetPolicy = errors.New(errResourceDoesNotExistOnTargetPolicy)

Expand Down Expand Up @@ -139,6 +142,29 @@ func NewErrFailedToVerifyDocAccessWithACP(
)
}

func NewErrFailedToAddDocActorRelationshipWithACP(
inner error,
Type string,
policyID string,
resourceName string,
docID string,
relation string,
requestActor string,
targetActor string,
) error {
return errors.Wrap(
errFailedToAddDocActorRelationshipWithACP,
inner,
errors.NewKV("Type", Type),
errors.NewKV("PolicyID", policyID),
errors.NewKV("ResourceName", resourceName),
errors.NewKV("DocID", docID),
errors.NewKV("Relation", relation),
errors.NewKV("RequestActor", requestActor),
errors.NewKV("TargetActor", targetActor),
)
}

func newErrPolicyDoesNotExistWithACP(
inner error,
policyID string,
Expand Down Expand Up @@ -209,6 +235,26 @@ func newErrExprOfRequiredPermissionHasInvalidChar(
)
}

func NewErrMissingRequiredArgToAddDocActorRelationship(
policyID string,
resourceName string,
docID string,
relation string,
requestActor string,
targetActor string,
) error {
return errors.New(
errMissingRequiredArgToAddDocActorRelationship,
errors.NewKV("PolicyID", policyID),
errors.NewKV("ResourceName", resourceName),
errors.NewKV("DocID", docID),
errors.NewKV("Relation", relation),
errors.NewKV("RequestActor", requestActor),
errors.NewKV("TargetActor", targetActor),
)
}


func newErrInvalidActorID(
inner error,
id string,
Expand Down
100 changes: 99 additions & 1 deletion acp/source_hub_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,27 @@ type sourceHubClient interface {
docID string,
) (bool, error)

// AddActorRelationship creates a relationship within a policy which ties the target actor
// with the specified object, which means that the set of high level rules defined in the
// policy will now apply to target actor as well.
//
// If failure occurs, the result will return an error. Upon success the boolean value will
// be true if the relationship with actor already existed (no-op), and false if a new
// relationship was made.
//
// Note: The requester identity must either be the owner of the object (being shared) or
// the manager (i.e. the relation has `manages` defined in the policy).
AddActorRelationship(
ctx context.Context,
policyID string,
resourceName string,
objectID string,
relation string,
requester identity.Identity,
targetActor string,
creationTime *protoTypes.Timestamp,
) (bool, error)

// Close closes any resources in use by acp.
Close() error
}
Expand Down Expand Up @@ -129,7 +150,7 @@ func (a *sourceHubBridge) Start(ctx context.Context) error {

func (a *sourceHubBridge) AddPolicy(ctx context.Context, creator identity.Identity, policy string) (string, error) {
// Having a creator identity is a MUST requirement for adding a policy.
if creator.DID == "" {
if creator.DID == "" { // TODO-ACP: FIX TO CHECK ALL THE Identity is not empty
return "", ErrPolicyCreatorMustNotBeEmpty
}

Expand Down Expand Up @@ -335,6 +356,83 @@ func (a *sourceHubBridge) CheckDocAccess(
}
}

func (a *sourceHubBridge) AddDocActorRelationship(
ctx context.Context,
policyID string,
resourceName string,
docID string,
relation string,
requestActor identity.Identity,
targetActor string,
) (bool, error) {
// Must have all required args.
if policyID == "" ||
resourceName == "" ||
docID == "" ||
relation == "" ||
requestActor == (identity.Identity{}) ||
targetActor == "" {
return false, NewErrMissingRequiredArgToAddDocActorRelationship(
policyID,
resourceName,
docID,
relation,
requestActor.DID,
targetActor,
)
}

exists, err := a.client.AddActorRelationship(
ctx,
policyID,
resourceName,
docID,
relation,
requestActor,
targetActor,
protoTypes.TimestampNow(),
)

if err != nil {
return false, NewErrFailedToAddDocActorRelationshipWithACP(
err,
"Local",
policyID,
resourceName,
docID,
relation,
requestActor.DID,
targetActor,
)
}

if exists {
log.InfoContext(
ctx,
"Document and actor already have that relationship [no-op]",
corelog.Any("PolicyID", policyID),
corelog.Any("ResourceName", resourceName),
corelog.Any("DocID", docID),
corelog.Any("Relation", relation),
corelog.Any("RequestActor", requestActor.DID),
corelog.Any("TargetActor", targetActor),
)
return true, nil
} else {
log.InfoContext(
ctx,
"Document and actor have now formed that relationship",
corelog.Any("PolicyID", policyID),
corelog.Any("ResourceName", resourceName),
corelog.Any("DocID", docID),
corelog.Any("Relation", relation),
corelog.Any("RequestActor", requestActor.DID),
corelog.Any("TargetActor", targetActor),
)
return false, nil
}
}

func (a *sourceHubBridge) SupportsP2P() bool {
_, ok := a.client.(*acpSourceHub)
return ok
Expand Down
25 changes: 25 additions & 0 deletions cli/acp_relationship.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// 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 cli

import (
"github.com/spf13/cobra"
)

func MakeACPRelationshipCommand() *cobra.Command {
var cmd = &cobra.Command{
Use: "relation",
Short: "Interact with the acp relationship features of DefraDB instance",
Long: `Interact with the acp relationship features of DefraDB instance`,
}

return cmd
}
Loading

0 comments on commit 568a7c8

Please sign in to comment.