Merge "services/device/internal/impl: shard device unit tests (2)"
diff --git a/examples/hello/hello_test.go b/examples/hello/hello_v23_test.go
similarity index 100%
rename from examples/hello/hello_test.go
rename to examples/hello/hello_v23_test.go
diff --git a/profiles/internal/rpc/client.go b/profiles/internal/rpc/client.go
index 2efc1b3..f00c4ba 100644
--- a/profiles/internal/rpc/client.go
+++ b/profiles/internal/rpc/client.go
@@ -82,33 +82,19 @@
// TODO(jhahn): Add monitoring the network interface changes.
ipNets []*net.IPNet
- // We support concurrent calls to StartCall and Close, so we must protect the
- // vcMap. Everything else is initialized upon client construction, and safe
- // to use concurrently.
- vcMapMu sync.Mutex
- vcMap map[vcMapKey]*vcInfo
+ vcCache *vc.VCCache
dc vc.DischargeClient
}
var _ rpc.Client = (*client)(nil)
-type vcInfo struct {
- vc stream.VC
- remoteEP naming.Endpoint
-}
-
-type vcMapKey struct {
- endpoint string
- clientPublicKey string // clientPublicKey = "" means we are running unencrypted (i.e. SecurityNone)
-}
-
func InternalNewClient(streamMgr stream.Manager, ns namespace.T, opts ...rpc.ClientOpt) (rpc.Client, error) {
c := &client{
streamMgr: streamMgr,
ns: ns,
ipNets: ipNetworks(),
- vcMap: make(map[vcMapKey]*vcInfo),
+ vcCache: vc.NewVCCache(),
}
c.dc = InternalNewDischargeClient(nil, c, 0)
for _, opt := range opts {
@@ -125,54 +111,54 @@
}
func (c *client) createFlow(ctx *context.T, principal security.Principal, ep naming.Endpoint, vcOpts []stream.VCOpt) (stream.Flow, *verror.SubErr) {
- c.vcMapMu.Lock()
- defer c.vcMapMu.Unlock()
-
suberr := func(err error) *verror.SubErr {
return &verror.SubErr{Err: err, Options: verror.Print}
}
- if c.vcMap == nil {
+ found, err := c.vcCache.ReservedFind(ep, principal)
+ if err != nil {
return nil, suberr(verror.New(errClientCloseAlreadyCalled, ctx))
}
-
- vcKey := vcMapKey{endpoint: ep.String()}
- if principal != nil {
- vcKey.clientPublicKey = principal.PublicKey().String()
- }
- if vcinfo := c.vcMap[vcKey]; vcinfo != nil {
- if flow, err := vcinfo.vc.Connect(); err == nil {
+ defer c.vcCache.Unreserve(ep, principal)
+ if found != nil {
+ // We are serializing the creation of all flows per VC. This is okay
+ // because if one flow creation is to block, it is likely that all others
+ // for that VC would block as well.
+ if flow, err := found.Connect(); err == nil {
return flow, nil
}
// If the vc fails to establish a new flow, we assume it's
- // broken, remove it from the map, and proceed to establishing
+ // broken, remove it from the cache, and proceed to establishing
// a new vc.
+ //
+ // TODO(suharshs): The decision to redial 1 time when the dialing the vc
+ // in the cache fails is a bit inconsistent with the behavior when a newly
+ // dialed vc.Connect fails. We should revisit this.
+ //
// TODO(caprita): Should we distinguish errors due to vc being
// closed from other errors? If not, should we call vc.Close()
- // before removing the vc from the map?
- delete(c.vcMap, vcKey)
+ // before removing the vc from the cache?
+ if err := c.vcCache.Delete(found); err != nil {
+ return nil, suberr(verror.New(errClientCloseAlreadyCalled, ctx))
+ }
}
+
sm := c.streamMgr
- c.vcMapMu.Unlock()
- vc, err := sm.Dial(ep, principal, vcOpts...)
- c.vcMapMu.Lock()
+ v, err := sm.Dial(ep, principal, vcOpts...)
if err != nil {
return nil, suberr(err)
}
- if c.vcMap == nil {
+
+ flow, err := v.Connect()
+ if err != nil {
+ return nil, suberr(err)
+ }
+
+ if err := c.vcCache.Insert(v.(*vc.VC)); err != nil {
sm.ShutdownEndpoint(ep)
return nil, suberr(verror.New(errClientCloseAlreadyCalled, ctx))
}
- if othervc, exists := c.vcMap[vcKey]; exists {
- go vc.Close(nil)
- vc = othervc.vc
- } else {
- c.vcMap[vcKey] = &vcInfo{vc: vc, remoteEP: ep}
- }
- flow, err := vc.Connect()
- if err != nil {
- return nil, suberr(err)
- }
+
return flow, nil
}
@@ -744,12 +730,9 @@
func (c *client) Close() {
defer vlog.LogCall()()
- c.vcMapMu.Lock()
- for _, v := range c.vcMap {
- c.streamMgr.ShutdownEndpoint(v.remoteEP)
+ for _, v := range c.vcCache.Close() {
+ c.streamMgr.ShutdownEndpoint(v.RemoteEndpoint())
}
- c.vcMap = nil
- c.vcMapMu.Unlock()
}
// flowClient implements the RPC client-side protocol for a single RPC, over a
diff --git a/profiles/internal/rpc/stream/vc/vc_cache.go b/profiles/internal/rpc/stream/vc/vc_cache.go
new file mode 100644
index 0000000..d962cfa
--- /dev/null
+++ b/profiles/internal/rpc/stream/vc/vc_cache.go
@@ -0,0 +1,114 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package vc
+
+import (
+ "sync"
+
+ "v.io/v23/naming"
+ "v.io/v23/security"
+ "v.io/v23/verror"
+)
+
+var errVCCacheClosed = reg(".errVCCacheClosed", "vc cache has been closed")
+
+// VCCache implements a set of VIFs keyed by the endpoint of the remote end and the
+// local principal. Multiple goroutines can invoke methods on the VCCache simultaneously.
+type VCCache struct {
+ mu sync.Mutex
+ cache map[vcKey]*VC // GUARDED_BY(mu)
+ started map[vcKey]bool // GUARDED_BY(mu)
+ cond *sync.Cond
+}
+
+// NewVCCache returns a new cache for VCs.
+func NewVCCache() *VCCache {
+ c := &VCCache{
+ cache: make(map[vcKey]*VC),
+ started: make(map[vcKey]bool),
+ }
+ c.cond = sync.NewCond(&c.mu)
+ return c
+}
+
+// ReservedFind returns a VC where the remote end of the underlying connection
+// is identified by the provided (ep, p.PublicKey). Returns nil if there is no
+// such VC.
+//
+// Iff the cache is closed, ReservedFind will return an error.
+// If ReservedFind has no error, the caller is required to call Unreserve, to avoid deadlock.
+// The ep, and p.PublicKey in Unreserve must be the same as used in the ReservedFind call.
+// During this time, all new ReservedFind calls for this ep and p will Block until
+// the corresponding Unreserve call is made.
+func (c *VCCache) ReservedFind(ep naming.Endpoint, p security.Principal) (*VC, error) {
+ k := c.key(ep, p)
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ for c.started[k] {
+ c.cond.Wait()
+ }
+ if c.cache == nil {
+ return nil, verror.New(errVCCacheClosed, nil)
+ }
+ c.started[k] = true
+ return c.cache[k], nil
+}
+
+// Unreserve marks the status of the ep, p as no longer started, and
+// broadcasts waiting threads.
+func (c *VCCache) Unreserve(ep naming.Endpoint, p security.Principal) {
+ c.mu.Lock()
+ delete(c.started, c.key(ep, p))
+ c.cond.Broadcast()
+ c.mu.Unlock()
+}
+
+// Insert adds vc to the cache and returns an error iff the cache has been closed.
+func (c *VCCache) Insert(vc *VC) error {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ if c.cache == nil {
+ return verror.New(errVCCacheClosed, nil)
+ }
+ c.cache[c.key(vc.RemoteEndpoint(), vc.LocalPrincipal())] = vc
+ return nil
+}
+
+// Close marks the VCCache as closed and returns the VCs remaining in the cache.
+func (c *VCCache) Close() []*VC {
+ c.mu.Lock()
+ vcs := make([]*VC, 0, len(c.cache))
+ for _, vc := range c.cache {
+ vcs = append(vcs, vc)
+ }
+ c.cache = nil
+ c.started = nil
+ c.mu.Unlock()
+ return vcs
+}
+
+// Delete removes vc from the cache, returning an error iff the cache has been closed.
+func (c *VCCache) Delete(vc *VC) error {
+ c.mu.Lock()
+ defer c.mu.Unlock()
+ if c.cache == nil {
+ return verror.New(errVCCacheClosed, nil)
+ }
+ delete(c.cache, c.key(vc.RemoteEndpoint(), vc.LocalPrincipal()))
+ return nil
+}
+
+type vcKey struct {
+ remoteEP string
+ localPublicKey string // localPublicKey = "" means we are running unencrypted (i.e. SecurityNone)
+}
+
+func (c *VCCache) key(ep naming.Endpoint, p security.Principal) vcKey {
+ k := vcKey{remoteEP: ep.String()}
+ if p != nil {
+ k.localPublicKey = p.PublicKey().String()
+ }
+ return k
+}
diff --git a/profiles/internal/rpc/stream/vc/vc_cache_test.go b/profiles/internal/rpc/stream/vc/vc_cache_test.go
new file mode 100644
index 0000000..8bfa144
--- /dev/null
+++ b/profiles/internal/rpc/stream/vc/vc_cache_test.go
@@ -0,0 +1,123 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package vc
+
+import (
+ "testing"
+
+ inaming "v.io/x/ref/profiles/internal/naming"
+ "v.io/x/ref/test/testutil"
+)
+
+func TestInsertDelete(t *testing.T) {
+ cache := NewVCCache()
+ ep, err := inaming.NewEndpoint("foo:8888")
+ if err != nil {
+ t.Fatal(err)
+ }
+ p := testutil.NewPrincipal("test")
+ vc := &VC{remoteEP: ep, localPrincipal: p}
+ otherEP, err := inaming.NewEndpoint("foo:8888")
+ if err != nil {
+ t.Fatal(err)
+ }
+ otherP := testutil.NewPrincipal("test")
+ otherVC := &VC{remoteEP: otherEP, localPrincipal: otherP}
+
+ cache.Insert(vc)
+ cache.Insert(otherVC)
+ cache.Delete(vc)
+ if got, want := cache.Close(), []*VC{otherVC}; !vcsEqual(got, want) {
+ t.Errorf("got %v, want %v", got, want)
+ }
+}
+
+func TestInsertClose(t *testing.T) {
+ cache := NewVCCache()
+ ep, err := inaming.NewEndpoint("foo:8888")
+ if err != nil {
+ t.Fatal(err)
+ }
+ p := testutil.NewPrincipal("test")
+ vc := &VC{remoteEP: ep, localPrincipal: p}
+
+ if err := cache.Insert(vc); err != nil {
+ t.Errorf("the cache is not closed yet")
+ }
+ if got, want := cache.Close(), []*VC{vc}; !vcsEqual(got, want) {
+ t.Errorf("got %v, want %v", got, want)
+ }
+ if err := cache.Insert(vc); err == nil {
+ t.Errorf("the cache has been closed")
+ }
+}
+
+func TestReservedFind(t *testing.T) {
+ cache := NewVCCache()
+ ep, err := inaming.NewEndpoint("foo:8888")
+ if err != nil {
+ t.Fatal(err)
+ }
+ p := testutil.NewPrincipal("test")
+ vc := &VC{remoteEP: ep, localPrincipal: p}
+ cache.Insert(vc)
+
+ // We should be able to find the vc in the cache.
+ if got, err := cache.ReservedFind(ep, p); err != nil || got != vc {
+ t.Errorf("got %v, want %v, err: %v", got, vc, err)
+ }
+
+ // If we change the endpoint or the principal, we should get nothing.
+ otherEP, err := inaming.NewEndpoint("bar: 7777")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if got, err := cache.ReservedFind(otherEP, p); err != nil || got != nil {
+ t.Errorf("got %v, want <nil>, err: %v", got, err)
+ }
+ if got, err := cache.ReservedFind(ep, testutil.NewPrincipal("wrong")); err != nil || got != nil {
+ t.Errorf("got %v, want <nil>, err: %v", got, err)
+ }
+
+ // A subsequent ReservedFind call that matches a previous failed ReservedFind
+ // should block until a matching Unreserve call is made.
+ ch := make(chan *VC, 1)
+ go func(ch chan *VC) {
+ vc, err := cache.ReservedFind(otherEP, p)
+ if err != nil {
+ t.Fatal(err)
+ }
+ ch <- vc
+ }(ch)
+
+ // We insert the otherEP into the cache.
+ otherVC := &VC{remoteEP: otherEP, localPrincipal: p}
+ cache.Insert(otherVC)
+ cache.Unreserve(otherEP, p)
+
+ // Now the cache.BlcokingFind should have returned the correct otherVC.
+ if cachedVC := <-ch; cachedVC != otherVC {
+ t.Errorf("got %v, want %v", cachedVC, otherVC)
+ }
+}
+
+func vcsEqual(a, b []*VC) bool {
+ if len(a) != len(b) {
+ return false
+ }
+ m := make(map[*VC]int)
+ for _, v := range a {
+ m[v]++
+ }
+ for _, v := range b {
+ m[v]--
+ }
+ for _, i := range m {
+ if i != 0 {
+ return false
+ }
+ }
+ return true
+}
diff --git a/profiles/internal/rpc/stream/vif/set.go b/profiles/internal/rpc/stream/vif/set.go
index b966735..497e43b 100644
--- a/profiles/internal/rpc/stream/vif/set.go
+++ b/profiles/internal/rpc/stream/vif/set.go
@@ -45,7 +45,8 @@
return s.find(network, address, true)
}
-// Unblock broadcasts all threads
+// Unblock marks the status of the network, address as no longer started, and
+// broadcasts waiting threads.
func (s *Set) Unblock(network, address string) {
s.mu.Lock()
delete(s.started, key(network, address))
@@ -60,7 +61,7 @@
return s.find(network, address, false)
}
-// Insert adds a VIF to the set
+// Insert adds a VIF to the set.
func (s *Set) Insert(vif *VIF) {
addr := vif.conn.RemoteAddr()
k := key(addr.Network(), addr.String())
@@ -76,7 +77,7 @@
vif.addSet(s)
}
-// Delete removes a VIF from the set
+// Delete removes a VIF from the set.
func (s *Set) Delete(vif *VIF) {
vif.removeSet(s)
addr := vif.conn.RemoteAddr()
diff --git a/services/identity/internal/caveats/browser_caveat_selector.go b/services/identity/internal/caveats/browser_caveat_selector.go
index 792f4b2..37e1079 100644
--- a/services/identity/internal/caveats/browser_caveat_selector.go
+++ b/services/identity/internal/caveats/browser_caveat_selector.go
@@ -28,7 +28,7 @@
func (s *browserCaveatSelector) Render(blessingExtension, state, redirectURL string, w http.ResponseWriter, r *http.Request) error {
tmplargs := struct {
- Extension, Macaroon, MacaroonURL, AssetsPrefix, BlessingName string
+ Email, Macaroon, MacaroonURL, AssetsPrefix, BlessingName string
}{blessingExtension, state, redirectURL, s.assetsPrefix, s.blessingName}
w.Header().Set("Context-Type", "text/html")
if err := templates.SelectCaveats.Execute(w, tmplargs); err != nil {
diff --git a/services/identity/internal/server/identityd.go b/services/identity/internal/server/identityd.go
index 80c8f8c..9a34e15 100644
--- a/services/identity/internal/server/identityd.go
+++ b/services/identity/internal/server/identityd.go
@@ -188,6 +188,7 @@
GoogleServers, DischargeServers []string
ListBlessingsRoute string
AssetsPrefix string
+ Email string
}{
Self: principal.BlessingStore().Default(),
GoogleServers: args.GoogleServers,
diff --git a/services/identity/internal/templates/caveats.go b/services/identity/internal/templates/caveats.go
index 047de74..72caced 100644
--- a/services/identity/internal/templates/caveats.go
+++ b/services/identity/internal/templates/caveats.go
@@ -11,11 +11,119 @@
var selectCaveats = template.Must(template.New("bless").Parse(`<!doctype html>
<html>
<head>
- {{template "head" .}}
- <script src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.7.0/moment.min.js"></script>
- <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
-
<title>Add Blessing - Vanadium Identity Provider</title>
+
+ {{template "head" .}}
+
+</head>
+
+<body class="identityprovider-layout">
+
+ <header>
+ <nav class="left">
+ <a href="#" class="logo">Vanadium</a>
+ <span class="service-name">Identity Provider</span>
+ </nav>
+ <nav class="right">
+ <a href="#">{{.Email}}</a>
+ </nav>
+ </header>
+
+ <main class="add-blessing">
+
+ <form method="POST" id="caveats-form" name="input"
+ action="{{.MacaroonURL}}" role="form">
+ <input type="text" class="hidden" name="macaroon" value="{{.Macaroon}}">
+
+ <h1 class="page-head">Add blessing</h1>
+ <p>
+ This blessing allows the Vanadium Identity Provider to authorize your
+ application's credentials and provides your application access to the
+ data associated with your Google Account. Blessing names contain the
+ email address associated with your Google Account, and will be visible
+ to peers you connect to.
+ </p>
+
+ <div class="note">
+ <p>
+ <strong>
+ Using Vanadium in production applications is discouraged at this
+ time.</strong><br>
+ During this preview, the
+ <a href="https://v.io/glossary.html#blessing-root" target="_">
+ blessing root
+ </a>
+ may change without notice.
+ </p>
+ </div>
+
+ <label for="blessingExtension">Blessing name</label>
+ <div class="value">
+ {{.BlessingName}}/{{.Email}}/
+ <input name="blessingExtension" type="text" placeholder="extension">
+ <input type="hidden" id="timezoneOffset" name="timezoneOffset">
+ </div>
+
+ <label>Caveats</label>
+ <div class="caveatRow">
+ <div class="define-caveat">
+ <span class="selected value RevocationCaveatSelected">
+ Active until revoked
+ </span>
+ <span class="selected value ExpiryCaveatSelected hidden">
+ Expires on
+ </span>
+ <span class="selected value MethodCaveatSelected hidden">
+ Allowed methods are
+ </span>
+ <span class="selected value PeerBlessingsCaveatSelected hidden">
+ Allowed peers are
+ </span>
+
+ <select name="caveat" class="caveats hidden">
+ <option name="RevocationCaveat" value="RevocationCaveat"
+ class="cavOption">Active until revoked</option>
+ <option name="ExpiryCaveat" value="ExpiryCaveat"
+ class="cavOption">Expires on</option>
+ <option name="MethodCaveat" value="MethodCaveat"
+ class="cavOption">Allowed methods are</option>
+ <option name="PeerBlessingsCaveat" value="PeerBlessingsCaveat"
+ class="cavOption">Allowed peers are</option>
+ </select>
+
+ <input type="text" class="caveatInput hidden"
+ id="RevocationCaveat" name="RevocationCaveat">
+ <input type="datetime-local" class="caveatInput expiry hidden"
+ id="ExpiryCaveat" name="ExpiryCaveat">
+ <input type="text" class="caveatInput hidden"
+ id="MethodCaveat" name="MethodCaveat"
+ placeholder="comma-separated method list">
+ <input type="text" class="caveatInput hidden"
+ id="PeerBlessingsCaveat" name="PeerBlessingsCaveat"
+ placeholder="comma-separated blessing list">
+ </div>
+ <div class="add-caveat">
+ <a href="#" class="addMore">Add more caveats</a>
+ </div>
+ </div>
+
+ <div class="action-buttons">
+ <button class="button-tertiary" id="cancel" type="button">Cancel</button>
+ <button class="button-primary" type="submit">Bless</button>
+ </div>
+
+ <p class="disclaimer-text">
+ By clicking "Bless", you agree to the Google
+ <a href="https://www.google.com/intl/en/policies/terms/">General Terms of Service</a>,
+ <a href="https://developers.google.com/terms/">APIs Terms of Service</a>,
+ and <a href="https://www.google.com/intl/en/policies/privacy/">Privacy Policy</a>
+ </p>
+ </form>
+ </main>
+
+ <script src="{{.AssetsPrefix}}/identity/moment.js"></script>
+ <script src="{{.AssetsPrefix}}/identity/jquery.js"></script>
+
<script>
$(document).ready(function() {
var numCaveats = 1;
@@ -125,110 +233,5 @@
});
});
</script>
-</head>
-
-<body class="identityprovider-layout">
-
- <header>
- <nav class="left">
- <a href="#" class="logo">Vanadium</a>
- <span class="service-name">Identity Provider</span>
- </nav>
- <nav class="right">
- <a href="#">{{.Extension}}</a>
- </nav>
- </header>
-
- <main class="add-blessing">
- <form method="POST" id="caveats-form" name="input"
- action="{{.MacaroonURL}}" role="form">
- <input type="text" class="hidden" name="macaroon" value="{{.Macaroon}}">
-
- <h1 class="page-head">Add blessing</h1>
- <p>
- This blessing allows the Vanadium Identity Provider to authorize your
- application's credentials and provides your application access to the
- data associated with your Google Account. Blessing names contain the
- email address associated with your Google Account, and will be visible
- to peers you connect to.
- </p>
-
- <div class="note">
- <p>
- <strong>
- Using Vanadium in production applications is discouraged at this
- time.</strong><br>
- During this preview, the
- <a href="https://v.io/glossary.html#blessing-root" target="_blank">
- blessing root
- </a>
- may change without notice.
- </p>
- </div>
-
- <label for="blessingExtension">Blessing name</label>
- <div class="value">
- {{.BlessingName}}/{{.Extension}}/
- <input name="blessingExtension" type="text" placeholder="extension">
- <input type="hidden" id="timezoneOffset" name="timezoneOffset">
- </div>
-
- <label>Caveats</label>
- <div class="caveatRow">
- <div class="define-caveat">
- <span class="selected value RevocationCaveatSelected">
- Active until revoked
- </span>
- <span class="selected value ExpiryCaveatSelected hidden">
- Expires on
- </span>
- <span class="selected value MethodCaveatSelected hidden">
- Allowed methods are
- </span>
- <span class="selected value PeerBlessingsCaveatSelected hidden">
- Allowed peers are
- </span>
-
- <select name="caveat" class="caveats hidden">
- <option name="RevocationCaveat" value="RevocationCaveat"
- class="cavOption">Active until revoked</option>
- <option name="ExpiryCaveat" value="ExpiryCaveat"
- class="cavOption">Expires on</option>
- <option name="MethodCaveat" value="MethodCaveat"
- class="cavOption">Allowed methods are</option>
- <option name="PeerBlessingsCaveat" value="PeerBlessingsCaveat"
- class="cavOption">Allowed peers are</option>
- </select>
-
- <input type="text" class="caveatInput hidden"
- id="RevocationCaveat" name="RevocationCaveat">
- <input type="datetime-local" class="caveatInput expiry hidden"
- id="ExpiryCaveat" name="ExpiryCaveat">
- <input type="text" class="caveatInput hidden"
- id="MethodCaveat" name="MethodCaveat"
- placeholder="comma-separated method list">
- <input type="text" class="caveatInput hidden"
- id="PeerBlessingsCaveat" name="PeerBlessingsCaveat"
- placeholder="comma-separated blessing list">
- </div>
- <div class="add-caveat">
- <a href="#" class="addMore">Add more caveats</a>
- </div>
- </div>
-
- <div class="action-buttons">
- <button class="button-tertiary" id="cancel" type="button">Cancel</button>
- <button class="button-primary" type="submit">Bless</button>
- </div>
-
- <p class="disclaimer-text">
- By clicking "Bless", you agree to the Google
- <a href="https://www.google.com/intl/en/policies/terms/">General Terms of Service</a>,
- <a href="https://developers.google.com/terms/">APIs Terms of Service</a>,
- and <a href="https://www.google.com/intl/en/policies/privacy/">Privacy Policy</a>
- </p>
- </form>
- </main>
-
</body>
</html>`))
diff --git a/services/identity/internal/templates/head.go b/services/identity/internal/templates/head.go
deleted file mode 100644
index 609deba..0000000
--- a/services/identity/internal/templates/head.go
+++ /dev/null
@@ -1,46 +0,0 @@
-// Copyright 2015 The Vanadium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-package templates
-
-var headPartial = `{{define "head"}}
- <meta
- name="viewport"
- content="width=device-width,
- initial-scale=1,
- maximum-scale=1,
- user-scalable=no,
- minimal-ui">
- <meta
- name="apple-mobile-web-app-capable"
- content="yes">
-
- <meta
- name="apple-mobile-web-app-status-bar-style"
- content="black">
-
-
- <link href='//fonts.googleapis.com/css?family=Source+Code+Pro:400,500|Roboto:500,400italic,300,500italic,300italic,400'
- rel='stylesheet'
- type='text/css'>
-
- <link rel="stylesheet" href="{{.AssetsPrefix}}/identity.css">
-
- <link rel="apple-touch-icon" sizes="57x57" href="{{.AssetsPrefix}}/favicons/apple-touch-icon-57x57.png">
- <link rel="apple-touch-icon" sizes="114x114" href="{{.AssetsPrefix}}/favicons/apple-touch-icon-114x114.png">
- <link rel="apple-touch-icon" sizes="72x72" href="{{.AssetsPrefix}}/favicons/apple-touch-icon-72x72.png">
- <link rel="apple-touch-icon" sizes="144x144" href="{{.AssetsPrefix}}/favicons/apple-touch-icon-144x144.png">
- <link rel="apple-touch-icon" sizes="60x60" href="{{.AssetsPrefix}}/favicons/apple-touch-icon-60x60.png">
- <link rel="apple-touch-icon" sizes="120x120" href="{{.AssetsPrefix}}/favicons/apple-touch-icon-120x120.png">
- <link rel="apple-touch-icon" sizes="76x76" href="{{.AssetsPrefix}}/favicons/apple-touch-icon-76x76.png">
- <link rel="apple-touch-icon" sizes="152x152" href="{{.AssetsPrefix}}/favicons/apple-touch-icon-152x152.png">
- <link rel="apple-touch-icon" sizes="180x180" href="{{.AssetsPrefix}}/favicons/apple-touch-icon-180x180.png">
- <link rel="icon" type="image/png" href="{{.AssetsPrefix}}/favicons/favicon-192x192.png" sizes="192x192">
- <link rel="icon" type="image/png" href="{{.AssetsPrefix}}/favicons/favicon-160x160.png" sizes="160x160">
- <link rel="icon" type="image/png" href="{{.AssetsPrefix}}/favicons/favicon-96x96.png" sizes="96x96">
- <link rel="icon" type="image/png" href="{{.AssetsPrefix}}/favicons/favicon-16x16.png" sizes="16x16">
- <link rel="icon" type="image/png" href="{{.AssetsPrefix}}/favicons/favicon-32x32.png" sizes="32x32">
- <meta name="msapplication-TileColor" content="#da532c">
- <meta name="msapplication-TileImage" content="{{.AssetsPrefix}}/favicons/mstile-144x144.png">
-{{end}}`
diff --git a/services/identity/internal/templates/home.go b/services/identity/internal/templates/home.go
index 5ab6136..03d8d67 100644
--- a/services/identity/internal/templates/home.go
+++ b/services/identity/internal/templates/home.go
@@ -6,91 +6,31 @@
import "html/template"
-var Home = template.Must(home.Parse(headPartial))
+var Home = template.Must(homeWithHeader.Parse(sidebarPartial))
+var homeWithHead = template.Must(home.Parse(headPartial))
+var homeWithHeader = template.Must(homeWithHead.Parse(headerPartial))
var home = template.Must(template.New("main").Parse(`<!doctype html>
<html>
<head>
- {{template "head" .}}
<title>Vanadium Identity Provider</title>
+ {{template "head" .}}
</head>
-<body class="home-layout">
-<main>
-<section class="intro">
- <div class="intro-container">
- <h1 class="head">
- Vanadium Identity Provider
- </h1>
-
- <h3>
- This is a Vanadium Identity Provider that provides blessings with the
- name prefix {{.Self}}.
- </h3>
-
- <div class="buttons grid">
- <a href="/auth/google/{{.ListBlessingsRoute}}" class="button-passive cell">
- Your Blessings
+<body class="identityprovider-layout">
+ {{template "header" .}}
+ <main>
+ <h1 class="page-head">Authorize Vanadium apps with Google</h1>
+ <p>
+ The Vanadium Identity Provider authorizes Vanadium blessings based on your Google Account.<br>
+ <a href="http://v.io/glossary.html#identity-provider">Learn more</a>
+ </p>
+ <p>
+ <a href="/auth/google/{{.ListBlessingsRoute}}" class="button-passive">
+ Show blessings
</a>
- </div>
- </div>
-</section>
-
-<section class="mission">
- <div class="grid">
- <div class="cell">
- <h2>Public Key</h2>
- <p>
- The public key of this provider is <code>{{.Self.PublicKey}}</code>.</br>
- The root names and public key (in DER encoded <a href="http://en.wikipedia.org/wiki/X.690#DER_encoding">format</a>)
- are available in a <a class="btn btn-xs btn-primary" href="/auth/blessing-root">JSON</a> object.
- </p>
- </div>
- {{if .GoogleServers}}
- <div class="cell">
- <h2>Blessings</h2>
- <p>
- Blessings (using Google OAuth to fetch an email address) are provided via
- Vanadium RPCs to: <code>{{range .GoogleServers}}{{.}}{{end}}</code>
- </p>
- </div>
- {{end}}
- </div>
-</section>
-
-<section class="mission">
- <div class="grid">
- {{if .ListBlessingsRoute}}
- <div class="cell">
- <h2>Blessings Log</h2>
- <p>
- You can <a class="btn btn-xs btn-primary" href="/auth/google/{{.ListBlessingsRoute}}">enumerate</a>
- blessings provided with your email address.
- </p>
- </div>
- {{end}}
- {{if .DischargeServers}}
- <div class="cell">
- <h2>Discharges</h2>
- <p>
- RevocationCaveat Discharges are provided via Vanadium RPCs to:
- <code>{{range .DischargeServers}}{{.}}{{end}}</code>
- </p>
- </div>
- {{end}}
- </div>
-</section>
-
-<footer>
- <nav class="main">
- <a href="https://github.com/vanadium/issues/issues/new?labels=Area:%20Website">Site Feedback</a>
- </nav>
-
- <nav class="social">
- <a href="https://github.com/vanadium" class="icon-github"></a>
- <a href="https://twitter.com/vdotio" class="icon-twitter"></a>
- </nav>
-</footer>
-</main>
+ </p>
+ {{template "sidebar" .}}
+ </main>
</body>
</html>`))
diff --git a/services/identity/internal/templates/list_blessings.go b/services/identity/internal/templates/list_blessings.go
index 8b26163..944ba97 100644
--- a/services/identity/internal/templates/list_blessings.go
+++ b/services/identity/internal/templates/list_blessings.go
@@ -6,25 +6,89 @@
import "html/template"
-var ListBlessings = template.Must(listBlessings.Parse(headPartial))
+var ListBlessings = template.Must(listWithHeader.Parse(sidebarPartial))
+var listWithHead = template.Must(listBlessings.Parse(headPartial))
+var listWithHeader = template.Must(listWithHead.Parse(headerPartial))
var listBlessings = template.Must(template.New("auditor").Parse(`<!doctype html>
<html>
<head>
+ <title>Blessings for {{.Email}} - Vanadium Identity Provider</title>
{{template "head" .}}
- <title>Blessings for {{.Email}}</title>
- <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/css/toastr.min.css">
- <script src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.7.0/moment.min.js"></script>
- <script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
- <script src="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/js/toastr.min.js"></script>
+ <link rel="stylesheet" href="{{.AssetsPrefix}}/identity/toastr.css">
+</head>
+<body class="identityprovider-layout">
+ {{template "header" .}}
+ <main>
+ <h1 class="page-head">Authorize Vanadium apps with Google</h1>
+ <p>
+ The Vanadium Identity Provider authorizes Vanadium blessings based on your Google Account.<br>
+ <a href="http://v.io/glossary.html#identity-provider">Learn more</a>
+ </p>
+
+ {{range .Log}}
+ {{if .Error}}
+ <h1>Error</h1>
+ <p>
+ Failed to read audit log.<br>
+ {{.Error}}
+ </p>
+ {{else}}
+ <div class="blessings-list">
+ <div class="blessings-header">
+ <h1>Your blessings</h1>
+ <h5>Issued</h5>
+ <h5>Revoked</h5>
+ </div>
+
+ <div class="blessings-item">
+ <div class="blessing-details">
+ <h3>{{.Blessed}}</h3>
+ <p>
+ <b>Public Key</b><br>
+ {{.Blessed.PublicKey}}
+ </p>
+ <p class="blessing-caveats">
+ <b>Caveats</b><br>
+ {{range $index, $cav := .Caveats}}
+ {{if ne $index 0}}
+ {{end}}
+ {{$cav}}<br>
+ {{end}}
+ </p>
+ </div>
+
+ <div class="blessing-issued unixtime" data-unixtime={{.Timestamp.Unix}}>{{.Timestamp.String}}</div>
+
+ <div class="blessing-revoked">
+ {{ if .Token }}
+ <button class="revoke button-passive" value="{{.Token}}">Revoke</button>
+ {{ else if not .RevocationTime.IsZero }}
+ <p class="unixtime" data-unixtime={{.RevocationTime.Unix}}>{{.RevocationTime.String}}</p>
+ {{ end }}
+ </div>
+ </div>
+ {{end}}
+ {{else}}
+ <h1>Your blessings</h1>
+ <p>
+ <a href="http://v.io/installation">Install Vanadium</a> to set up your first blessing.
+ </p>
+ {{end}}
+ {{template "sidebar" .}}
+ </main>
+
+ <script src="{{.AssetsPrefix}}/identity/toastr.js"></script>
+ <script src="{{.AssetsPrefix}}/identity/moment.js"></script>
+ <script src="{{.AssetsPrefix}}/identity/jquery.js"></script>
<script>
function setTimeText(elem) {
var timestamp = elem.data("unixtime");
var m = moment(timestamp*1000.0);
var style = elem.data("style");
if (style === "absolute") {
- elem.html("<a href='#'>" + m.format("dd, MMM Do YYYY, h:mm:ss a") + "</a>");
+ elem.html("<a href='#'>" + m.format("MMM DD, YYYY h:mm:ss a") + "</a>");
elem.data("style", "fromNow");
} else {
elem.html("<a href='#'>" + m.fromNow() + "</a>");
@@ -53,7 +117,7 @@
failMessage(revokeButton);
return;
}
- revokeButton.replaceWith("<div>Just Revoked!</div>");
+ revokeButton.replaceWith("<div>Revoked just now</div>");
}).fail(function(xhr, textStatus){
failMessage(revokeButton);
console.error('Bad request: %s', status, xhr)
@@ -66,100 +130,9 @@
$(this).addClass("bg-danger");
});
toastr.options.closeButton = true;
- toastr.error('Unable to revoke identity!', 'Error!')
+ toastr.error('Unable to revoke identity', 'Error')
}
</script>
-</head>
-<body class="default-layout">
- <header>
- <nav class="left">
- <a href="#" class="logo">Vanadium</a>
- </nav>
-
- <nav class="main">
- <a href="#">Blessing Log</a>
- </nav>
-
- <nav class="right">
- <a href="#">{{.Email}}</a>
- </nav>
- </header>
-
- <main style="margin-left: 0px; max-width: 100%;">
-
- <!-- Begin ID Server information -->
- <div class="grid">
- <div class="cell">
- <h2>Public Key</h2>
- <p>
- The public key of this provider is <code>{{.Self.PublicKey}}</code>.</br>
- The root names and public key (in DER encoded <a href="http://en.wikipedia.org/wiki/X.690#DER_encoding">format</a>)
- are available in a <a class="btn btn-xs btn-primary" href="/auth/blessing-root">JSON</a> object.
- </p>
- </div>
- {{if .GoogleServers}}
- <div class="cell">
- <h2>Blessings</h2>
- <p>
- Blessings (using Google OAuth to fetch an email address) are provided via
- Vanadium RPCs to: <code>{{range .GoogleServers}}{{.}}{{end}}</code>
- </p>
- </div>
- {{end}}
- {{if .DischargeServers}}
- <div class="cell">
- <h2>Discharges</h2>
- <p>
- RevocationCaveat Discharges are provided via Vanadium RPCs to:
- <code>{{range .DischargeServers}}{{.}}{{end}}</code>
- </p>
- </div>
- {{end}}
- </div>
- <!-- End ID Server information -->
-
- <table class="blessing-table">
- <tr>
- <td>Blessed as</td>
- <td>Public Key</td>
- <td>Issued</td>
- <td class="td-wide">Caveats</td>
- <td>Revoked</td>
- </tr>
- {{range .Log}}
- {{if .Error}}
- <tr class="">
- <td colspan="5">Failed to read audit log: Error: {{.Error}}</td>
- </tr>
- {{else}}
- <tr>
- <td>{{.Blessed}}</td>
- <td>{{.Blessed.PublicKey}}</td>
- <td><div class="unixtime" data-unixtime={{.Timestamp.Unix}}>{{.Timestamp.String}}</div></td>
- <td class="td-wide">
- {{range $index, $cav := .Caveats}}
- {{if ne $index 0}}
- <hr>
- {{end}}
- {{$cav}}</br>
- {{end}}
- </td>
- <td>
- {{ if .Token }}
- <button class="revoke button-passive" value="{{.Token}}">Revoke</button>
- {{ else if not .RevocationTime.IsZero }}
- <div class="unixtime" data-unixtime={{.RevocationTime.Unix}}>{{.RevocationTime.String}}</div>
- {{ end }}
- </td>
- </tr>
- {{end}}
- {{else}}
- <tr>
- <td colspan=5>No blessings issued</td>
- </tr>
- {{end}}
- </table>
- </main>
</body>
</html>`))
diff --git a/services/identity/internal/templates/partials.go b/services/identity/internal/templates/partials.go
new file mode 100644
index 0000000..3594acf
--- /dev/null
+++ b/services/identity/internal/templates/partials.go
@@ -0,0 +1,116 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package templates
+
+var headPartial = `{{define "head"}}
+ <meta
+ name="viewport"
+ content="width=device-width,
+ initial-scale=1,
+ maximum-scale=1,
+ user-scalable=no,
+ minimal-ui">
+ <meta
+ name="apple-mobile-web-app-capable"
+ content="yes">
+
+ <meta
+ name="apple-mobile-web-app-status-bar-style"
+ content="black">
+
+
+ <link href='//fonts.googleapis.com/css?family=Source+Code+Pro:400,500|Roboto:500,400italic,300,500italic,300italic,400'
+ rel='stylesheet'
+ type='text/css'>
+
+ <link rel="stylesheet" href="{{.AssetsPrefix}}/identity.css">
+
+ <link rel="apple-touch-icon" sizes="57x57" href="{{.AssetsPrefix}}/favicons/apple-touch-icon-57x57.png">
+ <link rel="apple-touch-icon" sizes="114x114" href="{{.AssetsPrefix}}/favicons/apple-touch-icon-114x114.png">
+ <link rel="apple-touch-icon" sizes="72x72" href="{{.AssetsPrefix}}/favicons/apple-touch-icon-72x72.png">
+ <link rel="apple-touch-icon" sizes="144x144" href="{{.AssetsPrefix}}/favicons/apple-touch-icon-144x144.png">
+ <link rel="apple-touch-icon" sizes="60x60" href="{{.AssetsPrefix}}/favicons/apple-touch-icon-60x60.png">
+ <link rel="apple-touch-icon" sizes="120x120" href="{{.AssetsPrefix}}/favicons/apple-touch-icon-120x120.png">
+ <link rel="apple-touch-icon" sizes="76x76" href="{{.AssetsPrefix}}/favicons/apple-touch-icon-76x76.png">
+ <link rel="apple-touch-icon" sizes="152x152" href="{{.AssetsPrefix}}/favicons/apple-touch-icon-152x152.png">
+ <link rel="apple-touch-icon" sizes="180x180" href="{{.AssetsPrefix}}/favicons/apple-touch-icon-180x180.png">
+ <link rel="icon" type="image/png" href="{{.AssetsPrefix}}/favicons/favicon-192x192.png" sizes="192x192">
+ <link rel="icon" type="image/png" href="{{.AssetsPrefix}}/favicons/favicon-160x160.png" sizes="160x160">
+ <link rel="icon" type="image/png" href="{{.AssetsPrefix}}/favicons/favicon-96x96.png" sizes="96x96">
+ <link rel="icon" type="image/png" href="{{.AssetsPrefix}}/favicons/favicon-16x16.png" sizes="16x16">
+ <link rel="icon" type="image/png" href="{{.AssetsPrefix}}/favicons/favicon-32x32.png" sizes="32x32">
+ <meta name="msapplication-TileColor" content="#da532c">
+ <meta name="msapplication-TileImage" content="{{.AssetsPrefix}}/favicons/mstile-144x144.png">
+{{end}}`
+
+var headerPartial = `{{define "header"}}
+ <header>
+ <nav class="left">
+ <a href="#" class="logo">Vanadium</a>
+ <span class="service-name">Identity Provider</span>
+ </nav>
+ <nav class="right">
+ {{if .Email}}
+ <a href="#">{{.Email}}</a>
+ {{end}}
+ </nav>
+ </header>
+{{end}}`
+
+var sidebarPartial = `{{define "sidebar"}}
+<section class="provider-info">
+ <div class="provider-info-section">
+ <h5>Root name</h5>
+ <span class="provider-address">
+ dev.v.io/root
+ </span>
+
+ <h5>Public key</h5>
+ <span class="provider-address">
+ {{.Self.PublicKey}}
+ </span>
+
+ <p>
+ Get this provider’s root name and public key as a <a
+ href="http://en.wikipedia.org/wiki/X.690#DER_encoding" target="_blank">
+ DER</a>-encoded <a href="/auth/blessing-root" target="_blank">
+ JSON object</a>.
+ </p>
+ </div>
+
+ {{if .GoogleServers}}
+ <div class="provider-info-section">
+ <h5>Blessings</h5>
+ <p>
+ Provided via Vanadium RPC to:
+ <span class="provider-address">
+ {{range .GoogleServers}}{{.}}{{end}}
+ </span>
+ </p>
+ </div>
+ {{end}}
+
+ {{if .DischargeServers}}
+ <div class="provider-info-section">
+ <h5>Discharges</h5>
+ <p>
+ Provided via Vanadium RPC to:
+ <span class="provider-address">
+ {{range .DischargeServers}}{{.}}{{end}}
+ </span>
+ </p>
+ </div>
+ {{end}}
+
+ <div class="provider-info-section">
+ <h5>Learn more</h5>
+ <p>
+ Vanadium Concepts: <a href="">Security</a><br>
+ Tutorial: <a href="">Principals and Blessings</a><br>
+ Tutorial: <a href="">Third Party Caveats</a><br>
+ </p>
+ </div>
+</section>
+{{end}}`