diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 0000000..81dad9c --- /dev/null +++ b/.codecov.yml @@ -0,0 +1,3 @@ +ignore: + - "testutil/" + - "scripts/" diff --git a/.golangci.yml b/.golangci.yml index 17c3feb..b65edf4 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -93,6 +93,7 @@ issues: linters: - dupl - funlen + - goconst - path: "_test\\.go" linters: - revive diff --git a/Makefile b/Makefile index 9b43db5..2c9bebf 100644 --- a/Makefile +++ b/Makefile @@ -81,6 +81,7 @@ mock: ## Generate all the mocks (for tests) @mockgen -source=credential/parser.go -package testutil -destination testutil/credential_mocks.go @mockgen -package testutil -destination testutil/dataverse_client_mocks.go -mock_names QueryClient=MockDataverseQueryClient github.com/axone-protocol/axone-contract-schema/go/dataverse-schema/v5 QueryClient @mockgen -package testutil -destination testutil/cognitarium_client_mocks.go -mock_names QueryClient=MockCognitariumQueryClient github.com/axone-protocol/axone-contract-schema/go/cognitarium-schema/v5 QueryClient + @mockgen -package testutil -destination testutil/law_stone_client_mocks.go -mock_names QueryClient=MockLawStoneQueryClient github.com/axone-protocol/axone-contract-schema/go/law-stone-schema/v5 QueryClient @mockgen -package testutil -destination testutil/signer_mocks.go github.com/hyperledger/aries-framework-go/pkg/doc/verifiable Signer @mockgen -source=credential/generate.go -package testutil -destination testutil/generate_mocks.go diff --git a/auth/proxy.go b/auth/proxy.go index 0e62297..5aa7e76 100644 --- a/auth/proxy.go +++ b/auth/proxy.go @@ -49,14 +49,13 @@ func (a *authProxy) Authenticate(ctx context.Context, credential []byte) (*Ident return nil, fmt.Errorf("credential not intended for this service: `%s` (target: `%s`)", a.serviceID, authClaim.ToService) } - // TODO: get authorized actions from governance, ex: - res, err := a.dvClient.ExecGov(ctx, a.govAddr, fmt.Sprintf("can(Action,'%s').", authClaim.ID)) + actions, err := a.dvClient.AskGovPermittedActions(ctx, a.govAddr, authClaim.ID) if err != nil { - return nil, err + return nil, fmt.Errorf("failed to query governance for permitted actions: %w", err) } return &Identity{ DID: authClaim.ID, - AuthorizedActions: res.([]string), + AuthorizedActions: actions, }, nil } diff --git a/auth/proxy_test.go b/auth/proxy_test.go index 680611f..3def664 100644 --- a/auth/proxy_test.go +++ b/auth/proxy_test.go @@ -63,7 +63,7 @@ func TestAuthProxy_Authenticate(t *testing.T) { } mockDataverse := testutil.NewMockClient(controller) - mockDataverse.EXPECT().ExecGov(gomock.Any(), gomock.Any(), gomock.Any()).Return([]string(nil), nil).MaxTimes(1) + mockDataverse.EXPECT().AskGovPermittedActions(gomock.Any(), gomock.Any(), gomock.Any()).Return([]string(nil), nil).MaxTimes(1) aProxy := auth.NewProxy( "did:key:zQ3shZxyDoD3QorxHJrFS68EjzDgQZSqZcj3wQqc1ngbF1vgz", diff --git a/dataverse/client.go b/dataverse/client.go index 87f3baf..d0fefd7 100644 --- a/dataverse/client.go +++ b/dataverse/client.go @@ -6,6 +6,7 @@ import ( cgschema "github.com/axone-protocol/axone-contract-schema/go/cognitarium-schema/v5" dvschema "github.com/axone-protocol/axone-contract-schema/go/dataverse-schema/v5" + lsschema "github.com/axone-protocol/axone-contract-schema/go/law-stone-schema/v5" "google.golang.org/grpc" ) @@ -14,12 +15,29 @@ type Client interface { // It queries the cognitarium to get the governance address (law-stone contract address) // of a resource. The resource is identified by its DID. GetResourceGovAddr(context.Context, string) (string, error) - ExecGov(context.Context, string, string) (interface{}, error) + + // AskGovPermittedActions returns the permitted actions for a resource identified by its DID. + // It queries the law-stone contract to get the permitted actions for a resource using the following predicate: + // ```prolog + // tell_permitted_actions(DID, Actions). + // ``` + AskGovPermittedActions(context.Context, string, string) ([]string, error) + + // AskGovTellAction queries the law-stone contract to check if a given action is permitted for a resource. + // It uses the following predicate: + // ```prolog + // tell(DID, Action, Result, Evidence). + // ``` + // The function returns true if Result is 'permitted', false otherwise. + AskGovTellAction(context.Context, string, string, string) (bool, error) } +type LawStoneFactory func(string) (lsschema.QueryClient, error) + type client struct { dataverseClient dvschema.QueryClient cognitariumClient cgschema.QueryClient + lawStoneFactory LawStoneFactory } func NewClient(ctx context.Context, @@ -44,6 +62,9 @@ func NewClient(ctx context.Context, return &client{ dataverseClient, cognitariumClient, + func(addr string) (lsschema.QueryClient, error) { + return lsschema.NewQueryClient(grpcAddr, addr, opts...) + }, }, nil } diff --git a/dataverse/export_test.go b/dataverse/export_test.go index 2be1204..24e493c 100644 --- a/dataverse/export_test.go +++ b/dataverse/export_test.go @@ -8,10 +8,12 @@ import ( func NewDataverseClient( dataverseClient dvschema.QueryClient, cognitariumClient cgschema.QueryClient, + lawStoneFactory LawStoneFactory, ) Client { return &client{ dataverseClient, cognitariumClient, + lawStoneFactory, } } diff --git a/dataverse/governance.go b/dataverse/governance.go index 85ae156..123cbb3 100644 --- a/dataverse/governance.go +++ b/dataverse/governance.go @@ -3,8 +3,10 @@ package dataverse import ( "context" "fmt" + "strings" cgschema "github.com/axone-protocol/axone-contract-schema/go/cognitarium-schema/v5" + lsschema "github.com/axone-protocol/axone-contract-schema/go/law-stone-schema/v5" ) func (c *client) GetResourceGovAddr(ctx context.Context, resourceDID string) (string, error) { @@ -29,6 +31,51 @@ func (c *client) GetResourceGovAddr(ctx context.Context, resourceDID string) (st return string(*code.Value.Full), nil } -func (c *client) ExecGov(_ context.Context, _ string, _ string) (interface{}, error) { - panic("not implemented") +func (c *client) AskGovPermittedActions(ctx context.Context, addr, did string) ([]string, error) { + gov, err := c.lawStoneFactory(addr) + if err != nil { + return nil, fmt.Errorf("failed to create law-stone client: %w", err) + } + + response, err := gov.Ask(ctx, &lsschema.QueryMsg_Ask{Query: fmt.Sprintf("tell_permitted_actions('%s',Actions).", did)}) + if err != nil { + return nil, fmt.Errorf("failed to query law-stone contract: %w", err) + } + + if len(response.Answer.Results) != 1 { + return nil, nil + } + if len(response.Answer.Results[0].Substitutions) != 1 { + return nil, nil + } + + result := response.Answer.Results[0].Substitutions[0].Expression + result = result[1 : len(result)-1] + actions := make([]string, 0) + for _, action := range strings.Split(result, ",") { + actions = append(actions, strings.Trim(action, "'")) + } + + return actions, nil +} + +func (c *client) AskGovTellAction(ctx context.Context, addr, did, action string) (bool, error) { + gov, err := c.lawStoneFactory(addr) + if err != nil { + return false, fmt.Errorf("failed to create law-stone client: %w", err) + } + + response, err := gov.Ask(ctx, &lsschema.QueryMsg_Ask{Query: fmt.Sprintf("tell('%s','%s',Result,_).", did, action)}) + if err != nil { + return false, fmt.Errorf("failed to query law-stone contract: %w", err) + } + + if len(response.Answer.Results) != 1 { + return false, nil + } + if len(response.Answer.Results[0].Substitutions) != 1 { + return false, nil + } + + return response.Answer.Results[0].Substitutions[0].Expression == "permitted", nil } diff --git a/dataverse/governance_test.go b/dataverse/governance_test.go index db1136f..e892799 100644 --- a/dataverse/governance_test.go +++ b/dataverse/governance_test.go @@ -6,6 +6,7 @@ import ( "testing" cgschema "github.com/axone-protocol/axone-contract-schema/go/cognitarium-schema/v5" + lsschema "github.com/axone-protocol/axone-contract-schema/go/law-stone-schema/v5" "github.com/axone-protocol/axone-sdk/dataverse" "github.com/axone-protocol/axone-sdk/testutil" . "github.com/smartystreets/goconvey/convey" @@ -135,6 +136,7 @@ func TestClient_GetResourceGovAddr(t *testing.T) { client := dataverse.NewDataverseClient( mockDataverseClient, mockCognitarium, + nil, ) Convey("When GetResourceGovAddr is called", func() { @@ -154,3 +156,322 @@ func TestClient_GetResourceGovAddr(t *testing.T) { }) } } + +func TestClient_AskGovPermittedActions(t *testing.T) { + tests := []struct { + name string + addr string + did string + response *lsschema.AskResponse + responseError error + wantErr error + wantResult []string + }{ + { + name: "law stone client new error", + addr: "error", + did: "did:key:zQ3shuwMJWYXRi64qiGojsV9bPN6Dtugz5YFM2ESPtkaNxTZ5", + response: nil, + responseError: nil, + wantErr: fmt.Errorf("failed to create law-stone client: error"), + wantResult: nil, + }, + { + name: "law stone client ask error", + addr: "foo", + did: "did:key:zQ3shuwMJWYXRi64qiGojsV9bPN6Dtugz5YFM2ESPtkaNxTZ5", + response: nil, + responseError: fmt.Errorf("error"), + wantErr: fmt.Errorf("failed to query law-stone contract: error"), + wantResult: nil, + }, + { + name: "no results in response", + addr: "foo", + did: "did:key:zQ3shuwMJWYXRi64qiGojsV9bPN6Dtugz5YFM2ESPtkaNxTZ5", + response: &lsschema.AskResponse{ + Answer: &lsschema.Answer{ + Results: []lsschema.Result{}, + }, + }, + responseError: nil, + wantErr: nil, + wantResult: nil, + }, + { + name: "no substitutions in response", + addr: "foo", + did: "did:key:zQ3shuwMJWYXRi64qiGojsV9bPN6Dtugz5YFM2ESPtkaNxTZ5", + response: &lsschema.AskResponse{ + Answer: &lsschema.Answer{ + Results: []lsschema.Result{ + { + Substitutions: []lsschema.Substitution{}, + }, + }, + }, + }, + responseError: nil, + wantErr: nil, + wantResult: nil, + }, + { + name: "single action in response", + addr: "foo", + did: "did:key:zQ3shuwMJWYXRi64qiGojsV9bPN6Dtugz5YFM2ESPtkaNxTZ5", + response: &lsschema.AskResponse{ + Answer: &lsschema.Answer{ + Results: []lsschema.Result{ + { + Substitutions: []lsschema.Substitution{ + { + Expression: "['read']", + }, + }, + }, + }, + }, + }, + responseError: nil, + wantErr: nil, + wantResult: []string{"read"}, + }, + { + name: "multiple actions in response", + addr: "foo", + did: "did:key:zQ3shuwMJWYXRi64qiGojsV9bPN6Dtugz5YFM2ESPtkaNxTZ5", + response: &lsschema.AskResponse{ + Answer: &lsschema.Answer{ + Results: []lsschema.Result{ + { + Substitutions: []lsschema.Substitution{ + { + Expression: "['read','store']", + }, + }, + }, + }, + }, + }, + responseError: nil, + wantErr: nil, + wantResult: []string{"read", "store"}, + }, + { + name: "quoted/unquoted actions in response", + addr: "foo", + did: "did:key:zQ3shuwMJWYXRi64qiGojsV9bPN6Dtugz5YFM2ESPtkaNxTZ5", + response: &lsschema.AskResponse{ + Answer: &lsschema.Answer{ + Results: []lsschema.Result{ + { + Substitutions: []lsschema.Substitution{ + { + Expression: "['read',store]", + }, + }, + }, + }, + }, + }, + responseError: nil, + wantErr: nil, + wantResult: []string{"read", "store"}, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + Convey("Given a mocked dataverse client", t, func() { + controller := gomock.NewController(t) + defer controller.Finish() + + lawStoneMock := testutil.NewMockLawStoneQueryClient(controller) + if test.addr != "error" { + lawStoneMock.EXPECT(). + Ask(gomock.Any(), gomock.Eq(&lsschema.QueryMsg_Ask{Query: fmt.Sprintf("tell_permitted_actions('%s',Actions).", test.did)})). + Return(test.response, test.responseError). + Times(1) + } + + client := dataverse.NewDataverseClient( + testutil.NewMockDataverseQueryClient(controller), + testutil.NewMockCognitariumQueryClient(controller), + func(addr string) (lsschema.QueryClient, error) { + if addr == "error" { + return nil, fmt.Errorf("error") + } + + return lawStoneMock, nil + }, + ) + + Convey("When AskGovPermittedActions is called", func() { + actions, err := client.AskGovPermittedActions(context.Background(), test.addr, test.did) + + Convey("Then the permitted actions should be returned", func() { + if test.wantErr == nil { + So(err, ShouldBeNil) + So(actions, ShouldResemble, test.wantResult) + } else { + So(err.Error(), ShouldEqual, test.wantErr.Error()) + So(actions, ShouldBeNil) + } + }) + }) + }) + }) + } +} + +func TestClient_AskGovTellAction(t *testing.T) { + tests := []struct { + name string + addr string + did string + action string + response *lsschema.AskResponse + responseError error + wantErr error + wantResult bool + }{ + { + name: "law stone client new error", + addr: "error", + did: "did:key:zQ3shuwMJWYXRi64qiGojsV9bPN6Dtugz5YFM2ESPtkaNxTZ5", + action: "read", + response: nil, + responseError: nil, + wantErr: fmt.Errorf("failed to create law-stone client: error"), + wantResult: false, + }, + { + name: "law stone client ask error", + addr: "foo", + did: "did:key:zQ3shuwMJWYXRi64qiGojsV9bPN6Dtugz5YFM2ESPtkaNxTZ5", + action: "read", + response: nil, + responseError: fmt.Errorf("error"), + wantErr: fmt.Errorf("failed to query law-stone contract: error"), + wantResult: false, + }, + { + name: "no results in response", + addr: "foo", + did: "did:key:zQ3shuwMJWYXRi64qiGojsV9bPN6Dtugz5YFM2ESPtkaNxTZ5", + action: "read", + response: &lsschema.AskResponse{ + Answer: &lsschema.Answer{ + Results: []lsschema.Result{}, + }, + }, + responseError: nil, + wantErr: nil, + wantResult: false, + }, + { + name: "no substitutions in response", + addr: "foo", + did: "did:key:zQ3shuwMJWYXRi64qiGojsV9bPN6Dtugz5YFM2ESPtkaNxTZ5", + action: "read", + response: &lsschema.AskResponse{ + Answer: &lsschema.Answer{ + Results: []lsschema.Result{ + { + Substitutions: []lsschema.Substitution{}, + }, + }, + }, + }, + responseError: nil, + wantErr: nil, + wantResult: false, + }, + { + name: "permitted", + addr: "foo", + did: "did:key:zQ3shuwMJWYXRi64qiGojsV9bPN6Dtugz5YFM2ESPtkaNxTZ5", + action: "read", + response: &lsschema.AskResponse{ + Answer: &lsschema.Answer{ + Results: []lsschema.Result{ + { + Substitutions: []lsschema.Substitution{ + { + Expression: "permitted", + }, + }, + }, + }, + }, + }, + responseError: nil, + wantErr: nil, + wantResult: true, + }, + { + name: "prohibited", + addr: "foo", + did: "did:key:zQ3shuwMJWYXRi64qiGojsV9bPN6Dtugz5YFM2ESPtkaNxTZ5", + action: "store", + response: &lsschema.AskResponse{ + Answer: &lsschema.Answer{ + Results: []lsschema.Result{ + { + Substitutions: []lsschema.Substitution{ + { + Expression: "prohibited", + }, + }, + }, + }, + }, + }, + responseError: nil, + wantErr: nil, + wantResult: false, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + Convey("Given a mocked dataverse client", t, func() { + controller := gomock.NewController(t) + defer controller.Finish() + + lawStoneMock := testutil.NewMockLawStoneQueryClient(controller) + if test.addr != "error" { + lawStoneMock.EXPECT(). + Ask(gomock.Any(), gomock.Eq(&lsschema.QueryMsg_Ask{Query: fmt.Sprintf("tell('%s','%s',Result,_).", test.did, test.action)})). + Return(test.response, test.responseError). + Times(1) + } + + client := dataverse.NewDataverseClient( + testutil.NewMockDataverseQueryClient(controller), + testutil.NewMockCognitariumQueryClient(controller), + func(addr string) (lsschema.QueryClient, error) { + if addr == "error" { + return nil, fmt.Errorf("error") + } + + return lawStoneMock, nil + }, + ) + + Convey("When AskGovTellAction is called", func() { + result, err := client.AskGovTellAction(context.Background(), test.addr, test.did, test.action) + + Convey("Then it should indicate if the action is allowed", func() { + if test.wantErr == nil { + So(err, ShouldBeNil) + } else { + So(err.Error(), ShouldEqual, test.wantErr.Error()) + } + So(result, ShouldResemble, test.wantResult) + }) + }) + }) + }) + } +} diff --git a/go.mod b/go.mod index 85b87fe..a6f0bab 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.22.5 require ( github.com/axone-protocol/axone-contract-schema/go/cognitarium-schema/v5 v5.0.0-20240826124342-6e6abdf73c9a github.com/axone-protocol/axone-contract-schema/go/dataverse-schema/v5 v5.0.0-20240826124342-6e6abdf73c9a + github.com/axone-protocol/axone-contract-schema/go/law-stone-schema/v5 v5.0.0-20240829161824-0ad316c6fda9 github.com/axone-protocol/axoned/v9 v9.0.0 github.com/btcsuite/btcd v0.22.0-beta github.com/cosmos/cosmos-sdk v0.50.9 diff --git a/go.sum b/go.sum index be5f7c8..540a974 100644 --- a/go.sum +++ b/go.sum @@ -93,6 +93,8 @@ github.com/axone-protocol/axone-contract-schema/go/cognitarium-schema/v5 v5.0.0- github.com/axone-protocol/axone-contract-schema/go/cognitarium-schema/v5 v5.0.0-20240826124342-6e6abdf73c9a/go.mod h1:50VhFXluOq+7HLCsYhE/iaw3yRKohfC4i5dotePt8UI= github.com/axone-protocol/axone-contract-schema/go/dataverse-schema/v5 v5.0.0-20240826124342-6e6abdf73c9a h1:l4I0GhJitReEy+Sa5lSBN/fyjO8ISuduFxw3n7DHajE= github.com/axone-protocol/axone-contract-schema/go/dataverse-schema/v5 v5.0.0-20240826124342-6e6abdf73c9a/go.mod h1:ztbeFSQhlyxmoBMdCDXT3k9I8VswfCeq3F8TVrxpPVg= +github.com/axone-protocol/axone-contract-schema/go/law-stone-schema/v5 v5.0.0-20240829161824-0ad316c6fda9 h1:i5TEHxnkprtp3uOz4nXokxWzGxjLEKYJmRfKsQ6HYzY= +github.com/axone-protocol/axone-contract-schema/go/law-stone-schema/v5 v5.0.0-20240829161824-0ad316c6fda9/go.mod h1:en5YIi2VcOYS8vkUGB6HzAUfTk3miolwnzm+Qs9FtfM= github.com/axone-protocol/axoned/v9 v9.0.0 h1:tED2oKxf9KF4S8hbpDuUJL/91pEoYrV1femALJovRmY= github.com/axone-protocol/axoned/v9 v9.0.0/go.mod h1:9RDsnrYBqXPQPOMFTWiTFULImclBrvUL1U89PUAimHA= github.com/axone-protocol/prolog v1.0.0 h1:CASA1QrPOWhYox8YUStML33rekoA/7Gnp/ldDPZqCTA= diff --git a/provider/storage/http.go b/provider/storage/http.go index ae49497..b6810a7 100644 --- a/provider/storage/http.go +++ b/provider/storage/http.go @@ -4,6 +4,7 @@ import ( "context" "io" "net/http" + "strings" "time" "github.com/axone-protocol/axone-sdk/auth" @@ -26,14 +27,16 @@ func (p *Proxy) HTTPReadHandler() auth.AuthenticatedHandler { return func(id *auth.Identity, writer http.ResponseWriter, request *http.Request) { resource, err := p.Read(context.Background(), id, mux.Vars(request)["path"]) if err != nil { - // ... + writer.WriteHeader(http.StatusUnauthorized) + _, _ = io.Copy(writer, strings.NewReader(err.Error())) return } - writer.WriteHeader(http.StatusOK) if _, err := io.Copy(writer, resource); err != nil { + writer.WriteHeader(http.StatusInternalServerError) return } + writer.WriteHeader(http.StatusOK) } } @@ -41,13 +44,15 @@ func (p *Proxy) HTTPStoreHandler() auth.AuthenticatedHandler { return func(id *auth.Identity, writer http.ResponseWriter, request *http.Request) { vc, err := p.Store(context.Background(), id, mux.Vars(request)["path"], request.Body) if err != nil { - // ... + writer.WriteHeader(http.StatusUnauthorized) + _, _ = io.Copy(writer, strings.NewReader(err.Error())) return } - writer.WriteHeader(http.StatusOK) if _, err := io.Copy(writer, vc); err != nil { + writer.WriteHeader(http.StatusInternalServerError) return } + writer.WriteHeader(http.StatusOK) } } diff --git a/provider/storage/proxy.go b/provider/storage/proxy.go index 52a29f8..c265e3e 100644 --- a/provider/storage/proxy.go +++ b/provider/storage/proxy.go @@ -4,13 +4,14 @@ import ( "bytes" "context" "errors" - "fmt" "io" "github.com/axone-protocol/axone-sdk/auth" "github.com/axone-protocol/axone-sdk/credential" + "github.com/axone-protocol/axone-sdk/credential/template" "github.com/axone-protocol/axone-sdk/dataverse" "github.com/axone-protocol/axone-sdk/keys" + "github.com/piprate/json-gold/ld" ) const ( @@ -20,8 +21,10 @@ const ( type Proxy struct { key *keys.Key + baseURL string dvClient dataverse.Client authProxy auth.Proxy + vcParser *credential.DefaultParser // given a resource id return its stream readFn func(context.Context, string) (io.Reader, error) @@ -32,9 +35,9 @@ type Proxy struct { func NewProxy( ctx context.Context, key *keys.Key, - serviceID string, + baseURL string, dvClient dataverse.Client, - authParser credential.Parser[*credential.AuthClaim], + documentLoader ld.DocumentLoader, readFn func(context.Context, string) (io.Reader, error), storeFn func(context.Context, string, io.Reader) error, ) (*Proxy, error) { @@ -43,10 +46,16 @@ func NewProxy( return nil, err } + if baseURL[len(baseURL)-1] != '/' { + baseURL += "/" + } + return &Proxy{ key: key, + baseURL: baseURL, dvClient: dvClient, - authProxy: auth.NewProxy(gov, serviceID, dvClient, authParser), + authProxy: auth.NewProxy(gov, key.DID, dvClient, credential.NewAuthParser(documentLoader)), + vcParser: credential.NewDefaultParser(documentLoader), readFn: readFn, storeFn: storeFn, }, nil @@ -57,29 +66,28 @@ func (p *Proxy) Authenticate(ctx context.Context, credential []byte) (*auth.Iden } func (p *Proxy) Read(ctx context.Context, id *auth.Identity, resourceID string) (io.Reader, error) { - // check authenticated identity resolved authorized actions if !id.Can(readAction) { return nil, errors.New("unauthorized") } - // get resource gov addr govAddr, err := p.dvClient.GetResourceGovAddr(ctx, resourceID) if err != nil { return nil, err } - // exec resource gov - _, err = p.dvClient.ExecGov(ctx, govAddr, fmt.Sprintf("can('%s','%s').", readAction, p.key.DID)) + ok, err := p.dvClient.AskGovTellAction(ctx, govAddr, p.key.DID, readAction) if err != nil { return nil, err } - // fetch resource data + if !ok { + return nil, errors.New("unauthorized") + } + return p.readFn(ctx, resourceID) } func (p *Proxy) Store(ctx context.Context, id *auth.Identity, resourceID string, src io.Reader) (io.Reader, error) { - // check authenticated identity resolved authorized actions if !id.Can(storeAction) { return nil, errors.New("unauthorized") } @@ -88,5 +96,19 @@ func (p *Proxy) Store(ctx context.Context, id *auth.Identity, resourceID string, return nil, err } - return bytes.NewReader([]byte("publication VC")), nil + vc, err := credential.New( + template.NewPublication(resourceID, p.baseURL+resourceID, p.key.DID), + credential.WithParser(p.vcParser), + credential.WithSigner(p.key, p.key.DID), + ).Generate() + if err != nil { + return nil, err + } + + raw, err := vc.MarshalJSON() + if err != nil { + return nil, err + } + + return bytes.NewReader(raw), nil } diff --git a/testutil/dataverse_mocks.go b/testutil/dataverse_mocks.go index 9af1d18..69cf15f 100644 --- a/testutil/dataverse_mocks.go +++ b/testutil/dataverse_mocks.go @@ -39,19 +39,34 @@ func (m *MockClient) EXPECT() *MockClientMockRecorder { return m.recorder } -// ExecGov mocks base method. -func (m *MockClient) ExecGov(arg0 context.Context, arg1, arg2 string) (any, error) { +// AskGovPermittedActions mocks base method. +func (m *MockClient) AskGovPermittedActions(arg0 context.Context, arg1, arg2 string) ([]string, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ExecGov", arg0, arg1, arg2) - ret0, _ := ret[0].(any) + ret := m.ctrl.Call(m, "AskGovPermittedActions", arg0, arg1, arg2) + ret0, _ := ret[0].([]string) ret1, _ := ret[1].(error) return ret0, ret1 } -// ExecGov indicates an expected call of ExecGov. -func (mr *MockClientMockRecorder) ExecGov(arg0, arg1, arg2 any) *gomock.Call { +// AskGovPermittedActions indicates an expected call of AskGovPermittedActions. +func (mr *MockClientMockRecorder) AskGovPermittedActions(arg0, arg1, arg2 any) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ExecGov", reflect.TypeOf((*MockClient)(nil).ExecGov), arg0, arg1, arg2) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AskGovPermittedActions", reflect.TypeOf((*MockClient)(nil).AskGovPermittedActions), arg0, arg1, arg2) +} + +// AskGovTellAction mocks base method. +func (m *MockClient) AskGovTellAction(arg0 context.Context, arg1, arg2, arg3 string) (bool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "AskGovTellAction", arg0, arg1, arg2, arg3) + ret0, _ := ret[0].(bool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// AskGovTellAction indicates an expected call of AskGovTellAction. +func (mr *MockClientMockRecorder) AskGovTellAction(arg0, arg1, arg2, arg3 any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AskGovTellAction", reflect.TypeOf((*MockClient)(nil).AskGovTellAction), arg0, arg1, arg2, arg3) } // GetResourceGovAddr mocks base method. diff --git a/testutil/law_stone_client_mocks.go b/testutil/law_stone_client_mocks.go new file mode 100644 index 0000000..71158f8 --- /dev/null +++ b/testutil/law_stone_client_mocks.go @@ -0,0 +1,102 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: github.com/axone-protocol/axone-contract-schema/go/law-stone-schema/v5 (interfaces: QueryClient) +// +// Generated by this command: +// +// mockgen -package testutil -destination testutil/law_stone_client_mocks.go -mock_names QueryClient=MockLawStoneQueryClient github.com/axone-protocol/axone-contract-schema/go/law-stone-schema/v5 QueryClient +// + +// Package testutil is a generated GoMock package. +package testutil + +import ( + context "context" + reflect "reflect" + + schema "github.com/axone-protocol/axone-contract-schema/go/law-stone-schema/v5" + gomock "go.uber.org/mock/gomock" + grpc "google.golang.org/grpc" +) + +// MockLawStoneQueryClient is a mock of QueryClient interface. +type MockLawStoneQueryClient struct { + ctrl *gomock.Controller + recorder *MockLawStoneQueryClientMockRecorder +} + +// MockLawStoneQueryClientMockRecorder is the mock recorder for MockLawStoneQueryClient. +type MockLawStoneQueryClientMockRecorder struct { + mock *MockLawStoneQueryClient +} + +// NewMockLawStoneQueryClient creates a new mock instance. +func NewMockLawStoneQueryClient(ctrl *gomock.Controller) *MockLawStoneQueryClient { + mock := &MockLawStoneQueryClient{ctrl: ctrl} + mock.recorder = &MockLawStoneQueryClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockLawStoneQueryClient) EXPECT() *MockLawStoneQueryClientMockRecorder { + return m.recorder +} + +// Ask mocks base method. +func (m *MockLawStoneQueryClient) Ask(arg0 context.Context, arg1 *schema.QueryMsg_Ask, arg2 ...grpc.CallOption) (*schema.AskResponse, error) { + m.ctrl.T.Helper() + varargs := []any{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Ask", varargs...) + ret0, _ := ret[0].(*schema.AskResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Ask indicates an expected call of Ask. +func (mr *MockLawStoneQueryClientMockRecorder) Ask(arg0, arg1 any, arg2 ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ask", reflect.TypeOf((*MockLawStoneQueryClient)(nil).Ask), varargs...) +} + +// Program mocks base method. +func (m *MockLawStoneQueryClient) Program(arg0 context.Context, arg1 *schema.QueryMsg_Program, arg2 ...grpc.CallOption) (*schema.ProgramResponse, error) { + m.ctrl.T.Helper() + varargs := []any{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "Program", varargs...) + ret0, _ := ret[0].(*schema.ProgramResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Program indicates an expected call of Program. +func (mr *MockLawStoneQueryClientMockRecorder) Program(arg0, arg1 any, arg2 ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Program", reflect.TypeOf((*MockLawStoneQueryClient)(nil).Program), varargs...) +} + +// ProgramCode mocks base method. +func (m *MockLawStoneQueryClient) ProgramCode(arg0 context.Context, arg1 *schema.QueryMsg_ProgramCode, arg2 ...grpc.CallOption) (*string, error) { + m.ctrl.T.Helper() + varargs := []any{arg0, arg1} + for _, a := range arg2 { + varargs = append(varargs, a) + } + ret := m.ctrl.Call(m, "ProgramCode", varargs...) + ret0, _ := ret[0].(*string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ProgramCode indicates an expected call of ProgramCode. +func (mr *MockLawStoneQueryClientMockRecorder) ProgramCode(arg0, arg1 any, arg2 ...any) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]any{arg0, arg1}, arg2...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProgramCode", reflect.TypeOf((*MockLawStoneQueryClient)(nil).ProgramCode), varargs...) +}