diff --git a/control/controlclient/auto_test.go b/control/controlclient/auto_test.go index 43d5f8180..131b1296a 100644 --- a/control/controlclient/auto_test.go +++ b/control/controlclient/auto_test.go @@ -31,6 +31,7 @@ import ( "tailscale.com/tailcfg" "tailscale.com/testy" "tailscale.io/control" // not yet released + "tailscale.io/control/cfgdb" ) func TestTest(t *testing.T) { @@ -716,6 +717,69 @@ func TestRefresh(t *testing.T) { c1.Shutdown() } +func TestAuthKey(t *testing.T) { + var nowMu sync.Mutex + now := time.Now() // Server and Client use this variable as the current time + timeNow := func() time.Time { + nowMu.Lock() + defer nowMu.Unlock() + return now + } + + s := newServer(t) + s.control.TimeNow = timeNow + defer s.close() + + const loginName = "testuser1@example.com" + user, err := s.control.DB().FindOrCreateUser("google", loginName, "", "") + if err != nil { + t.Fatal(err) + } + + t.Run("one-off", func(t *testing.T) { + oneOffKey, err := s.control.DB().NewAPIKey(user.ID, cfgdb.KeyCapabilities{ + Bits: cfgdb.KeyCapOneOffNodeAuth, + }) + if err != nil { + t.Fatal(err) + } + + c1 := s.newClientWithKey(t, "c1", string(oneOffKey)) + c1.Login(nil, 0) + c1.waitStatus(t, stateAuthenticated) + c1.waitStatus(t, stateSynchronized) + c1.Shutdown() + + // Key won't work a second time. + c2 := s.newClientWithKey(t, "c2", string(oneOffKey)) + c2.Login(nil, 0) + status := c2.readStatus(t) + if e, substr := status.New.Err, `revoked`; !strings.Contains(e, substr) { + t.Errorf("Err=%q, expect substring %q", e, substr) + } + c2.Shutdown() + }) + + t.Run("followup", func(t *testing.T) { + key, err := s.control.DB().NewAPIKey(user.ID, cfgdb.KeyCapabilities{ + Bits: cfgdb.KeyCapNodeAuth, + }) + if err != nil { + t.Fatal(err) + } + + c1 := s.newClient(t, "c1") + c1.Login(nil, 0) + c1.waitStatus(t, stateURLVisitRequired) + + c1.direct.authKey = string(key) + c1.Login(nil, 0) + c1.waitStatus(t, stateAuthenticated) + c1.waitStatus(t, stateSynchronized) + c1.Shutdown() + }) +} + func TestExpectedProvider(t *testing.T) { s := newServer(t) defer s.close() @@ -1008,6 +1072,10 @@ type statusChange struct { } func (s *server) newClient(t *testing.T, name string) *client { + return s.newClientWithKey(t, name, "") +} + +func (s *server) newClientWithKey(t *testing.T, name, authKey string) *client { t.Helper() ch := make(chan statusChange, 1024) @@ -1033,6 +1101,7 @@ func (s *server) newClient(t *testing.T, name string) *client { return zstd.NewReader(nil) }, KeepAlive: true, + AuthKey: authKey, }) ctlc.SetStatusFunc(func(new Status) { select {