From 82f078fcd00bd0ddc987a7400426fe6b026068ad Mon Sep 17 00:00:00 2001 From: Adrian Dombeck Date: Wed, 6 May 2026 13:18:36 +0200 Subject: [PATCH 01/11] refactor: Improve type and variable names Call the PAM return value a "return value" instead of an "exit status" because I found the naming confusing. --- pam/internal/adapter/gdmmodel.go | 2 +- pam/internal/adapter/gdmmodel_test.go | 222 +++++++++++++------------- pam/internal/adapter/model.go | 36 ++--- pam/internal/adapter/return.go | 6 +- pam/pam.go | 22 +-- 5 files changed, 144 insertions(+), 144 deletions(-) diff --git a/pam/internal/adapter/gdmmodel.go b/pam/internal/adapter/gdmmodel.go index 9b8d8e8708..d9cbcefce1 100644 --- a/pam/internal/adapter/gdmmodel.go +++ b/pam/internal/adapter/gdmmodel.go @@ -25,7 +25,7 @@ type gdmModel struct { waitingAuth bool // Given the bubbletea async nature we may end up receiving and forwarding - // events after we've got a PamReturnStatus and even after the PAM module + // events after we've got a PamReturnValue and even after the PAM module // has returned to libpam caller (since go goroutines can still be alive). // However, after the quit point we should really not interact anymore with // GDM or we'll make it crash (as it doesn't expect any conversation diff --git a/pam/internal/adapter/gdmmodel_test.go b/pam/internal/adapter/gdmmodel_test.go index b91f65ad56..2cce848f26 100644 --- a/pam/internal/adapter/gdmmodel_test.go +++ b/pam/internal/adapter/gdmmodel_test.go @@ -48,7 +48,7 @@ func TestGdmModel(t *testing.T) { // However we do return a PAM error in such case because that's what we're // going to return to the PAM stack in case authentication process has not // been completed fully. - gdmTestEarlyStopExitStatus := errNoExitStatus + gdmTestEarlyStopReturnValue := pamNoReturnValue gdmTestIgnoreStage := proto.Stage(-1) @@ -101,7 +101,7 @@ func TestGdmModel(t *testing.T) { convError map[string]error timeout time.Duration - wantExitStatus PamReturnStatus + wantPAMReturnValue PamReturnValue wantGdmRequests []gdm.RequestType wantGdmEvents []gdm.EventType wantGdmAuthRes []*authd.IAResponse @@ -129,7 +129,7 @@ func TestGdmModel(t *testing.T) { gdm.EventType_startAuthentication, gdm.EventType_authEvent, }, - wantExitStatus: gdmTestEarlyStopExitStatus, + wantPAMReturnValue: gdmTestEarlyStopReturnValue, }, "Broker_selection_stage_caused_by_PAM_user_selection": { pamUser: "pam-preset-user", @@ -148,8 +148,8 @@ func TestGdmModel(t *testing.T) { gdm.EventType_startAuthentication, gdm.EventType_authEvent, }, - wantStage: proto.Stage_brokerSelection, - wantExitStatus: gdmTestEarlyStopExitStatus, + wantStage: proto.Stage_brokerSelection, + wantPAMReturnValue: gdmTestEarlyStopReturnValue, }, "Broker_selection_stage_caused_by_client_side_user_selection": { gdmEvents: []*gdm.EventData{ @@ -171,8 +171,8 @@ func TestGdmModel(t *testing.T) { gdm.EventType_startAuthentication, gdm.EventType_authEvent, }, - wantStage: proto.Stage_brokerSelection, - wantExitStatus: gdmTestEarlyStopExitStatus, + wantStage: proto.Stage_brokerSelection, + wantPAMReturnValue: gdmTestEarlyStopReturnValue, }, "Challenge_stage_caused_by_server_side_broker_and_authMode_selection": { clientOptions: append(slices.Clone(singleBrokerClientOptions), @@ -199,8 +199,8 @@ func TestGdmModel(t *testing.T) { wantNoGdmEvents: []gdm.EventType{ gdm.EventType_authEvent, }, - wantStage: proto.Stage_challenge, - wantExitStatus: gdmTestEarlyStopExitStatus, + wantStage: proto.Stage_challenge, + wantPAMReturnValue: gdmTestEarlyStopReturnValue, }, "Challenge_stage_caused_by_client_side_broker_and_authMode_selection": { gdmEvents: []*gdm.EventData{ @@ -235,8 +235,8 @@ func TestGdmModel(t *testing.T) { wantNoGdmEvents: []gdm.EventType{ gdm.EventType_authEvent, }, - wantStage: proto.Stage_challenge, - wantExitStatus: gdmTestEarlyStopExitStatus, + wantStage: proto.Stage_challenge, + wantPAMReturnValue: gdmTestEarlyStopReturnValue, }, "Authenticated_with_preset_PAM_user_and_server_side_broker_and_authMode_selection": { clientOptions: append(slices.Clone(singleBrokerClientOptions), @@ -269,9 +269,9 @@ func TestGdmModel(t *testing.T) { gdm.EventType_startAuthentication, gdm.EventType_authEvent, }, - wantGdmAuthRes: []*authd.IAResponse{{Access: auth.Granted}}, - wantStage: proto.Stage_challenge, - wantExitStatus: PamSuccess{BrokerID: firstBrokerInfo.Id}, + wantGdmAuthRes: []*authd.IAResponse{{Access: auth.Granted}}, + wantStage: proto.Stage_challenge, + wantPAMReturnValue: PamSuccess{BrokerID: firstBrokerInfo.Id}, }, "Authenticated_with_preset_PAM_user_using_legacy_challenge_and_server_side_broker_and_authMode_selection": { clientOptions: append(slices.Clone(singleBrokerClientOptions), @@ -304,9 +304,9 @@ func TestGdmModel(t *testing.T) { gdm.EventType_startAuthentication, gdm.EventType_authEvent, }, - wantGdmAuthRes: []*authd.IAResponse{{Access: auth.Granted}}, - wantStage: proto.Stage_challenge, - wantExitStatus: PamSuccess{BrokerID: firstBrokerInfo.Id}, + wantGdmAuthRes: []*authd.IAResponse{{Access: auth.Granted}}, + wantStage: proto.Stage_challenge, + wantPAMReturnValue: PamSuccess{BrokerID: firstBrokerInfo.Id}, }, "Authenticated_with_preset_PAM_user_updated_and_server_side_broker_and_authMode_selection": { clientOptions: append(slices.Clone(singleBrokerClientOptions), @@ -360,9 +360,9 @@ func TestGdmModel(t *testing.T) { gdm.EventType_startAuthentication, gdm.EventType_authEvent, }, - wantGdmAuthRes: []*authd.IAResponse{{Access: auth.Granted}}, - wantStage: proto.Stage_challenge, - wantExitStatus: PamSuccess{BrokerID: firstBrokerInfo.Id}, + wantGdmAuthRes: []*authd.IAResponse{{Access: auth.Granted}}, + wantStage: proto.Stage_challenge, + wantPAMReturnValue: PamSuccess{BrokerID: firstBrokerInfo.Id}, }, "Authenticated_with_message_with_preset_PAM_user_and_server_side_broker_and_authMode_selection": { clientOptions: append(slices.Clone(multiBrokerClientOptions), @@ -404,7 +404,7 @@ func TestGdmModel(t *testing.T) { Access: auth.Granted, Msg: "Hi GDM, it's a pleasure to get you in!", }}, - wantExitStatus: PamSuccess{ + wantPAMReturnValue: PamSuccess{ BrokerID: firstBrokerInfo.Id, msg: "Hi GDM, it's a pleasure to get you in!", }, @@ -451,7 +451,7 @@ func TestGdmModel(t *testing.T) { Access: auth.Granted, Msg: "", }}, - wantExitStatus: PamSuccess{ + wantPAMReturnValue: PamSuccess{ BrokerID: firstBrokerInfo.Id, }, }, @@ -494,7 +494,7 @@ func TestGdmModel(t *testing.T) { wantGdmAuthRes: []*authd.IAResponse{{ Access: auth.Granted, }}, - wantExitStatus: PamSuccess{ + wantPAMReturnValue: PamSuccess{ BrokerID: firstBrokerInfo.Id, }, }, @@ -539,7 +539,7 @@ func TestGdmModel(t *testing.T) { Access: auth.Granted, Msg: "Hi GDM, it's a pleasure to change your password!", }}, - wantExitStatus: PamSuccess{ + wantPAMReturnValue: PamSuccess{ BrokerID: firstBrokerInfo.Id, msg: "Hi GDM, it's a pleasure to change your password!", }, @@ -605,7 +605,7 @@ func TestGdmModel(t *testing.T) { Msg: "Hi GDM, it's a pleasure to change your password!", }, }, - wantExitStatus: PamSuccess{ + wantPAMReturnValue: PamSuccess{ BrokerID: firstBrokerInfo.Id, msg: "Hi GDM, it's a pleasure to change your password!", }, @@ -716,7 +716,7 @@ func TestGdmModel(t *testing.T) { Msg: "The password is the same as the old one", }, }, - wantExitStatus: gdmTestEarlyStopExitStatus, + wantPAMReturnValue: gdmTestEarlyStopReturnValue, }, "Authentication_is_ignored_if_not_requested_by_model_first": { clientOptions: append(slices.Clone(singleBrokerClientOptions), @@ -743,8 +743,8 @@ func TestGdmModel(t *testing.T) { gdm.EventType_startAuthentication, gdm.EventType_authEvent, }, - wantStage: proto.Stage_userSelection, - wantExitStatus: gdmTestEarlyStopExitStatus, + wantStage: proto.Stage_userSelection, + wantPAMReturnValue: gdmTestEarlyStopReturnValue, }, "Cancelled_with_preset_PAM_user_and_server_side_broker_and_authMode_selection": { clientOptions: append(slices.Clone(multiBrokerClientOptions), @@ -775,9 +775,9 @@ func TestGdmModel(t *testing.T) { gdm.EventType_startAuthentication, gdm.EventType_authEvent, }, - wantStage: proto.Stage_challenge, - wantGdmAuthRes: []*authd.IAResponse{{Access: auth.Cancelled}}, - wantExitStatus: gdmTestEarlyStopExitStatus, + wantStage: proto.Stage_challenge, + wantGdmAuthRes: []*authd.IAResponse{{Access: auth.Cancelled}}, + wantPAMReturnValue: gdmTestEarlyStopReturnValue, }, "Explicitly_cancelled_with_preset_PAM_user_and_server_side_broker_and_authMode_selection": { clientOptions: append(slices.Clone(multiBrokerClientOptions), @@ -807,8 +807,8 @@ func TestGdmModel(t *testing.T) { gdm.EventType_uiLayoutReceived, gdm.EventType_startAuthentication, }, - wantStage: proto.Stage_challenge, - wantExitStatus: gdmTestEarlyStopExitStatus, + wantStage: proto.Stage_challenge, + wantPAMReturnValue: gdmTestEarlyStopReturnValue, }, "Explicitly_cancelled_for_a_waiting_auth_mode_with_preset_PAM_user_and_server_side_broker_and_authMode_selection": { clientOptions: append(slices.Clone(multiBrokerClientOptions), @@ -847,9 +847,9 @@ func TestGdmModel(t *testing.T) { gdm.EventType_startAuthentication, gdm.EventType_authEvent, }, - wantGdmAuthRes: []*authd.IAResponse{{Access: auth.Cancelled}}, - wantStage: proto.Stage_challenge, - wantExitStatus: gdmTestEarlyStopExitStatus, + wantGdmAuthRes: []*authd.IAResponse{{Access: auth.Cancelled}}, + wantStage: proto.Stage_challenge, + wantPAMReturnValue: gdmTestEarlyStopReturnValue, }, "Implicitly_cancelled_for_a_waiting_auth_mode_with_preset_PAM_user_and_server_side_broker_and_authMode_selection": { clientOptions: append(slices.Clone(multiBrokerClientOptions), @@ -892,9 +892,9 @@ func TestGdmModel(t *testing.T) { gdm.EventType_startAuthentication, gdm.EventType_authEvent, }, - wantGdmAuthRes: []*authd.IAResponse{{Access: auth.Cancelled}}, - wantStage: proto.Stage_brokerSelection, - wantExitStatus: gdmTestEarlyStopExitStatus, + wantGdmAuthRes: []*authd.IAResponse{{Access: auth.Cancelled}}, + wantStage: proto.Stage_brokerSelection, + wantPAMReturnValue: gdmTestEarlyStopReturnValue, }, "Authenticated_with_preset_PAM_user_and_server_side_broker_and_authMode_selection_and_after_various_retries": { clientOptions: append(slices.Clone(singleBrokerClientOptions), @@ -948,8 +948,8 @@ func TestGdmModel(t *testing.T) { {Access: auth.Retry}, {Access: auth.Granted}, }, - wantStage: proto.Stage_challenge, - wantExitStatus: PamSuccess{BrokerID: firstBrokerInfo.Id}, + wantStage: proto.Stage_challenge, + wantPAMReturnValue: PamSuccess{BrokerID: firstBrokerInfo.Id}, }, "Authenticated_after_client_side_user_and_broker_and_authMode_selection": { clientOptions: append(slices.Clone(multiBrokerClientOptions), @@ -988,9 +988,9 @@ func TestGdmModel(t *testing.T) { gdm.EventType_startAuthentication, gdm.EventType_authEvent, }, - wantStage: proto.Stage_challenge, - wantGdmAuthRes: []*authd.IAResponse{{Access: auth.Granted}}, - wantExitStatus: PamSuccess{BrokerID: secondBrokerInfo.Id}, + wantStage: proto.Stage_challenge, + wantGdmAuthRes: []*authd.IAResponse{{Access: auth.Granted}}, + wantPAMReturnValue: PamSuccess{BrokerID: secondBrokerInfo.Id}, }, "Authenticated_after_client_side_user_and_broker_and_authMode_selection_and_after_various_retries": { clientOptions: append(slices.Clone(singleBrokerClientOptions), @@ -1052,8 +1052,8 @@ func TestGdmModel(t *testing.T) { {Access: auth.Retry}, {Access: auth.Granted}, }, - wantStage: proto.Stage_challenge, - wantExitStatus: PamSuccess{BrokerID: firstBrokerInfo.Id}, + wantStage: proto.Stage_challenge, + wantPAMReturnValue: PamSuccess{BrokerID: firstBrokerInfo.Id}, }, "Cancelled_auth_after_client_side_user_and_broker_and_authMode_selection": { clientOptions: append(slices.Clone(singleBrokerClientOptions), @@ -1095,9 +1095,9 @@ func TestGdmModel(t *testing.T) { wantMessages: []tea.Msg{ startAuthentication{}, }, - wantGdmAuthRes: []*authd.IAResponse{{Access: auth.Cancelled}}, - wantStage: proto.Stage_challenge, - wantExitStatus: gdmTestEarlyStopExitStatus, + wantGdmAuthRes: []*authd.IAResponse{{Access: auth.Cancelled}}, + wantStage: proto.Stage_challenge, + wantPAMReturnValue: gdmTestEarlyStopReturnValue, }, "AuthMode_selection_stage_from_client_after_server_side_broker_and_auth_mode_selection_if_there_is_only_one_auth_mode": { clientOptions: append(slices.Clone(singleBrokerClientOptions), @@ -1135,8 +1135,8 @@ func TestGdmModel(t *testing.T) { wantNoGdmEvents: []gdm.EventType{ gdm.EventType_authEvent, }, - wantStage: proto.Stage_authModeSelection, - wantExitStatus: gdmTestEarlyStopExitStatus, + wantStage: proto.Stage_authModeSelection, + wantPAMReturnValue: gdmTestEarlyStopReturnValue, }, "AuthMode_selection_stage_from_client_after_server_side_broker_and_auth_mode_selection_with_multiple_auth_modes": { clientOptions: append(slices.Clone(singleBrokerClientOptions), @@ -1175,8 +1175,8 @@ func TestGdmModel(t *testing.T) { wantNoGdmEvents: []gdm.EventType{ gdm.EventType_authEvent, }, - wantStage: proto.Stage_authModeSelection, - wantExitStatus: gdmTestEarlyStopExitStatus, + wantStage: proto.Stage_authModeSelection, + wantPAMReturnValue: gdmTestEarlyStopReturnValue, }, "AuthMode_selection_stage_from_client_after_client_side_broker_and_auth_mode_selection_if_there_is_only_one_auth_mode": { gdmEvents: []*gdm.EventData{ @@ -1221,8 +1221,8 @@ func TestGdmModel(t *testing.T) { gdm.EventType_startAuthentication, gdm.EventType_authEvent, }, - wantStage: proto.Stage_authModeSelection, - wantExitStatus: gdmTestEarlyStopExitStatus, + wantStage: proto.Stage_authModeSelection, + wantPAMReturnValue: gdmTestEarlyStopReturnValue, }, "Authenticated_after_auth_selection_stage_from_client_after_client_side_broker_and_auth_mode_selection_if_there_is_only_one_auth_mode": { clientOptions: append(slices.Clone(singleBrokerClientOptions), @@ -1288,7 +1288,7 @@ func TestGdmModel(t *testing.T) { wantGdmAuthRes: []*authd.IAResponse{ {Access: auth.Granted}, }, - wantExitStatus: PamSuccess{BrokerID: firstBrokerInfo.Id}, + wantPAMReturnValue: PamSuccess{BrokerID: firstBrokerInfo.Id}, }, "Authenticated_after_auth_selection_stage_from_client_after_client_side_broker_and_auth_mode_selection_with_multiple_auth_modes": { clientOptions: append(slices.Clone(singleBrokerClientOptions), @@ -1355,7 +1355,7 @@ func TestGdmModel(t *testing.T) { wantGdmAuthRes: []*authd.IAResponse{ {Access: auth.Granted}, }, - wantExitStatus: PamSuccess{BrokerID: firstBrokerInfo.Id}, + wantPAMReturnValue: PamSuccess{BrokerID: firstBrokerInfo.Id}, }, "Authenticated_with_qrcode_after_auth_selection_stage_from_client_after_client_side_broker_and_auth_mode_selection": { supportedLayouts: []*authd.UILayout{ @@ -1426,7 +1426,7 @@ func TestGdmModel(t *testing.T) { wantGdmAuthRes: []*authd.IAResponse{ {Access: auth.Granted}, }, - wantExitStatus: PamSuccess{BrokerID: firstBrokerInfo.Id}, + wantPAMReturnValue: PamSuccess{BrokerID: firstBrokerInfo.Id}, }, "Authenticated_with_qrcode_regenerated_after_auth_selection_stage_from_client_after_client_side_broker_and_auth_mode_selection": { timeout: 30 * time.Second, @@ -1513,7 +1513,7 @@ func TestGdmModel(t *testing.T) { {Access: auth.Cancelled}, {Access: auth.Granted}, }, - wantExitStatus: PamSuccess{BrokerID: firstBrokerInfo.Id}, + wantPAMReturnValue: PamSuccess{BrokerID: firstBrokerInfo.Id}, }, "Authenticated_with_qrcode_regenerated_after_wait_started_at_auth_selection_stage_from_client_after_client_side_broker_and_auth_mode_selection": { supportedLayouts: []*authd.UILayout{ @@ -1602,7 +1602,7 @@ func TestGdmModel(t *testing.T) { {Access: auth.Cancelled}, {Access: auth.Granted}, }, - wantExitStatus: PamSuccess{BrokerID: firstBrokerInfo.Id}, + wantPAMReturnValue: PamSuccess{BrokerID: firstBrokerInfo.Id}, }, "Broker_selection_stage_from_client_after_client_side_broker_and_auth_mode_selection_if_there_is_only_one_auth_mode": { gdmEvents: []*gdm.EventData{ @@ -1655,8 +1655,8 @@ func TestGdmModel(t *testing.T) { wantNoGdmEvents: []gdm.EventType{ gdm.EventType_authEvent, }, - wantStage: proto.Stage_brokerSelection, - wantExitStatus: gdmTestEarlyStopExitStatus, + wantStage: proto.Stage_brokerSelection, + wantPAMReturnValue: gdmTestEarlyStopReturnValue, }, "User_selection_stage_from_client_after_client_side_broker_and_auth_mode_selection_if_there_is_only_one_auth_mode": { gdmEvents: []*gdm.EventData{ @@ -1718,8 +1718,8 @@ func TestGdmModel(t *testing.T) { wantNoGdmEvents: []gdm.EventType{ gdm.EventType_authEvent, }, - wantStage: proto.Stage_userSelection, - wantExitStatus: gdmTestEarlyStopExitStatus, + wantStage: proto.Stage_userSelection, + wantPAMReturnValue: gdmTestEarlyStopReturnValue, }, // Error cases @@ -1733,7 +1733,7 @@ func TestGdmModel(t *testing.T) { gdm.EventType_brokersReceived, gdm.EventType_userSelected, }, - wantExitStatus: pamError{ + wantPAMReturnValue: pamError{ status: pam.ErrCredUnavail, msg: "UI does not support any layouts", }, @@ -1749,15 +1749,15 @@ func TestGdmModel(t *testing.T) { gdm.EventType_brokersReceived, gdm.EventType_userSelected, }, - wantExitStatus: pamError{ + wantPAMReturnValue: pamError{ status: pam.ErrAuthinfoUnavail, msg: "could not get current available brokers: brokers loading failed", }, wantNoBrokers: true, }, "Error_on_forced_quit": { - messages: []tea.Msg{tea.Quit()}, - wantExitStatus: gdmTestEarlyStopExitStatus, + messages: []tea.Msg{tea.Quit()}, + wantPAMReturnValue: gdmTestEarlyStopReturnValue, wantNoGdmEvents: []gdm.EventType{ gdm.EventType_userSelected, }, @@ -1777,7 +1777,7 @@ func TestGdmModel(t *testing.T) { wantNoGdmEvents: []gdm.EventType{ gdm.EventType_userSelected, }, - wantExitStatus: pamError{ + wantPAMReturnValue: pamError{ status: pam.ErrSystem, msg: "Sending GDM poll failed: Conversation error: poll response data member 0 invalid: missing event data", }, @@ -1793,7 +1793,7 @@ func TestGdmModel(t *testing.T) { wantNoGdmEvents: []gdm.EventType{ gdm.EventType_userSelected, }, - wantExitStatus: pamError{ + wantPAMReturnValue: pamError{ status: pam.ErrSystem, msg: "Sending GDM poll failed: Conversation error: poll response data member 0 invalid: missing event type", }, @@ -1809,7 +1809,7 @@ func TestGdmModel(t *testing.T) { gdm.EventType_brokersReceived, gdm.EventType_userSelected, }, - wantExitStatus: pamError{ + wantPAMReturnValue: pamError{ status: pam.ErrAuthinfoUnavail, msg: "No brokers available", }, @@ -1828,7 +1828,7 @@ func TestGdmModel(t *testing.T) { gdm.EventType_userSelected, gdm.EventType_brokersReceived, }, - wantExitStatus: pamError{ + wantPAMReturnValue: pamError{ status: pam.ErrSystem, msg: "error during broker selection", }, @@ -1849,7 +1849,7 @@ func TestGdmModel(t *testing.T) { gdm.EventType_brokersReceived, gdm.EventType_brokerSelected, }, - wantExitStatus: pamError{ + wantPAMReturnValue: pamError{ status: pam.ErrSystem, msg: "no session ID returned by broker", }, @@ -1869,7 +1869,7 @@ func TestGdmModel(t *testing.T) { gdm.EventType_brokersReceived, gdm.EventType_brokerSelected, }, - wantExitStatus: pamError{ + wantPAMReturnValue: pamError{ status: pam.ErrSystem, msg: "no encryption key returned by broker", }, @@ -1891,7 +1891,7 @@ func TestGdmModel(t *testing.T) { gdm.EventType_userSelected, gdm.EventType_brokersReceived, }, - wantExitStatus: pamError{ + wantPAMReturnValue: pamError{ status: pam.ErrSystem, msg: "encryption key sent by broker is not a valid base64 encoded string: illegal base64 data at input byte 2", }, @@ -1914,7 +1914,7 @@ func TestGdmModel(t *testing.T) { gdm.EventType_userSelected, gdm.EventType_brokersReceived, }, - wantExitStatus: pamError{ + wantPAMReturnValue: pamError{ status: pam.ErrSystem, msg: gdmTestIgnoredMessage, }, @@ -1945,7 +1945,7 @@ func TestGdmModel(t *testing.T) { gdm.EventType_brokerSelected, }, wantStage: gdmTestIgnoreStage, - wantExitStatus: pamError{ + wantPAMReturnValue: pamError{ status: pam.ErrSystem, msg: "invalid empty UI Layout information from broker", }, @@ -1976,7 +1976,7 @@ func TestGdmModel(t *testing.T) { gdm.EventType_brokerSelected, }, wantStage: gdmTestIgnoreStage, - wantExitStatus: pamError{ + wantPAMReturnValue: pamError{ status: pam.ErrCredUnavail, msg: "no supported authentication mode available for this provider", }, @@ -2006,7 +2006,7 @@ func TestGdmModel(t *testing.T) { gdm.EventType_brokersReceived, }, wantStage: gdmTestIgnoreStage, - wantExitStatus: pamError{ + wantPAMReturnValue: pamError{ status: pam.ErrSystem, msg: "can't select authentication mode: error selecting auth mode", }, @@ -2043,7 +2043,7 @@ func TestGdmModel(t *testing.T) { gdm.EventType_uiLayoutReceived, }, wantStage: gdmTestIgnoreStage, - wantExitStatus: pamError{ + wantPAMReturnValue: pamError{ status: pam.ErrSystem, msg: `Sending GDM event failed: Conversation error: unknown layout type: "invalid layout"`, }, @@ -2080,7 +2080,7 @@ func TestGdmModel(t *testing.T) { gdm.EventType_uiLayoutReceived, }, wantStage: proto.Stage_challenge, - wantExitStatus: pamError{ + wantPAMReturnValue: pamError{ status: pam.ErrSystem, msg: "some authentication error", }, @@ -2115,7 +2115,7 @@ func TestGdmModel(t *testing.T) { gdm.EventType_uiLayoutReceived, }, wantStage: gdmTestIgnoreStage, - wantExitStatus: pamError{ + wantPAMReturnValue: pamError{ status: pam.ErrSystem, msg: "invalid json data from provider: invalid character 'i' looking for beginning of value", }, @@ -2163,7 +2163,7 @@ func TestGdmModel(t *testing.T) { Access: auth.Denied, Msg: "you're not allowed!", }}, - wantExitStatus: pamError{ + wantPAMReturnValue: pamError{ status: pam.ErrAuth, msg: "you're not allowed!", }, @@ -2207,7 +2207,7 @@ func TestGdmModel(t *testing.T) { }, wantStage: gdmTestIgnoreStage, wantGdmAuthRes: []*authd.IAResponse{{Access: auth.Denied}}, - wantExitStatus: pamError{ + wantPAMReturnValue: pamError{ status: pam.ErrAuth, msg: "Access denied", }, @@ -2260,7 +2260,7 @@ func TestGdmModel(t *testing.T) { {Access: auth.Retry}, {Access: auth.Denied}, }, - wantExitStatus: pamError{ + wantPAMReturnValue: pamError{ status: pam.ErrAuth, msg: "Access denied", }, @@ -2302,7 +2302,7 @@ func TestGdmModel(t *testing.T) { Access: auth.Denied, Msg: `Access "" is not valid`, }}, - wantExitStatus: pamError{ + wantPAMReturnValue: pamError{ status: pam.ErrSystem, msg: `Unknown authentication access: ""`, }, @@ -2347,7 +2347,7 @@ func TestGdmModel(t *testing.T) { Access: auth.Denied, Msg: `Access "no way you get here!" is not valid`, }}, - wantExitStatus: pamError{ + wantPAMReturnValue: pamError{ status: pam.ErrSystem, msg: `Unknown authentication access: "no way you get here!"`, }, @@ -2374,7 +2374,7 @@ func TestGdmModel(t *testing.T) { gdm.EventType_authEvent, }, wantStage: gdmTestIgnoreStage, - wantExitStatus: pamError{ + wantPAMReturnValue: pamError{ status: pam.ErrSystem, msg: `unknown PAM stage: "-1"`, }, @@ -2385,14 +2385,14 @@ func TestGdmModel(t *testing.T) { Type: gdm.DataType_hello, }): errors.New("this is an hello error"), }, - wantExitStatus: pamError{ + wantPAMReturnValue: pamError{ status: pam.ErrCredUnavail, msg: "GDM initialization failed: Conversation error: this is an hello error", }, }, "Error_during_hello_on_protocol_mismatch": { protoVersion: 99999999, - wantExitStatus: pamError{ + wantPAMReturnValue: pamError{ status: pam.ErrCredUnavail, msg: "GDM protocol initialization failed, type hello, version 99999999", }, @@ -2401,7 +2401,7 @@ func TestGdmModel(t *testing.T) { convError: map[string]error{ gdm_test.DataToJSON(t, &gdm.Data{Type: gdm.DataType_poll}): errors.New("this is a poll error"), }, - wantExitStatus: pamError{ + wantPAMReturnValue: pamError{ status: pam.ErrSystem, msg: "Sending GDM poll failed: Conversation error: this is a poll error", }, @@ -2423,7 +2423,7 @@ func TestGdmModel(t *testing.T) { gdmEvents: []*gdm.EventData{ gdm_test.ChangeStageEvent(proto.Stage_brokerSelection), }, - wantExitStatus: pamError{ + wantPAMReturnValue: pamError{ status: pam.ErrSystem, msg: "Changing GDM stage failed: Conversation error: this is a stage change error", }, @@ -2439,7 +2439,7 @@ func TestGdmModel(t *testing.T) { }): errors.New("this is an UI capabilities request error"), }, wantNoBrokers: true, - wantExitStatus: pamError{ + wantPAMReturnValue: pamError{ status: pam.ErrSystem, msg: "Sending GDM UI capabilities Request failed: Conversation error: this is an UI capabilities request error", }, @@ -2483,7 +2483,7 @@ func TestGdmModel(t *testing.T) { gdm.EventType_authEvent, }, wantStage: proto.Stage_userSelection, - wantExitStatus: pamError{ + wantPAMReturnValue: pamError{ status: pam.ErrPermDenied, msg: `Changing username "gdm-pam-preset-user" to "another-selected-user" is not allowed`, }, @@ -2506,8 +2506,8 @@ func TestGdmModel(t *testing.T) { messagesToSend := tc.messages messagesToWait := append(tc.messages, tc.wantMessages...) - if tc.wantExitStatus != gdmTestEarlyStopExitStatus { - messagesToWait = append(messagesToWait, tc.wantExitStatus) + if tc.wantPAMReturnValue != gdmTestEarlyStopReturnValue { + messagesToWait = append(messagesToWait, tc.wantPAMReturnValue) } gdmMutex := sync.Mutex{} @@ -2527,9 +2527,9 @@ func TestGdmModel(t *testing.T) { wantRequests: tc.wantGdmRequests, } - var exitStatus PamReturnStatus + var pamReturnValue PamReturnValue uiModel := newUIModelForClients(pam_test.NewModuleTransactionDummy(gdmHandler), - Gdm, tc.sessionMode, tc.client, nil, &exitStatus) + Gdm, tc.sessionMode, tc.client, nil, &pamReturnValue) appState := gdmTestUIModel{ uiModel: uiModel, @@ -2673,20 +2673,20 @@ func TestGdmModel(t *testing.T) { appState.mu.Lock() defer appState.mu.Unlock() - if tc.wantExitStatus.Message() == gdmTestIgnoredMessage { - switch wantRet := tc.wantExitStatus.(type) { + if tc.wantPAMReturnValue.Message() == gdmTestIgnoredMessage { + switch wantRet := tc.wantPAMReturnValue.(type) { case PamReturnError: - exitErr, ok := exitStatus.(PamReturnError) - require.True(t, ok, "exit status should be an error") - require.Equal(t, wantRet.Status(), exitErr.Status()) + pamErr, ok := pamReturnValue.(PamReturnError) + require.True(t, ok, "Expected PAM return value to be an error") + require.Equal(t, wantRet.Status(), pamErr.Status()) case PamSuccess: - _, ok := exitStatus.(PamSuccess) - require.True(t, ok, "exit status should be a success") + _, ok := pamReturnValue.(PamSuccess) + require.True(t, ok, "Expected PAM return value to be a success") default: - t.Fatalf("Unexpected exit status: %v", wantRet) + t.Fatalf("Unexpected type for wantPAMReturnValue: %T", tc.wantPAMReturnValue) } } else { - require.Equal(t, tc.wantExitStatus, exitStatus) + require.Equal(t, tc.wantPAMReturnValue, pamReturnValue) } require.True(t, appState.gdmModel.conversationsStopped, @@ -2728,14 +2728,14 @@ func TestGdmModel(t *testing.T) { gdm_test.RequireEqualData(t, tc.wantGdmAuthRes, gdmHandler.authEvents) - if r, ok := tc.wantExitStatus.(PamReturnError); ok { - // If the model exited with error and that matches, we don't - // care much comparing all the expectations, since the final exit status + if r, ok := tc.wantPAMReturnValue.(PamReturnError); ok { + // If the model returned an error and that matches, we don't + // care much comparing all the expectations, since the final return value // is matching what we expect. switch r.Status() { case pam.ErrIgnore, pam.ErrAuth: default: - if r != gdmTestEarlyStopExitStatus { + if r != gdmTestEarlyStopReturnValue { return } if slices.Contains(tc.messages, tea.Quit()) { diff --git a/pam/internal/adapter/model.go b/pam/internal/adapter/model.go index 26a054b33f..d2c897b9cd 100644 --- a/pam/internal/adapter/model.go +++ b/pam/internal/adapter/model.go @@ -78,9 +78,9 @@ type uiModel struct { gdmModel gdmModel nativeModel nativeModel - // exitStatus is a pointer to the [PamReturnStatus] value where the - // exit status will be written to. - exitStatus *PamReturnStatus + // pamReturnValue is a pointer to the [PamReturnValue] where the + // return value will be written to. + pamReturnValue *PamReturnValue } /* global events */ @@ -128,29 +128,29 @@ type ChangeStage struct { type StageChanged ChangeStage // NewUIModel creates and initializes the main model orchestrator. -func NewUIModel(mTx pam.ModuleTransaction, clientType PamClientType, mode authd.SessionMode, conn *grpc.ClientConn, exitStatus *PamReturnStatus) tea.Model { +func NewUIModel(mTx pam.ModuleTransaction, clientType PamClientType, mode authd.SessionMode, conn *grpc.ClientConn, pamReturnValue *PamReturnValue) tea.Model { var userServiceClient authd.UserServiceClient if conn != nil && isSSHSession(mTx) { userServiceClient = authd.NewUserServiceClient(conn) } - m := newUIModelForClients(mTx, clientType, mode, authd.NewPAMClient(conn), userServiceClient, exitStatus) + m := newUIModelForClients(mTx, clientType, mode, authd.NewPAMClient(conn), userServiceClient, pamReturnValue) m.conn = conn return m } // newUIModelForClients is the internal implementation of [NewUIModel] for testing purposes. -func newUIModelForClients(mTx pam.ModuleTransaction, clientType PamClientType, mode authd.SessionMode, pamClient authd.PAMClient, userServiceClient authd.UserServiceClient, exitStatus *PamReturnStatus) uiModel { +func newUIModelForClients(mTx pam.ModuleTransaction, clientType PamClientType, mode authd.SessionMode, pamClient authd.PAMClient, userServiceClient authd.UserServiceClient, pamReturnValue *PamReturnValue) uiModel { m := uiModel{ - pamMTx: mTx, - clientType: clientType, - sessionMode: mode, - exitStatus: exitStatus, - client: pamClient, + pamMTx: mTx, + clientType: clientType, + sessionMode: mode, + pamReturnValue: pamReturnValue, + client: pamClient, } - if m.exitStatus != nil { - *m.exitStatus = errNoExitStatus + if m.pamReturnValue != nil { + *m.pamReturnValue = pamNoReturnValue } switch m.clientType { @@ -261,16 +261,16 @@ func (m uiModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, m.startHealthCheck() // Exit cases - case PamReturnStatus: + case PamReturnValue: safeMessageDebug(msg) - if m.exitStatus == nil { + if m.pamReturnValue == nil { return m, m.quit() } - if *m.exitStatus != errNoExitStatus { + if *m.pamReturnValue != pamNoReturnValue { // Nothing to do, we're already exiting... return m, nil } - *m.exitStatus = msg + *m.pamReturnValue = msg return m, m.quit() // Events @@ -560,7 +560,7 @@ func MsgFilter(model tea.Model, msg tea.Msg) tea.Msg { return msg } -var errNoExitStatus = pamError{status: pam.ErrSystem, msg: "model did not return anything"} +var pamNoReturnValue = pamError{status: pam.ErrSystem, msg: "model did not return anything"} // username returns currently selected user name. func (m uiModel) username() string { diff --git a/pam/internal/adapter/return.go b/pam/internal/adapter/return.go index 824f574885..14d112bc7d 100644 --- a/pam/internal/adapter/return.go +++ b/pam/internal/adapter/return.go @@ -6,14 +6,14 @@ import ( // Various signalling return messaging to PAM. -// PamReturnStatus is the interface that all PAM return types should implement. -type PamReturnStatus interface { +// PamReturnValue is the interface that all PAM return types should implement. +type PamReturnValue interface { Message() string } // PamReturnError is an interface that PAM errors return types should implement. type PamReturnError interface { - PamReturnStatus + PamReturnValue Status() pam.Error } diff --git a/pam/pam.go b/pam/pam.go index 89b2b4a168..e566a2f90d 100644 --- a/pam/pam.go +++ b/pam/pam.go @@ -94,7 +94,7 @@ func showPamMessage(mTx pam.ModuleTransaction, style pam.Style, msg string) erro return nil } -func sendReturnMessageToPam(mTx pam.ModuleTransaction, retStatus adapter.PamReturnStatus) { +func sendReturnMessageToPam(mTx pam.ModuleTransaction, retStatus adapter.PamReturnValue) { msg := retStatus.Message() if msg == "" { return @@ -324,8 +324,8 @@ func (h *pamModule) handleAuthRequest(mode authd.SessionMode, mTx pam.ModuleTran } defer closeConn() - var exitStatus adapter.PamReturnStatus - appState := adapter.NewUIModel(mTx, pamClientType, mode, conn, &exitStatus) + var pamReturnValue adapter.PamReturnValue + appState := adapter.NewUIModel(mTx, pamClientType, mode, conn, &pamReturnValue) teaOpts = append(teaOpts, tea.WithFilter(adapter.MsgFilter)) p := tea.NewProgram(appState, teaOpts...) if _, err := p.Run(); err != nil { @@ -333,24 +333,24 @@ func (h *pamModule) handleAuthRequest(mode authd.SessionMode, mTx pam.ModuleTran return pam.ErrAbort } - switch exitStatus := exitStatus.(type) { + switch returnValue := pamReturnValue.(type) { case adapter.PamSuccess: - if shouldSendAuthMessage(pamClientType, exitStatus.Message(), true) { - sendReturnMessageToPam(mTx, exitStatus) + if shouldSendAuthMessage(pamClientType, returnValue.Message(), true) { + sendReturnMessageToPam(mTx, returnValue) } return nil case adapter.PamReturnError: - if shouldSendAuthMessage(pamClientType, exitStatus.Message(), false) { - sendReturnMessageToPam(mTx, exitStatus) + if shouldSendAuthMessage(pamClientType, returnValue.Message(), false) { + sendReturnMessageToPam(mTx, returnValue) } - return fmt.Errorf("%w: %s", exitStatus.Status(), exitStatus.Message()) + return fmt.Errorf("%w: %s", returnValue.Status(), returnValue.Message()) default: // Preserve the previous behavior of showing any message associated with // unexpected exit statuses before returning the system error. - sendReturnMessageToPam(mTx, exitStatus) - return fmt.Errorf("%w: unknown exit code: %#v", pam.ErrSystem, exitStatus) + sendReturnMessageToPam(mTx, returnValue) + return fmt.Errorf("%w: unknown exit code: %#v", pam.ErrSystem, returnValue) } } From f95aaeb6dd1e4370ec5668a5a07a9bf7585fe986 Mon Sep 17 00:00:00 2001 From: Adrian Dombeck Date: Tue, 13 Jan 2026 16:35:16 +0100 Subject: [PATCH 02/11] pam: Set PAM_AUTHTOK on successful authentication By setting PAM_AUTHTOK the GNOME keyring is unlocked. UDENG-8799 --- pam/internal/adapter/authentication.go | 10 +++++++++- pam/internal/adapter/gdmmodel_test.go | 21 +++++++++++++-------- pam/internal/adapter/return.go | 1 + pam/pam.go | 5 +++++ 4 files changed, 28 insertions(+), 9 deletions(-) diff --git a/pam/internal/adapter/authentication.go b/pam/internal/adapter/authentication.go index 8caa6ac35b..f77396603d 100644 --- a/pam/internal/adapter/authentication.go +++ b/pam/internal/adapter/authentication.go @@ -361,7 +361,15 @@ func (m authenticationModel) Update(msg tea.Msg) (authModel authenticationModel, switch msg.access { case auth.Granted: - return m, sendEvent(PamSuccess{BrokerID: m.currentBrokerID, msg: authMsg}) + var secret string + if msg.secret != nil { + secret = *msg.secret + } else if m.currentSecret != "" { + secret = m.currentSecret + } else { + log.Warningf(context.Background(), "authentication granted, but no secret is available, cannot set PAM_AUTHTOK") + } + return m, sendEvent(PamSuccess{BrokerID: m.currentBrokerID, AuthTok: secret, msg: authMsg}) case auth.Retry: m.errorMsg = authMsg diff --git a/pam/internal/adapter/gdmmodel_test.go b/pam/internal/adapter/gdmmodel_test.go index 2cce848f26..b4167f358d 100644 --- a/pam/internal/adapter/gdmmodel_test.go +++ b/pam/internal/adapter/gdmmodel_test.go @@ -271,7 +271,7 @@ func TestGdmModel(t *testing.T) { }, wantGdmAuthRes: []*authd.IAResponse{{Access: auth.Granted}}, wantStage: proto.Stage_challenge, - wantPAMReturnValue: PamSuccess{BrokerID: firstBrokerInfo.Id}, + wantPAMReturnValue: PamSuccess{BrokerID: firstBrokerInfo.Id, AuthTok: "gdm-good-password"}, }, "Authenticated_with_preset_PAM_user_using_legacy_challenge_and_server_side_broker_and_authMode_selection": { clientOptions: append(slices.Clone(singleBrokerClientOptions), @@ -306,7 +306,7 @@ func TestGdmModel(t *testing.T) { }, wantGdmAuthRes: []*authd.IAResponse{{Access: auth.Granted}}, wantStage: proto.Stage_challenge, - wantPAMReturnValue: PamSuccess{BrokerID: firstBrokerInfo.Id}, + wantPAMReturnValue: PamSuccess{BrokerID: firstBrokerInfo.Id, AuthTok: "gdm-good-password"}, }, "Authenticated_with_preset_PAM_user_updated_and_server_side_broker_and_authMode_selection": { clientOptions: append(slices.Clone(singleBrokerClientOptions), @@ -362,7 +362,7 @@ func TestGdmModel(t *testing.T) { }, wantGdmAuthRes: []*authd.IAResponse{{Access: auth.Granted}}, wantStage: proto.Stage_challenge, - wantPAMReturnValue: PamSuccess{BrokerID: firstBrokerInfo.Id}, + wantPAMReturnValue: PamSuccess{BrokerID: firstBrokerInfo.Id, AuthTok: "gdm-good-password"}, }, "Authenticated_with_message_with_preset_PAM_user_and_server_side_broker_and_authMode_selection": { clientOptions: append(slices.Clone(multiBrokerClientOptions), @@ -406,6 +406,7 @@ func TestGdmModel(t *testing.T) { }}, wantPAMReturnValue: PamSuccess{ BrokerID: firstBrokerInfo.Id, + AuthTok: "gdm-good-password", msg: "Hi GDM, it's a pleasure to get you in!", }, }, @@ -453,6 +454,7 @@ func TestGdmModel(t *testing.T) { }}, wantPAMReturnValue: PamSuccess{ BrokerID: firstBrokerInfo.Id, + AuthTok: "gdm-good-password", }, }, "New_password_changed_after_server_side_broker_and_authMode_selection": { @@ -496,6 +498,7 @@ func TestGdmModel(t *testing.T) { }}, wantPAMReturnValue: PamSuccess{ BrokerID: firstBrokerInfo.Id, + AuthTok: "gdm-good-password", }, }, "New_password_changed_with_message_with_preset_PAM_user_and_server_side_broker_and_authMode_selection": { @@ -541,6 +544,7 @@ func TestGdmModel(t *testing.T) { }}, wantPAMReturnValue: PamSuccess{ BrokerID: firstBrokerInfo.Id, + AuthTok: "gdm-good-password", msg: "Hi GDM, it's a pleasure to change your password!", }, }, @@ -607,6 +611,7 @@ func TestGdmModel(t *testing.T) { }, wantPAMReturnValue: PamSuccess{ BrokerID: firstBrokerInfo.Id, + AuthTok: "gdm-good-password", msg: "Hi GDM, it's a pleasure to change your password!", }, }, @@ -949,7 +954,7 @@ func TestGdmModel(t *testing.T) { {Access: auth.Granted}, }, wantStage: proto.Stage_challenge, - wantPAMReturnValue: PamSuccess{BrokerID: firstBrokerInfo.Id}, + wantPAMReturnValue: PamSuccess{BrokerID: firstBrokerInfo.Id, AuthTok: "gdm-good-password"}, }, "Authenticated_after_client_side_user_and_broker_and_authMode_selection": { clientOptions: append(slices.Clone(multiBrokerClientOptions), @@ -990,7 +995,7 @@ func TestGdmModel(t *testing.T) { }, wantStage: proto.Stage_challenge, wantGdmAuthRes: []*authd.IAResponse{{Access: auth.Granted}}, - wantPAMReturnValue: PamSuccess{BrokerID: secondBrokerInfo.Id}, + wantPAMReturnValue: PamSuccess{BrokerID: secondBrokerInfo.Id, AuthTok: "gdm-good-password"}, }, "Authenticated_after_client_side_user_and_broker_and_authMode_selection_and_after_various_retries": { clientOptions: append(slices.Clone(singleBrokerClientOptions), @@ -1053,7 +1058,7 @@ func TestGdmModel(t *testing.T) { {Access: auth.Granted}, }, wantStage: proto.Stage_challenge, - wantPAMReturnValue: PamSuccess{BrokerID: firstBrokerInfo.Id}, + wantPAMReturnValue: PamSuccess{BrokerID: firstBrokerInfo.Id, AuthTok: "gdm-good-password"}, }, "Cancelled_auth_after_client_side_user_and_broker_and_authMode_selection": { clientOptions: append(slices.Clone(singleBrokerClientOptions), @@ -1288,7 +1293,7 @@ func TestGdmModel(t *testing.T) { wantGdmAuthRes: []*authd.IAResponse{ {Access: auth.Granted}, }, - wantPAMReturnValue: PamSuccess{BrokerID: firstBrokerInfo.Id}, + wantPAMReturnValue: PamSuccess{BrokerID: firstBrokerInfo.Id, AuthTok: "gdm-good-password"}, }, "Authenticated_after_auth_selection_stage_from_client_after_client_side_broker_and_auth_mode_selection_with_multiple_auth_modes": { clientOptions: append(slices.Clone(singleBrokerClientOptions), @@ -1355,7 +1360,7 @@ func TestGdmModel(t *testing.T) { wantGdmAuthRes: []*authd.IAResponse{ {Access: auth.Granted}, }, - wantPAMReturnValue: PamSuccess{BrokerID: firstBrokerInfo.Id}, + wantPAMReturnValue: PamSuccess{BrokerID: firstBrokerInfo.Id, AuthTok: "1234"}, }, "Authenticated_with_qrcode_after_auth_selection_stage_from_client_after_client_side_broker_and_auth_mode_selection": { supportedLayouts: []*authd.UILayout{ diff --git a/pam/internal/adapter/return.go b/pam/internal/adapter/return.go index 14d112bc7d..2dbafdba14 100644 --- a/pam/internal/adapter/return.go +++ b/pam/internal/adapter/return.go @@ -20,6 +20,7 @@ type PamReturnError interface { // PamSuccess signals PAM module to return with provided pam.Success and Quit tea.Model. type PamSuccess struct { BrokerID string + AuthTok string msg string } diff --git a/pam/pam.go b/pam/pam.go index e566a2f90d..ff5399a20c 100644 --- a/pam/pam.go +++ b/pam/pam.go @@ -338,6 +338,11 @@ func (h *pamModule) handleAuthRequest(mode authd.SessionMode, mTx pam.ModuleTran if shouldSendAuthMessage(pamClientType, returnValue.Message(), true) { sendReturnMessageToPam(mTx, returnValue) } + if returnValue.AuthTok != "" { + if err := mTx.SetItem(pam.Authtok, returnValue.AuthTok); err != nil { + return err + } + } return nil case adapter.PamReturnError: From 1e5f2ce98cb64fcda317d2d63673cd65fce18fc5 Mon Sep 17 00:00:00 2001 From: Adrian Dombeck Date: Fri, 17 Apr 2026 15:08:58 +0200 Subject: [PATCH 03/11] pam: Add TODO comments --- pam/internal/adapter/authentication.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pam/internal/adapter/authentication.go b/pam/internal/adapter/authentication.go index f77396603d..722d3b2644 100644 --- a/pam/internal/adapter/authentication.go +++ b/pam/internal/adapter/authentication.go @@ -240,6 +240,8 @@ func (m authenticationModel) Update(msg tea.Msg) (authModel authenticationModel, // If the session is for authentication, we allow the user to set the same password again, to avoid // that the user is forced to change their password if e.g. device code flow is forced when // the refresh token is expired. + // TODO: This will not select the correct secret in case the last authentication step uses a secret + // which is not the local password (e.g. OTP). oldPassword = m.currentSecret } @@ -362,6 +364,8 @@ func (m authenticationModel) Update(msg tea.Msg) (authModel authenticationModel, switch msg.access { case auth.Granted: var secret string + // TODO: This will not select the correct secret in case the last authentication step uses a secret + // which is not the local password (e.g. OTP). if msg.secret != nil { secret = *msg.secret } else if m.currentSecret != "" { From e84f8b4e7c29398b63b738a4ecd96ca065b9a0d2 Mon Sep 17 00:00:00 2001 From: Adrian Dombeck Date: Fri, 24 Apr 2026 11:02:54 +0200 Subject: [PATCH 04/11] e2e-tests/vm: Fix ssh script called without release / VM name Ensure that VM_NAME is exported to the environment of the ssh script to avoid Error: Missing required argument --- e2e-tests/vm/provision-authd.sh | 2 +- e2e-tests/vm/provision-ubuntu.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/e2e-tests/vm/provision-authd.sh b/e2e-tests/vm/provision-authd.sh index 103f0c83b6..9fe2cb25bc 100755 --- a/e2e-tests/vm/provision-authd.sh +++ b/e2e-tests/vm/provision-authd.sh @@ -141,7 +141,7 @@ assert_env_vars RELEASE BROKER ARTIFACTS_DIR="${ARTIFACTS_DIR:-${DATA_DIR}/${RELEASE}}" if [ -z "${VM_NAME:-}" ]; then - VM_NAME="${VM_NAME_BASE}-${RELEASE}" + export VM_NAME="${VM_NAME_BASE}-${RELEASE}" fi export VM_NAME diff --git a/e2e-tests/vm/provision-ubuntu.sh b/e2e-tests/vm/provision-ubuntu.sh index dca684fcca..3a55c4b8f2 100755 --- a/e2e-tests/vm/provision-ubuntu.sh +++ b/e2e-tests/vm/provision-ubuntu.sh @@ -113,7 +113,7 @@ ARTIFACTS_DIR="${ARTIFACTS_DIR:-${DATA_DIR}/${RELEASE}}" CLOUD_INIT_TEMPLATE="${SCRIPT_DIR}/cloud-init-template-${RELEASE_NAME}.yaml" if [ -z "${VM_NAME:-}" ]; then - VM_NAME="${VM_NAME_BASE}-${RELEASE}" + export VM_NAME="${VM_NAME_BASE}-${RELEASE}" fi function cloud_init_finished() { From e9211fd6f8ce5ab1be2e2714a5a3511e60a07ec0 Mon Sep 17 00:00:00 2001 From: Adrian Dombeck Date: Wed, 6 May 2026 00:03:14 +0200 Subject: [PATCH 05/11] e2e-tests: Support multiple commands in execute_as_user For example to pipe the output of one command into another. --- e2e-tests/resources/SSH.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/e2e-tests/resources/SSH.py b/e2e-tests/resources/SSH.py index 7083228a26..455f63c3c8 100644 --- a/e2e-tests/resources/SSH.py +++ b/e2e-tests/resources/SSH.py @@ -53,7 +53,8 @@ async def execute_as_user(self, user:str, command: str, timeout: int|None = 30) command = (f"sudo -u {user} " f"XDG_RUNTIME_DIR=/run/user/$(id -u {user}) " f"DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u {user})/bus " - f"{command}") + "-- " + f"sh -c \"{command}\"") return await self.execute(command, timeout) @keyword From b891208d53c0e1737382dfcbfdfec93e89035f8d Mon Sep 17 00:00:00 2001 From: Adrian Dombeck Date: Fri, 24 Apr 2026 13:59:42 +0200 Subject: [PATCH 06/11] e2e-tests: Test that GNOME keyring is unlocked --- e2e-tests/resources/authd.resource | 4 ++++ e2e-tests/tests/login_gdm.robot | 2 ++ e2e-tests/vm/cloud-init-template-noble.yaml | 2 ++ e2e-tests/vm/cloud-init-template-resolute.yaml | 2 ++ e2e-tests/vm/cloud-init-template-stonking.yaml | 2 ++ 5 files changed, 12 insertions(+) diff --git a/e2e-tests/resources/authd.resource b/e2e-tests/resources/authd.resource index e485dded9b..02065529d9 100644 --- a/e2e-tests/resources/authd.resource +++ b/e2e-tests/resources/authd.resource @@ -72,3 +72,7 @@ Check That Remote User Can Run Sudo Commands SSH.Execute echo ${local_password} | sudo -S touch sudo-test.txt ${output} = SSH.Execute ls -l sudo-test.txt Should Contain ${output} sudo-test.txt + + +Check that GNOME keyring is unlocked + SSH.Execute As Current User echo '' | secret-tool store --label=test test key diff --git a/e2e-tests/tests/login_gdm.robot b/e2e-tests/tests/login_gdm.robot index 30e2dc9746..4284f932c7 100644 --- a/e2e-tests/tests/login_gdm.robot +++ b/e2e-tests/tests/login_gdm.robot @@ -22,6 +22,7 @@ Test login with GDM # Log in with remote user with device code flow via GDM Log In With Remote User Through GDM: QR Code ${username} ${local_password} + Check that GNOME keyring is unlocked # Check remote user is properly added to the system Check If User Was Added Properly ${username} @@ -30,3 +31,4 @@ Test login with GDM # Log in with remote user with local password via GDM Log In With Remote User Through GDM: Local Password ${username} ${local_password} + Check that GNOME keyring is unlocked diff --git a/e2e-tests/vm/cloud-init-template-noble.yaml b/e2e-tests/vm/cloud-init-template-noble.yaml index dc640bdd97..14a31b9a6e 100644 --- a/e2e-tests/vm/cloud-init-template-noble.yaml +++ b/e2e-tests/vm/cloud-init-template-noble.yaml @@ -29,6 +29,8 @@ packages: - gnome-terminal - gsettings-ubuntu-schemas - libpam-pwquality + # Required to test that the keyring is unlocked (ships secret-tool) + - libsecret-tools - openssh-server - policykit-desktop-privileges # Required to get the VM's IP address diff --git a/e2e-tests/vm/cloud-init-template-resolute.yaml b/e2e-tests/vm/cloud-init-template-resolute.yaml index 8a0f284ac2..41916c9a96 100644 --- a/e2e-tests/vm/cloud-init-template-resolute.yaml +++ b/e2e-tests/vm/cloud-init-template-resolute.yaml @@ -28,6 +28,8 @@ packages: - gnome-shell-ubuntu-extensions - gsettings-ubuntu-schemas - libpam-pwquality + # Required to test that the keyring is unlocked (ships secret-tool) + - libsecret-tools - openssh-server - policykit-desktop-privileges # Required to get the VM's IP address diff --git a/e2e-tests/vm/cloud-init-template-stonking.yaml b/e2e-tests/vm/cloud-init-template-stonking.yaml index 97a27aa719..cb46df010a 100644 --- a/e2e-tests/vm/cloud-init-template-stonking.yaml +++ b/e2e-tests/vm/cloud-init-template-stonking.yaml @@ -28,6 +28,8 @@ packages: - gnome-shell-ubuntu-extensions - gsettings-ubuntu-schemas - libpam-pwquality + # Required to test that the keyring is unlocked (ships secret-tool) + - libsecret-tools - openssh-server - policykit-desktop-privileges # Required to get the VM's IP address From f7dc9438985883a7bd0fe9380748387af7c91028 Mon Sep 17 00:00:00 2001 From: Adrian Dombeck Date: Wed, 6 May 2026 09:48:34 +0200 Subject: [PATCH 07/11] e2e-tests: Bump timeout when launching apps I'm repeatedly seeing the launch of ptyxis time out after 30 seconds. Let's see if bumping the timeout fixes it. --- e2e-tests/resources/utils.resource | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/e2e-tests/resources/utils.resource b/e2e-tests/resources/utils.resource index 8ce4e85182..5044660e8d 100644 --- a/e2e-tests/resources/utils.resource +++ b/e2e-tests/resources/utils.resource @@ -215,7 +215,7 @@ Submit Run Command Dialog Launch App [Arguments] ${app-id} - SSH.Execute As Current User gtk-launch ${app-id} + SSH.Execute As Current User gtk-launch ${app-id} timeout=60 Cancel Operation From d36216ba85f5f6c549db7f6641661005d2484f03 Mon Sep 17 00:00:00 2001 From: Adrian Dombeck Date: Thu, 18 Jun 2026 02:15:53 +0200 Subject: [PATCH 08/11] pam/go-exec: Print PAM_AUTHTOK in test builds to track it in golden files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PAM_AUTHTOK can only be read by service modules — applications get PAM_BAD_ITEM from pam_get_item(3). Since pam-runner is an application, it cannot read the token directly after pam_authenticate/pam_chauthtok returns. Instead, use a reportAuthtok hook in pam.go (no-op by default, following the debugMessageFormatter pattern) that is overridden under the pam_debug build tag to print the token to stdout. This works for both the exec child (which inherits the PAM module's stdout so output reaches pam-runner's PTY) and the GDM native .so path (also built with pam_debug in tests). The print happens right after SetItem, so it appears before pam-runner's printPamResult output, and the existing waitForRunnerResult regex captures it naturally in the final snapshot. Co-Authored-By: Claude Sonnet 4.6 --- .../Authenticate_user_and_add_it_to_local_group | 1 + .../Authenticate_user_and_offer_password_reset | 1 + ...ate_user_and_reset_password_while_enforcing_policy | 1 + ...eset_password_with_case_insensitive_user_selection | 3 +++ .../Authenticate_user_successfully | 1 + ...enticate_user_successfully_after_trying_empty_user | 1 + ..._user_successfully_with_invalid_connection_timeout | 1 + ...r_successfully_with_password_only_supported_method | 1 + .../Authenticate_user_successfully_with_preset_user | 1 + .../Authenticate_user_successfully_with_upper_case | 1 + ...cate_user_successfully_with_upper_case_preset_user | 1 + .../Authenticate_user_switching_auth_mode | 1 + .../Authenticate_user_switching_username | 1 + .../Authenticate_user_with_form_mode_with_button | 1 + .../TestCLIAuthenticate/Authenticate_user_with_mfa | 1 + ...with_mfa_and_reset_password_while_enforcing_policy | 1 + ...uthenticate_with_warnings_on_unsupported_arguments | 1 + ...on_if_newpassword_does_not_match_required_criteria | 1 + .../Prevent_user_from_switching_username | 1 + .../Remember_last_successful_broker_and_mode | 2 ++ .../TestCLIChangeAuthTok/Change_passwd_after_MFA_auth | 1 + ...assword_successfully_and_authenticate_with_new_one | 2 ++ ..._and_authenticate_with_new_one_with_different_case | 2 ++ ...ry_if_new_password_does_not_match_quality_criteria | 1 + .../Retry_if_new_password_is_rejected_by_broker | 2 ++ .../Retry_if_new_password_is_same_of_previous | 1 + .../Retry_if_password_confirmation_is_not_the_same | 1 + .../Authenticate_user_and_accept_password_reset | 1 + .../Authenticate_user_and_add_it_to_local_group | 1 + .../Authenticate_user_and_offer_password_reset | 1 + ...ate_user_and_reset_password_while_enforcing_policy | 1 + ...eset_password_with_case_insensitive_user_selection | 3 +++ .../Authenticate_user_on_ssh_service | 1 + ..._on_ssh_service_with_custom_name_and_auth_info_env | 1 + ...on_ssh_service_with_custom_name_and_connection_env | 1 + .../Authenticate_user_successfully | 1 + ..._successfully_using_upper_case_with_user_selection | 1 + ..._user_successfully_with_invalid_connection_timeout | 1 + ...r_successfully_with_password_only_supported_method | 1 + ...ully_with_password_only_supported_method_in_polkit | 1 + .../Authenticate_user_successfully_with_upper_case | 1 + ...Authenticate_user_successfully_with_user_selection | 1 + .../Authenticate_user_switching_auth_mode | 1 + .../Authenticate_user_switching_username | 1 + .../Authenticate_user_with_form_mode_with_button | 1 + ...enticate_user_with_form_mode_with_button_in_polkit | 1 + ...r_with_form_mode_with_button_two_supported_methods | 1 + .../TestNativeAuthenticate/Authenticate_user_with_mfa | 1 + ...with_mfa_and_reset_password_while_enforcing_policy | 1 + ...Authenticate_user_with_mfa_and_reset_same_password | 1 + ...uthenticate_with_warnings_on_unsupported_arguments | 1 + ...on_if_newpassword_does_not_match_required_criteria | 1 + .../Prevent_preset_user_from_switching_username | 1 + .../Remember_last_successful_broker_and_mode | 2 ++ .../Change_passwd_after_MFA_auth | 1 + ...assword_successfully_and_authenticate_with_new_one | 2 ++ ..._and_authenticate_with_new_one_with_different_case | 2 ++ ...h_single_broker_and_password_only_supported_method | 2 ++ ...ry_if_new_password_does_not_match_quality_criteria | 1 + .../Retry_if_new_password_is_rejected_by_broker | 1 + .../Retry_if_new_password_is_same_of_previous | 1 + .../Retry_if_password_confirmation_is_not_the_same | 1 + pam/pam.go | 6 ++++++ pam/pam_authtok_debug.go | 11 +++++++++++ 64 files changed, 91 insertions(+) create mode 100644 pam/pam_authtok_debug.go diff --git a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_and_add_it_to_local_group b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_and_add_it_to_local_group index 045ae0cd42..c35c4317ab 100644 --- a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_and_add_it_to_local_group +++ b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_and_add_it_to_local_group @@ -19,6 +19,7 @@ Gimme your password: Press escape key to go back to select the authentication method ──────────────────────────────────────────────────────────────────────────────── + PAM_AUTHTOK: "goodpass" PAM Authenticate() User: "user-local-groups-integration-auth-cli@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_and_offer_password_reset b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_and_offer_password_reset index 9de5dba592..34ef12826a 100644 --- a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_and_offer_password_reset +++ b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_and_offer_password_reset @@ -34,6 +34,7 @@ New password: Press escape key to go back to choose the provider ──────────────────────────────────────────────────────────────────────────────── + PAM_AUTHTOK: "goodpass" PAM Authenticate() User: "user-can-reset@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_and_reset_password_while_enforcing_policy b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_and_reset_password_while_enforcing_policy index 52afcb4cc6..27585dbc5a 100644 --- a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_and_reset_password_while_enforcing_policy +++ b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_and_reset_password_while_enforcing_policy @@ -57,6 +57,7 @@ Confirm password: Press escape key to go back to choose the provider ──────────────────────────────────────────────────────────────────────────────── + PAM_AUTHTOK: "authd2404" PAM Authenticate() User: "user-needs-reset-integration-mandatory@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_and_reset_password_with_case_insensitive_user_selection b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_and_reset_password_with_case_insensitive_user_selection index 7af3a74caa..dea592798c 100644 --- a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_and_reset_password_with_case_insensitive_user_selection +++ b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_and_reset_password_with_case_insensitive_user_selection @@ -58,6 +58,7 @@ Confirm password: Press escape key to go back to choose the provider ──────────────────────────────────────────────────────────────────────────────── + PAM_AUTHTOK: "authd2404" PAM Authenticate() User: "user-needs-reset-integration-case-insensitive-authenticate-user-and-reset-password-with-case-insensitive-user-selection@example.com" Result: success @@ -78,6 +79,7 @@ Gimme your password: Press escape key to go back to select the authentication method ──────────────────────────────────────────────────────────────────────────────── + PAM_AUTHTOK: "authd2404" PAM Authenticate() User: "user-needs-reset-integration-case-insensitive-authenticate-user-and-reset-password-with-case-insensitive-user-selection@example.com" Result: success @@ -98,6 +100,7 @@ Gimme your password: Press escape key to go back to select the authentication method ──────────────────────────────────────────────────────────────────────────────── + PAM_AUTHTOK: "authd2404" PAM Authenticate() User: "user-needs-reset-integration-case-insensitive-authenticate-user-and-reset-password-with-case-insensitive-user-selection@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_successfully b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_successfully index 557b532819..c1a9fd920c 100644 --- a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_successfully +++ b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_successfully @@ -19,6 +19,7 @@ Gimme your password: Press escape key to go back to select the authentication method ──────────────────────────────────────────────────────────────────────────────── + PAM_AUTHTOK: "goodpass" PAM Authenticate() User: "user-integration-simple-testcliauthenticate@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_successfully_after_trying_empty_user b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_successfully_after_trying_empty_user index 52729238e3..3a607f1e8a 100644 --- a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_successfully_after_trying_empty_user +++ b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_successfully_after_trying_empty_user @@ -23,6 +23,7 @@ Gimme your password: Press escape key to go back to select the authentication method ──────────────────────────────────────────────────────────────────────────────── + PAM_AUTHTOK: "goodpass" PAM Authenticate() User: "user-integration-was-empty@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_successfully_with_invalid_connection_timeout b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_successfully_with_invalid_connection_timeout index c5adeb289d..a1e1096596 100644 --- a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_successfully_with_invalid_connection_timeout +++ b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_successfully_with_invalid_connection_timeout @@ -19,6 +19,7 @@ Gimme your password: Press escape key to go back to select the authentication method ──────────────────────────────────────────────────────────────────────────────── + PAM_AUTHTOK: "goodpass" PAM Authenticate() User: "user-integration-invalid-timeout-testcliauthenticate@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_successfully_with_password_only_supported_method b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_successfully_with_password_only_supported_method index e5b0bf8d58..6c7af33864 100644 --- a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_successfully_with_password_only_supported_method +++ b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_successfully_with_password_only_supported_method @@ -19,6 +19,7 @@ Gimme your password: Press escape key to go back to choose the provider ──────────────────────────────────────────────────────────────────────────────── + PAM_AUTHTOK: "goodpass" PAM Authenticate() User: "user-auth-modes-password-integration-cli@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_successfully_with_preset_user b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_successfully_with_preset_user index 30c79ac43f..7bc3c84723 100644 --- a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_successfully_with_preset_user +++ b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_successfully_with_preset_user @@ -13,6 +13,7 @@ Gimme your password: Press escape key to go back to select the authentication method ──────────────────────────────────────────────────────────────────────────────── + PAM_AUTHTOK: "goodpass" PAM Authenticate() User: "user-integration-preset-testcliauthenticate@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_successfully_with_upper_case b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_successfully_with_upper_case index cb21aec151..2de2bc95b1 100644 --- a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_successfully_with_upper_case +++ b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_successfully_with_upper_case @@ -19,6 +19,7 @@ Gimme your password: Press escape key to go back to select the authentication method ──────────────────────────────────────────────────────────────────────────────── + PAM_AUTHTOK: "goodpass" PAM Authenticate() User: "user-integration-upper-case-testcliauthenticate@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_successfully_with_upper_case_preset_user b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_successfully_with_upper_case_preset_user index d58650a01a..976726a5e7 100644 --- a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_successfully_with_upper_case_preset_user +++ b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_successfully_with_upper_case_preset_user @@ -13,6 +13,7 @@ Gimme your password: Press escape key to go back to select the authentication method ──────────────────────────────────────────────────────────────────────────────── + PAM_AUTHTOK: "goodpass" PAM Authenticate() User: "user-integration-preset-upper-case-testcliauthenticate@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_switching_auth_mode b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_switching_auth_mode index 2148a0fce3..a7d8ee0c1e 100644 --- a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_switching_auth_mode +++ b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_switching_auth_mode @@ -69,6 +69,7 @@ Gimme your password: Press escape key to go back to select the authentication method ──────────────────────────────────────────────────────────────────────────────── + PAM_AUTHTOK: "goodpass" PAM Authenticate() User: "user-integration-switch-mode@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_switching_username b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_switching_username index 4057bf1cfb..3820797705 100644 --- a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_switching_username +++ b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_switching_username @@ -28,6 +28,7 @@ Gimme your password: Press escape key to go back to select the authentication method ──────────────────────────────────────────────────────────────────────────────── + PAM_AUTHTOK: "goodpass" PAM Authenticate() User: "user-integration-username-switched@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_with_form_mode_with_button b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_with_form_mode_with_button index d5b9b8db74..5944f09ad7 100644 --- a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_with_form_mode_with_button +++ b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_with_form_mode_with_button @@ -47,6 +47,7 @@ Enter your one time credential: Press escape key to go back to select the authentication method ──────────────────────────────────────────────────────────────────────────────── + PAM_AUTHTOK: "temporary pass00" PAM Authenticate() User: "user-integration-form-w-button@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_with_mfa b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_with_mfa index deeafa5253..13d489e0c7 100644 --- a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_with_mfa +++ b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_with_mfa @@ -67,6 +67,7 @@ Unlock your phone +33... or accept request on web interface Press escape key to go back to select the authentication method ──────────────────────────────────────────────────────────────────────────────── + PAM_AUTHTOK: "goodpass" PAM Authenticate() User: "user-mfa@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_with_mfa_and_reset_password_while_enforcing_policy b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_with_mfa_and_reset_password_while_enforcing_policy index 8e294f8969..32dc3dd285 100644 --- a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_with_mfa_and_reset_password_while_enforcing_policy +++ b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_user_with_mfa_and_reset_password_while_enforcing_policy @@ -114,6 +114,7 @@ Confirm password: Press escape key to go back to choose the provider ──────────────────────────────────────────────────────────────────────────────── + PAM_AUTHTOK: "authd2404" PAM Authenticate() User: "user-mfa-with-reset@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_with_warnings_on_unsupported_arguments b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_with_warnings_on_unsupported_arguments index 3df5add7b0..e0ae897313 100644 --- a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_with_warnings_on_unsupported_arguments +++ b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Authenticate_with_warnings_on_unsupported_arguments @@ -19,6 +19,7 @@ Gimme your password: Press escape key to go back to select the authentication method ──────────────────────────────────────────────────────────────────────────────── + PAM_AUTHTOK: "goodpass" PAM Authenticate() User: "user2@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Deny_authentication_if_newpassword_does_not_match_required_criteria b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Deny_authentication_if_newpassword_does_not_match_required_criteria index fdc1d6eeb2..cd0a18a6a1 100644 --- a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Deny_authentication_if_newpassword_does_not_match_required_criteria +++ b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Deny_authentication_if_newpassword_does_not_match_required_criteria @@ -132,6 +132,7 @@ Confirm password: Press escape key to go back to choose the provider ──────────────────────────────────────────────────────────────────────────────── + PAM_AUTHTOK: "authd2404" PAM Authenticate() User: "user-needs-reset@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Prevent_user_from_switching_username b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Prevent_user_from_switching_username index e5c174dd22..7a04276a30 100644 --- a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Prevent_user_from_switching_username +++ b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Prevent_user_from_switching_username @@ -35,6 +35,7 @@ Gimme your password: Press escape key to go back to select the authentication method ──────────────────────────────────────────────────────────────────────────────── + PAM_AUTHTOK: "goodpass" PAM Authenticate() User: "user-integration-pam-preset@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Remember_last_successful_broker_and_mode b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Remember_last_successful_broker_and_mode index f1fa136ccf..f963fb0b55 100644 --- a/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Remember_last_successful_broker_and_mode +++ b/pam/integration-tests/testdata/golden/TestCLIAuthenticate/Remember_last_successful_broker_and_mode @@ -41,6 +41,7 @@ Enter your one time credential: Press escape key to go back to select the authentication method ──────────────────────────────────────────────────────────────────────────────── + PAM_AUTHTOK: "temporary pass0" PAM Authenticate() User: "user-integration-remember-mode@example.com" Result: success @@ -65,6 +66,7 @@ Enter your one time credential: Press escape key to go back to select the authentication method ──────────────────────────────────────────────────────────────────────────────── + PAM_AUTHTOK: "temporary pass0" PAM Authenticate() User: "user-integration-remember-mode@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Change_passwd_after_MFA_auth b/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Change_passwd_after_MFA_auth index 2d5706519b..a41dd389c8 100644 --- a/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Change_passwd_after_MFA_auth +++ b/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Change_passwd_after_MFA_auth @@ -99,6 +99,7 @@ Confirm password: Press escape key to go back to choose the provider ──────────────────────────────────────────────────────────────────────────────── + PAM_AUTHTOK: "authd2404" PAM ChangeAuthTok() User: "user-mfa-integration-cli-passwd@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one b/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one index 2638ecea9c..9eb8962b4d 100644 --- a/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one +++ b/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one @@ -52,6 +52,7 @@ Confirm password: Press escape key to go back to choose the provider ──────────────────────────────────────────────────────────────────────────────── + PAM_AUTHTOK: "authd2404" PAM ChangeAuthTok() User: "user-integration-cli-passwd-change-password-successfully-and-authenticate-with-new-one@example.com" Result: success @@ -72,6 +73,7 @@ Gimme your password: Press escape key to go back to select the authentication method ──────────────────────────────────────────────────────────────────────────────── + PAM_AUTHTOK: "authd2404" PAM Authenticate() User: "user-integration-cli-passwd-change-password-successfully-and-authenticate-with-new-one@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one_with_different_case b/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one_with_different_case index 9dae08fd47..69359324f0 100644 --- a/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one_with_different_case +++ b/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one_with_different_case @@ -52,6 +52,7 @@ Confirm password: Press escape key to go back to choose the provider ──────────────────────────────────────────────────────────────────────────────── + PAM_AUTHTOK: "authd2404" PAM ChangeAuthTok() User: "user-integration-case-insensitive-testclichangeauthtok@example.com" Result: success @@ -72,6 +73,7 @@ Gimme your password: Press escape key to go back to select the authentication method ──────────────────────────────────────────────────────────────────────────────── + PAM_AUTHTOK: "authd2404" PAM Authenticate() User: "user-integration-case-insensitive-testclichangeauthtok@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Retry_if_new_password_does_not_match_quality_criteria b/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Retry_if_new_password_does_not_match_quality_criteria index aa9bd20f87..04b290f19e 100644 --- a/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Retry_if_new_password_does_not_match_quality_criteria +++ b/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Retry_if_new_password_does_not_match_quality_criteria @@ -126,6 +126,7 @@ Confirm password: Press escape key to go back to choose the provider ──────────────────────────────────────────────────────────────────────────────── + PAM_AUTHTOK: "authd2404" PAM ChangeAuthTok() User: "user-integration-cli-passwd-retry-if-new-password-does-not-match-quality-criteria@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Retry_if_new_password_is_rejected_by_broker b/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Retry_if_new_password_is_rejected_by_broker index ea03b3d5f6..3d46400198 100644 --- a/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Retry_if_new_password_is_rejected_by_broker +++ b/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Retry_if_new_password_is_rejected_by_broker @@ -85,6 +85,7 @@ Confirm password: Press escape key to go back to choose the provider ──────────────────────────────────────────────────────────────────────────────── + PAM_AUTHTOK: "authd2404" PAM ChangeAuthTok() User: "user-integration-cli-passwd-retry-if-new-password-is-rejected-by-broker@example.com" Result: success @@ -169,6 +170,7 @@ Confirm password: Press escape key to go back to choose the provider ──────────────────────────────────────────────────────────────────────────────── + PAM_AUTHTOK: "goodpass" PAM ChangeAuthTok() User: "user-integration-cli-passwd-retry-if-new-password-is-rejected-by-broker@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Retry_if_new_password_is_same_of_previous b/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Retry_if_new_password_is_same_of_previous index fe1906bd6b..9f539eeaaa 100644 --- a/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Retry_if_new_password_is_same_of_previous +++ b/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Retry_if_new_password_is_same_of_previous @@ -67,6 +67,7 @@ Confirm password: Press escape key to go back to choose the provider ──────────────────────────────────────────────────────────────────────────────── + PAM_AUTHTOK: "authd2404" PAM ChangeAuthTok() User: "user-integration-cli-passwd-retry-if-new-password-is-same-of-previous@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Retry_if_password_confirmation_is_not_the_same b/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Retry_if_password_confirmation_is_not_the_same index c26b508b4c..3c0bcc580d 100644 --- a/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Retry_if_password_confirmation_is_not_the_same +++ b/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Retry_if_password_confirmation_is_not_the_same @@ -85,6 +85,7 @@ Confirm password: Press escape key to go back to choose the provider ──────────────────────────────────────────────────────────────────────────────── + PAM_AUTHTOK: "authd2404" PAM ChangeAuthTok() User: "user-integration-cli-passwd-retry-if-password-confirmation-is-not-the-same@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_and_accept_password_reset b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_and_accept_password_reset index e9af1ce633..52205e48de 100644 --- a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_and_accept_password_reset +++ b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_and_accept_password_reset @@ -20,6 +20,7 @@ Enter your new password (3 days until mandatory): > Confirm Password: > + PAM_AUTHTOK: "authd2404" PAM Authenticate() User: "user-can-reset-integration-accept-native-testnativeauthenticate@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_and_add_it_to_local_group b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_and_add_it_to_local_group index 467155af4b..6a83eb59b0 100644 --- a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_and_add_it_to_local_group +++ b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_and_add_it_to_local_group @@ -7,6 +7,7 @@ Choose your provider: Enter 'r' to cancel the request and go back to select the authentication method Gimme your password: > + PAM_AUTHTOK: "goodpass" PAM Authenticate() User: "user-local-groups-integration-auth-native-testnativeauthenticate@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_and_offer_password_reset b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_and_offer_password_reset index 7bdb0e373b..c7d4c40577 100644 --- a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_and_offer_password_reset +++ b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_and_offer_password_reset @@ -14,6 +14,7 @@ Password reset, 1 step(s) missing Or enter 'r' to go back to choose the provider Choose action: > 2 + PAM_AUTHTOK: "goodpass" PAM Authenticate() User: "user-can-reset-integration-skip-native-testnativeauthenticate@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_and_reset_password_while_enforcing_policy b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_and_reset_password_while_enforcing_policy index 7fbc125505..522580e941 100644 --- a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_and_reset_password_while_enforcing_policy +++ b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_and_reset_password_while_enforcing_policy @@ -14,6 +14,7 @@ Enter your new password: > Confirm Password: > + PAM_AUTHTOK: "authd2404" PAM Authenticate() User: "user-needs-reset-integration-mandatory-native-testnativeauthenticate@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_and_reset_password_with_case_insensitive_user_selection b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_and_reset_password_with_case_insensitive_user_selection index 9e1f8ce3b3..9623faa7cd 100644 --- a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_and_reset_password_with_case_insensitive_user_selection +++ b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_and_reset_password_with_case_insensitive_user_selection @@ -16,6 +16,7 @@ Enter your new password: > Confirm Password: > + PAM_AUTHTOK: "authd2404" PAM Authenticate() User: "user-needs-reset-integration-case-insensitive-native-testnativeauthenticate@example.com" Result: success @@ -25,6 +26,7 @@ Username: USER-NEEDS-RESET-INTEGRATION-CASE-INSENSITIVE-NATIVE-TESTNATIVEAUTHENT Enter 'r' to cancel the request and go back to select the authentication method Gimme your password: > + PAM_AUTHTOK: "authd2404" PAM Authenticate() User: "user-needs-reset-integration-case-insensitive-native-testnativeauthenticate@example.com" Result: success @@ -34,6 +36,7 @@ Username: user-needs-reset-integration-Case-INSENSITIVE-native-testnativeauthent Enter 'r' to cancel the request and go back to select the authentication method Gimme your password: > + PAM_AUTHTOK: "authd2404" PAM Authenticate() User: "user-needs-reset-integration-case-insensitive-native-testnativeauthenticate@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_on_ssh_service b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_on_ssh_service index cfec047d41..8ca837696f 100644 --- a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_on_ssh_service +++ b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_on_ssh_service @@ -7,6 +7,7 @@ Choose your provider: Enter 'r' to cancel the request and go back to select the authentication method Gimme your password: > + PAM_AUTHTOK: "goodpass" PAM Authenticate() User: "user-integration-pre-check-ssh-service-native-testnativeauthenticate@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_on_ssh_service_with_custom_name_and_auth_info_env b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_on_ssh_service_with_custom_name_and_auth_info_env index 73f310afad..ebae0c11f9 100644 --- a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_on_ssh_service_with_custom_name_and_auth_info_env +++ b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_on_ssh_service_with_custom_name_and_auth_info_env @@ -7,6 +7,7 @@ Choose your provider: Enter 'r' to cancel the request and go back to select the authentication method Gimme your password: > + PAM_AUTHTOK: "goodpass" PAM Authenticate() User: "user-integration-pre-check-ssh-auth-info-native-testnativeauthenticate@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_on_ssh_service_with_custom_name_and_connection_env b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_on_ssh_service_with_custom_name_and_connection_env index 7f5db7b541..bebb0a7dfc 100644 --- a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_on_ssh_service_with_custom_name_and_connection_env +++ b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_on_ssh_service_with_custom_name_and_connection_env @@ -7,6 +7,7 @@ Choose your provider: Enter 'r' to cancel the request and go back to select the authentication method Gimme your password: > + PAM_AUTHTOK: "goodpass" PAM Authenticate() User: "user-integration-pre-check-ssh-connection-native-testnativeauthenticate@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_successfully b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_successfully index bd5850d2ce..304eba6242 100644 --- a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_successfully +++ b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_successfully @@ -7,6 +7,7 @@ Choose your provider: Enter 'r' to cancel the request and go back to select the authentication method Gimme your password: > + PAM_AUTHTOK: "goodpass" PAM Authenticate() User: "user-integration-native-authenticate-user-successfully@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_successfully_using_upper_case_with_user_selection b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_successfully_using_upper_case_with_user_selection index 22503cc904..d6212fb5b5 100644 --- a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_successfully_using_upper_case_with_user_selection +++ b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_successfully_using_upper_case_with_user_selection @@ -9,6 +9,7 @@ Choose your provider: Enter 'r' to cancel the request and go back to select the authentication method Gimme your password: > + PAM_AUTHTOK: "goodpass" PAM Authenticate() User: "user-integration-selection-upper-case-native-testnativeauthenticate@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_successfully_with_invalid_connection_timeout b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_successfully_with_invalid_connection_timeout index 39aee02516..d82da599c5 100644 --- a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_successfully_with_invalid_connection_timeout +++ b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_successfully_with_invalid_connection_timeout @@ -7,6 +7,7 @@ Choose your provider: Enter 'r' to cancel the request and go back to select the authentication method Gimme your password: > + PAM_AUTHTOK: "goodpass" PAM Authenticate() User: "user-integration-native-authenticate-user-successfully-with-invalid-connection-timeout@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_successfully_with_password_only_supported_method b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_successfully_with_password_only_supported_method index 643e4da66b..bfa00b5e9b 100644 --- a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_successfully_with_password_only_supported_method +++ b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_successfully_with_password_only_supported_method @@ -7,6 +7,7 @@ Choose your provider: Enter 'r' to cancel the request and go back to choose the provider Gimme your password: > + PAM_AUTHTOK: "goodpass" PAM Authenticate() User: "user-auth-modes-password-integration-native-testnativeauthenticate@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_successfully_with_password_only_supported_method_in_polkit b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_successfully_with_password_only_supported_method_in_polkit index b9f46ceb97..39e924fffc 100644 --- a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_successfully_with_password_only_supported_method_in_polkit +++ b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_successfully_with_password_only_supported_method_in_polkit @@ -1,6 +1,7 @@ == Password authentication == Gimme your password: > + PAM_AUTHTOK: "goodpass" PAM Authenticate() User: "user-auth-modes-password-integration-polkit-native-testnativeauthenticate@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_successfully_with_upper_case b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_successfully_with_upper_case index 7fe15fd1a8..72b47bffdf 100644 --- a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_successfully_with_upper_case +++ b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_successfully_with_upper_case @@ -7,6 +7,7 @@ Choose your provider: Enter 'r' to cancel the request and go back to select the authentication method Gimme your password: > + PAM_AUTHTOK: "goodpass" PAM Authenticate() User: "user-integration-upper-case-native-testnativeauthenticate@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_successfully_with_user_selection b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_successfully_with_user_selection index fc9de3aa1f..ac709d37ac 100644 --- a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_successfully_with_user_selection +++ b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_successfully_with_user_selection @@ -9,6 +9,7 @@ Choose your provider: Enter 'r' to cancel the request and go back to select the authentication method Gimme your password: > + PAM_AUTHTOK: "goodpass" PAM Authenticate() User: "user-integration-user-selection-native-testnativeauthenticate@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_switching_auth_mode b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_switching_auth_mode index ea4b7e8cc0..cdde95ba6d 100644 --- a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_switching_auth_mode +++ b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_switching_auth_mode @@ -177,6 +177,7 @@ Choose your authentication method: Enter 'r' to cancel the request and go back to select the authentication method Enter your pin code: > 4242 + PAM_AUTHTOK: "4242" PAM Authenticate() User: "user-integration-switch-mode-native-testnativeauthenticate@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_switching_username b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_switching_username index 463ad5583b..de502e1c14 100644 --- a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_switching_username +++ b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_switching_username @@ -16,6 +16,7 @@ Choose your provider: Enter 'r' to cancel the request and go back to select the authentication method Gimme your password: > + PAM_AUTHTOK: "goodpass" PAM Authenticate() User: "user-integration-native-username-switched-authenticate-user-switching-username@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_with_form_mode_with_button b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_with_form_mode_with_button index c7bf941026..04206da176 100644 --- a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_with_form_mode_with_button +++ b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_with_form_mode_with_button @@ -35,6 +35,7 @@ Choose action: Enter 'r' to cancel the request and go back to select the authentication method Enter your one time credential: > temporary pass00 + PAM_AUTHTOK: "temporary pass00" PAM Authenticate() User: "user-integration-native-authenticate-user-with-form-mode-with-button@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_with_form_mode_with_button_in_polkit b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_with_form_mode_with_button_in_polkit index 8efc9f9c2b..1a14999263 100644 --- a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_with_form_mode_with_button_in_polkit +++ b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_with_form_mode_with_button_in_polkit @@ -28,6 +28,7 @@ Choose action: Enter 'r' to cancel the request and go back to select the authentication method Enter your one time credential: > temporary pass00 + PAM_AUTHTOK: "temporary pass00" PAM Authenticate() User: "user-integration-native-authenticate-user-with-form-mode-with-button-in-polkit@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_with_form_mode_with_button_two_supported_methods b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_with_form_mode_with_button_two_supported_methods index f8b0855010..7a14863723 100644 --- a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_with_form_mode_with_button_two_supported_methods +++ b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_with_form_mode_with_button_two_supported_methods @@ -29,6 +29,7 @@ Choose action: Enter 'r' to cancel the request and go back to select the authentication method Enter your one time credential: > temporary pass00 + PAM_AUTHTOK: "temporary pass00" PAM Authenticate() User: "user-auth-modes-totp_with_button,password-integration-native@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_with_mfa b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_with_mfa index 9ccc44170b..88fa1340c1 100644 --- a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_with_mfa +++ b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_with_mfa @@ -53,6 +53,7 @@ Unlock your phone +33... or accept request on web interface: Press Enter to wait for authentication or enter 'r' to go back to select the authentication method Plug your fido device and press with your thumb: > + PAM_AUTHTOK: "goodpass" PAM Authenticate() User: "user-mfa-integration-auth-native-testnativeauthenticate@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_with_mfa_and_reset_password_while_enforcing_policy b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_with_mfa_and_reset_password_while_enforcing_policy index d68b1689ca..1ad4b1d7b1 100644 --- a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_with_mfa_and_reset_password_while_enforcing_policy +++ b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_with_mfa_and_reset_password_while_enforcing_policy @@ -35,6 +35,7 @@ Enter your new password (3 days until mandatory): > Confirm Password: > + PAM_AUTHTOK: "authd2404" PAM Authenticate() User: "user-mfa-with-reset-integration-pwquality-native-testnativeauthenticate@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_with_mfa_and_reset_same_password b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_with_mfa_and_reset_same_password index 9c3a3c38c3..524a67ddb2 100644 --- a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_with_mfa_and_reset_same_password +++ b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_user_with_mfa_and_reset_same_password @@ -25,6 +25,7 @@ Enter your new password (3 days until mandatory): > Confirm Password: > + PAM_AUTHTOK: "authd2404" PAM Authenticate() User: "user-mfa-with-reset-integration-same-password-native-testnativeauthenticate@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_with_warnings_on_unsupported_arguments b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_with_warnings_on_unsupported_arguments index 04637db022..705ae53645 100644 --- a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_with_warnings_on_unsupported_arguments +++ b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Authenticate_with_warnings_on_unsupported_arguments @@ -7,6 +7,7 @@ Choose your provider: Enter 'r' to cancel the request and go back to select the authentication method Gimme your password: > + PAM_AUTHTOK: "goodpass" PAM Authenticate() User: "user-integration-native-authenticate-with-warnings-on-unsupported-arguments@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Deny_authentication_if_newpassword_does_not_match_required_criteria b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Deny_authentication_if_newpassword_does_not_match_required_criteria index 4a66ffdc1b..e5b9cf0e0c 100644 --- a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Deny_authentication_if_newpassword_does_not_match_required_criteria +++ b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Deny_authentication_if_newpassword_does_not_match_required_criteria @@ -36,6 +36,7 @@ Enter your new password: > Confirm Password: > + PAM_AUTHTOK: "authd2404" PAM Authenticate() User: "user-needs-reset-integration-bad-password-native-testnativeauthenticate@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Prevent_preset_user_from_switching_username b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Prevent_preset_user_from_switching_username index b4f52fcf48..b1089301bc 100644 --- a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Prevent_preset_user_from_switching_username +++ b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Prevent_preset_user_from_switching_username @@ -37,6 +37,7 @@ Choose your provider: Enter 'r' to cancel the request and go back to select the authentication method Gimme your password: > + PAM_AUTHTOK: "goodpass" PAM Authenticate() User: "user-integration-native-prevent-preset-user-from-switching-username@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Remember_last_successful_broker_and_mode b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Remember_last_successful_broker_and_mode index 8f80ce59f6..5ac13b6f9d 100644 --- a/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Remember_last_successful_broker_and_mode +++ b/pam/integration-tests/testdata/golden/TestNativeAuthenticate/Remember_last_successful_broker_and_mode @@ -29,6 +29,7 @@ Choose action: Enter 'r' to cancel the request and go back to select the authentication method Enter your one time credential: > temporary pass0 + PAM_AUTHTOK: "temporary pass0" PAM Authenticate() User: "user-integration-native-remember-last-successful-broker-and-mode@example.com" Result: success @@ -43,6 +44,7 @@ Choose action: Enter 'r' to cancel the request and go back to select the authentication method Enter your one time credential: > temporary pass0 + PAM_AUTHTOK: "temporary pass0" PAM Authenticate() User: "user-integration-native-remember-last-successful-broker-and-mode@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Change_passwd_after_MFA_auth b/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Change_passwd_after_MFA_auth index d151f5e767..3cd0648a8b 100644 --- a/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Change_passwd_after_MFA_auth +++ b/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Change_passwd_after_MFA_auth @@ -60,6 +60,7 @@ Enter your new password: > Confirm Password: > + PAM_AUTHTOK: "authd2404" PAM ChangeAuthTok() User: "user-mfa-integration-native-passwd-testnativechangeauthtok@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one b/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one index fedf0a9fc3..06bb90d954 100644 --- a/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one +++ b/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one @@ -15,6 +15,7 @@ Enter your new password: > Confirm Password: > + PAM_AUTHTOK: "authd2404" PAM ChangeAuthTok() User: "user-integration-simple-testnativechangeauthtok@example.com" Result: success @@ -24,6 +25,7 @@ Username: user-integration-simple-testnativechangeauthtok@example.com Enter 'r' to cancel the request and go back to select the authentication method Gimme your password: > + PAM_AUTHTOK: "authd2404" PAM Authenticate() User: "user-integration-simple-testnativechangeauthtok@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one_with_different_case b/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one_with_different_case index 7974c0c245..00e39785e2 100644 --- a/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one_with_different_case +++ b/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one_with_different_case @@ -15,6 +15,7 @@ Enter your new password: > Confirm Password: > + PAM_AUTHTOK: "authd2404" PAM ChangeAuthTok() User: "user-integration-case-insensitive-testnativechangeauthtok@example.com" Result: success @@ -24,6 +25,7 @@ Username: user-integration-case-insensitive-testnativechangeauthtok@example.com Enter 'r' to cancel the request and go back to select the authentication method Gimme your password: > + PAM_AUTHTOK: "authd2404" PAM Authenticate() User: "user-integration-case-insensitive-testnativechangeauthtok@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one_with_single_broker_and_password_only_supported_method b/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one_with_single_broker_and_password_only_supported_method index f20234f494..bbddebbe8c 100644 --- a/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one_with_single_broker_and_password_only_supported_method +++ b/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one_with_single_broker_and_password_only_supported_method @@ -6,6 +6,7 @@ Enter your new password: > Confirm Password: > + PAM_AUTHTOK: "authd2404" PAM ChangeAuthTok() User: "user-auth-modes-password,mandatoryreset-integration-polkit-testnativechangeauthtok@example.com" Result: success @@ -14,6 +15,7 @@ PAM ChangeAuthTok() Enter 'r' to cancel the request and go back to select the authentication method Gimme your password: > + PAM_AUTHTOK: "authd2404" PAM Authenticate() User: "user-auth-modes-password,mandatoryreset-integration-polkit-testnativechangeauthtok@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Retry_if_new_password_does_not_match_quality_criteria b/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Retry_if_new_password_does_not_match_quality_criteria index 73fd925105..d2412ed592 100644 --- a/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Retry_if_new_password_does_not_match_quality_criteria +++ b/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Retry_if_new_password_does_not_match_quality_criteria @@ -42,6 +42,7 @@ Enter your new password: > Confirm Password: > + PAM_AUTHTOK: "authd2404" PAM ChangeAuthTok() User: "user-integration-native-passwd-retry-if-new-password-does-not-match-quality-criteria@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Retry_if_new_password_is_rejected_by_broker b/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Retry_if_new_password_is_rejected_by_broker index 91dfee1622..ce708012a6 100644 --- a/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Retry_if_new_password_is_rejected_by_broker +++ b/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Retry_if_new_password_is_rejected_by_broker @@ -22,6 +22,7 @@ Enter your new password: > Confirm Password: > + PAM_AUTHTOK: "authd2404" PAM ChangeAuthTok() User: "user-integration-native-passwd-retry-if-new-password-is-rejected-by-broker@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Retry_if_new_password_is_same_of_previous b/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Retry_if_new_password_is_same_of_previous index f643fbd924..f353cf1d8a 100644 --- a/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Retry_if_new_password_is_same_of_previous +++ b/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Retry_if_new_password_is_same_of_previous @@ -20,6 +20,7 @@ Enter your new password: > Confirm Password: > + PAM_AUTHTOK: "authd2404" PAM ChangeAuthTok() User: "user-integration-native-passwd-retry-if-new-password-is-same-of-previous@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Retry_if_password_confirmation_is_not_the_same b/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Retry_if_password_confirmation_is_not_the_same index 30fccea2e8..6e0b692cfe 100644 --- a/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Retry_if_password_confirmation_is_not_the_same +++ b/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Retry_if_password_confirmation_is_not_the_same @@ -22,6 +22,7 @@ Enter your new password: > Confirm Password: > + PAM_AUTHTOK: "authd2404" PAM ChangeAuthTok() User: "user-integration-native-passwd-retry-if-password-confirmation-is-not-the-same@example.com" Result: success diff --git a/pam/pam.go b/pam/pam.go index ff5399a20c..1905b69823 100644 --- a/pam/pam.go +++ b/pam/pam.go @@ -48,6 +48,11 @@ const ( defaultConnectionTimeout = 2 * time.Second ) +// reportAuthtok is called after PAM_AUTHTOK is set. It is a no-op by default; +// the pam_debug build overrides it to print the token so it appears in golden +// files. +var reportAuthtok = func(authtok string) {} + var supportedArgs = []string{ "debug", // When this is set to "true", then debug logging is enabled. "logfile", // The path of the file that will be used for logging. @@ -342,6 +347,7 @@ func (h *pamModule) handleAuthRequest(mode authd.SessionMode, mTx pam.ModuleTran if err := mTx.SetItem(pam.Authtok, returnValue.AuthTok); err != nil { return err } + reportAuthtok(returnValue.AuthTok) } return nil diff --git a/pam/pam_authtok_debug.go b/pam/pam_authtok_debug.go new file mode 100644 index 0000000000..0512a4dccc --- /dev/null +++ b/pam/pam_authtok_debug.go @@ -0,0 +1,11 @@ +//go:build pam_debug + +package main + +import "fmt" + +func init() { + reportAuthtok = func(authtok string) { + fmt.Printf(" PAM_AUTHTOK: %q\n", authtok) + } +} From e9e24eb424733896afb6454a57ef0f92d1ce1bea Mon Sep 17 00:00:00 2001 From: Adrian Dombeck Date: Wed, 24 Jun 2026 00:46:08 +0200 Subject: [PATCH 09/11] e2e-tests: enable AI agents to author tests Writing these e2e tests blind is hard for an agent: most assertions go through OCR, which is non-deterministic, and there was no documented way to verify an OCR match without running a full suite against the VM. Add an agent-facing authoring guide and a wrapper that launches YARF's interactive console against the live VM. The console gives the missing feedback loop: drive the screen to a state and probe what the OCR actually reads before committing a match, instead of guessing. The guide also records the assertion priority order (SSH/journal over OCR) and the OCR/keyword idioms the existing suite relies on, so agents compose from them rather than reinventing brittle sequences. Co-Authored-By: Claude Opus 4.8 --- e2e-tests/AGENTS.md | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/e2e-tests/AGENTS.md b/e2e-tests/AGENTS.md index a010b52b16..0f8ee19039 100644 --- a/e2e-tests/AGENTS.md +++ b/e2e-tests/AGENTS.md @@ -33,12 +33,16 @@ exist to close that gap: observable over SSH or in the journal, assert it there — it's deterministic and fast. The suite already does this where it can: see `Check Home Directory` and `Check If Owner Was Registered` in `resources/broker.resource`, which use - `SSH.Execute` instead of reading the screen. + `SSH.Execute` and `getent` instead of reading the screen. 2. **OCR (`Match Text` / `Find Text` / `Read Text`).** Use this only when the behavior is genuinely only observable on screen (GDM, the PAM CLI/TUI inside a `machinectl` session, terminal output you can't reach over SSH). Follow the OCR hygiene rules below. +Note that SSH is often *not* possible — e.g. anything happening inside the GDM +greeter or a `machinectl login` session before the user's shell exists. Don't +force an SSH assertion where it doesn't fit; use OCR and make it robust. + ## Don't sleep — poll instead Avoid `Builtin.Sleep` to wait for asynchronous work to finish (e.g. authd's @@ -145,6 +149,27 @@ file. The console history is saved to `rfdebug_history.log` in the output dir. For a region that OCR keeps misreading, the console also exposes `Grab Templates` and a region-of-interest selector to crop and scope matches. +## OCR hygiene rules + +These come straight from patterns the existing suite relies on — reproduce them: + +- **Strip spaces and normalize case** when matching machine-generated codes. The + device user code handling in `Continue Log In With Remote User: Authenticate + In External Browser` removes OCR-inserted spaces and upper-cases the result, + because OCR adds phantom spaces and mis-cases characters. +- **Prefer `regex:` matches** for anything variable. `Find Text` / `Match Text` + accept `regex:`; use it instead of brittle exact strings when the + text contains values that vary or that OCR renders inconsistently. +- **Scope with a `region`** when a short string risks matching elsewhere on + screen, or when OCR is unreliable over the full frame. +- **Give realistic timeouts.** `Match Text` waits up to its timeout for text to + appear; remote/network-dependent screens use generous timeouts (e.g. `120`) + in the existing tests — match that, don't use the short default for slow + screens. +- **Don't match the command echo.** When checking terminal output, use the + `base64 cmd-finished` trick (above) so you don't match the typed command + before it has run. + ## Running and reading results Run a single test (or omit the file to run all) using the existing script. It automatically loads the From 7be8ee6ed610939e701b7e50a420277c6c3866d8 Mon Sep 17 00:00:00 2001 From: Adrian Dombeck Date: Thu, 25 Jun 2026 18:23:06 +0200 Subject: [PATCH 10/11] pam: Set PAM_OLDAUTHTOK when changing the local password After changing their local password with passwd, a user's GNOME login keyring would no longer unlock at login: the keyring stayed locked under the old password and secret-tool would hang waiting on an unlock prompt. pam_gnome_keyring re-keys an existing keyring during chauthtok using PAM_OLDAUTHTOK (to open it) and PAM_AUTHTOK (the new passphrase). We set PAM_AUTHTOK but never PAM_OLDAUTHTOK, so the keyring could not be re-keyed and was orphaned under the old password. During a password change the user authenticates with their old local password before setting the new one, so the previous step's secret is the old password. Carry it through to PAM_OLDAUTHTOK, but only for a CHANGE_PASSWORD session and only when the old and new secrets differ, so plain authentication never sets a spurious PAM_OLDAUTHTOK. Co-Authored-By: Claude Opus 4.8 --- .../Change_passwd_after_MFA_auth | 1 + ...successfully_and_authenticate_with_new_one | 1 + ...henticate_with_new_one_with_different_case | 1 + ...w_password_does_not_match_quality_criteria | 1 + ...etry_if_new_password_is_rejected_by_broker | 2 ++ .../Retry_if_new_password_is_same_of_previous | 1 + ...y_if_password_confirmation_is_not_the_same | 1 + .../Change_passwd_after_MFA_auth | 1 + ...successfully_and_authenticate_with_new_one | 1 + ...henticate_with_new_one_with_different_case | 1 + ..._broker_and_password_only_supported_method | 1 + ...w_password_does_not_match_quality_criteria | 1 + ...etry_if_new_password_is_rejected_by_broker | 1 + .../Retry_if_new_password_is_same_of_previous | 1 + ...y_if_password_confirmation_is_not_the_same | 1 + pam/internal/adapter/authentication.go | 20 ++++++++++++++++++- pam/internal/adapter/return.go | 7 ++++++- pam/pam.go | 10 ++++++++++ pam/pam_authtok_debug.go | 3 +++ 19 files changed, 54 insertions(+), 2 deletions(-) diff --git a/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Change_passwd_after_MFA_auth b/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Change_passwd_after_MFA_auth index a41dd389c8..ce3c89173e 100644 --- a/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Change_passwd_after_MFA_auth +++ b/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Change_passwd_after_MFA_auth @@ -100,6 +100,7 @@ Confirm password: Press escape key to go back to choose the provider ──────────────────────────────────────────────────────────────────────────────── PAM_AUTHTOK: "authd2404" + PAM_OLDAUTHTOK: "goodpass" PAM ChangeAuthTok() User: "user-mfa-integration-cli-passwd@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one b/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one index 9eb8962b4d..303e75dbd3 100644 --- a/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one +++ b/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one @@ -53,6 +53,7 @@ Confirm password: Press escape key to go back to choose the provider ──────────────────────────────────────────────────────────────────────────────── PAM_AUTHTOK: "authd2404" + PAM_OLDAUTHTOK: "goodpass" PAM ChangeAuthTok() User: "user-integration-cli-passwd-change-password-successfully-and-authenticate-with-new-one@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one_with_different_case b/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one_with_different_case index 69359324f0..adca04257a 100644 --- a/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one_with_different_case +++ b/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one_with_different_case @@ -53,6 +53,7 @@ Confirm password: Press escape key to go back to choose the provider ──────────────────────────────────────────────────────────────────────────────── PAM_AUTHTOK: "authd2404" + PAM_OLDAUTHTOK: "goodpass" PAM ChangeAuthTok() User: "user-integration-case-insensitive-testclichangeauthtok@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Retry_if_new_password_does_not_match_quality_criteria b/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Retry_if_new_password_does_not_match_quality_criteria index 04b290f19e..09bc88f9b9 100644 --- a/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Retry_if_new_password_does_not_match_quality_criteria +++ b/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Retry_if_new_password_does_not_match_quality_criteria @@ -127,6 +127,7 @@ Confirm password: Press escape key to go back to choose the provider ──────────────────────────────────────────────────────────────────────────────── PAM_AUTHTOK: "authd2404" + PAM_OLDAUTHTOK: "goodpass" PAM ChangeAuthTok() User: "user-integration-cli-passwd-retry-if-new-password-does-not-match-quality-criteria@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Retry_if_new_password_is_rejected_by_broker b/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Retry_if_new_password_is_rejected_by_broker index 3d46400198..156775f767 100644 --- a/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Retry_if_new_password_is_rejected_by_broker +++ b/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Retry_if_new_password_is_rejected_by_broker @@ -86,6 +86,7 @@ Confirm password: Press escape key to go back to choose the provider ──────────────────────────────────────────────────────────────────────────────── PAM_AUTHTOK: "authd2404" + PAM_OLDAUTHTOK: "goodpass" PAM ChangeAuthTok() User: "user-integration-cli-passwd-retry-if-new-password-is-rejected-by-broker@example.com" Result: success @@ -171,6 +172,7 @@ Confirm password: Press escape key to go back to choose the provider ──────────────────────────────────────────────────────────────────────────────── PAM_AUTHTOK: "goodpass" + PAM_OLDAUTHTOK: "authd2404" PAM ChangeAuthTok() User: "user-integration-cli-passwd-retry-if-new-password-is-rejected-by-broker@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Retry_if_new_password_is_same_of_previous b/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Retry_if_new_password_is_same_of_previous index 9f539eeaaa..da8320897e 100644 --- a/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Retry_if_new_password_is_same_of_previous +++ b/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Retry_if_new_password_is_same_of_previous @@ -68,6 +68,7 @@ Confirm password: Press escape key to go back to choose the provider ──────────────────────────────────────────────────────────────────────────────── PAM_AUTHTOK: "authd2404" + PAM_OLDAUTHTOK: "goodpass" PAM ChangeAuthTok() User: "user-integration-cli-passwd-retry-if-new-password-is-same-of-previous@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Retry_if_password_confirmation_is_not_the_same b/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Retry_if_password_confirmation_is_not_the_same index 3c0bcc580d..3d6933ff11 100644 --- a/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Retry_if_password_confirmation_is_not_the_same +++ b/pam/integration-tests/testdata/golden/TestCLIChangeAuthTok/Retry_if_password_confirmation_is_not_the_same @@ -86,6 +86,7 @@ Confirm password: Press escape key to go back to choose the provider ──────────────────────────────────────────────────────────────────────────────── PAM_AUTHTOK: "authd2404" + PAM_OLDAUTHTOK: "goodpass" PAM ChangeAuthTok() User: "user-integration-cli-passwd-retry-if-password-confirmation-is-not-the-same@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Change_passwd_after_MFA_auth b/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Change_passwd_after_MFA_auth index 3cd0648a8b..093870bc16 100644 --- a/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Change_passwd_after_MFA_auth +++ b/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Change_passwd_after_MFA_auth @@ -61,6 +61,7 @@ Enter your new password: Confirm Password: > PAM_AUTHTOK: "authd2404" + PAM_OLDAUTHTOK: "goodpass" PAM ChangeAuthTok() User: "user-mfa-integration-native-passwd-testnativechangeauthtok@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one b/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one index 06bb90d954..9054bf2802 100644 --- a/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one +++ b/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one @@ -16,6 +16,7 @@ Enter your new password: Confirm Password: > PAM_AUTHTOK: "authd2404" + PAM_OLDAUTHTOK: "goodpass" PAM ChangeAuthTok() User: "user-integration-simple-testnativechangeauthtok@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one_with_different_case b/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one_with_different_case index 00e39785e2..bfedca8e6b 100644 --- a/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one_with_different_case +++ b/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one_with_different_case @@ -16,6 +16,7 @@ Enter your new password: Confirm Password: > PAM_AUTHTOK: "authd2404" + PAM_OLDAUTHTOK: "goodpass" PAM ChangeAuthTok() User: "user-integration-case-insensitive-testnativechangeauthtok@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one_with_single_broker_and_password_only_supported_method b/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one_with_single_broker_and_password_only_supported_method index bbddebbe8c..125fbaf680 100644 --- a/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one_with_single_broker_and_password_only_supported_method +++ b/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Change_password_successfully_and_authenticate_with_new_one_with_single_broker_and_password_only_supported_method @@ -7,6 +7,7 @@ Enter your new password: Confirm Password: > PAM_AUTHTOK: "authd2404" + PAM_OLDAUTHTOK: "goodpass" PAM ChangeAuthTok() User: "user-auth-modes-password,mandatoryreset-integration-polkit-testnativechangeauthtok@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Retry_if_new_password_does_not_match_quality_criteria b/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Retry_if_new_password_does_not_match_quality_criteria index d2412ed592..c947bfdba1 100644 --- a/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Retry_if_new_password_does_not_match_quality_criteria +++ b/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Retry_if_new_password_does_not_match_quality_criteria @@ -43,6 +43,7 @@ Enter your new password: Confirm Password: > PAM_AUTHTOK: "authd2404" + PAM_OLDAUTHTOK: "goodpass" PAM ChangeAuthTok() User: "user-integration-native-passwd-retry-if-new-password-does-not-match-quality-criteria@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Retry_if_new_password_is_rejected_by_broker b/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Retry_if_new_password_is_rejected_by_broker index ce708012a6..ae69fc2333 100644 --- a/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Retry_if_new_password_is_rejected_by_broker +++ b/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Retry_if_new_password_is_rejected_by_broker @@ -23,6 +23,7 @@ Enter your new password: Confirm Password: > PAM_AUTHTOK: "authd2404" + PAM_OLDAUTHTOK: "goodpass" PAM ChangeAuthTok() User: "user-integration-native-passwd-retry-if-new-password-is-rejected-by-broker@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Retry_if_new_password_is_same_of_previous b/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Retry_if_new_password_is_same_of_previous index f353cf1d8a..66e4543900 100644 --- a/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Retry_if_new_password_is_same_of_previous +++ b/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Retry_if_new_password_is_same_of_previous @@ -21,6 +21,7 @@ Enter your new password: Confirm Password: > PAM_AUTHTOK: "authd2404" + PAM_OLDAUTHTOK: "goodpass" PAM ChangeAuthTok() User: "user-integration-native-passwd-retry-if-new-password-is-same-of-previous@example.com" Result: success diff --git a/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Retry_if_password_confirmation_is_not_the_same b/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Retry_if_password_confirmation_is_not_the_same index 6e0b692cfe..bf220761e3 100644 --- a/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Retry_if_password_confirmation_is_not_the_same +++ b/pam/integration-tests/testdata/golden/TestNativeChangeAuthTok/Retry_if_password_confirmation_is_not_the_same @@ -23,6 +23,7 @@ Enter your new password: Confirm Password: > PAM_AUTHTOK: "authd2404" + PAM_OLDAUTHTOK: "goodpass" PAM ChangeAuthTok() User: "user-integration-native-passwd-retry-if-password-confirmation-is-not-the-same@example.com" Result: success diff --git a/pam/internal/adapter/authentication.go b/pam/internal/adapter/authentication.go index 722d3b2644..2e2580525d 100644 --- a/pam/internal/adapter/authentication.go +++ b/pam/internal/adapter/authentication.go @@ -373,7 +373,25 @@ func (m authenticationModel) Update(msg tea.Msg) (authModel authenticationModel, } else { log.Warningf(context.Background(), "authentication granted, but no secret is available, cannot set PAM_AUTHTOK") } - return m, sendEvent(PamSuccess{BrokerID: m.currentBrokerID, AuthTok: secret, msg: authMsg}) + + // During a password change the user authenticates with their old local + // password before setting the new one, so the previous step's secret is + // the old password. Pass it along as PAM_OLDAUTHTOK so pam_gnome_keyring + // can re-key the existing keyring instead of leaving it locked under the + // old password. We only do this when the old and new secrets differ and + // the new one came from this step (msg.secret), to avoid setting a + // spurious PAM_OLDAUTHTOK during plain authentication. + var oldSecret string + if m.mode == authd.SessionMode_CHANGE_PASSWORD && msg.secret != nil && + m.currentSecret != "" && m.currentSecret != secret { + oldSecret = m.currentSecret + } + return m, sendEvent(PamSuccess{ + BrokerID: m.currentBrokerID, + AuthTok: secret, + OldAuthTok: oldSecret, + msg: authMsg, + }) case auth.Retry: m.errorMsg = authMsg diff --git a/pam/internal/adapter/return.go b/pam/internal/adapter/return.go index 2dbafdba14..eb1f1a49cd 100644 --- a/pam/internal/adapter/return.go +++ b/pam/internal/adapter/return.go @@ -21,7 +21,12 @@ type PamReturnError interface { type PamSuccess struct { BrokerID string AuthTok string - msg string + // OldAuthTok is the previous local password, set only when the user changed + // their password (chauthtok) and entered the old one as part of the flow. It + // lets PAM_OLDAUTHTOK be set so modules like pam_gnome_keyring can re-key an + // existing secret store instead of orphaning it. + OldAuthTok string + msg string } // Message returns the message that should be sent to pam as info message. diff --git a/pam/pam.go b/pam/pam.go index 1905b69823..fde673441c 100644 --- a/pam/pam.go +++ b/pam/pam.go @@ -53,6 +53,10 @@ const ( // files. var reportAuthtok = func(authtok string) {} +// reportOldAuthtok is called after PAM_OLDAUTHTOK is set. Like reportAuthtok it +// is a no-op by default and overridden by the pam_debug build. +var reportOldAuthtok = func(oldAuthtok string) {} + var supportedArgs = []string{ "debug", // When this is set to "true", then debug logging is enabled. "logfile", // The path of the file that will be used for logging. @@ -349,6 +353,12 @@ func (h *pamModule) handleAuthRequest(mode authd.SessionMode, mTx pam.ModuleTran } reportAuthtok(returnValue.AuthTok) } + if returnValue.OldAuthTok != "" { + if err := mTx.SetItem(pam.Oldauthtok, returnValue.OldAuthTok); err != nil { + return err + } + reportOldAuthtok(returnValue.OldAuthTok) + } return nil case adapter.PamReturnError: diff --git a/pam/pam_authtok_debug.go b/pam/pam_authtok_debug.go index 0512a4dccc..3b101cbf7a 100644 --- a/pam/pam_authtok_debug.go +++ b/pam/pam_authtok_debug.go @@ -8,4 +8,7 @@ func init() { reportAuthtok = func(authtok string) { fmt.Printf(" PAM_AUTHTOK: %q\n", authtok) } + reportOldAuthtok = func(oldAuthtok string) { + fmt.Printf(" PAM_OLDAUTHTOK: %q\n", oldAuthtok) + } } From 4359c7a5898447187189195cdaac1e52ab3b3837 Mon Sep 17 00:00:00 2001 From: Adrian Dombeck Date: Thu, 25 Jun 2026 18:23:13 +0200 Subject: [PATCH 11/11] e2e-tests: Test that passwd re-keys the GNOME keyring Guards against the login keyring being orphaned when a remote user changes their local password: log in with device auth, store a secret in the keyring, change the password with passwd, log back in with the new password, and require the stored secret to still be retrievable. The secret round-trip is what makes this discriminating: if the password change left the old keyring locked (or a fresh one was created), the lookup after re-login fails. Co-Authored-By: Claude Opus 4.8 --- e2e-tests/resources/authd.resource | 16 +++++++ .../passwd_changes_keyring_password.robot | 48 +++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 e2e-tests/tests/passwd_changes_keyring_password.robot diff --git a/e2e-tests/resources/authd.resource b/e2e-tests/resources/authd.resource index 02065529d9..98f32ecd09 100644 --- a/e2e-tests/resources/authd.resource +++ b/e2e-tests/resources/authd.resource @@ -76,3 +76,19 @@ Check That Remote User Can Run Sudo Commands Check that GNOME keyring is unlocked SSH.Execute As Current User echo '' | secret-tool store --label=test test key + + +Store Secret In GNOME Keyring + [Arguments] ${secret} + # Storing succeeds only if the keyring is unlocked, so this both seeds a + # known secret and asserts the keyring is currently unlocked. + SSH.Execute As Current User printf '%s' '${secret}' | secret-tool store --label=e2e-test e2e-test keyring-persistence + + +Check Secret In GNOME Keyring + [Arguments] ${expected_secret} + # Looking up the secret succeeds only if the *same* keyring that stored it is + # unlocked. If the password change re-created the keyring instead of re-keying + # it, the secret is gone and this fails. + ${secret} = SSH.Execute As Current User secret-tool lookup e2e-test keyring-persistence + Should Be Equal ${secret} ${expected_secret} diff --git a/e2e-tests/tests/passwd_changes_keyring_password.robot b/e2e-tests/tests/passwd_changes_keyring_password.robot new file mode 100644 index 0000000000..69a69a1c62 --- /dev/null +++ b/e2e-tests/tests/passwd_changes_keyring_password.robot @@ -0,0 +1,48 @@ +*** Settings *** +Resource resources/utils.resource +Resource resources/authd.resource + +Resource resources/broker.resource + +# Test Tags robot:exit-on-failure + +Test Setup utils.Test Setup snapshot=%{BROKER}-installed +Test Teardown utils.Test Teardown + + +*** Variables *** +${snapshot} %{BROKER}-installed +${username} %{E2E_USER} +${local_password} qwer1234 +${new_password} passwd1234 +${keyring_secret} s3cr3t-survives-passwd + + +*** Test Cases *** +Changing the local password also changes the keyring password + [Documentation] Changing a remote user's local password with `passwd` must re-key the + ... GNOME login keyring so it keeps unlocking with the new password. + ... + ... Regression guard: the keyring is unlocked from PAM_AUTHTOK at login. If the + ... password change does not propagate the old/new password to the keyring's PAM + ... module, the next login creates a fresh keyring and any previously stored secret + ... is lost. We seed a secret before the change and require it to still be there + ... after logging in again with the new password. + + # Log in with device authentication. This creates the login keyring and + # unlocks it with ${local_password}. + Log In With Remote User Through GDM: QR Code ${username} ${local_password} + Check that GNOME keyring is unlocked + Store Secret In GNOME Keyring ${keyring_secret} + + # Change the local password with `passwd`. + Open Terminal + Change Password ${local_password} ${new_password} + Close Focused Window + Log Out + + # Log in again with the new local password. The keyring must unlock with it + # and still contain the secret stored before the change. + Log In With Remote User Through GDM: Local Password ${username} ${new_password} + Check that GNOME keyring is unlocked + Check Secret In GNOME Keyring ${keyring_secret}