Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions examplebroker/broker.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,10 +360,10 @@ func (b *Broker) NewSession(ctx context.Context, username, lang, mode string) (s
}

// GetAuthenticationModes returns the list of supported authentication modes for the selected broker depending on session info.
func (b *Broker) GetAuthenticationModes(ctx context.Context, sessionID string, supportedUILayouts []map[string]string) (authenticationModes []map[string]string, err error) {
func (b *Broker) GetAuthenticationModes(ctx context.Context, sessionID string, supportedUILayouts []map[string]string) (authenticationModes []map[string]string, msg string, err error) {
sessionInfo, err := b.sessionInfo(sessionID)
if err != nil {
return nil, err
return nil, "", err
}

log.Debugf(ctx, "Supported UI layouts by %s, %#v", sessionID, supportedUILayouts)
Expand All @@ -376,12 +376,12 @@ func (b *Broker) GetAuthenticationModes(ctx context.Context, sessionID string, s
// If the user needs or can reset the password, we only show those authentication modes.
if sessionInfo.currentAuthStep == sessionInfo.neededAuthSteps && sessionInfo.pwdChange != noReset {
if sessionInfo.currentAuthStep < 2 {
return nil, errors.New("password reset is not allowed before authentication")
return nil, "", errors.New("password reset is not allowed before authentication")
}

allModes = getPasswdResetModes(sessionInfo, supportedUILayouts)
if sessionInfo.pwdChange == mustReset && len(allModes) == 0 {
return nil, fmt.Errorf("user %q must reset password, but no mode was provided for it", sessionInfo.username)
return nil, "", fmt.Errorf("user %q must reset password, but no mode was provided for it", sessionInfo.username)
}
}

Expand Down Expand Up @@ -426,10 +426,10 @@ func (b *Broker) GetAuthenticationModes(ctx context.Context, sessionID string, s
sessionInfo.allModes = allModes

if err := b.updateSession(sessionID, sessionInfo); err != nil {
return nil, err
return nil, "", err
}

return authenticationModes, nil
return authenticationModes, "", nil
}

func getSupportedModes(sessionInfo sessionInfo, supportedUILayouts []map[string]string) map[string]authMode {
Expand Down
8 changes: 4 additions & 4 deletions examplebroker/dbus.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,12 @@ func (b *Bus) NewSession(username, lang, mode string) (sessionID, encryptionKey
}

// GetAuthenticationModes is the method through which the broker and the daemon will communicate once dbusInterface.GetAuthenticationModes is called.
func (b *Bus) GetAuthenticationModes(sessionID string, supportedUILayouts []map[string]string) (authenticationModes []map[string]string, dbusErr *dbus.Error) {
authenticationModes, err := b.broker.GetAuthenticationModes(context.Background(), sessionID, supportedUILayouts)
func (b *Bus) GetAuthenticationModes(sessionID string, supportedUILayouts []map[string]string) (authenticationModes []map[string]string, msg string, dbusErr *dbus.Error) {
authenticationModes, msg, err := b.broker.GetAuthenticationModes(context.Background(), sessionID, supportedUILayouts)
if err != nil {
return nil, dbus.MakeFailedError(err)
return nil, "", dbus.MakeFailedError(err)
}
return authenticationModes, nil
return authenticationModes, msg, nil
}

// SelectAuthenticationMode is the method through which the broker and the daemon will communicate once dbusInterface.SelectAuthenticationMode is called.
Expand Down
12 changes: 6 additions & 6 deletions internal/brokers/broker.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const LocalBrokerName = "local"

type brokerer interface {
NewSession(ctx context.Context, username, lang, mode string) (sessionID, encryptionKey string, err error)
GetAuthenticationModes(ctx context.Context, sessionID string, supportedUILayouts []map[string]string) (authenticationModes []map[string]string, err error)
GetAuthenticationModes(ctx context.Context, sessionID string, supportedUILayouts []map[string]string) (authenticationModes []map[string]string, msg string, err error)
SelectAuthenticationMode(ctx context.Context, sessionID, authenticationModeName string) (uiLayoutInfo map[string]string, err error)
IsAuthenticated(ctx context.Context, sessionID, authenticationData string) (access, data string, err error)
EndSession(ctx context.Context, sessionID string) (err error)
Expand Down Expand Up @@ -105,27 +105,27 @@ func (b Broker) newSession(ctx context.Context, username, lang, mode string) (se
}

// GetAuthenticationModes calls the broker corresponding method, stripping broker ID prefix from sessionID.
func (b *Broker) GetAuthenticationModes(ctx context.Context, sessionID string, supportedUILayouts []map[string]string) (authenticationModes []map[string]string, err error) {
func (b *Broker) GetAuthenticationModes(ctx context.Context, sessionID string, supportedUILayouts []map[string]string) (authenticationModes []map[string]string, msg string, err error) {
sessionID = b.parseSessionID(sessionID)

b.layoutValidatorsMu.Lock()
b.layoutValidators[sessionID] = generateValidators(ctx, sessionID, supportedUILayouts)
b.layoutValidatorsMu.Unlock()

authenticationModes, err = b.brokerer.GetAuthenticationModes(ctx, sessionID, supportedUILayouts)
authenticationModes, msg, err = b.brokerer.GetAuthenticationModes(ctx, sessionID, supportedUILayouts)
if err != nil {
return nil, err
return nil, "", err
}

for _, a := range authenticationModes {
for _, key := range []string{layouts.ID, layouts.Label} {
if _, exists := a[key]; !exists {
return nil, fmt.Errorf("invalid authentication mode, missing %q key: %v", key, a)
return nil, "", fmt.Errorf("invalid authentication mode, missing %q key: %v", key, a)
}
}
}

return authenticationModes, nil
return authenticationModes, msg, nil
}

// SelectAuthenticationMode calls the broker corresponding method, stripping broker ID prefix from sessionID.
Expand Down
2 changes: 1 addition & 1 deletion internal/brokers/broker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ func TestGetAuthenticationModes(t *testing.T) {
supportedUILayouts = append(supportedUILayouts, supportedLayouts[layout])
}

gotModes, err := b.GetAuthenticationModes(context.Background(), prefixID(t, tc.sessionID), supportedUILayouts)
gotModes, _, err := b.GetAuthenticationModes(context.Background(), prefixID(t, tc.sessionID), supportedUILayouts)
if tc.wantErr {
require.Error(t, err, "GetAuthenticationModes should return an error, but did not")
return
Expand Down
21 changes: 20 additions & 1 deletion internal/brokers/dbusbroker.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,26 @@ func (b dbusBroker) NewSession(ctx context.Context, username, lang, mode string)
}

// GetAuthenticationModes calls the corresponding method on the broker bus and returns the authentication modes supported by it.
func (b dbusBroker) GetAuthenticationModes(ctx context.Context, sessionID string, supportedUILayouts []map[string]string) (authenticationModes []map[string]string, err error) {
func (b dbusBroker) GetAuthenticationModes(ctx context.Context, sessionID string, supportedUILayouts []map[string]string) (authenticationModes []map[string]string, msg string, err error) {
call, err := b.call(ctx, "GetAuthenticationModesV2", sessionID, supportedUILayouts)
var dbusError dbus.Error
if errors.As(err, &dbusError) && dbusError.Name == "org.freedesktop.DBus.Error.UnknownMethod" {
log.Debugf(ctx, "GetAuthenticationModesV2 not supported, falling back to GetAuthenticationModes")
authenticationModes, err = b.getAuthenticationModesV1(ctx, sessionID, supportedUILayouts)
return authenticationModes, "", err
}
if err != nil {
return nil, "", err
}

if err = call.Store(&authenticationModes, &msg); err != nil {
return nil, "", err
}

return authenticationModes, msg, nil
}

func (b dbusBroker) getAuthenticationModesV1(ctx context.Context, sessionID string, supportedUILayouts []map[string]string) (authenticationModes []map[string]string, err error) {
call, err := b.call(ctx, "GetAuthenticationModes", sessionID, supportedUILayouts)
if err != nil {
return nil, err
Expand Down
4 changes: 2 additions & 2 deletions internal/brokers/localbroker.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ func (b localBroker) NewSession(ctx context.Context, username, lang, mode string
}

//nolint:unused // We still need localBroker to implement the brokerer interface, even though this method should never be called on it.
func (b localBroker) GetAuthenticationModes(ctx context.Context, sessionID string, supportedUILayouts []map[string]string) (authenticationModes []map[string]string, err error) {
return nil, errors.New("GetAuthenticationModes should never be called on local broker")
func (b localBroker) GetAuthenticationModes(ctx context.Context, sessionID string, supportedUILayouts []map[string]string) (authenticationModes []map[string]string, msg string, err error) {
return nil, "", errors.New("GetAuthenticationModes should never be called on local broker")
}

//nolint:unused // We still need localBroker to implement the brokerer interface, even though this method should never be called on it.
Expand Down
17 changes: 14 additions & 3 deletions internal/proto/authd/authd.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions internal/proto/authd/authd.proto
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ message UILayout {

message GAMResponse {
repeated AuthenticationMode authentication_modes = 1;
optional string msg = 2;

message AuthenticationMode {
string id = 1;
Expand Down
4 changes: 4 additions & 0 deletions internal/services/errmessages/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@ type ToDisplayError struct {
func NewToDisplayError(err error) error {
return ToDisplayError{err}
}

func (e ToDisplayError) Unwrap() error {
return e.error
}
8 changes: 7 additions & 1 deletion internal/services/pam/pam.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ func (s Service) GetAuthenticationModes(ctx context.Context, req *authd.GAMReque
supportedLayouts = append(supportedLayouts, layout)
}

authenticationModes, err := broker.GetAuthenticationModes(ctx, sessionID, supportedLayouts)
authenticationModes, msg, err := broker.GetAuthenticationModes(ctx, sessionID, supportedLayouts)
if err != nil {
log.Errorf(ctx, "GetAuthenticationModes: Could not get authentication modes for session %q: %v", sessionID, err)
return nil, err
Expand All @@ -209,8 +209,14 @@ func (s Service) GetAuthenticationModes(ctx context.Context, req *authd.GAMReque
})
}

var msgPtr *string
if msg != "" {
msgPtr = &msg
}
Comment on lines +212 to +215
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it's time to move ptrValue outside the _test.go code?


return &authd.GAMResponse{
AuthenticationModes: authModes,
Msg: msgPtr,
}, nil
}

Expand Down
12 changes: 6 additions & 6 deletions internal/testutils/broker.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,26 +138,26 @@ func (b *BrokerBusMock) NewSession(username, lang, mode string) (sessionID, encr
}

// GetAuthenticationModes returns default values to be used in tests or an error if requested.
func (b *BrokerBusMock) GetAuthenticationModes(sessionID string, supportedUILayouts []map[string]string) (authenticationModes []map[string]string, dbusErr *dbus.Error) {
func (b *BrokerBusMock) GetAuthenticationModes(sessionID string, supportedUILayouts []map[string]string) (authenticationModes []map[string]string, msg string, dbusErr *dbus.Error) {
sessionID = parseSessionID(sessionID)
switch sessionID {
case "gam_invalid":
return []map[string]string{
{"invalid": "invalid"},
}, nil
}, "", nil
case "gam_empty":
return nil, nil
return nil, "", nil
case "gam_error":
return nil, dbus.MakeFailedError(fmt.Errorf("broker %q: GetAuthenticationModes errored out", b.name))
return nil, "", dbus.MakeFailedError(fmt.Errorf("broker %q: GetAuthenticationModes errored out", b.name))
case "gam_multiple_modes":
return []map[string]string{
{layouts.ID: "mode1", layouts.Label: "Mode 1"},
{layouts.ID: "mode2", layouts.Label: "Mode 2"},
}, nil
}, "", nil
default:
return []map[string]string{
{layouts.ID: "mode1", layouts.Label: "Mode 1"},
}, nil
}, "", nil
}
}

Expand Down
8 changes: 8 additions & 0 deletions pam/internal/adapter/authmodeselection.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type supportedUILayoutsSet struct{}
// authModesReceived is the internal event signalling that the supported authentication modes have been received.
type authModesReceived struct {
authModes []*authd.GAMResponse_AuthenticationMode
msg string
}

// authModeSelected is the internal event signalling that the an authentication mode has been selected.
Expand Down Expand Up @@ -244,8 +245,15 @@ func getAuthenticationModes(client authd.PAMClient, sessionID string, uiLayouts
}
log.Debug(context.TODO(), "authModes", authModes)

var msg string
if gamResp.Msg != nil {
msg = *gamResp.Msg
log.Debugf(context.TODO(), "GetAuthenticationModes message: %q", msg)
}

return authModesReceived{
authModes: authModes,
msg: msg,
}
}
}
Expand Down
24 changes: 21 additions & 3 deletions pam/internal/adapter/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,20 @@ var (
Light: "#909090",
Dark: "#7f7f7f",
}).Padding(1, 0, 0, 2)

warningMsgStyle = lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{
// Use a light yellow/orange color for warnings.
Light: "#f0c674",
Dark: "#e6bf69",
}).Padding(1, 0, 0, 2)
)

// sessionInfo contains the global broker session information.
type sessionInfo struct {
brokerID string
sessionID string
encryptionKey *rsa.PublicKey
brokerID string
sessionID string
encryptionKey *rsa.PublicKey
getAuthenticationModesMessage string
}

// uiModel is the global models orchestrator.
Expand Down Expand Up @@ -281,6 +288,12 @@ func (m uiModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
return m, m.userSelectionModel.SelectUser()

case authModesReceived:
if msg.msg != "" {
safeMessageDebug(msg, "GetAuthenticationModes message: %q", msg.msg)
m.currentSession.getAuthenticationModesMessage = msg.msg
}

case UsernameSelected:
safeMessageDebug(msg, "user: %q", m.username())
if m.username() == "" {
Expand Down Expand Up @@ -447,6 +460,11 @@ func (m uiModel) View() string {

view := viewBuilder.String()

if m.currentSession != nil && m.currentSession.getAuthenticationModesMessage != "" {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do not forget the native model (for ssh and friends) too :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. The GDM part is much more important though, without it it doesn't make sense to merge this IMO. If you get the GDM part done, I can implement the missing parts for the native model.

warningMessage := warningMsgStyle.Render(m.currentSession.getAuthenticationModesMessage)
view = lipgloss.JoinVertical(lipgloss.Left, view, warningMessage)
}

if len(view) > 0 && m.canGoBack() {
infoMessage := infoMsgStyle.Render(fmt.Sprintf("Press escape key to %s",
goBackLabel(m.previousStage())))
Expand Down
Loading