blob: f869b31d13d5e7aa67247582de604b10063e22e2 [file] [log] [blame]
// 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 handlers
import (
"encoding/base64"
"encoding/json"
"net/http"
"strings"
"sync"
"v.io/v23/security"
"v.io/v23/vom"
"v.io/x/ref/services/identity/internal/util"
)
// BlessingRoot is an http.Handler implementation that renders the server's
// blessing names and public key in a json string.
type BlessingRoot struct {
P security.Principal
}
// Cached response so we don't have to bless and encode every time somebody
// hits this route.
var (
cacheMu sync.RWMutex
cachedResponseJson []byte
cachedResponseBase64VOM []byte
)
func (b BlessingRoot) ServeHTTP(w http.ResponseWriter, r *http.Request) {
switch format := r.FormValue("output"); format {
case "base64vom":
b.base64vomResponse(w, r)
default:
b.jsonResponse(w, r)
}
}
func (b BlessingRoot) base64vomResponse(w http.ResponseWriter, r *http.Request) {
cacheMu.RLock()
if cachedResponseBase64VOM != nil {
respondString(w, "text/plain", cachedResponseBase64VOM)
cacheMu.RUnlock()
return
}
cacheMu.RUnlock()
blessings, _ := b.P.BlessingStore().Default()
roots := security.RootBlessings(blessings)
strs := make([]string, len(roots))
for i, r := range roots {
v, err := vom.Encode(r)
if err != nil {
util.HTTPServerError(w, err)
}
strs[i] = base64.URLEncoding.EncodeToString(v)
}
ret := []byte(strings.Join(strs, "\n"))
cacheMu.Lock()
cachedResponseBase64VOM = ret
cacheMu.Unlock()
respondString(w, "text/plain", cachedResponseBase64VOM)
}
func (b BlessingRoot) jsonResponse(w http.ResponseWriter, r *http.Request) {
cacheMu.RLock()
if cachedResponseJson != nil {
respondString(w, "application/json", cachedResponseJson)
cacheMu.RUnlock()
return
}
cacheMu.RUnlock()
// The identity service itself is blessed by a more protected key.
// Use the root certificate as the identity provider.
//
// TODO(ashankar): This is making the assumption that the identity
// service has a single blessing, which may not be true in general.
// Revisit this.
def, _ := b.P.BlessingStore().Default()
name, der, err := util.RootCertificateDetails(def)
if err != nil {
util.HTTPServerError(w, err)
return
}
str := base64.URLEncoding.EncodeToString(der)
// TODO(suharshs): Ideally this struct would be BlessingRootResponse but vdl does
// not currently allow field annotations. Once those are allowed, then use that
// here.
rootInfo := struct {
Names []string `json:"names"`
PublicKey string `json:"publicKey"`
}{
Names: []string{name},
PublicKey: str,
}
res, err := json.Marshal(rootInfo)
if err != nil {
util.HTTPServerError(w, err)
return
}
cacheMu.Lock()
cachedResponseJson = res
cacheMu.Unlock()
respondString(w, "application/json", res)
}
func respondString(w http.ResponseWriter, contentType string, res []byte) {
w.Header().Set("Content-Type", contentType)
w.Write(res)
}