| // 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 := vom.NewEncoder(buf) |
| if encoder.Encode(bOrigin); err != nil { |
| return err |
| } |
| decoder := vom.NewDecoder(buf) |
| 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", 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, NewInMemorySerializer()) |
| 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 := NewInMemorySerializer() |
| 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, NewInMemorySerializer()) |
| if err != nil { |
| t.Fatalf("NewPrincipalManager failed: %v", err) |
| } |
| |
| googleAccount := "fred/jim@gmail.com" |
| googleBlessings := accountBlessing(root, googleAccount) |
| |
| if err := m.AddAccount(googleAccount, googleBlessings); err != nil { |
| t.Errorf("AddAccount(%v, %v) failed: %v", googleAccount, googleBlessings, err) |
| } |
| |
| // Test with unknown origin. |
| unknownOrigin := "http://unknown.com" |
| if m.OriginHasAccount(unknownOrigin) { |
| t.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 { |
| t.Errorf("security.MethodCaveat failed: %v", err) |
| } |
| |
| if err := m.AddOrigin(origin1, googleAccount, []security.Caveat{methodCav}, nil); err != nil { |
| t.Errorf("AddOrigin failed: %v", err) |
| } |
| |
| if !m.OriginHasAccount(origin1) { |
| t.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 { |
| t.Errorf("security.NewExpiryCaveat(%v) failed: %v", futureTime, err) |
| } |
| |
| if err := m.AddOrigin(origin2, googleAccount, []security.Caveat{futureExpCav}, []time.Time{futureTime}); err != nil { |
| t.Errorf("AddOrigin failed: %v", err) |
| } |
| |
| if !m.OriginHasAccount(origin2) { |
| t.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 { |
| t.Errorf("security.NewExpiryCaveat(%v) failed: %v", pastTime, err) |
| } |
| |
| if err := m.AddOrigin(origin3, googleAccount, []security.Caveat{futureExpCav, pastExpCav}, []time.Time{futureTime, pastTime}); err != nil { |
| t.Errorf("AddOrigin failed: %v", err) |
| } |
| |
| if m.OriginHasAccount(origin3) { |
| t.Errorf("Expected m.OriginHasAccount(%v) to be false but it was true.", origin3) |
| } |
| } |