| // 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 security_test |
| |
| import ( |
| "fmt" |
| "testing" |
| "time" |
| |
| "v.io/v23" |
| "v.io/v23/context" |
| "v.io/v23/rpc" |
| "v.io/v23/security" |
| securitylib "v.io/x/ref/lib/security" |
| _ "v.io/x/ref/runtime/factories/generic" |
| "v.io/x/ref/test" |
| "v.io/x/ref/test/testutil" |
| ) |
| |
| type expiryDischarger struct { |
| durations map[string]time.Duration |
| } |
| |
| func (ed *expiryDischarger) Discharge(ctx *context.T, call rpc.ServerCall, cav security.Caveat, _ security.DischargeImpetus) (security.Discharge, error) { |
| tp := cav.ThirdPartyDetails() |
| if tp == nil { |
| return security.Discharge{}, fmt.Errorf("discharger: %v does not represent a third-party caveat", cav) |
| } |
| if err := tp.Dischargeable(ctx, call.Security()); err != nil { |
| return security.Discharge{}, fmt.Errorf("third-party caveat %v cannot be discharged for this context: %v", cav, err) |
| } |
| duration := ed.durations[tp.ID()] |
| if duration == 0 { |
| duration = time.Minute |
| } |
| expiry, err := security.NewExpiryCaveat(time.Now().Add(duration)) |
| if err != nil { |
| return security.Discharge{}, fmt.Errorf("failed to create an expiration on the discharge: %v", err) |
| } |
| d, err := call.Security().LocalPrincipal().MintDischarge(cav, expiry) |
| if err != nil { |
| return security.Discharge{}, err |
| } |
| ctx.Infof("got discharge on sever %#v", d) |
| return d, nil |
| } |
| |
| func inRange(v, start, end time.Time) error { |
| if v.Before(start) || v.After(end) { |
| return fmt.Errorf("Got %v, wanted value in (%v, %v)", v, start, end) |
| } |
| return nil |
| } |
| |
| func TestPrepareDischarges(t *testing.T) { |
| ctx, shutdown := test.V23InitWithMounttable() |
| defer shutdown() |
| |
| pclient := testutil.NewPrincipal("client") |
| cctx, err := v23.WithPrincipal(ctx, pclient) |
| if err != nil { |
| t.Fatal(err) |
| } |
| pdischarger := testutil.NewPrincipal("discharger") |
| dctx, err := v23.WithPrincipal(ctx, pdischarger) |
| if err != nil { |
| t.Fatal(err) |
| } |
| defaultBlessings := func(p security.Principal) security.Blessings { |
| d, _ := p.BlessingStore().Default() |
| return d |
| } |
| security.AddToRoots(pclient, defaultBlessings(pdischarger)) |
| security.AddToRoots(pclient, defaultBlessings(v23.GetPrincipal(ctx))) |
| security.AddToRoots(pdischarger, defaultBlessings(pclient)) |
| security.AddToRoots(pdischarger, defaultBlessings(v23.GetPrincipal(ctx))) |
| |
| expcav, err := security.NewExpiryCaveat(time.Now().Add(time.Hour)) |
| if err != nil { |
| t.Fatal(err) |
| } |
| expiryDur := 100 * time.Millisecond |
| tpcav, err := security.NewPublicKeyCaveat( |
| pdischarger.PublicKey(), |
| "discharger", |
| security.ThirdPartyRequirements{}, |
| expcav) |
| if err != nil { |
| t.Fatal(err) |
| } |
| tpcavLong, err := security.NewPublicKeyCaveat( |
| pdischarger.PublicKey(), |
| "discharger", |
| security.ThirdPartyRequirements{}, |
| expcav) |
| if err != nil { |
| t.Fatal(err) |
| } |
| cbless, err := pclient.BlessSelf("clientcaveats", tpcav, tpcavLong) |
| if err != nil { |
| t.Fatal(err) |
| } |
| tpid := tpcav.ThirdPartyDetails().ID() |
| |
| dctx, _, err = v23.WithNewServer(dctx, |
| "discharger", |
| &expiryDischarger{map[string]time.Duration{ |
| tpcav.ThirdPartyDetails().ID(): 100 * time.Millisecond, |
| }}, |
| security.AllowEveryone()) |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| // Fetch discharges for tpcav. |
| beforeFetch := time.Now() |
| discharges, refreshTime := securitylib.PrepareDischarges(cctx, cbless, |
| nil, "", nil) |
| afterFetch := time.Now() |
| if len(discharges) != 2 { |
| t.Errorf("Got %d discharges, expected 2.", len(discharges)) |
| } |
| dis, has := discharges[tpid] |
| if !has { |
| t.Errorf("Got %#v, Expected discharge for %s", discharges, tpid) |
| } |
| // The refreshTime should be expiryDur/2 ms after the fetch, since that's half the |
| // lifetime of the discharge. |
| if err := inRange(refreshTime, beforeFetch.Add(expiryDur/2), afterFetch.Add(expiryDur/2)); err != nil { |
| t.Error(err) |
| } |
| if err := inRange(dis.Expiry(), beforeFetch.Add(expiryDur), afterFetch.Add(expiryDur)); err != nil { |
| t.Error(err) |
| } |
| time.Sleep(dis.Expiry().Sub(time.Now())) |
| |
| // Preparing Discharges again to get fresh discharges. |
| beforeFetch = time.Now() |
| discharges, refreshTime = securitylib.PrepareDischarges(cctx, cbless, |
| nil, "", nil) |
| afterFetch = time.Now() |
| if len(discharges) != 2 { |
| t.Errorf("Got %d discharges, expected 2.", len(discharges)) |
| } |
| dis, has = discharges[tpid] |
| if !has { |
| t.Errorf("Got %#v, Expected discharge for %s", discharges, tpid) |
| } |
| if err := inRange(refreshTime, beforeFetch.Add(expiryDur/2), afterFetch.Add(expiryDur/2)); err != nil { |
| t.Error(err) |
| } |
| if err := inRange(dis.Expiry(), beforeFetch.Add(expiryDur), afterFetch.Add(expiryDur)); err != nil { |
| t.Error(err) |
| } |
| } |