Asim Shankar | 6c247c4 | 2014-09-29 17:51:53 -0700 | [diff] [blame] | 1 | package audit_test |
| 2 | |
| 3 | import ( |
| 4 | "crypto/ecdsa" |
| 5 | "crypto/elliptic" |
| 6 | "crypto/rand" |
| 7 | "errors" |
| 8 | "reflect" |
| 9 | "strings" |
| 10 | "testing" |
| 11 | "time" |
| 12 | |
Jiri Simsa | 764efb7 | 2014-12-25 20:57:03 -0800 | [diff] [blame] | 13 | "v.io/core/veyron/security/audit" |
| 14 | "v.io/core/veyron2/security" |
Asim Shankar | 6c247c4 | 2014-09-29 17:51:53 -0700 | [diff] [blame] | 15 | ) |
| 16 | |
| 17 | func TestAuditingPrincipal(t *testing.T) { |
| 18 | var ( |
| 19 | thirdPartyCaveat, discharge = newThirdPartyCaveatAndDischarge(t) |
| 20 | wantErr = errors.New("call failed") // The error returned by call calls to mockID operations |
| 21 | |
| 22 | mockP = new(mockPrincipal) |
| 23 | auditor = new(mockAuditor) |
| 24 | p = audit.NewPrincipal(mockP, auditor) |
| 25 | ) |
| 26 | tests := []struct { |
| 27 | Method string |
| 28 | Args V |
| 29 | Result interface{} // Result returned by the Method call. |
| 30 | AuditResult bool // If true, Result should appear in the audit log. If false, it should not. |
| 31 | }{ |
| 32 | {"BlessSelf", V{"self"}, newBlessing(t, "blessing"), true}, |
| 33 | {"Bless", V{newPrincipal(t).PublicKey(), newBlessing(t, "root"), "extension", security.UnconstrainedUse()}, newBlessing(t, "root/extension"), true}, |
| 34 | {"MintDischarge", V{thirdPartyCaveat, security.UnconstrainedUse()}, discharge, false}, |
| 35 | {"Sign", V{make([]byte, 10)}, security.Signature{R: []byte{1}, S: []byte{1}}, false}, |
| 36 | } |
| 37 | for _, test := range tests { |
| 38 | // Test1: If the underlying operation fails, the error should be returned and nothing should be audited. |
| 39 | mockP.NextError = wantErr |
| 40 | results, err := call(p, test.Method, test.Args) |
| 41 | if err != nil { |
| 42 | t.Errorf("failed to invoke p.%v(%#v): %v", test.Method, test.Args, err) |
| 43 | continue |
| 44 | } |
| 45 | if got, ok := results[len(results)-1].(error); !ok || got != wantErr { |
| 46 | t.Errorf("p.%v(%#v) returned (..., %v), want (..., %v)", test.Method, test.Args, got, wantErr) |
| 47 | } |
| 48 | if audited := auditor.Release(); !reflect.DeepEqual(audited, audit.Entry{}) { |
| 49 | t.Errorf("p.%v(%#v) resulted in [%+v] being written to the audit log, nothing should have been", test.Method, test.Args, audited) |
| 50 | } |
| 51 | |
| 52 | // Test2: If the auditor fails, then the operation should fail too. |
| 53 | auditor.NextError = errors.New("auditor failed") |
| 54 | results, err = call(p, test.Method, test.Args) |
| 55 | if err != nil { |
| 56 | t.Errorf("failed to invoke p.%v(%#v): %v", test.Method, test.Args, err) |
| 57 | continue |
| 58 | } |
| 59 | if got, ok := results[len(results)-1].(error); !ok || !strings.HasSuffix(got.Error(), "auditor failed") { |
| 60 | t.Errorf("p.%v(%#v) returned %v when auditor failed, wanted (..., %v)", test.Method, test.Args, results, "... auditor failed") |
| 61 | } |
| 62 | |
| 63 | // Test3: If the underlying operation succeeds, should return the same value and write to the audit log. |
| 64 | now := time.Now() |
| 65 | mockP.NextResult = test.Result |
| 66 | results, err = call(p, test.Method, test.Args) |
| 67 | audited := auditor.Release() |
| 68 | if err != nil { |
| 69 | t.Errorf("failed to invoke p.%v(%#v): %v", test.Method, test.Args, err) |
| 70 | continue |
| 71 | } |
| 72 | if got := results[len(results)-1]; got != nil { |
| 73 | t.Errorf("p.%v(%#v) returned an error: %v", test.Method, test.Args, got) |
| 74 | } |
| 75 | if got := results[0]; !reflect.DeepEqual(got, test.Result) { |
| 76 | t.Errorf("p.%v(%#v) returned %v(%T) want %v(%T)", test.Method, test.Args, got, got, test.Result, test.Result) |
| 77 | } |
| 78 | if audited.Timestamp.Before(now) || audited.Timestamp.IsZero() { |
| 79 | t.Errorf("p.%v(%#v) audited the time as %v, should have been a time after %v", test.Method, test.Args, audited.Timestamp, now) |
| 80 | } |
| 81 | if want := (audit.Entry{ |
| 82 | Method: test.Method, |
| 83 | Arguments: []interface{}(test.Args), |
| 84 | Results: sliceOrNil(test.AuditResult, test.Result), |
| 85 | Timestamp: audited.Timestamp, // Hard to come up with the expected timestamp, relying on sanity check above. |
| 86 | }); !reflect.DeepEqual(audited, want) { |
| 87 | t.Errorf("p.%v(%#v) resulted in [%#v] being audited, wanted [%#v]", test.Method, test.Args, audited, want) |
| 88 | } |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | func TestUnauditedMethodsOnPrincipal(t *testing.T) { |
| 93 | var ( |
| 94 | auditor = new(mockAuditor) |
| 95 | p = newPrincipal(t) |
| 96 | auditedP = audit.NewPrincipal(p, auditor) |
| 97 | ) |
| 98 | blessing, err := p.BlessSelf("self") |
| 99 | if err != nil { |
| 100 | t.Fatal(err) |
| 101 | } |
| 102 | tests := []struct { |
| 103 | Method string |
| 104 | Args V |
| 105 | }{ |
| 106 | {"PublicKey", V{}}, |
| 107 | {"Roots", V{}}, |
| 108 | {"AddToRoots", V{blessing}}, |
| 109 | {"BlessingStore", V{}}, |
| 110 | } |
| 111 | |
| 112 | for _, test := range tests { |
| 113 | want, err := call(p, test.Method, test.Args) |
| 114 | if err != nil { |
| 115 | t.Fatalf("%v: %v", test.Method, err) |
| 116 | } |
| 117 | got, err := call(auditedP, test.Method, test.Args) |
| 118 | if err != nil { |
| 119 | t.Fatalf("%v: %v", test.Method, err) |
| 120 | } |
| 121 | if !reflect.DeepEqual(got, want) { |
| 122 | t.Errorf("Got %v, want %v", got, want) |
| 123 | } |
| 124 | if gotEntry := auditor.Release(); !reflect.DeepEqual(gotEntry, audit.Entry{}) { |
| 125 | t.Errorf("Unexpected entry in audit log: %v", gotEntry) |
| 126 | } |
| 127 | } |
| 128 | } |
| 129 | |
| 130 | type mockPrincipal struct { |
| 131 | NextResult interface{} |
| 132 | NextError error |
| 133 | } |
| 134 | |
| 135 | func (p *mockPrincipal) reset() { |
| 136 | p.NextError = nil |
| 137 | p.NextResult = nil |
| 138 | } |
| 139 | |
| 140 | func (p *mockPrincipal) Bless(security.PublicKey, security.Blessings, string, security.Caveat, ...security.Caveat) (security.Blessings, error) { |
| 141 | defer p.reset() |
| 142 | b, _ := p.NextResult.(security.Blessings) |
| 143 | return b, p.NextError |
| 144 | } |
| 145 | |
| 146 | func (p *mockPrincipal) BlessSelf(string, ...security.Caveat) (security.Blessings, error) { |
| 147 | defer p.reset() |
| 148 | b, _ := p.NextResult.(security.Blessings) |
| 149 | return b, p.NextError |
| 150 | } |
| 151 | |
| 152 | func (p *mockPrincipal) Sign([]byte) (sig security.Signature, err error) { |
| 153 | defer p.reset() |
| 154 | sig, _ = p.NextResult.(security.Signature) |
| 155 | err = p.NextError |
| 156 | return |
| 157 | } |
| 158 | |
| 159 | func (p *mockPrincipal) MintDischarge(security.ThirdPartyCaveat, security.Caveat, ...security.Caveat) (security.Discharge, error) { |
| 160 | defer p.reset() |
| 161 | d, _ := p.NextResult.(security.Discharge) |
| 162 | return d, p.NextError |
| 163 | } |
| 164 | |
gauthamt | f826393 | 2014-12-16 10:59:09 -0800 | [diff] [blame] | 165 | func (p *mockPrincipal) BlessingsByName(name security.BlessingPattern) []security.Blessings { |
| 166 | return nil |
| 167 | } |
| 168 | |
gauthamt | 8dc9a18 | 2015-01-08 18:03:18 -0800 | [diff] [blame] | 169 | func (p *mockPrincipal) BlessingsInfo(b security.Blessings) map[string][]security.Caveat { |
gauthamt | f826393 | 2014-12-16 10:59:09 -0800 | [diff] [blame] | 170 | return nil |
| 171 | } |
| 172 | |
Asim Shankar | 6c247c4 | 2014-09-29 17:51:53 -0700 | [diff] [blame] | 173 | func (p *mockPrincipal) PublicKey() security.PublicKey { return p.NextResult.(security.PublicKey) } |
| 174 | func (p *mockPrincipal) Roots() security.BlessingRoots { return nil } |
| 175 | func (p *mockPrincipal) BlessingStore() security.BlessingStore { return nil } |
| 176 | func (p *mockPrincipal) AddToRoots(b security.Blessings) error { return nil } |
| 177 | |
| 178 | type mockAuditor struct { |
| 179 | LastEntry audit.Entry |
| 180 | NextError error |
| 181 | } |
| 182 | |
| 183 | func (a *mockAuditor) Audit(entry audit.Entry) error { |
| 184 | if a.NextError != nil { |
| 185 | err := a.NextError |
| 186 | a.NextError = nil |
| 187 | return err |
| 188 | } |
| 189 | a.LastEntry = entry |
| 190 | return nil |
| 191 | } |
| 192 | |
| 193 | func (a *mockAuditor) Release() audit.Entry { |
| 194 | entry := a.LastEntry |
| 195 | a.LastEntry = audit.Entry{} |
| 196 | return entry |
| 197 | } |
| 198 | |
| 199 | type V []interface{} |
| 200 | |
| 201 | func call(receiver interface{}, method string, args V) (results []interface{}, err interface{}) { |
| 202 | defer func() { |
| 203 | err = recover() |
| 204 | }() |
| 205 | callargs := make([]reflect.Value, len(args)) |
| 206 | for idx, arg := range args { |
| 207 | callargs[idx] = reflect.ValueOf(arg) |
| 208 | } |
| 209 | callresults := reflect.ValueOf(receiver).MethodByName(method).Call(callargs) |
| 210 | results = make([]interface{}, len(callresults)) |
| 211 | for idx, res := range callresults { |
| 212 | results[idx] = res.Interface() |
| 213 | } |
| 214 | return |
| 215 | } |
| 216 | |
| 217 | func sliceOrNil(include bool, item interface{}) []interface{} { |
| 218 | if item != nil && include { |
| 219 | return []interface{}{item} |
| 220 | } |
| 221 | return nil |
| 222 | } |
| 223 | |
| 224 | func newPrincipal(t *testing.T) security.Principal { |
| 225 | key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) |
| 226 | if err != nil { |
| 227 | t.Fatal(err) |
| 228 | } |
| 229 | signer := security.NewInMemoryECDSASigner(key) |
Asim Shankar | 48bf0e6 | 2014-10-03 16:27:05 -0700 | [diff] [blame] | 230 | p, err := security.CreatePrincipal(signer, nil, nil) |
Asim Shankar | 6c247c4 | 2014-09-29 17:51:53 -0700 | [diff] [blame] | 231 | if err != nil { |
| 232 | t.Fatal(err) |
| 233 | } |
| 234 | return p |
| 235 | } |
| 236 | |
| 237 | func newCaveat(c security.Caveat, err error) security.Caveat { |
| 238 | if err != nil { |
| 239 | panic(err) |
| 240 | } |
| 241 | return c |
| 242 | } |
| 243 | |
| 244 | func newBlessing(t *testing.T, name string) security.Blessings { |
| 245 | b, err := newPrincipal(t).BlessSelf(name) |
| 246 | if err != nil { |
| 247 | t.Fatal(err) |
| 248 | } |
| 249 | return b |
| 250 | } |
| 251 | |
| 252 | func newThirdPartyCaveatAndDischarge(t *testing.T) (security.ThirdPartyCaveat, security.Discharge) { |
| 253 | p := newPrincipal(t) |
| 254 | c, err := security.NewPublicKeyCaveat(p.PublicKey(), "location", security.ThirdPartyRequirements{}, newCaveat(security.MethodCaveat("method"))) |
| 255 | if err != nil { |
| 256 | t.Fatal(err) |
| 257 | } |
| 258 | d, err := p.MintDischarge(c, security.UnconstrainedUse()) |
| 259 | if err != nil { |
| 260 | t.Fatal(err) |
| 261 | } |
| 262 | return c, d |
| 263 | } |