chore: remove client; this is a server-only project
This commit is contained in:
parent
cd6641e377
commit
0f4a210c04
1 changed files with 0 additions and 615 deletions
|
|
@ -1,615 +0,0 @@
|
|||
// Command client is a minimal trust relay client: it generates keys, publishes
|
||||
// signed objects, queries the relay, and performs the auth handshake. It is the
|
||||
// "how a client uses the protocol" reference; no network logic lives in the
|
||||
// codec packages.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.n1ko.dev/Niko/niko_trust/internal/address"
|
||||
"git.n1ko.dev/Niko/niko_trust/internal/identity/signer"
|
||||
"git.n1ko.dev/Niko/niko_trust/internal/protocol"
|
||||
"git.n1ko.dev/Niko/niko_trust/internal/tce"
|
||||
"git.n1ko.dev/Niko/niko_trust/internal/transport"
|
||||
"git.n1ko.dev/Niko/niko_trust/internal/verify"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 {
|
||||
log.Fatal("usage: client <keygen|publish-claim|get|claims|auth> [flags]")
|
||||
}
|
||||
switch os.Args[1] {
|
||||
case "keygen":
|
||||
cmdKeygen(os.Args[2:])
|
||||
case "publish-claim":
|
||||
cmdPublishClaim(os.Args[2:])
|
||||
case "publish-request":
|
||||
cmdPublishRequest(os.Args[2:])
|
||||
case "respond":
|
||||
cmdRespond(os.Args[2:])
|
||||
case "revoke":
|
||||
cmdRevoke(os.Args[2:])
|
||||
case "get":
|
||||
cmdGet(os.Args[2:])
|
||||
case "claims":
|
||||
cmdClaims(os.Args[2:])
|
||||
case "requests":
|
||||
cmdRequests(os.Args[2:])
|
||||
case "responses":
|
||||
cmdResponses(os.Args[2:])
|
||||
case "revocations":
|
||||
cmdRevocations(os.Args[2:])
|
||||
case "auth":
|
||||
cmdAuth(os.Args[2:])
|
||||
case "verify":
|
||||
cmdVerify(os.Args[2:])
|
||||
default:
|
||||
log.Fatalf("unknown command %q", os.Args[1])
|
||||
}
|
||||
}
|
||||
|
||||
func loadSigner(path string) *signer.Signer {
|
||||
raw, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
log.Fatalf("read seed: %v", err)
|
||||
}
|
||||
seed, err := hex.DecodeString(strings.TrimSpace(string(raw)))
|
||||
if err != nil {
|
||||
log.Fatalf("bad seed: %v", err)
|
||||
}
|
||||
s, err := signer.FromSeed(seed)
|
||||
if err != nil {
|
||||
log.Fatalf("signer: %v", err)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func cmdKeygen(args []string) {
|
||||
fs := flag.NewFlagSet("keygen", flag.ExitOnError)
|
||||
out := fs.String("out", "", "write hex seed to this file (default stdout)")
|
||||
fs.Parse(args)
|
||||
|
||||
s, err := signer.Generate()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if *out != "" {
|
||||
if err := os.WriteFile(*out, []byte(hex.EncodeToString(s.Seed())+"\n"), 0o600); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Printf("address %s\nseed written to %s\n", s.Address().String(), *out)
|
||||
return
|
||||
}
|
||||
fmt.Printf("address %s\nseed %s\n", s.Address().String(), hex.EncodeToString(s.Seed()))
|
||||
}
|
||||
|
||||
func cmdPublishClaim(args []string) {
|
||||
fs := flag.NewFlagSet("publish-claim", flag.ExitOnError)
|
||||
seedPath := fs.String("seed", "", "issuer seed file")
|
||||
subject := fs.String("subject", "", "subject trust address")
|
||||
key := fs.String("key", "claim", "claim key (predicate)")
|
||||
valType := fs.String("type", "bool", "bool|string|number")
|
||||
val := fs.String("val", "true", "claim value")
|
||||
url := fs.String("url", "http://localhost:8080", "relay URL")
|
||||
fs.Parse(args)
|
||||
|
||||
s := loadSigner(*seedPath)
|
||||
sub, err := address.Parse(*subject)
|
||||
if err != nil {
|
||||
log.Fatalf("subject: %v", err)
|
||||
}
|
||||
subPub := sub.PubKey()
|
||||
var v tce.Value
|
||||
switch *valType {
|
||||
case "bool":
|
||||
v = tce.Bool(*val == "true")
|
||||
case "string":
|
||||
v = tce.String(*val)
|
||||
case "number":
|
||||
v = tce.Number(*val)
|
||||
default:
|
||||
log.Fatalf("unknown value type %q", *valType)
|
||||
}
|
||||
|
||||
c := &protocol.Claim{
|
||||
Issuer: s.Public(),
|
||||
Subject: subPub,
|
||||
Claims: map[string]tce.Value{*key: v},
|
||||
CreatedAt: uint64(time.Now().Unix()),
|
||||
ExpiresAt: uint64(time.Now().Add(24 * time.Hour).Unix()),
|
||||
Serial: 1,
|
||||
Nonce: randBytes(tce.NonceSize),
|
||||
}
|
||||
tceBytes, err := protocol.EncodeClaim(c)
|
||||
if err != nil {
|
||||
log.Fatalf("encode: %v", err)
|
||||
}
|
||||
sig := s.Sign(tceBytes)
|
||||
env := &transport.Envelope{TCE: tceBytes, Signature: sig}
|
||||
|
||||
postJSON(*url+"/v1/objects", env)
|
||||
}
|
||||
|
||||
func cmdGet(args []string) {
|
||||
fs := flag.NewFlagSet("get", flag.ExitOnError)
|
||||
id := fs.String("id", "", "object id")
|
||||
url := fs.String("url", "http://localhost:8080", "relay URL")
|
||||
fs.Parse(args)
|
||||
getJSON(*url + "/v1/objects/" + *id)
|
||||
}
|
||||
|
||||
func cmdClaims(args []string) {
|
||||
fs := flag.NewFlagSet("claims", flag.ExitOnError)
|
||||
subject := fs.String("subject", "", "subject trust address")
|
||||
token := fs.String("token", "", "session token (from auth)")
|
||||
url := fs.String("url", "http://localhost:8080", "relay URL")
|
||||
fs.Parse(args)
|
||||
|
||||
u := *url + "/v1/claims?subject=" + *subject
|
||||
req, err := http.NewRequest(http.MethodGet, u, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if *token != "" {
|
||||
req.Header.Set("Authorization", "Bearer "+*token)
|
||||
}
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
fmt.Println(resp.Status)
|
||||
fmt.Println(string(body))
|
||||
}
|
||||
|
||||
func cmdAuth(args []string) {
|
||||
fs := flag.NewFlagSet("auth", flag.ExitOnError)
|
||||
seedPath := fs.String("seed", "", "client seed file")
|
||||
scope := fs.String("scope", "read:claims", "requested scope")
|
||||
audience := fs.String("audience", "", "server audience (default: fetched from /v1/config)")
|
||||
url := fs.String("url", "http://localhost:8080", "relay URL")
|
||||
fs.Parse(args)
|
||||
|
||||
s := loadSigner(*seedPath)
|
||||
|
||||
// Resolve the audience the server expects assertions to be bound to.
|
||||
aud := *audience
|
||||
if aud == "" {
|
||||
resp, err := http.Get(*url + "/v1/config")
|
||||
if err != nil {
|
||||
log.Fatalf("config: %v", err)
|
||||
}
|
||||
cfg := struct{ Audience string `json:"audience"` }{}
|
||||
if resp.StatusCode >= 300 {
|
||||
log.Fatalf("config: %s", resp.Status)
|
||||
}
|
||||
_ = json.NewDecoder(resp.Body).Decode(&cfg)
|
||||
resp.Body.Close()
|
||||
aud = cfg.Audience
|
||||
}
|
||||
if aud == "" {
|
||||
log.Fatal("could not determine server audience")
|
||||
}
|
||||
|
||||
// 1. Obtain a challenge from the server.
|
||||
chRes := struct{ Challenge string `json:"challenge"` }{}
|
||||
postJSONDecode(*url+"/v1/auth/challenge", nil, &chRes)
|
||||
ch, err := hex.DecodeString(chRes.Challenge)
|
||||
if err != nil {
|
||||
log.Fatalf("challenge: %v", err)
|
||||
}
|
||||
|
||||
// 2. Build and sign an AuthAssertion bound to the server's audience.
|
||||
a := &protocol.AuthAssertion{
|
||||
PubKey: s.Public(),
|
||||
Challenge: ch,
|
||||
Scope: *scope,
|
||||
Audience: aud,
|
||||
CreatedAt: uint64(time.Now().Unix()),
|
||||
}
|
||||
b, err := protocol.EncodeAuthAssertion(a)
|
||||
if err != nil {
|
||||
log.Fatalf("encode: %v", err)
|
||||
}
|
||||
sig := s.Sign(b)
|
||||
env := &transport.Envelope{TCE: b, Signature: sig}
|
||||
|
||||
// 3. Present it; the server verifies and returns a session token.
|
||||
out := struct {
|
||||
SessionToken string `json:"session_token"`
|
||||
Identity string `json:"identity"`
|
||||
Scope string `json:"scope"`
|
||||
}{}
|
||||
postJSONDecode(*url+"/v1/auth/assert", env, &out)
|
||||
fmt.Printf("session %s\nidentity %s\nscope %s\n", out.SessionToken, out.Identity, out.Scope)
|
||||
}
|
||||
|
||||
func randBytes(n int) []byte {
|
||||
b := make([]byte, n)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func postJSON(u string, v any) {
|
||||
var buf bytes.Buffer
|
||||
if err := json.NewEncoder(&buf).Encode(v); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
resp, err := http.Post(u, "application/json", &buf)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
fmt.Println(resp.Status)
|
||||
fmt.Println(string(body))
|
||||
}
|
||||
|
||||
func postJSONDecode(u string, v any, out any) {
|
||||
var buf bytes.Buffer
|
||||
if v != nil {
|
||||
if err := json.NewEncoder(&buf).Encode(v); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
resp, err := http.Post(u, "application/json", &buf)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
if resp.StatusCode >= 300 {
|
||||
log.Fatalf("%s: %s", resp.Status, string(body))
|
||||
}
|
||||
if out != nil {
|
||||
if err := json.Unmarshal(body, out); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getJSON(u string) {
|
||||
resp, err := http.Get(u)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
fmt.Println(resp.Status)
|
||||
fmt.Println(string(body))
|
||||
}
|
||||
|
||||
func getAuthJSON(u, token string) {
|
||||
req, err := http.NewRequest(http.MethodGet, u, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if token != "" {
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
}
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
fmt.Println(resp.Status)
|
||||
fmt.Println(string(body))
|
||||
}
|
||||
|
||||
func mustID(s string) tce.ID {
|
||||
id, err := tce.ParseID(s)
|
||||
if err != nil {
|
||||
log.Fatalf("bad id %q: %v", s, err)
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
func cmdPublishRequest(args []string) {
|
||||
fs := flag.NewFlagSet("publish-request", flag.ExitOnError)
|
||||
seedPath := fs.String("seed", "", "sender seed file")
|
||||
recipient := fs.String("recipient", "", "recipient trust address")
|
||||
action := fs.String("action", "", "opaque action the recipient approves")
|
||||
message := fs.String("message", "", "human-readable message")
|
||||
url := fs.String("url", "http://localhost:8080", "relay URL")
|
||||
fs.Parse(args)
|
||||
|
||||
s := loadSigner(*seedPath)
|
||||
rec, err := address.Parse(*recipient)
|
||||
if err != nil {
|
||||
log.Fatalf("recipient: %v", err)
|
||||
}
|
||||
now := uint64(time.Now().Unix())
|
||||
req := &protocol.ApprovalRequest{
|
||||
Sender: s.Public(),
|
||||
Recipient: rec.PubKey(),
|
||||
Action: *action,
|
||||
Message: *message,
|
||||
CreatedAt: now,
|
||||
ExpiresAt: now + 30,
|
||||
Nonce: randBytes(tce.NonceSize),
|
||||
}
|
||||
tceBytes, err := protocol.EncodeApprovalRequest(req)
|
||||
if err != nil {
|
||||
log.Fatalf("encode: %v", err)
|
||||
}
|
||||
sig := s.Sign(tceBytes)
|
||||
postJSON(*url+"/v1/objects", &transport.Envelope{TCE: tceBytes, Signature: sig})
|
||||
}
|
||||
|
||||
func cmdRespond(args []string) {
|
||||
fs := flag.NewFlagSet("respond", flag.ExitOnError)
|
||||
seedPath := fs.String("seed", "", "responder seed file")
|
||||
request := fs.String("request", "", "request object id")
|
||||
decision := fs.String("decision", "allow", "allow|deny")
|
||||
url := fs.String("url", "http://localhost:8080", "relay URL")
|
||||
fs.Parse(args)
|
||||
|
||||
s := loadSigner(*seedPath)
|
||||
|
||||
// Fetch the request to confirm we are its recipient.
|
||||
env := struct {
|
||||
TCE string `json:"tce"`
|
||||
}{}
|
||||
greq, err := http.Get(*url + "/v1/objects/" + *request)
|
||||
if err != nil {
|
||||
log.Fatalf("fetch request: %v", err)
|
||||
}
|
||||
body, _ := io.ReadAll(greq.Body)
|
||||
greq.Body.Close()
|
||||
if greq.StatusCode >= 300 {
|
||||
log.Fatalf("fetch request: %s", greq.Status)
|
||||
}
|
||||
if err := json.Unmarshal(body, &env); err != nil {
|
||||
log.Fatalf("request json: %v", err)
|
||||
}
|
||||
reqBytes, err := base64.StdEncoding.DecodeString(env.TCE)
|
||||
if err != nil {
|
||||
log.Fatalf("request tce: %v", err)
|
||||
}
|
||||
req, err := protocol.DecodeApprovalRequest(reqBytes)
|
||||
if err != nil {
|
||||
log.Fatalf("decode request: %v", err)
|
||||
}
|
||||
if transport.AddrOf(req.Recipient) != s.Address().String() {
|
||||
log.Fatalf("this key is not the recipient of the request")
|
||||
}
|
||||
var dec protocol.Decision
|
||||
switch *decision {
|
||||
case "allow":
|
||||
dec = protocol.Allow
|
||||
case "deny":
|
||||
dec = protocol.Deny
|
||||
default:
|
||||
log.Fatalf("unknown decision %q", *decision)
|
||||
}
|
||||
resp := &protocol.ApprovalResponse{
|
||||
RequestHash: mustID(*request),
|
||||
Responder: s.Public(),
|
||||
Decision: dec,
|
||||
CreatedAt: uint64(time.Now().Unix()),
|
||||
Nonce: randBytes(tce.NonceSize),
|
||||
}
|
||||
tceBytes, err := protocol.EncodeApprovalResponse(resp)
|
||||
if err != nil {
|
||||
log.Fatalf("encode: %v", err)
|
||||
}
|
||||
sig := s.Sign(tceBytes)
|
||||
postJSON(*url+"/v1/objects", &transport.Envelope{TCE: tceBytes, Signature: sig})
|
||||
}
|
||||
|
||||
func cmdRevoke(args []string) {
|
||||
fs := flag.NewFlagSet("revoke", flag.ExitOnError)
|
||||
seedPath := fs.String("seed", "", "issuer seed file")
|
||||
claim := fs.String("claim", "", "claim object id to withdraw")
|
||||
reason := fs.String("reason", "", "revocation reason")
|
||||
url := fs.String("url", "http://localhost:8080", "relay URL")
|
||||
fs.Parse(args)
|
||||
|
||||
s := loadSigner(*seedPath)
|
||||
rv := &protocol.Revocation{
|
||||
Issuer: s.Public(),
|
||||
ClaimID: mustID(*claim),
|
||||
Reason: *reason,
|
||||
CreatedAt: uint64(time.Now().Unix()),
|
||||
Nonce: randBytes(tce.NonceSize),
|
||||
}
|
||||
tceBytes, err := protocol.EncodeRevocation(rv)
|
||||
if err != nil {
|
||||
log.Fatalf("encode: %v", err)
|
||||
}
|
||||
sig := s.Sign(tceBytes)
|
||||
postJSON(*url+"/v1/objects", &transport.Envelope{TCE: tceBytes, Signature: sig})
|
||||
}
|
||||
|
||||
func cmdRequests(args []string) {
|
||||
fs := flag.NewFlagSet("requests", flag.ExitOnError)
|
||||
recipient := fs.String("recipient", "", "recipient trust address")
|
||||
token := fs.String("token", "", "session token (from auth, scope read:requests)")
|
||||
url := fs.String("url", "http://localhost:8080", "relay URL")
|
||||
fs.Parse(args)
|
||||
getAuthJSON(*url+"/v1/requests?recipient="+*recipient, *token)
|
||||
}
|
||||
|
||||
func cmdResponses(args []string) {
|
||||
fs := flag.NewFlagSet("responses", flag.ExitOnError)
|
||||
request := fs.String("request", "", "request object id")
|
||||
token := fs.String("token", "", "session token (from auth, scope read:responses)")
|
||||
url := fs.String("url", "http://localhost:8080", "relay URL")
|
||||
fs.Parse(args)
|
||||
getAuthJSON(*url+"/v1/responses?request="+*request, *token)
|
||||
}
|
||||
|
||||
func cmdRevocations(args []string) {
|
||||
fs := flag.NewFlagSet("revocations", flag.ExitOnError)
|
||||
claim := fs.String("claim", "", "claim object id")
|
||||
token := fs.String("token", "", "session token (from auth, scope read:revocations)")
|
||||
url := fs.String("url", "http://localhost:8080", "relay URL")
|
||||
fs.Parse(args)
|
||||
getAuthJSON(*url+"/v1/revocations?claim="+*claim, *token)
|
||||
}
|
||||
|
||||
// rawEnvelope decodes a relay envelope JSON object into a transport.Envelope.
|
||||
func rawEnvelope(r json.RawMessage) *transport.Envelope {
|
||||
var e struct {
|
||||
TCE string `json:"tce"`
|
||||
Signature string `json:"signature"`
|
||||
ObjectID string `json:"object_id"`
|
||||
}
|
||||
_ = json.Unmarshal(r, &e)
|
||||
tceB, err1 := base64.StdEncoding.DecodeString(e.TCE)
|
||||
sigB, err2 := base64.StdEncoding.DecodeString(e.Signature)
|
||||
if err1 != nil || err2 != nil {
|
||||
return nil
|
||||
}
|
||||
return &transport.Envelope{TCE: tceB, Signature: sigB, ObjectID: e.ObjectID}
|
||||
}
|
||||
|
||||
// fetchEnvelopes GETs a relay list endpoint and returns its envelopes.
|
||||
func fetchEnvelopes(u, token string) []*transport.Envelope {
|
||||
req, err := http.NewRequest(http.MethodGet, u, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if token != "" {
|
||||
req.Header.Set("Authorization", "Bearer "+token)
|
||||
}
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
if resp.StatusCode >= 300 {
|
||||
log.Fatalf("%s: %s", resp.Status, string(body))
|
||||
}
|
||||
var wrap struct {
|
||||
Claims []json.RawMessage `json:"claims"`
|
||||
Requests []json.RawMessage `json:"requests"`
|
||||
Responses []json.RawMessage `json:"responses"`
|
||||
Revocations []json.RawMessage `json:"revocations"`
|
||||
}
|
||||
if err := json.Unmarshal(body, &wrap); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
out := make([]*transport.Envelope, 0)
|
||||
for _, r := range wrap.Claims {
|
||||
if e := rawEnvelope(r); e != nil {
|
||||
out = append(out, e)
|
||||
}
|
||||
}
|
||||
for _, r := range wrap.Requests {
|
||||
if e := rawEnvelope(r); e != nil {
|
||||
out = append(out, e)
|
||||
}
|
||||
}
|
||||
for _, r := range wrap.Responses {
|
||||
if e := rawEnvelope(r); e != nil {
|
||||
out = append(out, e)
|
||||
}
|
||||
}
|
||||
for _, r := range wrap.Revocations {
|
||||
if e := rawEnvelope(r); e != nil {
|
||||
out = append(out, e)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func cmdVerify(args []string) {
|
||||
fs := flag.NewFlagSet("verify", flag.ExitOnError)
|
||||
subject := fs.String("subject", "", "subject trust address")
|
||||
predicate := fs.String("predicate", "", "claim key to evaluate")
|
||||
approversStr := fs.String("approvers", "", "comma-separated approver addresses (optional)")
|
||||
token := fs.String("token", "", "session token (scope read, or per-endpoint tokens)")
|
||||
url := fs.String("url", "http://localhost:8080", "relay URL")
|
||||
fs.Parse(args)
|
||||
|
||||
sub, err := address.Parse(*subject)
|
||||
if err != nil {
|
||||
log.Fatalf("subject: %v", err)
|
||||
}
|
||||
var approvers []address.Address
|
||||
for _, a := range strings.Split(*approversStr, ",") {
|
||||
a = strings.TrimSpace(a)
|
||||
if a == "" {
|
||||
continue
|
||||
}
|
||||
ad, err := address.Parse(a)
|
||||
if err != nil {
|
||||
log.Fatalf("approver %q: %v", a, err)
|
||||
}
|
||||
approvers = append(approvers, ad)
|
||||
}
|
||||
|
||||
g := verify.NewGraph()
|
||||
|
||||
// Claims about the subject.
|
||||
for _, e := range fetchEnvelopes(*url+"/v1/claims?subject="+*subject, *token) {
|
||||
if err := g.Add(e); err != nil {
|
||||
log.Printf("skip claim: %v", err)
|
||||
}
|
||||
}
|
||||
// Revocations for each claim we saw.
|
||||
for _, e := range fetchEnvelopes(*url+"/v1/claims?subject="+*subject, *token) {
|
||||
id := e.ContentID()
|
||||
for _, r := range fetchEnvelopes(*url+"/v1/revocations?claim="+id, *token) {
|
||||
if err := g.Add(r); err != nil {
|
||||
log.Printf("skip revocation: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Approvals, if required: requests to each approver and their responses.
|
||||
for _, ad := range approvers {
|
||||
for _, req := range fetchEnvelopes(*url+"/v1/requests?recipient="+ad.String(), *token) {
|
||||
if err := g.Add(req); err != nil {
|
||||
log.Printf("skip request: %v", err)
|
||||
}
|
||||
for _, resp := range fetchEnvelopes(*url+"/v1/responses?request="+req.ContentID(), *token) {
|
||||
if err := g.Add(resp); err != nil {
|
||||
log.Printf("skip response: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res := g.Evaluate(verify.Policy{
|
||||
Subject: sub,
|
||||
Predicate: *predicate,
|
||||
Approvers: approvers,
|
||||
Now: uint64(time.Now().Unix()),
|
||||
})
|
||||
if res.Trusted {
|
||||
fmt.Println("TRUSTED")
|
||||
} else {
|
||||
fmt.Println("NOT TRUSTED")
|
||||
}
|
||||
if res.Issuer.String() != "" {
|
||||
fmt.Printf("issuer %s\n", res.Issuer)
|
||||
}
|
||||
if res.ApprovedBy.String() != "" {
|
||||
fmt.Printf("approved_by %s\n", res.ApprovedBy)
|
||||
}
|
||||
if res.Revoked {
|
||||
fmt.Println("revoked: yes")
|
||||
}
|
||||
if res.Reason != "" {
|
||||
fmt.Printf("reason %s\n", res.Reason)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue