Skip to content
Snippets Groups Projects
Commit 15184f73 authored by Lyuben Penkovski's avatar Lyuben Penkovski
Browse files

Merge branch '3-export-endpoint' into 'main'

HTTP endpoint for exporting policy evaluation results

Closes #3 and #5

See merge request !2
parents 0a5b43ca a610db86
No related branches found
No related tags found
1 merge request!2HTTP endpoint for exporting policy evaluation results
Pipeline #51447 passed
Showing
with 1524 additions and 27 deletions
...@@ -4,16 +4,20 @@ import ( ...@@ -4,16 +4,20 @@ import (
"context" "context"
"errors" "errors"
"log" "log"
"net"
"net/http" "net/http"
"time" "time"
"github.com/kelseyhightower/envconfig" "github.com/kelseyhightower/envconfig"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.uber.org/zap" "go.uber.org/zap"
"go.uber.org/zap/zapcore" "go.uber.org/zap/zapcore"
goahttp "goa.design/goa/v3/http" goahttp "goa.design/goa/v3/http"
goa "goa.design/goa/v3/pkg" goa "goa.design/goa/v3/pkg"
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
"code.vereign.com/gaiax/tsa/golib/cache"
"code.vereign.com/gaiax/tsa/golib/graceful" "code.vereign.com/gaiax/tsa/golib/graceful"
goahealth "code.vereign.com/gaiax/tsa/infohub/gen/health" goahealth "code.vereign.com/gaiax/tsa/infohub/gen/health"
goahealthsrv "code.vereign.com/gaiax/tsa/infohub/gen/http/health/server" goahealthsrv "code.vereign.com/gaiax/tsa/infohub/gen/http/health/server"
...@@ -21,10 +25,14 @@ import ( ...@@ -21,10 +25,14 @@ import (
goaopenapisrv "code.vereign.com/gaiax/tsa/infohub/gen/http/openapi/server" goaopenapisrv "code.vereign.com/gaiax/tsa/infohub/gen/http/openapi/server"
goainfohub "code.vereign.com/gaiax/tsa/infohub/gen/infohub" goainfohub "code.vereign.com/gaiax/tsa/infohub/gen/infohub"
"code.vereign.com/gaiax/tsa/infohub/gen/openapi" "code.vereign.com/gaiax/tsa/infohub/gen/openapi"
"code.vereign.com/gaiax/tsa/infohub/internal/clients/policy"
"code.vereign.com/gaiax/tsa/infohub/internal/clients/vault"
"code.vereign.com/gaiax/tsa/infohub/internal/config" "code.vereign.com/gaiax/tsa/infohub/internal/config"
"code.vereign.com/gaiax/tsa/infohub/internal/credential"
"code.vereign.com/gaiax/tsa/infohub/internal/service" "code.vereign.com/gaiax/tsa/infohub/internal/service"
"code.vereign.com/gaiax/tsa/infohub/internal/service/health" "code.vereign.com/gaiax/tsa/infohub/internal/service/health"
"code.vereign.com/gaiax/tsa/infohub/internal/service/infohub" "code.vereign.com/gaiax/tsa/infohub/internal/service/infohub"
"code.vereign.com/gaiax/tsa/infohub/internal/storage"
) )
var Version = "0.0.0+development" var Version = "0.0.0+development"
...@@ -45,13 +53,46 @@ func main() { ...@@ -45,13 +53,46 @@ func main() {
logger.Info("infohub service started", zap.String("version", Version), zap.String("goa", goa.Version())) logger.Info("infohub service started", zap.String("version", Version), zap.String("goa", goa.Version()))
// connect to mongo db
db, err := mongo.Connect(
context.Background(),
options.Client().ApplyURI(cfg.Mongo.Addr).SetAuth(options.Credential{
Username: cfg.Mongo.User,
Password: cfg.Mongo.Pass,
}),
)
if err != nil {
logger.Fatal("error connecting to mongodb", zap.Error(err))
}
defer db.Disconnect(context.Background()) //nolint:errcheck
// create storage
storage, err := storage.New(db, cfg.Mongo.DB, cfg.Mongo.Collection, logger)
if err != nil {
logger.Fatal("error connecting to database", zap.Error(err))
}
vault, err := vault.New(cfg.Vault.Addr, cfg.Vault.Token, cfg.Vault.Keyname)
if err != nil {
logger.Fatal("error creating vault client", zap.Error(err))
}
httpClient := httpClient()
credentials := credential.NewIssuer(cfg.Credential.IssuerName, cfg.Credential.Keyname, vault, httpClient)
// create policy client
policy := policy.New(cfg.Policy.Addr, httpClient)
// create cache client
cache := cache.New(cfg.Cache.Addr)
// create services // create services
var ( var (
infohubSvc goainfohub.Service infohubSvc goainfohub.Service
healthSvc goahealth.Service healthSvc goahealth.Service
) )
{ {
infohubSvc = infohub.New(logger) infohubSvc = infohub.New(storage, policy, cache, credentials, logger)
healthSvc = health.New() healthSvc = health.New()
} }
...@@ -145,18 +186,18 @@ func errFormatter(e error) goahttp.Statuser { ...@@ -145,18 +186,18 @@ func errFormatter(e error) goahttp.Statuser {
return service.NewErrorResponse(e) return service.NewErrorResponse(e)
} }
//func httpClient() *http.Client { func httpClient() *http.Client {
// return &http.Client{ return &http.Client{
// Transport: &http.Transport{ Transport: &http.Transport{
// Proxy: http.ProxyFromEnvironment, Proxy: http.ProxyFromEnvironment,
// DialContext: (&net.Dialer{ DialContext: (&net.Dialer{
// Timeout: 30 * time.Second, Timeout: 30 * time.Second,
// }).DialContext, }).DialContext,
// MaxIdleConns: 100, MaxIdleConns: 100,
// MaxIdleConnsPerHost: 100, MaxIdleConnsPerHost: 100,
// TLSHandshakeTimeout: 10 * time.Second, TLSHandshakeTimeout: 10 * time.Second,
// IdleConnTimeout: 60 * time.Second, IdleConnTimeout: 60 * time.Second,
// }, },
// Timeout: 30 * time.Second, Timeout: 30 * time.Second,
// } }
//} }
FROM golang:1.17.10-alpine3.15 as builder
ENV GOPRIVATE=code.vereign.com
RUN apk add git
WORKDIR /go/src/code.vereign.com/gaiax/tsa/infohub
ADD . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-X main.Version=$(git describe --tags --always)" -mod=vendor -o /tmp/infohub ./cmd/infohub/...
FROM alpine:3.15 as runner
COPY --from=builder /tmp/infohub /opt/infohub
WORKDIR /opt
CMD ["./infohub"]
FROM golang:1.17.10
ENV GO111MODULE=on
ENV GOPRIVATE=code.vereign.com
RUN go install github.com/canthefason/go-watcher/cmd/watcher@v0.2.4
ADD . /go/src/code.vereign.com/gaiax/tsa/infohub
WORKDIR /go/src/code.vereign.com/gaiax/tsa/infohub
RUN go install -mod=vendor ./cmd/infohub/...
EXPOSE 8080
ENTRYPOINT ["sh", "-c", "/go/bin/watcher -run code.vereign.com/gaiax/tsa/infohub/cmd/infohub -watch code.vereign.com/gaiax/tsa/infohub"]
...@@ -3,29 +3,102 @@ module code.vereign.com/gaiax/tsa/infohub ...@@ -3,29 +3,102 @@ module code.vereign.com/gaiax/tsa/infohub
go 1.17 go 1.17
require ( require (
code.vereign.com/gaiax/tsa/golib v0.0.0-20220516062342-25fce7999743 code.vereign.com/gaiax/tsa/golib v0.0.0-20220603082703-12e9e3c06615
github.com/hashicorp/vault/api v1.6.0
github.com/hyperledger/aries-framework-go v0.1.8
github.com/kelseyhightower/envconfig v1.4.0 github.com/kelseyhightower/envconfig v1.4.0
github.com/piprate/json-gold v0.4.1
go.mongodb.org/mongo-driver v1.9.1
go.uber.org/zap v1.21.0 go.uber.org/zap v1.21.0
goa.design/goa/v3 v3.7.5 goa.design/goa/v3 v3.7.5
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
) )
require ( require (
github.com/VictoriaMetrics/fastcache v1.5.7 // indirect
github.com/armon/go-metrics v0.3.9 // indirect
github.com/armon/go-radix v1.0.0 // indirect
github.com/btcsuite/btcd v0.22.0-beta // indirect
github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce // indirect
github.com/cenkalti/backoff/v3 v3.0.0 // indirect
github.com/cespare/xxhash/v2 v2.1.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dimfeld/httppath v0.0.0-20170720192232-ee938bf73598 // indirect github.com/dimfeld/httppath v0.0.0-20170720192232-ee938bf73598 // indirect
github.com/dimfeld/httptreemux/v5 v5.4.0 // indirect github.com/dimfeld/httptreemux/v5 v5.4.0 // indirect
github.com/fatih/color v1.7.0 // indirect
github.com/go-stack/stack v1.8.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/tink/go v1.6.1-0.20210519071714-58be99b3c4d0 // indirect
github.com/google/uuid v1.3.0 // indirect github.com/google/uuid v1.3.0 // indirect
github.com/gopherjs/gopherjs v1.17.2 // indirect github.com/gopherjs/gopherjs v1.17.2 // indirect
github.com/gorilla/websocket v1.5.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-hclog v0.16.2 // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-plugin v1.4.3 // indirect
github.com/hashicorp/go-retryablehttp v0.6.6 // indirect
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 // indirect
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.5 // indirect
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
github.com/hashicorp/go-sockaddr v1.0.2 // indirect
github.com/hashicorp/go-uuid v1.0.2 // indirect
github.com/hashicorp/go-version v1.2.0 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hashicorp/vault/sdk v0.5.0 // indirect
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect
github.com/hyperledger/aries-framework-go/spi v0.0.0-20220322085443-50e8f9bd208b // indirect
github.com/jtolds/gls v4.20.0+incompatible // indirect github.com/jtolds/gls v4.20.0+incompatible // indirect
github.com/kilic/bls12-381 v0.1.1-0.20210503002446-7b7597926c69 // indirect
github.com/klauspost/compress v1.13.6 // indirect
github.com/manveru/faker v0.0.0-20171103152722-9fbc68a78c4d // indirect github.com/manveru/faker v0.0.0-20171103152722-9fbc68a78c4d // indirect
github.com/mattn/go-colorable v0.1.6 // indirect
github.com/mattn/go-isatty v0.0.12 // indirect
github.com/mitchellh/copystructure v1.0.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/go-testing-interface v1.0.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.0 // indirect
github.com/mr-tron/base58 v1.1.3 // indirect
github.com/multiformats/go-base32 v0.0.3 // indirect
github.com/multiformats/go-multibase v0.0.1 // indirect
github.com/oklog/run v1.0.0 // indirect
github.com/pierrec/lz4 v2.5.2+incompatible // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
github.com/ryanuber/go-glob v1.0.0 // indirect
github.com/sergi/go-diff v1.2.0 // indirect github.com/sergi/go-diff v1.2.0 // indirect
github.com/smartystreets/assertions v1.13.0 // indirect github.com/smartystreets/assertions v1.13.0 // indirect
github.com/square/go-jose/v3 v3.0.0-20200630053402-0a67ce9b0693 // indirect
github.com/stretchr/testify v1.7.0 // indirect
github.com/teserakt-io/golang-ed25519 v0.0.0-20210104091850-3888c087a4c8 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.0.2 // indirect
github.com/xdg-go/stringprep v1.0.2 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
github.com/zach-klippenstein/goregen v0.0.0-20160303162051-795b5e3961ea // indirect github.com/zach-klippenstein/goregen v0.0.0-20160303162051-795b5e3961ea // indirect
go.uber.org/atomic v1.7.0 // indirect go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.6.0 // indirect go.uber.org/multierr v1.6.0 // indirect
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect
golang.org/x/net v0.0.0-20220225172249-27dd8689420f // indirect
golang.org/x/sys v0.0.0-20220317061510-51cd9980dadf // indirect golang.org/x/sys v0.0.0-20220317061510-51cd9980dadf // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 // indirect
golang.org/x/tools v0.1.10 // indirect golang.org/x/tools v0.1.10 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/genproto v0.0.0-20220317150908-0efb43f6373e // indirect
google.golang.org/grpc v1.46.0 // indirect
google.golang.org/protobuf v1.28.0 // indirect
gopkg.in/square/go-jose.v2 v2.5.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
) )
This diff is collapsed.
package policy
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"code.vereign.com/gaiax/tsa/golib/errors"
)
const headerEvaluationID = "x-evaluation-id"
type Client struct {
addr string
httpClient *http.Client
}
func New(addr string, httpClient *http.Client) *Client {
return &Client{
addr: addr,
httpClient: httpClient,
}
}
// Evaluate calls the policy service to execute the given policy.
// The policy is expected as a string path uniquely identifying the
// policy that has to be evaluated. For example, with policy = `gaiax/didResolve/1.0`,
// the client will do HTTP request to http://policyhost/policy/gaiax/didResolve/1.0/evaluation.
func (c *Client) Evaluate(ctx context.Context, policy string, data interface{}, evaluationID string) ([]byte, error) {
uri := c.addr + "/policy/" + policy + "/evaluation"
policyURL, err := url.ParseRequestURI(uri)
if err != nil {
return nil, errors.New(errors.BadRequest, "invalid policy evaluation URL", err)
}
jsonData, err := json.Marshal(data)
if err != nil {
return nil, err
}
req, err := http.NewRequest(http.MethodPost, policyURL.String(), bytes.NewReader(jsonData))
if err != nil {
return nil, err
}
if evaluationID != "" {
req.Header.Set(headerEvaluationID, evaluationID)
}
resp, err := c.httpClient.Do(req.WithContext(ctx))
if err != nil {
return nil, err
}
defer resp.Body.Close() // nolint:errcheck
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected response on policy evaluation: %s", resp.Status)
}
return io.ReadAll(resp.Body)
}
package vault
import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"net/http"
vaultpkg "github.com/hashicorp/vault/api"
)
type Client struct {
keyname string
client *vaultpkg.Client
}
func New(addr string, token string, keyname string) (*Client, error) {
cfg := vaultpkg.DefaultConfig()
cfg.Address = addr
client, err := vaultpkg.NewClient(cfg)
if err != nil {
return nil, err
}
client.SetToken(token)
return &Client{
client: client,
keyname: keyname,
}, nil
}
func (c *Client) Sign(data []byte) ([]byte, error) {
body := map[string]interface{}{
"input": base64.StdEncoding.EncodeToString(data),
}
req := c.client.NewRequest(http.MethodPost, "/v1/transit/sign/"+c.keyname)
if err := req.SetJSONBody(body); err != nil {
return nil, err
}
// nolint:staticcheck
res, err := c.client.RawRequestWithContext(context.Background(), req)
if err != nil {
return nil, err
}
defer res.Body.Close()
if res.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected response from vault: %s", res.Status)
}
// expected response from the sign operation
var response struct {
Data struct {
Signature string `json:"signature"`
} `json:"data"`
}
if err := json.NewDecoder(res.Body).Decode(&response); err != nil {
return nil, err
}
if len(response.Data.Signature) == 0 {
return nil, fmt.Errorf("unexpected response: no signature")
}
return []byte(response.Data.Signature), nil
}
...@@ -3,7 +3,13 @@ package config ...@@ -3,7 +3,13 @@ package config
import "time" import "time"
type Config struct { type Config struct {
HTTP httpConfig HTTP httpConfig
Mongo mongoConfig
Policy policyConfig
Cache cacheConfig
Vault vaultConfig
Credential credentialConfig
LogLevel string `envconfig:"LOG_LEVEL" default:"INFO"` LogLevel string `envconfig:"LOG_LEVEL" default:"INFO"`
} }
...@@ -14,3 +20,30 @@ type httpConfig struct { ...@@ -14,3 +20,30 @@ type httpConfig struct {
ReadTimeout time.Duration `envconfig:"HTTP_READ_TIMEOUT" default:"10s"` ReadTimeout time.Duration `envconfig:"HTTP_READ_TIMEOUT" default:"10s"`
WriteTimeout time.Duration `envconfig:"HTTP_WRITE_TIMEOUT" default:"10s"` WriteTimeout time.Duration `envconfig:"HTTP_WRITE_TIMEOUT" default:"10s"`
} }
type mongoConfig struct {
Addr string `envconfig:"MONGO_ADDR" required:"true"`
User string `envconfig:"MONGO_USER" required:"true"`
Pass string `envconfig:"MONGO_PASS" required:"true"`
DB string `envconfig:"MONGO_DBNAME" default:"infohub"`
Collection string `envconfig:"MONGO_COLLECTION" default:"exports"`
}
type vaultConfig struct {
Addr string `envconfig:"VAULT_ADDR" required:"true"`
Token string `envconfig:"VAULT_TOKEN" required:"true"`
Keyname string `envconfig:"VAULT_KEYNAME" required:"true"`
}
type credentialConfig struct {
IssuerName string `envconfig:"CRED_ISSUER_NAME" required:"true"`
Keyname string `envconfig:"CRED_KEYNAME" required:"true"`
}
type policyConfig struct {
Addr string `envconfig:"POLICY_ADDR" required:"true"`
}
type cacheConfig struct {
Addr string `envconfig:"CACHE_ADDR" required:"true"`
}
package credential
import (
"net/http"
"time"
"github.com/hyperledger/aries-framework-go/pkg/doc/signature/jsonld"
"github.com/hyperledger/aries-framework-go/pkg/doc/signature/suite"
"github.com/hyperledger/aries-framework-go/pkg/doc/signature/suite/ed25519signature2018"
"github.com/hyperledger/aries-framework-go/pkg/doc/util"
"github.com/hyperledger/aries-framework-go/pkg/doc/verifiable"
"github.com/piprate/json-gold/ld"
)
type Signer interface {
Sign(data []byte) ([]byte, error)
}
type Issuer struct {
issuerName string
signer Signer
keyname string
// proofContext is used to generate linked data proof
proofContext *verifiable.LinkedDataProofContext
docLoader *ld.CachingDocumentLoader
}
func NewIssuer(issuerName string, keyname string, signer Signer, httpClient *http.Client) *Issuer {
sigSuite := ed25519signature2018.New(
suite.WithSigner(signer),
suite.WithVerifier(ed25519signature2018.NewPublicKeyVerifier()))
proofContext := &verifiable.LinkedDataProofContext{
Suite: sigSuite,
SignatureType: ed25519signature2018.SignatureType,
SignatureRepresentation: verifiable.SignatureProofValue,
VerificationMethod: keyname,
}
loader := ld.NewDefaultDocumentLoader(httpClient)
return &Issuer{
issuerName: issuerName,
signer: signer,
keyname: keyname,
docLoader: ld.NewCachingDocumentLoader(loader),
proofContext: proofContext,
}
}
func (i *Issuer) NewCredential(contexts []string, subjectID string, subject map[string]interface{}, proof bool) (*verifiable.Credential, error) {
jsonldContexts := []string{"https://www.w3.org/2018/credentials/v1"}
jsonldContexts = append(jsonldContexts, contexts...)
vc := &verifiable.Credential{
Context: jsonldContexts,
Types: []string{verifiable.VCType},
Issuer: verifiable.Issuer{ID: i.issuerName},
Issued: &util.TimeWrapper{Time: time.Now()},
Subject: verifiable.Subject{
ID: subjectID,
CustomFields: subject,
},
}
if proof {
if err := vc.AddLinkedDataProof(i.proofContext, jsonld.WithDocumentLoader(i.docLoader)); err != nil {
return nil, err
}
}
return vc, nil
}
func (i *Issuer) NewPresentation(contexts []string, vc ...*verifiable.Credential) (*verifiable.Presentation, error) {
jsonldContexts := []string{"https://www.w3.org/2018/credentials/v1"}
jsonldContexts = append(jsonldContexts, contexts...)
vp, err := verifiable.NewPresentation(verifiable.WithCredentials(vc...))
if err != nil {
return nil, err
}
vp.Context = jsonldContexts
vp.ID = i.issuerName
vp.Type = []string{verifiable.VPType}
if err := vp.AddLinkedDataProof(i.proofContext, jsonld.WithDocumentLoader(i.docLoader)); err != nil {
return nil, err
}
return vp, nil
}
...@@ -2,21 +2,119 @@ package infohub ...@@ -2,21 +2,119 @@ package infohub
import ( import (
"context" "context"
"fmt" "encoding/json"
"github.com/hyperledger/aries-framework-go/pkg/doc/verifiable"
"go.uber.org/zap" "go.uber.org/zap"
"code.vereign.com/gaiax/tsa/golib/errors"
"code.vereign.com/gaiax/tsa/infohub/gen/infohub" "code.vereign.com/gaiax/tsa/infohub/gen/infohub"
"code.vereign.com/gaiax/tsa/infohub/internal/storage"
) )
var exportAccepted = map[string]interface{}{"result": "accepted"}
type Storage interface {
ExportConfiguration(ctx context.Context, exportName string) (*storage.ExportConfiguration, error)
}
type Policy interface {
Evaluate(ctx context.Context, policy string, data interface{}, evaluationID string) ([]byte, error)
}
type Cache interface {
Get(ctx context.Context, key, namespace, scope string) ([]byte, error)
}
type Credentials interface {
NewCredential(contexts []string, subjectID string, subject map[string]interface{}, proof bool) (*verifiable.Credential, error)
NewPresentation(contexts []string, credentials ...*verifiable.Credential) (*verifiable.Presentation, error)
}
type Service struct { type Service struct {
logger *zap.Logger storage Storage
policy Policy
cache Cache
credentials Credentials
logger *zap.Logger
} }
func New(logger *zap.Logger) *Service { func New(storage Storage, policy Policy, cache Cache, cred Credentials, logger *zap.Logger) *Service {
return &Service{logger: logger} return &Service{
storage: storage,
policy: policy,
cache: cache,
credentials: cred,
logger: logger,
}
} }
func (s *Service) Export(ctx context.Context, req *infohub.ExportRequest) (interface{}, error) { func (s *Service) Export(ctx context.Context, req *infohub.ExportRequest) (interface{}, error) {
return nil, fmt.Errorf("not implemented") logger := s.logger.With(zap.String("exportName", req.ExportName))
exportCfg, err := s.storage.ExportConfiguration(ctx, req.ExportName)
if err != nil {
logger.Error("error getting export configuration", zap.Error(err))
return nil, err
}
// get the results of all policies configured in the export
results := make(map[string][]byte)
for policy := range exportCfg.Policies {
res, err := s.cache.Get(ctx, exportCacheKey(req.ExportName, policy), "", "")
if err != nil {
if errors.Is(errors.NotFound, err) {
if err := s.triggerExport(ctx, exportCfg); err != nil {
logger.Error("error performing export", zap.Error(err))
return nil, err
}
return exportAccepted, nil
}
logger.Error("failed to get policy result from cache", zap.Error(err))
return nil, err
}
results[policy] = res
}
// create separate verifiable credential for each policy result
var creds []*verifiable.Credential
for policy, result := range results {
var res map[string]interface{}
if err := json.Unmarshal(result, &res); err != nil {
logger.Error("error decoding policy result as json", zap.Error(err))
return nil, errors.New("error creating export", err)
}
// credentials do not include proof, because the final VP will include a proof for all
cred, err := s.credentials.NewCredential(exportCfg.Contexts, policy, res, false)
if err != nil {
logger.Error("failed to create verifiable credential", zap.Error(err))
return nil, errors.New("error creating export", err)
}
creds = append(creds, cred)
}
// bundle all credentials in a verifiable presentation with proof
vp, err := s.credentials.NewPresentation(exportCfg.Contexts, creds...)
if err != nil {
logger.Error("failed to create verifiable presentation", zap.Error(err))
return nil, errors.New("error creating export", err)
}
return vp, nil
}
func (s *Service) triggerExport(ctx context.Context, exportCfg *storage.ExportConfiguration) error {
s.logger.Info("export triggered", zap.String("exportName", exportCfg.ExportName))
for policy, input := range exportCfg.Policies {
cacheKey := exportCacheKey(exportCfg.ExportName, policy)
_, err := s.policy.Evaluate(ctx, policy, input, cacheKey)
if err != nil {
return err
}
}
return nil
}
func exportCacheKey(exportName string, policyName string) string {
return exportName + ":" + policyName
} }
package storage
import (
"context"
"strings"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.uber.org/zap"
"code.vereign.com/gaiax/tsa/golib/errors"
)
type ExportConfiguration struct {
ExportName string
Contexts []string
Policies map[string]interface{}
}
type Storage struct {
exportConfig *mongo.Collection
logger *zap.Logger
}
func New(db *mongo.Client, dbname, collection string, logger *zap.Logger) (*Storage, error) {
if err := db.Ping(context.Background(), nil); err != nil {
return nil, err
}
return &Storage{
exportConfig: db.Database(dbname).Collection(collection),
logger: logger,
}, nil
}
func (s *Storage) ExportConfiguration(ctx context.Context, exportName string) (*ExportConfiguration, error) {
result := s.exportConfig.FindOne(ctx, bson.M{
"exportName": exportName,
})
if result.Err() != nil {
if strings.Contains(result.Err().Error(), "no documents in result") {
return nil, errors.New(errors.NotFound, "export configuration not found")
}
return nil, result.Err()
}
var expcfg ExportConfiguration
if err := result.Decode(&expcfg); err != nil {
return nil, err
}
return &expcfg, nil
}
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment