github.com/jaylevin/jenkins-library@v1.230.4/pkg/config/vault_test.go (about) 1 package config 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "path" 8 "strings" 9 "testing" 10 11 "github.com/stretchr/testify/mock" 12 13 "github.com/SAP/jenkins-library/pkg/config/mocks" 14 "github.com/stretchr/testify/assert" 15 ) 16 17 func TestVaultConfigLoad(t *testing.T) { 18 const secretName = "testSecret" 19 const secretNameOverrideKey = "mySecretVaultSecretName" 20 t.Parallel() 21 t.Run("Load secret from vault", func(t *testing.T) { 22 vaultMock := &mocks.VaultMock{} 23 stepConfig := StepConfig{Config: map[string]interface{}{ 24 "vaultPath": "team1", 25 }} 26 stepParams := []StepParameters{stepParam(secretName, "vaultSecret", secretNameOverrideKey, secretName)} 27 vaultData := map[string]string{secretName: "value1"} 28 29 vaultMock.On("GetKvSecret", path.Join("team1", secretName)).Return(vaultData, nil) 30 resolveAllVaultReferences(&stepConfig, vaultMock, stepParams) 31 assert.Equal(t, "value1", stepConfig.Config[secretName]) 32 }) 33 34 t.Run("Load secret from Vault with path override", func(t *testing.T) { 35 vaultMock := &mocks.VaultMock{} 36 stepConfig := StepConfig{Config: map[string]interface{}{ 37 "vaultPath": "team1", 38 secretNameOverrideKey: "overrideSecretName", 39 }} 40 stepParams := []StepParameters{stepParam(secretName, "vaultSecret", secretNameOverrideKey, secretName)} 41 vaultData := map[string]string{secretName: "value1"} 42 43 vaultMock.On("GetKvSecret", path.Join("team1", "overrideSecretName")).Return(vaultData, nil) 44 resolveAllVaultReferences(&stepConfig, vaultMock, stepParams) 45 assert.Equal(t, "value1", stepConfig.Config[secretName]) 46 }) 47 48 t.Run("Secrets are not overwritten", func(t *testing.T) { 49 vaultMock := &mocks.VaultMock{} 50 stepConfig := StepConfig{Config: map[string]interface{}{ 51 "vaultPath": "team1", 52 secretName: "preset value", 53 "vaultDisableOverwrite": true, 54 }} 55 stepParams := []StepParameters{stepParam(secretName, "vaultSecret", secretNameOverrideKey, secretName)} 56 vaultData := map[string]string{secretName: "value1"} 57 vaultMock.On("GetKvSecret", path.Join("team1", secretName)).Return(vaultData, nil) 58 resolveAllVaultReferences(&stepConfig, vaultMock, stepParams) 59 60 assert.Equal(t, "preset value", stepConfig.Config[secretName]) 61 }) 62 63 t.Run("Secrets can be overwritten", func(t *testing.T) { 64 vaultMock := &mocks.VaultMock{} 65 stepConfig := StepConfig{Config: map[string]interface{}{ 66 "vaultPath": "team1", 67 secretName: "preset value", 68 }} 69 stepParams := []StepParameters{stepParam(secretName, "vaultSecret", secretNameOverrideKey, secretName)} 70 vaultData := map[string]string{secretName: "value1"} 71 vaultMock.On("GetKvSecret", path.Join("team1", secretName)).Return(vaultData, nil) 72 resolveAllVaultReferences(&stepConfig, vaultMock, stepParams) 73 74 assert.Equal(t, "value1", stepConfig.Config[secretName]) 75 }) 76 77 t.Run("Error is passed through", func(t *testing.T) { 78 vaultMock := &mocks.VaultMock{} 79 stepConfig := StepConfig{Config: map[string]interface{}{ 80 "vaultPath": "team1", 81 }} 82 stepParams := []StepParameters{stepParam(secretName, "vaultSecret", secretNameOverrideKey, secretName)} 83 vaultMock.On("GetKvSecret", path.Join("team1", secretName)).Return(nil, fmt.Errorf("test")) 84 resolveAllVaultReferences(&stepConfig, vaultMock, stepParams) 85 assert.Len(t, stepConfig.Config, 1) 86 }) 87 88 t.Run("Secret doesn't exist", func(t *testing.T) { 89 vaultMock := &mocks.VaultMock{} 90 stepConfig := StepConfig{Config: map[string]interface{}{ 91 "vaultPath": "team1", 92 }} 93 stepParams := []StepParameters{stepParam(secretName, "vaultSecret", secretNameOverrideKey, secretName)} 94 vaultMock.On("GetKvSecret", path.Join("team1", secretName)).Return(nil, nil) 95 resolveAllVaultReferences(&stepConfig, vaultMock, stepParams) 96 assert.Len(t, stepConfig.Config, 1) 97 }) 98 99 t.Run("Alias names should be considered", func(t *testing.T) { 100 aliasName := "alias" 101 vaultMock := &mocks.VaultMock{} 102 stepConfig := StepConfig{Config: map[string]interface{}{ 103 "vaultPath": "team1", 104 }} 105 param := stepParam(secretName, "vaultSecret", secretNameOverrideKey, secretName) 106 addAlias(¶m, aliasName) 107 stepParams := []StepParameters{param} 108 vaultData := map[string]string{aliasName: "value1"} 109 vaultMock.On("GetKvSecret", path.Join("team1", secretName)).Return(vaultData, nil) 110 resolveAllVaultReferences(&stepConfig, vaultMock, stepParams) 111 assert.Equal(t, "value1", stepConfig.Config[secretName]) 112 }) 113 114 t.Run("Search over multiple paths", func(t *testing.T) { 115 vaultMock := &mocks.VaultMock{} 116 stepConfig := StepConfig{Config: map[string]interface{}{ 117 "vaultBasePath": "team2", 118 "vaultPath": "team1", 119 }} 120 stepParams := []StepParameters{ 121 stepParam(secretName, "vaultSecret", secretNameOverrideKey, secretName), 122 } 123 vaultData := map[string]string{secretName: "value1"} 124 vaultMock.On("GetKvSecret", path.Join("team1", secretName)).Return(nil, nil) 125 vaultMock.On("GetKvSecret", path.Join("team2/GROUP-SECRETS", secretName)).Return(vaultData, nil) 126 resolveAllVaultReferences(&stepConfig, vaultMock, stepParams) 127 assert.Equal(t, "value1", stepConfig.Config[secretName]) 128 }) 129 130 t.Run("No BasePath is stepConfig.Configured", func(t *testing.T) { 131 vaultMock := &mocks.VaultMock{} 132 stepConfig := StepConfig{Config: map[string]interface{}{}} 133 stepParams := []StepParameters{stepParam(secretName, "vaultSecret", secretNameOverrideKey, secretName)} 134 resolveAllVaultReferences(&stepConfig, vaultMock, stepParams) 135 assert.Equal(t, nil, stepConfig.Config[secretName]) 136 vaultMock.AssertNotCalled(t, "GetKvSecret", mock.AnythingOfType("string")) 137 }) 138 } 139 140 func TestVaultSecretFiles(t *testing.T) { 141 const secretName = "testSecret" 142 const secretNameOverrideKey = "mySecretVaultSecretName" 143 t.Run("Test Vault Secret File Reference", func(t *testing.T) { 144 vaultMock := &mocks.VaultMock{} 145 stepConfig := StepConfig{Config: map[string]interface{}{ 146 "vaultPath": "team1", 147 }} 148 stepParams := []StepParameters{stepParam(secretName, "vaultSecretFile", secretNameOverrideKey, secretName)} 149 vaultData := map[string]string{secretName: "value1"} 150 vaultMock.On("GetKvSecret", path.Join("team1", secretName)).Return(vaultData, nil) 151 resolveAllVaultReferences(&stepConfig, vaultMock, stepParams) 152 assert.NotNil(t, stepConfig.Config[secretName]) 153 path := stepConfig.Config[secretName].(string) 154 contentByte, err := ioutil.ReadFile(path) 155 assert.NoError(t, err) 156 content := string(contentByte) 157 assert.Equal(t, content, "value1") 158 }) 159 160 os.RemoveAll(VaultSecretFileDirectory) 161 VaultSecretFileDirectory = "" 162 163 t.Run("Test temporary secret file cleanup", func(t *testing.T) { 164 vaultMock := &mocks.VaultMock{} 165 stepConfig := StepConfig{Config: map[string]interface{}{ 166 "vaultPath": "team1", 167 }} 168 stepParams := []StepParameters{stepParam(secretName, "vaultSecretFile", secretNameOverrideKey, secretName)} 169 vaultData := map[string]string{secretName: "value1"} 170 assert.NoDirExists(t, VaultSecretFileDirectory) 171 vaultMock.On("GetKvSecret", path.Join("team1", secretName)).Return(vaultData, nil) 172 resolveAllVaultReferences(&stepConfig, vaultMock, stepParams) 173 assert.NotNil(t, stepConfig.Config[secretName]) 174 path := stepConfig.Config[secretName].(string) 175 assert.DirExists(t, VaultSecretFileDirectory) 176 assert.FileExists(t, path) 177 RemoveVaultSecretFiles() 178 assert.NoFileExists(t, path) 179 assert.NoDirExists(t, VaultSecretFileDirectory) 180 }) 181 } 182 183 func TestMixinVault(t *testing.T) { 184 vaultServerUrl := "https://testServer" 185 vaultPath := "testPath" 186 config := StepConfig{ 187 Config: map[string]interface{}{}, 188 HookConfig: nil, 189 } 190 general := map[string]interface{}{ 191 "vaultPath": vaultPath, 192 } 193 steps := map[string]interface{}{ 194 "vaultServerUrl": vaultServerUrl, 195 "unknownConfig": "test", 196 } 197 198 config.mixinVaultConfig(nil, general, steps) 199 200 assert.Contains(t, config.Config, "vaultServerUrl") 201 assert.Equal(t, vaultServerUrl, config.Config["vaultServerUrl"]) 202 assert.Contains(t, config.Config, "vaultPath") 203 assert.Equal(t, vaultPath, config.Config["vaultPath"]) 204 assert.NotContains(t, config.Config, "unknownConfig") 205 206 } 207 208 func stepParam(name, refType, vaultSecretNameProperty, defaultSecretNameName string) StepParameters { 209 return StepParameters{ 210 Name: name, 211 Aliases: []Alias{}, 212 ResourceRef: []ResourceReference{ 213 { 214 Type: refType, 215 Name: vaultSecretNameProperty, 216 Default: defaultSecretNameName, 217 }, 218 }, 219 } 220 } 221 222 func addAlias(param *StepParameters, aliasName string) { 223 alias := Alias{Name: aliasName} 224 param.Aliases = append(param.Aliases, alias) 225 } 226 227 func TestResolveVaultTestCredentials(t *testing.T) { 228 t.Parallel() 229 t.Run("Default test credential prefix", func(t *testing.T) { 230 t.Parallel() 231 // init 232 vaultMock := &mocks.VaultMock{} 233 envPrefix := "PIPER_TESTCREDENTIAL_" 234 stepConfig := StepConfig{Config: map[string]interface{}{ 235 "vaultPath": "team1", 236 "vaultTestCredentialPath": "appCredentials", 237 "vaultTestCredentialKeys": []interface{}{"appUser", "appUserPw"}, 238 }} 239 240 defer os.Unsetenv("PIPER_TESTCREDENTIAL_APPUSER") 241 defer os.Unsetenv("PIPER_TESTCREDENTIAL_APPUSERPW") 242 243 // mock 244 vaultData := map[string]string{"appUser": "test-user", "appUserPw": "password1234"} 245 vaultMock.On("GetKvSecret", "team1/appCredentials").Return(vaultData, nil) 246 247 // test 248 resolveVaultTestCredentials(&stepConfig, vaultMock) 249 250 // assert 251 for k, v := range vaultData { 252 env := envPrefix + strings.ToUpper(k) 253 assert.NotEmpty(t, os.Getenv(env)) 254 assert.Equal(t, os.Getenv(env), v) 255 } 256 }) 257 258 t.Run("Custom general purpose credential prefix along with fixed standard prefix", func(t *testing.T) { 259 t.Parallel() 260 // init 261 vaultMock := &mocks.VaultMock{} 262 envPrefix := "CUSTOM_MYCRED_" 263 standardEnvPrefix := "PIPER_VAULTCREDENTIAL_" 264 stepConfig := StepConfig{Config: map[string]interface{}{ 265 "vaultPath": "team1", 266 "vaultCredentialPath": "appCredentials", 267 "vaultCredentialKeys": []interface{}{"appUser", "appUserPw"}, 268 "vaultCredentialEnvPrefix": envPrefix, 269 }} 270 271 defer os.Unsetenv("CUSTOM_MYCRED_APPUSER") 272 defer os.Unsetenv("CUSTOM_MYCRED_APPUSERPW") 273 defer os.Unsetenv("PIPER_VAULTCREDENTIAL_APPUSER") 274 defer os.Unsetenv("PIPER_VAULTCREDENTIAL_APPUSERPW") 275 276 // mock 277 vaultData := map[string]string{"appUser": "test-user", "appUserPw": "password1234"} 278 vaultMock.On("GetKvSecret", "team1/appCredentials").Return(vaultData, nil) 279 280 // test 281 resolveVaultCredentials(&stepConfig, vaultMock) 282 283 // assert 284 for k, v := range vaultData { 285 env := envPrefix + strings.ToUpper(k) 286 assert.NotEmpty(t, os.Getenv(env)) 287 assert.Equal(t, os.Getenv(env), v) 288 standardEnv := standardEnvPrefix + strings.ToUpper(k) 289 assert.NotEmpty(t, os.Getenv(standardEnv)) 290 assert.Equal(t, os.Getenv(standardEnv), v) 291 } 292 }) 293 294 t.Run("Custom test credential prefix", func(t *testing.T) { 295 t.Parallel() 296 // init 297 vaultMock := &mocks.VaultMock{} 298 envPrefix := "CUSTOM_CREDENTIAL_" 299 stepConfig := StepConfig{Config: map[string]interface{}{ 300 "vaultPath": "team1", 301 "vaultTestCredentialPath": "appCredentials", 302 "vaultTestCredentialKeys": []interface{}{"appUser", "appUserPw"}, 303 "vaultTestCredentialEnvPrefix": envPrefix, 304 }} 305 306 defer os.Unsetenv("CUSTOM_CREDENTIAL_APPUSER") 307 defer os.Unsetenv("CUSTOM_CREDENTIAL_APPUSERPW") 308 309 // mock 310 vaultData := map[string]string{"appUser": "test-user", "appUserPw": "password1234"} 311 vaultMock.On("GetKvSecret", "team1/appCredentials").Return(vaultData, nil) 312 313 // test 314 resolveVaultTestCredentials(&stepConfig, vaultMock) 315 316 // assert 317 for k, v := range vaultData { 318 env := envPrefix + strings.ToUpper(k) 319 assert.NotEmpty(t, os.Getenv(env)) 320 assert.Equal(t, os.Getenv(env), v) 321 } 322 }) 323 } 324 325 func Test_convertEnvVar(t *testing.T) { 326 type args struct { 327 s string 328 } 329 tests := []struct { 330 name string 331 args args 332 want string 333 }{ 334 { 335 name: "empty string", 336 args: args{""}, 337 want: "", 338 }, 339 { 340 name: "alphanumerical string", 341 args: args{"myApp1"}, 342 want: "MYAPP1", 343 }, 344 { 345 name: "string with hyphen", 346 args: args{"my_App-1"}, 347 want: "MY_APP_1", 348 }, 349 { 350 name: "string with special characters", 351 args: args{"my_App?-(1]"}, 352 want: "MY_APP_1", 353 }, 354 } 355 for _, tt := range tests { 356 t.Run(tt.name, func(t *testing.T) { 357 if got := convertEnvVar(tt.args.s); got != tt.want { 358 t.Errorf("convertEnvironment() = %v, want %v", got, tt.want) 359 } 360 }) 361 } 362 }