Skip to content

Commit

Permalink
qrcodemodel: Rate limit the emission of reselect auth mode events
Browse files Browse the repository at this point in the history
When hitting the Enter key for a while, bubbletea sends lots of events
and we may imply sending reselectAuthMode{} events faster than we can
actually handle the cancellation/reauthModeSelection in the whole stack.

This never happened (ever) when using the example broker, but it may
actually happen in a real world scenario where the broker may not cancel
the requests quickly enough.

Do this instead of avoiding the handling of the reselectAuthMode event
because it's better to prevent bubbletea to break our (fragile) system.
  • Loading branch information
3v1n0 committed Sep 6, 2024
1 parent 8ffdf09 commit 2f96976
Show file tree
Hide file tree
Showing 3 changed files with 28 additions and 14 deletions.
8 changes: 4 additions & 4 deletions pam/integration-tests/testdata/tapes/cli/qr_code.tape
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Show
Hide
Type "./pam_authd login socket=${AUTHD_TESTS_CLI_AUTHENTICATE_TESTS_SOCK}"
Enter
Sleep 300ms
Sleep 800ms
Show

Hide
Expand All @@ -47,17 +47,17 @@ Show

Hide
Enter
Sleep 300ms
Sleep 800ms
Show

Hide
Enter
Sleep 300ms
Sleep 800ms
Show

Hide
Enter
Sleep 300ms
Sleep 800ms
Show

Hide
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@ Show

Hide
Tab
Sleep 300ms
Sleep 500ms
Show

Hide
Enter@1ms 50
Enter@1ms 800
Sleep 10s
Show

Expand Down
30 changes: 22 additions & 8 deletions pam/internal/adapter/qrcodemodel.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,30 @@
package adapter

import (
"context"
"fmt"
"os"
"strings"
"time"

tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/muesli/termenv"
"github.com/skip2/go-qrcode"
"github.com/ubuntu/authd"
"github.com/ubuntu/authd/internal/log"
)

var centeredStyle = lipgloss.NewStyle().Align(lipgloss.Center, lipgloss.Top)

// reselectionRateLimit is the amount of time (in ms) that we wait before regenerating the qrcode.
const reselectionRateLimit = int64(300)

// qrcodeModel is the form layout type to allow authenticating and return a challenge.
type qrcodeModel struct {
label string
buttonModel *buttonModel
label string
buttonModel *buttonModel
selectionTimestamp int64

content string
code string
Expand All @@ -39,12 +46,13 @@ func newQRCodeModel(content, code, label, buttonLabel string, wait bool) (qrcode
}

return qrcodeModel{
label: label,
buttonModel: button,
content: content,
code: code,
qrCode: qrCode,
wait: wait,
label: label,
buttonModel: button,
selectionTimestamp: time.Now().UnixMilli(),
content: content,
code: code,
qrCode: qrCode,
wait: wait,
}, nil
}

Expand Down Expand Up @@ -73,6 +81,12 @@ func (m qrcodeModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if m.buttonModel == nil {
return m, nil
}
now := time.Now().UnixMilli()
if now-m.selectionTimestamp < reselectionRateLimit {
log.Debug(context.TODO(), "Button press ignored, too fast!")
return m, nil
}
m.selectionTimestamp = now
return m, sendEvent(reselectAuthMode{})
}
}
Expand Down

0 comments on commit 2f96976

Please sign in to comment.