Skip to content

Commit

Permalink
DIF Resolver
Browse files Browse the repository at this point in the history
The application now provides a `resolver` command that can be used
to deploy a DIF Universal Resolver driver for the `did:algo` method.

More information:
https://github.com/decentralized-identity/universal-resolver
  • Loading branch information
bcessa committed Jan 12, 2023
1 parent cedb15f commit 4cd378a
Show file tree
Hide file tree
Showing 43 changed files with 1,026 additions and 545 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,9 @@ jobs:
# Setup buf
- name: Setup buf
id: buf-setup
uses: bufbuild/buf-setup-action@v1.9.0
uses: bufbuild/buf-setup-action@v1.11.0
with:
version: 1.9.0
version: 1.11.0
github_token: ${{ github.token }}

# Static analysis
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/maintenance.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ jobs:
name: "close stale issues and pull requests"
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v6
- uses: actions/stale@v7
with:
# On the 'debug' mode the action will not perform any operation.
# Add the secret ACTIONS_STEP_DEBUG with a value of 'true' in the repository.
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ jobs:
# Use goreleaser to create the new release
# https://github.com/goreleaser/goreleaser-action
- name: Create release
uses: goreleaser/goreleaser-action@v3
uses: goreleaser/goreleaser-action@v4
if: startsWith(github.ref, 'refs/tags/')
with:
version: latest
Expand Down
2 changes: 2 additions & 0 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ archives:
- src: '*.md'
dst: docs
strip_parent: true
rlcp: true
# generate integrity checksums
# https://goreleaser.com/customization/checksum/
checksum:
Expand All @@ -74,6 +75,7 @@ checksum:
# https://goreleaser.com/customization/source/
source:
enabled: true
rlcp: true
# produce test releases
# https://goreleaser.com/customization/snapshots/
snapshot:
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pkg?="..."
# locally (on a dev container) or using a builder image.
buf:=buf
ifndef REMOTE_CONTAINERS_SOCKETS
buf=docker run --platform linux/amd64 --rm -it -v $(shell pwd):/workdir ghcr.io/bryk-io/buf-builder:1.9.0 buf
buf=docker run --platform linux/amd64 --rm -it -v $(shell pwd):/workdir ghcr.io/bryk-io/buf-builder:1.11.0 buf
endif

help:
Expand Down
19 changes: 5 additions & 14 deletions agent/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ import (

"github.com/algorand/go-algorand-sdk/client/v2/algod"
"github.com/algorand/go-algorand-sdk/client/v2/indexer"
"github.com/algorandfoundation/did-algo/info"
protoV1 "github.com/algorandfoundation/did-algo/proto/did/v1"
"go.bryk.io/pkg/did"
"go.bryk.io/pkg/did/resolver"
"go.bryk.io/pkg/log"
"go.bryk.io/pkg/net/rpc"
"go.bryk.io/pkg/otel"
Expand Down Expand Up @@ -83,14 +83,14 @@ func (h *Handler) Retrieve(req *protoV1.QueryRequest) (*did.Identifier, *did.Pro
// Verify method is supported
if !h.isSupported(req.Method) {
h.oop.WithFields(logFields).Warning("non supported method")
return nil, nil, errors.New("non supported method")
return nil, nil, errors.New(resolver.ErrMethodNotSupported)
}

// Retrieve document from storage
id, proof, err := h.store.Get(req)
if err != nil {
h.oop.WithFields(logFields).Warning(err.Error())
return nil, nil, err
return nil, nil, errors.New(resolver.ErrNotFound)
}
return id, proof, nil
}
Expand Down Expand Up @@ -145,9 +145,7 @@ func (h *Handler) Process(req *protoV1.ProcessRequest) (string, error) {
}

// AccountInformation returns details about the crypto account specified.
func (h *Handler) AccountInformation(
ctx context.Context,
req *protoV1.AccountInformationRequest) (*protoV1.AccountInformationResponse, error) {
func (h *Handler) AccountInformation(ctx context.Context, req *protoV1.AccountInformationRequest) (*protoV1.AccountInformationResponse, error) { // nolint: lll
ai, err := h.algoNode.AccountInformation(req.Address).Do(ctx)
if err != nil {
h.oop.WithFields(log.Fields{
Expand Down Expand Up @@ -183,9 +181,7 @@ func (h *Handler) AccountInformation(

// AccountActivity opens a channel to monitor near real-time account activity.
// The channel must be closed using the provided context when no longer needed.
func (h *Handler) AccountActivity(
ctx context.Context,
req *protoV1.AccountActivityRequest) (<-chan *protoV1.AccountActivityResponse, error) {
func (h *Handler) AccountActivity(ctx context.Context, req *protoV1.AccountActivityRequest) (<-chan *protoV1.AccountActivityResponse, error) { // nolint: lll
check := time.NewTicker(time.Duration(5) * time.Second)
sink := make(chan *protoV1.AccountActivityResponse)
go func() {
Expand Down Expand Up @@ -296,11 +292,6 @@ func (h *Handler) QueryResponseFilter() rpc.GatewayInterceptor {
}

// Return result
res.Header().Set("content-type", "application/json")
res.Header().Set("x-content-type-options", "nosniff")
res.Header().Set("x-algoid-build-code", info.BuildCode)
res.Header().Set("x-algoid-build-timestamp", info.BuildTimestamp)
res.Header().Set("x-algoid-version", info.CoreVersion)
res.WriteHeader(status)
_, _ = res.Write(response)
return errors.New("prevent further processing")
Expand Down
23 changes: 6 additions & 17 deletions client/cli/cmd/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package cmd
import (
"errors"
"fmt"
"net/http"
"os"
"syscall"
"time"
Expand Down Expand Up @@ -281,7 +280,12 @@ func getAgentGateway(handler *agent.Handler) (*rpc.Gateway, error) {
gwOpts := []rpc.GatewayOption{
rpc.WithClientOptions(gwCl...),
rpc.WithInterceptor(handler.QueryResponseFilter()),
rpc.WithGatewayMiddleware(customGatewayHeaders()),
rpc.WithGatewayMiddleware(mw.Headers(map[string]string{
"x-agent-version": info.CoreVersion,
"x-agent-build-code": info.BuildCode,
"x-agent-release": releaseCode(),
"x-content-type-options": "nosniff",
})),
}
if viper.GetBool("agent.proxy_protocol") {
log.Debug("enable PROXY protocol support")
Expand All @@ -294,21 +298,6 @@ func getAgentGateway(handler *agent.Handler) (*rpc.Gateway, error) {
return gw, nil
}

// Add version details as custom headers to all responses from the
// agent's HTTP gateway.
func customGatewayHeaders() func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("x-agent-version", info.CoreVersion)
w.Header().Add("x-agent-build-code", info.BuildCode)
w.Header().Add("x-agent-release", releaseCode())
w.Header().Add("x-content-type-options", "nosniff")
next.ServeHTTP(w, r)
}
return http.HandlerFunc(fn)
}
}

// Return the proper storage handler instance based on the connection
// details provided.
func getStorage(info *storageSettings) (agent.Storage, error) {
Expand Down
145 changes: 145 additions & 0 deletions client/cli/cmd/resolver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package cmd

import (
"context"
"net/http"
"os"
"syscall"
"time"

"github.com/algorandfoundation/did-algo/client/internal"
"github.com/algorandfoundation/did-algo/info"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"go.bryk.io/pkg/cli"
pkgHttp "go.bryk.io/pkg/net/http"
"go.bryk.io/pkg/otel"
)

var resolverCmd = &cobra.Command{
Use: "resolver",
Short: "Start a standalone resolver server",
RunE: runResolverServer,
Long: `Resolver server
Resolver server provides a DIF compatible endpoint for DID
document resolution. The server endpoint can be used as a
standalone Universal Resolver Driver.
More information:
https://github.com/decentralized-identity/universal-resolver`,
}

func init() {
params := []cli.Param{
{
Name: "port",
Usage: "TCP port to use for the server",
FlagKey: "resolver.port",
ByDefault: 9091,
Short: "p",
},
{
Name: "proxy-protocol",
Usage: "enable support for PROXY protocol",
FlagKey: "resolver.proxy_protocol",
ByDefault: false,
Short: "P",
},
{
Name: "tls",
Usage: "enable secure communications using TLS with provided credentials",
FlagKey: "resolver.tls.enabled",
ByDefault: false,
},
{
Name: "tls-ca",
Usage: "TLS custom certificate authority (path to PEM file)",
FlagKey: "resolver.tls.ca",
ByDefault: "",
},
{
Name: "tls-cert",
Usage: "TLS certificate (path to PEM file)",
FlagKey: "resolver.tls.cert",
ByDefault: "/etc/algoid/tls/tls.crt",
},
{
Name: "tls-key",
Usage: "TLS private key (path to PEM file)",
FlagKey: "resolver.tls.key",
ByDefault: "/etc/algoid/tls/tls.key",
},
{
Name: "agent",
Usage: "Network agent to communicate with",
FlagKey: "resolver.client.node",
ByDefault: internal.DefaultAgentEndpoint,
Short: "a",
},
{
Name: "agent-insecure",
Usage: "Use an insecure connection to the network agent",
FlagKey: "resolver.client.insecure",
ByDefault: false,
},
}
if err := cli.SetupCommandParams(resolverCmd, params, viper.GetViper()); err != nil {
panic(err)
}
rootCmd.AddCommand(resolverCmd)
}

func runResolverServer(cmd *cobra.Command, args []string) error {
// Observability operator
oop, err := otel.NewOperator([]otel.OperatorOption{
otel.WithLogger(log),
otel.WithServiceName("algoid"),
otel.WithServiceVersion(info.CoreVersion),
otel.WithHostMetrics(),
otel.WithRuntimeMetrics(5 * time.Second),
}...)
if err != nil {
return err
}
defer oop.Shutdown(context.Background())

// Load settings
conf := new(internal.ResolverSettings)
conf.Load(viper.GetViper())

// Resolver instance
conn, err := getClientConnection(conf.Client)
if err != nil {
return err
}
rr, err := conf.Resolver(conn)
if err != nil {
return err
}

// Start server
mux := http.NewServeMux()
mux.HandleFunc("/1.0/identifiers/", rr.ResolutionHandler)
srv, err := pkgHttp.NewServer(conf.ServerOpts(mux, releaseCode())...)
if err != nil {
return err
}
go func() {
_ = srv.Start()
}()

// wait for system signals
log.Info("waiting for incoming requests")
<-cli.SignalsHandler([]os.Signal{
syscall.SIGHUP,
syscall.SIGINT,
syscall.SIGTERM,
syscall.SIGQUIT,
})

// stop server
log.Info("preparing to exit")
_ = conn.Close() // client internal API client connection
return srv.Stop(true)
}
60 changes: 8 additions & 52 deletions client/cli/cmd/retrieve.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@ import (
"errors"
"fmt"

"github.com/algorandfoundation/did-algo/client/store"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"go.bryk.io/pkg/cli"
"go.bryk.io/pkg/did"
)

Expand All @@ -21,18 +18,6 @@ var retrieveCmd = &cobra.Command{
}

func init() {
params := []cli.Param{
{
Name: "validate",
Usage: "validate cryptographic proof received",
FlagKey: "resolve.validate",
ByDefault: false,
Short: "v",
},
}
if err := cli.SetupCommandParams(retrieveCmd, params, viper.GetViper()); err != nil {
panic(err)
}
rootCmd.AddCommand(retrieveCmd)
}

Expand All @@ -52,45 +37,16 @@ func runRetrieveCmd(_ *cobra.Command, args []string) error {
log.Info("retrieving record")
response, err := resolve(args[0])
if err != nil {
return fmt.Errorf("failed to resolve DID: %w", err)
}

// If no validation is required/supported, print response as-is
if !viper.GetBool("resolve.validate") {
log.Warning("skipping validation")
fmt.Printf("%s\n", response)
return nil
return err
}

// Assume a local record structure
ir := new(store.IdentifierRecord)
err = json.Unmarshal(response, ir)
if err != nil || ir.Document == nil || ir.Proof == nil {
log.Warning("validation is not supported")
} else {
// Restore ID instance
id, err := did.FromDocument(ir.Document)
if err != nil {
return err
}

// Verify proof
key := id.VerificationMethod(ir.Proof.VerificationMethod)
if key == nil {
return errors.New("verification method not present in DID document")
}
data, err := id.Document(true).NormalizedLD()
if err != nil {
return err
}
if key.VerifyProof(data, ir.Proof) {
log.Info("DID document is valid")
} else {
log.Warning("DID document is invalid")
}
// Pretty-print retrieved document
log.Warning("skipping validation")
doc := new(did.Document)
if err = json.Unmarshal(response, doc); err != nil {
return err
}

// Print response as output
fmt.Printf("%s\n", response)
output, _ := json.MarshalIndent(doc, "", " ")
fmt.Printf("%s\n", output)
return nil
}

0 comments on commit 4cd378a

Please sign in to comment.