diff --git a/.github/config/.terraform.lock.hcl b/.github/config/.terraform.lock.hcl index 567b5b4..d2fb6b0 100644 --- a/.github/config/.terraform.lock.hcl +++ b/.github/config/.terraform.lock.hcl @@ -2,24 +2,24 @@ # Manual edits may be lost in future updates. provider "registry.terraform.io/integrations/github" { - version = "6.3.1" + version = "6.6.0" constraints = ">= 6.2.0" hashes = [ - "h1:fMctJXbbaQU4sBAxAayAVa9wDyIIdSBZX8KzFphKFC0=", - "zh:25ae1cb97ec528e6b7e9330489f4a33acc0fa80b909c113a8445656bc524c5b9", - "zh:3e1f6300dc10e52a54f13352770ed79f25ff4ba9ac49b776c52a655a3488a20b", - "zh:4aaf2877ec22e63358d7c9cd48c7d7947d1a1dc4d03231f0af193d8975d5918a", - "zh:4b904a81fac12a2a7606c8d811cb9c4e13581adcaaa19e503a067ac95c515925", - "zh:54fe7e0dca04e698631a5b86bdd43ef09a31375e68f8f89970b4315cd5fc6312", - "zh:6b14f92cf62784eaf20f43ef58ce966735f30d43deeab077943bd410c0d8b8b2", - "zh:86c49a1c11c024b26b6750c446f104922a3fe8464d3706a5fb9a4a05c6ca0b0a", - "zh:8939fb6332c4a58c4e90245eb9f0110987ccafff06b45a7ed513f2759a2abe6a", - "zh:8b4068a78c1f357325d1151facdb1aff506b9cd79d2bab21a55651255a130e2f", - "zh:ae22f5e52f534f19811d7f9480b4eb442f12ff16367b3893abb4e449b029ff6b", - "zh:afae9cfd9d49002ddfea552aa4844074b9974bd56ff2c2458f2297fe0df56a5b", - "zh:bc7a434408eb16a4fbceec0bd86b108a491408b727071402ad572cdb1afa2eb7", - "zh:c8e4728ea2d2c6e3d2c1bc5e7d92ed1121c02bab687702ec2748e3a6a0844150", - "zh:f6314b2cff0c0a07a216501cda51b35e6a4c66a2418c7c9966ccfe701e01b6b0", + "h1:Yq0DZYKVFwPdc+v5NnXYcgTYWKInSkjv5WjyWMODj9U=", + "zh:0b1b5342db6a17de7c71386704e101be7d6761569e03fb3ff1f3d4c02c32d998", + "zh:2fb663467fff76852126b58315d9a1a457e3b04bec51f04bf1c0ddc9dfbb3517", + "zh:4183e557a1dfd413dae90ca4bac37dbbe499eae5e923567371f768053f977800", + "zh:48b2979f88fb55cdb14b7e4c37c44e0dfbc21b7a19686ce75e339efda773c5c2", + "zh:5d803fb06625e0bcf83abb590d4235c117fa7f4aa2168fa3d5f686c41bc529ec", + "zh:6f1dd094cbab36363583cda837d7ca470bef5f8abf9b19f23e9cd8b927153498", + "zh:772edb5890d72b32868f9fdc0a9a1d4f4701d8e7f8acb37a7ac530d053c776e3", + "zh:798f443dbba6610431dcef832047f6917fb5a4e184a3a776c44e6213fb429cc6", + "zh:cc08dfcc387e2603f6dbaff8c236c1254185450d6cadd6bad92879fe7e7dbce9", + "zh:d5e2c8d7f50f91d6847ddce27b10b721bdfce99c1bbab42a68fa271337d73d63", + "zh:e69a0045440c706f50f84a84ff8b1df520ec9bf757de4b8f9959f2ed20c3f440", + "zh:efc5358573a6403cbea3a08a2fcd2407258ac083d9134c641bdcb578966d8bdf", + "zh:f627a255e5809ec2375f79949c79417847fa56b9e9222ea7c45a463eb663f137", + "zh:f7c02f762e4cf1de7f58bde520798491ccdd54a5bd52278d579c146d1d07d4f0", "zh:fbd1fee2c9df3aa19cf8851ce134dea6e45ea01cb85695c1726670c285797e25", ] } diff --git a/.github/config/MODULE.MD b/.github/config/MODULE.MD index 4a0f4ab..4822c4f 100644 --- a/.github/config/MODULE.MD +++ b/.github/config/MODULE.MD @@ -9,14 +9,13 @@ | Name | Version | |------|---------| -| [github](#provider\_github) | 6.3.1 | +| [github](#provider\_github) | 6.6.0 | ## Modules | Name | Source | Version | |------|--------|---------| -| [keyfactor\_github\_test\_environment\_12\_3\_0\_kc](#module\_keyfactor\_github\_test\_environment\_12\_3\_0\_kc) | git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git | main | -| [keyfactor\_github\_test\_environment\_ad\_10\_5\_0](#module\_keyfactor\_github\_test\_environment\_ad\_10\_5\_0) | git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git | main | +| [keyfactor\_github\_test\_environment\_ses\_2441](#module\_keyfactor\_github\_test\_environment\_ses\_2441) | git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git | main | ## Resources @@ -28,11 +27,15 @@ | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [keyfactor\_auth\_token\_url\_12\_3\_0\_KC](#input\_keyfactor\_auth\_token\_url\_12\_3\_0\_KC) | The hostname of the KeyCloak instance to authenticate to for a Keyfactor Command access token | `string` | `"https://int-oidc-lab.eastus2.cloudapp.azure.com:8444/realms/Keyfactor/protocol/openid-connect/token"` | no | +| [keyfactor\_auth\_token\_url\_12\_3\_0\_KC](#input\_keyfactor\_auth\_token\_url\_12\_3\_0\_KC) | The hostname of the KeyCloak instance to authenticate to for a Keyfactor Command access token | `string` | `"https://int1230-oauth.eastus2.cloudapp.azure.com:8444/realms/Keyfactor/protocol/openid-connect/token"` | no | +| [keyfactor\_auth\_token\_url\_ses\_2441](#input\_keyfactor\_auth\_token\_url\_ses\_2441) | The hostname of the KeyCloak instance to authenticate to for a Keyfactor Command access token | `string` | `"https://auth.kftestlab.com/oauth2/token"` | no | | [keyfactor\_client\_id\_12\_3\_0](#input\_keyfactor\_client\_id\_12\_3\_0) | The client ID to authenticate with the Keyfactor instance using Keycloak client credentials | `string` | n/a | yes | +| [keyfactor\_client\_id\_ses\_2441](#input\_keyfactor\_client\_id\_ses\_2441) | The client ID to authenticate with the Keyfactor instance using Keycloak client credentials | `string` | n/a | yes | | [keyfactor\_client\_secret\_12\_3\_0](#input\_keyfactor\_client\_secret\_12\_3\_0) | The client secret to authenticate with the Keyfactor instance using Keycloak client credentials | `string` | n/a | yes | +| [keyfactor\_client\_secret\_ses\_2441](#input\_keyfactor\_client\_secret\_ses\_2441) | The client secret to authenticate with the Keyfactor instance using Keycloak client credentials | `string` | n/a | yes | | [keyfactor\_hostname\_10\_5\_0](#input\_keyfactor\_hostname\_10\_5\_0) | The hostname of the Keyfactor instance | `string` | `"integrations1050-lab.kfdelivery.com"` | no | -| [keyfactor\_hostname\_12\_3\_0\_KC](#input\_keyfactor\_hostname\_12\_3\_0\_KC) | The hostname of the Keyfactor instance | `string` | `"int-oidc-lab.eastus2.cloudapp.azure.com"` | no | +| [keyfactor\_hostname\_12\_3\_0\_KC](#input\_keyfactor\_hostname\_12\_3\_0\_KC) | The hostname of the Keyfactor instance | `string` | `"int1230-oauth.eastus2.cloudapp.azure.com"` | no | +| [keyfactor\_hostname\_ses\_2441](#input\_keyfactor\_hostname\_ses\_2441) | The hostname of the Keyfactor instance | `string` | `"int2441.kftestlab.com"` | no | | [keyfactor\_password\_10\_5\_0](#input\_keyfactor\_password\_10\_5\_0) | The password to authenticate with the Keyfactor instance | `string` | n/a | yes | | [keyfactor\_username\_10\_5\_0](#input\_keyfactor\_username\_10\_5\_0) | The username to authenticate with the Keyfactor instance | `string` | n/a | yes | diff --git a/.github/config/README.md b/.github/config/README.md index 42fee46..9380eae 100644 --- a/.github/config/README.md +++ b/.github/config/README.md @@ -58,14 +58,13 @@ module "keyfactor_github_test_environment_12_3_0_kc" { | Name | Version | |------|---------| -| [github](#provider\_github) | 6.3.1 | +| [github](#provider\_github) | 6.6.0 | ## Modules | Name | Source | Version | |------|--------|---------| -| [keyfactor\_github\_test\_environment\_12\_3\_0\_kc](#module\_keyfactor\_github\_test\_environment\_12\_3\_0\_kc) | git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git | main | -| [keyfactor\_github\_test\_environment\_ad\_10\_5\_0](#module\_keyfactor\_github\_test\_environment\_ad\_10\_5\_0) | git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git | main | +| [keyfactor\_github\_test\_environment\_ses\_2441](#module\_keyfactor\_github\_test\_environment\_ses\_2441) | git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git | main | ## Resources @@ -77,11 +76,15 @@ module "keyfactor_github_test_environment_12_3_0_kc" { | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [keyfactor\_auth\_token\_url\_12\_3\_0\_KC](#input\_keyfactor\_auth\_token\_url\_12\_3\_0\_KC) | The hostname of the KeyCloak instance to authenticate to for a Keyfactor Command access token | `string` | `"https://int-oidc-lab.eastus2.cloudapp.azure.com:8444/realms/Keyfactor/protocol/openid-connect/token"` | no | +| [keyfactor\_auth\_token\_url\_12\_3\_0\_KC](#input\_keyfactor\_auth\_token\_url\_12\_3\_0\_KC) | The hostname of the KeyCloak instance to authenticate to for a Keyfactor Command access token | `string` | `"https://int1230-oauth.eastus2.cloudapp.azure.com:8444/realms/Keyfactor/protocol/openid-connect/token"` | no | +| [keyfactor\_auth\_token\_url\_ses\_2441](#input\_keyfactor\_auth\_token\_url\_ses\_2441) | The hostname of the KeyCloak instance to authenticate to for a Keyfactor Command access token | `string` | `"https://auth.kftestlab.com/oauth2/token"` | no | | [keyfactor\_client\_id\_12\_3\_0](#input\_keyfactor\_client\_id\_12\_3\_0) | The client ID to authenticate with the Keyfactor instance using Keycloak client credentials | `string` | n/a | yes | +| [keyfactor\_client\_id\_ses\_2441](#input\_keyfactor\_client\_id\_ses\_2441) | The client ID to authenticate with the Keyfactor instance using Keycloak client credentials | `string` | n/a | yes | | [keyfactor\_client\_secret\_12\_3\_0](#input\_keyfactor\_client\_secret\_12\_3\_0) | The client secret to authenticate with the Keyfactor instance using Keycloak client credentials | `string` | n/a | yes | +| [keyfactor\_client\_secret\_ses\_2441](#input\_keyfactor\_client\_secret\_ses\_2441) | The client secret to authenticate with the Keyfactor instance using Keycloak client credentials | `string` | n/a | yes | | [keyfactor\_hostname\_10\_5\_0](#input\_keyfactor\_hostname\_10\_5\_0) | The hostname of the Keyfactor instance | `string` | `"integrations1050-lab.kfdelivery.com"` | no | -| [keyfactor\_hostname\_12\_3\_0\_KC](#input\_keyfactor\_hostname\_12\_3\_0\_KC) | The hostname of the Keyfactor instance | `string` | `"int-oidc-lab.eastus2.cloudapp.azure.com"` | no | +| [keyfactor\_hostname\_12\_3\_0\_KC](#input\_keyfactor\_hostname\_12\_3\_0\_KC) | The hostname of the Keyfactor instance | `string` | `"int1230-oauth.eastus2.cloudapp.azure.com"` | no | +| [keyfactor\_hostname\_ses\_2441](#input\_keyfactor\_hostname\_ses\_2441) | The hostname of the Keyfactor instance | `string` | `"int2441.kftestlab.com"` | no | | [keyfactor\_password\_10\_5\_0](#input\_keyfactor\_password\_10\_5\_0) | The password to authenticate with the Keyfactor instance | `string` | n/a | yes | | [keyfactor\_username\_10\_5\_0](#input\_keyfactor\_username\_10\_5\_0) | The username to authenticate with the Keyfactor instance | `string` | n/a | yes | diff --git a/.github/config/environments.tf b/.github/config/environments.tf index d26fab7..193e4ee 100644 --- a/.github/config/environments.tf +++ b/.github/config/environments.tf @@ -1,13 +1,13 @@ -module "keyfactor_github_test_environment_ad_10_5_0" { - source = "git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git?ref=main" - - gh_environment_name = "KFC_10_5_0" - gh_repo_name = data.github_repository.repo.name - keyfactor_hostname = var.keyfactor_hostname_10_5_0 - keyfactor_username = var.keyfactor_username_10_5_0 - keyfactor_password = var.keyfactor_password_10_5_0 - keyfactor_config_file = base64encode(file("${path.module}/command_config.json")) -} +# module "keyfactor_github_test_environment_ad_10_5_0" { +# source = "git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git?ref=main" +# +# gh_environment_name = "KFC_10_5_0" +# gh_repo_name = data.github_repository.repo.name +# keyfactor_hostname = var.keyfactor_hostname_10_5_0 +# keyfactor_username = var.keyfactor_username_10_5_0 +# keyfactor_password = var.keyfactor_password_10_5_0 +# keyfactor_config_file = base64encode(file("${path.module}/command_config.json")) +# } # module "keyfactor_github_test_environment_11_5_0_kc" { # source = "git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-kc.git?ref=main" @@ -21,15 +21,29 @@ module "keyfactor_github_test_environment_ad_10_5_0" { # keyfactor_tls_skip_verify = true # } -module "keyfactor_github_test_environment_12_3_0_kc" { +# module "keyfactor_github_test_environment_12_3_0_kc" { +# source = "git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git?ref=main" +# +# gh_environment_name = "KFC_12_3_0_KC" +# gh_repo_name = data.github_repository.repo.name +# keyfactor_hostname = var.keyfactor_hostname_12_3_0_KC +# keyfactor_auth_token_url = var.keyfactor_auth_token_url_12_3_0_KC +# keyfactor_client_id = var.keyfactor_client_id_12_3_0 +# keyfactor_client_secret = var.keyfactor_client_secret_12_3_0 +# keyfactor_tls_skip_verify = true +# keyfactor_config_file = base64encode(file("${path.module}/command_config.json")) +# } + +module "keyfactor_github_test_environment_ses_2441" { source = "git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git?ref=main" - gh_environment_name = "KFC_12_3_0_KC" + gh_environment_name = "ses_2441" gh_repo_name = data.github_repository.repo.name - keyfactor_hostname = var.keyfactor_hostname_12_3_0_KC - keyfactor_auth_token_url = var.keyfactor_auth_token_url_12_3_0_KC - keyfactor_client_id = var.keyfactor_client_id_12_3_0 - keyfactor_client_secret = var.keyfactor_client_secret_12_3_0 + keyfactor_hostname = var.keyfactor_hostname_ses_2441 + keyfactor_auth_token_url = var.keyfactor_auth_token_url_ses_2441 + keyfactor_client_id = var.keyfactor_client_id_ses_2441 + keyfactor_client_secret = var.keyfactor_client_secret_ses_2441 keyfactor_tls_skip_verify = true + keyfactor_api_path = "/Keyfactor/API" keyfactor_config_file = base64encode(file("${path.module}/command_config.json")) -} +} \ No newline at end of file diff --git a/.github/config/variables.tf b/.github/config/variables.tf index 29ffd56..55b40db 100644 --- a/.github/config/variables.tf +++ b/.github/config/variables.tf @@ -37,3 +37,25 @@ variable "keyfactor_auth_token_url_12_3_0_KC" { default = "https://int1230-oauth.eastus2.cloudapp.azure.com:8444/realms/Keyfactor/protocol/openid-connect/token" } +variable "keyfactor_client_id_ses_2441" { + description = "The client ID to authenticate with the Keyfactor instance using Keycloak client credentials" + type = string +} + +variable "keyfactor_client_secret_ses_2441" { + description = "The client secret to authenticate with the Keyfactor instance using Keycloak client credentials" + type = string +} + +variable "keyfactor_hostname_ses_2441" { + description = "The hostname of the Keyfactor instance" + type = string + default = "int2441.kftestlab.com" + +} + +variable "keyfactor_auth_token_url_ses_2441" { + description = "The hostname of the KeyCloak instance to authenticate to for a Keyfactor Command access token" + type = string + default = "https://auth.kftestlab.com/oauth2/token" +} \ No newline at end of file diff --git a/.github/workflows/go_tests.yml b/.github/workflows/go_tests.yml index 6a09a83..bcb842a 100644 --- a/.github/workflows/go_tests.yml +++ b/.github/workflows/go_tests.yml @@ -12,7 +12,8 @@ jobs: matrix: environment: # - "KFC_10_5_0" - - "KFC_12_3_0_KC" +# - "KFC_12_3_0_KC" + - "ses_2441" environment: ${{ matrix.environment }} steps: - name: Check out code @@ -21,15 +22,11 @@ jobs: - name: Set up Go uses: actions/setup-go@v5 with: - go-version: 1.22 + go-version: 1.24 - name: Get Public IP run: curl -s https://api.ipify.org - - name: Validate lab cert is present - run: | - cat lib/certs/int-oidc-lab.eastus2.cloudapp.azure.com.crt - - name: Run tests run: | if [ -n "${{ secrets.KEYFACTOR_AUTH_CONFIG_B64 }}" ]; then @@ -48,4 +45,6 @@ jobs: KEYFACTOR_AUTH_HOSTNAME: ${{ vars.KEYFACTOR_AUTH_HOSTNAME }} KEYFACTOR_SKIP_VERIFY: ${{ vars.KEYFACTOR_SKIP_VERIFY }} TEST_KEYFACTOR_AD_AUTH: ${{ vars.TEST_KEYFACTOR_AD_AUTH }} - TEST_KEYFACTOR_KC_AUTH: ${{ vars.TEST_KEYFACTOR_KC_AUTH }} \ No newline at end of file + TEST_KEYFACTOR_OAUTH: ${{ vars.TEST_KEYFACTOR_OAUTH }} + TEST_UNTRUSTED_CERT: ${{ vars.TEST_UNTRUSTED_CERT }} + KEYFACTOR_API_PATH: ${{ vars.KEYFACTOR_API_PATH }} \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 0978c43..b5a8d32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +# v1.3.0 + +## Features +- Add support for fetching an oauth2 token using the `client_credentials` grant type without connecting to Keyfactor Command. +- Add placeholders for omitted `Authorization` header in the `curl` command string output in trace logging. + +## Bug Fixes +- Log `curl` command string at `trace` level after request is sent to include any transport mutations. + +## Chores +- Bump Go version to `1.24`. + # v1.2.0 ## Features diff --git a/auth_providers/auth_basic_test.go b/auth_providers/auth_basic_test.go index 5c404ae..5aa3c50 100644 --- a/auth_providers/auth_basic_test.go +++ b/auth_providers/auth_basic_test.go @@ -25,8 +25,8 @@ import ( ) func TestBasicAuthAuthenticator_GetHttpClient(t *testing.T) { - // Skip test if TEST_KEYFACTOR_KC_AUTH is set to 1 or true - if os.Getenv("TEST_KEYFACTOR_KC_AUTH") == "1" || os.Getenv("TEST_KEYFACTOR_KC_AUTH") == "true" { + // Skip test if TEST_KEYFACTOR_OAUTH is set to 1 or true + if os.Getenv("TEST_KEYFACTOR_OAUTH") == "1" || os.Getenv("TEST_KEYFACTOR_OAUTH") == "true" { t.Skip("Skipping TestBasicAuthAuthenticator_GetHttpClient") return } @@ -46,8 +46,8 @@ func TestBasicAuthAuthenticator_GetHttpClient(t *testing.T) { } func TestCommandAuthConfigBasic_ValidateAuthConfig(t *testing.T) { - // Skip test if TEST_KEYFACTOR_KC_AUTH is set to 1 or true - if os.Getenv("TEST_KEYFACTOR_KC_AUTH") == "1" || os.Getenv("TEST_KEYFACTOR_KC_AUTH") == "true" { + // Skip test if TEST_KEYFACTOR_OAUTH is set to 1 or true + if os.Getenv("TEST_KEYFACTOR_OAUTH") == "1" || os.Getenv("TEST_KEYFACTOR_OAUTH") == "true" { t.Skip("Skipping TestBasicAuthAuthenticator_GetHttpClient") return } @@ -63,8 +63,8 @@ func TestCommandAuthConfigBasic_ValidateAuthConfig(t *testing.T) { } func TestCommandAuthConfigBasic_GetHttpClient(t *testing.T) { - // Skip test if TEST_KEYFACTOR_KC_AUTH is set to 1 or true - if os.Getenv("TEST_KEYFACTOR_KC_AUTH") == "1" || os.Getenv("TEST_KEYFACTOR_KC_AUTH") == "true" { + // Skip test if TEST_KEYFACTOR_OAUTH is set to 1 or true + if os.Getenv("TEST_KEYFACTOR_OAUTH") == "1" || os.Getenv("TEST_KEYFACTOR_OAUTH") == "true" { t.Skip("Skipping TestBasicAuthAuthenticator_GetHttpClient") return } @@ -85,8 +85,8 @@ func TestCommandAuthConfigBasic_GetHttpClient(t *testing.T) { } func TestCommandAuthConfigBasic_Authenticate(t *testing.T) { - // Skip test if TEST_KEYFACTOR_KC_AUTH is set to 1 or true - if os.Getenv("TEST_KEYFACTOR_KC_AUTH") == "1" || os.Getenv("TEST_KEYFACTOR_KC_AUTH") == "true" { + // Skip test if TEST_KEYFACTOR_OAUTH is set to 1 or true + if os.Getenv("TEST_KEYFACTOR_OAUTH") == "1" || os.Getenv("TEST_KEYFACTOR_OAUTH") == "true" { t.Skip("Skipping TestBasicAuthAuthenticator_GetHttpClient") return } @@ -230,8 +230,8 @@ func TestCommandAuthConfigBasic_Authenticate(t *testing.T) { } func TestCommandAuthConfigBasic_Build(t *testing.T) { - // Skip test if TEST_KEYFACTOR_KC_AUTH is set to 1 or true - if os.Getenv("TEST_KEYFACTOR_KC_AUTH") == "1" || os.Getenv("TEST_KEYFACTOR_KC_AUTH") == "true" { + // Skip test if TEST_KEYFACTOR_OAUTH is set to 1 or true + if os.Getenv("TEST_KEYFACTOR_OAUTH") == "1" || os.Getenv("TEST_KEYFACTOR_OAUTH") == "true" { t.Skip("Skipping TestBasicAuthAuthenticator_GetHttpClient") return } diff --git a/auth_providers/auth_core.go b/auth_providers/auth_core.go index 91f3504..538a22a 100644 --- a/auth_providers/auth_core.go +++ b/auth_providers/auth_core.go @@ -501,12 +501,13 @@ func (c *CommandAuthConfig) Authenticate() error { } c.HttpClient.Timeout = time.Duration(c.HttpClientTimeout) * time.Second - curlStr, cErr := RequestToCurl(req) - if cErr == nil { + + cResp, cErr := c.HttpClient.Do(req) + curlStr, curlErr := RequestToCurl(req) + if curlErr == nil { log.Printf("[TRACE] curl command: %s", curlStr) } - cResp, cErr := c.HttpClient.Do(req) if cErr != nil { return cErr } else if cResp == nil { @@ -514,6 +515,7 @@ func (c *CommandAuthConfig) Authenticate() error { } defer cResp.Body.Close() + log.Printf("[DEBUG] request to Keyfactor Command API returned status code %d", cResp.StatusCode) // check if body is empty if cResp.Body == nil { @@ -798,19 +800,56 @@ func RequestToCurl(req *http.Request) (string, error) { // Add headers for name, values := range req.Header { for _, value := range values { + // check if is Authorization header and skip it + if strings.EqualFold(name, "Authorization") { + // check if basic auth and skip it + if strings.HasPrefix(value, "Basic ") { + // Remove credentials and put in env variables as placeholder + log.Printf( + "[DEBUG] Found Basic auth in Authorization header, " + + "replacing with env variable references", + ) + curlCommand.WriteString( + fmt.Sprintf( + "-H %q ", fmt.Sprintf( + "%s: Basic $(echo -n $\"%s,$%s\" | base64)", name, + EnvKeyfactorUsername, EnvKeyfactorPassword, + ), + ), + ) + continue + } else if strings.HasPrefix(value, "Bearer ") { + // Remove credentials and put in env variables as placeholder + log.Printf("[DEBUG] Found Bearer token in Authorization header, replacing with kfutil command to fetch token") + curlCommand.WriteString( + fmt.Sprintf( + "-H %q ", fmt.Sprintf( + "%s: Bearer $(kfutil auth fetch-oauth-token)", name, + ), + ), + ) + continue + } else { + // Skip other Authorization headers + log.Printf("[ERROR] Skipping unhandled Authorization header: %s", name) + continue + } + } curlCommand.WriteString(fmt.Sprintf("-H %q ", fmt.Sprintf("%s: %s", name, value))) } } // Add the body if it exists if req.Method == http.MethodPost || req.Method == http.MethodPut { - body, err := io.ReadAll(req.Body) - if err != nil { - return "", err - } - req.Body = io.NopCloser(bytes.NewBuffer(body)) // Restore the request body + if req.Body != nil { + body, err := io.ReadAll(req.Body) + if err != nil { + return "", err + } + req.Body = io.NopCloser(bytes.NewBuffer(body)) // Restore the request body - curlCommand.WriteString(fmt.Sprintf("--data %q ", string(body))) + curlCommand.WriteString(fmt.Sprintf("--data %q ", string(body))) + } } return curlCommand.String(), nil diff --git a/auth_providers/auth_core_test.go b/auth_providers/auth_core_test.go index efcf2a9..43eb765 100644 --- a/auth_providers/auth_core_test.go +++ b/auth_providers/auth_core_test.go @@ -16,6 +16,7 @@ package auth_providers_test import ( "net/http" + "strings" "testing" "github.com/Keyfactor/keyfactor-auth-client-go/auth_providers" @@ -102,3 +103,75 @@ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7Q2+1+2+1+2+1+2+1+2+ t.Fatalf("expected non-zero blocks") } } + +func TestRequestToCurl(t *testing.T) { + tests := []struct { + name string + method string + url string + headers map[string]string + wantInCurl []string + notWantInCurl []string + }{ + { + name: "Basic Auth", + method: "GET", + url: "https://example.com/api", + headers: map[string]string{ + "Authorization": "Basic dXNlcjpwYXNz", + }, + wantInCurl: []string{ + "curl", "-X", "GET", "https://example.com/api", + "-H", "Authorization: Basic", + }, + notWantInCurl: []string{ + "Authorization: Basic dXNlcjpwYXNz", + }, + }, + { + name: "Bearer Auth", + method: "POST", + url: "https://example.com/token", + headers: map[string]string{ + "Authorization": "Bearer testtoken", + "Content-Type": "application/json", + }, + wantInCurl: []string{ + "curl", "-X", "POST", "https://example.com/token", + "-H", "Authorization: Bearer", + "-H", "Content-Type: application/json", + }, + notWantInCurl: []string{ + "Authorization: Bearer testtoken", + }, + }, + } + + for _, tt := range tests { + req, err := http.NewRequest(tt.method, tt.url, nil) + if err != nil { + t.Fatalf("failed to create request: %v", err) + } + for k, v := range tt.headers { + req.Header.Set(k, v) + } + + curlStr, err := auth_providers.RequestToCurl(req) + if err != nil { + t.Errorf("%s: RequestToCurl returned error: %v", tt.name, err) + continue + } + for _, want := range tt.wantInCurl { + if !strings.Contains(curlStr, want) { + t.Errorf("%s: curl string missing %q\nGot: %s", tt.name, want, curlStr) + } + } + + for _, notWant := range tt.notWantInCurl { + if strings.Contains(curlStr, notWant) { + t.Errorf("%s: curl string contains unwanted %q\nGot: %s", tt.name, notWant, curlStr) + } + } + t.Logf("%s: curl command: %s", tt.name, curlStr) + } +} diff --git a/auth_providers/auth_oauth.go b/auth_providers/auth_oauth.go index da9dd32..d402b75 100644 --- a/auth_providers/auth_oauth.go +++ b/auth_providers/auth_oauth.go @@ -18,6 +18,7 @@ import ( "context" "crypto/x509" "fmt" + "log" "net/http" "os" "strings" @@ -439,16 +440,79 @@ func (b *CommandConfigOauth) GetServerConfig() *Server { return &server } +// GetAccessToken returns the OAuth2 token source for the given configuration. +func (b *CommandConfigOauth) GetAccessToken() (*oauth2.Token, error) { + if b == nil { + return nil, fmt.Errorf("CommandConfigOauth is nil") + } + + _ = b.ValidateAuthConfig() // sets client config if not already set but eats the error so that we can return and + // error fetching the token + + if b.AccessToken != "" && (b.ClientID == "" || b.ClientSecret == "" || b.TokenURL == "") { + log.Printf("[DEBUG] Access token is explicitly set, and no client credentials are provided. Using static token source.") + return &oauth2.Token{ + AccessToken: b.AccessToken, + TokenType: DefaultTokenPrefix, + Expiry: b.Expiry, + }, nil + } + + log.Printf("[DEBUG] Getting OAuth2 token source for client ID: %s", b.ClientID) + if b.ClientID == "" || b.ClientSecret == "" || b.TokenURL == "" { + return nil, fmt.Errorf("client ID, client secret, and token URL must be provided") + } + + config := &clientcredentials.Config{ + ClientID: b.ClientID, + ClientSecret: b.ClientSecret, + TokenURL: b.TokenURL, + Scopes: b.Scopes, + } + + if b.Audience != "" { + log.Printf("[DEBUG] Setting audience for OAuth2 token source: %s", b.Audience) + config.EndpointParams = map[string][]string{ + "audience": {b.Audience}, + } + } + + ctx := context.Background() + log.Printf("[DEBUG] Returning call config.TokenSource() for client ID: %s", b.ClientID) + tokenSource := config.TokenSource(ctx) + if tokenSource == nil { + return nil, fmt.Errorf("failed to create token source for client ID: %s", b.ClientID) + } + token, tErr := tokenSource.Token() + if tErr != nil { + return nil, fmt.Errorf("failed to retrieve token for client ID %s: %w", b.ClientID, tErr) + } + if token == nil || token.AccessToken == "" { + return nil, fmt.Errorf("received empty OAuth token for client ID: %s", b.ClientID) + } + + return token, nil +} + // RoundTrip executes a single HTTP transaction, adding the OAuth2 token to the request func (t *oauth2Transport) RoundTrip(req *http.Request) (*http.Response, error) { + log.Printf("[DEBUG] Attempting to get oAuth token from: %s %s", req.Method, req.URL) token, err := t.src.Token() if err != nil { + return nil, fmt.Errorf("failed to retrieve OAuth token: %w", err) } + if token == nil || token.AccessToken == "" { + return nil, fmt.Errorf("received empty OAuth token") + } + // Clone the request to avoid mutating the original + log.Printf("[DEBUG] Adding oAuth token to request: %s %s", req.Method, req.URL) reqCopy := req.Clone(req.Context()) token.SetAuthHeader(reqCopy) + requestCurlStr, _ := RequestToCurl(reqCopy) + log.Printf("[TRACE] curl command: %s", requestCurlStr) return t.base.RoundTrip(reqCopy) } diff --git a/auth_providers/auth_oauth_test.go b/auth_providers/auth_oauth_test.go index 1d8e565..720a9ae 100644 --- a/auth_providers/auth_oauth_test.go +++ b/auth_providers/auth_oauth_test.go @@ -188,15 +188,22 @@ func TestCommandConfigOauth_Authenticate(t *testing.T) { // end test case // Begin test case - noParamsConfig = &auth_providers.CommandConfigOauth{} - httpsFailEnvExpected := []string{"tls: failed to verify certificate"} - authOauthTest( - t, - fmt.Sprintf("w/o env %s", auth_providers.EnvKeyfactorCACert), - true, - noParamsConfig, - httpsFailEnvExpected..., - ) + + if os.Getenv("TEST_UNTRUSTED_CERT") == "1" || os.Getenv("TEST_UNTRUSTED_CERT") == "true" { + noParamsConfig = &auth_providers.CommandConfigOauth{} + httpsFailEnvExpected := []string{"tls: failed to verify certificate"} + t.Log("Testing oAuth with https fail env") + t.Logf("Setting environment variable %s", auth_providers.EnvKeyfactorSkipVerify) + os.Setenv(auth_providers.EnvKeyfactorSkipVerify, "false") + authOauthTest( + t, + fmt.Sprintf("w/o env %s", auth_providers.EnvKeyfactorCACert), + true, + noParamsConfig, + httpsFailEnvExpected..., + ) + } + // end test case t.Log("Testing oAuth with invalid config file path") @@ -256,7 +263,7 @@ func TestCommandConfigOauth_Authenticate(t *testing.T) { } fullParamsInvalidPassConfig.WithSkipVerify(true) invalidCredsExpectedError := []string{ - "oauth2", "unauthorized_client", "Invalid client or Invalid client credentials", + "oauth2", "fail", "invalid", "client", } authOauthTest(t, "w/ full params & invalid pass", true, fullParamsInvalidPassConfig, invalidCredsExpectedError...) @@ -314,15 +321,17 @@ func TestCommandConfigOauth_Authenticate(t *testing.T) { t.Logf("Unsetting environment variable %s", auth_providers.EnvKeyfactorSkipVerify) os.Unsetenv(auth_providers.EnvKeyfactorSkipVerify) - t.Log("Testing oAuth with valid implicit config file https fail") - httpsFailConfigFile := &auth_providers.CommandConfigOauth{} - httpsFailConfigFile. - WithConfigProfile("oauth") - httpsFailConfigFileExpected := []string{"tls: failed to verify certificate"} - authOauthTest( - t, "oAuth with valid implicit config file https fail", true, httpsFailConfigFile, - httpsFailConfigFileExpected..., - ) + if os.Getenv("TEST_UNTRUSTED_CERT") == "1" || os.Getenv("TEST_UNTRUSTED_CERT") == "true" { + t.Log("Testing oAuth with valid implicit config file https fail") + httpsFailConfigFile := &auth_providers.CommandConfigOauth{} + httpsFailConfigFile. + WithConfigProfile("oauth") + httpsFailConfigFileExpected := []string{"tls: failed to verify certificate"} + authOauthTest( + t, "oAuth with valid implicit config file https fail", true, httpsFailConfigFile, + httpsFailConfigFileExpected..., + ) + } t.Log("Testing oAuth with invalid profile implicit config file") invProfile := &auth_providers.CommandConfigOauth{} @@ -346,6 +355,18 @@ func TestCommandConfigOauth_Authenticate(t *testing.T) { authOauthTest(t, "with invalid creds implicit config file", true, invCmdHost, invHostExpectedError...) } +func TestCommandConfigOauth_GetAccessToken(t *testing.T) { + clientID, clientSecret, tokenURL := exportOAuthEnvVariables() + t.Log("Testing auth with w/ full params variables") + fullParamsConfig := &auth_providers.CommandConfigOauth{ + ClientID: clientID, + ClientSecret: clientSecret, + TokenURL: tokenURL, + } + fullParamsConfig.WithSkipVerify(true) + authOauthTest(t, "w/ GetAccessToken w/ full params variables", false, fullParamsConfig) +} + func TestCommandConfigOauth_Build(t *testing.T) { // Skip test if TEST_KEYFACTOR_AD_AUTH is set to 1 or true if os.Getenv("TEST_KEYFACTOR_AD_AUTH") == "1" || os.Getenv("TEST_KEYFACTOR_AD_AUTH") == "true" { @@ -376,6 +397,23 @@ func authOauthTest( t.Run( fmt.Sprintf("oAuth Auth Test %s", testName), func(t *testing.T) { + // oauth credentials should always generate an access token + oauthToken, tErr := config.GetAccessToken() + if !allowFail { + if tErr != nil { + t.Errorf("oAuth auth test '%s' failed to get token source with %v", testName, tErr) + t.FailNow() + return + } + + if oauthToken == nil || oauthToken.AccessToken == "" { + t.Errorf("oAuth auth test '%s' failed to get token source", testName) + t.FailNow() + return + } + //t.Logf("token %s", at.AccessToken) + t.Logf("oAuth auth test '%s' succeeded", testName) + } err := config.Authenticate() if allowFail { if err == nil { @@ -472,23 +510,23 @@ func DownloadCertificate(input string, outputPath string) error { // Set default output path to current working directory if none is provided if outputPath == "" { - cwd, err := os.Getwd() - if err != nil { - return fmt.Errorf("failed to get current working directory: %v", err) + cwd, cwdErr := os.Getwd() + if cwdErr != nil { + return fmt.Errorf("failed to get current working directory: %v", cwdErr) } outputPath = cwd } // Ensure the output directory exists - if err := os.MkdirAll(outputPath, os.ModePerm); err != nil { - return fmt.Errorf("failed to create output directory: %v", err) + if dirErr := os.MkdirAll(filepath.Dir(outputPath), os.ModePerm); dirErr != nil { + return fmt.Errorf("failed to create output directory: %v", dirErr) } // Create the output file - outputFile := filepath.Join(outputPath, fmt.Sprintf("%s.crt", hostname)) - file, err := os.Create(outputFile) - if err != nil { - return fmt.Errorf("failed to create file %s: %v", outputFile, err) + outputFile := filepath.Join(outputPath) + file, fErr := os.Create(outputFile) + if fErr != nil { + return fmt.Errorf("failed to create file %s: %v", outputFile, fErr) } defer file.Close() @@ -502,9 +540,9 @@ func DownloadCertificate(input string, outputPath string) error { } // Send an HTTP GET request to the server - resp, err := httpClient.Get(input) - if err != nil { - return fmt.Errorf("failed to connect to %s: %v", input, err) + resp, respErr := httpClient.Get(input) + if respErr != nil { + return fmt.Errorf("failed to connect to %s: %v", input, respErr) } defer resp.Body.Close() @@ -516,14 +554,14 @@ func DownloadCertificate(input string, outputPath string) error { // Write the entire certificate chain to the output file in PEM format for _, cert := range tlsConnState.PeerCertificates { - err = pem.Encode( + pemErr := pem.Encode( file, &pem.Block{ Type: "CERTIFICATE", Bytes: cert.Raw, }, ) - if err != nil { - return fmt.Errorf("failed to write certificate to file: %v", err) + if pemErr != nil { + return fmt.Errorf("failed to write certificate to file: %v", pemErr) } } diff --git a/go.mod b/go.mod index 1cd9b76..79537f1 100644 --- a/go.mod +++ b/go.mod @@ -14,30 +14,32 @@ module github.com/Keyfactor/keyfactor-auth-client-go -go 1.23 +go 1.24 + +toolchain go1.24.3 require ( - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1 - github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.3.0 + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 + github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.3.1 github.com/stretchr/testify v1.10.0 - golang.org/x/oauth2 v0.25.0 + golang.org/x/oauth2 v0.30.0 gopkg.in/yaml.v2 v2.4.0 ) require ( - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.0 // indirect - github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect + github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/golang-jwt/jwt/v5 v5.2.1 // indirect + github.com/golang-jwt/jwt/v5 v5.2.2 // indirect github.com/google/uuid v1.6.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/crypto v0.32.0 // indirect - golang.org/x/net v0.34.0 // indirect - golang.org/x/sys v0.29.0 // indirect - golang.org/x/text v0.21.0 // indirect + golang.org/x/crypto v0.39.0 // indirect + golang.org/x/net v0.41.0 // indirect + golang.org/x/sys v0.33.0 // indirect + golang.org/x/text v0.26.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 0d38ac5..43c21a3 100644 --- a/go.sum +++ b/go.sum @@ -1,33 +1,31 @@ -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 h1:g0EZJwz7xkXQiZAI5xi9f3WWFYBlX1CPTrR+NDToRkQ= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0/go.mod h1:XCW7KnZet0Opnr7HccfUw1PLc4CjHqpcaxW8DHklNkQ= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1 h1:1mvYtZfWQAnwNah/C+Z+Jb9rQH95LPE2vlmMuWAHJk8= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1/go.mod h1:75I/mXtme1JyWFtz8GocPHVFyH421IBoZErnO16dd0k= -github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.1 h1:Bk5uOhSAenHyR5P61D/NzeQCv+4fEVV8mOkJ82NqpWw= -github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.1/go.mod h1:QZ4pw3or1WPmRBxf0cHd1tknzrT54WPBOQoGutCPvSU= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.3.0 h1:WLUIpeyv04H0RCcQHaA4TNoyrQ39Ox7V+re+iaqzTe0= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.3.0/go.mod h1:hd8hTTIY3VmUVPRHNH7GVCHO3SHgXkJKZHReby/bnUQ= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.0 h1:eXnN9kaS8TiDwXjoie3hMRLuwdUBUMW9KRgOqB3mCaw= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.0/go.mod h1:XIpam8wumeZ5rVMuhdDQLMfIPDf1WO3IzrCRO3e3e3o= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 h1:B+blDbyVIG3WaikNxPnhPiJ1MThR03b3vKGtER95TP4= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.3.1 h1:mrkDCdkMsD4l9wjFGhofFHFrV43Y3c53RSLKOCJ5+Ow= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.3.1/go.mod h1:hPv41DbqMmnxcGralanA/kVlfdH5jv3T4LxGku2E1BY= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1 h1:bFWuoEKg+gImo7pvkiQEFAc8ocibADgXeiLAxWhWmkI= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1/go.mod h1:Vih/3yc6yac2JzU4hzpaDupBJP0Flaia9rXXrU8xyww= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= -github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 h1:kYRSnvJju5gYVyhkij+RTJ/VR6QIUaCfWeaFm2ycsjQ= -github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= +github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs= +github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= -github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= +github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs= -github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw= +github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU= +github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -38,23 +36,23 @@ github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmd github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= -github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= +github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI= +github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= -golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= -golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= -golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= -golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= +golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=