// Copyright IBM Corp. 2016, 2025
// SPDX-License-Identifier: BUSL-1.1

package configutil

import (
	"reflect"
	"testing"

	"github.com/hashicorp/go-kms-wrapping/wrappers/ocikms/v2"
	"github.com/stretchr/testify/require"
)

func Test_getEnvConfig(t *testing.T) {
	tests := []struct {
		name    string
		kms     *KMS
		envVars map[string]string
		want    map[string]string
	}{
		{
			"AliCloud wrapper",
			&KMS{
				Type:     "alicloudkms",
				Priority: 1,
			},
			map[string]string{"ALICLOUD_REGION": "test_region", "ALICLOUD_DOMAIN": "test_domain", "ALICLOUD_ACCESS_KEY": "test_access_key", "ALICLOUD_SECRET_KEY": "test_secret_key", "VAULT_ALICLOUDKMS_SEAL_KEY_ID": "test_key_id"},
			map[string]string{"region": "test_region", "domain": "test_domain", "access_key": "test_access_key", "secret_key": "test_secret_key", "kms_key_id": "test_key_id"},
		},
		{
			"AWS KMS wrapper",
			&KMS{
				Type:     "awskms",
				Priority: 1,
			},
			map[string]string{"AWS_REGION": "test_region", "AWS_ACCESS_KEY_ID": "test_access_key", "AWS_SECRET_ACCESS_KEY": "test_secret_key", "VAULT_AWSKMS_SEAL_KEY_ID": "test_key_id"},
			map[string]string{"region": "test_region", "access_key": "test_access_key", "secret_key": "test_secret_key", "kms_key_id": "test_key_id"},
		},
		{
			"Azure KeyVault wrapper",
			&KMS{
				Type:     "azurekeyvault",
				Priority: 1,
			},
			map[string]string{"AZURE_TENANT_ID": "test_tenant_id", "AZURE_CLIENT_ID": "test_client_id", "AZURE_CLIENT_SECRET": "test_client_secret", "AZURE_ENVIRONMENT": "test_environment", "VAULT_AZUREKEYVAULT_VAULT_NAME": "test_vault_name", "VAULT_AZUREKEYVAULT_KEY_NAME": "test_key_name"},
			map[string]string{"tenant_id": "test_tenant_id", "client_id": "test_client_id", "client_secret": "test_client_secret", "environment": "test_environment", "vault_name": "test_vault_name", "key_name": "test_key_name"},
		},
		{
			"GCP CKMS wrapper",
			&KMS{
				Type:     "gcpckms",
				Priority: 1,
			},
			map[string]string{"GOOGLE_CREDENTIALS": "test_credentials", "GOOGLE_PROJECT": "test_project", "GOOGLE_REGION": "test_region", "VAULT_GCPCKMS_SEAL_KEY_RING": "test_key_ring", "VAULT_GCPCKMS_SEAL_CRYPTO_KEY": "test_crypto_key"},
			map[string]string{"credentials": "test_credentials", "project": "test_project", "region": "test_region", "key_ring": "test_key_ring", "crypto_key": "test_crypto_key"},
		},
		{
			"OCI KMS wrapper",
			&KMS{
				Type:     "ocikms",
				Priority: 1,
			},
			map[string]string{"VAULT_OCIKMS_SEAL_KEY_ID": "test_key_id", "VAULT_OCIKMS_CRYPTO_ENDPOINT": "test_crypto_endpoint", "VAULT_OCIKMS_MANAGEMENT_ENDPOINT": "test_management_endpoint"},
			map[string]string{"key_id": "test_key_id", "crypto_endpoint": "test_crypto_endpoint", "management_endpoint": "test_management_endpoint"},
		},
		{
			"Transit wrapper",
			&KMS{
				Type:     "transit",
				Priority: 1,
			},
			map[string]string{"VAULT_ADDR": "test_address", "VAULT_TOKEN": "test_token", "VAULT_TRANSIT_SEAL_KEY_NAME": "test_key_name", "VAULT_TRANSIT_SEAL_MOUNT_PATH": "test_mount_path"},
			map[string]string{"address": "test_address", "token": "test_token", "key_name": "test_key_name", "mount_path": "test_mount_path"},
		},
		{
			"Environment vars not set",
			&KMS{
				Type:     "awskms",
				Priority: 1,
			},
			map[string]string{},
			map[string]string{},
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			for envName, envVal := range tt.envVars {
				t.Setenv(envName, envVal)
			}

			if got := GetEnvConfigFunc(tt.kms); !reflect.DeepEqual(got, tt.want) {
				t.Errorf("getEnvConfig() = %v, want %v", got, tt.want)
			}
		})
	}
}

// TestParseKMSesURLConformance tests that all config attrs whose values can be
// URLs, IP addresses, or host:port addresses, when configured with an IPv6
// address, the normalized to be conformant with RFC-5942 §4
// See: https://rfc-editor.org/rfc/rfc5952.html
func TestParseKMSesURLConformance(t *testing.T) {
	t.Parallel()

	for name, tc := range map[string]struct {
		config     string
		expected   map[string]string
		shouldFail bool
	}{
		"alicloudkms ipv4": {
			config: `
seal "alicloudkms" {
  region     = "us-east-1"
	domain     = "kms.us-east-1.aliyuncs.com"
  access_key = "0wNEpMMlzy7szvai"
  secret_key = "PupkTg8jdmau1cXxYacgE736PJj4cA"
  kms_key_id = "08c33a6f-4e0a-4a1b-a3fa-7ddfa1d4fb73"
}`,
			expected: map[string]string{
				"region":     "us-east-1",
				"domain":     "kms.us-east-1.aliyuncs.com",
				"access_key": "0wNEpMMlzy7szvai",
				"secret_key": "PupkTg8jdmau1cXxYacgE736PJj4cA",
				"kms_key_id": "08c33a6f-4e0a-4a1b-a3fa-7ddfa1d4fb73",
			},
		},
		"alicloudkms ipv6": {
			config: `
seal "alicloudkms" {
  region     = "us-east-1"
	domain     = "2001:db8:0:0:0:0:2:1"
  access_key = "0wNEpMMlzy7szvai"
  secret_key = "PupkTg8jdmau1cXxYacgE736PJj4cA"
  kms_key_id = "08c33a6f-4e0a-4a1b-a3fa-7ddfa1d4fb73"
}`,
			expected: map[string]string{
				"region":     "us-east-1",
				"domain":     "2001:db8::2:1",
				"access_key": "0wNEpMMlzy7szvai",
				"secret_key": "PupkTg8jdmau1cXxYacgE736PJj4cA",
				"kms_key_id": "08c33a6f-4e0a-4a1b-a3fa-7ddfa1d4fb73",
			},
		},
		"awskms ipv4": {
			config: `
seal "awskms" {
  region     = "us-east-1"
  access_key = "AKIAIOSFODNN7EXAMPLE"
  secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
  kms_key_id = "19ec80b0-dfdd-4d97-8164-c6examplekey"
	endpoint   = "https://vpce-0e1bb1852241f8cc6-pzi0do8n.kms.us-east-1.vpce.amazonaws.com"
}`,
			expected: map[string]string{
				"region":     "us-east-1",
				"access_key": "AKIAIOSFODNN7EXAMPLE",
				"secret_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
				"kms_key_id": "19ec80b0-dfdd-4d97-8164-c6examplekey",
				"endpoint":   "https://vpce-0e1bb1852241f8cc6-pzi0do8n.kms.us-east-1.vpce.amazonaws.com",
			},
		},
		"awskms ipv6": {
			config: `
seal "awskms" {
  region     = "us-east-1"
  access_key = "AKIAIOSFODNN7EXAMPLE"
  secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
  kms_key_id = "19ec80b0-dfdd-4d97-8164-c6examplekey"
  endpoint   = "https://[2001:db8:0:0:0:0:2:1]:5984/my-aws-endpoint"
}`,
			expected: map[string]string{
				"region":     "us-east-1",
				"access_key": "AKIAIOSFODNN7EXAMPLE",
				"secret_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
				"kms_key_id": "19ec80b0-dfdd-4d97-8164-c6examplekey",
				"endpoint":   "https://[2001:db8::2:1]:5984/my-aws-endpoint",
			},
		},
		"azurekeyvault ipv4": {
			config: `
seal "azurekeyvault" {
  tenant_id      = "46646709-b63e-4747-be42-516edeaf1e14"
  client_id      = "03dc33fc-16d9-4b77-8152-3ec568f8af6e"
  client_secret  = "DUJDS3..."
  vault_name     = "hc-vault"
  key_name       = "vault_key"
	resource       = "vault.azure.net"
}`,
			expected: map[string]string{
				"tenant_id":     "46646709-b63e-4747-be42-516edeaf1e14",
				"client_id":     "03dc33fc-16d9-4b77-8152-3ec568f8af6e",
				"client_secret": "DUJDS3...",
				"vault_name":    "hc-vault",
				"key_name":      "vault_key",
				"resource":      "vault.azure.net",
			},
		},
		"azurekeyvault ipv6": {
			config: `
seal "azurekeyvault" {
  tenant_id      = "46646709-b63e-4747-be42-516edeaf1e14"
  client_id      = "03dc33fc-16d9-4b77-8152-3ec568f8af6e"
  client_secret  = "DUJDS3..."
  vault_name     = "hc-vault"
  key_name       = "vault_key"
  resource       = "2001:db8:0:0:0:0:2:1",
}`,
			expected: map[string]string{
				"tenant_id":     "46646709-b63e-4747-be42-516edeaf1e14",
				"client_id":     "03dc33fc-16d9-4b77-8152-3ec568f8af6e",
				"client_secret": "DUJDS3...",
				"vault_name":    "hc-vault",
				"key_name":      "vault_key",
				"resource":      "2001:db8::2:1",
			},
		},
		"ocikms ipv4": {
			config: `
seal "ocikms" {
	key_id               = "ocid1.key.oc1.iad.afnxza26aag4s.abzwkljsbapzb2nrha5nt3s7s7p42ctcrcj72vn3kq5qx"
	crypto_endpoint      = "https://afnxza26aag4s-crypto.kms.us-ashburn-1.oraclecloud.com"
	management_endpoint  = "https://afnxza26aag4s-management.kms.us-ashburn-1.oraclecloud.com"
	auth_type_api_key    = "true"
}`,
			expected: map[string]string{
				"key_id":              "ocid1.key.oc1.iad.afnxza26aag4s.abzwkljsbapzb2nrha5nt3s7s7p42ctcrcj72vn3kq5qx",
				"crypto_endpoint":     "https://afnxza26aag4s-crypto.kms.us-ashburn-1.oraclecloud.com",
				"management_endpoint": "https://afnxza26aag4s-management.kms.us-ashburn-1.oraclecloud.com",
				"auth_type_api_key":   "true",
			},
		},
		"ocikms ipv6": {
			config: `
seal "ocikms" {
  key_id               = "https://[2001:db8:0:0:0:0:2:1]/abzwkljsbapzb2nrha5nt3s7s7p42ctcrcj72vn3kq5qx"
  crypto_endpoint      = "https://[2001:db8:0:0:0:0:2:1]/afnxza26aag4s-crypto"
  management_endpoint  = "https://[2001:db8:0:0:0:0:2:1]/afnxza26aag4s-management"
  auth_type_api_key    = "true"
}`,
			expected: map[string]string{
				"key_id":              "https://[2001:db8::2:1]/abzwkljsbapzb2nrha5nt3s7s7p42ctcrcj72vn3kq5qx",
				"crypto_endpoint":     "https://[2001:db8::2:1]/afnxza26aag4s-crypto",
				"management_endpoint": "https://[2001:db8::2:1]/afnxza26aag4s-management",
				"auth_type_api_key":   "true",
			},
		},
		"transit ipv4": {
			config: `
seal "transit" {
  address            = "https://vault:8200"
  token              = "s.Qf1s5zigZ4OX6akYjQXJC1jY"
  disable_renewal    = "false"
  key_name           = "transit_key_name"
  mount_path         = "transit/"
  namespace          = "ns1/"
}
`,
			expected: map[string]string{
				"address":         "https://vault:8200",
				"token":           "s.Qf1s5zigZ4OX6akYjQXJC1jY",
				"disable_renewal": "false",
				"key_name":        "transit_key_name",
				"mount_path":      "transit/",
				"namespace":       "ns1/",
			},
		},
		"transit ipv6": {
			config: `
seal "transit" {
  address            = "https://[2001:db8:0:0:0:0:2:1]:8200"
  token              = "s.Qf1s5zigZ4OX6akYjQXJC1jY"
  disable_renewal    = "false"
  key_name           = "transit_key_name"
  mount_path         = "transit/"
  namespace          = "ns1/"
}
`,
			expected: map[string]string{
				"address":         "https://[2001:db8::2:1]:8200",
				"token":           "s.Qf1s5zigZ4OX6akYjQXJC1jY",
				"disable_renewal": "false",
				"key_name":        "transit_key_name",
				"mount_path":      "transit/",
				"namespace":       "ns1/",
			},
		},
	} {
		t.Run(name, func(t *testing.T) {
			t.Parallel()
			kmses, err := ParseKMSes(tc.config)
			require.NoError(t, err)
			require.Len(t, kmses, 1)
			require.EqualValues(t, tc.expected, kmses[0].Config)
		})
	}
}

// TestMergeKMSEnvConfigAddrConformance tests that all env config whose values
// can be URLs, IP addresses, or host:port addresses, when configured with an
// an IPv6 address, the normalized to be conformant with RFC-5942 §4
// See: https://rfc-editor.org/rfc/rfc5952.html
func TestMergeKMSEnvConfigAddrConformance(t *testing.T) {
	for name, tc := range map[string]struct {
		sealType  string // default to name if none given
		kmsConfig map[string]string
		envVars   map[string]string
		expected  map[string]string
	}{
		"alicloudkms": {
			kmsConfig: map[string]string{
				"region":     "us-east-1",
				"domain":     "kms.us-east-1.aliyuncs.com",
				"access_key": "0wNEpMMlzy7szvai",
				"secret_key": "PupkTg8jdmau1cXxYacgE736PJj4cA",
				"kms_key_id": "08c33a6f-4e0a-4a1b-a3fa-7ddfa1d4fb73",
			},
			envVars: map[string]string{"ALICLOUD_DOMAIN": "2001:db8:0:0:0:0:2:1"},
			expected: map[string]string{
				"region":     "us-east-1",
				"domain":     "2001:db8::2:1",
				"access_key": "0wNEpMMlzy7szvai",
				"secret_key": "PupkTg8jdmau1cXxYacgE736PJj4cA",
				"kms_key_id": "08c33a6f-4e0a-4a1b-a3fa-7ddfa1d4fb73",
			},
		},
		"awskms": {
			kmsConfig: map[string]string{
				"region":     "us-east-1",
				"access_key": "AKIAIOSFODNN7EXAMPLE",
				"secret_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
				"kms_key_id": "19ec80b0-dfdd-4d97-8164-c6examplekey",
				"endpoint":   "https://vpce-0e1bb1852241f8cc6-pzi0do8n.kms.us-east-1.vpce.amazonaws.com",
			},
			envVars: map[string]string{"AWS_KMS_ENDPOINT": "https://[2001:db8:0:0:0:0:2:1]:5984/my-aws-endpoint"},
			expected: map[string]string{
				"region":     "us-east-1",
				"access_key": "AKIAIOSFODNN7EXAMPLE",
				"secret_key": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
				"kms_key_id": "19ec80b0-dfdd-4d97-8164-c6examplekey",
				"endpoint":   "https://[2001:db8::2:1]:5984/my-aws-endpoint",
			},
		},
		"azurekeyvault": {
			kmsConfig: map[string]string{
				"tenant_id":     "46646709-b63e-4747-be42-516edeaf1e14",
				"client_id":     "03dc33fc-16d9-4b77-8152-3ec568f8af6e",
				"client_secret": "DUJDS3...",
				"vault_name":    "hc-vault",
				"key_name":      "vault_key",
				"resource":      "vault.azure.net",
			},
			envVars: map[string]string{"AZURE_AD_RESOURCE": "2001:db8:0:0:0:0:2:1"},
			expected: map[string]string{
				"tenant_id":     "46646709-b63e-4747-be42-516edeaf1e14",
				"client_id":     "03dc33fc-16d9-4b77-8152-3ec568f8af6e",
				"client_secret": "DUJDS3...",
				"vault_name":    "hc-vault",
				"key_name":      "vault_key",
				"resource":      "2001:db8::2:1",
			},
		},
		"ocikms wrapper env vars": {
			sealType: "ocikms",
			kmsConfig: map[string]string{
				"key_id":              "ocid1.key.oc1.iad.afnxza26aag4s.abzwkljsbapzb2nrha5nt3s7s7p42ctcrcj72vn3kq5qx",
				"crypto_endpoint":     "https://afnxza26aag4s-crypto.kms.us-ashburn-1.oraclecloud.com",
				"management_endpoint": "https://afnxza26aag4s-management.kms.us-ashburn-1.oraclecloud.com",
				"auth_type_api_key":   "true",
			},
			envVars: map[string]string{
				ocikms.EnvOciKmsWrapperKeyId:              "https://[2001:db8:0:0:0:0:2:1]/abzwkljsbapzb2nrha5nt3s7s7p42ctcrcj72vn3kq5qx",
				ocikms.EnvOciKmsWrapperCryptoEndpoint:     "https://[2001:db8:0:0:0:0:2:1]/afnxza26aag4s-crypto",
				ocikms.EnvOciKmsWrapperManagementEndpoint: "https://[2001:db8:0:0:0:0:2:1]/afnxza26aag4s-management",
			},
			expected: map[string]string{
				"key_id":              "https://[2001:db8::2:1]/abzwkljsbapzb2nrha5nt3s7s7p42ctcrcj72vn3kq5qx",
				"crypto_endpoint":     "https://[2001:db8::2:1]/afnxza26aag4s-crypto",
				"management_endpoint": "https://[2001:db8::2:1]/afnxza26aag4s-management",
				"auth_type_api_key":   "true",
			},
		},
		"ocikms vault env vars": {
			sealType: "ocikms",
			kmsConfig: map[string]string{
				"key_id":              "ocid1.key.oc1.iad.afnxza26aag4s.abzwkljsbapzb2nrha5nt3s7s7p42ctcrcj72vn3kq5qx",
				"crypto_endpoint":     "https://afnxza26aag4s-crypto.kms.us-ashburn-1.oraclecloud.com",
				"management_endpoint": "https://afnxza26aag4s-management.kms.us-ashburn-1.oraclecloud.com",
				"auth_type_api_key":   "true",
			},
			envVars: map[string]string{
				ocikms.EnvVaultOciKmsSealKeyId:              "https://[2001:db8:0:0:0:0:2:1]/abzwkljsbapzb2nrha5nt3s7s7p42ctcrcj72vn3kq5qx",
				ocikms.EnvVaultOciKmsSealCryptoEndpoint:     "https://[2001:db8:0:0:0:0:2:1]/afnxza26aag4s-crypto",
				ocikms.EnvVaultOciKmsSealManagementEndpoint: "https://[2001:db8:0:0:0:0:2:1]/afnxza26aag4s-management",
			},
			expected: map[string]string{
				"key_id":              "https://[2001:db8::2:1]/abzwkljsbapzb2nrha5nt3s7s7p42ctcrcj72vn3kq5qx",
				"crypto_endpoint":     "https://[2001:db8::2:1]/afnxza26aag4s-crypto",
				"management_endpoint": "https://[2001:db8::2:1]/afnxza26aag4s-management",
				"auth_type_api_key":   "true",
			},
		},
		"transit addr not in config": {
			sealType: "transit",
			kmsConfig: map[string]string{
				"token":           "s.Qf1s5zigZ4OX6akYjQXJC1jY",
				"disable_renewal": "false",
				"key_name":        "transit_key_name",
				"mount_path":      "transit/",
				"namespace":       "ns1/",
			},
			envVars: map[string]string{"VAULT_ADDR": "https://[2001:db8:0:0:0:0:2:1]:8200"},
			expected: map[string]string{
				// NOTE: If our address has not been configured we'll fall back to VAULT_ADDR for transit.
				"address":         "https://[2001:db8::2:1]:8200",
				"token":           "s.Qf1s5zigZ4OX6akYjQXJC1jY",
				"disable_renewal": "false",
				"key_name":        "transit_key_name",
				"mount_path":      "transit/",
				"namespace":       "ns1/",
			},
		},
		"transit addr in config": {
			sealType: "transit",
			kmsConfig: map[string]string{
				"address":         "https://vault:8200",
				"token":           "s.Qf1s5zigZ4OX6akYjQXJC1jY",
				"disable_renewal": "false",
				"key_name":        "transit_key_name",
				"mount_path":      "transit/",
				"namespace":       "ns1/",
			},
			envVars: map[string]string{"VAULT_ADDR": "https://[2001:db8:0:0:0:0:2:1]:8200"},
			expected: map[string]string{
				// NOTE: If our address has been configured we don't consider VAULT_ADDR
				"address":         "https://vault:8200",
				"token":           "s.Qf1s5zigZ4OX6akYjQXJC1jY",
				"disable_renewal": "false",
				"key_name":        "transit_key_name",
				"mount_path":      "transit/",
				"namespace":       "ns1/",
			},
		},
	} {
		t.Run(name, func(t *testing.T) {
			typ := name
			if tc.sealType != "" {
				typ = tc.sealType
			}
			kms := &KMS{
				Type:   typ,
				Config: tc.kmsConfig,
			}

			for envName, envVal := range tc.envVars {
				t.Setenv(envName, envVal)
			}

			require.NoError(t, mergeKMSEnvConfig(kms))
			require.EqualValues(t, tc.expected, kms.Config)
		})
	}
}
