example: add sub-delegation
This commit is contained in:
committed by
Michael Muré
parent
55f38fef4a
commit
c670433335
9
toolkit/_example/Readme.md
Normal file
9
toolkit/_example/Readme.md
Normal file
@@ -0,0 +1,9 @@
|
||||

|
||||

|
||||
|
||||
TODO
|
||||
|
||||
- differences with a real system
|
||||
- issuer protocol + token exchange
|
||||
- opinionated with HTTP
|
||||
- toolkit is helpers, you can change or write your own thing
|
||||
41
toolkit/_example/_protocol-issuer/request-resolver.go
Normal file
41
toolkit/_example/_protocol-issuer/request-resolver.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/did"
|
||||
"github.com/ucan-wg/go-ucan/pkg/command"
|
||||
|
||||
"github.com/INFURA/go-ucan-toolkit/issuer"
|
||||
)
|
||||
|
||||
func RequestResolver(r *http.Request) (*issuer.ResolvedRequest, error) {
|
||||
// Let's make up a simple json protocol
|
||||
req := struct {
|
||||
Audience string `json:"aud"`
|
||||
Cmd string `json:"cmd"`
|
||||
Subject string `json:"sub"`
|
||||
}{}
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aud, err := did.Parse(req.Audience)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd, err := command.Parse(req.Cmd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sub, err := did.Parse(req.Subject)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &issuer.ResolvedRequest{
|
||||
Audience: aud,
|
||||
Cmd: cmd,
|
||||
Subject: sub,
|
||||
}, nil
|
||||
}
|
||||
59
toolkit/_example/_protocol-issuer/requester.go
Normal file
59
toolkit/_example/_protocol-issuer/requester.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"iter"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/did"
|
||||
"github.com/ucan-wg/go-ucan/pkg/command"
|
||||
"github.com/ucan-wg/go-ucan/token/delegation"
|
||||
|
||||
"github.com/INFURA/go-ucan-toolkit/client"
|
||||
"github.com/INFURA/go-ucan-toolkit/issuer"
|
||||
)
|
||||
|
||||
var _ client.DelegationRequester = &Requester{}
|
||||
|
||||
type Requester struct {
|
||||
issuerURL string
|
||||
}
|
||||
|
||||
func NewRequester(issuerURL string) *Requester {
|
||||
return &Requester{issuerURL: issuerURL}
|
||||
}
|
||||
|
||||
func (r Requester) RequestDelegation(ctx context.Context, audience did.DID, cmd command.Command, subject did.DID) (iter.Seq2[*delegation.Bundle, error], error) {
|
||||
log.Printf("requesting delegation for %s on %s", cmd, subject)
|
||||
|
||||
// we match the simple json protocol of the issuer
|
||||
data := struct {
|
||||
Audience string `json:"aud"`
|
||||
Cmd string `json:"cmd"`
|
||||
Subject string `json:"sub"`
|
||||
}{
|
||||
Audience: audience.String(),
|
||||
Cmd: cmd.String(),
|
||||
Subject: subject.String(),
|
||||
}
|
||||
buf := &bytes.Buffer{}
|
||||
err := json.NewEncoder(buf).Encode(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, "http://"+r.issuerURL, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := http.DefaultClient.Do(req.WithContext(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return issuer.DecodeResponse(res)
|
||||
}
|
||||
153
toolkit/_example/alice-client-issuer/alice.go
Normal file
153
toolkit/_example/alice-client-issuer/alice.go
Normal file
@@ -0,0 +1,153 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/libp2p/go-libp2p/core/crypto"
|
||||
"github.com/ucan-wg/go-ucan/did"
|
||||
"github.com/ucan-wg/go-ucan/pkg/command"
|
||||
"github.com/ucan-wg/go-ucan/pkg/container"
|
||||
"github.com/ucan-wg/go-ucan/pkg/policy"
|
||||
"github.com/ucan-wg/go-ucan/pkg/policy/literal"
|
||||
"github.com/ucan-wg/go-ucan/token/delegation"
|
||||
|
||||
example "github.com/INFURA/go-ucan-toolkit/_example"
|
||||
protocol "github.com/INFURA/go-ucan-toolkit/_example/_protocol-issuer"
|
||||
"github.com/INFURA/go-ucan-toolkit/client"
|
||||
"github.com/INFURA/go-ucan-toolkit/issuer"
|
||||
"github.com/INFURA/go-ucan-toolkit/server/bearer"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
// register as handler of the interrupt signal to trigger the teardown
|
||||
quit := make(chan os.Signal, 1)
|
||||
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
|
||||
|
||||
go func() {
|
||||
<-quit
|
||||
cancel()
|
||||
}()
|
||||
|
||||
err := run(ctx,
|
||||
example.AliceIssuerUrl, example.AlicePrivKey, example.AliceDid,
|
||||
example.ServiceIssuerUrl, example.ServiceUrl, example.ServiceDid)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func run(ctx context.Context, ownIssuerUrl string, priv crypto.PrivKey, d did.DID,
|
||||
serviceIssuerUrl string, serviceUrl string, serviceDid did.DID) error {
|
||||
log.Printf("Alice DID is %s", d.String())
|
||||
|
||||
issuingLogic := func(iss did.DID, aud did.DID, cmd command.Command, subject did.DID) (*delegation.Token, error) {
|
||||
log.Printf("issuing delegation to %v for %v to operate on %v", aud, cmd, subject)
|
||||
|
||||
// As another example, we'll force Bob to use a specific HTTP sub-path
|
||||
policies, err := policy.Construct(
|
||||
policy.Equal(".http.path", literal.String(fmt.Sprintf("/%s/%s", iss.String(), aud.String()))),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return delegation.New(iss, aud, cmd, policies, subject)
|
||||
}
|
||||
|
||||
cli, err := client.NewWithIssuer(priv, protocol.NewRequester(serviceIssuerUrl), issuingLogic)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go startIssuerHttp(ctx, ownIssuerUrl, cli)
|
||||
|
||||
for {
|
||||
proofs, err := cli.PrepareInvoke(ctx, command.MustParse("/foo/bar"), serviceDid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = makeRequest(ctx, d, serviceUrl, proofs)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case <-time.After(20 * time.Second):
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func startIssuerHttp(ctx context.Context, issuerUrl string, cli *client.WithIssuer) {
|
||||
handler := issuer.HttpWrapper(cli, protocol.RequestResolver)
|
||||
|
||||
srv := &http.Server{
|
||||
Addr: issuerUrl,
|
||||
Handler: handler,
|
||||
}
|
||||
|
||||
go func() {
|
||||
if err := srv.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
|
||||
log.Fatalf("listen: %s\n", err)
|
||||
}
|
||||
}()
|
||||
|
||||
log.Printf("issuer listening on %s\n", srv.Addr)
|
||||
|
||||
<-ctx.Done()
|
||||
|
||||
if err := srv.Shutdown(ctx); err != nil && !errors.Is(err, context.Canceled) {
|
||||
log.Printf("issuer error: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func makeRequest(ctx context.Context, clientDid did.DID, serviceUrl string, proofs container.Writer) error {
|
||||
// we construct a URL that include the client DID as path, as requested by the UCAN policy we get issued
|
||||
u, err := url.JoinPath("http://"+serviceUrl, clientDid.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, u, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = bearer.AddBearerContainerCompressed(req.Header, proofs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res, err := http.DefaultClient.Do(req.WithContext(ctx))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != http.StatusOK {
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unexpected status code: %d, error reading body: %w", res.StatusCode, err)
|
||||
}
|
||||
return fmt.Errorf("unexpected status code: %d, body: %v", res.StatusCode, string(body))
|
||||
}
|
||||
|
||||
log.Printf("response status code: %d", res.StatusCode)
|
||||
|
||||
return nil
|
||||
}
|
||||
111
toolkit/_example/bob-client/bob.go
Normal file
111
toolkit/_example/bob-client/bob.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/did"
|
||||
"github.com/ucan-wg/go-ucan/pkg/command"
|
||||
"github.com/ucan-wg/go-ucan/pkg/container"
|
||||
|
||||
example "github.com/INFURA/go-ucan-toolkit/_example"
|
||||
protocol "github.com/INFURA/go-ucan-toolkit/_example/_protocol-issuer"
|
||||
"github.com/INFURA/go-ucan-toolkit/client"
|
||||
"github.com/INFURA/go-ucan-toolkit/server/bearer"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
// register as handler of the interrupt signal to trigger the teardown
|
||||
quit := make(chan os.Signal, 1)
|
||||
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
|
||||
|
||||
go func() {
|
||||
<-quit
|
||||
cancel()
|
||||
}()
|
||||
|
||||
err := run(ctx, example.AliceIssuerUrl, example.AliceDid, example.ServiceUrl, example.ServiceDid)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func run(ctx context.Context, aliceUrl string, aliceDid did.DID, serverUrl string, serviceDid did.DID) error {
|
||||
// Let's generate a keypair for our client:
|
||||
priv, d, err := did.GenerateEd25519()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("Bob DID is %s", d.String())
|
||||
|
||||
cli, err := client.NewClient(priv, protocol.NewRequester(aliceUrl))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
proofs, err := cli.PrepareInvoke(ctx, command.MustParse("/foo/bar"), serviceDid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = makeRequest(ctx, d, serverUrl, aliceDid, proofs)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil
|
||||
case <-time.After(5 * time.Second):
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func makeRequest(ctx context.Context, clientDid did.DID, serviceUrl string, aliceDid did.DID, proofs container.Writer) error {
|
||||
// we construct a URL that include the our DID and Alice DID as path, as requested by the UCAN policy we get issued
|
||||
u, err := url.JoinPath("http://"+serviceUrl, aliceDid.String(), clientDid.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, u, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = bearer.AddBearerContainerCompressed(req.Header, proofs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res, err := http.DefaultClient.Do(req.WithContext(ctx))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != http.StatusOK {
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unexpected status code: %d, error reading body: %w", res.StatusCode, err)
|
||||
}
|
||||
return fmt.Errorf("unexpected status code: %d, body: %v", res.StatusCode, string(body))
|
||||
}
|
||||
|
||||
log.Printf("response status code: %d", res.StatusCode)
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"iter"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/ucan-wg/go-ucan/did"
|
||||
"github.com/ucan-wg/go-ucan/pkg/command"
|
||||
"github.com/ucan-wg/go-ucan/pkg/container"
|
||||
"github.com/ucan-wg/go-ucan/token/delegation"
|
||||
|
||||
example "github.com/INFURA/go-ucan-toolkit/_example"
|
||||
"github.com/INFURA/go-ucan-toolkit/client"
|
||||
"github.com/INFURA/go-ucan-toolkit/issuer"
|
||||
"github.com/INFURA/go-ucan-toolkit/server/bearer"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
|
||||
// register as handler of the interrupt signal to trigger the teardown
|
||||
quit := make(chan os.Signal, 1)
|
||||
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM, os.Interrupt)
|
||||
|
||||
go func() {
|
||||
<-quit
|
||||
cancel()
|
||||
}()
|
||||
|
||||
err := run(ctx, example.IssuerUrl, example.ServerUrl, example.ServiceDid)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
var _ client.DelegationRequester = &requester{}
|
||||
|
||||
type requester struct {
|
||||
issuerURL string
|
||||
}
|
||||
|
||||
func (r requester) RequestDelegation(ctx context.Context, audience did.DID, cmd command.Command, subject did.DID) (iter.Seq2[*delegation.Bundle, error], error) {
|
||||
log.Printf("requesting delegation for %s on %s", cmd, subject)
|
||||
|
||||
// we match the simple json protocol of the issuer
|
||||
data := struct {
|
||||
Audience string `json:"aud"`
|
||||
Cmd string `json:"cmd"`
|
||||
Subject string `json:"sub"`
|
||||
}{
|
||||
Audience: audience.String(),
|
||||
Cmd: cmd.String(),
|
||||
Subject: subject.String(),
|
||||
}
|
||||
buf := &bytes.Buffer{}
|
||||
err := json.NewEncoder(buf).Encode(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, "http://"+r.issuerURL, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res, err := http.DefaultClient.Do(req.WithContext(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return issuer.DecodeResponse(res)
|
||||
}
|
||||
|
||||
func run(ctx context.Context, issuerUrl string, serverUrl string, serviceDid did.DID) error {
|
||||
// Let's generate a keypair for our client:
|
||||
priv, d, err := did.GenerateEd25519()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("client DID is %s", d.String())
|
||||
|
||||
cli, err := client.NewClient(priv, requester{issuerURL: issuerUrl})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case <-time.After(5 * time.Second):
|
||||
proofs, err := cli.PrepareInvoke(ctx, command.MustParse("/foo/bar"), serviceDid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = makeRequest(ctx, d, serverUrl, proofs)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func makeRequest(ctx context.Context, clientDid did.DID, serverUrl string, proofs container.Writer) error {
|
||||
// we construct a URL that include the client DID as path, as requested by the UCAN policy we get issued
|
||||
u, err := url.JoinPath("http://"+serverUrl, clientDid.String())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, u, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = bearer.AddBearerContainerCompressed(req.Header, proofs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res, err := http.DefaultClient.Do(req.WithContext(ctx))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != http.StatusOK {
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unexpected status code: %d, error reading body: %w", res.StatusCode, err)
|
||||
}
|
||||
return fmt.Errorf("unexpected status code: %d, body: %v", res.StatusCode, string(body))
|
||||
}
|
||||
|
||||
log.Printf("response status code: %d", res.StatusCode)
|
||||
|
||||
return nil
|
||||
}
|
||||
57
toolkit/_example/diagram.puml
Normal file
57
toolkit/_example/diagram.puml
Normal file
@@ -0,0 +1,57 @@
|
||||
@startuml
|
||||
left to right direction
|
||||
|
||||
rectangle Service as owner {
|
||||
rectangle Issuer as issuer
|
||||
rectangle Executor as exec
|
||||
}
|
||||
|
||||
node resource as res
|
||||
|
||||
owner --> res : Controls
|
||||
exec --> res : Allow access to
|
||||
|
||||
rectangle "Alice" as alice {
|
||||
rectangle Client as aliceclient
|
||||
|
||||
}
|
||||
|
||||
aliceclient --> issuer : [1] request delegation
|
||||
aliceclient <-- issuer : [2] issue delegation A
|
||||
|
||||
aliceclient --> exec : [3] make request with A
|
||||
@enduml
|
||||
|
||||
|
||||
@startuml
|
||||
left to right direction
|
||||
|
||||
rectangle Service as owner {
|
||||
rectangle Issuer as issuer
|
||||
rectangle Executor as exec
|
||||
}
|
||||
|
||||
node resource as res
|
||||
|
||||
owner --> res : Controls
|
||||
exec --> res : Allow access to
|
||||
|
||||
rectangle "Alice" as alice {
|
||||
rectangle Client as aliceclient
|
||||
rectangle Issuer as aliceissuer
|
||||
}
|
||||
|
||||
aliceclient --> issuer : [1] request delegation
|
||||
aliceclient <-- issuer : [2] issue delegation A
|
||||
|
||||
aliceclient --> exec : [3] make request with A
|
||||
|
||||
rectangle "Bob" as bob {
|
||||
rectangle Client as bobclient
|
||||
}
|
||||
|
||||
bobclient --> aliceissuer : [4] request delegation
|
||||
bobclient <-- aliceissuer : [5] issue delegation B\nalso returns A
|
||||
|
||||
bobclient -down-> exec : [6] make request with A+B
|
||||
@enduml
|
||||
BIN
toolkit/_example/scenario1.png
Normal file
BIN
toolkit/_example/scenario1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
BIN
toolkit/_example/scenario2.png
Normal file
BIN
toolkit/_example/scenario2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
@@ -2,7 +2,6 @@ package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
@@ -20,6 +19,7 @@ import (
|
||||
"github.com/ucan-wg/go-ucan/token/delegation"
|
||||
|
||||
example "github.com/INFURA/go-ucan-toolkit/_example"
|
||||
protocol "github.com/INFURA/go-ucan-toolkit/_example/_protocol-issuer"
|
||||
"github.com/INFURA/go-ucan-toolkit/issuer"
|
||||
)
|
||||
|
||||
@@ -35,7 +35,7 @@ func main() {
|
||||
cancel()
|
||||
}()
|
||||
|
||||
err := run(ctx, example.IssuerUrl, example.ServicePrivKey)
|
||||
err := run(ctx, example.ServiceIssuerUrl, example.ServicePrivKey)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
os.Exit(1)
|
||||
@@ -50,7 +50,12 @@ func run(ctx context.Context, issuerUrl string, servicePrivKey crypto.PrivKey) e
|
||||
// Here, we enforce that the caller uses its own DID endpoint (an arbitrary construct for this example).
|
||||
// You will notice that the server doesn't need to know about this logic to enforce it.
|
||||
policies, err := policy.Construct(
|
||||
policy.Equal(".http.path", literal.String(fmt.Sprintf("/%s", aud.String()))),
|
||||
policy.Or(
|
||||
// allow exact path
|
||||
policy.Equal(".http.path", literal.String(fmt.Sprintf("/%s", aud.String()))),
|
||||
// allow sub-path
|
||||
policy.Like(".http.path", fmt.Sprintf("/%s/*", aud.String())),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -67,35 +72,7 @@ func run(ctx context.Context, issuerUrl string, servicePrivKey crypto.PrivKey) e
|
||||
return err
|
||||
}
|
||||
|
||||
handler := issuer.HttpWrapper(rootIssuer, func(r *http.Request) (*issuer.ResolvedRequest, error) {
|
||||
// Let's make up a simple json protocol
|
||||
req := struct {
|
||||
Audience string `json:"aud"`
|
||||
Cmd string `json:"cmd"`
|
||||
Subject string `json:"sub"`
|
||||
}{}
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aud, err := did.Parse(req.Audience)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmd, err := command.Parse(req.Cmd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sub, err := did.Parse(req.Subject)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &issuer.ResolvedRequest{
|
||||
Audience: aud,
|
||||
Cmd: cmd,
|
||||
Subject: sub,
|
||||
}, nil
|
||||
})
|
||||
handler := issuer.HttpWrapper(rootIssuer, protocol.RequestResolver)
|
||||
|
||||
srv := &http.Server{
|
||||
Addr: issuerUrl,
|
||||
@@ -27,14 +27,16 @@ func main() {
|
||||
cancel()
|
||||
}()
|
||||
|
||||
err := run(ctx, example.ServerUrl, example.ServiceDid)
|
||||
err := run(ctx, example.ServiceUrl, example.ServiceDid)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func run(ctx context.Context, serverUrl string, serviceDID did.DID) error {
|
||||
func run(ctx context.Context, serviceUrl string, serviceDID did.DID) error {
|
||||
log.Printf("service DID is %s\n", serviceDID.String())
|
||||
|
||||
// we'll make a simple handling pipeline:
|
||||
// - exectx.ExtractMW to extract and decode the UCAN context, verify the service DID
|
||||
// - exectx.HttpExtArgsVerify to verify the HTTP policies
|
||||
@@ -50,7 +52,8 @@ func run(ctx context.Context, serverUrl string, serviceDID did.DID) error {
|
||||
|
||||
switch ucanCtx.Command().String() {
|
||||
case "/foo/bar":
|
||||
log.Printf("handled command %v for %v", ucanCtx.Command(), ucanCtx.Invocation().Issuer())
|
||||
log.Printf("handled command %v at %v for %v", ucanCtx.Command(), r.URL.Path, ucanCtx.Invocation().Issuer())
|
||||
log.Printf("proof is %v", ucanCtx.Invocation().Proof())
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("OK"))
|
||||
default:
|
||||
@@ -64,7 +67,7 @@ func run(ctx context.Context, serverUrl string, serviceDID did.DID) error {
|
||||
handler = exectx.ExtractMW(handler, serviceDID)
|
||||
|
||||
srv := &http.Server{
|
||||
Addr: serverUrl,
|
||||
Addr: serviceUrl,
|
||||
Handler: handler,
|
||||
}
|
||||
|
||||
@@ -9,16 +9,27 @@ import (
|
||||
|
||||
// Endpoints
|
||||
|
||||
var ServerUrl = ":8080"
|
||||
var IssuerUrl = ":8081"
|
||||
var ServiceUrl = ":8080"
|
||||
var ServiceIssuerUrl = ":8081"
|
||||
|
||||
var AliceIssuerUrl = ":8082"
|
||||
|
||||
// Service
|
||||
|
||||
var ServicePrivKey crypto.PrivKey
|
||||
var ServiceDid did.DID
|
||||
|
||||
// Alice
|
||||
|
||||
var AlicePrivKey crypto.PrivKey
|
||||
var AliceDid did.DID
|
||||
|
||||
func init() {
|
||||
privRaw, _ := base64.StdEncoding.DecodeString("CAESQGs7hPBRBmxH1UmHrdcPrBkecuFUuCWHK0kMJvZYCBqIa35SGxUdXVGuigQDkMpf7xO4C2C2Acl8QTtSrYS7Cnc=")
|
||||
ServicePrivKey, _ = crypto.UnmarshalPrivateKey(privRaw)
|
||||
servPrivRaw, _ := base64.StdEncoding.DecodeString("CAESQGs7hPBRBmxH1UmHrdcPrBkecuFUuCWHK0kMJvZYCBqIa35SGxUdXVGuigQDkMpf7xO4C2C2Acl8QTtSrYS7Cnc=")
|
||||
ServicePrivKey, _ = crypto.UnmarshalPrivateKey(servPrivRaw)
|
||||
ServiceDid, _ = did.FromPrivKey(ServicePrivKey)
|
||||
|
||||
alicePrivRaw, _ := base64.StdEncoding.DecodeString("CAESQFESA31nDYUhXXwbCNSFvg7M+TOFgyxy0tVX6o+TkJAKqAwDvtGxZeGyUjibGd/op+xOLvzE6BrTIOw62K3yLp8=")
|
||||
AlicePrivKey, _ = crypto.UnmarshalPrivateKey(alicePrivRaw)
|
||||
AliceDid, _ = did.FromPrivKey(AlicePrivKey)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user