Skip to content

Commit

Permalink
refactor: Change local_acp implementation to use acp_core (#2691)
Browse files Browse the repository at this point in the history
## Relevant issue(s)


Also the related issue won't close upon merge, nor is it picked up by
github at the moment, because you just have #2694 in the PR description.

So please don't forget to add the resolving key word (close, closes,
closed, fix, fixes, fixed, resolve, resolves, resolved) with it.

So you can put for example:

Resolve #2694

## Description

This PR changes the underlying implementation of the `ACPLocal`
Reference Monitor from SourceHub to ACP Core.
The changes aim to allow the WASM build which was broken since the
inclusion of SourceHub as a dependency.

Notable changes from this refactor include:
- Replace SourceHub as a dependency for acp_core for ACP Local 
- The field `name` in a `Policy` is now correctly marked as required.
This was a consequence of an issue on SourceHub side which allowed
unnamed policies to be included. Therefore, in order to correct the
tests, a name was given to all policies in the codebase.
- The previous implementation of ACP Local made use of a special notion
of account which had no sequence numbers. As such, re-submitting a
Policy generated the same ID. This behavior isn't compatible with how
SourceHub deals with IDs and the new implementation using ACP Core
reflects that. That is, every created Policy, independent of its
payload, will have unique IDs.
- Updated the `Identity` struct to generate a DID instead of a SourceHub
address

## Tasks

- [x] I made sure the code is well commented, particularly
hard-to-understand areas.
- [] I made sure the repository-held documentation is changed
accordingly.
- [x] I made sure the pull request title adheres to the conventional
commit style (the subset used in the project can be found in
[tools/configs/chglog/config.yml](tools/configs/chglog/config.yml)).
- [x] I made sure to discuss its limitations such as threats to
validity, vulnerability to mistake and misuse, robustness to
invalidation of assumptions, resource requirements, ...

## How has this been tested?

Test suite ie `make test`

Specify the platform(s) on which this was tested:
- Arch Linux
  • Loading branch information
Lodek authored Jun 19, 2024
1 parent 9f50a73 commit 6573d8c
Show file tree
Hide file tree
Showing 66 changed files with 773 additions and 929 deletions.
201 changes: 130 additions & 71 deletions acp/acp_local.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,60 @@ package acp

import (
"context"
"errors"

protoTypes "github.com/cosmos/gogoproto/types"
"github.com/sourcenetwork/acp_core/pkg/auth"
"github.com/sourcenetwork/acp_core/pkg/engine"
"github.com/sourcenetwork/acp_core/pkg/runtime"
"github.com/sourcenetwork/acp_core/pkg/types"
"github.com/sourcenetwork/immutable"
"github.com/sourcenetwork/sourcehub/x/acp/embedded"
"github.com/sourcenetwork/sourcehub/x/acp/types"
)

const localACPStoreName = "local_acp"

// ACPLocal represents a local acp implementation that makes no remote calls.
type ACPLocal struct {
pathToStore immutable.Option[string]
localACP *embedded.LocalACP
engine types.ACPEngineServer
manager runtime.RuntimeManager
}

var _ sourceHubClient = (*ACPLocal)(nil)

func mapACPCorePolicy(pol *types.Policy) policy {
resources := make(map[string]*resource)
for _, coreResource := range pol.Resources {
resource := mapACPCoreResource(coreResource)
resources[resource.Name] = resource
}

return policy{
ID: pol.Id,
Resources: resources,
}
}

func mapACPCoreResource(policy *types.Resource) *resource {
perms := make(map[string]*permission)
for _, corePermission := range policy.Permissions {
perm := mapACPCorePermission(corePermission)
perms[perm.Name] = perm
}

return &resource{
Name: policy.Name,
Permissions: perms,
}
}

func mapACPCorePermission(perm *types.Permission) *permission {
return &permission{
Name: perm.Name,
Expression: perm.Expression,
}
}

func (l *ACPLocal) Init(ctx context.Context, path string) {
if path == "" {
l.pathToStore = immutable.None[string]()
Expand All @@ -36,73 +75,79 @@ func (l *ACPLocal) Init(ctx context.Context, path string) {
}

func (l *ACPLocal) Start(ctx context.Context) error {
var localACP embedded.LocalACP
var manager runtime.RuntimeManager
var err error
var opts []runtime.Opt
var storeLocation string

if !l.pathToStore.HasValue() { // Use a non-persistent, i.e. in memory store.
localACP, err = embedded.NewLocalACP(
embedded.WithInMemStore(),
)

if err != nil {
return NewErrInitializationOfACPFailed(err, "Local", "in-memory")
}
storeLocation = "in-memory"
opts = append(opts, runtime.WithMemKV())
} else { // Use peristent storage.
acpStorePath := l.pathToStore.Value() + "/" + embedded.DefaultDataDir
localACP, err = embedded.NewLocalACP(
embedded.WithPersistentStorage(acpStorePath),
)
if err != nil {
return NewErrInitializationOfACPFailed(err, "Local", l.pathToStore.Value())
}
storeLocation = l.pathToStore.Value()
acpStorePath := storeLocation + "/" + localACPStoreName
opts = append(opts, runtime.WithPersistentKV(acpStorePath))
}

l.localACP = &localACP
manager, err = runtime.NewRuntimeManager(opts...)
if err != nil {
return NewErrInitializationOfACPFailed(err, "Local", storeLocation)
}

l.manager = manager
l.engine = engine.NewACPEngine(manager)
return nil
}

func (l *ACPLocal) Close() error {
return l.localACP.Close()
return l.manager.Terminate()
}

func (l *ACPLocal) AddPolicy(
ctx context.Context,
creatorID string,
policy string,
policyMarshalType types.PolicyMarshalingType,
marshalType policyMarshalType,
creationTime *protoTypes.Timestamp,
) (string, error) {
createPolicy := types.MsgCreatePolicy{
Creator: creatorID,
principal, err := auth.NewDIDPrincipal(creatorID)
if err != nil {
return "", newErrInvalidActorID(err, creatorID)
}
ctx = auth.InjectPrincipal(ctx, principal)

createPolicy := types.CreatePolicyRequest{
Policy: policy,
MarshalType: policyMarshalType,
MarshalType: types.PolicyMarshalingType(marshalType),
CreationTime: protoTypes.TimestampNow(),
}

createPolicyResponse, err := l.localACP.GetMsgService().CreatePolicy(
l.localACP.GetCtx(),
&createPolicy,
)
response, err := l.engine.CreatePolicy(ctx, &createPolicy)
if err != nil {
return "", err
}

return createPolicyResponse.Policy.Id, nil
return response.Policy.Id, nil
}

func (l *ACPLocal) Policy(
ctx context.Context,
policyID string,
) (*types.Policy, error) {
queryPolicyResponse, err := l.localACP.GetQueryService().Policy(
l.localACP.GetCtx(),
&types.QueryPolicyRequest{Id: policyID},
)
) (immutable.Option[policy], error) {
none := immutable.None[policy]()

request := types.GetPolicyRequest{Id: policyID}
response, err := l.engine.GetPolicy(ctx, &request)

if err != nil {
return nil, err
if errors.Is(err, types.ErrPolicyNotFound) {
return none, nil
}
return none, err
}

return queryPolicyResponse.Policy, nil
policy := mapACPCorePolicy(response.Policy)
return immutable.Some(policy), nil
}

func (l *ACPLocal) RegisterObject(
Expand All @@ -112,36 +157,51 @@ func (l *ACPLocal) RegisterObject(
resourceName string,
objectID string,
creationTime *protoTypes.Timestamp,
) (types.RegistrationResult, error) {
registerDocResponse, err := l.localACP.GetMsgService().RegisterObject(
l.localACP.GetCtx(),
&types.MsgRegisterObject{
Creator: actorID,
PolicyId: policyID,
Object: types.NewObject(resourceName, objectID),
CreationTime: creationTime,
},
)
) (RegistrationResult, error) {
principal, err := auth.NewDIDPrincipal(actorID)
if err != nil {
return types.RegistrationResult(0), err
return RegistrationResult_NoOp, newErrInvalidActorID(err, actorID)
}

ctx = auth.InjectPrincipal(ctx, principal)
req := types.RegisterObjectRequest{
PolicyId: policyID,
Object: types.NewObject(resourceName, objectID),
CreationTime: creationTime,
}

return registerDocResponse.Result, nil
registerDocResponse, err := l.engine.RegisterObject(ctx, &req)

if err != nil {
return RegistrationResult_NoOp, err
}

result := RegistrationResult(registerDocResponse.Result)
return result, nil
}

func (l *ACPLocal) ObjectOwner(
ctx context.Context,
policyID string,
resourceName string,
objectID string,
) (*types.QueryObjectOwnerResponse, error) {
return l.localACP.GetQueryService().ObjectOwner(
l.localACP.GetCtx(),
&types.QueryObjectOwnerRequest{
PolicyId: policyID,
Object: types.NewObject(resourceName, objectID),
},
)
) (immutable.Option[string], error) {
none := immutable.None[string]()

req := types.GetObjectRegistrationRequest{
PolicyId: policyID,
Object: types.NewObject(resourceName, objectID),
}
result, err := l.engine.GetObjectRegistration(ctx, &req)
if err != nil {
return none, err
}

if result.IsRegistered {
return immutable.Some(result.OwnerId), nil
}

return none, nil
}

func (l *ACPLocal) VerifyAccessRequest(
Expand All @@ -152,26 +212,25 @@ func (l *ACPLocal) VerifyAccessRequest(
resourceName string,
docID string,
) (bool, error) {
checkDocResponse, err := l.localACP.GetQueryService().VerifyAccessRequest(
l.localACP.GetCtx(),
&types.QueryVerifyAccessRequestRequest{
PolicyId: policyID,
AccessRequest: &types.AccessRequest{
Operations: []*types.Operation{
{
Object: types.NewObject(resourceName, docID),
Permission: permission.String(),
},
},
Actor: &types.Actor{
Id: actorID,
req := types.VerifyAccessRequestRequest{
PolicyId: policyID,
AccessRequest: &types.AccessRequest{
Operations: []*types.Operation{
{
Object: types.NewObject(resourceName, docID),
Permission: permission.String(),
},
},
Actor: &types.Actor{
Id: actorID,
},
},
)
}
resp, err := l.engine.VerifyAccessRequest(ctx, &req)

if err != nil {
return false, err
}

return checkDocResponse.Valid, nil
return resp.Valid, nil
}
Loading

0 comments on commit 6573d8c

Please sign in to comment.