blob: 2f86ef4f4a64c4e8cbe030eadb7226475fe21c4f [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 principal
import (
"bytes"
"fmt"
"net/url"
"reflect"
"testing"
"time"
"v.io/v23/context"
"v.io/v23/security"
"v.io/v23/verror"
"v.io/v23/vom"
"v.io/x/ref/test/testutil"
)
func accountBlessing(p security.Principal, name string) security.Blessings {
// Ideally the account blessing would be from a principal representing
// the identity provider but for testing purpose we mock it out using a
// self-blessing.
return blessSelf(p, name)
}
type tester struct {
googleAccount, facebookAccount, origin string
googleBlessings, facebookBlessings security.Blessings
}
func (t *tester) testSetters(m *PrincipalManager) error {
// Test AddAccount.
if err := m.AddAccount(t.googleAccount, t.googleBlessings); err != nil {
return fmt.Errorf("AddAccount(%v, %v) failed: %v", t.googleAccount, t.googleBlessings, err)
}
if err := m.AddAccount(t.facebookAccount, t.facebookBlessings); err != nil {
return fmt.Errorf("AddAccount(%v, %v) failed: %v", t.facebookAccount, t.facebookBlessings, err)
}
// Test AddOrigin.
cav, err := security.NewMethodCaveat("Foo")
if err != nil {
return fmt.Errorf("security.MethodCaveat failed: %v", err)
}
if err := m.AddOrigin(t.origin, t.googleAccount, []security.Caveat{cav}, nil); err != nil {
return fmt.Errorf("AddOrigin failed: %v", err)
}
if err := matchesErrorID(m.AddOrigin(t.origin, "nonExistingAccount", nil, nil), errUnknownAccount.ID); err != nil {
return fmt.Errorf("AddOrigin(..., 'nonExistingAccount', ...): %v", err)
}
return nil
}
func (t *tester) testGetters(m *PrincipalManager) error {
// Test Principal.
pOrigin, err := m.Principal(t.origin)
if err != nil {
return fmt.Errorf("Principal failed: %v", err)
}
bOrigin := pOrigin.BlessingStore().Default()
// Validate the integrity of the bits.
buf := new(bytes.Buffer)
encoder, err := vom.NewEncoder(buf)
if err != nil {
return err
}
if encoder.Encode(bOrigin); err != nil {
return err
}
decoder, err := vom.NewDecoder(buf)
if err != nil {
return err
}
var decoded security.Blessings
if err := decoder.Decode(&decoded); err != nil {
return err
}
if !reflect.DeepEqual(decoded, bOrigin) {
return fmt.Errorf("reflect.DeepEqual(%v, %v) failed after validBlessing", decoded, bOrigin)
}
bnames := func(b security.Blessings, method string) ([]string, []security.RejectedBlessing) {
ctx, cancel := context.RootContext()
defer cancel()
return security.RemoteBlessingNames(ctx, security.NewCall(&security.CallParams{
LocalPrincipal: pOrigin,
RemoteBlessings: b,
Method: method}))
}
// Validate the blessings in various contexts.
want := []string{t.googleAccount + security.ChainSeparator + url.QueryEscape(t.origin)}
if got, _ := bnames(bOrigin, "Foo"); !reflect.DeepEqual(got, want) {
return fmt.Errorf("with method 'Foo', got blessing: %v, want: %v", got, want)
}
if got, _ := bnames(bOrigin, "Bar"); len(got) != 0 {
return fmt.Errorf("with method 'Bar', got blessing: %v, want empty", got)
}
unknownOrigin := "http://unknown.com:80"
_, err = m.Principal(unknownOrigin)
if merr := matchesErrorID(err, verror.ErrNoExist.ID); merr != nil {
return fmt.Errorf("Principal(%v): %v, errorid=%v", unknownOrigin, merr)
}
// Test BlessingsForAccount.
if got, err := m.BlessingsForAccount(t.googleAccount); err != nil || !reflect.DeepEqual(got, t.googleBlessings) {
return fmt.Errorf("BlessingsForAccount(%v): got: %v, %v want: %v, nil", t.googleAccount, got, err, t.googleBlessings)
}
if got, err := m.BlessingsForAccount(t.facebookAccount); err != nil || !reflect.DeepEqual(got, t.facebookBlessings) {
return fmt.Errorf("BlessingsForAccount(%v): got: %v, %v, want: %v, nil", t.facebookAccount, got, err, t.facebookBlessings)
}
nonExistingAccount := "nonExistingAccount"
if got, err := m.BlessingsForAccount(nonExistingAccount); !got.IsZero() {
return fmt.Errorf("BlessingsForAccount(%v): got: %v, want nil", nonExistingAccount, got)
} else if merr := matchesError(err, "unknown account"); merr != nil {
return fmt.Errorf("BlessingsForAccount(%v) returned error: %v", nonExistingAccount, merr)
}
return nil
}
func newTester(root security.Principal) *tester {
googleAccount := "google/alice@gmail.com"
facebookAccount := "facebook/alice@facebook.com"
return &tester{
googleAccount: googleAccount,
facebookAccount: facebookAccount,
origin: "https://sampleapp-1.com:443",
googleBlessings: accountBlessing(root, googleAccount),
facebookBlessings: accountBlessing(root, facebookAccount),
}
}
func TestPrincipalManager(t *testing.T) {
root := testutil.NewPrincipal()
m, err := NewPrincipalManager(root, &InMemorySerializer{})
if err != nil {
t.Fatalf("NewPrincipalManager failed: %v", err)
}
mt := newTester(root)
if err := mt.testSetters(m); err != nil {
t.Fatal(err)
}
if err := mt.testGetters(m); err != nil {
t.Fatal(err)
}
}
func TestPrincipalManagerPersistence(t *testing.T) {
root := testutil.NewPrincipal()
serializer := &InMemorySerializer{}
m, err := NewPrincipalManager(root, serializer)
if err != nil {
t.Fatalf("NewPrincipalManager failed: %v", err)
}
mt := newTester(root)
if err := mt.testSetters(m); err != nil {
t.Fatal(err)
}
if err := mt.testGetters(m); err != nil {
t.Fatal(err)
}
if m, err = NewPrincipalManager(root, serializer); err != nil {
t.Fatalf("NewPrincipalManager failed: %v", err)
}
if err := mt.testGetters(m); err != nil {
t.Fatal(err)
}
}
func TestOriginHasAccount(t *testing.T) {
root := testutil.NewPrincipal()
m, err := NewPrincipalManager(root, &InMemorySerializer{})
if err != nil {
t.Fatalf("NewPrincipalManager failed: %v", err)
}
googleAccount := "fred/jim@gmail.com"
// Test with unknown origin.
unknownOrigin := "http://unknown.com"
if m.OriginHasAccount(unknownOrigin) {
fmt.Errorf("Expected m.OriginHasAccount(%v) to be false but it was true.", unknownOrigin)
}
// Test with no expiration caveat.
origin1 := "http://origin-1.com"
methodCav, err := security.NewMethodCaveat("Foo")
if err != nil {
fmt.Errorf("security.MethodCaveat failed: %v", err)
}
if err := m.AddOrigin(origin1, googleAccount, []security.Caveat{methodCav}, nil); err != nil {
fmt.Errorf("AddOrigin failed: %v", err)
}
if !m.OriginHasAccount(origin1) {
fmt.Errorf("Expected m.OriginHasAccount(%v) to be true but it was false.", origin1)
}
// Test with expiration caveat in the future.
origin2 := "http://origin-2.com"
futureTime := time.Now().Add(5 * time.Minute)
futureExpCav, err := security.NewExpiryCaveat(futureTime)
if err != nil {
fmt.Errorf("security.NewExpiryCaveat(%v) failed: %v", futureTime, err)
}
if err := m.AddOrigin(origin2, googleAccount, []security.Caveat{futureExpCav}, []time.Time{futureTime}); err != nil {
fmt.Errorf("AddOrigin failed: %v", err)
}
if !m.OriginHasAccount(origin2) {
fmt.Errorf("Expected m.OriginHasAccount(%v) to be true but it was false.", origin2)
}
// Test with expiration caveats in the past and future.
origin3 := "http://origin-3.com"
pastTime := time.Now().Add(-5 * time.Minute)
pastExpCav, err := security.NewExpiryCaveat(pastTime)
if err != nil {
fmt.Errorf("security.NewExpiryCaveat(%v) failed: %v", pastTime, err)
}
if err := m.AddOrigin(origin3, googleAccount, []security.Caveat{futureExpCav, pastExpCav}, []time.Time{futureTime, pastTime}); err != nil {
fmt.Errorf("AddOrigin failed: %v", err)
}
if m.OriginHasAccount(origin3) {
fmt.Errorf("Expected m.OriginHasAccount(%v) to be false but it was true.", origin3)
}
}