Skip to content

Commit

Permalink
fix: make in mender-connect to request a new JWT token on its own
Browse files Browse the repository at this point in the history
Fix to make `mender-connect` request a new JWT token on its own, so its not dependent on `mender-update` to do so. 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.

Changelog: None

Ticket: MEN-6877
Signed-off-by: Sebastian Opsahl <sebastian.opsahl@northern.tech>
  • Loading branch information
SebOpsahl committed Mar 10, 2024
1 parent 046bf46 commit 9c8b3a8
Show file tree
Hide file tree
Showing 2 changed files with 101 additions and 88 deletions.
54 changes: 24 additions & 30 deletions app/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -322,47 +322,41 @@ 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 {
if (token != d.serverJwt || serverURL != d.serverUrl) &&
(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
d.serverUrl = serverURL
d.serverJwt = token
d.postEvent(e)
log.Tracef("(dbusEventLoop) posting Event: %s", e.event)
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")
}

Expand Down
135 changes: 77 additions & 58 deletions app/daemon_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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:
}
}
})
}
}

Expand Down

0 comments on commit 9c8b3a8

Please sign in to comment.