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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions pkg/keycloak/keycloak.go
Original file line number Diff line number Diff line change
Expand Up @@ -317,17 +317,30 @@ func (h *Helper) SetPassword(realm, userID, password string) error {
return h.Client.SetPassword(ctx, h.Token, userID, realm, password, false)
}

func (h *Helper) ExecuteActionsEmail(realm, userID string, actions []string) error {
func (h *Helper) ExecuteActionsEmail(realm, userID string, actions []string, lifespanSeconds int) error {
if actions == nil {
actions = []string{}
}

ctx, cancel := context.WithTimeout(wait.CtxSeconds(10))
defer cancel()
return h.Client.ExecuteActionsEmail(ctx, h.Token, realm, gocloak.ExecuteActionsEmail{
params := gocloak.ExecuteActionsEmail{
UserID: &userID,
Actions: &actions,
})
}
/*
* gocloak's Lifespan is *int with `omitempty` -- that only checks
* pointer-nilness, not the pointee's value, so a non-nil pointer to 0
* would still serialize as an explicit lifespan=0 query param and mint
* an already-expired action token. Only set it for a genuinely
* positive value; anything else falls back to Keycloak's own realm
* default, matching this method's pre-lifespan-parameter behavior.
*/
if lifespanSeconds > 0 {
params.Lifespan = gocloak.IntP(lifespanSeconds)
}

ctx, cancel := context.WithTimeout(wait.CtxSeconds(10))
defer cancel()
return h.Client.ExecuteActionsEmail(ctx, h.Token, realm, params)
}

func (h *Helper) DeleteUser(realm, userID string) error {
Expand Down
83 changes: 62 additions & 21 deletions pkg/keycloak/keycloak_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -939,32 +939,67 @@ func TestHelperExecuteActionsEmail(t *testing.T) {
errBoom := errors.New("boom")

tests := []struct {
name string
actions []string
expectedActions []string
clientError error
expectedError error
name string
actions []string
expectedActions []string
lifespanSeconds int
expectedLifespan *int
clientError error
expectedError error
}{
{
name: "Should send the given actions when actions is non-nil",
actions: []string{"UPDATE_PASSWORD"},
expectedActions: []string{"UPDATE_PASSWORD"},
clientError: nil,
expectedError: nil,
name: "Should send the given actions and lifespan when actions is non-nil",
actions: []string{"UPDATE_PASSWORD"},
expectedActions: []string{"UPDATE_PASSWORD"},
lifespanSeconds: 300,
expectedLifespan: gocloak.IntP(300),
clientError: nil,
expectedError: nil,
},
{
name: "Should send an empty slice instead of nil when actions is nil",
actions: nil,
expectedActions: []string{},
clientError: nil,
expectedError: nil,
name: "Should send an empty slice instead of nil when actions is nil",
actions: nil,
expectedActions: []string{},
lifespanSeconds: 300,
expectedLifespan: gocloak.IntP(300),
clientError: nil,
expectedError: nil,
},
{
name: "Should propagate an error when the client call fails",
actions: []string{"UPDATE_PASSWORD"},
expectedActions: []string{"UPDATE_PASSWORD"},
clientError: errBoom,
expectedError: errBoom,
name: "Should thread through a different lifespan value rather than a hardcoded one",
actions: []string{"UPDATE_PASSWORD"},
expectedActions: []string{"UPDATE_PASSWORD"},
lifespanSeconds: 600,
expectedLifespan: gocloak.IntP(600),
clientError: nil,
expectedError: nil,
},
{
name: "Should leave Lifespan nil when lifespanSeconds is zero, instead of sending an already-expired lifespan=0",
actions: []string{"UPDATE_PASSWORD"},
expectedActions: []string{"UPDATE_PASSWORD"},
lifespanSeconds: 0,
expectedLifespan: nil,
clientError: nil,
expectedError: nil,
},
{
name: "Should leave Lifespan nil when lifespanSeconds is negative",
actions: []string{"UPDATE_PASSWORD"},
expectedActions: []string{"UPDATE_PASSWORD"},
lifespanSeconds: -1,
expectedLifespan: nil,
clientError: nil,
expectedError: nil,
},
{
name: "Should propagate an error when the client call fails",
actions: []string{"UPDATE_PASSWORD"},
expectedActions: []string{"UPDATE_PASSWORD"},
lifespanSeconds: 300,
expectedLifespan: gocloak.IntP(300),
clientError: errBoom,
expectedError: errBoom,
},
}

Expand All @@ -978,11 +1013,17 @@ func TestHelperExecuteActionsEmail(t *testing.T) {
require.Equal(t, "user-id", *params.UserID)
require.NotNil(t, params.Actions)
require.Equal(t, tc.expectedActions, *params.Actions)
if tc.expectedLifespan == nil {
require.Nil(t, params.Lifespan)
} else {
require.NotNil(t, params.Lifespan)
require.Equal(t, *tc.expectedLifespan, *params.Lifespan)
}
}).
Return(tc.clientError)

h := &Helper{Client: client}
err := h.ExecuteActionsEmail("master", "user-id", tc.actions)
err := h.ExecuteActionsEmail("master", "user-id", tc.actions, tc.lifespanSeconds)

require.ErrorIs(t, err, tc.expectedError)
})
Expand Down