blob: d0955305c48956764dc1f3a31af2a4c46042d804 [file] [log] [blame]
package rt
import (
"errors"
"fmt"
"reflect"
"sync"
"veyron.io/veyron/veyron/security/serialization"
"veyron.io/veyron/veyron2/security"
"veyron.io/veyron/veyron2/vlog"
)
const (
blessingStoreDataFile = "blessingstore.data"
blessingStoreSigFile = "blessingstore.sig"
)
var errStoreAddMismatch = errors.New("blessing's public key does not match store's public key")
type markedBlessings struct {
Blessings security.Blessings
Patterns []security.BlessingPattern
}
type persistentState struct {
// Store contains a set of Blessings marked with a set of BlessingsPatterns.
// These blessings are intended to be shared with peers whose blessings
// match the corresponding pattern. All Blessings in the store must have
// the same public key.
Store []markedBlessings
// Default is the default Blessings to be shared with peers for which
// no other information is available to select blessings.
Default security.Blessings
}
// blessingStore implements security.BlessingStore.
type blessingStore struct {
state persistentState // GUARDED_BY(mu)
publicKey security.PublicKey
dir string
signer serialization.Signer
mu sync.RWMutex
}
func (s *blessingStore) Add(blessings security.Blessings, forPeers security.BlessingPattern) error {
if !reflect.DeepEqual(blessings.PublicKey(), s.publicKey) {
return errStoreAddMismatch
}
if !forPeers.IsValid() {
return fmt.Errorf("%q is an invalid BlessingPattern", forPeers)
}
s.mu.Lock()
defer s.mu.Unlock()
var entry *markedBlessings
for i, mb := range s.state.Store {
if !reflect.DeepEqual(mb.Blessings, blessings) {
continue
}
entry = &s.state.Store[i]
for _, p := range mb.Patterns {
if p == forPeers {
return nil
}
}
break
}
if entry == nil {
s.state.Store = append(s.state.Store, markedBlessings{blessings, []security.BlessingPattern{forPeers}})
} else {
entry.Patterns = append(entry.Patterns, forPeers)
}
if err := s.save(); err != nil {
if entry == nil {
s.state.Store = s.state.Store[:len(s.state.Store)-1]
} else {
entry.Patterns = entry.Patterns[:len(entry.Patterns)-1]
}
return err
}
return nil
}
func (s *blessingStore) ForPeer(peerBlessings ...string) security.Blessings {
s.mu.RLock()
defer s.mu.RUnlock()
var matchingBlessings []security.Blessings
for _, mb := range s.state.Store {
for _, p := range mb.Patterns {
if p.MatchedBy(peerBlessings...) {
matchingBlessings = append(matchingBlessings, mb.Blessings)
break
}
}
}
blessings, err := security.UnionOfBlessings(matchingBlessings...)
if err != nil {
// This case should never be hit.
vlog.Errorf("BlessingStore: %s is broken, could union Blessings obtained from it: %s", s, err)
return nil
}
return blessings
}
func (s *blessingStore) Default() security.Blessings {
s.mu.RLock()
defer s.mu.RUnlock()
if s.state.Default != nil {
return s.state.Default
}
return s.ForPeer()
}
func (s *blessingStore) SetDefault(blessings security.Blessings) error {
s.mu.Lock()
defer s.mu.Unlock()
if !reflect.DeepEqual(blessings.PublicKey(), s.publicKey) {
return errStoreAddMismatch
}
oldDefault := s.state.Default
s.state.Default = blessings
if err := s.save(); err != nil {
s.state.Default = oldDefault
}
return nil
}
func (s *blessingStore) PublicKey() security.PublicKey {
return s.publicKey
}
func (s *blessingStore) String() string {
return fmt.Sprintf("{state: %v, publicKey: %v, dir: %v}", s.state, s.publicKey, s.dir)
}
func (s *blessingStore) save() error {
if (s.signer == nil) && (s.dir == "") {
return nil
}
return encodeAndStore(s.state, s.dir, blessingStoreDataFile, blessingStoreSigFile, s.signer)
}
// NewInMemoryBlessingStore returns an in-memory security.BlessingStore for a
// principal with the provided PublicKey.
//
// The returned BlessingStore is initialized with an empty set of blessings.
func NewInMemoryBlessingStore(publicKey security.PublicKey) security.BlessingStore {
return &blessingStore{
publicKey: publicKey,
}
}
// NewPersistingBlessingStore returns a security.BlessingStore for a principal
// with the provided PublicKey that signs and persists all updates to the
// specified directory. Signing is carried out using the provided signer.
//
// The returned BlessingStore is initialized from the existing data present in
// the directory. The data is verified to have been written by a persisting
// BlessingStore object constructed from the same PublicKey and signer.
//
// Any errors obtained in reading or verifying the data are returned.
func NewPersistingBlessingStore(publicKey security.PublicKey, directory string, signer serialization.Signer) (security.BlessingStore, error) {
if directory == "" || signer == nil {
return nil, errors.New("directory or signer is not specified")
}
s := &blessingStore{
publicKey: publicKey,
dir: directory,
signer: signer,
}
if err := decodeFromStorage(&s.state, s.dir, blessingStoreDataFile, blessingStoreSigFile, s.signer.PublicKey()); err != nil {
return nil, err
}
for _, mb := range s.state.Store {
if !reflect.DeepEqual(mb.Blessings.PublicKey(), publicKey) {
return nil, fmt.Errorf("directory contains Blessings: %v that are not for the provided PublicKey: %v", mb.Blessings, publicKey)
}
}
return s, nil
}