Skip to content

Commit eeb2f59

Browse files
authored
EEBus: keep pairing requests pending while devices are being configured (#33046)
1 parent 176509d commit eeb2f59

2 files changed

Lines changed: 72 additions & 5 deletions

File tree

server/eebus/eebus.go

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,9 @@ type EEBus struct {
8787
// unknown ski may still belong to a device that is not configured yet.
8888
configured bool
8989

90+
// pending contains pairing requests received while still configuring
91+
pending map[string]shipapi.ServiceIdentity
92+
9093
ski string
9194

9295
paired []shipapi.ServiceIdentity // devices paired via SHIP Pairing Service
@@ -122,8 +125,21 @@ func ConfigComplete() {
122125
}
123126

124127
instance.mux.Lock()
125-
defer instance.mux.Unlock()
126128
instance.configured = true
129+
130+
// deny requests left pending during configuration whose ski remained unknown
131+
var deny []shipapi.ServiceIdentity
132+
for _, identity := range instance.pending {
133+
if len(instance.clients[identity.SKI]) == 0 {
134+
deny = append(deny, identity)
135+
}
136+
}
137+
clear(instance.pending)
138+
instance.mux.Unlock()
139+
140+
for _, identity := range deny {
141+
instance.service.CancelPairing(identity)
142+
}
127143
}
128144

129145
func GetStatus() any {
@@ -207,10 +223,16 @@ func NewServer(other Config) (*EEBus, error) {
207223
ski: ski,
208224
clients: make(map[string][]Device),
209225
connected: make(map[string]bool),
226+
pending: make(map[string]shipapi.ServiceIdentity),
210227
}
211228

212229
c.service = service.NewService(configuration, c)
213230
c.service.SetLogging(c)
231+
232+
// keep pairing requests from unknown skis pending instead of aborting the ship
233+
// handshake- the ski may still be registered by a device that is being configured
234+
c.service.UserIsAbleToApproveOrCancelPairingRequests(true)
235+
214236
if err := c.service.Setup(); err != nil {
215237
if errors.Is(err, shipapi.ErrInvalidSKI) {
216238
const hint = "The stored EEBUS certificate has an invalid Subject Key Identifier (SKI).\n" +
@@ -587,6 +609,7 @@ func (c *EEBus) ServicePairingDetailUpdate(identity shipapi.ServiceIdentity, det
587609
// device configuration is still running- leave the request pending
588610
// instead of denying a ski that is about to be registered
589611
c.log.DEBUG.Printf("pairing request from %s while configuring, left pending", identity.SKI)
612+
c.pending[identity.SKI] = identity
590613
return
591614
}
592615

@@ -601,16 +624,22 @@ func (c *EEBus) ServiceAutoTrusted(service eebusapi.ServiceInterface, identity s
601624
c.log.INFO.Printf("service trusted: %s", identity.ShipID)
602625

603626
c.mux.Lock()
604-
defer c.mux.Unlock()
605627
c.upsertPairing(identity)
606628

607629
// connect may run before trust is established, so clientsFor skips consumers
608630
// registered without ski; wake them now that the device is paired
631+
var clients []Device
609632
if c.connected[identity.SKI] {
610-
for _, client := range c.clientsFor(identity.SKI) {
611-
client.Connect(true)
612-
}
633+
clients = c.clientsFor(identity.SKI)
613634
}
635+
c.mux.Unlock()
636+
637+
for _, client := range clients {
638+
client.Connect(true)
639+
}
640+
641+
// registering the now trusted identity approves a handshake still pending trust
642+
c.service.RegisterRemoteService(identity)
614643
}
615644

616645
func (c *EEBus) ServiceAutoTrustFailed(service eebusapi.ServiceInterface, identity shipapi.ServiceIdentity, reason error) {

server/eebus/eebus_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,51 @@ func TestPairingDeniedOnlyWhenConfigured(t *testing.T) {
8888
c := &EEBus{
8989
log: util.NewLogger("test"),
9090
clients: make(map[string][]Device),
91+
pending: make(map[string]shipapi.ServiceIdentity),
9192
service: service,
9293
}
9394

9495
// still configuring - no CancelPairing expected
9596
c.ServicePairingDetailUpdate(identity, detail)
97+
require.Len(t, c.pending, 1)
9698

9799
service.EXPECT().CancelPairing(identity).Once()
98100
c.configured = true
99101
c.ServicePairingDetailUpdate(identity, detail)
100102
}
103+
104+
// TestConfigCompleteResolvesPending guards that a request left pending during
105+
// configuration is denied afterwards unless its ski belongs to a configured device-
106+
// ship-go keeps prolonging the pending handshake until somebody decides.
107+
func TestConfigCompleteResolvesPending(t *testing.T) {
108+
identity := shipapi.NewServiceIdentity("aabbcc", "", "")
109+
110+
newInstance := func(t *testing.T, clients map[string][]Device) *eebusmocks.ServiceInterface {
111+
service := eebusmocks.NewServiceInterface(t)
112+
instance = &EEBus{
113+
log: util.NewLogger("test"),
114+
clients: clients,
115+
pending: map[string]shipapi.ServiceIdentity{identity.SKI: identity},
116+
service: service,
117+
}
118+
t.Cleanup(func() { instance = nil })
119+
return service
120+
}
121+
122+
t.Run("unknown ski denied", func(t *testing.T) {
123+
service := newInstance(t, make(map[string][]Device))
124+
service.EXPECT().CancelPairing(identity).Once()
125+
126+
ConfigComplete()
127+
require.True(t, instance.configured)
128+
require.Empty(t, instance.pending)
129+
})
130+
131+
t.Run("configured ski kept", func(t *testing.T) {
132+
// no CancelPairing expected- the device registered its ski meanwhile
133+
newInstance(t, map[string][]Device{identity.SKI: {&mockDevice{}}})
134+
135+
ConfigComplete()
136+
require.True(t, instance.configured)
137+
})
138+
}

0 commit comments

Comments
 (0)