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 5, 2024
1 parent 8ffdf09 commit f253ffd
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 13 deletions.
6 changes: 3 additions & 3 deletions pam/integration-tests/testdata/tapes/cli/qr_code.tape
Original file line number Diff line number Diff line change
Expand Up @@ -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 @@ -52,8 +52,8 @@ Sleep 300ms
Show

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

Sleep 300ms
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 f253ffd

Please sign in to comment.