blob: 3ec1f6a3d92ab2de89c9c0edc0fd6f45715be64e [file] [log] [blame]
// Copyright 2016 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 syncbase
import (
"strings"
"v.io/v23"
"v.io/v23/context"
"v.io/v23/discovery"
"v.io/v23/security"
)
const visibilityKey = "vis"
// Discovery implements v.io/v23/discovery.T for syncbase based
// applications.
// TODO(mattr): Actually this is not syncbase specific. At some
// point we should just replace the result of v23.NewDiscovery
// with this.
type Discovery struct {
nhDiscovery discovery.T
// TODO(mattr): Add global discovery.
}
// NewDiscovery creates a new syncbase discovery object.
func NewDiscovery(ctx *context.T) (discovery.T, error) {
nhDiscovery, err := v23.NewDiscovery(ctx)
if err != nil {
return nil, err
}
return &Discovery{nhDiscovery: nhDiscovery}, nil
}
// Scan implements v.io/v23/discovery/T.Scan.
func (d *Discovery) Scan(ctx *context.T, query string) (<-chan discovery.Update, error) {
nhUpdates, err := d.nhDiscovery.Scan(ctx, query)
if err != nil {
return nil, err
}
// Currently setting visibility on the neighborhood discovery
// service turns IBE encryption on. We currently don't have the
// infrastructure support for IBE, so that would make our advertisements
// unreadable by everyone.
// Instead we add the visibility list to the attributes of the advertisement
// and filter on the client side. This is a temporary measure until
// IBE is set up. See v.io/i/1345.
updates := make(chan discovery.Update)
go func() {
for u := range nhUpdates {
patterns := splitPatterns(u.Attribute(visibilityKey))
if len(patterns) > 0 && !matchesPatterns(ctx, patterns) {
continue
}
updates <- update{u}
}
close(updates)
}()
return updates, nil
}
// Advertise implements v.io/v23/discovery/T.Advertise.
func (d *Discovery) Advertise(ctx *context.T, ad *discovery.Advertisement, visibility []security.BlessingPattern) (<-chan struct{}, error) {
// Currently setting visibility on the neighborhood discovery
// service turns IBE encryption on. We currently don't have the
// infrastructure support for IBE, so that would make our advertisements
// unreadable by everyone.
// Instead we add the visibility list to the attributes of the advertisement
// and filter on the client side. This is a temporary measure until
// IBE is set up. See v.io/i/1345.
adCopy := *ad
if len(visibility) > 0 {
adCopy.Attributes = make(discovery.Attributes, len(ad.Attributes)+1)
for k, v := range ad.Attributes {
adCopy.Attributes[k] = v
}
patterns := joinPatterns(visibility)
adCopy.Attributes[visibilityKey] = patterns
}
ch, err := d.nhDiscovery.Advertise(ctx, &adCopy, nil)
ad.Id = adCopy.Id
return ch, err
}
func matchesPatterns(ctx *context.T, patterns []security.BlessingPattern) bool {
p := v23.GetPrincipal(ctx)
blessings := p.BlessingStore().PeerBlessings()
for _, b := range blessings {
names := security.BlessingNames(p, b)
for _, pattern := range patterns {
if pattern.MatchedBy(names...) {
return true
}
}
}
return false
}
// update wraps the discovery.Update to remove the visibility attribute which we add.
type update struct {
discovery.Update
}
func (u update) Attribute(name string) string {
if name == visibilityKey {
return ""
}
return u.Update.Attribute(name)
}
func (u update) Advertisement() discovery.Advertisement {
cp := u.Update.Advertisement()
orig := cp.Attributes
cp.Attributes = make(discovery.Attributes, len(orig))
for k, v := range orig {
if k != visibilityKey {
cp.Attributes[k] = v
}
}
return cp
}
// blessingSeparator is used to join multiple blessings into a
// single string.
// Note that comma cannot appear in blessings, see:
// v.io/v23/security/certificate.go
const blessingsSeparator = ','
// joinPatterns concatenates the elements of a to create a single string.
// The string can be split again with SplitPatterns.
func joinPatterns(a []security.BlessingPattern) string {
if len(a) == 0 {
return ""
}
if len(a) == 1 {
return string(a[0])
}
n := (len(a) - 1)
for i := 0; i < len(a); i++ {
n += len(a[i])
}
b := make([]byte, n)
bp := copy(b, a[0])
for _, s := range a[1:] {
b[bp] = blessingsSeparator
bp++
bp += copy(b[bp:], s)
}
return string(b)
}
// splitPatterns splits BlessingPatterns that were joined with
// JoinBlessingPattern.
func splitPatterns(patterns string) []security.BlessingPattern {
if patterns == "" {
return nil
}
n := strings.Count(patterns, string(blessingsSeparator)) + 1
out := make([]security.BlessingPattern, n)
last, start := 0, 0
for i, r := range patterns {
if r == blessingsSeparator {
out[last] = security.BlessingPattern(patterns[start:i])
last++
start = i + 1
}
}
out[last] = security.BlessingPattern(patterns[start:])
return out
}