Merge "veyron/runtimes/google/lib/sync/semaphore: update comment about the behavior of Close() w.r.t. Dec/DecN."
diff --git a/lib/glob/glob.go b/lib/glob/glob.go
index b1356c1..a69be73 100644
--- a/lib/glob/glob.go
+++ b/lib/glob/glob.go
@@ -75,42 +75,49 @@
}
// MatchInitialSegment tries to match segment against the initial element of g.
-// Returns a boolean indicating whether the match was successful and the
-// Glob representing the unmatched remainder of g.
-func (g *Glob) MatchInitialSegment(segment string) (bool, *Glob) {
+// Returns:
+// matched, a boolean indicating whether the match was successful;
+// exact, a boolean indicating whether segment matched a fixed string pattern;
+// remainder, a Glob representing the unmatched remainder of g.
+func (g *Glob) MatchInitialSegment(segment string) (matched bool, exact bool, remainder *Glob) {
if len(g.elems) == 0 {
if !g.recursive {
- return false, nil
+ return false, false, nil
}
- return true, g
+ return true, true, g
}
if matches, err := filepath.Match(g.elems[0], segment); err != nil {
panic("Error in glob pattern found.")
} else if matches {
- return true, g.Split(1)
+ _, fixed := isFixed(g.elems[0])
+ return true, fixed, g.Split(1)
}
- return false, nil
+ return false, false, nil
}
// PartialMatch tries matching elems against part of a glob pattern.
-// The first return value is true if each element e_i of elems matches
-// the (start + i)th element of the glob pattern. If the first return
-// value is true, the second return value returns the unmatched suffix
-// of the pattern. It will be empty if the pattern is completely
-// matched.
+// Returns:
+// matched, a boolean indicating whether each element e_i of elems matches the
+// (start + i)th element of the glob pattern;
+// exact, a boolean indicating whether elems matched a fixed string pattern;
+// remainder, a Glob representing the unmatched remainder of g. remainder will
+// be empty if the pattern is completely matched.
//
// Note that if the glob is recursive elems can have more elements then
// the glob pattern and still get a true result.
-func (g *Glob) PartialMatch(start int, elems []string) (bool, *Glob) {
+func (g *Glob) PartialMatch(start int, elems []string) (matched bool, exact bool, remainder *Glob) {
g = g.Split(start)
- for ; len(elems) > 0; elems = elems[1:] {
- var matched bool
- if matched, g = g.MatchInitialSegment(elems[0]); !matched {
- return false, nil
+ allExact := true
+ for i := 0; i < len(elems); i++ {
+ var matched, exact bool
+ if matched, exact, g = g.MatchInitialSegment(elems[i]); !matched {
+ return false, false, nil
+ } else if !exact {
+ allExact = false
}
}
- return true, g
+ return true, allExact, g
}
// isFixed returns the unescaped string and true if 's' is a pattern specifying
diff --git a/lib/glob/glob_test.go b/lib/glob/glob_test.go
index bbef347..785a1eb 100644
--- a/lib/glob/glob_test.go
+++ b/lib/glob/glob_test.go
@@ -38,3 +38,63 @@
}
}
}
+
+func TestExactMatch(t *testing.T) {
+ tests := []struct {
+ pattern string
+ elems []string
+ matched bool
+ exact bool
+ }{
+ // Test one element, fixed.
+ {"a", []string{"a"}, true, true},
+ {"a", []string{"b"}, false, false},
+ {"\\\\", []string{"\\"}, true, true},
+ // Test one element, containing *.
+ {"*", []string{"*"}, true, false},
+ {"*", []string{"abc"}, true, false},
+ {"\\*", []string{"*"}, true, true},
+ {"\\*", []string{"abc"}, false, false},
+ {"\\\\*", []string{"\\"}, true, false},
+ {"\\\\*", []string{"\\*"}, true, false},
+ {"\\\\*", []string{"\\abc"}, true, false},
+ // Test one element, containing ?.
+ {"?", []string{"?"}, true, false},
+ {"?", []string{"a"}, true, false},
+ {"\\?", []string{"?"}, true, true},
+ {"\\?", []string{"a"}, false, false},
+ {"\\\\?", []string{"\\"}, false, false},
+ {"\\\\?", []string{"\\?"}, true, false},
+ {"\\\\?", []string{"\\a"}, true, false},
+ // Test one element, containing [].
+ {"[abc]", []string{"c"}, true, false},
+ {"\\[abc\\]", []string{"[abc]"}, true, true},
+ {"\\[abc\\]", []string{"a"}, false, false},
+ {"\\\\[abc]", []string{"\\a"}, true, false},
+ {"\\\\[abc]", []string{"a"}, false, false},
+ {"[\\\\]", []string{"\\"}, true, false},
+ {"[\\\\]", []string{"a"}, false, false},
+ // Test multiple elements.
+ {"a/b", []string{"a", "b"}, true, true},
+ {"a/\\*", []string{"a", "*"}, true, true},
+ {"a/\\?", []string{"a", "?"}, true, true},
+ {"a/\\[b\\]", []string{"a", "[b]"}, true, true},
+ {"a/*", []string{"a", "bc"}, true, false},
+ {"a/?", []string{"a", "b"}, true, false},
+ {"a/[bc]", []string{"a", "b"}, true, false},
+ {"a/*/c", []string{"a", "b", "c"}, true, false},
+ {"a/?/c", []string{"a", "b", "c"}, true, false},
+ {"a/[bc]/d", []string{"a", "b", "d"}, true, false},
+ }
+ for _, test := range tests {
+ g, err := Parse(test.pattern)
+ if err != nil {
+ t.Fatalf("parsing %q: %q", test.pattern, err.Error())
+ }
+ matched, exact, _ := g.PartialMatch(0, test.elems)
+ if matched != test.matched || exact != test.exact {
+ t.Fatalf("%v.PartialMatch(0, %v) got (%v, %v), expected (%v, %v)",
+ test.pattern, test.elems, matched, exact, test.matched, test.exact)
+ }
+ }
+}
diff --git a/lib/stats/glob.go b/lib/stats/glob.go
index f2db758..1390853 100644
--- a/lib/stats/glob.go
+++ b/lib/stats/glob.go
@@ -50,7 +50,7 @@
return
}
for name, child := range n.children {
- if ok, left := g.MatchInitialSegment(name); ok {
+ if ok, _, left := g.MatchInitialSegment(name); ok {
globStepLocked(path.Join(prefix, name), left, child, updatedSince, includeValues, result)
}
}
diff --git a/profiles/roaming/init.go b/profiles/roaming/init.go
index d5221f0..ce5862a 100644
--- a/profiles/roaming/init.go
+++ b/profiles/roaming/init.go
@@ -43,11 +43,6 @@
flag.StringVar(&listenProxyFlag, "veyron.proxy", "", "proxy to use")
rt.RegisterProfile(New())
- ListenSpec = &ipc.ListenSpec{
- Protocol: listenProtocolFlag.Protocol,
- Address: listenAddressFlag.String(),
- Proxy: listenProxyFlag,
- }
}
type profile struct {
@@ -92,6 +87,12 @@
func (p *profile) Init(rt veyron2.Runtime, publisher *config.Publisher) {
log := rt.Logger()
+ ListenSpec = &ipc.ListenSpec{
+ Protocol: listenProtocolFlag.Protocol,
+ Address: listenAddressFlag.String(),
+ Proxy: listenProxyFlag,
+ }
+
state, err := netstate.GetAccessibleIPs()
if err != nil {
log.Infof("failed to determine network state")
diff --git a/runtimes/google/ipc/full_test.go b/runtimes/google/ipc/full_test.go
index bb9f3b6..be1655c 100644
--- a/runtimes/google/ipc/full_test.go
+++ b/runtimes/google/ipc/full_test.go
@@ -23,7 +23,6 @@
isecurity "veyron.io/veyron/veyron/runtimes/google/security"
tnaming "veyron.io/veyron/veyron/runtimes/google/testing/mocks/naming"
vsecurity "veyron.io/veyron/veyron/security"
- "veyron.io/veyron/veyron/security/caveat"
"veyron.io/veyron/veyron2"
"veyron.io/veyron/veyron2/ipc"
@@ -569,7 +568,7 @@
}
func mkThirdPartyCaveat(discharger security.PublicID, location string, c security.Caveat) security.Caveat {
- tpc, err := caveat.NewPublicKeyCaveat(c, discharger.PublicKey(), location, security.ThirdPartyRequirements{})
+ tpc, err := security.NewPublicKeyCaveat(discharger.PublicKey(), location, security.ThirdPartyRequirements{}, c)
if err != nil {
panic(err)
}
@@ -605,7 +604,7 @@
dischargerID = serverID.PublicID()
mkClientID = func(req security.ThirdPartyRequirements) security.PrivateID {
- tpc, err := caveat.NewPublicKeyCaveat(newCaveat(alwaysValidCaveat{}), dischargerID.PublicKey(), "mountpoint/discharger", req)
+ tpc, err := security.NewPublicKeyCaveat(dischargerID.PublicKey(), "mountpoint/discharger", req, newCaveat(alwaysValidCaveat{}))
if err != nil {
t.Fatalf("Failed to create ThirdPartyCaveat: %v", err)
}
diff --git a/runtimes/google/naming/namespace/all_test.go b/runtimes/google/naming/namespace/all_test.go
index 479b5bd..bbac581 100644
--- a/runtimes/google/naming/namespace/all_test.go
+++ b/runtimes/google/naming/namespace/all_test.go
@@ -91,7 +91,7 @@
if g.Finished() || len(tree) == 0 {
return nil
}
- if ok, left := g.MatchInitialSegment(tree[0]); ok {
+ if ok, _, left := g.MatchInitialSegment(tree[0]); ok {
if err := t.globLoop(call, naming.Join(prefix, tree[0]), left, tree[1:]); err != nil {
return err
}
diff --git a/runtimes/google/security/identity_chain.go b/runtimes/google/security/identity_chain.go
index d21aea6..2855a36 100644
--- a/runtimes/google/security/identity_chain.go
+++ b/runtimes/google/security/identity_chain.go
@@ -13,7 +13,6 @@
"veyron.io/veyron/veyron/runtimes/google/security/keys"
vsecurity "veyron.io/veyron/veyron/security"
- "veyron.io/veyron/veyron/security/caveat"
"veyron.io/veyron/veyron2/security"
"veyron.io/veyron/veyron2/security/wire"
@@ -232,7 +231,16 @@
}
func (id *chainPrivateID) MintDischarge(cav security.ThirdPartyCaveat, ctx security.Context, duration time.Duration, dischargeCaveats []security.Caveat) (security.Discharge, error) {
- return caveat.NewPublicKeyDischarge(id, cav, ctx, duration, dischargeCaveats)
+ // TODO(ashankar): HACK using new API to fit into old. This will go away anyway when we get rid of PrivateID and PublicID before release.
+ principal, err := security.CreatePrincipal(id.signer)
+ if err != nil {
+ return nil, err
+ }
+ expiry, err := security.ExpiryCaveat(time.Now().Add(duration))
+ if err != nil {
+ return nil, err
+ }
+ return principal.MintDischarge(cav, ctx, expiry, dischargeCaveats...)
}
func (id *chainPrivateID) Sign(message []byte) (security.Signature, error) {
diff --git a/runtimes/google/security/identity_test.go b/runtimes/google/security/identity_test.go
index 33b9726..c10538f 100644
--- a/runtimes/google/security/identity_test.go
+++ b/runtimes/google/security/identity_test.go
@@ -11,7 +11,6 @@
"time"
"veyron.io/veyron/veyron/lib/testutil/blackbox"
- "veyron.io/veyron/veyron/security/caveat"
"veyron.io/veyron/veyron2/security"
"veyron.io/veyron/veyron2/security/wire"
"veyron.io/veyron/veyron2/vlog"
@@ -473,7 +472,7 @@
func TestThirdPartyCaveatMinting(t *testing.T) {
minter := newChain("minter")
- cav, err := caveat.NewPublicKeyCaveat(newCaveat(proximityCaveat{}), minter.PublicID().PublicKey(), "location", security.ThirdPartyRequirements{})
+ cav, err := security.NewPublicKeyCaveat(minter.PublicID().PublicKey(), "location", security.ThirdPartyRequirements{}, newCaveat(proximityCaveat{}))
if err != nil {
t.Fatalf("security.NewPublicKeyCaveat failed: %s", err)
}
@@ -505,7 +504,7 @@
return derive(bless(id.PublicID(), googleChain, name), id)
}
mkTPCaveat := func(id security.PrivateID) security.ThirdPartyCaveat {
- c, err := caveat.NewPublicKeyCaveat(newCaveat(alwaysValidCaveat{}), id.PublicID().PublicKey(), fmt.Sprintf("%v location", id.PublicID()), security.ThirdPartyRequirements{})
+ c, err := security.NewPublicKeyCaveat(id.PublicID().PublicKey(), fmt.Sprintf("%v location", id.PublicID()), security.ThirdPartyRequirements{}, newCaveat(alwaysValidCaveat{}))
if err != nil {
t.Fatalf("NewPublicKeyCaveat with PublicKey of: %v failed: %s", id, err)
}
@@ -567,7 +566,7 @@
ctxEmpty: "missing discharge",
ctxGoogleAtOther: "security.peerBlessingsCaveat=[google] fails validation",
ctxExpired: "security.unixTimeExpiryCaveat",
- ctxInvalid: "invalid signature",
+ ctxInvalid: "signature verification on discharge",
}
)
@@ -626,7 +625,7 @@
func TestThirdPartyCaveatAccessors(t *testing.T) {
mkTPCaveat := func(id security.PublicID) security.ThirdPartyCaveat {
- tpCav, err := caveat.NewPublicKeyCaveat(newCaveat(alwaysValidCaveat{}), id.PublicKey(), "someLocation", security.ThirdPartyRequirements{})
+ tpCav, err := security.NewPublicKeyCaveat(id.PublicKey(), "someLocation", security.ThirdPartyRequirements{}, newCaveat(alwaysValidCaveat{}))
if err != nil {
t.Fatalf("NewPublicKeyCaveat with PublicKey of: %v failed: %s", id, err)
}
diff --git a/security/caveat/public_key_caveat.go b/security/caveat/public_key_caveat.go
deleted file mode 100644
index 76e8548..0000000
--- a/security/caveat/public_key_caveat.go
+++ /dev/null
@@ -1,222 +0,0 @@
-// Package caveat will be removed as the new security API takes shape before November 1, 2014.
-package caveat
-
-import (
- "crypto/rand"
- "crypto/sha256"
- "encoding/binary"
- "fmt"
- "time"
-
- vsecurity "veyron.io/veyron/veyron/security"
-
- "veyron.io/veyron/veyron2/security"
- "veyron.io/veyron/veyron2/security/wire"
- "veyron.io/veyron/veyron2/vom"
-)
-
-// nonceLength specifies the length in bytes of the random nonce used
-// in publicKeyCaveat and publicKeyDischarge.
-//
-// TODO(ataly, ashankar): Nonce length of 16 bytes was chosen very conservatively.
-// The purpose of the random nonce is essentially to separate caveats that have
-// the same restriction and validation key (in order to prevent discharge replay).
-// Can we use 4bytes instead?.
-const nonceLength = 16
-
-// publicKeyCaveat implements security.ThirdPartyCaveat. It specifies a restriction,
-// a validation key and the location (object name) of the third-party responsible
-// for discharging the caveat. A discharge for this caveat is a signed assertion whose
-// signature can be verified using the validation key.
-//
-// TODO(ataly): This should be a VDL defined type.
-type publicKeyCaveat struct {
- // RandNonce specifies a cryptographically random nonce (of fixed length) that
- // uniquely identifies the caveat.
- RandNonce []uint8
- // DischargeMintingCaveat specifies the caveat that has to be validated
- // before minting a discharge for a publicKeyCaveat.
- DischargeMintingCaveat security.Caveat
- // ValidationKey specifies the public key of the discharging-party.
- ValidationKey wire.PublicKey
- // ThirdPartyLocation specifies the object name of the discharging-party.
- ThirdPartyLocation string
- // ThirdPartyRequirements specify the information wanted in order to issue a discharge.
- ThirdPartyRequirements security.ThirdPartyRequirements
-}
-
-// ID returns a unique 32bytes long identity for the caveat based on the random nonce
-// and SHA-256 hash of the restriction embedded in the caveat.
-//
-// TODO(ataly, ashankar): A 256bit hash is probably much stronger that what we need
-// here. Can we truncate the hash to 96bits?
-func (c *publicKeyCaveat) ID() string {
- return id(c.RandNonce, c.DischargeMintingCaveat.ValidatorVOM)
-}
-
-func (c *publicKeyCaveat) Location() string {
- return c.ThirdPartyLocation
-}
-
-func (c *publicKeyCaveat) String() string {
- return fmt.Sprintf("publicKeyCaveat{DischargeMintingCaveat: (%v bytes), ThirdPartyLocation: %q}", len(c.DischargeMintingCaveat.ValidatorVOM), c.ThirdPartyLocation)
-}
-
-func (c *publicKeyCaveat) Requirements() security.ThirdPartyRequirements {
- return c.ThirdPartyRequirements
-}
-
-func (c *publicKeyCaveat) Validate(ctx security.Context) error {
- // Check whether the context has a dicharge matching the caveat's ID.
- dis, ok := ctx.Discharges()[c.ID()]
- if !ok {
- return fmt.Errorf("missing discharge")
- }
-
- pkDischarge, ok := dis.(*publicKeyDischarge)
- if !ok {
- return fmt.Errorf("caveat of type %T cannot be validated with discharge of type %T", c, dis)
- }
-
- // Validate all caveats present on the discharge.
- validators, err := vsecurity.CaveatValidators(pkDischarge.Caveats...)
- if err != nil {
- return err
- }
- for _, v := range validators {
- if err := v.Validate(ctx); err != nil {
- return err
- }
- }
-
- // Check the discharge signature with the validation key from the caveat.
- key, err := c.ValidationKey.Decode()
- if err != nil {
- return err
- }
- if !pkDischarge.Signature.Verify(key, pkDischarge.contentHash()) {
- return fmt.Errorf("discharge %v has invalid signature", dis)
- }
- return nil
-}
-
-// publicKeyDischarge implements security.Discharge. It specifies a discharge
-// for a publicKeyCaveat, and includes a signature that can be verified with
-// the validation key of the publicKeyCaveat. Additionally, the discharge may
-// also include caveats which must all validate in order for the discharge to
-// be considered valid.
-//
-// TODO(ataly): This should be a VDL-defined type?
-type publicKeyDischarge struct {
- // CaveatID is used to match this Discharge to the the ThirdPartyCaveat it is for.
- CaveatID string
-
- // Caveats under which this Discharge is valid.
- Caveats []security.Caveat
-
- // Signature on the contents of the discharge that can be verified using the
- // validaton key in the publicKeycaveat this discharge is for.
- Signature security.Signature
-}
-
-func (d *publicKeyDischarge) ID() string {
- return d.CaveatID
-}
-
-func (d *publicKeyDischarge) ThirdPartyCaveats() []security.ThirdPartyCaveat {
- return vsecurity.ThirdPartyCaveats(d.Caveats...)
-}
-
-func (d *publicKeyDischarge) sign(signer security.PrivateID) (err error) {
- d.Signature, err = signer.Sign(d.contentHash())
- return
-}
-
-func (d *publicKeyDischarge) contentHash() []byte {
- h := sha256.New()
- tmp := make([]byte, binary.MaxVarintLen64)
-
- wire.WriteString(h, tmp, d.CaveatID)
- for _, cav := range d.Caveats {
- wire.WriteBytes(h, tmp, cav.ValidatorVOM)
- }
- return h.Sum(nil)
-}
-
-// NewPublicKeyCaveat returns a security.ThirdPartyCaveat which requires a
-// discharge from a principal identified by the public key 'key' and present
-// at the object name 'location'. This discharging principal is expected to
-// validate 'caveat' before issuing a discharge.
-func NewPublicKeyCaveat(caveat security.Caveat, key security.PublicKey, location string, requirements security.ThirdPartyRequirements) (security.ThirdPartyCaveat, error) {
- nonce := make([]uint8, nonceLength)
- if _, err := rand.Read(nonce); err != nil {
- return nil, err
- }
-
- var validationKey wire.PublicKey
- if err := validationKey.Encode(key); err != nil {
- return nil, err
- }
-
- return &publicKeyCaveat{
- RandNonce: nonce,
- DischargeMintingCaveat: caveat,
- ValidationKey: validationKey,
- ThirdPartyLocation: location,
- ThirdPartyRequirements: requirements,
- }, nil
-}
-
-// NewPublicKeyDischarge returns a new discharge for the provided 'tp'
-// after validating any restrictions specified in it under context 'ctx'.
-//
-// The ID of the discharge is the same as the ID of 'caveat', and the discharge
-// will be usable for the provided 'duration' only if:
-// (1) 'caveats' are met when using the discharge.
-// (2) 'tp' was obtained from NewPublicKeyCaveat using a key that is the same as
-// the provided signer's PublicKey.
-func NewPublicKeyDischarge(signer security.PrivateID, tp security.ThirdPartyCaveat, ctx security.Context, duration time.Duration, caveats []security.Caveat) (security.Discharge, error) {
- pkCaveat, ok := tp.(*publicKeyCaveat)
- if !ok {
- return nil, fmt.Errorf("cannot mint discharges for %T", tp)
- }
-
- v, err := vsecurity.CaveatValidators(pkCaveat.DischargeMintingCaveat)
- if err != nil {
- return nil, err
- }
- if err := v[0].Validate(ctx); err != nil {
- return nil, err
- }
-
- expiryCaveat, err := security.ExpiryCaveat(time.Now().Add(duration))
- if err != nil {
- return nil, err
- }
-
- caveats = append(caveats, expiryCaveat)
- discharge := &publicKeyDischarge{CaveatID: tp.ID(), Caveats: caveats}
-
- // TODO(ashankar,ataly): Should signer necessarily be the same as ctx.LocalID()?
- // If so, need the PrivateID object corresponding to ctx.LocalID.
- if err := discharge.sign(signer); err != nil {
- return nil, err
- }
- return discharge, nil
-}
-
-// id calculates the sha256 hash of length-delimited byte slices
-func id(slices ...[]byte) string {
- h := sha256.New()
- tmp := make([]byte, binary.MaxVarintLen64)
-
- for _, slice := range slices {
- wire.WriteBytes(h, tmp, slice)
- }
- return string(h.Sum(nil))
-}
-
-func init() {
- vom.Register(publicKeyCaveat{})
- vom.Register(publicKeyDischarge{})
-}
diff --git a/services/identity/handlers/bless.go b/services/identity/handlers/bless.go
index 70d43b7..468df38 100644
--- a/services/identity/handlers/bless.go
+++ b/services/identity/handlers/bless.go
@@ -2,11 +2,14 @@
import (
"fmt"
+ "html/template"
"net/http"
+ "strings"
"time"
"veyron.io/veyron/veyron/services/identity/util"
"veyron.io/veyron/veyron2/security"
+ "veyron.io/veyron/veyron2/vlog"
)
// Bless is an http.HandlerFunc that renders/processes a form that takes as
@@ -19,11 +22,11 @@
return
}
if r.Method != "POST" {
- util.HTTPBadRequest(w, r, fmt.Errorf("Only GET or POST requests accepted"))
+ util.HTTPBadRequest(w, r, fmt.Errorf("only GET or POST requests accepted"))
return
}
if err := r.ParseForm(); err != nil {
- util.HTTPBadRequest(w, r, fmt.Errorf("Failed to parse form: %v", err))
+ util.HTTPBadRequest(w, r, fmt.Errorf("failed to parse form: %v", err))
return
}
duration, err := time.ParseDuration(defaultIfEmpty(r.FormValue("duration"), "24h"))
@@ -42,7 +45,11 @@
return
}
name := r.FormValue("name")
- blessed, err := blessor.Bless(blessee, name, duration, nil)
+ caveats, err := caveats(r)
+ if err != nil {
+ util.HTTPBadRequest(w, r, fmt.Errorf("failed to created caveats: ", err))
+ }
+ blessed, err := blessor.Bless(blessee, name, duration, caveats)
if err != nil {
util.HTTPBadRequest(w, r, fmt.Errorf("%q.Bless(%q, %q) failed: %v", blessor, blessee, name, err))
return
@@ -50,6 +57,29 @@
util.HTTPSend(w, blessed)
}
+func caveats(r *http.Request) ([]security.Caveat, error) {
+ if err := r.ParseForm(); err != nil {
+ return nil, err
+ }
+ var caveats []security.Caveat
+ for i, cavName := range r.Form["caveat"] {
+ if cavName == "none" {
+ continue
+ }
+ args := strings.Split(r.Form[cavName][i], ",")
+ cavInfo, ok := caveatMap[cavName]
+ if !ok {
+ return nil, fmt.Errorf("unable to create caveat %s: caveat does not exist", cavName)
+ }
+ caveat, err := cavInfo.New(args...)
+ if err != nil {
+ return nil, fmt.Errorf("unable to create caveat %s(%v): cavInfo.New failed: %v", cavName, args, err)
+ }
+ caveats = append(caveats, caveat)
+ }
+ return caveats, nil
+}
+
func decodeBlessor(r *http.Request) (security.PrivateID, error) {
var blessor security.PrivateID
b64 := r.FormValue("blessor")
@@ -74,14 +104,52 @@
}
func renderForm(w http.ResponseWriter, r *http.Request) {
- w.Write([]byte(`
-<!doctype html>
+ w.Header().Set("Context-Type", "text/html")
+ if err := tmpl.Execute(w, caveatMap); err != nil {
+ vlog.Errorf("Unable to execute bless page template: %v", err)
+ util.HTTPServerError(w, err)
+ }
+}
+
+var tmpl = template.Must(template.New("bless").Parse(`<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Veyron Identity Derivation</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css">
+<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
+<script>
+ // TODO(suharshs): Move this and other JS/CSS to an assets directory in identity server.
+ $(document).ready(function() {
+ $('.caveatInput').hide(); // Hide all the inputs at start.
+
+ // When a caveat selector changes show the corresponding input box.
+ $('body').on('change', '.caveats', function (){
+ // Grab the div encapsulating the select and the corresponding inputs.
+ var caveatSelector = $(this).parents(".caveatRow");
+ // Hide the visible inputs and show the selected one.
+ caveatSelector.find('.caveatInput').hide();
+ caveatSelector.find('#'+$(this).val()).show();
+ });
+
+ // Upon clicking the '+' button a new caveat selector should appear.
+ $('body').on('click', '.addCaveat', function() {
+ var selector = $(this).parents(".caveatRow");
+ var newSelector = selector.clone();
+ // Hide all inputs since nothing is selected in this clone.
+ newSelector.find('.caveatInput').hide();
+ selector.after(newSelector);
+ // Change the '+' button to a '-' button.
+ $(this).replaceWith('<button type="button" class="btn btn-danger btn-sm removeCaveat">-</button>')
+ });
+
+ // Upon clicking the '-' button caveats should be removed.
+ $('body').on('click', '.removeCaveat', function() {
+ $(this).parents(".caveatRow").remove();
+ });
+ });
+</script>
</head>
<body class="container">
<form class="form-signin" method="POST" name="input" action="/bless/">
@@ -93,12 +161,31 @@
<input type="text" class="form-control" name="name" placeholder="Name">
<br/>
<input type="text" class="form-control" name="duration" placeholder="Duration. Defaults to 24h">
+<div class="caveatRow row">
+ <br/>
+ <div class="col-md-4">
+ <select name="caveat" class="form-control caveats">
+ <option value="none" selected="selected">Select a caveat.</option>
+ {{ $caveatMap := . }}
+ {{range $key, $value := $caveatMap}}
+ <option name="{{$key}}" value="{{$key}}">{{$key}}</option>
+ {{end}}
+ </select>
+ </div>
+ <div class="col-md-7">
+ {{range $key, $entry := $caveatMap}}
+ <input type="text" id="{{$key}}" class="form-control caveatInput" name="{{$key}}" placeholder="{{$entry.Placeholder}}">
+ {{end}}
+ </div>
+ <div class="col-md-1">
+ <button type="button" class="btn btn-info btn-sm addCaveat">+</button>
+ </div>
+</div>
<br/>
<button class="btn btn-lg btn-primary btn-block" type="submit">Bless</button>
</form>
</body>
</html>`))
-}
func defaultIfEmpty(str, def string) string {
if len(str) == 0 {
@@ -106,3 +193,49 @@
}
return str
}
+
+// caveatMap is a map from Caveat name to caveat information.
+// To add to this map append
+// key = "CaveatName"
+// New = func that returns instantiation of specific caveat wrapped in security.Caveat.
+// Placeholder = the placeholder text for the html input element.
+var caveatMap = map[string]struct {
+ New func(args ...string) (security.Caveat, error)
+ Placeholder string
+}{
+ "ExpiryCaveat": {
+ New: func(args ...string) (security.Caveat, error) {
+ if len(args) != 1 {
+ return security.Caveat{}, fmt.Errorf("must pass exactly one duration string.")
+ }
+ dur, err := time.ParseDuration(args[0])
+ if err != nil {
+ return security.Caveat{}, fmt.Errorf("parse duration failed: %v", err)
+ }
+ return security.ExpiryCaveat(time.Now().Add(dur))
+ },
+ Placeholder: "i.e. 2h45m. Valid time units are ns, us (or µs), ms, s, m, h.",
+ },
+ "MethodCaveat": {
+ New: func(args ...string) (security.Caveat, error) {
+ if len(args) < 1 {
+ return security.Caveat{}, fmt.Errorf("must pass at least one method")
+ }
+ return security.MethodCaveat(args[0], args[1:]...)
+ },
+ Placeholder: "Comma-separated method names.",
+ },
+ "PeerBlessingsCaveat": {
+ New: func(args ...string) (security.Caveat, error) {
+ if len(args) < 1 {
+ return security.Caveat{}, fmt.Errorf("must pass at least one blessing pattern")
+ }
+ var patterns []security.BlessingPattern
+ for _, arg := range args {
+ patterns = append(patterns, security.BlessingPattern(arg))
+ }
+ return security.PeerBlessingsCaveat(patterns[0], patterns[1:]...)
+ },
+ Placeholder: "Comma-separated blessing patterns.",
+ },
+}
diff --git a/services/identity/revocation/bless.go b/services/identity/revocation/bless.go
index 57dcb71..473bcd4 100644
--- a/services/identity/revocation/bless.go
+++ b/services/identity/revocation/bless.go
@@ -12,16 +12,18 @@
// Bless creates a blessing on behalf of the identity server.
func Bless(server security.PrivateID, blessee security.PublicID, email string, duration time.Duration, revocationCaveat security.ThirdPartyCaveat) (security.PublicID, error) {
+ // TODO(suharshs): Pass caveats to here when macaroon new oauth flow is complete.
+ var caveats []security.Caveat
if revocationCaveat != nil {
caveat, err := security.NewCaveat(revocationCaveat)
if err != nil {
return nil, err
}
- // TODO(suharshs): Extend the duration for blessings with provided revocaionCaveats
- return server.Bless(blessee, email, duration, []security.Caveat{caveat})
+ // revocationCaveat must be prepended because it is assumed to be first by ReadBlessAuditEntry.
+ caveats = append([]security.Caveat{caveat}, caveats...)
}
- // return a blessing with a more limited duration, since there is no revocation caveat
- return server.Bless(blessee, email, duration, nil)
+ // TODO(suharshs): Extend the duration for blessings with provided revocaionCaveats.
+ return server.Bless(blessee, email, duration, caveats)
}
type BlessingAuditEntry struct {
diff --git a/services/identity/revocation/revocation_manager.go b/services/identity/revocation/revocation_manager.go
index c05bd5e..f79e740 100644
--- a/services/identity/revocation/revocation_manager.go
+++ b/services/identity/revocation/revocation_manager.go
@@ -10,7 +10,6 @@
"sync"
"time"
- "veyron.io/veyron/veyron/security/caveat"
"veyron.io/veyron/veyron/services/identity/util"
"veyron.io/veyron/veyron2/security"
"veyron.io/veyron/veyron2/vom"
@@ -35,7 +34,7 @@
if err != nil {
return nil, err
}
- cav, err := caveat.NewPublicKeyCaveat(restriction, dischargerID.PublicKey(), dischargerLocation, security.ThirdPartyRequirements{})
+ cav, err := security.NewPublicKeyCaveat(dischargerID.PublicKey(), dischargerLocation, security.ThirdPartyRequirements{}, restriction)
if err != nil {
return nil, err
}
diff --git a/services/mgmt/debug/dispatcher.go b/services/mgmt/debug/dispatcher.go
index a0c86cc..9c6e3b8 100644
--- a/services/mgmt/debug/dispatcher.go
+++ b/services/mgmt/debug/dispatcher.go
@@ -62,7 +62,7 @@
return err
}
for _, leaf := range i.leaves {
- if ok, left := g.MatchInitialSegment(leaf); ok {
+ if ok, _, left := g.MatchInitialSegment(leaf); ok {
if err := i.leafGlob(call, leaf, left.String()); err != nil {
return err
}
diff --git a/services/mgmt/logreader/impl/logdir_invoker.go b/services/mgmt/logreader/impl/logdir_invoker.go
index 5beb139..ef7a60b 100644
--- a/services/mgmt/logreader/impl/logdir_invoker.go
+++ b/services/mgmt/logreader/impl/logdir_invoker.go
@@ -78,7 +78,7 @@
if fileName == "." || fileName == ".." {
continue
}
- if ok, left := g.MatchInitialSegment(fileName); ok {
+ if ok, _, left := g.MatchInitialSegment(fileName); ok {
if err := i.globStep(path.Join(name, fileName), left, file.IsDir(), call); err != nil {
return err
}
diff --git a/services/mounttable/lib/mounttable.go b/services/mounttable/lib/mounttable.go
index 922f4c7..85936ec 100644
--- a/services/mounttable/lib/mounttable.go
+++ b/services/mounttable/lib/mounttable.go
@@ -357,7 +357,7 @@
// Recurse through the children.
for k, c := range n.children {
- if ok, suffix := pattern.MatchInitialSegment(k); ok {
+ if ok, _, suffix := pattern.MatchInitialSegment(k); ok {
mt.globStep(c, naming.Join(name, k), suffix, context, reply)
}
}
diff --git a/services/mounttable/lib/neighborhood.go b/services/mounttable/lib/neighborhood.go
index ad66bfb..acdb46c 100644
--- a/services/mounttable/lib/neighborhood.go
+++ b/services/mounttable/lib/neighborhood.go
@@ -244,7 +244,7 @@
switch len(ns.elems) {
case 0:
for k, n := range nh.neighbors() {
- if ok, _ := g.MatchInitialSegment(k); !ok {
+ if ok, _, _ := g.MatchInitialSegment(k); !ok {
continue
}
if err := sender.Send(types.MountEntry{Name: k, Servers: n}); err != nil {
diff --git a/services/security/discharger/revoker.go b/services/security/discharger/revoker.go
index e7c23a1..8ccea7b 100644
--- a/services/security/discharger/revoker.go
+++ b/services/security/discharger/revoker.go
@@ -9,7 +9,6 @@
"os"
"path/filepath"
"sync"
- "veyron.io/veyron/veyron/security/caveat"
ssecurity "veyron.io/veyron/veyron/services/security"
"veyron.io/veyron/veyron2/ipc"
"veyron.io/veyron/veyron2/security"
@@ -73,7 +72,7 @@
if err != nil {
return revocation, nil, err
}
- cav, err := caveat.NewPublicKeyCaveat(restriction, dischargerID.PublicKey(), dischargerLocation, security.ThirdPartyRequirements{})
+ cav, err := security.NewPublicKeyCaveat(dischargerID.PublicKey(), dischargerLocation, security.ThirdPartyRequirements{}, restriction)
return revocation, cav, err
}