From d7063f8c6741dc959cfa8e68b50df2980158c83c Mon Sep 17 00:00:00 2001 From: Sebastian Opsahl Date: Mon, 4 Mar 2024 21:31:51 +0100 Subject: [PATCH] fix: Fix in mender-connect to request a new JWT token on its own Changelog: Previously, mender-connect relied the Mender client to request new JWT tokens. Since the split of mender into mender-update and mender-auth, it is theoretically possible to run mender-connect without mender-update, in which case it wouldn't request JWT tokens without this fix. Ticket: MEN-6877 Signed-off-by: Sebastian Opsahl --- app/daemon.go | 51 ++++++++--------- app/daemon_test.go | 135 ++++++++++++++++++++++++++------------------- 2 files changed, 99 insertions(+), 87 deletions(-) diff --git a/app/daemon.go b/app/daemon.go index 8670df8..3345662 100644 --- a/app/daemon.go +++ b/app/daemon.go @@ -322,47 +322,40 @@ func (d *MenderShellDaemon) dbusEventLoop(client mender.AuthClient) { if len(p) > 1 && p[0].ParamType == dbus.GDBusTypeString && p[1].ParamType == dbus.GDBusTypeString { + token := p[0].ParamData.(string) serverURL := p[1].ParamData.(string) d.processJwtTokenStateChange(token, serverURL) + if len(token) > 0 && len(serverURL) > 0 { log.Tracef("dbusEventLoop: got a token len=%d, ServerURL=%s", len(token), serverURL) - if token != d.serverJwt || serverURL != d.serverUrl { - log.Debugf( - "dbusEventLoop: new token or ServerURL, reconnecting. len=%d, ServerURL=%s", - len(token), - serverURL, - ) - needsReconnect = true - - // If the server (Mender client proxy) closed the connection, it is likely that - // both messageLoop is asking for a reconnection and we got JwtTokenStateChange - // signal. So drain here the reconnect channel and reconnect only once - if d.needsReconnect() { - log.Debug("dbusEventLoop: drained reconnect req channel") - } - + e := MenderShellDaemonEvent{ + event: EventReconnect, + data: token, + id: "(dbusEventLoop)", } - // TODO: moving these assignments one scope up would make d.authorized redundant... - d.serverJwt = token + log.Tracef("(dbusEventLoop) posting Event: %s", e.event) d.serverUrl = serverURL + d.serverJwt = token + d.postEvent(e) + needsReconnect = false + + // If the server (Mender client proxy) closed the connection, it is likely that + // both messageLoop is asking for a reconnection and we got JwtTokenStateChange + // signal. So drain here the reconnect channel and reconnect only once + if d.needsReconnect() { + log.Debug("dbusEventLoop: drained reconnect req channel") + } } } - if needsReconnect && d.authorized { - jwtToken, serverURL, _ := client.GetJWTToken() - e := MenderShellDaemonEvent{ - event: EventReconnect, - data: jwtToken, - id: "(dbusEventLoop)", + + if needsReconnect { + _, err := client.FetchJWTToken() + if err != nil { + log.Errorf("dbusEventLoop: error fetching JWT token") } - log.Tracef("(dbusEventLoop) posting Event: %s", e.event) - d.serverUrl = serverURL - d.serverJwt = jwtToken - d.postEvent(e) - needsReconnect = false } } - log.Trace("dbusEventLoop: returning") } diff --git a/app/daemon_test.go b/app/daemon_test.go index b509bbb..1ebcb25 100644 --- a/app/daemon_test.go +++ b/app/daemon_test.go @@ -739,94 +739,113 @@ func TestTimeToSweepSessions(t *testing.T) { func TestDBusEventLoop(t *testing.T) { currentUser, err := user.Current() if err != nil { - t.Errorf("cant get current user: %s", err.Error()) - return + t.Fatalf("cant get current user: %s", err) } testCases := []struct { name string - err error token string + err error timeout time.Duration }{ { - name: "stopped-gracefully", - token: "the-token", + name: "stopped-gracefully", + token: "the-token", + timeout: 4 * time.Second, }, { name: "token_not_returned_wait_forever", token: "", timeout: 15 * time.Second, }, + { + name: "token_not_returned_try_reconnection", + token: "", + timeout: 2 * time.Second, + }, } for _, tc := range testCases { - if tc.name == "token_not_returned_wait_forever" { - timeout := time.After(tc.timeout) - done := make(chan bool) - go func() { - t.Run(tc.name, func(t *testing.T) { - d := NewDaemon(&config.MenderShellConfig{ - MenderShellConfigFromFile: config.MenderShellConfigFromFile{ - ShellCommand: "/bin/sh", - User: currentUser.Name, - Terminal: config.TerminalConfig{ - Width: 24, - Height: 80, - }, - }, - }) + d := NewDaemon(&config.MenderShellConfig{ + MenderShellConfigFromFile: config.MenderShellConfigFromFile{ + ShellCommand: "/bin/sh", + User: currentUser.Name, + Terminal: config.TerminalConfig{ + Width: 24, + Height: 80, + }, + }, + }) - dbusAPI := &dbusmocks.DBusAPI{} - defer dbusAPI.AssertExpectations(t) - client := &authmocks.AuthClient{} - client.On("WaitForJwtTokenStateChange").Return([]dbus.SignalParams{ - { - ParamType: "s", - ParamData: tc.token, - }, - }, tc.err) - client.On("GetJWTToken").Return(tc.token, "", tc.err) - d.dbusEventLoop(client) - }) - done <- true - }() + dbusAPI := &dbusmocks.DBusAPI{} + defer dbusAPI.AssertExpectations(t) + client := &authmocks.AuthClient{} - select { - case <-timeout: - t.Logf("ok: expected to run forever") - case <-done: - } - } else { - t.Run(tc.name, func(t *testing.T) { - d := NewDaemon(&config.MenderShellConfig{ - MenderShellConfigFromFile: config.MenderShellConfigFromFile{ - ShellCommand: "/bin/sh", - User: currentUser.Name, - Terminal: config.TerminalConfig{ - Width: 24, - Height: 80, - }, + t.Run(tc.name, func(t *testing.T) { + switch tc.name { + case "token_not_returned_wait_forever": + timeout := time.After(tc.timeout) + done := make(chan bool) + client.On("WaitForJwtTokenStateChange").Return([]dbus.SignalParams{ + { + ParamType: "", + ParamData: nil, }, - }) + }, tc.err) + go func() { + d.dbusEventLoop(client) + done <- true + }() + select { + case <-timeout: + t.Logf("ok: expected to run forever") + case <-done: + } - dbusAPI := &dbusmocks.DBusAPI{} - defer dbusAPI.AssertExpectations(t) - client := &authmocks.AuthClient{} + case "stopped-gracefully": + timeout := time.After(tc.timeout) + done := make(chan bool) client.On("WaitForJwtTokenStateChange").Return([]dbus.SignalParams{ { ParamType: "s", ParamData: tc.token, }, }, tc.err) - client.On("GetJWTToken").Return(tc.token, "", tc.err) go func() { - time.Sleep(time.Second) + d.dbusEventLoop(client) + done <- true + }() + + select { + case <-timeout: + client.AssertNotCalled(t, "FetchJWTToken") d.stop = true + <-done + case <-done: + t.Error("dbusEventLoop returned unexpectedly early") + } + + case "token_not_returned_try_reconnection": + timeout := time.After(tc.timeout) + done := make(chan bool) + client.On("WaitForJwtTokenStateChange").Return([]dbus.SignalParams{ + { + ParamType: "", + ParamData: nil, + }, + }, tc.err) + go func() { + d.dbusEventLoop(client) + client.AssertCalled(t, "FetchJWTToken") + done <- true }() - d.dbusEventLoop(client) - }) - } + select { + case <-timeout: + t.Log("ok: expected to run forever") + case <-done: + } + } + }) } }