diff --git a/lib/config/fileconf.go b/lib/config/fileconf.go index b38ac12efd94d..546a98f41bbbc 100644 --- a/lib/config/fileconf.go +++ b/lib/config/fileconf.go @@ -1282,6 +1282,9 @@ type HardwareKey struct { // SerialNumberValidation contains optional settings for hardware key // serial number validation, including whether it is enabled. SerialNumberValidation *HardwareKeySerialNumberValidation `yaml:"serial_number_validation,omitempty"` + + // PINCacheTTL specifies how long to cache the user's PIV PIN. + PINCacheTTL time.Duration `yaml:"pin_cache_ttl,omitempty"` } func (h *HardwareKey) Parse() (*types.HardwareKey, error) { @@ -1291,7 +1294,10 @@ func (h *HardwareKey) Parse() (*types.HardwareKey, error) { } } - hk := &types.HardwareKey{PIVSlot: string(h.PIVSlot)} + hk := &types.HardwareKey{ + PIVSlot: string(h.PIVSlot), + PinCacheTTL: types.Duration(h.PINCacheTTL), + } if h.SerialNumberValidation != nil { var err error diff --git a/lib/config/fileconf_test.go b/lib/config/fileconf_test.go index 4070fe9adc443..160e4bc93205e 100644 --- a/lib/config/fileconf_test.go +++ b/lib/config/fileconf_test.go @@ -981,6 +981,119 @@ func TestSSHSection(t *testing.T) { } } +func TestHardwareKeyConfig(t *testing.T) { + for _, tc := range []struct { + name string + mutate func(cfgMap) + expectReadError require.ErrorAssertionFunc + expectParseError require.ErrorAssertionFunc + expectHardwareKeyConfig *types.HardwareKey + }{ + { + name: "OK empty", + mutate: func(cfg cfgMap) { + cfg["auth_service"].(cfgMap)["authentication"] = cfgMap{ + "hardware_key": cfgMap{}, + } + }, + expectHardwareKeyConfig: &types.HardwareKey{}, + }, + { + name: "OK piv_slot", + mutate: func(cfg cfgMap) { + cfg["auth_service"].(cfgMap)["authentication"] = cfgMap{ + "hardware_key": cfgMap{ + "piv_slot": "9a", + }, + } + }, + expectHardwareKeyConfig: &types.HardwareKey{ + PIVSlot: "9a", + }, + }, + { + name: "NOK piv_slot unsupported slot", + mutate: func(cfg cfgMap) { + cfg["auth_service"].(cfgMap)["authentication"] = cfgMap{ + "hardware_key": cfgMap{ + "piv_slot": "8f", + }, + } + }, + expectParseError: func(t require.TestingT, err error, i ...interface{}) { + require.Error(t, err) + require.True(t, trace.IsBadParameter(err), "got err = %v, want BadParameter", err) + }, + }, + { + name: "OK serial_number_validation", + mutate: func(cfg cfgMap) { + cfg["auth_service"].(cfgMap)["authentication"] = cfgMap{ + "hardware_key": cfgMap{ + "serial_number_validation": cfgMap{ + "enabled": true, + "serial_number_trait_name": "custom_trait_name", + }, + }, + } + }, + expectHardwareKeyConfig: &types.HardwareKey{ + SerialNumberValidation: &types.HardwareKeySerialNumberValidation{ + Enabled: true, + SerialNumberTraitName: "custom_trait_name", + }, + }, + }, + { + name: "OK pin_cache_ttl", + mutate: func(cfg cfgMap) { + cfg["auth_service"].(cfgMap)["authentication"] = cfgMap{ + "hardware_key": cfgMap{ + "pin_cache_ttl": "1m", + }, + } + }, + expectHardwareKeyConfig: &types.HardwareKey{ + PinCacheTTL: types.Duration(time.Minute), + }, + }, + { + name: "NOK pin_cache_ttl not a duration", + mutate: func(cfg cfgMap) { + cfg["auth_service"].(cfgMap)["authentication"] = cfgMap{ + "hardware_key": cfgMap{ + "pin_cache_ttl": "1minute", + }, + } + }, + expectReadError: require.Error, + }, + } { + t.Run(tc.name, func(t *testing.T) { + text := bytes.NewBuffer(editConfig(t, tc.mutate)) + + cfg, err := ReadConfig(text) + if tc.expectReadError != nil { + tc.expectReadError(t, err) + return + } + require.NoError(t, err) + + cap, err := cfg.Auth.Authentication.Parse() + if tc.expectParseError != nil { + tc.expectParseError(t, err) + return + } + require.NoError(t, err) + + hardwareKeyConfig, err := cap.GetHardwareKey() + require.NoError(t, err) + + require.Equal(t, tc.expectHardwareKeyConfig, hardwareKeyConfig) + }) + } +} + func TestX11Config(t *testing.T) { testCases := []struct { desc string