diff --git a/.github/config/.terraform.lock.hcl b/.github/config/.terraform.lock.hcl new file mode 100644 index 0000000..567b5b4 --- /dev/null +++ b/.github/config/.terraform.lock.hcl @@ -0,0 +1,25 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/integrations/github" { + version = "6.3.1" + 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", + "zh:fbd1fee2c9df3aa19cf8851ce134dea6e45ea01cb85695c1726670c285797e25", + ] +} diff --git a/.github/config/MODULE.MD b/.github/config/MODULE.MD new file mode 100644 index 0000000..b5afcef --- /dev/null +++ b/.github/config/MODULE.MD @@ -0,0 +1,65 @@ +## Requirements + +| Name | Version | +|---------------------------------------------------------------------------|---------| +| [terraform](#requirement\_terraform) | >= 1.0 | +| [github](#requirement\_github) | >=6.2 | + +## Providers + +| Name | Version | +|------------------------------------------------------------|---------| +| [github](#provider\_github) | 6.3.1 | + +## Modules + +| Name | Source | Version | +|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------|---------| +| [keyfactor\_github\_test\_environment\_10\_5\_0](#module\_keyfactor\_github\_test\_environment\_10\_5\_0) | git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git | main | +| [keyfactor\_github\_test\_environment\_10\_5\_0\_CLEAN](#module\_keyfactor\_github\_test\_environment\_10\_5\_0\_CLEAN) | git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git | main | +| [keyfactor\_github\_test\_environment\_11\_5\_0](#module\_keyfactor\_github\_test\_environment\_11\_5\_0) | git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git | main | +| [keyfactor\_github\_test\_environment\_11\_5\_0\_CLEAN](#module\_keyfactor\_github\_test\_environment\_11\_5\_0\_CLEAN) | git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git | main | +| [keyfactor\_github\_test\_environment\_11\_5\_0\_OAUTH](#module\_keyfactor\_github\_test\_environment\_11\_5\_0\_OAUTH) | git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git | main | +| [keyfactor\_github\_test\_environment\_11\_5\_0\_OAUTH\_CLEAN](#module\_keyfactor\_github\_test\_environment\_11\_5\_0\_OAUTH\_CLEAN) | git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git | main | +| [keyfactor\_github\_test\_environment\_12\_3\_0\_AD](#module\_keyfactor\_github\_test\_environment\_12\_3\_0\_AD) | git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git | main | +| [keyfactor\_github\_test\_environment\_12\_3\_0\_AD\_CLEAN](#module\_keyfactor\_github\_test\_environment\_12\_3\_0\_AD\_CLEAN) | git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git | main | +| [keyfactor\_github\_test\_environment\_12\_3\_0\_OAUTH](#module\_keyfactor\_github\_test\_environment\_12\_3\_0\_OAUTH) | git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git | main | +| [keyfactor\_github\_test\_environment\_12\_3\_0\_OAUTH\_CLEAN](#module\_keyfactor\_github\_test\_environment\_12\_3\_0\_OAUTH\_CLEAN) | git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git | main | + +## Resources + +| Name | Type | +|---------------------------------------------------------------------------------------------------------------------------|-------------| +| [github_repository.repo](https://registry.terraform.io/providers/integrations/github/latest/docs/data-sources/repository) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|---------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------|----------|-----------------------------------------------------------------------------------------------------------|:--------:| +| [keyfactor\_auth\_token\_url](#input\_keyfactor\_auth\_token\_url) | The token URL to authenticate with the Keyfactor instance using oauth2 client credentials | `string` | `"https://int-oidc-lab.eastus2.cloudapp.azure.com:8444/realms/Keyfactor/protocol/openid-connect/token"` | no | +| [keyfactor\_client\_id](#input\_keyfactor\_client\_id) | The client ID to authenticate with the Keyfactor instance using oauth2 client credentials | `string` | n/a | yes | +| [keyfactor\_client\_secret](#input\_keyfactor\_client\_secret) | The client secret to authenticate with the Keyfactor instance using oauth2 client credentials | `string` | n/a | yes | +| [keyfactor\_hostname\_10\_5\_0](#input\_keyfactor\_hostname\_10\_5\_0) | The hostname of the Keyfactor v10.5.x instance | `string` | `"integrations1050-lab.kfdelivery.com"` | no | +| [keyfactor\_hostname\_10\_5\_0\_CLEAN](#input\_keyfactor\_hostname\_10\_5\_0\_CLEAN) | The hostname of the Keyfactor v10.5.x instance with no stores or orchestrators. This is used for store-type tests. | `string` | `"int1050-test-clean.kfdelivery.com"` | no | +| [keyfactor\_hostname\_11\_5\_0](#input\_keyfactor\_hostname\_11\_5\_0) | The hostname of the Keyfactor v11.5.x instance | `string` | `"integrations1150-lab.kfdelivery.com"` | no | +| [keyfactor\_hostname\_11\_5\_0\_CLEAN](#input\_keyfactor\_hostname\_11\_5\_0\_CLEAN) | The hostname of the Keyfactor v11.5.x instance with no stores or orchestrators. This is used for store-type tests. | `string` | `"int1150-test-clean.kfdelivery.com"` | no | +| [keyfactor\_hostname\_11\_5\_0\_OAUTH](#input\_keyfactor\_hostname\_11\_5\_0\_OAUTH) | The hostname of the Keyfactor instance | `string` | `"int-oidc-lab.eastus2.cloudapp.azure.com"` | no | +| [keyfactor\_hostname\_11\_5\_0\_OAUTH\_CLEAN](#input\_keyfactor\_hostname\_11\_5\_0\_OAUTH\_CLEAN) | The hostname of the Keyfactor instance | `string` | `"int1150-oauth-test-clean.eastus2.cloudapp.azure.com"` | no | +| [keyfactor\_hostname\_12\_3\_0](#input\_keyfactor\_hostname\_12\_3\_0) | The hostname of the Keyfactor v12.3.x instance | `string` | `"integrations1230-lab.kfdelivery.com"` | no | +| [keyfactor\_hostname\_12\_3\_0\_CLEAN](#input\_keyfactor\_hostname\_12\_3\_0\_CLEAN) | The hostname of the Keyfactor v12.3.x instance with no stores or orchestrators. This is used for store-type tests. | `string` | `"int1230-test-clean.kfdelivery.com"` | no | +| [keyfactor\_hostname\_12\_3\_0\_OAUTH](#input\_keyfactor\_hostname\_12\_3\_0\_OAUTH) | The hostname of the Keyfactor instance | `string` | `"int-oidc-lab.eastus2.cloudapp.azure.com"` | no | +| [keyfactor\_password\_AD](#input\_keyfactor\_password\_AD) | The password to authenticate with Keyfactor instance that uses AD authentication | `string` | n/a | yes | +| [keyfactor\_username\_AD](#input\_keyfactor\_username\_AD) | The username to authenticate with a Keyfactor instance that uses AD authentication | `string` | n/a | yes | +| [kfc1230\_client\_id](#input\_kfc1230\_client\_id) | The client ID to authenticate with the Keyfactor instance using oauth2 client credentials | `string` | n/a | yes | +| [kfc1230\_client\_secret](#input\_kfc1230\_client\_secret) | The client secret to authenticate with the Keyfactor instance using oauth2 client credentials | `string` | n/a | yes | +| [kfc1230\_oauth\_hostname](#input\_kfc1230\_oauth\_hostname) | The hostname of the Keyfactor instance | `string` | `"int1230c-oauth.eastus2.cloudapp.azure.com"` | no | +| [kfc1230\_oauth\_token\_url](#input\_kfc1230\_oauth\_token\_url) | The hostname of the Keyfactor instance | `string` | `"https://int1230c-oauth.eastus2.cloudapp.azure.com:8444/realms/Keyfactor/protocol/openid-connect/token"` | no | +| [kfc1230c\_ad\_hostname](#input\_kfc1230c\_ad\_hostname) | The hostname of the Keyfactor instance | `string` | `"int1230c-ad.eastus2.cloudapp.azure.com"` | no | +| [kfc1230c\_client\_id](#input\_kfc1230c\_client\_id) | The client ID to authenticate with the Keyfactor instance using oauth2 client credentials | `string` | n/a | yes | +| [kfc1230c\_client\_secret](#input\_kfc1230c\_client\_secret) | The client secret to authenticate with the Keyfactor instance using oauth2 client credentials | `string` | n/a | yes | +| [kfc1230c\_oauth\_hostname](#input\_kfc1230c\_oauth\_hostname) | The hostname of the Keyfactor instance | `string` | `"int1230c-oauth.eastus2.cloudapp.azure.com"` | no | +| [kfc1230c\_oauth\_token\_url](#input\_kfc1230c\_oauth\_token\_url) | The hostname of the Keyfactor instance | `string` | `"https://int1230c-oauth.eastus2.cloudapp.azure.com:8444/realms/Keyfactor/protocol/openid-connect/token"` | no | + +## Outputs + +No outputs. diff --git a/.github/config/Makefile b/.github/config/Makefile new file mode 100644 index 0000000..f67d9df --- /dev/null +++ b/.github/config/Makefile @@ -0,0 +1,26 @@ +.DEFAULT_GOAL := help + +##@ Utility +help: ## Display this help + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) + +deps: ## Install deps for macos + @brew install pre-commit tflint terraform terraform-docs + +docs: ## Run terraform-docs to update module docs. + @terraform-docs markdown . > MODULE.MD + @terraform-docs markdown table --output-file README.md --output-mode inject . + +lint: ## Run tflint + @tflint + +validate: ## Run terraform validate + @terraform init --upgrade + @terraform validate + +precommit/add: ## Install pre-commit hook + @pre-commit install + +precommit/remove: ## Uninstall pre-commit hook + @pre-commit uninstall + diff --git a/.github/config/README.md b/.github/config/README.md new file mode 100644 index 0000000..2149532 --- /dev/null +++ b/.github/config/README.md @@ -0,0 +1,115 @@ +# GitHub Test Environment Setup + +This code sets up GitHub environments for testing against Keyfactor Command instances that are configured to use +Active Directory or Keycloak for authentication. + +## Requirements + +1. Terraform >= 1.0 +2. GitHub Provider >= 6.2 +3. Keyfactor Command instance(s) configured to use Active Directory or Keycloak for authentication +4. AD or Keycloak credentials for authenticating to the Keyfactor Command instance(s) +5. A GitHub token with access and permissions to the repository where the environments will be created + +## Adding a new environment + +Modify the `environments.tf` file to include the new environment module. The module should be named appropriately. +Example: + +### Active Directory Environment + +```hcl +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" # Keyfactor Command 10.5.0 environment using Active Directory(/Basic Auth) + gh_repo_name = data.github_repository.repo.name + keyfactor_hostname = var.keyfactor_hostname_10_5_0 + keyfactor_username = var.keyfactor_username_AD + keyfactor_password = var.keyfactor_password_AD +} +``` + +### oAuth Client Environment + +```hcl +module "keyfactor_github_test_environment_12_3_0_kc" { + source = "git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-kc.git?ref=main" + + gh_environment_name = "KFC_12_3_0_KC" # Keyfactor Command 12.3.0 environment using Keycloak + gh_repo_name = data.github_repository.repo.name + keyfactor_hostname = var.keyfactor_hostname_12_3_0_OAUTH + keyfactor_auth_token_url = var.keyfactor_auth_token_url + keyfactor_client_id = var.keyfactor_client_id + keyfactor_client_secret = var.keyfactor_client_secret + keyfactor_tls_skip_verify = true +} +``` + + +## Requirements + +| Name | Version | +|---------------------------------------------------------------------------|---------| +| [terraform](#requirement\_terraform) | >= 1.0 | +| [github](#requirement\_github) | >=6.2 | + +## Providers + +| Name | Version | +|------------------------------------------------------------|---------| +| [github](#provider\_github) | 6.3.1 | + +## Modules + +| Name | Source | Version | +|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------|---------| +| [keyfactor\_github\_test\_environment\_10\_5\_0](#module\_keyfactor\_github\_test\_environment\_10\_5\_0) | git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git | main | +| [keyfactor\_github\_test\_environment\_10\_5\_0\_CLEAN](#module\_keyfactor\_github\_test\_environment\_10\_5\_0\_CLEAN) | git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git | main | +| [keyfactor\_github\_test\_environment\_11\_5\_0](#module\_keyfactor\_github\_test\_environment\_11\_5\_0) | git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git | main | +| [keyfactor\_github\_test\_environment\_11\_5\_0\_CLEAN](#module\_keyfactor\_github\_test\_environment\_11\_5\_0\_CLEAN) | git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git | main | +| [keyfactor\_github\_test\_environment\_11\_5\_0\_OAUTH](#module\_keyfactor\_github\_test\_environment\_11\_5\_0\_OAUTH) | git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git | main | +| [keyfactor\_github\_test\_environment\_11\_5\_0\_OAUTH\_CLEAN](#module\_keyfactor\_github\_test\_environment\_11\_5\_0\_OAUTH\_CLEAN) | git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git | main | +| [keyfactor\_github\_test\_environment\_12\_3\_0\_AD](#module\_keyfactor\_github\_test\_environment\_12\_3\_0\_AD) | git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git | main | +| [keyfactor\_github\_test\_environment\_12\_3\_0\_AD\_CLEAN](#module\_keyfactor\_github\_test\_environment\_12\_3\_0\_AD\_CLEAN) | git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git | main | +| [keyfactor\_github\_test\_environment\_12\_3\_0\_OAUTH](#module\_keyfactor\_github\_test\_environment\_12\_3\_0\_OAUTH) | git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git | main | +| [keyfactor\_github\_test\_environment\_12\_3\_0\_OAUTH\_CLEAN](#module\_keyfactor\_github\_test\_environment\_12\_3\_0\_OAUTH\_CLEAN) | git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git | main | + +## Resources + +| Name | Type | +|---------------------------------------------------------------------------------------------------------------------------|-------------| +| [github_repository.repo](https://registry.terraform.io/providers/integrations/github/latest/docs/data-sources/repository) | data source | + +## Inputs + +| Name | Description | Type | Default | Required | +|---------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------|----------|-----------------------------------------------------------------------------------------------------------|:--------:| +| [keyfactor\_auth\_token\_url](#input\_keyfactor\_auth\_token\_url) | The token URL to authenticate with the Keyfactor instance using oauth2 client credentials | `string` | `"https://int-oidc-lab.eastus2.cloudapp.azure.com:8444/realms/Keyfactor/protocol/openid-connect/token"` | no | +| [keyfactor\_client\_id](#input\_keyfactor\_client\_id) | The client ID to authenticate with the Keyfactor instance using oauth2 client credentials | `string` | n/a | yes | +| [keyfactor\_client\_secret](#input\_keyfactor\_client\_secret) | The client secret to authenticate with the Keyfactor instance using oauth2 client credentials | `string` | n/a | yes | +| [keyfactor\_hostname\_10\_5\_0](#input\_keyfactor\_hostname\_10\_5\_0) | The hostname of the Keyfactor v10.5.x instance | `string` | `"integrations1050-lab.kfdelivery.com"` | no | +| [keyfactor\_hostname\_10\_5\_0\_CLEAN](#input\_keyfactor\_hostname\_10\_5\_0\_CLEAN) | The hostname of the Keyfactor v10.5.x instance with no stores or orchestrators. This is used for store-type tests. | `string` | `"int1050-test-clean.kfdelivery.com"` | no | +| [keyfactor\_hostname\_11\_5\_0](#input\_keyfactor\_hostname\_11\_5\_0) | The hostname of the Keyfactor v11.5.x instance | `string` | `"integrations1150-lab.kfdelivery.com"` | no | +| [keyfactor\_hostname\_11\_5\_0\_CLEAN](#input\_keyfactor\_hostname\_11\_5\_0\_CLEAN) | The hostname of the Keyfactor v11.5.x instance with no stores or orchestrators. This is used for store-type tests. | `string` | `"int1150-test-clean.kfdelivery.com"` | no | +| [keyfactor\_hostname\_11\_5\_0\_OAUTH](#input\_keyfactor\_hostname\_11\_5\_0\_OAUTH) | The hostname of the Keyfactor instance | `string` | `"int-oidc-lab.eastus2.cloudapp.azure.com"` | no | +| [keyfactor\_hostname\_11\_5\_0\_OAUTH\_CLEAN](#input\_keyfactor\_hostname\_11\_5\_0\_OAUTH\_CLEAN) | The hostname of the Keyfactor instance | `string` | `"int1150-oauth-test-clean.eastus2.cloudapp.azure.com"` | no | +| [keyfactor\_hostname\_12\_3\_0](#input\_keyfactor\_hostname\_12\_3\_0) | The hostname of the Keyfactor v12.3.x instance | `string` | `"integrations1230-lab.kfdelivery.com"` | no | +| [keyfactor\_hostname\_12\_3\_0\_CLEAN](#input\_keyfactor\_hostname\_12\_3\_0\_CLEAN) | The hostname of the Keyfactor v12.3.x instance with no stores or orchestrators. This is used for store-type tests. | `string` | `"int1230-test-clean.kfdelivery.com"` | no | +| [keyfactor\_hostname\_12\_3\_0\_OAUTH](#input\_keyfactor\_hostname\_12\_3\_0\_OAUTH) | The hostname of the Keyfactor instance | `string` | `"int-oidc-lab.eastus2.cloudapp.azure.com"` | no | +| [keyfactor\_password\_AD](#input\_keyfactor\_password\_AD) | The password to authenticate with Keyfactor instance that uses AD authentication | `string` | n/a | yes | +| [keyfactor\_username\_AD](#input\_keyfactor\_username\_AD) | The username to authenticate with a Keyfactor instance that uses AD authentication | `string` | n/a | yes | +| [kfc1230\_client\_id](#input\_kfc1230\_client\_id) | The client ID to authenticate with the Keyfactor instance using oauth2 client credentials | `string` | n/a | yes | +| [kfc1230\_client\_secret](#input\_kfc1230\_client\_secret) | The client secret to authenticate with the Keyfactor instance using oauth2 client credentials | `string` | n/a | yes | +| [kfc1230\_oauth\_hostname](#input\_kfc1230\_oauth\_hostname) | The hostname of the Keyfactor instance | `string` | `"int1230c-oauth.eastus2.cloudapp.azure.com"` | no | +| [kfc1230\_oauth\_token\_url](#input\_kfc1230\_oauth\_token\_url) | The hostname of the Keyfactor instance | `string` | `"https://int1230c-oauth.eastus2.cloudapp.azure.com:8444/realms/Keyfactor/protocol/openid-connect/token"` | no | +| [kfc1230c\_ad\_hostname](#input\_kfc1230c\_ad\_hostname) | The hostname of the Keyfactor instance | `string` | `"int1230c-ad.eastus2.cloudapp.azure.com"` | no | +| [kfc1230c\_client\_id](#input\_kfc1230c\_client\_id) | The client ID to authenticate with the Keyfactor instance using oauth2 client credentials | `string` | n/a | yes | +| [kfc1230c\_client\_secret](#input\_kfc1230c\_client\_secret) | The client secret to authenticate with the Keyfactor instance using oauth2 client credentials | `string` | n/a | yes | +| [kfc1230c\_oauth\_hostname](#input\_kfc1230c\_oauth\_hostname) | The hostname of the Keyfactor instance | `string` | `"int1230c-oauth.eastus2.cloudapp.azure.com"` | no | +| [kfc1230c\_oauth\_token\_url](#input\_kfc1230c\_oauth\_token\_url) | The hostname of the Keyfactor instance | `string` | `"https://int1230c-oauth.eastus2.cloudapp.azure.com:8444/realms/Keyfactor/protocol/openid-connect/token"` | no | + +## Outputs + +No outputs. + \ No newline at end of file diff --git a/.github/config/environments.tf b/.github/config/environments.tf new file mode 100644 index 0000000..fb16940 --- /dev/null +++ b/.github/config/environments.tf @@ -0,0 +1,83 @@ +module "keyfactor_github_test_environment_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_AD + keyfactor_password = var.keyfactor_password_AD + keyfactor_config_file = base64encode(file("${path.module}/command_config.json")) +} + +module "keyfactor_github_test_environment_10_5_0_CLEAN" { + source = "git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git?ref=main" + + gh_environment_name = "KFC_10_5_0_CLEAN" + gh_repo_name = data.github_repository.repo.name + keyfactor_hostname = var.keyfactor_hostname_10_5_0_CLEAN + keyfactor_username = var.keyfactor_username_AD + keyfactor_password = var.keyfactor_password_AD + keyfactor_config_file = base64encode(file("${path.module}/command_config.json")) +} + +module "keyfactor_github_test_environment_11_5_0" { + source = "git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git?ref=main" + + gh_environment_name = "KFC_11_5_0" + gh_repo_name = data.github_repository.repo.name + keyfactor_hostname = var.keyfactor_hostname_11_5_0 + keyfactor_username = var.keyfactor_username_AD + keyfactor_password = var.keyfactor_password_AD + keyfactor_config_file = base64encode(file("${path.module}/command_config.json")) +} + +module "keyfactor_github_test_environment_11_5_0_CLEAN" { + source = "git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git?ref=main" + + gh_environment_name = "KFC_11_5_0_CLEAN" + gh_repo_name = data.github_repository.repo.name + keyfactor_hostname = var.keyfactor_hostname_11_5_0_CLEAN + keyfactor_username = var.keyfactor_username_AD + keyfactor_password = var.keyfactor_password_AD + keyfactor_config_file = base64encode(file("${path.module}/command_config.json")) +} + +module "keyfactor_github_test_environment_11_5_0_OAUTH" { + source = "git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git?ref=main" + + gh_environment_name = "KFC_11_5_0_OAUTH" + gh_repo_name = data.github_repository.repo.name + keyfactor_hostname = var.keyfactor_hostname_11_5_0_OAUTH + keyfactor_auth_token_url = var.keyfactor_auth_token_url + keyfactor_client_id = var.keyfactor_client_id + keyfactor_client_secret = var.keyfactor_client_secret + keyfactor_tls_skip_verify = true + keyfactor_config_file = base64encode(file("${path.module}/command_config.json")) +} + +module "keyfactor_github_test_environment_11_5_0_OAUTH_CLEAN" { + source = "git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git?ref=main" + + gh_environment_name = "KFC_11_5_0_OAUTH_CLEAN" + gh_repo_name = data.github_repository.repo.name + keyfactor_hostname = var.keyfactor_hostname_11_5_0_OAUTH_CLEAN + keyfactor_auth_token_url = var.keyfactor_auth_token_url + keyfactor_client_id = var.keyfactor_client_id + keyfactor_client_secret = var.keyfactor_client_secret + keyfactor_tls_skip_verify = true + keyfactor_config_file = base64encode(file("${path.module}/command_config.json")) +} + +module "keyfactor_github_test_environment_12_3_0_AD" { + source = "git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git?ref=main" + gh_environment_name = "KFC_12_3_0_AD" + gh_repo_name = data.github_repository.repo.name + keyfactor_hostname = var.keyfactor_hostname_12_3_0 + keyfactor_username = var.keyfactor_username_AD + keyfactor_password = var.keyfactor_password_AD + keyfactor_tls_skip_verify = true + keyfactor_config_file = base64encode(file("${path.module}/command_config.json")) +} + + + diff --git a/.github/config/int1230_oauth.tf b/.github/config/int1230_oauth.tf new file mode 100644 index 0000000..3d8ff20 --- /dev/null +++ b/.github/config/int1230_oauth.tf @@ -0,0 +1,35 @@ +variable "kfc1230_oauth_hostname" { + description = "The hostname of the Keyfactor instance" + type = string + default = "int1230-oauth.eastus2.cloudapp.azure.com" +} + +variable "kfc1230_oauth_token_url" { + description = "The hostname of the Keyfactor instance" + type = string + default = "https://int1230-oauth.eastus2.cloudapp.azure.com:8444/realms/Keyfactor/protocol/openid-connect/token" +} + + +variable "kfc1230_client_id" { + description = "The client ID to authenticate with the Keyfactor instance using oauth2 client credentials" + type = string + +} +variable "kfc1230_client_secret" { + description = "The client secret to authenticate with the Keyfactor instance using oauth2 client credentials" + type = string +} + +module "keyfactor_github_test_environment_12_3_0_OAUTH" { + source = "git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git?ref=main" + + gh_environment_name = "KFC_12_3_0_OAUTH" + gh_repo_name = data.github_repository.repo.name + keyfactor_hostname = var.kfc1230_oauth_hostname + keyfactor_auth_token_url = var.kfc1230_oauth_token_url + keyfactor_client_id = var.kfc1230_client_id + keyfactor_client_secret = var.kfc1230_client_secret + keyfactor_tls_skip_verify = true + keyfactor_config_file = base64encode(file("${path.module}/int1230_oauth_command_config.json")) +} \ No newline at end of file diff --git a/.github/config/int1230c_ad.tf b/.github/config/int1230c_ad.tf new file mode 100644 index 0000000..63ca3d1 --- /dev/null +++ b/.github/config/int1230c_ad.tf @@ -0,0 +1,16 @@ +variable "kfc1230c_ad_hostname" { + description = "The hostname of the Keyfactor instance" + type = string + default = "int1230c-ad.eastus2.cloudapp.azure.com" +} + +module "keyfactor_github_test_environment_12_3_0_AD_CLEAN" { + source = "git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git?ref=main" + gh_environment_name = "KFC_12_3_0_AD_CLEAN" + gh_repo_name = data.github_repository.repo.name + keyfactor_hostname = var.kfc1230c_ad_hostname + keyfactor_username = var.keyfactor_username_AD + keyfactor_password = var.keyfactor_password_AD + keyfactor_tls_skip_verify = true + keyfactor_config_file = base64encode(file("${path.module}/command_config.json")) +} \ No newline at end of file diff --git a/.github/config/int1230c_oauth.tf b/.github/config/int1230c_oauth.tf new file mode 100644 index 0000000..b1a34d1 --- /dev/null +++ b/.github/config/int1230c_oauth.tf @@ -0,0 +1,33 @@ +variable "kfc1230c_oauth_hostname" { + description = "The hostname of the Keyfactor instance" + type = string + default = "int1230c-oauth.eastus2.cloudapp.azure.com" +} + +variable "kfc1230c_oauth_token_url" { + description = "The hostname of the Keyfactor instance" + type = string + default = "https://int1230c-oauth.eastus2.cloudapp.azure.com:8444/realms/Keyfactor/protocol/openid-connect/token" +} + + +variable "kfc1230c_client_id" { + description = "The client ID to authenticate with the Keyfactor instance using oauth2 client credentials" + type = string + +} +variable "kfc1230c_client_secret" { + description = "The client secret to authenticate with the Keyfactor instance using oauth2 client credentials" + type = string +} +module "keyfactor_github_test_environment_12_3_0_OAUTH_CLEAN" { + source = "git::ssh://git@github.com/Keyfactor/terraform-module-keyfactor-github-test-environment-ad.git?ref=main" + gh_environment_name = "KFC_12_3_0_OAUTH_CLEAN" + gh_repo_name = data.github_repository.repo.name + keyfactor_hostname = var.kfc1230c_oauth_hostname + keyfactor_auth_token_url = var.kfc1230c_oauth_token_url + keyfactor_client_id = var.kfc1230c_client_id + keyfactor_client_secret = var.kfc1230c_client_secret + keyfactor_tls_skip_verify = true + keyfactor_config_file = base64encode(file("${path.module}/int1230c_oauth_command_config.json")) +} \ No newline at end of file diff --git a/.github/config/providers.tf b/.github/config/providers.tf new file mode 100644 index 0000000..0de8b5c --- /dev/null +++ b/.github/config/providers.tf @@ -0,0 +1,20 @@ +terraform { + required_version = ">= 1.0" + required_providers { + github = { + source = "integrations/github" + version = ">=6.2" + } + } + backend "azurerm" { + resource_group_name = "integrations-infra" + storage_account_name = "integrationstfstate" + container_name = "tfstate" + key = "github/repos/kfutil/environments.tfstate" + } +} + +provider "github" { + # Configuration options + owner = "Keyfactor" +} \ No newline at end of file diff --git a/.github/config/repo.tf b/.github/config/repo.tf new file mode 100644 index 0000000..7ac3974 --- /dev/null +++ b/.github/config/repo.tf @@ -0,0 +1,3 @@ +data "github_repository" "repo" { + name = "kfutil" +} \ No newline at end of file diff --git a/.github/config/variables.tf b/.github/config/variables.tf new file mode 100644 index 0000000..3d557a2 --- /dev/null +++ b/.github/config/variables.tf @@ -0,0 +1,85 @@ +// Hosts +variable "keyfactor_hostname_10_5_0" { + description = "The hostname of the Keyfactor v10.5.x instance" + type = string + default = "integrations1050-lab.kfdelivery.com" +} + +variable "keyfactor_hostname_10_5_0_CLEAN" { + description = "The hostname of the Keyfactor v10.5.x instance with no stores or orchestrators. This is used for store-type tests." + type = string + default = "int1050-test-clean.kfdelivery.com" +} + + +variable "keyfactor_hostname_11_5_0" { + description = "The hostname of the Keyfactor v11.5.x instance" + type = string + default = "integrations1150-lab.kfdelivery.com" +} + +variable "keyfactor_hostname_11_5_0_CLEAN" { + description = "The hostname of the Keyfactor v11.5.x instance with no stores or orchestrators. This is used for store-type tests." + type = string + default = "int1150-test-clean.kfdelivery.com" +} + +variable "keyfactor_hostname_11_5_0_OAUTH" { + description = "The hostname of the Keyfactor instance" + type = string + default = "int-oidc-lab.eastus2.cloudapp.azure.com" +} + +variable "keyfactor_hostname_11_5_0_OAUTH_CLEAN" { + description = "The hostname of the Keyfactor instance" + type = string + default = "int1150-oauth-test-clean.eastus2.cloudapp.azure.com" +} + + +variable "keyfactor_hostname_12_3_0" { + description = "The hostname of the Keyfactor v12.3.x instance" + type = string + default = "integrations1230-lab.kfdelivery.com" +} + +variable "keyfactor_hostname_12_3_0_CLEAN" { + description = "The hostname of the Keyfactor v12.3.x instance with no stores or orchestrators. This is used for store-type tests." + type = string + default = "int1230-test-clean.kfdelivery.com" +} + +variable "keyfactor_hostname_12_3_0_OAUTH" { + description = "The hostname of the Keyfactor instance" + type = string + default = "int-oidc-lab.eastus2.cloudapp.azure.com" +} + + +// Authentication +variable "keyfactor_username_AD" { + description = "The username to authenticate with a Keyfactor instance that uses AD authentication" + type = string +} + +variable "keyfactor_password_AD" { + description = "The password to authenticate with Keyfactor instance that uses AD authentication" + type = string +} + +variable "keyfactor_client_id" { + description = "The client ID to authenticate with the Keyfactor instance using oauth2 client credentials" + type = string +} + +variable "keyfactor_client_secret" { + description = "The client secret to authenticate with the Keyfactor instance using oauth2 client credentials" + type = string +} + +variable "keyfactor_auth_token_url" { + description = "The token URL to authenticate with the Keyfactor instance using oauth2 client credentials" + type = string + default = "https://int-oidc-lab.eastus2.cloudapp.azure.com:8444/realms/Keyfactor/protocol/openid-connect/token" +} + diff --git a/.github/workflows/container.yml b/.github/workflows/container.yml index a245712..3b64e9d 100644 --- a/.github/workflows/container.yml +++ b/.github/workflows/container.yml @@ -1,7 +1,7 @@ name: Build and Release Container on: release: - types: [released] + types: [ released ] push: branches: - '*' @@ -152,12 +152,12 @@ jobs: # Base command to create a manifest list with the selected tag(s) and push CMD="docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ $(printf '${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@sha256:%s ' *)" - + # If the branch is 'release-*', add the 'edge' tag if [[ "${{ github.ref }}" == refs/heads/release-* ]]; then CMD="$CMD -t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:edge" fi - + # Execute the command eval "$CMD" diff --git a/.github/workflows/keyfactor-bootstrap-workflow.yml b/.github/workflows/keyfactor-bootstrap-workflow.yml index a4c7eaf..bd970a9 100644 --- a/.github/workflows/keyfactor-bootstrap-workflow.yml +++ b/.github/workflows/keyfactor-bootstrap-workflow.yml @@ -83,7 +83,7 @@ jobs: hf="True" fi - # The intention is to use this to set the make_latest:false property when + # The intention is to use this to set the make_latest:false property when # dispatching the create-release action, but it is not *yet* a configurable option echo "IS_HOTFIX=$hf" | tee -a "$GITHUB_ENV" else @@ -113,114 +113,33 @@ jobs: echo "IS_HOTFIX=${{ env.IS_HOTFIX }}" | tee -a "$GITHUB_OUTPUT" echo "LATEST_TAG=${{ env.LATEST_TAG }}" | tee -a "$GITHUB_OUTPUT" -# check-package-version: -# needs: get-versions -# if: github.event_name == 'pull_request' && needs.get-versions.outputs.IS_RELEASE_BRANCH == 'True' -# outputs: -# release_version: ${{ steps.create_release.outputs.current_tag }} -# release_url: ${{ steps.create_release.outputs.upload_url }} -# update_version: ${{ steps.check_version.outputs.update_version }} -# next_version: ${{ steps.set-semver-info.outputs.new_version }} -# runs-on: ubuntu-latest -# steps: -# - name: Check out the code -# uses: actions/checkout@v3 -# - run: | -# echo "INC_LEVEL=${{ needs.get-versions.outputs.INC_LEVEL}}" -# - name: Check if initial release -# if: needs.get-versions.outputs.LATEST_TAG == '' -# run: | -# echo "INITIAL_VERSION=${{needs.get-versions.outputs.PR_BASE_TAG}}.0-rc.0" | tee -a "$GITHUB_STEP_SUMMARY" | tee -a "$GITHUB_ENV" -# echo "MANUAL_VERSION=${{needs.get-versions.outputs.PR_BASE_TAG}}.0-rc.0" | tee -a "$GITHUB_ENV" -# - name: Set semver info -# id: set-semver-info -# if: needs.get-versions.outputs.LATEST_TAG != '' -# uses: fiddlermikey/action-bump-semver@main -# with: -# current_version: ${{ needs.get-versions.outputs.LATEST_TAG}} -# level: ${{ needs.get-versions.outputs.INC_LEVEL}} -# preID: rc -# - name: Show next sem-version -# if: needs.get-versions.outputs.LATEST_TAG != '' -# run: | -# echo "MANUAL_VERSION=${{ steps.set-semver-info.outputs.new_version }}" > "$GITHUB_ENV" -# - run: | -# echo "Next version: ${{ env.MANUAL_VERSION }}" | tee -a "$GITHUB_STEP_SUMMARY" -# -# - name: Get Package Version -# id: get-pkg-version -# run: | -# pwd -# ls -la -# echo "CURRENT_PKG_VERSION=$(cat pkg/version/version.go | grep 'const VERSION' | awk '{print $NF}' | tr -d '"')" | tee -a "$GITHUB_ENV" -# - name: Compare package version -# id: check_version -# run: | -# if [ "${{ env.CURRENT_PKG_VERSION }}" != "${{ env.MANUAL_VERSION }}" ]; then -# echo "Updating version in version.go" -# echo "update_version=true" | tee -a $GITHUB_ENV | tee -a $GITHUB_OUTPUT -# echo "update_version=true" | tee -a "$GITHUB_STEP_SUMMARY" -# else -# echo "Versions match, no update needed" -# echo "update_version=false" | tee -a $GITHUB_ENV | tee -a $GITHUB_OUTPUT -# echo "update_version=false" | tee -a $GITHUB_STEP_SUMMARY -# fi -# env: -# UPDATE_VERSION: ${{ steps.check_version.outputs.update_version }} -# -# - name: Set Outputs -# id: set-outputs -# if: needs.get-versions.outputs.LATEST_TAG != '' -# run: | -# echo "UPDATE_VERSION=${{ steps.check_version.outputs.update_version }}" | tee -a "$GITHUB_OUTPUT" -# echo "CURRENT_PKG_VERSION=${{ env.CURRENT_PKG_VERSION }}" | tee -a "$GITHUB_OUTPUT" -# echo "MANUAL_VERSION=${{ env.MANUAL_VERSION }}" | tee -a "$GITHUB_OUTPUT" -# echo "NEW_PKG_VERSION=${{ env.MANUAL_VERSION }}" | tee -a "$GITHUB_OUTPUT" -# -# update-pkg-version: -# needs: -# - check-package-version -# runs-on: ubuntu-latest -# -# steps: -# - name: Checkout repository -# uses: actions/checkout@v3 -# with: -# token: ${{ secrets.V2BUILDTOKEN}} -# - name: No Update -# if: ${{ needs.check-package-version.outputs.update_version != 'true' }} -# run: | -# echo "Versions match, no update needed" -# exit 0 -# -# - name: Commit to PR branch -# id: commit-version -# if: ${{ needs.check-package-version.outputs.update_version == 'true' }} -# env: -# AUTHOR_EMAIL: keyfactor@keyfactor.github.io -# AUTHOR_NAME: Keyfactor Robot -# GITHUB_TOKEN: ${{ secrets.V2BUILDTOKEN}} -# run: | -# git remote -v -# echo "Checking out ${{ github.head_ref }}" -# git fetch -# echo "git checkout -b ${{ github.head_ref }}" -# git checkout -b ${{ github.head_ref }} -# git reset --hard origin/${{ github.head_ref }} -# sed -i "s/const VERSION = .*/const VERSION = \"${{ needs.check-package-version.outputs.next_version }}\"/" pkg/version/version.go -# git add pkg/version/version.go -# git config --global user.email "${{ env.AUTHOR_EMAIL }}" -# git config --global user.name "${{ env.AUTHOR_NAME }}" -# git commit -m "Bump package version to ${{ needs.check-package-version.outputs.next_version }}" -# git push --set-upstream origin ${{ github.head_ref }} -# echo "Version mismatch! Please create a new pull request with the updated version." -# exit 1 - call-starter-workflow: - uses: keyfactor/actions/.github/workflows/starter.yml@v2 + uses: keyfactor/actions/.github/workflows/starter.yml@v3 needs: get-versions secrets: token: ${{ secrets.V2BUILDTOKEN}} APPROVE_README_PUSH: ${{ secrets.APPROVE_README_PUSH}} gpg_key: ${{ secrets.KF_GPG_PRIVATE_KEY }} - gpg_pass: ${{ secrets.KF_GPG_PASSPHRASE }} \ No newline at end of file + gpg_pass: ${{ secrets.KF_GPG_PASSPHRASE }} + scan_token: ${{ secrets.SAST_TOKEN }} + + # Tester Install Script + Test_Install_Script: + runs-on: kfutil-runner-set + needs: + - get-versions + # - call-starter-workflow + steps: + - name: Test Quick Install Script + env: + VERSION: ${{ needs.get-versions.outputs.NEXT_VERSION }} + run: | + echo "Testing Install Script for version: $VERSION" + sudo apt update && sudo apt upgrade -y && sudo apt install -y curl wget unzip jq openssl && sudo apt clean + echo curl -s "https://raw.githubusercontent.com/Keyfactor/kfutil/${GITHUB_REF_NAME}/install.sh" + GITHUB_REF_NAME_ENCODED=$(echo -n "${GITHUB_REF_NAME}" | jq -sRr @uri) + VERIFY_CHECKSUM=0 + bash <(curl -s "https://raw.githubusercontent.com/Keyfactor/kfutil/${GITHUB_REF_NAME_ENCODED}/install.sh") + which kfutil + kfutil version + rm $(which kfutil) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 93ed013..54f2ca1 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,38 +1,43 @@ name: go tests on: -# workflow_dispatch: -# workflow_run: -# workflows: -# - "Check and Update Package Version" -# types: -# - completed -# branches: -# - "*" + # workflow_dispatch: + # workflow_run: + # workflows: + # - "Check and Update Package Version" + # types: + # - completed + # branches: + # - "*" push: branches: - '*' jobs: build: - runs-on: ubuntu-latest + runs-on: kfutil-runner-set steps: - name: Checkout code uses: actions/checkout@v4 - name: Set up Go uses: actions/setup-go@v5 with: - go-version: "1.21" + go-version: "1.23" + - name: Set up private repo access for go get + run: | + git config --global url."https://$GITHUB_TOKEN:x-oauth-basic@github.com/".insteadOf "https://github.com/" + env: + GITHUB_TOKEN: ${{ secrets.V2BUILDTOKEN}} + - name: Install dependencies run: go mod download && go mod tidy - name: Install Azure CLI run: | curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash az --version - # 10.x.x kf_10_x_x: - runs-on: ubuntu-latest + runs-on: kfutil-runner-set needs: - build steps: @@ -43,409 +48,623 @@ jobs: ### Store Type Tests Test_StoreTypes_KFC_10_5_0: - runs-on: ubuntu-latest + runs-on: kfutil-runner-set needs: - build - kf_10_x_x + environment: "KFC_10_5_0_CLEAN" env: - SECRET_NAME: "command-config-1050-clean" - KEYFACTOR_HOSTNAME: "int1050-test-clean.kfdelivery.com" - KEYFACTOR_DOMAIN: "command" - KEYFACTOR_USERNAME: ${{ secrets.LAB_USERNAME }} - KEYFACTOR_PASSWORD: ${{ secrets.LAB_PASSWORD }} + GITHUB_TOKEN: ${{ secrets.V2BUILDTOKEN}} + KEYFACTOR_PASSWORD: ${{ secrets.KEYFACTOR_PASSWORD }} + KEYFACTOR_USERNAME: ${{ secrets.KEYFACTOR_USERNAME }} + KEYFACTOR_AUTH_CONFIG_B64: ${{ secrets.KEYFACTOR_AUTH_CONFIG_B64 }} + KEYFACTOR_HOSTNAME: ${{ vars.KEYFACTOR_HOSTNAME }} + KEYFACTOR_AUTH_HOSTNAME: ${{ vars.KEYFACTOR_AUTH_HOSTNAME }} + KEYFACTOR_SKIP_VERIFY: ${{ vars.KEYFACTOR_SKIP_VERIFY }} + steps: - - name: Checkout code + - name: Check out code uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: 1.23 + + - name: Get Public IP + run: curl -s https://api.ipify.org + + - name: Set up private repo access for go get + run: | + git config --global url."https://$GITHUB_TOKEN:x-oauth-basic@github.com/".insteadOf "https://github.com/" + - name: Run tests run: | - export KFUTIL_DEBUG=1 - go test -v ./cmd -run "^Test_StoreTypes*" + unset KFUTIL_DEBUG + go test -timeout 20m -v ./cmd -run "^Test_StoreTypes*" ### Store Tests Test_Stores_KFC_10_5_0: - runs-on: ubuntu-latest + runs-on: kfutil-runner-set needs: - build - kf_10_x_x - - Test_StoreTypes_KFC_10_5_0 + # - Test_StoreTypes_KFC_10_5_0 + environment: "KFC_10_5_0" env: - SECRET_NAME: "command-config-1050" - KEYFACTOR_HOSTNAME: "integrations1050-lab.kfdelivery.com" - KEYFACTOR_DOMAIN: "command" - KEYFACTOR_USERNAME: ${{ secrets.LAB_USERNAME }} - KEYFACTOR_PASSWORD: ${{ secrets.LAB_PASSWORD }} + GITHUB_TOKEN: ${{ secrets.V2BUILDTOKEN}} + KEYFACTOR_PASSWORD: ${{ secrets.KEYFACTOR_PASSWORD }} + KEYFACTOR_USERNAME: ${{ secrets.KEYFACTOR_USERNAME }} + KEYFACTOR_AUTH_CONFIG_B64: ${{ secrets.KEYFACTOR_AUTH_CONFIG_B64 }} + KEYFACTOR_HOSTNAME: ${{ vars.KEYFACTOR_HOSTNAME }} + KEYFACTOR_AUTH_HOSTNAME: ${{ vars.KEYFACTOR_AUTH_HOSTNAME }} + KEYFACTOR_SKIP_VERIFY: ${{ vars.KEYFACTOR_SKIP_VERIFY }} steps: - - name: Checkout code + - name: Check out code uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: 1.23 + + - name: Get Public IP + run: curl -s https://api.ipify.org + + - name: Set up private repo access for go get + run: | + git config --global url."https://$GITHUB_TOKEN:x-oauth-basic@github.com/".insteadOf "https://github.com/" + - name: Run tests - run: go test -v ./cmd -run "^Test_Stores_*" + run: go test -timeout 20m -v ./cmd -run "^Test_Stores_*" ### PAM Tests Test_PAM_KFC_10_5_0: - runs-on: ubuntu-latest + runs-on: kfutil-runner-set needs: - build - kf_10_x_x - - Test_StoreTypes_KFC_10_5_0 + # - Test_StoreTypes_KFC_10_5_0 + environment: "KFC_10_5_0" env: - SECRET_NAME: "command-config-1050" - KEYFACTOR_HOSTNAME: "integrations1050-lab.kfdelivery.com" - KEYFACTOR_DOMAIN: "command" - KEYFACTOR_USERNAME: ${{ secrets.LAB_USERNAME }} - KEYFACTOR_PASSWORD: ${{ secrets.LAB_PASSWORD }} + GITHUB_TOKEN: ${{ secrets.V2BUILDTOKEN}} + KEYFACTOR_PASSWORD: ${{ secrets.KEYFACTOR_PASSWORD }} + KEYFACTOR_USERNAME: ${{ secrets.KEYFACTOR_USERNAME }} + KEYFACTOR_AUTH_CONFIG_B64: ${{ secrets.KEYFACTOR_AUTH_CONFIG_B64 }} + KEYFACTOR_HOSTNAME: ${{ vars.KEYFACTOR_HOSTNAME }} + KEYFACTOR_AUTH_HOSTNAME: ${{ vars.KEYFACTOR_AUTH_HOSTNAME }} + KEYFACTOR_SKIP_VERIFY: ${{ vars.KEYFACTOR_SKIP_VERIFY }} steps: - - name: Checkout code + - name: Check out code uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: 1.23 + + - name: Get Public IP + run: curl -s https://api.ipify.org + + - name: Set up private repo access for go get + run: | + git config --global url."https://$GITHUB_TOKEN:x-oauth-basic@github.com/".insteadOf "https://github.com/" + + + - name: Display working directory + run: | + pwd + ls -ltr + ls -ltr ./artifacts/pam + - name: Run tests run: | unset KFUTIL_DEBUG - go test -v ./cmd -run "^Test_PAM*" - + go test -timeout 20m -v ./cmd -run "^Test_PAM*" ### PAM Tests AKV Auth Provider Test_AKV_PAM_KFC_10_5_0: runs-on: self-hosted needs: - Test_PAM_KFC_10_5_0 + environment: "KFC_10_5_0" env: SECRET_NAME: "command-config-1050-az" + GITHUB_TOKEN: ${{ secrets.V2BUILDTOKEN}} steps: - - name: Checkout code + - name: Check out code uses: actions/checkout@v4 + - name: Set up Go uses: actions/setup-go@v5 with: - go-version: "1.21" + go-version: 1.23 + + - name: Get Public IP + run: curl -s https://api.ipify.org + + - name: Set up private repo access for go get + run: | + git config --global url."https://$GITHUB_TOKEN:x-oauth-basic@github.com/".insteadOf "https://github.com/" + + - name: Install dependencies run: go mod download && go mod tidy + - name: Get secret from Azure Key Vault run: | - . ./examples/auth/akv/akv_auth.sh + . ./examples/auth/akv/akv_auth_v2.sh cat $HOME/.keyfactor/command_config.json + - name: Install kfutil run: | + echo "Installing kfutil on self-hosted runner" make install - - name: Run tests - run: | - go test -v ./cmd -run "^Test_PAM*" - - ## KFC 11.x.x - kf_11_x_x: - runs-on: ubuntu-latest - needs: - - build - steps: - - name: Checkout code - uses: actions/checkout@v4 - name: Run tests - run: echo "Running tests for KF 11.x.x" + run: | + go test -timeout 20m -v ./cmd -run "^Test_PAM*" + + + # ## KFC 11.x.x + # kf_11_x_x: + # runs-on: kfutil-runner-set + # needs: + # - build + # steps: + # - name: Checkout code + # uses: actions/checkout@v4 + # - name: Run tests + # run: echo "Running tests for KF 11.x.x" + # + # ### Store Type Tests + # Test_StoreTypes_KFC_11_1_2: + # runs-on: kfutil-runner-set + # needs: + # - build + # - kf_11_x_x + # env: + # SECRET_NAME: "command-config-1112-clean" + # KEYFACTOR_HOSTNAME: "int1112-test-clean.kfdelivery.com" + # KEYFACTOR_DOMAIN: "command" + # KEYFACTOR_USERNAME: ${{ secrets.LAB_USERNAME }} + # KEYFACTOR_PASSWORD: ${{ secrets.LAB_PASSWORD }} + # GITHUB_TOKEN: ${{ secrets.V2BUILDTOKEN}} + # steps: + # - name: Checkout code + # uses: actions/checkout@v4 + # - name: Run tests + # run: | + # unset KFUTIL_DEBUG + # go test -timeout 20m -v ./cmd -run "^Test_StoreTypes*" + # + # + # ### Store Tests + # Test_Stores_KFC_11_1_2: + # runs-on: kfutil-runner-set + # needs: + # - build + # - kf_11_x_x + # - Test_StoreTypes_KFC_11_1_2 + # env: + # SECRET_NAME: "command-config-1112" + # KEYFACTOR_HOSTNAME: "integrations1112-lab.kfdelivery.com" + # KEYFACTOR_DOMAIN: "command" + # KEYFACTOR_USERNAME: ${{ secrets.LAB_USERNAME }} + # KEYFACTOR_PASSWORD: ${{ secrets.LAB_PASSWORD }} + # GITHUB_TOKEN: ${{ secrets.V2BUILDTOKEN}} + # steps: + # - name: Checkout code + # uses: actions/checkout@v4 + # - name: Set up private repo access for go get + # run: | + # git config --global url."https://$GITHUB_TOKEN:x-oauth-basic@github.com/".insteadOf "https://github.com/" + # - name: Run tests + # run: go test -timeout 20m -v ./cmd -run "^Test_Stores_*" + # + # ### PAM Tests + # Test_PAM_KFC_11_1_2: + # runs-on: kfutil-runner-set + # needs: + # - build + # - kf_11_x_x + # - Test_StoreTypes_KFC_11_1_2 + # env: + # SECRET_NAME: "command-config-1112" + # KEYFACTOR_HOSTNAME: "integrations1112-lab.kfdelivery.com" + # KEYFACTOR_DOMAIN: "command" + # KEYFACTOR_USERNAME: ${{ secrets.LAB_USERNAME }} + # KEYFACTOR_PASSWORD: ${{ secrets.LAB_PASSWORD }} + # GITHUB_TOKEN: ${{ secrets.V2BUILDTOKEN}} + # steps: + # - name: Checkout code + # uses: actions/checkout@v4 + # - name: Set up private repo access for go get + # run: | + # git config --global url."https://$GITHUB_TOKEN:x-oauth-basic@github.com/".insteadOf "https://github.com/" + # - name: Run tests + # run: | + # unset KFUTIL_DEBUG + # go test -timeout 20m -v ./cmd -run "^Test_PAM*" + # + # + # ### PAM Tests AKV Auth Provider + # Test_AKV_PAM_KFC_11_1_2: + # runs-on: self-hosted + # needs: + # - Test_PAM_KFC_11_1_2 + # env: + # SECRET_NAME: "command-config-1112-az" + # steps: + # - name: Checkout code + # uses: actions/checkout@v4 + # - name: Set up Go + # uses: actions/setup-go@v5 + # with: + # go-version: "1.21" + # - name: Set up private repo access for go get + # run: | + # git config --global url."https://$GITHUB_TOKEN:x-oauth-basic@github.com/".insteadOf "https://github.com/" + # - name: Install dependencies + # run: go mod download && go mod tidy + # - name: Get secret from Azure Key Vault + # run: | + # . ./examples/auth/akv/akv_auth.sh + # cat $HOME/.keyfactor/command_config.json + # - name: Install kfutil + # run: | + # make install + # - name: Run tests + # run: | + # go test -timeout 20m -v ./cmd -run "^Test_PAM*" ## KFC 12.x.x kf_12_x_x: - runs-on: ubuntu-latest + runs-on: kfutil-runner-set needs: - build steps: - - name: Checkout code + - name: Check out code uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: 1.23 + + - name: Get Public IP + run: curl -s https://api.ipify.org + + - name: Set up private repo access for go get + run: | + git config --global url."https://$GITHUB_TOKEN:x-oauth-basic@github.com/".insteadOf "https://github.com/" + - name: Run tests run: echo "Running tests for KF 12.x.x" ### Store Type Tests - Test_StoreTypes_KFC_12_0_0: - runs-on: ubuntu-latest + # Test_StoreTypes_KFC_12_3_0: + # runs-on: kfutil-runner-set + # needs: + # - build + # - kf_12_x_x + # environment: "KFC_12_3_0_CLEAN" + # env: + # GITHUB_TOKEN: ${{ secrets.V2BUILDTOKEN}} + # KEYFACTOR_PASSWORD: ${{ secrets.KEYFACTOR_PASSWORD }} + # KEYFACTOR_USERNAME: ${{ secrets.KEYFACTOR_USERNAME }} + # KEYFACTOR_AUTH_CONFIG_B64: ${{ secrets.KEYFACTOR_AUTH_CONFIG_B64 }} + # KEYFACTOR_HOSTNAME: ${{ vars.KEYFACTOR_HOSTNAME }} + # KEYFACTOR_SKIP_VERIFY: ${{ vars.KEYFACTOR_SKIP_VERIFY }} + # steps: + # - name: Check out code + # uses: actions/checkout@v4 + # + # - name: Set up Go + # uses: actions/setup-go@v5 + # with: + # go-version: 1.23 + # + # - name: Get Public IP + # run: curl -s https://api.ipify.org + # + # - name: Set up private repo access for go get + # run: | + # git config --global url."https://$GITHUB_TOKEN:x-oauth-basic@github.com/".insteadOf "https://github.com/" + # + # - name: Run tests + # run: | + # unset KFUTIL_DEBUG + # go test -timeout 20m -v ./cmd -run "^Test_StoreTypes*" + + Test_StoreTypes_KFC_12_3_0_OAUTH: + runs-on: kfutil-runner-set needs: - build - - kf_11_x_x + - kf_12_x_x + environment: "KFC_12_3_0_OAUTH_CLEAN" env: - SECRET_NAME: "command-config-1200-clean" - KEYFACTOR_HOSTNAME: "int1200-test-clean.kfdelivery.com" - KEYFACTOR_DOMAIN: "command" - KEYFACTOR_USERNAME: ${{ secrets.LAB_USERNAME }} - KEYFACTOR_PASSWORD: ${{ secrets.LAB_PASSWORD }} + GITHUB_TOKEN: ${{ secrets.V2BUILDTOKEN}} + KEYFACTOR_AUTH_CONFIG_B64: ${{ secrets.KEYFACTOR_AUTH_CONFIG_B64 }} + KEYFACTOR_AUTH_CLIENT_ID: ${{ secrets.KEYFACTOR_AUTH_CLIENT_ID }} + KEYFACTOR_AUTH_CLIENT_SECRET: ${{ secrets.KEYFACTOR_AUTH_CLIENT_SECRET }} + KEYFACTOR_AUTH_TOKEN_URL: ${{ vars.KEYFACTOR_AUTH_TOKEN_URL }} + KEYFACTOR_HOSTNAME: ${{ vars.KEYFACTOR_HOSTNAME }} + KEYFACTOR_AUTH_HOSTNAME: ${{ vars.KEYFACTOR_AUTH_HOSTNAME }} + KEYFACTOR_SKIP_VERIFY: ${{ vars.KEYFACTOR_SKIP_VERIFY }} steps: - - name: Checkout code + - name: Check out code uses: actions/checkout@v4 - - name: Run tests - run: | - unset KFUTIL_DEBUG - go test -v ./cmd -run "^Test_StoreTypes*" - Test_StoreTypes_KFC_11_2_0: - runs-on: ubuntu-latest - needs: - - build - - kf_11_x_x - env: - SECRET_NAME: "command-config-1120-clean" - KEYFACTOR_HOSTNAME: "int1120-test-clean.kfdelivery.com" - KEYFACTOR_DOMAIN: "command" - KEYFACTOR_USERNAME: ${{ secrets.LAB_USERNAME }} - KEYFACTOR_PASSWORD: ${{ secrets.LAB_PASSWORD }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - - name: Run tests + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: 1.23 + + - name: Get Public IP + run: curl -s https://api.ipify.org + + - name: Set up private repo access for go get run: | - unset KFUTIL_DEBUG - go test -v ./cmd -run "^Test_StoreTypes*" + git config --global url."https://$GITHUB_TOKEN:x-oauth-basic@github.com/".insteadOf "https://github.com/" - Test_StoreTypes_KFC_11_1_2: - runs-on: ubuntu-latest - needs: - - build - - kf_11_x_x - env: - SECRET_NAME: "command-config-1112-clean" - KEYFACTOR_HOSTNAME: "int1112-test-clean.kfdelivery.com" - KEYFACTOR_DOMAIN: "command" - KEYFACTOR_USERNAME: ${{ secrets.LAB_USERNAME }} - KEYFACTOR_PASSWORD: ${{ secrets.LAB_PASSWORD }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - name: Run tests run: | unset KFUTIL_DEBUG - go test -v ./cmd -run "^Test_StoreTypes*" - + go test -timeout 20m -v ./cmd -run "^Test_StoreTypes*" ### Store Tests - Test_Stores_KFC_12_0_0: - runs-on: ubuntu-latest + # Test_Stores_KFC_12_3_0: + # runs-on: kfutil-runner-set + # needs: + # - build + # - kf_12_x_x + # - Test_StoreTypes_KFC_12_3_0 + # environment: "KFC_12_3_0" + # env: + # GITHUB_TOKEN: ${{ secrets.V2BUILDTOKEN}} + # KEYFACTOR_PASSWORD: ${{ secrets.KEYFACTOR_PASSWORD }} + # KEYFACTOR_USERNAME: ${{ secrets.KEYFACTOR_USERNAME }} + # KEYFACTOR_AUTH_CONFIG_B64: ${{ secrets.KEYFACTOR_AUTH_CONFIG_B64 }} + # KEYFACTOR_HOSTNAME: ${{ vars.KEYFACTOR_HOSTNAME }} + # KEYFACTOR_SKIP_VERIFY: ${{ vars.KEYFACTOR_SKIP_VERIFY }} + # steps: + # - name: Check out code + # uses: actions/checkout@v4 + # + # - name: Set up Go + # uses: actions/setup-go@v5 + # with: + # go-version: 1.23 + # + # - name: Get Public IP + # run: curl -s https://api.ipify.org + # + # - name: Set up private repo access for go get + # run: | + # git config --global url."https://$GITHUB_TOKEN:x-oauth-basic@github.com/".insteadOf "https://github.com/" + # + # - name: Run tests + # run: go test -timeout 20m -v ./cmd -run "^Test_Stores_*" + Test_Stores_KFC_12_3_0_OAUTH: + runs-on: kfutil-runner-set needs: - build - kf_12_x_x - - Test_StoreTypes_KFC_12_0_0 - env: - SECRET_NAME: "command-config-1200" - KEYFACTOR_HOSTNAME: "integrations1200-lab.kfdelivery.com" - KEYFACTOR_DOMAIN: "command" - KEYFACTOR_USERNAME: ${{ secrets.LAB_USERNAME }} - KEYFACTOR_PASSWORD: ${{ secrets.LAB_PASSWORD }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - - name: Run tests - run: go test -v ./cmd -run "^Test_Stores_*" - Test_Stores_KFC_11_2_0: - runs-on: ubuntu-latest - needs: - - build - - kf_11_x_x - - Test_StoreTypes_KFC_11_2_0 - env: - SECRET_NAME: "command-config-1120" - KEYFACTOR_HOSTNAME: "integrations1120-lab.kfdelivery.com" - KEYFACTOR_DOMAIN: "command" - KEYFACTOR_USERNAME: ${{ secrets.LAB_USERNAME }} - KEYFACTOR_PASSWORD: ${{ secrets.LAB_PASSWORD }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - - name: Run tests - run: go test -v ./cmd -run "^Test_Stores_*" - Test_Stores_KFC_11_1_2: - runs-on: ubuntu-latest - needs: - - build - - kf_11_x_x - - Test_StoreTypes_KFC_11_1_2 + # - Test_StoreTypes_KFC_12_3_0_OAUTH + environment: "KFC_12_3_0_OAUTH" env: - SECRET_NAME: "command-config-1112" - KEYFACTOR_HOSTNAME: "integrations1112-lab.kfdelivery.com" - KEYFACTOR_DOMAIN: "command" - KEYFACTOR_USERNAME: ${{ secrets.LAB_USERNAME }} - KEYFACTOR_PASSWORD: ${{ secrets.LAB_PASSWORD }} + GITHUB_TOKEN: ${{ secrets.V2BUILDTOKEN}} + KEYFACTOR_AUTH_CONFIG_B64: ${{ secrets.KEYFACTOR_AUTH_CONFIG_B64 }} + KEYFACTOR_AUTH_CLIENT_ID: ${{ secrets.KEYFACTOR_AUTH_CLIENT_ID }} + KEYFACTOR_AUTH_CLIENT_SECRET: ${{ secrets.KEYFACTOR_AUTH_CLIENT_SECRET }} + KEYFACTOR_AUTH_TOKEN_URL: ${{ vars.KEYFACTOR_AUTH_TOKEN_URL }} + KEYFACTOR_HOSTNAME: ${{ vars.KEYFACTOR_HOSTNAME }} + KEYFACTOR_AUTH_HOSTNAME: ${{ vars.KEYFACTOR_AUTH_HOSTNAME }} + KEYFACTOR_SKIP_VERIFY: ${{ vars.KEYFACTOR_SKIP_VERIFY }} steps: - - name: Checkout code + - name: Check out code uses: actions/checkout@v4 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: 1.23 + + - name: Get Public IP + run: curl -s https://api.ipify.org + + - name: Set up private repo access for go get + run: | + git config --global url."https://$GITHUB_TOKEN:x-oauth-basic@github.com/".insteadOf "https://github.com/" + - name: Run tests - run: go test -v ./cmd -run "^Test_Stores_*" + run: go test -timeout 20m -v ./cmd -run "^Test_Stores_*" ### PAM Tests - Test_PAM_KFC_12_0_0: - runs-on: ubuntu-latest + # Test_PAM_KFC_12_3_0: + # runs-on: kfutil-runner-set + # needs: + # - build + # - kf_12_x_x + # - Test_StoreTypes_KFC_12_3_0 + # environment: "KFC_12_3_0" + # env: + # GITHUB_TOKEN: ${{ secrets.V2BUILDTOKEN}} + # KEYFACTOR_PASSWORD: ${{ secrets.KEYFACTOR_PASSWORD }} + # KEYFACTOR_USERNAME: ${{ secrets.KEYFACTOR_USERNAME }} + # KEYFACTOR_AUTH_CONFIG_B64: ${{ secrets.KEYFACTOR_AUTH_CONFIG_B64 }} + # KEYFACTOR_HOSTNAME: ${{ vars.KEYFACTOR_HOSTNAME }} + # KEYFACTOR_SKIP_VERIFY: ${{ vars.KEYFACTOR_SKIP_VERIFY }} + # steps: + # - name: Check out code + # uses: actions/checkout@v4 + # + # - name: Set up Go + # uses: actions/setup-go@v5 + # with: + # go-version: 1.23 + # + # - name: Get Public IP + # run: curl -s https://api.ipify.org + # + # - name: Set up private repo access for go get + # run: | + # git config --global url."https://$GITHUB_TOKEN:x-oauth-basic@github.com/".insteadOf "https://github.com/" + # + # - name: Run tests + # run: | + # unset KFUTIL_DEBUG + # go test -timeout 20m -v ./cmd -run "^Test_PAM*" + + Test_PAM_KFC_12_3_0_OAUTH: + runs-on: kfutil-runner-set needs: - build - kf_12_x_x - - Test_StoreTypes_KFC_12_0_0 + # - Test_StoreTypes_KFC_12_3_0_OAUTH + environment: "KFC_12_3_0_OAUTH" env: - SECRET_NAME: "command-config-1200" - KEYFACTOR_HOSTNAME: "integrations1200-lab.kfdelivery.com" - KEYFACTOR_DOMAIN: "command" - KEYFACTOR_USERNAME: ${{ secrets.LAB_USERNAME }} - KEYFACTOR_PASSWORD: ${{ secrets.LAB_PASSWORD }} + GITHUB_TOKEN: ${{ secrets.V2BUILDTOKEN}} + KEYFACTOR_AUTH_CONFIG_B64: ${{ secrets.KEYFACTOR_AUTH_CONFIG_B64 }} + KEYFACTOR_AUTH_CLIENT_ID: ${{ secrets.KEYFACTOR_AUTH_CLIENT_ID }} + KEYFACTOR_AUTH_CLIENT_SECRET: ${{ secrets.KEYFACTOR_AUTH_CLIENT_SECRET }} + KEYFACTOR_AUTH_TOKEN_URL: ${{ vars.KEYFACTOR_AUTH_TOKEN_URL }} + KEYFACTOR_HOSTNAME: ${{ vars.KEYFACTOR_HOSTNAME }} + KEYFACTOR_AUTH_HOSTNAME: ${{ vars.KEYFACTOR_AUTH_HOSTNAME }} + KEYFACTOR_SKIP_VERIFY: ${{ vars.KEYFACTOR_SKIP_VERIFY }} steps: - - name: Checkout code + - name: Check out code uses: actions/checkout@v4 - - name: Run tests - run: | - unset KFUTIL_DEBUG - go test -v ./cmd -run "^Test_PAM*" + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: 1.23 + + - name: Get Public IP + run: curl -s https://api.ipify.org - Test_PAM_KFC_11_2_0: - runs-on: ubuntu-latest - needs: - - build - - kf_11_x_x - - Test_StoreTypes_KFC_11_2_0 - env: - SECRET_NAME: "command-config-1120" - KEYFACTOR_HOSTNAME: "integrations1120-lab.kfdelivery.com" - KEYFACTOR_DOMAIN: "command" - KEYFACTOR_USERNAME: ${{ secrets.LAB_USERNAME }} - KEYFACTOR_PASSWORD: ${{ secrets.LAB_PASSWORD }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - - name: Run tests + - name: Set up private repo access for go get run: | - unset KFUTIL_DEBUG - go test -v ./cmd -run "^Test_PAM*" + git config --global url."https://$GITHUB_TOKEN:x-oauth-basic@github.com/".insteadOf "https://github.com/" + - name: Display working directory + run: | + pwd + ls -ltr + ls -ltr ./artifacts/pam - Test_PAM_KFC_11_1_2: - runs-on: ubuntu-latest - needs: - - build - - kf_11_x_x - - Test_StoreTypes_KFC_11_1_2 - env: - SECRET_NAME: "command-config-1112" - KEYFACTOR_HOSTNAME: "integrations1112-lab.kfdelivery.com" - KEYFACTOR_DOMAIN: "command" - KEYFACTOR_USERNAME: ${{ secrets.LAB_USERNAME }} - KEYFACTOR_PASSWORD: ${{ secrets.LAB_PASSWORD }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - name: Run tests run: | unset KFUTIL_DEBUG - go test -v ./cmd -run "^Test_PAM*" + go test -timeout 20m -v ./cmd -run "^Test_PAM*" ### PAM Tests AKV Auth Provider - Test_AKV_PAM_KFC_12_0_0: + # Test_AKV_PAM_KFC_12_3_0: + # runs-on: self-hosted + # needs: + # - Test_PAM_KFC_12_3_0 + # environment: "KFC_12_3_0" + # env: + # SECRET_NAME: "command-config-1230-az" + # steps: + # - name: Check out code + # uses: actions/checkout@v4 + # + # - name: Set up Go + # uses: actions/setup-go@v5 + # with: + # go-version: 1.23 + # + # - name: Get Public IP + # run: curl -s https://api.ipify.org + # + # - name: Set up private repo access for go get + # run: | + # git config --global url."https://$GITHUB_TOKEN:x-oauth-basic@github.com/".insteadOf "https://github.com/" + # + # - name: Install dependencies + # run: go mod download && go mod tidy + # + # - name: Get secret from Azure Key Vault + # run: | + # . ./examples/auth/akv/akv_auth.sh + # cat $HOME/.keyfactor/command_config.json + # + # - name: Install kfutil + # run: | + # make install + # - name: Run tests + # run: | + # go test -timeout 20m -v ./cmd -run "^Test_PAM*" + + Test_AKV_PAM_KFC_12_3_0_OAUTH: runs-on: self-hosted needs: - - Test_PAM_KFC_12_0_0 + - Test_PAM_KFC_12_3_0_OAUTH + environment: "KFC_12_3_0_OAUTH" env: - SECRET_NAME: "command-config-1200-az" + SECRET_NAME: "command-config-1230-oauth-az" steps: - - name: Checkout code + - name: Check out code uses: actions/checkout@v4 + - name: Set up Go uses: actions/setup-go@v5 with: - go-version: "1.21" - - name: Install dependencies - run: go mod download && go mod tidy - - name: Get secret from Azure Key Vault - run: | - . ./examples/auth/akv/akv_auth.sh - cat $HOME/.keyfactor/command_config.json - - name: Install kfutil - run: | - make install - - name: Run tests - run: | - go test -v ./cmd -run "^Test_PAM*" + go-version: 1.23 + - name: Get Public IP + run: curl -s https://api.ipify.org - Test_AKV_PAM_KFC_11_2_0: - runs-on: self-hosted - needs: - - Test_PAM_KFC_11_2_0 - env: - SECRET_NAME: "command-config-1120-az" - steps: - - name: Checkout code - uses: actions/checkout@v4 - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version: "1.21" - - name: Install dependencies - run: go mod download && go mod tidy - - name: Get secret from Azure Key Vault - run: | - . ./examples/auth/akv/akv_auth.sh - cat $HOME/.keyfactor/command_config.json - - name: Install kfutil - run: | - make install - - name: Run tests + - name: Set up private repo access for go get run: | - go test -v ./cmd -run "^Test_PAM*" + git config --global url."https://$GITHUB_TOKEN:x-oauth-basic@github.com/".insteadOf "https://github.com/" - - Test_AKV_PAM_KFC_11_1_2: - runs-on: self-hosted - needs: - - Test_PAM_KFC_11_1_2 - env: - SECRET_NAME: "command-config-1112-az" - steps: - - name: Checkout code - uses: actions/checkout@v4 - - name: Set up Go - uses: actions/setup-go@v5 - with: - go-version: "1.21" - name: Install dependencies run: go mod download && go mod tidy + - name: Get secret from Azure Key Vault run: | . ./examples/auth/akv/akv_auth.sh cat $HOME/.keyfactor/command_config.json + - name: Install kfutil run: | make install + - name: Run tests run: | - go test -v ./cmd -run "^Test_PAM*" - - - # Tester Install Script - Test_Install_Script: - runs-on: ubuntu-latest - steps: - - name: Test Quick Install Script - run: | - bash <(curl -s https://raw.githubusercontent.com/Keyfactor/kfutil/${GITHUB_REF_NAME}/install.sh) - which kfutil - kfutil version - rm $(which kfutil) + go test -timeout 20m -v ./cmd -run "^Test_PAM*" # Package Tests Test_Kfutil_pkg: - runs-on: ubuntu-latest + runs-on: kfutil-runner-set needs: - build env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - # Checkout code - # https://github.com/actions/checkout - - name: Checkout code - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 + - name: Check out code + uses: actions/checkout@v4 - # Setup GoLang build environment - # https://github.com/actions/setup-go - - name: Set up Go 1.x - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 + - name: Set up Go + uses: actions/setup-go@v5 with: - go-version-file: 'go.mod' - cache: true + go-version: 1.23 + + - name: Get Public IP + run: curl -s https://api.ipify.org + + - name: Set up private repo access for go get + run: | + git config --global url."https://$GITHUB_TOKEN:x-oauth-basic@github.com/".insteadOf "https://github.com/" - # Install dependencies - name: Install dependencies - run: go mod download + run: go mod download && go mod tidy # Run the tests with coverage found in the pkg directory - name: Run tests - run: go test -v -cover ./pkg/... + run: go test -timeout 20m -v -cover ./pkg/... diff --git a/.github/workflows/update-stores.yml b/.github/workflows/update-stores.yml index 3d6bd4a..0e42dfc 100644 --- a/.github/workflows/update-stores.yml +++ b/.github/workflows/update-stores.yml @@ -61,7 +61,7 @@ jobs: run: | echo "PR_BRANCH=${{steps.check-branch.outputs.PR_BRANCH}}" | tee -a $GITHUB_ENV -# If the branch with an open PR already exists, first check out that branch from kfutil + # If the branch with an open PR already exists, first check out that branch from kfutil - name: Check out existing repo merge branch if: env.PR_BRANCH == 'commit' uses: actions/checkout@v4 @@ -71,9 +71,9 @@ jobs: .github path: './merge-folder/' token: ${{ secrets.V2BUILDTOKEN }} - ref: '${{env.KFUTIL_ARG}}' + ref: '${{env.KFUTIL_ARG}}' -# If the branch does not exist, first check out the main branch from kfutil. + # If the branch does not exist, first check out the main branch from kfutil. - name: Check out main if: env.PR_BRANCH == 'create' uses: actions/checkout@v4 @@ -84,13 +84,13 @@ jobs: path: './merge-folder/' token: ${{ secrets.V2BUILDTOKEN }} -# Save a copy of the original json + # Save a copy of the original json - name: Save original store_types.json run: | echo "Saving original store_types.json as store_types.sav.json" cp ./merge-folder/store_types.json ./merge-folder/store_types.sav.json -# Checkout and run the python tool + # Checkout and run the python tool - name: Check out python merge tool repo uses: actions/checkout@v4 with: @@ -110,44 +110,44 @@ jobs: if: success() uses: actions/upload-artifact@v3 with: - name: store-types + name: store-types path: | - ./tools/store-type-merge/store_types.json - ./merge-folder/store_types.sav.json + ./tools/store-type-merge/store_types.json + ./merge-folder/store_types.sav.json - name: Save Invalid Store Types JSON Artifact if: success() uses: actions/upload-artifact@v3 with: - name: invalid-repos - path: ./tools/store-type-merge/invalid_repos.json + name: invalid-repos + path: ./tools/store-type-merge/invalid_repos.json - name: Save logs directory if: success() uses: actions/upload-artifact@v3 with: - name: logs - path: ./tools/store-type-merge/log + name: logs + path: ./tools/store-type-merge/log -# Copy the result to the pr commit folder + # Copy the result to the pr commit folder - name: Copy store-type-merge results run: | echo "Saving original store_types.json as store_types.sav.json" cp -f ./tools/store-type-merge/store_types.json ./merge-folder/store_types.json -# Diff the new json against the saved copy and set an UPDATE_FILE variable + # Diff the new json against the saved copy and set an UPDATE_FILE variable - name: Diff the results run: | echo "Diff the results" echo "Set UPDATE_FILE=1 if differences" - if cmp -s ./merge-folder/store_types.json ./merge-folder/store_types.sav.json ; - then echo "UPDATE_FILE=F" | tee -a $GITHUB_ENV; - else echo "UPDATE_FILE=T" | tee -a $GITHUB_ENV; + if cmp -s ./merge-folder/store_types.json ./merge-folder/store_types.sav.json ; + then echo "UPDATE_FILE=F" | tee -a $GITHUB_ENV; + else echo "UPDATE_FILE=T" | tee -a $GITHUB_ENV; fi diff ./merge-folder/store_types.json ./merge-folder/store_types.sav.json || true -# There are two different steps with a condition to check the PR_BRANCH env var -# Both steps will contain a check for the UPDATE_FILE variable before running + # There are two different steps with a condition to check the PR_BRANCH env var + # Both steps will contain a check for the UPDATE_FILE variable before running - name: Add and Commit to newly created branch if: ${{ env.UPDATE_FILE == 'T' && env.PR_BRANCH == 'create' }} uses: Keyfactor/add-and-commit@v9.1.3 @@ -182,8 +182,8 @@ jobs: console.log("Commit to ${{env.KFUTIL_ARG}} for PR") const owner = context.repo.owner; const repo = context.repo.repo; - const baseBranch = 'main'; - const newBranch = '${{env.KFUTIL_ARG}}'; + const baseBranch = 'main'; + const newBranch = '${{env.KFUTIL_ARG}}'; const response = await github.rest.pulls.create({ owner, repo, diff --git a/.goreleaser.yml b/.goreleaser.yml index ba8a4fd..df5f4b3 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -12,9 +12,9 @@ builds: - CGO_ENABLED=0 mod_timestamp: '{{ .CommitTimestamp }}' flags: - - -trimpath + - '-trimpath' ldflags: - - '-s -w -X main.version={{.Version}} -X main.commit={{.Commit}}' + - '-s -w -X version.VERSION={{.Version}} -X main.commit={{.Commit}}' goos: - freebsd - windows diff --git a/CHANGELOG.md b/CHANGELOG.md index fe29d84..010f977 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,29 +1,109 @@ +# v1.6.0 + +## Features + +### CLI + +- `auth`: Added support for authenticating to Keyfactor Command using a oAuth2 client credentials or access token. +- `logout`: Added support for logging out of specific `profile` and `config-file`. +- `logout`: Added `yes|no` prompt for logout actions, which can be skipped by using the `--no-prompt` flag. + +### Store Types + +- `store-types create`: Added support for creating store types from a local file in `integration-manifest.json` format. +- `store-types create`: Added support for creating store types specified by a Keyfactor repo name and optional branch + ref. + +## Fixes + +### CLI + +- Fixed an issue where the CLI would sometimes terminate with no error messages when calling the + `keyfactor-go-client-sdk` +- `auth`: When passing `--config` and/or `--profile` flags, and a failure occurs, the CLI will now return an error + message + rather attempt environment variable and default config file/profile fallbacks. + +### Stores + +- `import csv`: Converts all `int` properties to `string` since Keyfactor Command does not support `int` properties. +- `import csv`: Returns useful error message when invalid `store-type-name` or `store-type-id` are passed rather than + panic. + +## Chores + +- `deps`: Bump `go` version to `1.23`. +- `deps`: Bump `azure-sdk-for-go/sdk/azidentity` version to `v1.8.0`. +- `deps`: Bump `AzureAD/microsoft-authentication-library-for-go` to `v1.3.2`. +- `deps`: Bump `keyfactor-go-client-sdk` version to `v2.0.0`. +- `deps`: Bump `keyfactor-go-client` version to `v3.0.0`. +- `deps`: Bump `creack/pty` to `v1.1.24`. +- `deps`: Bump `stretchr/testify` to `v1.10.0`. +- `deps`: Bump `x/crypto` to `v0.30.0`. +- `deps`: Bump `x/term` to `v0.27.0`. +- `deps`: Bump `x/sys` to `v0.28.0`. +- `deps`: Bump `x/text` to `v0.21.0`. + +# v1.5.1 + +## Fixes + +- fix(pkg): Bump module version to `v1.5.1` to fix an issue with the `1.5.0` release. + +# v1.5.0 + +## Features + +### CLI + +- The CLI will now embed the store_type definitions for each release of `kfutil`. +- Add global flag `--offline` to allow for offline mode. This will prevent the CLI from making requests to GitHub for + store types and store type templates and will use embedded store types and templates instead. + +## Fixes + +### Stores + +- `stores export --all`: Correctly paginates through all stores when exporting. + +### CLI + +- No longer log before the `--debug` flag is evaluated. + # v1.4.0 + ## Features ### Stores -- `stores import generate-template`: New sub CLI to generate a CSV template for bulk importing stores. [See docs](docs/kfutil_stores_import_generate-template.md)`. + +- `stores import generate-template`: New sub CLI to generate a CSV template for bulk importing + stores. [See docs](docs/kfutil_stores_import_generate-template.md)`. - `stores delete`: Support for user interactive mode. - `stores delete`: Support of delete from CSV file. - `stores export`: Supports `--all` flag and user interactive mode ## Fixes + - Various null pointer references when nothing and/or empty inputs/responses are received. - Installer script checksum check now validates properly. #119 - `stores import` sub CLI is now listed and documented #71 ### Store Types -- Empty `storepath` values are no longer passed to the API. #56 + +- Empty `storepath` values are no longer passed to the API. #56 ### PAM Types + - Handle duplicate provider type that is already created without crashing. #139 ## Docs + - [Examples for certificate store bulk operations](https://github.com/Keyfactor/kfutil/tree/epic_54795/examples/cert_stores/bulk_operations#readme) # v1.3.2 ### Package + - Bump deps `cobra` version to `v1.8.0`, `azcore` version to `v1.9.0`, `pty` version to `v1.1.21` # v1.3.1 @@ -31,17 +111,23 @@ ## Bug Fixes ### Package + - Bump package version to `1.3.1` to fix an issue with the `1.3.0` release. ### Installer + - Remove `v` prefix from installer URL path to accommodate for the new build process. # v1.3.0 + ## Features ### StoreTypes -- Added `--output-to-integration-manifest` flag to `kfutil store-types get` to download a remote certificate store type definition into an `integration-manifest.json` file locally. -- Updated usage: `kfutil store-types get [-i | -n ] [-b ] [-g | --output-to-integration-manifest]` + +- Added `--output-to-integration-manifest` flag to `kfutil store-types get` to download a remote certificate store type + definition into an `integration-manifest.json` file locally. +- Updated usage: + `kfutil store-types get [-i | -n ] [-b ] [-g | --output-to-integration-manifest]` # v1.2.1 @@ -61,7 +147,7 @@ # v1.2.0 ## Features - + ### Auth - Added support for sourcing credentials from [Azure Key Vault using Azure ID](docs/auth_providers.md#azure-key-vault) diff --git a/README.md b/README.md index 08fc1b1..f7f7d47 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ - # Keyfactor Command Utility (kfutil) -`kfutil` is a go-lang CLI wrapper for Keyfactor Command API. It also includes other utility/helper functions around automating common Keyfactor Command operations. +`kfutil` is a go-lang CLI wrapper for Keyfactor Command API. It also includes other utility/helper functions around +automating common Keyfactor Command operations. #### Integration status: Production - Ready for use in production environments. @@ -11,21 +11,21 @@ This API client allows for programmatic management of Keyfactor resources. ## Support for Keyfactor Command Utility (kfutil) -Keyfactor Command Utility (kfutil) is open source and supported on best effort level for this tool/library/client. This means customers can report Bugs, Feature Requests, Documentation amendment or questions as well as requests for customer information required for setup that needs Keyfactor access to obtain. Such requests do not follow normal SLA commitments for response or resolution. If you have a support issue, please open a support ticket via the Keyfactor Support Portal at https://support.keyfactor.com/ - -###### To report a problem or suggest a new feature, use the **[Issues](../../issues)** tab. If you want to contribute actual bug fixes or proposed enhancements, use the **[Pull requests](../../pulls)** tab. - ---- - - ---- - +Keyfactor Command Utility (kfutil) is open source and supported on best effort level for this tool/library/client. This +means customers can report Bugs, Feature Requests, Documentation amendment or questions as well as requests for customer +information required for setup that needs Keyfactor access to obtain. Such requests do not follow normal SLA commitments +for response or resolution. If you have a support issue, please open a support ticket via the Keyfactor Support Portal +at https://support.keyfactor.com/ +To report a problem or suggest a new feature, use the **[Issues](../../issues)** tab. If you want to contribute actual +bug fixes or proposed enhancements, use the **[Pull requests](../../pulls)** tab. ## Quickstart ### Linux/MacOS + #### Prerequisites: + - [jq](https://stedolan.github.io/jq/download/) CLI tool, used to parse JSON output. - Either - [curl](https://curl.se/download.html) CLI tool, used to download the release files. @@ -35,15 +35,19 @@ Keyfactor Command Utility (kfutil) is open source and supported on best effort l - `$HOME/.local/bin` in your `$PATH` and exists if not running as root, else `/usr/local/bin` if running as root. #### Installation: + ```bash bash <(curl -s https://raw.githubusercontent.com/Keyfactor/kfutil/main/install.sh) ```` ### Windows + #### Prerequisites: + - Powershell 5.1 or later #### Installation: + ```powershell Invoke-WebRequest -Uri "https://raw.githubusercontent.com/Keyfactor/kfutil/main/install.ps1" -OutFile "install.ps1" # Install kfutil to $HOME/AppData/Local/Microsoft/WindowsApps. @@ -51,40 +55,106 @@ Invoke-WebRequest -Uri "https://raw.githubusercontent.com/Keyfactor/kfutil/main/ .\install.ps1 ``` -## Environmental Variables +## Environment Variables + +### Global + +| Name | Description | Default | +|-------------------------------|-----------------------------------------------------------------------------------------------------------------|----------------------------------------| +| KEYFACTOR_HOSTNAME | Keyfactor Command hostname without protocol and port | | +| KEYFACTOR_PORT | Keyfactor Command port | `443` | +| KEYFACTOR_API_PATH | Keyfactor Command API Path | `KeyfactorAPI` | +| KEYFACTOR_SKIP_VERIFY | Skip TLS verification when connecting to Keyfactor Command | `false` | +| KEYFACTOR_CA_CERT | Either a file path or PEM encoded string to a CA certificate to trust when communicating with Keyfactor Command | | +| KEYFACTOR_CLIENT_TIMEOUT | Timeout for HTTP client requests to Keyfactor Command | `60s` | +| KEYFACTOR_AUTH_CONFIG_FILE | Path to a JSON file containing the authentication configuration | `$HOME/.keyfactor/command_config.json` | +| KEYFACTOR_AUTH_CONFIG_PROFILE | Profile to use from the authentication configuration file | `default` | + +### Basic Auth + +Currently `Basic Authentication` via `Active Directory` is the *ONLY* supported method of `Basic Authentication`. + +| Name | Description | Default | +|--------------------|---------------------------------------------------------------------------------------------|---------| +| KEYFACTOR_USERNAME | Active Directory username to authenticate to Keyfactor Command API | | +| KEYFACTOR_PASSWORD | Password associated with Active Directory username to authenticate to Keyfactor Command API | | +| KEYFACTOR_DOMAIN | Active Directory domain of user. Can be implied from username if it contains `@` or `\\` | | + +### oAuth Credentials + +| Name | Description | Default | +|------------------------------|---------------------------------------------------------------------------------------------------------------------------------|----------| +| KEYFACTOR_AUTH_CLIENT_ID | Keyfactor Auth Client ID | | +| KEYFACTOR_AUTH_CLIENT_SECRET | Keyfactor Auth Client Secret | | +| KEYFACTOR_AUTH_TOKEN_URL | URL to request an access token from Keyfactor Auth | | +| KEYFACTOR_AUTH_SCOPES | Scopes to request when authenticating to Keyfactor Command API. Each scope MUST be separated by `,` | `openid` | +| KEYFACTOR_AUTH_AUDIENCE | Audience to request when authenticating to Keyfactor Command API | | +| KEYFACTOR_AUTH_ACCESS_TOKEN | Access token to use to authenticate to Keyfactor Command API. This can be supplied directly or generated via client credentials | | +| KEYFACTOR_AUTH_CA_CERT | Either a file path or PEM encoded string to a CA certificate to use when connecting to Keyfactor Auth | | + +### kfutil specific All the variables listed below need to be set in your environment. The `kfutil` command will look for these variables -and use them if they are set. If they are not set, the utility will fail to connect to Keyfactor. - -| Variable Name | Description | -|--------------------|------------------------------------------------------------------------------------------| -| KEYFACTOR_HOSTNAME | The hostname of your Keyfactor instance. ex: `my.domain.com` | -| KEYFACTOR_USERNAME | The username to use to connect to Keyfactor. Do not include the domain. ex: `myusername` | -| KEYFACTOR_PASSWORD | The password to use to connect to Keyfactor. ex: `mypassword` | -| KEYFACTOR_DOMAIN | The domain to use to connect to Keyfactor. ex: `mydomain` | -| KEYFACTOR_API_PATH | The path to the Keyfactor API. Defaults to `/KeyfactorAPI`. | -| KFUTIL_EXP | Set to `1` or `true` to enable experimental features. | -| KFUTIL_DEBUG | Set to `1` or `true` to enable debug logging. | +and use them if they are set. + +| Variable Name | Description | +|---------------|-------------------------------------------------------| +| KFUTIL_EXP | Set to `1` or `true` to enable experimental features. | +| KFUTIL_DEBUG | Set to `1` or `true` to enable debug logging. | ### Linux/MacOS: +Below are examples of setting the environment variables in Linux/MacOS to be used with `kfutil`. + +#### Active Directory Basic Authentication + +This is the minimum required configuration to authenticate to Keyfactor Command using Active Directory username, +password auth. ```bash export KEYFACTOR_HOSTNAME="" -export KEYFACTOR_USERNAME="" # Do not include domain +export KEYFACTOR_USERNAME="" export KEYFACTOR_PASSWORD="" -export KEYFACTOR_DOMAIN="" +export KEYFACTOR_DOMAIN="" # Optional if username contains domain ``` -Additional variables: +#### oAuth Client Credentials + +This is the minimum required configuration to authenticate to Keyfactor Command using oAuth client credentials. +```bash +export KEYFACTOR_HOSTNAME="" +export KEYFACTOR_AUTH_CLIENT_ID=" -p 'Cmd' ``` - - diff --git a/cmd/certificates.go b/cmd/certificates.go index 0384474..447b5d7 100644 --- a/cmd/certificates.go +++ b/cmd/certificates.go @@ -15,9 +15,10 @@ package cmd import ( "fmt" - "github.com/Keyfactor/keyfactor-go-client/v2/api" "log" + "github.com/Keyfactor/keyfactor-go-client/v3/api" + "github.com/spf13/cobra" ) @@ -61,5 +62,11 @@ func certToString(response *api.GetCertificateResponse) string { if len(sansString) > 0 { sansString = sansString[:len(sansString)-1] } - return fmt.Sprintf("DN=(%s),SANs=(%s),TP=(%s),ID=(%d)", response.IssuedDN, sansString, response.Thumbprint, response.Id) + return fmt.Sprintf( + "DN=(%s),SANs=(%s),TP=(%s),ID=(%d)", + response.IssuedDN, + sansString, + response.Thumbprint, + response.Id, + ) } diff --git a/cmd/constants.go b/cmd/constants.go index b0caea3..55779dd 100644 --- a/cmd/constants.go +++ b/cmd/constants.go @@ -16,19 +16,24 @@ package cmd import "fmt" const ( - ColorRed = "\033[31m" - ColorWhite = "\033[37m" - DefaultAPIPath = "KeyfactorAPI" - DefaultConfigFileName = "command_config.json" - FailedAuthMsg = "Login failed!" - SuccessfulAuthMsg = "Login successful!" - XKeyfactorRequestedWith = "APIClient" - XKeyfactorApiVersion = "1" - FlagGitRef = "git-ref" - FlagFromFile = "from-file" - DebugFuncEnter = "entered: %s" - DebugFuncExit = "exiting: %s" - DebugFuncCall = "calling: %s" + ColorRed = "\033[31m" + ColorWhite = "\033[37m" + DefaultAPIPath = "KeyfactorAPI" + DefaultConfigFileName = "command_config.json" + DefaultStoreTypesFileName = "store_types.json" + DefaultGitRepo = "kfutil" + DefaultGitRef = "main" + FailedAuthMsg = "Login failed!" + SuccessfulAuthMsg = "Login successful!" + XKeyfactorRequestedWith = "APIClient" + XKeyfactorApiVersion = "1" + FlagGitRef = "git-ref" + FlagGitRepo = "repo" + FlagFromFile = "from-file" + DebugFuncEnter = "entered: %s" + DebugFuncExit = "exiting: %s" + DebugFuncCall = "calling: %s" + MinHttpTimeout = 3 ) var ProviderTypeChoices = []string{ diff --git a/cmd/containers.go b/cmd/containers.go index 2b29dfe..d177845 100644 --- a/cmd/containers.go +++ b/cmd/containers.go @@ -60,9 +60,7 @@ var containersGetCmd = &cobra.Command{ return debugErr } - // Authenticate - authConfig := createAuthConfigFromParams(kfcHostName, kfcUsername, kfcPassword, kfcDomain, kfcAPIPath) - kfClient, _ := initClient(configFile, profile, providerType, providerProfile, noPrompt, authConfig, false) + kfClient, _ := initClient(false) agents, aErr := kfClient.GetStoreContainer(id) if aErr != nil { @@ -95,7 +93,7 @@ var containersUpdateCmd = &cobra.Command{ } // Authenticate - //authConfig := createAuthConfigFromParams(kfcHostName, kfcUsername, kfcPassword, kfcDomain, kfcAPIPath) + // // CLI Logic return fmt.Errorf("update store containers not implemented") @@ -119,7 +117,7 @@ var containersDeleteCmd = &cobra.Command{ } // Authenticate - //authConfig := createAuthConfigFromParams(kfcHostName, kfcUsername, kfcPassword, kfcDomain, kfcAPIPath) + // //kfClient, _ := initClient(configFile, profile, providerType, providerProfile, noPrompt, authConfig, false) // CLI Logic @@ -144,8 +142,8 @@ var containersListCmd = &cobra.Command{ } // Authenticate - authConfig := createAuthConfigFromParams(kfcHostName, kfcUsername, kfcPassword, kfcDomain, kfcAPIPath) - kfClient, _ := initClient(configFile, profile, providerType, providerProfile, noPrompt, authConfig, false) + // + kfClient, _ := initClient(false) // CLI Logic agents, aErr := kfClient.GetStoreContainers() diff --git a/cmd/export.go b/cmd/export.go index 07d0431..a4b5737 100644 --- a/cmd/export.go +++ b/cmd/export.go @@ -18,12 +18,13 @@ import ( "context" "encoding/json" "fmt" - "github.com/Keyfactor/keyfactor-go-client-sdk/api/keyfactor" - "github.com/Keyfactor/keyfactor-go-client/v2/api" - "github.com/rs/zerolog/log" - "github.com/spf13/cobra" "os" "strconv" + + "github.com/Keyfactor/keyfactor-go-client-sdk/v2/api/keyfactor" + "github.com/Keyfactor/keyfactor-go-client/v3/api" + "github.com/rs/zerolog/log" + "github.com/spf13/cobra" ) var exportPath string @@ -136,21 +137,13 @@ var exportCmd = &cobra.Command{ SecurityRoles: []api.CreateSecurityRoleArg{}, } - log.Debug().Msgf("%s: createAuthConfigFromParams", DebugFuncCall) - authConfig := createAuthConfigFromParams(kfcHostName, kfcUsername, kfcPassword, kfcDomain, kfcAPIPath) - - if authConfig == nil { - log.Error().Msg("auth config is nil, invalid client configuration") - return fmt.Errorf(FailedAuthMsg) - } - exportPath := cmd.Flag("file").Value.String() log.Debug().Str("exportPath", exportPath).Msg("exportPath") log.Debug().Msgf("%s: initGenClient", DebugFuncCall) - kfClient, clientErr := initGenClient(configFile, profile, noPrompt, authConfig, false) + kfClient, clientErr := initGenClient(false) log.Debug().Msgf("%s: initClient", DebugFuncCall) - oldkfClient, oldClientErr := initClient(configFile, profile, "", "", noPrompt, authConfig, false) + oldkfClient, oldClientErr := initClient(false) if clientErr != nil { log.Error().Err(clientErr).Send() @@ -371,8 +364,10 @@ func getIssuedAlerts(kfClient *keyfactor.APIClient) []keyfactor.KeyfactorApiMode func getDeniedAlerts(kfClient *keyfactor.APIClient) []keyfactor.KeyfactorApiModelsAlertsDeniedDeniedAlertCreationRequest { alerts, _, reqErr := kfClient.DeniedAlertApi.DeniedAlertGetDeniedAlerts( - context.Background()).XKeyfactorRequestedWith( - XKeyfactorRequestedWith).XKeyfactorApiVersion(XKeyfactorApiVersion).Execute() + context.Background(), + ).XKeyfactorRequestedWith( + XKeyfactorRequestedWith, + ).XKeyfactorApiVersion(XKeyfactorApiVersion).Execute() if reqErr != nil { fmt.Printf("%s Error! Unable to get denied cert alerts %s%s\n", ColorRed, reqErr, ColorWhite) } @@ -575,7 +570,13 @@ func init() { exportCmd.Flags().Lookup("collections").NoOptDefVal = "true" exportCmd.Flags().BoolVarP(&fMetadata, "metadata", "m", false, "export metadata to JSON file") exportCmd.Flags().Lookup("metadata").NoOptDefVal = "true" - exportCmd.Flags().BoolVarP(&fExpirationAlerts, "expiration-alerts", "e", false, "export expiration cert alerts to JSON file") + exportCmd.Flags().BoolVarP( + &fExpirationAlerts, + "expiration-alerts", + "e", + false, + "export expiration cert alerts to JSON file", + ) exportCmd.Flags().Lookup("expiration-alerts").NoOptDefVal = "true" exportCmd.Flags().BoolVarP(&fIssuedAlerts, "issued-alerts", "i", false, "export issued cert alerts to JSON file") exportCmd.Flags().Lookup("issued-alerts").NoOptDefVal = "true" @@ -585,7 +586,13 @@ func init() { exportCmd.Flags().Lookup("pending-alerts").NoOptDefVal = "true" exportCmd.Flags().BoolVarP(&fNetworks, "networks", "n", false, "export SSL networks to JSON file") exportCmd.Flags().Lookup("networks").NoOptDefVal = "true" - exportCmd.Flags().BoolVarP(&fWorkflowDefinitions, "workflow-definitions", "w", false, "export workflow definitions to JSON file") + exportCmd.Flags().BoolVarP( + &fWorkflowDefinitions, + "workflow-definitions", + "w", + false, + "export workflow definitions to JSON file", + ) exportCmd.Flags().Lookup("workflow-definitions").NoOptDefVal = "true" exportCmd.Flags().BoolVarP(&fReports, "reports", "r", false, "export reports to JSON file") exportCmd.Flags().Lookup("reports").NoOptDefVal = "true" diff --git a/cmd/helm_uo.go b/cmd/helm_uo.go index 44107e5..ceeee6e 100644 --- a/cmd/helm_uo.go +++ b/cmd/helm_uo.go @@ -18,12 +18,14 @@ package cmd import ( "fmt" + "log" + + "github.com/Keyfactor/keyfactor-auth-client-go/auth_providers" "github.com/spf13/cobra" "github.com/spf13/pflag" "kfutil/pkg/cmdutil" "kfutil/pkg/cmdutil/flags" "kfutil/pkg/helm" - "log" ) // DefaultValuesLocation TODO when Helm is ready, set this to the default values.yaml location in Git @@ -68,9 +70,27 @@ func (f *HelmUoFlags) AddFlags(flags *pflag.FlagSet) { f.FilenameFlags.AddFlags(flags) // Add custom flags - flags.StringVarP(f.GithubToken, "token", "t", *f.GithubToken, "Token used for related authentication - required for private repositories") - flags.StringVarP(f.OutPath, "out", "o", *f.OutPath, "Path to output the modified values.yaml file. This file can then be used with helm install -f to override the default values.") - flags.StringSliceVarP(f.Extensions, "extension", "e", *f.Extensions, "List of extensions to install. Should be in the format @. If no version is specified, the latest version will be downloaded.") + flags.StringVarP( + f.GithubToken, + "token", + "t", + *f.GithubToken, + "Token used for related authentication - required for private repositories", + ) + flags.StringVarP( + f.OutPath, + "out", + "o", + *f.OutPath, + "Path to output the modified values.yaml file. This file can then be used with helm install -f to override the default values.", + ) + flags.StringSliceVarP( + f.Extensions, + "extension", + "e", + *f.Extensions, + "List of extensions to install. Should be in the format @. If no version is specified, the latest version will be downloaded.", + ) } func NewCmdHelmUo() *cobra.Command { @@ -152,13 +172,13 @@ func (f *HelmUoFlags) ToOptions(cmd *cobra.Command, args []string) (*HelmUoOptio } // Get the command config entry from global flags - commandConfig, _ := authConfigFile(configFile, profile, "", noPrompt, false) + commandConfig, _ := auth_providers.ReadConfigFromJSON(configFile) // Get the hostname from the command config entry, ok := commandConfig.Servers[profile] if ok { - if entry.Hostname != "" { - options.CommandHostname = commandConfig.Servers[profile].Hostname + if entry.Host != "" { + options.CommandHostname = commandConfig.Servers[profile].Host } } diff --git a/cmd/helpers.go b/cmd/helpers.go index 0a07df8..52e0cdb 100644 --- a/cmd/helpers.go +++ b/cmd/helpers.go @@ -19,16 +19,19 @@ import ( "encoding/json" "errors" "fmt" - "github.com/google/uuid" - "github.com/rs/zerolog" - "github.com/rs/zerolog/log" - "github.com/spf13/cobra" "io" "net/http" "os" "path/filepath" "strconv" "time" + + "github.com/google/uuid" + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" + "github.com/spf13/cobra" + + stdlog "log" ) func boolToPointer(b bool) *bool { @@ -180,7 +183,6 @@ func getCurrentTime(f string) string { func informDebug(debugFlag bool) { debugModeEnabled := checkDebug(debugFlag) - zerolog.SetGlobalLevel(zerolog.Disabled) if debugModeEnabled { //zerolog.SetGlobalLevel(zerolog.InfoLevel) zerolog.SetGlobalLevel(zerolog.DebugLevel) @@ -188,9 +190,12 @@ func informDebug(debugFlag bool) { } func initLogger() { + stdlog.SetOutput(io.Discard) zerolog.TimeFieldFormat = zerolog.TimeFormatUnix + zerolog.SetGlobalLevel(zerolog.Disabled) // default to disabled log.Logger = log.With().Caller().Logger() log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339}) + } func intToPointer(i int) *int { @@ -328,11 +333,19 @@ func outputError(err error, isFatal bool, format string) { } func outputResult(result interface{}, format string) { + log.Debug(). + Interface("result", result). + Str("format", format). + Msg(fmt.Sprintf("%s outputResult", DebugFuncEnter)) if format == "json" { fmt.Println(result) } else { fmt.Println(fmt.Sprintf("%s", result)) } + log.Debug(). + Interface("result", result). + Str("format", format). + Msg(fmt.Sprintf("%s outputResult", DebugFuncExit)) } func readCSVHeader(filename string) ([]string, error) { diff --git a/cmd/import.go b/cmd/import.go index 5004a2e..0a1374a 100644 --- a/cmd/import.go +++ b/cmd/import.go @@ -18,12 +18,13 @@ import ( "context" "encoding/json" "fmt" - "github.com/Keyfactor/keyfactor-go-client-sdk/api/keyfactor" - "github.com/Keyfactor/keyfactor-go-client/v2/api" - "github.com/rs/zerolog/log" - "github.com/spf13/cobra" "io" "os" + + "github.com/Keyfactor/keyfactor-go-client-sdk/v2/api/keyfactor" + "github.com/Keyfactor/keyfactor-go-client/v3/api" + "github.com/rs/zerolog/log" + "github.com/spf13/cobra" ) type Body struct { @@ -68,12 +69,6 @@ var importCmd = &cobra.Command{ log.Info().Msg("Running import...") - log.Debug().Msgf("%s: createAuthConfigFromParams", DebugFuncCall) - authConfig := createAuthConfigFromParams(kfcHostName, kfcUsername, kfcPassword, kfcDomain, kfcAPIPath) - if authConfig == nil { - return fmt.Errorf("Error: %s", FailedAuthMsg) - } - exportPath := cmd.Flag("file").Value.String() log.Debug().Str("exportPath", exportPath).Msg("exportPath") @@ -106,9 +101,9 @@ var importCmd = &cobra.Command{ return jErr } log.Debug().Msgf("%s: initGenClient", DebugFuncCall) - kfClient, clientErr := initGenClient(configFile, profile, noPrompt, authConfig, false) + kfClient, clientErr := initGenClient(false) log.Debug().Msgf("%s: initClient", DebugFuncExit) - oldkfClient, oldClientErr := initClient(configFile, profile, "", "", noPrompt, authConfig, false) + oldkfClient, oldClientErr := initClient(false) if clientErr != nil { log.Error().Err(clientErr).Send() @@ -194,7 +189,10 @@ var importCmd = &cobra.Command{ }, } -func importCollections(collections []keyfactor.KeyfactorApiModelsCertificateCollectionsCertificateCollectionCreateRequest, kfClient *keyfactor.APIClient) { +func importCollections( + collections []keyfactor.KeyfactorApiModelsCertificateCollectionsCertificateCollectionCreateRequest, + kfClient *keyfactor.APIClient, +) { for _, collection := range collections { _, httpResp, reqErr := kfClient.CertificateCollectionApi. CertificateCollectionCreateCollection(context.Background()). @@ -209,7 +207,13 @@ func importCollections(collections []keyfactor.KeyfactorApiModelsCertificateColl log.Error().Err(jmErr).Send() } if reqErr != nil { - fmt.Printf("%s Error! Unable to create collection %s - %s%s\n", ColorRed, string(name), parseError(httpResp.Body), ColorWhite) + fmt.Printf( + "%s Error! Unable to create collection %s - %s%s\n", + ColorRed, + string(name), + parseError(httpResp.Body), + ColorWhite, + ) } else { n, jnErr := json.Marshal(collection.Name) if jnErr != nil { @@ -222,7 +226,10 @@ func importCollections(collections []keyfactor.KeyfactorApiModelsCertificateColl } } -func importMetadataFields(metadataFields []keyfactor.KeyfactorApiModelsMetadataFieldMetadataFieldCreateRequest, kfClient *keyfactor.APIClient) { +func importMetadataFields( + metadataFields []keyfactor.KeyfactorApiModelsMetadataFieldMetadataFieldCreateRequest, + kfClient *keyfactor.APIClient, +) { for _, metadata := range metadataFields { _, httpResp, reqErr := kfClient.MetadataFieldApi.MetadataFieldCreateMetadataField(context.Background()). XKeyfactorRequestedWith(XKeyfactorRequestedWith). @@ -238,7 +245,13 @@ func importMetadataFields(metadataFields []keyfactor.KeyfactorApiModelsMetadataF log.Error().Err(jmErr).Send() } log.Error().Err(reqErr).Send() - fmt.Printf("%s Error! Unable to create metadata field type %s - %s%s\n", ColorRed, string(n), parseError(httpResp.Body), ColorWhite) + fmt.Printf( + "%s Error! Unable to create metadata field type %s - %s%s\n", + ColorRed, + string(n), + parseError(httpResp.Body), + ColorWhite, + ) } else { log.Info().Msgf("Added %s to metadata field types.", string(n)) fmt.Println("Added", string(n), "to metadata field types.") @@ -246,36 +259,63 @@ func importMetadataFields(metadataFields []keyfactor.KeyfactorApiModelsMetadataF } } -func importIssuedCertAlerts(alerts []keyfactor.KeyfactorApiModelsAlertsIssuedIssuedAlertCreationRequest, kfClient *keyfactor.APIClient) { +func importIssuedCertAlerts( + alerts []keyfactor.KeyfactorApiModelsAlertsIssuedIssuedAlertCreationRequest, + kfClient *keyfactor.APIClient, +) { for _, alert := range alerts { _, httpResp, reqErr := kfClient.IssuedAlertApi.IssuedAlertAddIssuedAlert(context.Background()).XKeyfactorRequestedWith(XKeyfactorRequestedWith).Req(alert).XKeyfactorApiVersion(XKeyfactorApiVersion).Execute() name, _ := json.Marshal(alert.DisplayName) if reqErr != nil { - fmt.Printf("%s Error! Unable to create issued cert alert %s - %s%s\n", ColorRed, string(name), parseError(httpResp.Body), ColorWhite) + fmt.Printf( + "%s Error! Unable to create issued cert alert %s - %s%s\n", + ColorRed, + string(name), + parseError(httpResp.Body), + ColorWhite, + ) } else { fmt.Println("Added", string(name), "to issued cert alerts.") } } } -func importDeniedCertAlerts(alerts []keyfactor.KeyfactorApiModelsAlertsDeniedDeniedAlertCreationRequest, kfClient *keyfactor.APIClient) { +func importDeniedCertAlerts( + alerts []keyfactor.KeyfactorApiModelsAlertsDeniedDeniedAlertCreationRequest, + kfClient *keyfactor.APIClient, +) { for _, alert := range alerts { _, httpResp, reqErr := kfClient.DeniedAlertApi.DeniedAlertAddDeniedAlert(context.Background()).XKeyfactorRequestedWith(XKeyfactorRequestedWith).Req(alert).XKeyfactorApiVersion(XKeyfactorApiVersion).Execute() name, _ := json.Marshal(alert.DisplayName) if reqErr != nil { - fmt.Printf("%s Error! Unable to create denied cert alert %s - %s%s\n", ColorRed, string(name), parseError(httpResp.Body), ColorWhite) + fmt.Printf( + "%s Error! Unable to create denied cert alert %s - %s%s\n", + ColorRed, + string(name), + parseError(httpResp.Body), + ColorWhite, + ) } else { fmt.Println("Added", string(name), "to denied cert alerts.") } } } -func importPendingCertAlerts(alerts []keyfactor.KeyfactorApiModelsAlertsPendingPendingAlertCreationRequest, kfClient *keyfactor.APIClient) { +func importPendingCertAlerts( + alerts []keyfactor.KeyfactorApiModelsAlertsPendingPendingAlertCreationRequest, + kfClient *keyfactor.APIClient, +) { for _, alert := range alerts { _, httpResp, reqErr := kfClient.PendingAlertApi.PendingAlertAddPendingAlert(context.Background()).XKeyfactorRequestedWith(XKeyfactorRequestedWith).Req(alert).XKeyfactorApiVersion(XKeyfactorApiVersion).Execute() name, _ := json.Marshal(alert.DisplayName) if reqErr != nil { - fmt.Printf("%s Error! Unable to create pending cert alert %s - %s%s\n", ColorRed, string(name), parseError(httpResp.Body), ColorWhite) + fmt.Printf( + "%s Error! Unable to create pending cert alert %s - %s%s\n", + ColorRed, + string(name), + parseError(httpResp.Body), + ColorWhite, + ) } else { fmt.Println("Added", string(name), "to pending cert alerts.") } @@ -287,7 +327,13 @@ func importNetworks(networks []keyfactor.KeyfactorApiModelsSslCreateNetworkReque _, httpResp, reqErr := kfClient.SslApi.SslCreateNetwork(context.Background()).XKeyfactorRequestedWith(XKeyfactorRequestedWith).Network(network).XKeyfactorApiVersion(XKeyfactorApiVersion).Execute() name, _ := json.Marshal(network.Name) if reqErr != nil { - fmt.Printf("%s Error! Unable to create SSL network %s - %s%s\n", ColorRed, string(name), parseError(httpResp.Body), ColorWhite) + fmt.Printf( + "%s Error! Unable to create SSL network %s - %s%s\n", + ColorRed, + string(name), + parseError(httpResp.Body), + ColorWhite, + ) } else { fmt.Println("Added", string(name), "to SSL networks.") } @@ -295,7 +341,10 @@ func importNetworks(networks []keyfactor.KeyfactorApiModelsSslCreateNetworkReque } // identify matching templates between instances by name, then return the template Id of the matching template in the import instance -func findMatchingTemplates(exportedWorkflowDef exportKeyfactorAPIModelsWorkflowsDefinitionCreateRequest, kfClient *keyfactor.APIClient) *string { +func findMatchingTemplates( + exportedWorkflowDef exportKeyfactorAPIModelsWorkflowsDefinitionCreateRequest, + kfClient *keyfactor.APIClient, +) *string { importInstanceTemplates, _, _ := kfClient.TemplateApi.TemplateGetTemplates(context.Background()).XKeyfactorRequestedWith(XKeyfactorRequestedWith).XKeyfactorApiVersion(XKeyfactorApiVersion).Execute() for _, template := range importInstanceTemplates { importInstTempNameJson, _ := json.Marshal(template.TemplateName) @@ -309,7 +358,10 @@ func findMatchingTemplates(exportedWorkflowDef exportKeyfactorAPIModelsWorkflows return nil } -func importWorkflowDefinitions(workflowDefs []exportKeyfactorAPIModelsWorkflowsDefinitionCreateRequest, kfClient *keyfactor.APIClient) { +func importWorkflowDefinitions( + workflowDefs []exportKeyfactorAPIModelsWorkflowsDefinitionCreateRequest, + kfClient *keyfactor.APIClient, +) { for _, workflowDef := range workflowDefs { wJson, _ := json.Marshal(workflowDef) var workflowDefReq keyfactor.KeyfactorApiModelsWorkflowsDefinitionCreateRequest @@ -338,7 +390,13 @@ func importWorkflowDefinitions(workflowDefs []exportKeyfactorAPIModelsWorkflowsD } if reqErr != nil { - fmt.Printf("%s Error! Unable to create workflow definition %s - %s%s\n", ColorRed, string(name), parseError(httpResp.Body), ColorWhite) + fmt.Printf( + "%s Error! Unable to create workflow definition %s - %s%s\n", + ColorRed, + string(name), + parseError(httpResp.Body), + ColorWhite, + ) log.Error().Err(reqErr).Send() } else { fmt.Println("Added", string(name), "to workflow definitions.") @@ -401,7 +459,13 @@ func importBuiltInReports(reports []exportModelsReport, kfClient *keyfactor.APIC return } if reqErr != nil { - fmt.Printf("%s Error! Unable to update built-in report %s - %s%s\n", ColorRed, string(name), parseError(httpResp.Body), ColorWhite) + fmt.Printf( + "%s Error! Unable to update built-in report %s - %s%s\n", + ColorRed, + string(name), + parseError(httpResp.Body), + ColorWhite, + ) log.Error().Err(reqErr).Send() } else { fmt.Println("Updated", string(name), "in built-in reports.") @@ -416,7 +480,13 @@ func importCustomReports(reports []keyfactor.ModelsCustomReportCreationRequest, _, httpResp, reqErr := kfClient.ReportsApi.ReportsCreateCustomReport(context.Background()).XKeyfactorRequestedWith(XKeyfactorRequestedWith).Request(report).XKeyfactorApiVersion(XKeyfactorApiVersion).Execute() name, _ := json.Marshal(report.DisplayName) if reqErr != nil { - fmt.Printf("%s Error! Unable to create custom report %s - %s%s\n", ColorRed, string(name), parseError(httpResp.Body), ColorWhite) + fmt.Printf( + "%s Error! Unable to create custom report %s - %s%s\n", + ColorRed, + string(name), + parseError(httpResp.Body), + ColorWhite, + ) } else { fmt.Println("Added", string(name), "to custom reports.") } @@ -428,7 +498,13 @@ func importSecurityRoles(roles []api.CreateSecurityRoleArg, kfClient *api.Client _, reqErr := kfClient.CreateSecurityRole(&role) name, _ := json.Marshal(role.Name) if reqErr != nil { - fmt.Printf("%s Error! Unable to create security role %s - %s%s\n", ColorRed, string(name), reqErr, ColorWhite) + fmt.Printf( + "%s Error! Unable to create security role %s - %s%s\n", + ColorRed, + string(name), + reqErr, + ColorWhite, + ) } else { fmt.Println("Added", string(name), "to security roles.") } @@ -456,7 +532,13 @@ func init() { importCmd.Flags().Lookup("pending-alerts").NoOptDefVal = "true" importCmd.Flags().BoolVarP(&fNetworks, "networks", "n", false, "import SSL networks to JSON file") importCmd.Flags().Lookup("networks").NoOptDefVal = "true" - importCmd.Flags().BoolVarP(&fWorkflowDefinitions, "workflow-definitions", "w", false, "import workflow definitions to JSON file") + importCmd.Flags().BoolVarP( + &fWorkflowDefinitions, + "workflow-definitions", + "w", + false, + "import workflow definitions to JSON file", + ) importCmd.Flags().Lookup("workflow-definitions").NoOptDefVal = "true" importCmd.Flags().BoolVarP(&fReports, "reports", "r", false, "import reports to JSON file") importCmd.Flags().Lookup("reports").NoOptDefVal = "true" diff --git a/cmd/integration_manifest.go b/cmd/integration_manifest.go new file mode 100644 index 0000000..409c37f --- /dev/null +++ b/cmd/integration_manifest.go @@ -0,0 +1,30 @@ +package cmd + +import ( + "github.com/Keyfactor/keyfactor-go-client/v3/api" +) + +type IntegrationManifest struct { + Schema string `json:"$schema"` + IntegrationType string `json:"integration_type"` + Name string `json:"name"` + Status string `json:"status"` + LinkGithub bool `json:"link_github"` + UpdateCatalog bool `json:"update_catalog"` + SupportLevel string `json:"support_level"` + ReleaseDir string `json:"release_dir"` + ReleaseProject string `json:"release_project"` + Description string `json:"description"` + About About `json:"about"` +} + +type About struct { + Orchestrator Orchestrator `json:"orchestrator"` +} + +type Orchestrator struct { + UOFramework string `json:"UOFramework"` + PAMSupport bool `json:"pam_support"` + KeyfactorPlatformVersion string `json:"keyfactor_platform_version"` + StoreTypes []api.CertificateStoreType `json:"store_types"` +} diff --git a/cmd/inventory.go b/cmd/inventory.go index 0b004ec..28d4404 100644 --- a/cmd/inventory.go +++ b/cmd/inventory.go @@ -17,9 +17,10 @@ package cmd import ( "encoding/json" "fmt" - "github.com/Keyfactor/keyfactor-go-client/v2/api" - "github.com/spf13/cobra" "log" + + "github.com/Keyfactor/keyfactor-go-client/v3/api" + "github.com/spf13/cobra" ) // inventoryCmd represents the inventory command @@ -50,8 +51,6 @@ var inventoryClearCmd = &cobra.Command{ PreRun: nil, PreRunE: nil, Run: func(cmd *cobra.Command, args []string) { - - authConfig := createAuthConfigFromParams(kfcHostName, kfcUsername, kfcPassword, kfcDomain, kfcAPIPath) isExperimental := true _, expErr := isExperimentalFeatureEnabled(expEnabled, isExperimental) @@ -71,7 +70,7 @@ var inventoryClearCmd = &cobra.Command{ containerType, _ := cmd.Flags().GetStringSlice("container") allStores, _ := cmd.Flags().GetBool("all") - kfClient, _ := initClient(configFile, profile, "", "", noPrompt, authConfig, false) + kfClient, _ := initClient(false) if storeID == nil && machineName == nil && storeType == nil && containerType == nil && !allStores { fmt.Println("You must specify at least one of the following options: --sid, --client, --store-type, --container, --all") @@ -133,7 +132,11 @@ var inventoryClearCmd = &cobra.Command{ } if !force { - fmt.Printf("This will clear the inventory of ALL certificates in the store %s:%s. Are you sure you sure?! Press 'y' to continue? (y/n) ", store.ClientMachine, store.StorePath) + fmt.Printf( + "This will clear the inventory of ALL certificates in the store %s:%s. Are you sure you sure?! Press 'y' to continue? (y/n) ", + store.ClientMachine, + store.StorePath, + ) var answer string fmt.Scanln(&answer) if answer != "y" { @@ -145,7 +148,8 @@ var inventoryClearCmd = &cobra.Command{ for _, inv := range *sInvs { certs := inv.Certificates for _, cert := range certs { - st := api.CertificateStore{ //TODO: This conversion is a bit weird to have to do. Should be able to pass the store directly. + st := api.CertificateStore{ + //TODO: This conversion is a bit weird to have to do. Should be able to pass the store directly. CertificateStoreId: store.Id, Alias: cert.Thumbprint, Overwrite: true, @@ -163,12 +167,23 @@ var inventoryClearCmd = &cobra.Command{ if !dryRun { _, err := kfClient.RemoveCertificateFromStores(&removeReq) if err != nil { - fmt.Printf("Error removing certificate %s(%d) from store %s: %s\n", cert.IssuedDN, cert.Id, st.CertificateStoreId, err) + fmt.Printf( + "Error removing certificate %s(%d) from store %s: %s\n", + cert.IssuedDN, + cert.Id, + st.CertificateStoreId, + err, + ) log.Printf("[ERROR] %s", err) continue } } else { - fmt.Printf("Dry run: Would have removed certificate %s(%d) from store %s\n", cert.IssuedDN, cert.Id, st.CertificateStoreId) + fmt.Printf( + "Dry run: Would have removed certificate %s(%d) from store %s\n", + cert.IssuedDN, + cert.Id, + st.CertificateStoreId, + ) } } @@ -203,8 +218,6 @@ specified by Keyfactor command store ID, client machine name, store type, or con and one or more certificates must be specified. If multiple stores and/or certificates are specified, the command will attempt to add all the certificate(s) meeting the specified criteria to all stores meeting the specified criteria.`, Run: func(cmd *cobra.Command, args []string) { - - authConfig := createAuthConfigFromParams(kfcHostName, kfcUsername, kfcPassword, kfcDomain, kfcAPIPath) isExperimental := true _, expErr := isExperimentalFeatureEnabled(expEnabled, isExperimental) @@ -237,7 +250,7 @@ attempt to add all the certificate(s) meeting the specified criteria to all stor log.Fatalf("At least one certificate must be specified") } - kfClient, _ := initClient(configFile, profile, "", "", noPrompt, authConfig, false) + kfClient, _ := initClient(false) if storeIDs == nil && machineNames == nil && storeTypes == nil && containerType == nil && !allStores { fmt.Println("You must specify at least one of the following options: --sid, --client, --store-type, --container, --all") @@ -264,9 +277,11 @@ attempt to add all the certificate(s) meeting the specified criteria to all stor var filteredCerts []api.GetCertificateResponse for _, cn := range subjects { - cert, err := kfClient.ListCertificates(map[string]string{ - "subject": cn, - }) + cert, err := kfClient.ListCertificates( + map[string]string{ + "subject": cn, + }, + ) if err != nil { fmt.Printf("Unable to find certificate with subject: %s\n", cn) continue @@ -274,9 +289,11 @@ attempt to add all the certificate(s) meeting the specified criteria to all stor filteredCerts = append(filteredCerts, cert...) } for _, thumbprint := range thumbprints { - cert, err := kfClient.ListCertificates(map[string]string{ - "thumbprint": thumbprint, - }) + cert, err := kfClient.ListCertificates( + map[string]string{ + "thumbprint": thumbprint, + }, + ) if err != nil { fmt.Printf("Unable to find certificate with thumbprint: %s\n", thumbprint) continue @@ -284,9 +301,11 @@ attempt to add all the certificate(s) meeting the specified criteria to all stor filteredCerts = append(filteredCerts, cert...) } for _, certID := range certIDs { - cert, err := kfClient.ListCertificates(map[string]string{ - "id": certID, - }) + cert, err := kfClient.ListCertificates( + map[string]string{ + "id": certID, + }, + ) if err != nil { fmt.Printf("Unable to find certificate with ID: %s\n", certID) continue @@ -323,7 +342,8 @@ attempt to add all the certificate(s) meeting the specified criteria to all stor Immediate: boolToPointer(true), } for _, cert := range filteredCerts { - st := api.CertificateStore{ //TODO: This conversion is weird. Should be able to use the store directly. + st := api.CertificateStore{ + //TODO: This conversion is weird. Should be able to use the store directly. CertificateStoreId: store.Id, Alias: cert.Thumbprint, Overwrite: true, @@ -340,7 +360,13 @@ attempt to add all the certificate(s) meeting the specified criteria to all stor } if !dryRun { if !force { - fmt.Printf("This will add the certificate %s(%d) to certificate store %s%s's inventory. Are you sure you shouldPass to continue? (y/n) ", cert.IssuedCN, cert.Id, store.ClientMachine, store.StorePath) + fmt.Printf( + "This will add the certificate %s(%d) to certificate store %s%s's inventory. Are you sure you shouldPass to continue? (y/n) ", + cert.IssuedCN, + cert.Id, + store.ClientMachine, + store.StorePath, + ) var answer string fmt.Scanln(&answer) if answer != "y" { @@ -350,12 +376,23 @@ attempt to add all the certificate(s) meeting the specified criteria to all stor } _, err := kfClient.AddCertificateToStores(&addReq) if err != nil { - fmt.Printf("Error adding certificate %s(%d) to store %s: %s\n", cert.IssuedCN, cert.Id, st.CertificateStoreId, err) + fmt.Printf( + "Error adding certificate %s(%d) to store %s: %s\n", + cert.IssuedCN, + cert.Id, + st.CertificateStoreId, + err, + ) log.Printf("[ERROR] %s", err) continue } } else { - fmt.Printf("Dry run: Would have added certificate %s(%d) from store %s", cert.IssuedDN, cert.Id, st.CertificateStoreId) + fmt.Printf( + "Dry run: Would have added certificate %s(%d) from store %s", + cert.IssuedDN, + cert.Id, + st.CertificateStoreId, + ) } } @@ -370,7 +407,6 @@ var inventoryRemoveCmd = &cobra.Command{ Long: `Removes a certificate from the certificate store inventory.`, Run: func(cmd *cobra.Command, args []string) { - authConfig := createAuthConfigFromParams(kfcHostName, kfcUsername, kfcPassword, kfcDomain, kfcAPIPath) isExperimental := true _, expErr := isExperimentalFeatureEnabled(expEnabled, isExperimental) @@ -403,7 +439,7 @@ var inventoryRemoveCmd = &cobra.Command{ log.Fatalf("At least one certificate must be specified") } - kfClient, _ := initClient(configFile, profile, "", "", noPrompt, authConfig, false) + kfClient, _ := initClient(false) if storeIDs == nil && machineNames == nil && storeTypes == nil && containerType == nil && !allStores { fmt.Println("You must specify at least one of the following options: --sid, --client, --store-type, --container, --all") @@ -430,9 +466,11 @@ var inventoryRemoveCmd = &cobra.Command{ var filteredCerts []api.GetCertificateResponse for _, cn := range subjects { - cert, err := kfClient.ListCertificates(map[string]string{ - "subject": cn, - }) + cert, err := kfClient.ListCertificates( + map[string]string{ + "subject": cn, + }, + ) if err != nil { fmt.Printf("Unable to find certificate with subject: %s\n", cn) continue @@ -440,9 +478,11 @@ var inventoryRemoveCmd = &cobra.Command{ filteredCerts = append(filteredCerts, cert...) } for _, thumbprint := range thumbprints { - cert, err := kfClient.ListCertificates(map[string]string{ - "thumbprint": thumbprint, - }) + cert, err := kfClient.ListCertificates( + map[string]string{ + "thumbprint": thumbprint, + }, + ) if err != nil { fmt.Printf("Unable to find certificate with thumbprint: %s\n", thumbprint) continue @@ -450,9 +490,11 @@ var inventoryRemoveCmd = &cobra.Command{ filteredCerts = append(filteredCerts, cert...) } for _, certID := range certIDs { - cert, err := kfClient.ListCertificates(map[string]string{ - "id": certID, - }) + cert, err := kfClient.ListCertificates( + map[string]string{ + "id": certID, + }, + ) if err != nil { fmt.Printf("Unable to find certificate with ID: %s\n", certID) continue @@ -490,7 +532,8 @@ var inventoryRemoveCmd = &cobra.Command{ Immediate: boolToPointer(true), } for _, cert := range filteredCerts { - st := api.CertificateStore{ //TODO: This conversion is weird. Should be able to use the store directly. + st := api.CertificateStore{ + //TODO: This conversion is weird. Should be able to use the store directly. CertificateStoreId: store.Id, Alias: cert.Thumbprint, Overwrite: true, @@ -507,7 +550,12 @@ var inventoryRemoveCmd = &cobra.Command{ } if !dryRun { if !force { - fmt.Printf("This will remove the certificate %s from certificate store %s%s's inventory. Are you sure you shouldPass to continue? (y/n) ", certToString(&cert), store.ClientMachine, store.StorePath) + fmt.Printf( + "This will remove the certificate %s from certificate store %s%s's inventory. Are you sure you shouldPass to continue? (y/n) ", + certToString(&cert), + store.ClientMachine, + store.StorePath, + ) var answer string fmt.Scanln(&answer) if answer != "y" { @@ -517,12 +565,21 @@ var inventoryRemoveCmd = &cobra.Command{ } _, err := kfClient.RemoveCertificateFromStores(&removeReq) if err != nil { - fmt.Printf("Error removing certificate %s to store %s: %s\n", certToString(&cert), st.CertificateStoreId, err) + fmt.Printf( + "Error removing certificate %s to store %s: %s\n", + certToString(&cert), + st.CertificateStoreId, + err, + ) log.Printf("[ERROR] %s", err) continue } } else { - fmt.Printf("Dry run: Would have removed certificate %s from store %s\n", certToString(&cert), st.CertificateStoreId) + fmt.Printf( + "Dry run: Would have removed certificate %s from store %s\n", + certToString(&cert), + st.CertificateStoreId, + ) } } @@ -552,8 +609,6 @@ var inventoryShowCmd = &cobra.Command{ PreRun: nil, PreRunE: nil, Run: func(cmd *cobra.Command, args []string) { - - authConfig := createAuthConfigFromParams(kfcHostName, kfcUsername, kfcPassword, kfcDomain, kfcAPIPath) isExperimental := true _, expErr := isExperimentalFeatureEnabled(expEnabled, isExperimental) @@ -569,7 +624,7 @@ var inventoryShowCmd = &cobra.Command{ storeTypes, _ := cmd.Flags().GetStringSlice("store-type") containers, _ := cmd.Flags().GetStringSlice("container") - kfClient, _ := initClient(configFile, profile, "", "", noPrompt, authConfig, false) + kfClient, _ := initClient(false) if len(storeIDs) == 0 && len(clientMachineNames) == 0 && len(storeTypes) == 0 && len(containers) == 0 { fmt.Println("No filters specified. Unable to show inventory. Please specify at least one filter: [--sid, --client, --store-type, --container]") @@ -669,42 +724,182 @@ func init() { storesCmd.AddCommand(inventoryCmd) inventoryCmd.AddCommand(inventoryClearCmd) - inventoryClearCmd.Flags().StringSliceVar(&ids, "sid", []string{}, "The Keyfactor Command ID of the certificate store(s) remove all inventory from.") - inventoryClearCmd.Flags().StringSliceVar(&clients, "client", []string{}, "Remove all inventory from store(s) of specific client machine(s).") - inventoryClearCmd.Flags().StringSliceVar(&types, "store-type", []string{}, "Remove all inventory from store(s) of specific store type(s).") - inventoryClearCmd.Flags().StringSliceVar(&containers, "container", []string{}, "Remove all inventory from store(s) of specific container type(s).") + inventoryClearCmd.Flags().StringSliceVar( + &ids, + "sid", + []string{}, + "The Keyfactor Command ID of the certificate store(s) remove all inventory from.", + ) + inventoryClearCmd.Flags().StringSliceVar( + &clients, + "client", + []string{}, + "Remove all inventory from store(s) of specific client machine(s).", + ) + inventoryClearCmd.Flags().StringSliceVar( + &types, + "store-type", + []string{}, + "Remove all inventory from store(s) of specific store type(s).", + ) + inventoryClearCmd.Flags().StringSliceVar( + &containers, + "container", + []string{}, + "Remove all inventory from store(s) of specific container type(s).", + ) inventoryClearCmd.Flags().BoolVar(&all, "all", false, "Remove all inventory from all certificate stores.") - inventoryClearCmd.Flags().BoolVar(&force, "force", false, "Force removal of inventory without prompting for confirmation.") - inventoryClearCmd.Flags().BoolVar(&dryRun, "dry-run", false, "Do not remove inventory, only show what would be removed.") + inventoryClearCmd.Flags().BoolVar( + &force, + "force", + false, + "Force removal of inventory without prompting for confirmation.", + ) + inventoryClearCmd.Flags().BoolVar( + &dryRun, + "dry-run", + false, + "Do not remove inventory, only show what would be removed.", + ) inventoryCmd.AddCommand(inventoryAddCmd) - inventoryAddCmd.Flags().StringSliceVar(&ids, "sid", []string{}, "The Keyfactor Command ID of the certificate store(s) to add inventory to.") - inventoryAddCmd.Flags().StringSliceVar(&clients, "client", []string{}, "Add a certificate to all stores of specific client machine(s).") - inventoryAddCmd.Flags().StringSliceVar(&types, "store-type", []string{}, "Add a certificate to all stores of specific store type(s).") - inventoryAddCmd.Flags().StringSliceVar(&containers, "container", []string{}, "Add a certificate to all stores of specific container type(s).") - inventoryAddCmd.Flags().StringSliceVar(&thumbprints, "thumbprint", []string{}, "The thumbprint of the certificate(s) to add to the store(s).") - inventoryAddCmd.Flags().StringSliceVar(&cIDs, "cid", []string{}, "The Keyfactor command certificate ID(s) of the certificate to add to the store(s).") - inventoryAddCmd.Flags().StringSliceVar(&subjectNames, "cn", []string{}, "Subject name(s) of the certificate(s) to add to the store(s).") + inventoryAddCmd.Flags().StringSliceVar( + &ids, + "sid", + []string{}, + "The Keyfactor Command ID of the certificate store(s) to add inventory to.", + ) + inventoryAddCmd.Flags().StringSliceVar( + &clients, + "client", + []string{}, + "Add a certificate to all stores of specific client machine(s).", + ) + inventoryAddCmd.Flags().StringSliceVar( + &types, + "store-type", + []string{}, + "Add a certificate to all stores of specific store type(s).", + ) + inventoryAddCmd.Flags().StringSliceVar( + &containers, + "container", + []string{}, + "Add a certificate to all stores of specific container type(s).", + ) + inventoryAddCmd.Flags().StringSliceVar( + &thumbprints, + "thumbprint", + []string{}, + "The thumbprint of the certificate(s) to add to the store(s).", + ) + inventoryAddCmd.Flags().StringSliceVar( + &cIDs, + "cid", + []string{}, + "The Keyfactor command certificate ID(s) of the certificate to add to the store(s).", + ) + inventoryAddCmd.Flags().StringSliceVar( + &subjectNames, + "cn", + []string{}, + "Subject name(s) of the certificate(s) to add to the store(s).", + ) inventoryAddCmd.Flags().BoolVar(&all, "all-stores", false, "Add the certificate(s) to all certificate stores.") - inventoryAddCmd.Flags().BoolVar(&force, "force", false, "Force addition of inventory without prompting for confirmation.") + inventoryAddCmd.Flags().BoolVar( + &force, + "force", + false, + "Force addition of inventory without prompting for confirmation.", + ) inventoryAddCmd.Flags().BoolVar(&dryRun, "dry-run", false, "Do not add inventory, only show what would be added.") inventoryCmd.AddCommand(inventoryRemoveCmd) - inventoryRemoveCmd.Flags().StringSliceVar(&ids, "sid", []string{}, "The Keyfactor Command ID of the certificate store(s) to remove inventory from.") - inventoryRemoveCmd.Flags().StringSliceVar(&clients, "client", []string{}, "Remove certificate(s) from all stores of specific client machine(s).") - inventoryRemoveCmd.Flags().StringSliceVar(&types, "store-type", []string{}, "Remove certificate(s) from all stores of specific store type(s).") - inventoryRemoveCmd.Flags().StringSliceVar(&containers, "container", []string{}, "Remove certificate(s) from all stores of specific container type(s).") - inventoryRemoveCmd.Flags().StringSliceVar(&thumbprints, "thumbprint", []string{}, "The thumbprint of the certificate(s) to remove from the store(s).") - inventoryRemoveCmd.Flags().StringSliceVar(&cIDs, "cid", []string{}, "The Keyfactor command certificate ID(s) of the certificate to remove from the store(s).") - inventoryRemoveCmd.Flags().StringSliceVar(&subjectNames, "cn", []string{}, "Subject name(s) of the certificate(s) to remove from the store(s).") - inventoryRemoveCmd.Flags().BoolVar(&all, "all-stores", false, "Remove the certificate(s) from all certificate stores.") - inventoryRemoveCmd.Flags().BoolVar(&force, "force", false, "Force removal of inventory without prompting for confirmation.") - inventoryRemoveCmd.Flags().BoolVar(&dryRun, "dry-run", false, "Do not remove inventory, only show what would be removed.") + inventoryRemoveCmd.Flags().StringSliceVar( + &ids, + "sid", + []string{}, + "The Keyfactor Command ID of the certificate store(s) to remove inventory from.", + ) + inventoryRemoveCmd.Flags().StringSliceVar( + &clients, + "client", + []string{}, + "Remove certificate(s) from all stores of specific client machine(s).", + ) + inventoryRemoveCmd.Flags().StringSliceVar( + &types, + "store-type", + []string{}, + "Remove certificate(s) from all stores of specific store type(s).", + ) + inventoryRemoveCmd.Flags().StringSliceVar( + &containers, + "container", + []string{}, + "Remove certificate(s) from all stores of specific container type(s).", + ) + inventoryRemoveCmd.Flags().StringSliceVar( + &thumbprints, + "thumbprint", + []string{}, + "The thumbprint of the certificate(s) to remove from the store(s).", + ) + inventoryRemoveCmd.Flags().StringSliceVar( + &cIDs, + "cid", + []string{}, + "The Keyfactor command certificate ID(s) of the certificate to remove from the store(s).", + ) + inventoryRemoveCmd.Flags().StringSliceVar( + &subjectNames, + "cn", + []string{}, + "Subject name(s) of the certificate(s) to remove from the store(s).", + ) + inventoryRemoveCmd.Flags().BoolVar( + &all, + "all-stores", + false, + "Remove the certificate(s) from all certificate stores.", + ) + inventoryRemoveCmd.Flags().BoolVar( + &force, + "force", + false, + "Force removal of inventory without prompting for confirmation.", + ) + inventoryRemoveCmd.Flags().BoolVar( + &dryRun, + "dry-run", + false, + "Do not remove inventory, only show what would be removed.", + ) inventoryCmd.AddCommand(inventoryShowCmd) - inventoryShowCmd.Flags().StringSliceVar(&ids, "sid", []string{}, "The Keyfactor Command ID of the certificate store(s) to retrieve inventory from.") - inventoryShowCmd.Flags().StringSliceVar(&clients, "client", []string{}, "Show certificate inventories for stores of specific client machine(s).") - inventoryShowCmd.Flags().StringSliceVar(&types, "store-type", []string{}, "Show certificate inventories for stores of specific store type(s).") - inventoryShowCmd.Flags().StringSliceVar(&containers, "container", []string{}, "Show certificate inventories for stores of specific container type(s).") + inventoryShowCmd.Flags().StringSliceVar( + &ids, + "sid", + []string{}, + "The Keyfactor Command ID of the certificate store(s) to retrieve inventory from.", + ) + inventoryShowCmd.Flags().StringSliceVar( + &clients, + "client", + []string{}, + "Show certificate inventories for stores of specific client machine(s).", + ) + inventoryShowCmd.Flags().StringSliceVar( + &types, + "store-type", + []string{}, + "Show certificate inventories for stores of specific store type(s).", + ) + inventoryShowCmd.Flags().StringSliceVar( + &containers, + "container", + []string{}, + "Show certificate inventories for stores of specific container type(s).", + ) } diff --git a/cmd/login.go b/cmd/login.go index 527c786..eea4638 100644 --- a/cmd/login.go +++ b/cmd/login.go @@ -15,18 +15,19 @@ package cmd import ( - "encoding/json" + "bufio" "fmt" - "github.com/Keyfactor/keyfactor-go-client-sdk/api/keyfactor" - "github.com/Keyfactor/keyfactor-go-client/v2/api" - "github.com/google/go-cmp/cmp" - "github.com/rs/zerolog/log" - "github.com/spf13/cobra" - "golang.org/x/crypto/ssh/terminal" + "io" + stdlog "log" "os" "path" "strings" - "syscall" + + "github.com/Keyfactor/keyfactor-auth-client-go/auth_providers" + "github.com/google/go-cmp/cmp" + "github.com/rs/zerolog/log" + "github.com/spf13/cobra" + "golang.org/x/term" ) var loginCmd = &cobra.Command{ @@ -35,14 +36,15 @@ var loginCmd = &cobra.Command{ SuggestFor: nil, Short: "User interactive login to Keyfactor. Stores the credentials in the config file '$HOME/.keyfactor/command_config.json'.", GroupID: "", - Long: `Will prompt the user for a kfcUsername and kfcPassword and then attempt to login to Keyfactor. + Long: `Will prompt the user for a Username and Password and then attempt to login to Keyfactor. You can provide the --config flag to specify a config file to use. If not provided, the default config file will be used. The default config file is located at $HOME/.keyfactor/command_config.json. -To prevent the prompt for kfcUsername and kfcPassword, use the --no-prompt flag. If this flag is provided then -the CLI will default to using the environment variables: KEYFACTOR_HOSTNAME, KEYFACTOR_USERNAME, -KEYFACTOR_PASSWORD and KEYFACTOR_DOMAIN. +To prevent the prompt for Username and Password, use the --no-prompt flag. If this flag is provided then +the CLI will default to using the environment variables. + +For more information on the environment variables review the docs: https://github.com/Keyfactor/kfutil/tree/main?tab=readme-ov-file#environmental-variables -WARNING: The 'username'' and 'password' will be stored in the config file in plain text at: +WARNING: This will write the environmental credentials to disk and will be stored in the config file in plain text at: '$HOME/.keyfactor/command_config.json.' `, Example: "", @@ -60,114 +62,204 @@ WARNING: The 'username'' and 'password' will be stored in the config file in pla PreRunE: nil, RunE: func(cmd *cobra.Command, args []string) error { log.Info().Msg("Running login command") - logGlobals() + cmd.SilenceUsage = true // expEnabled checks isExperimental := false debugErr := warnExperimentalFeature(expEnabled, isExperimental) if debugErr != nil { return debugErr } + stdlog.SetOutput(io.Discard) + informDebug(debugFlag) + logGlobals() - // CLI Logic + var authType string var ( - authConfigFileErrs []error - authConfig ConfigurationFile - authErr error + isValidConfig bool + kfcOAuth *auth_providers.CommandConfigOauth + kfcBasicAuth *auth_providers.CommandAuthConfigBasic ) - if profile == "" && configFile == "" { + log.Debug().Msg("calling getEnvConfig()") + envConfig, envErr := getServerConfigFromEnv() + if envErr == nil { + log.Debug().Msg("getEnvConfig() returned") + log.Info(). + Str("host", envConfig.Host). + Str("authType", envConfig.AuthType). + Msg("Login successful via environment variables") + outputResult(fmt.Sprintf("Login successful via environment variables to %s", envConfig.Host), outputFormat) + if profile == "" { + profile = "default" + } + if configFile == "" { + userHomeDir, hErr := prepHomeDir() + if hErr != nil { + log.Error().Err(hErr) + return hErr + } + configFile = path.Join(userHomeDir, DefaultConfigFileName) + } + envConfigFile := auth_providers.Config{ + Servers: map[string]auth_providers.Server{}, + } + envConfigFile.Servers[profile] = *envConfig + wcErr := writeConfigFile(&envConfigFile, configFile) + if wcErr != nil { + return wcErr + } + return nil + } + + log.Error().Err(envErr).Msg("Unable to authenticate via environment variables") + + if profile == "" { profile = "default" - log.Info().Msg("Using default profile") - // Check for environment variables - var authEnvErr []error - if noPrompt { - log.Info().Msg("Using environment variables for configuration data.") - // First try to auth with environment variables - authConfig, authEnvErr = authEnvVars(configFile, profile, true) // always save config file is login is called - if authEnvErr != nil { - for _, err := range authEnvErr { - log.Error().Err(err) - //outputError(err, false, "") + } + if configFile == "" { + userHomeDir, hErr := prepHomeDir() + if hErr != nil { + log.Error().Err(hErr) + return hErr + } + configFile = path.Join(userHomeDir, DefaultConfigFileName) + } + + log.Debug(). + Str("configFile", configFile). + Str("profile", profile). + Msg("call: auth_providers.ReadConfigFromJSON()") + aConfig, aErr := auth_providers.ReadConfigFromJSON(configFile) + if aErr != nil { + log.Error().Err(aErr) + //return aErr + } + log.Debug().Msg("auth_providers.ReadConfigFromJSON() returned") + + var outputServer *auth_providers.Server + + // Attempt to read existing configuration file + if aConfig != nil { + serverConfig, serverExists := aConfig.Servers[profile] + if serverExists { + // validate the config and prompt for missing values + authType = serverConfig.GetAuthType() + switch authType { + case "oauth": + oauthConfig, oErr := serverConfig.GetOAuthClientConfig() + if oErr != nil { + log.Error().Err(oErr) } - } - if !validConfigFileEntry(authConfig, profile) { - // Attempt to auth with config file - log.Info().Msgf("Attempting to authenticate via config '%s' profile.", profile) - authConfig, authEnvErr = authConfigFile(configFile, profile, "", noPrompt, true) // always save config file is login is called - if authEnvErr != nil { - // Print out the error messages - for _, err := range authEnvErr { - log.Error().Err(err) - } + if oauthConfig == nil { + log.Error().Msg("OAuth configuration is empty") + break } - if !validConfigFileEntry(authConfig, profile) { - errMsg := fmt.Errorf("unable to authenticate with environment variables or config file, please review setup") - //log.Fatal(errMsg) - log.Error().Err(errMsg) - return errMsg + vErr := oauthConfig.ValidateAuthConfig() + if vErr == nil { + isValidConfig = true + } else { + log.Error(). + Err(vErr). + Msg("invalid OAuth configuration") + //break } - } - } else { - // Try user interactive login - log.Info().Msg("Attempting to implicitly authenticate via environment variables.") - log.Debug().Str("configFile", configFile). - Str("profile", profile). - Bool("noPrompt", noPrompt). - Msg("call: authEnvVars()") - authConfig, _ = authEnvVars(configFile, profile, false) // Silently load via env what you can - if !validConfigFileEntry(authConfig, profile) || !noPrompt { - log.Info().Msg("Attempting to authenticate via user interactive login.") - existingAuth := authConfig.Servers[profile] - log.Debug().Str("hostname", existingAuth.Hostname). - Str("username", existingAuth.Username). - Str("password", hashSecretValue(existingAuth.Password)). - Str("domain", existingAuth.Domain). - Str("apiPath", existingAuth.APIPath). - Msg("call: authInteractive()") - authConfig, authErr = authInteractive(existingAuth.Hostname, existingAuth.Username, existingAuth.Password, existingAuth.Domain, existingAuth.APIPath, profile, !noPrompt, true, configFile) - log.Debug().Msg("authInteractive() returned") - if authErr != nil { - log.Error().Err(authErr) - return authErr + outputServer = oauthConfig.GetServerConfig() + kfcOAuth = oauthConfig + case "basic": + basicConfig, bErr := serverConfig.GetBasicAuthClientConfig() + if bErr != nil { + log.Error().Err(bErr) + } + if basicConfig == nil { + log.Error().Msg("Basic Auth configuration is empty") + break } + vErr := basicConfig.ValidateAuthConfig() + if vErr == nil { + isValidConfig = true + } else { + log.Error(). + Err(vErr). + Msg("invalid Basic Auth configuration") + //break + } + outputServer = basicConfig.GetServerConfig() + kfcBasicAuth = basicConfig + default: + log.Error(). + Str("authType", authType). + Str("profile", profile). + Str("configFile", configFile). + Msg("unable to determine auth type from configuration") } } - //fmt.Println(fmt.Sprintf("Login successful!")) - outputResult(SuccessfulAuthMsg, outputFormat) - return nil - } else if configFile != "" || profile != "" { - // Attempt to auth with config file - log.Info().Msgf("Attempting to authenticate via config '%s' profile.", profile) - log.Debug().Str("configFile", configFile). - Str("profile", profile). - Bool("noPrompt", noPrompt). - Msg("call: authConfigFile()") - authConfig, authConfigFileErrs = authConfigFile(configFile, profile, "", noPrompt, true) // always save config file is login is called - log.Debug().Msg("authConfigFile() returned") - if authConfigFileErrs != nil { - // Print out the error messages - for _, err := range authConfigFileErrs { - //log.Println(err) - log.Error().Err(err) - outputError(err, false, outputFormat) - } + } + + if !noPrompt { + log.Debug().Msg("prompting for interactive login") + iConfig, iErr := authInteractive(outputServer, profile, !noPrompt, true, configFile) + if iErr != nil { + log.Error().Err(iErr) + return iErr } - if !validConfigFileEntry(authConfig, profile) && !noPrompt { - //Attempt to auth with user interactive login - log.Info().Msg("Attempting to authenticate via user interactive login.") - authEntry := authConfig.Servers[profile] - authConfig, authErr = authInteractive(authEntry.Hostname, authEntry.Username, authEntry.Password, authEntry.Domain, authEntry.APIPath, profile, false, true, configFile) - if authErr != nil { - //log.Println(authErr) - log.Error().Err(authErr) - outputResult(FailedAuthMsg, outputFormat) - return authErr + iServer, iServerExists := iConfig.Servers[profile] + if iServerExists { + authType = iServer.GetAuthType() + switch authType { + case "oauth": + kfcOAuth, _ = iServer.GetOAuthClientConfig() + outputServer = kfcOAuth.GetServerConfig() + oErr := kfcOAuth.ValidateAuthConfig() + if oErr == nil { + isValidConfig = true + } else { + log.Error().Err(oErr) + } + case "basic": + kfcBasicAuth, _ = iServer.GetBasicAuthClientConfig() + outputServer = kfcBasicAuth.GetServerConfig() + bErr := kfcBasicAuth.ValidateAuthConfig() + if bErr == nil { + isValidConfig = true + } else { + log.Error().Err(bErr) + } + default: + log.Error().Msg("unable to determine auth type from interactive configuration") } } - outputResult(SuccessfulAuthMsg, outputFormat) - return nil } + + if !isValidConfig { + log.Debug().Msg("prompting for interactive login") + return fmt.Errorf("unable to determine valid configuration") + } + + if authType == "oauth" { + log.Debug().Msg("attempting to authenticate via OAuth") + aErr := kfcOAuth.Authenticate() + if aErr != nil { + log.Error().Err(aErr) + return aErr + } + } else if authType == "basic" { + log.Debug().Msg("attempting to authenticate via Basic Auth") + aErr := kfcBasicAuth.Authenticate() + if aErr != nil { + log.Error().Err(aErr) + //outputError(aErr, true, outputFormat) + return aErr + } + } + + log.Info(). + Str("profile", profile). + Str("configFile", configFile). + Str("host", outputServer.Host). + Str("authType", authType). + Msg("Login successful") + outputResult(fmt.Sprintf("Login successful to %s", outputServer.Host), outputFormat) return nil }, PostRun: nil, @@ -191,27 +283,39 @@ func init() { RootCmd.AddCommand(loginCmd) } -func validConfig(hostname string, username string, password string, domain string) bool { - if hostname == "" || username == "" || password == "" { - return false - } - if domain == "" && (!strings.Contains(username, "@") || !strings.Contains(username, "\\")) { - return false +func writeConfigFile(configFile *auth_providers.Config, configPath string) error { + existingConfig, exErr := auth_providers.ReadConfigFromJSON(configPath) + if exErr != nil { + log.Error().Err(exErr) + wErr := auth_providers.WriteConfigToJSON(configPath, configFile) + if wErr != nil { + log.Error().Err(wErr) + return wErr + } + log.Info().Str("configPath", configPath).Msg("Configuration file written") + return nil } - return true -} -func validConfigFileEntry(configFile ConfigurationFile, profile string) bool { - if profile == "" { - profile = "default" + // Compare the existing config with the new config + if cmp.Equal(existingConfig, configFile) { + log.Info().Msg("Configuration file unchanged") + return nil } - if configFile.Servers[profile].Hostname == "" || configFile.Servers[profile].Username == "" || configFile.Servers[profile].Password == "" { - return false + + // Merge the existing config with the new config + mergedConfig, mErr := auth_providers.MergeConfigFromFile(configPath, configFile) + if mErr != nil { + log.Error().Err(mErr) + return mErr } - if configFile.Servers[profile].Domain == "" && (!strings.Contains(configFile.Servers[profile].Username, "@") || !strings.Contains(configFile.Servers[profile].Username, "\\")) { - return false + wErr := auth_providers.WriteConfigToJSON(configPath, mergedConfig) + if wErr != nil { + log.Error().Err(wErr) + return wErr } - return true + log.Info().Str("configPath", configPath).Msg("Configuration file updated") + return nil + } func getDomainFromUsername(username string) string { @@ -223,41 +327,15 @@ func getDomainFromUsername(username string) string { return "" } -func createConfigFile(hostname string, username string, password string, domain string, apiPath string, profileName string) ConfigurationFile { - output := ConfigurationFile{ - Servers: map[string]ConfigurationFileEntry{ - profileName: { - Hostname: hostname, - Username: username, - Password: password, - Domain: domain, - APIPath: apiPath, - }, - }, - } - return output -} - -func createAuthConfig(hostname string, username string, password string, domain string, apiPath string) api.AuthConfig { - output := api.AuthConfig{ - Hostname: hostname, - Username: username, - Password: password, - Domain: domain, - APIPath: apiPath, - } - return output -} - -func createAuthConfigFromConfigFile(configFileEntry ConfigurationFileEntry) api.AuthConfig { - output := api.AuthConfig{ - Hostname: configFileEntry.Hostname, - Username: configFileEntry.Username, - Password: configFileEntry.Password, - Domain: configFileEntry.Domain, - APIPath: configFileEntry.APIPath, +func promptForInteractiveYesNo(parameterName string) bool { + var input string + fmt.Printf("%s [y/n]: \n", parameterName) + _, err := fmt.Scanln(&input) + _ = handleInteractiveError(err, parameterName) + if strings.ToLower(input) == "y" || strings.ToLower(input) == "yes" { + return true } - return output + return false } func promptForInteractiveParameter(parameterName string, defaultValue string) string { @@ -284,16 +362,39 @@ func promptForInteractivePassword(parameterName string, defaultValue string) str if defaultValue != "" { passwordFill = "********" } - //log.Println("[DEBUG] kfcPassword: " + defaultValue) - fmt.Printf("Enter %s [%s]: \n", parameterName, passwordFill) - bytePassword, _ := terminal.ReadPassword(int(syscall.Stdin)) - // check if bytePassword is empty if so the return the default value - if len(bytePassword) == 0 { + fmt.Printf("Enter %s [%s]: ", parameterName, passwordFill) + + var password string + + // Check if we're in a terminal environment + if term.IsTerminal(int(os.Stdin.Fd())) { + // Terminal mode: read password securely + bytePassword, err := term.ReadPassword(int(os.Stdin.Fd())) + fmt.Println("") // for newline after password input + if err != nil { + fmt.Println("\nError reading password:", err) + return defaultValue + } + password = string(bytePassword) + } else { + // Non-terminal mode: read password as plain text + reader := bufio.NewReader(os.Stdin) + input, err := reader.ReadString('\n') + if err != nil { + fmt.Println("\nError reading password:", err) + return defaultValue + } + password = input + } + + // Trim newline and check if password is empty; if so, return default + if len(password) > 0 { + password = strings.Trim(password, "\n") + } + if password == "" { return defaultValue } - password := string(bytePassword) - fmt.Println("") return password } @@ -309,66 +410,167 @@ func handleInteractiveError(err error, parameterName string) error { return nil } -func saveConfigFile(configFile ConfigurationFile, configPath string, profileName string) (ConfigurationFile, error) { - if profileName == "" { - profileName = "default" +func authInteractive( + serverConf *auth_providers.Server, + profileName string, + forcePrompt bool, + saveConfig bool, + configPath string, +) (auth_providers.Config, error) { + if serverConf == nil { + serverConf = &auth_providers.Server{} + } + + if serverConf.Host == "" || forcePrompt { + serverConf.Host = promptForInteractiveParameter("Keyfactor Command HostName", serverConf.Host) + } + if serverConf.AuthType == "" || forcePrompt { + for { + serverConf.AuthType = promptForInteractiveParameter( + "Keyfactor Command AuthType [basic,oauth]", + serverConf.AuthType, + ) + if serverConf.AuthType == "oauth" || serverConf.AuthType == "basic" { + break + } else { + fmt.Println("Invalid auth type. Valid auth types are: oauth, basic") + } + } } + if serverConf.AuthType == "basic" { + if serverConf.Username == "" || forcePrompt { + serverConf.Username = promptForInteractiveParameter("Keyfactor Command Username", serverConf.Username) + } + if serverConf.Password == "" || forcePrompt { + serverConf.Password = promptForInteractivePassword("Keyfactor Command Password", serverConf.Password) + } + if serverConf.Domain == "" || forcePrompt { + userDomain := getDomainFromUsername(serverConf.Username) + if userDomain == "" { + serverConf.Domain = promptForInteractiveParameter("Keyfactor Command AD Domain", serverConf.Domain) + } else { + serverConf.Domain = userDomain + } + } + } else if serverConf.AuthType == "oauth" { + if serverConf.AccessToken == "" || forcePrompt { + log.Debug().Msg("prompting for OAuth access token") + serverConf.AccessToken = promptForInteractiveParameter( + "Keyfactor Command OAuth Access Token (to use client ID and secret, leave blank)", + serverConf.AccessToken, + ) + } + if serverConf.AccessToken == "" { + log.Debug().Msg("no oauth access token provided") + if serverConf.ClientID == "" || forcePrompt { + log.Debug(). + Str("serverConf.ClientID", serverConf.ClientID). + Msg("prompting for OAuth client ID") + serverConf.ClientID = promptForInteractiveParameter( + "Keyfactor Command OAuth Client ID", + serverConf.ClientID, + ) + } + if serverConf.ClientSecret == "" || forcePrompt { + log.Debug().Msg("prompting for OAuth client secret") + serverConf.ClientSecret = promptForInteractivePassword( + "Keyfactor Command OAuth Client Secret", + serverConf.ClientSecret, + ) + } - configurationErr := createOrUpdateConfigurationFile(configFile.Servers[profileName], profileName, configPath) - if configurationErr != nil { - //log.Fatal("[ERROR] Unable to save configuration file to disk: ", configurationErr) - log.Error().Err(configurationErr) - return configFile, configurationErr + if serverConf.OAuthTokenUrl == "" || forcePrompt { + log.Debug(). + Str("serverConf.OAuthTokenUrl", serverConf.OAuthTokenUrl). + Msg("prompting for OAuth token URL") + serverConf.OAuthTokenUrl = promptForInteractiveParameter( + "Keyfactor Command OAuth Token URL", + serverConf.OAuthTokenUrl, + ) + } + if len(serverConf.Scopes) == 0 || forcePrompt { + log.Debug(). + Strs("serverConf.Scopes", serverConf.Scopes). + Msg("prompting for OAuth scopes") + scopesCsv := promptForInteractiveParameter( + "OAuth Scopes", + strings.Join(serverConf.Scopes, ","), + ) + if scopesCsv != "" && scopesCsv != "," { + serverConf.Scopes = strings.Split(scopesCsv, ",") + } + } + if serverConf.Audience == "" || forcePrompt { + log.Debug().Msg("prompting for OAuth audience") + serverConf.Audience = promptForInteractiveParameter( + "OAuth Audience", + serverConf.Audience, + ) + } + } else { + log.Debug(). + Str("serverConf.AccessToken", hashSecretValue(serverConf.AccessToken)). + Msg("using provided OAuth access token") + } } - loadedConfig, loadErr := loadConfigurationFile(configPath, true) - if loadErr != nil { - //log.Fatal("[ERROR] Unable to load configuration file after save: ", loadErr) - log.Error().Err(loadErr) - return configFile, loadErr + + if serverConf.APIPath == "" || forcePrompt { + log.Debug(). + Str("serverConf.APIPath", serverConf.APIPath). + Msg("prompting for API path") + serverConf.APIPath = promptForInteractiveParameter("Keyfactor Command API path", serverConf.APIPath) } - return loadedConfig, nil -} -func authInteractive(hostname string, username string, password string, domain string, apiPath string, profileName string, forcePrompt bool, saveConfig bool, configPath string) (ConfigurationFile, error) { - if hostname == "" || forcePrompt { - hostname = promptForInteractiveParameter("Keyfactor Command kfcHostName", hostname) + if serverConf.CACertPath == "" || forcePrompt { + log.Debug(). + Str("serverConf.CACertPath", serverConf.CACertPath). + Msg("prompting for CA cert path") + serverConf.CACertPath = promptForInteractiveParameter("Keyfactor Command CA Cert Path", serverConf.CACertPath) } - if username == "" || forcePrompt { - username = promptForInteractiveParameter("Keyfactor Command kfcUsername", username) + if !serverConf.SkipTLSVerify || forcePrompt { + log.Debug(). + Bool("serverConf.SkipTLSVerify", serverConf.SkipTLSVerify). + Msg("prompting for Skip TLS Verify") + serverConf.SkipTLSVerify = promptForInteractiveParameter( + "Keyfactor Command Skip TLS Verify [true,false]", + fmt.Sprintf("%t", serverConf.SkipTLSVerify), + ) == "true" } - if password == "" || forcePrompt { - password = promptForInteractivePassword("Keyfactor Command kfcPassword", password) + + if profileName == "" { + profileName = auth_providers.DefaultConfigProfile } - if domain == "" || forcePrompt { - domain = getDomainFromUsername(username) - if domain == "" { - domain = promptForInteractiveParameter("Keyfactor Command AD kfcDomain", domain) + if configPath == "" { + log.Debug().Msg("configPath is empty, calling prepHomeDir()") + userHomeDir, hErr := prepHomeDir() + if hErr != nil { + //log.Println("[ERROR] Unable to create home directory: ", hErr) + log.Error().Err(hErr) + return auth_providers.Config{}, hErr } - } - if apiPath == "" || forcePrompt { - apiPath = promptForInteractiveParameter("Keyfactor Command API path", apiPath) + configPath = path.Join(userHomeDir, DefaultConfigFileName) } - if profileName == "" { - profileName = "default" + confFile := auth_providers.Config{ + Servers: map[string]auth_providers.Server{}, } - - confFile := createConfigFile(hostname, username, password, domain, apiPath, profileName) + confFile.Servers[profileName] = *serverConf if saveConfig { - savedConfigFile, saveErr := saveConfigFile(confFile, configPath, profileName) + log.Debug().Bool("saveConfig", saveConfig).Msg("calling writeConfigFile()") + saveErr := writeConfigFile(&confFile, configPath) if saveErr != nil { //log.Println("[ERROR] Unable to save configuration file to disk: ", saveErr) log.Error().Err(saveErr) return confFile, saveErr } - return savedConfigFile, nil } + log.Debug().Msg("authInteractive() returning") return confFile, nil } func prepHomeDir() (string, error) { - log.Debug().Msg("prepHomeDir() called") + log.Debug().Msg(fmt.Sprintf("%s prepHomeDir()", DebugFuncEnter)) // Set up home directory config userHomeDir, hErr := os.UserHomeDir() @@ -382,834 +584,21 @@ func prepHomeDir() (string, error) { } else { userHomeDir = fmt.Sprintf("%s/.keyfactor", userHomeDir) } - //log.Println("[DEBUG] Configuration directory: ", userHomeDir) + log.Debug().Str("userHomeDir", userHomeDir).Msg("Configuration directory") _, err := os.Stat(userHomeDir) if os.IsNotExist(err) { errDir := os.MkdirAll(userHomeDir, 0700) if errDir != nil { - fmt.Println("Unable to create login config file. ", errDir) - log.Printf("[ERROR] creating directory: %s", errDir) + outputError(fmt.Errorf("error creating home directory: %v", errDir), false, outputFormat) + log.Error().Err(errDir) + log.Debug().Msg(fmt.Sprintf("%s prepHomeDir() returning", DebugFuncExit)) return userHomeDir, errDir } } + log.Debug(). + Str("userHomeDir", userHomeDir). + Msg(fmt.Sprintf("%s prepHomeDir() returning", DebugFuncExit)) return userHomeDir, hErr } - -func loadConfigFileData(profileName string, configPath string, noPrompt bool, configurationFile ConfigurationFile) (string, string, string, string, string) { - log.Debug().Str("profileName", profileName). - Str("configPath", configPath). - Bool("noPrompt", noPrompt). - Msg("loadConfigFileData() called") - - log.Debug().Msg("calling authEnvVars()") - envConfig, _ := authEnvVars(configPath, profileName, false) //Load env vars first - log.Debug().Msg("authEnvVars() returned") - - // Check if profileName exists in config file - log.Debug().Str("profileName", profileName).Msg("checking if profileName exists in config file") - configProfile, profileExists := configurationFile.Servers[profileName] - if !profileExists { - log.Error(). - Str("profileName", profileName). - Msg("profileName does not exist in config file") - if noPrompt { // TODO: If profile doesn't exist how does this work? - log.Error().Msg("noPrompt is set, unable to prompt for missing profileName") - return envConfig.Servers[profileName].Hostname, envConfig.Servers[profileName].Username, envConfig.Servers[profileName].Password, envConfig.Servers[profileName].Domain, envConfig.Servers[profileName].APIPath - } - } else { - //log.Println("[INFO] Using kfutil config profileName: ", profileName) - log.Info().Str("profileName", profileName).Msg("Using kfutil config profileName") - hostName := configProfile.Hostname - userName := configProfile.Username - password := configProfile.Password - domain := configProfile.Domain - apiPath := configProfile.APIPath - authProvider := configProfile.AuthProvider - log.Debug().Str("hostName", hostName). - Str("userName", userName). - Str("password", hashSecretValue(password)). - Str("domain", domain). - Str("apiPath", apiPath). - Str("authProvider", fmt.Sprintf("%+v", authProvider)). - Msg("configProfile values") - - if authProvider.Type != "" && authProvider.Parameters != nil { - // cALL authViaProviderParams - log.Info().Str("authProvider.Type", authProvider.Type). - Msg("Authenticating via authProvider") - log.Debug().Msg("calling authViaProviderParams()") - authConfig, authErr := authViaProviderParams(&authProvider) - if authErr != nil { - //log.Println("[ERROR] Unable to authenticate via provider: ", authErr) - log.Error().Err(authErr).Msg("Unable to authenticate via provider") - return "", "", "", "", "" - } - - // Check if authProvider profile is set - if authProvider.Profile == "" { - authProvider.Profile = "default" - log.Info().Msg("Using default authProvider profile") - } - - hostName = authConfig.Servers[authProvider.Profile].Hostname - userName = authConfig.Servers[authProvider.Profile].Username - password = authConfig.Servers[authProvider.Profile].Password - domain = authConfig.Servers[authProvider.Profile].Domain - apiPath = authConfig.Servers[authProvider.Profile].APIPath - - log.Debug().Str("hostName", hostName). - Str("userName", userName). - Str("password", hashSecretValue(password)). - Str("domain", domain). - Str("apiPath", apiPath). - Msg("authConfig values") - - return hostName, userName, password, domain, apiPath - } - - if hostName == "" && envConfig.Servers[profileName].Hostname != "" { - hostName = envConfig.Servers[profileName].Hostname - } - if userName == "" && envConfig.Servers[profileName].Username != "" { - userName = envConfig.Servers[profileName].Username - } - if password == "" && envConfig.Servers[profileName].Password != "" { - password = envConfig.Servers[profileName].Password - } - if domain == "" && envConfig.Servers[profileName].Domain != "" { - domain = envConfig.Servers[profileName].Domain - } - if apiPath == "" && envConfig.Servers[profileName].APIPath != "" { - apiPath = envConfig.Servers[profileName].APIPath - } - - log.Debug().Str("hostName", hostName). - Str("userName", userName). - Str("password", hashSecretValue(password)). - Str("domain", domain). - Str("apiPath", apiPath). - Msg("configProfile values") - - return hostName, userName, password, domain, apiPath - } - log.Error().Msg("Unable to load config file data") - return "", "", "", "", "" -} - -func authViaProvider() (*api.Client, error) { - var clientAuth api.AuthConfig - var commandConfig ConfigurationFile - if providerType != "" { - log.Info().Str("providerType", providerType).Msg("attempting to auth via auth provider") - var providerConfig AuthProvider - if providerProfile == "" { - log.Info().Str("providerProfile", providerProfile).Msg("auth provider profile not set, defaulting to 'default'") - providerProfile = "default" - } - - providerConfig = AuthProvider{ - Type: providerType, - Profile: providerProfile, - Parameters: nil, - } - - if configFile == "" { - homeDir, hdErr := os.UserHomeDir() - if hdErr != nil { - homeDir, hdErr = os.Getwd() - if hdErr != nil { - homeDir = "." // Default to current directory - } - } - configFile = path.Join(homeDir, ".keyfactor", DefaultConfigFileName) - } - - // Load config file - log.Debug().Str("configFile", configFile).Msg("configFile is set, loading config file") - log.Debug().Msg("calling loadConfigurationFile()") - configurationFile, cErr := loadConfigurationFile(configFile, true) - log.Debug().Msg("loadConfigurationFile() returned") - if cErr != nil { - log.Error().Err(cErr).Msg("unable to load provider config file") - return nil, cErr - } - // look for profile in config file - log.Debug().Str("profile", profile). - Str("providerProfile", providerProfile). - Msg("checking if providerProfile exists in config file") - - providerConfigEntry, providerProfileExists := configurationFile.Servers[providerProfile] - if !providerProfileExists { - log.Error().Str("providerProfile", providerProfile).Msg("providerProfile does not exist in config file") - return nil, fmt.Errorf("providerProfile '%s' does not exist in config file", providerProfile) - } - params := providerConfigEntry.AuthProvider.Parameters - if params == nil { - log.Error().Msg("providerProfile parameters are empty") - return nil, fmt.Errorf("providerProfile '%s' parameters are empty", providerProfile) - } - providerConfig.Parameters = params - - log.Debug().Str("providerConfig.Type", providerConfig.Type). - Msg("call: authViaProviderParams()") - pvConfig, pErr := authViaProviderParams(&providerConfig) - log.Debug().Msg("returned: authViaProviderParams()") - if pErr != nil { - log.Error().Err(pErr). - Str("providerConfig.Type", providerConfig.Type). - Str("providerConfig.Profile", providerConfig.Profile). - Msg("unable to auth via provider") - return nil, pErr - } - log.Trace().Interface("pvConfig", pvConfig).Send() - - commandConfig = pvConfig - clientAuth.Username = commandConfig.Servers[providerProfile].Username - clientAuth.Password = commandConfig.Servers[providerProfile].Password - clientAuth.Domain = commandConfig.Servers[providerProfile].Domain - clientAuth.Hostname = commandConfig.Servers[providerProfile].Hostname - clientAuth.APIPath = commandConfig.Servers[providerProfile].APIPath - - log.Debug().Str("clientAuth.Username", clientAuth.Username). - Str("clientAuth.Password", hashSecretValue(clientAuth.Password)). - Str("clientAuth.Domain", clientAuth.Domain). - Str("clientAuth.Hostname", clientAuth.Hostname). - Str("clientAuth.APIPath", clientAuth.APIPath). - Msg("Client authentication params") - - log.Debug().Msg("call: api.NewKeyfactorClient()") - c, err := api.NewKeyfactorClient(&clientAuth) - log.Debug().Msg("complete: api.NewKeyfactorClient()") - - if err != nil { - //fmt.Printf("Error connecting to Keyfactor: %s\n", err) - outputError(err, true, "text") - //log.Fatalf("[ERROR] creating Keyfactor client: %s", err) - return nil, fmt.Errorf("unable to create Keyfactor Command client: %s", err) - } - log.Info().Msg("Keyfactor Command client created") - log.Debug().Str("flagAuthProvider", providerType). - Str("providerProfile", providerProfile). - Msg("returning from provider auth") - return c, nil - } - return nil, fmt.Errorf("unable to auth via provider, providerType is empty") -} - -func authViaProviderGenClient() (*keyfactor.APIClient, error) { - var commandConfig ConfigurationFile - if providerType != "" { - log.Info().Str("providerType", providerType).Msg("attempting to auth via auth provider") - var providerConfig AuthProvider - if providerProfile == "" { - log.Info().Str("providerProfile", providerProfile).Msg("auth provider profile not set, defaulting to 'default'") - providerProfile = "default" - } - - providerConfig = AuthProvider{ - Type: providerType, - Profile: providerProfile, - Parameters: nil, - } - - if configFile == "" { - homeDir, hdErr := os.UserHomeDir() - if hdErr != nil { - homeDir, hdErr = os.Getwd() - if hdErr != nil { - homeDir = "." // Default to current directory - } - } - configFile = path.Join(homeDir, ".keyfactor", DefaultConfigFileName) - } - - // Load config file - log.Debug().Str("configFile", configFile).Msg("configFile is set, loading config file") - log.Debug().Msg("calling loadConfigurationFile()") - configurationFile, cErr := loadConfigurationFile(configFile, true) - log.Debug().Msg("loadConfigurationFile() returned") - if cErr != nil { - log.Error().Err(cErr).Msg("unable to load provider config file") - return nil, cErr - } - // look for profile in config file - log.Debug().Str("profile", profile). - Str("providerProfile", providerProfile). - Msg("checking if providerProfile exists in config file") - - providerConfigEntry, providerProfileExists := configurationFile.Servers[providerProfile] - if !providerProfileExists { - log.Error().Str("providerProfile", providerProfile).Msg("providerProfile does not exist in config file") - return nil, fmt.Errorf("providerProfile '%s' does not exist in config file", providerProfile) - } - params := providerConfigEntry.AuthProvider.Parameters - if params == nil { - log.Error().Msg("providerProfile parameters are empty") - return nil, fmt.Errorf("providerProfile '%s' parameters are empty", providerProfile) - } - providerConfig.Parameters = params - - log.Debug().Str("providerConfig.Type", providerConfig.Type). - Msg("call: authViaProviderParams()") - pvConfig, pErr := authViaProviderParams(&providerConfig) - log.Debug().Msg("returned: authViaProviderParams()") - if pErr != nil { - log.Error().Err(pErr). - Str("providerConfig.Type", providerConfig.Type). - Str("providerConfig.Profile", providerConfig.Profile). - Msg("unable to auth via provider") - return nil, pErr - } - log.Trace().Interface("pvConfig", pvConfig).Send() - - commandConfig = pvConfig - sdkClientConfig := make(map[string]string) - sdkClientConfig["host"] = commandConfig.Servers[providerProfile].Hostname - sdkClientConfig["username"] = commandConfig.Servers[providerProfile].Username - sdkClientConfig["password"] = commandConfig.Servers[providerProfile].Password - sdkClientConfig["domain"] = commandConfig.Servers[providerProfile].Domain - sdkClientConfig["apiPath"] = commandConfig.Servers[providerProfile].APIPath - - log.Debug().Str("clientAuth.Username", sdkClientConfig["username"]). - Str("clientAuth.Password", hashSecretValue(sdkClientConfig["password"])). - Str("clientAuth.Domain", sdkClientConfig["domain"]). - Str("clientAuth.Hostname", sdkClientConfig["host"]). - Str("clientAuth.APIPath", sdkClientConfig["apiPath"]). - Msg("Client authentication params") - - log.Debug().Msg("call: api.NewKeyfactorClient()") - configuration := keyfactor.NewConfiguration(sdkClientConfig) - c := keyfactor.NewAPIClient(configuration) - log.Debug().Msg("complete: api.NewKeyfactorClient()") - log.Info().Msg("Keyfactor Command client created") - log.Debug().Str("flagAuthProvider", providerType). - Str("providerProfile", providerProfile). - Msg("returning from provider auth") - return c, nil - } - return nil, fmt.Errorf("unable to auth via provider, providerType is empty") -} - -func authViaProviderParams(providerConfig *AuthProvider) (ConfigurationFile, error) { - - pt := providerConfig.Type - // First check if provider type and provider config are not empty - if pt == "" || providerConfig == nil { - return ConfigurationFile{}, fmt.Errorf("provider type and provider config cannot be empty") - } - - // Check if auth provider is valid - if !validAuthProvider(pt) { - return ConfigurationFile{}, fmt.Errorf("invalid auth provider type '%s'. Valid auth providers are: %v", pt, ValidAuthProviders) - } - - // Check if provider type matches requested provider type - switch pt { - case "azure-id", "azid", "az-id", "azureid": - //load provider config params into AuthProviderAzureIdParams struct - log.Debug().Msg("authenticating via azure-id provider") - var providerParams AuthProviderAzureIDParams - log.Debug().Msg("marshalling providerConfig.Parameters") - paramsJson, _ := json.Marshal(providerConfig.Parameters) - log.Debug().Msg("unmarshalling providerParams") - jsonErr := json.Unmarshal(paramsJson, &providerParams) - if jsonErr != nil { - log.Error().Err(jsonErr).Msg("unable to unmarshal providerParams") - return ConfigurationFile{}, jsonErr - } - - // Check if required params are set - if providerParams.SecretName == "" || providerParams.AzureVaultName == "" { - return ConfigurationFile{}, fmt.Errorf("provider params secret_name and vault_name are required") - } - log.Debug().Msgf("providerParams: %+v", providerParams) - return providerParams.authenticate() - case "az-cli", "azcli", "azure-cli", "azurecli": - log.Debug().Msg("authenticating via azure-cli provider") - break - default: - //log.Println("[ERROR] invalid auth provider type") - log.Error().Msg("invalid auth provider type") - break - } - return ConfigurationFile{}, fmt.Errorf("invalid auth provider type '%s'. Valid auth providers are: %v", pt, ValidAuthProviders) -} - -func validAuthProvider(providerType string) bool { - log.Debug().Str("providerType", providerType).Msg("validAuthProvider() called") - if providerType == "" { - return true // default to kfcUsername/kfcPassword - } - for _, validProvider := range ValidAuthProviders { - if validProvider == providerType { - log.Debug().Str("providerType", providerType).Msg("is valid auth provider type") - return true - } - } - log.Error().Str("providerType", providerType).Msg("is not valid auth provider type") - return false -} - -func authConfigFile(configPath string, profileName string, authProviderProfile string, noPrompt bool, saveConfig bool) (ConfigurationFile, []error) { - var configurationFile ConfigurationFile - var ( - hostName string - userName string - password string - domain string - apiPath string - - //hostSet bool - //userSet bool - //passSet bool - //domainSet bool - //apiPathSet bool - //profileSet bool - ) - - //log.Println("[DEBUG] Using profileName: ", profileName) - log.Debug().Str("profileName", profileName). - Msg("Using profileName") - - if configPath == "" { - log.Debug().Msg("configPath is empty, setting to default") - log.Debug().Msg("calling prepHomeDir()") - userHomeDir, _ := prepHomeDir() - log.Debug().Msg("prepHomeDir() returned") - - configPath = fmt.Sprintf("%s/%s", userHomeDir, DefaultConfigFileName) - log.Debug().Str("configPath", configPath).Msg("configPath set") - } - configurationFile, _ = loadConfigurationFile(configPath, noPrompt) - - if configurationFile.Servers == nil { - configurationFile.Servers = make(map[string]ConfigurationFileEntry) - } - if profileName == "" { - log.Debug().Msg("profileName is empty, setting to default") - profileName = "default" - } - - log.Debug().Msg("calling loadConfigFileData()") - hostName, userName, password, domain, apiPath = loadConfigFileData(profileName, configPath, noPrompt, configurationFile) - log.Debug().Msg("loadConfigFileData() returned") - - log.Debug().Str("hostName", hostName). - Str("userName", userName). - Str("password", hashSecretValue(password)). - Str("domain", domain). - Str("apiPath", apiPath). - Msg("loadConfigFileData() values") - - log.Debug().Msg("calling createConfigFile()") - confFile := createConfigFile(hostName, userName, password, domain, apiPath, profileName) - log.Debug().Msg("createConfigFile() returned") - - if saveConfig { - log.Info().Str("configPath", configPath). - Str("profileName", profileName). - Msg("Saving configuration file") - log.Debug().Msg("calling saveConfigFile()") - savedConfigFile, saveErr := saveConfigFile(confFile, configPath, profileName) - log.Debug().Msg("saveConfigFile() returned") - if saveErr != nil { - log.Error().Err(saveErr) - return confFile, []error{saveErr} - } - log.Info().Str("configPath", configPath). - Str("profileName", profileName). - Msg("Configuration file saved") - - log.Debug().Msg("returning savedConfigFile") - return savedConfigFile, nil - } - - configurationFile.Servers[profileName] = confFile.Servers[profileName] - return configurationFile, nil -} - -func authEnvProvider(authProvider *AuthProvider, configProfile string) (ConfigurationFile, []error) { - //log.Println(fmt.Sprintf("[INFO] authenticating with auth provider '%s' params from environment variables", authProvider.Type)) - log.Info().Str("authProvider.Type", authProvider.Type).Msg("authenticating with auth provider params from environment variables") - - if configProfile == "" { - log.Debug().Msg("configProfile is empty, setting to default") - configProfile = "default" - } - // attempt to cast authProvider.Parameters to string - authProviderParams, ok := authProvider.Parameters.(string) - if !ok { - //log.Println("[ERROR] unable to cast authProvider.Parameters to string") - log.Error().Msg("unable to cast authProvider.Parameters to string") - return ConfigurationFile{}, []error{fmt.Errorf("invalid configuration, unable to cast authProvider.Parameters to string")} - } - - if strings.HasPrefix(authProviderParams, "{") && strings.HasSuffix(authProviderParams, "}") { - // authProviderParams is a json string - //log.Println("[DEBUG] authProviderParams is a json string") - log.Debug().Msg("authProviderParams is a json string") - var providerParams interface{} - //log.Println("[DEBUG] converting authProviderParams to unescaped json") - log.Debug().Msg("converting authProviderParams to unescaped json") - jsonErr := json.Unmarshal([]byte(authProviderParams), &providerParams) - if jsonErr != nil { - //log.Println("[ERROR] unable to unmarshal authProviderParams: ", jsonErr) - log.Error().Err(jsonErr).Msg("unable to unmarshal authProviderParams") - return ConfigurationFile{}, []error{jsonErr} - } - authProvider.Parameters = providerParams - } else { - // attempt to read as json file path - //log.Println("[DEBUG] authProviderParams is a json file path") - log.Debug().Msg("authProviderParams is a json file path") - var providerParams interface{} - var providerConfigFile ConfigurationFile - var authProviderConfig AuthProvider - //log.Println("[DEBUG] opening authProviderParams file ", authProviderParams) - log.Debug().Str("authProviderParams", authProviderParams).Msg("opening authProviderParams file") - - jsonFile, jsonFileErr := os.Open(authProviderParams) - if jsonFileErr != nil { - //log.Println("[ERROR] unable to open authProviderParams file: ", jsonFileErr) - log.Error().Err(jsonFileErr).Msg("unable to open authProviderParams file") - return ConfigurationFile{}, []error{jsonFileErr} - } - defer jsonFile.Close() - //log.Println(fmt.Sprintf("[DEBUG] reading authProviderParams file %s as bytes", authProviderParams)) - log.Debug().Str("authProviderParams", authProviderParams).Msg("reading authProviderParams file as bytes") - jsonBytes, jsonBytesErr := os.ReadFile(authProviderParams) - if jsonBytesErr != nil { - //log.Println("[ERROR] unable to read authProviderParams file: ", jsonBytesErr) - log.Error().Err(jsonBytesErr).Msg("unable to read authProviderParams file") - return ConfigurationFile{}, []error{jsonBytesErr} - } - //log.Println("[DEBUG] converting authProviderParams to unescaped json") - log.Debug().Msg("converting authProviderParams to unescaped json") - jsonErr := json.Unmarshal(jsonBytes, &providerParams) - if jsonErr != nil { - //log.Println("[ERROR] unable to unmarshal authProviderParams: ", jsonErr) - log.Error().Err(jsonErr).Msg("unable to unmarshal authProviderParams") - return ConfigurationFile{}, []error{jsonErr} - } - - //Check if provider params is a configuration file - //log.Println("[DEBUG] checking if authProviderParams is a configuration file") - log.Debug().Msg("checking if authProviderParams is a configuration file") - jsonErr = json.Unmarshal(jsonBytes, &providerConfigFile) - if jsonErr == nil && providerConfigFile.Servers != nil { - // lookup params based on configProfile - //log.Println("[DEBUG] authProviderParams is a configuration file") - log.Debug().Msg("authProviderParams is a configuration file") - // check to see if profile exists in config file - if _, isConfigFile := providerConfigFile.Servers[configProfile]; isConfigFile { - //log.Println(fmt.Sprintf("[DEBUG] profile '%s' found in authProviderParams file", configProfile)) - log.Debug().Str("configProfile", configProfile).Msg("profile found in authProviderParams file") - providerParams = providerConfigFile.Servers[configProfile] - // check if providerParams is a ConfigurationFileEntry - if _, isConfigFileEntry := providerParams.(ConfigurationFileEntry); !isConfigFileEntry { - //log.Println("[ERROR] unable to cast providerParams to ConfigurationFileEntry") - log.Error().Msg("unable to cast providerParams to ConfigurationFileEntry") - return ConfigurationFile{}, []error{fmt.Errorf("invalid configuration, unable to cast providerParams to ConfigurationFileEntry")} - } - // set providerParams to ConfigurationFileEntry.AuthProvider.Parameters - providerParams = providerConfigFile.Servers[configProfile].AuthProvider.Parameters - } else { - //log.Println(fmt.Sprintf("[DEBUG] profile '%s' not found in authProviderParams file", configProfile)) - log.Debug().Str("configProfile", configProfile).Msg("profile not found in authProviderParams file") - return ConfigurationFile{}, []error{fmt.Errorf("profile '%s' not found in authProviderParams file", configProfile)} - } - } else { - //check if provider params is an AuthProvider - //log.Println("[DEBUG] checking if authProviderParams is an AuthProvider") - log.Debug().Msg("checking if authProviderParams is an AuthProvider") - - //log.Println("[DEBUG] converting authProviderParams to unescaped json") - log.Debug().Msg("converting authProviderParams to unescaped json") - - //check if providerParams is a map[string]interface{} - if _, isMap := providerParams.(map[string]interface{}); isMap { - //check if 'auth_provider' key exists and if it does convert to json bytes - log.Debug().Msg("authProviderParams is a map") - if _, isAuthProvider := providerParams.(map[string]interface{})["auth_provider"]; isAuthProvider { - //log.Println("[DEBUG] authProviderParams is a map[string]interface{}") - //log.Println("[DEBUG] converting authProviderParams to unescaped json") - log.Debug().Msg("authProviderParams is a map[string]interface{}") - log.Debug().Msg("converting authProviderParams to unescaped json") - jsonBytes, jsonBytesErr = json.Marshal(providerParams.(map[string]interface{})["auth_provider"]) - if jsonBytesErr != nil { - //log.Println("[ERROR] unable to marshal authProviderParams: ", jsonBytesErr) - log.Error().Err(jsonBytesErr).Msg("unable to marshal authProviderParams") - return ConfigurationFile{}, []error{jsonBytesErr} - } - } - } - - jsonErr = json.Unmarshal(jsonBytes, &authProviderConfig) - if jsonErr == nil && authProviderConfig.Type != "" && authProviderConfig.Parameters != nil { - //log.Println("[DEBUG] authProviderParams is an AuthProvider") - log.Debug().Msg("authProviderParams is an AuthProvider") - providerParams = authProviderConfig.Parameters - } - } - authProvider.Parameters = providerParams - } - //log.Println("[INFO] Attempting to fetch kfutil creds from auth provider ", authProvider) - log.Info().Str("authProvider", fmt.Sprintf("%+v", authProvider)).Msg("Attempting to fetch kfutil creds from auth provider") - configFile, authErr := authViaProviderParams(authProvider) - if authErr != nil { - //log.Println("[ERROR] Unable to authenticate via provider: ", authErr) - log.Error().Err(authErr).Msg("Unable to authenticate via provider") - return ConfigurationFile{}, []error{authErr} - } - //log.Println("[INFO] Successfully retrieved kfutil creds via auth provider") - log.Info().Msg("Successfully retrieved kfutil creds via auth provider") - return configFile, nil -} - -func authEnvVars(configPath string, profileName string, saveConfig bool) (ConfigurationFile, []error) { - hostname, hostSet := os.LookupEnv("KEYFACTOR_HOSTNAME") - username, userSet := os.LookupEnv("KEYFACTOR_USERNAME") - password, passSet := os.LookupEnv("KEYFACTOR_PASSWORD") - domain, domainSet := os.LookupEnv("KEYFACTOR_DOMAIN") - apiPath, apiPathSet := os.LookupEnv("KEYFACTOR_API_PATH") - envProfileName, _ := os.LookupEnv("KFUTIL_PROFILE") - authProviderType, _ := os.LookupEnv("KFUTIL_AUTH_PROVIDER_TYPE") - authProviderProfile, _ := os.LookupEnv("KUTIL_AUTH_PROVIDER_PROFILE") - authProviderParams, _ := os.LookupEnv("KFUTIL_AUTH_PROVIDER_PARAMS") // this is a json string or a json file path - - if authProviderType != "" || authProviderParams != "" { - if authProviderParams == "" { - authProviderParams = fmt.Sprintf("%s/.keyfactor/%s", os.Getenv("HOME"), DefaultConfigFileName) - } - if authProviderProfile == "" { - authProviderProfile = "default" - } - authProvider := AuthProvider{ - Type: authProviderType, - Profile: authProviderProfile, - Parameters: authProviderParams, - } - //check if authProviderParams is a json string or a json file path - return authEnvProvider(&authProvider, profileName) - } - - if profileName == "" && envProfileName != "" { - profileName = envProfileName - } else if profileName == "" { - profileName = "default" - } - - log.Printf("KEYFACTOR_HOSTNAME: %s\n", hostname) - log.Printf("KEYFACTOR_USERNAME: %s\n", username) - log.Printf("KEYFACTOR_DOMAIN: %s\n", domain) - - if domain == "" && username != "" { - domain = getDomainFromUsername(username) - } - - var outputErr []error - if !hostSet { - outputErr = append(outputErr, fmt.Errorf("KEYFACTOR_HOSTNAME environment variable not set. Please set the KEYFACTOR_HOSTNAME environment variable")) - } - if !userSet { - outputErr = append(outputErr, fmt.Errorf("KEYFACTOR_USERNAME environment variable not set. Please set the KEYFACTOR_USERNAME environment variable")) - } - if !passSet { - outputErr = append(outputErr, fmt.Errorf("KEYFACTOR_PASSWORD environment variable not set. Please set the KEYFACTOR_PASSWORD environment variable")) - } - if !domainSet { - outputErr = append(outputErr, fmt.Errorf("KEYFACTOR_DOMAIN environment variable not set. Please set the KEYFACTOR_DOMAIN environment variable")) - } - if !apiPathSet { - apiPath = DefaultAPIPath - apiPathSet = true - } - - if !hostSet && !userSet && !passSet && !domainSet { - return ConfigurationFile{}, outputErr - } - - confFile := createConfigFile(hostname, username, password, domain, apiPath, profileName) - - if len(outputErr) > 0 { - return confFile, outputErr - } - - if saveConfig { - savedConfigFile, saveErr := saveConfigFile(confFile, configPath, profileName) - if saveErr != nil { - return confFile, []error{saveErr} - } - return savedConfigFile, nil - } - return confFile, nil -} - -func createOrUpdateConfigurationFile(cfgFile ConfigurationFileEntry, profile string, configPath string) error { - //log.Println("[INFO] Creating or updating configuration file") - log.Info().Str("configPath", configPath). - Str("profile", profile). - Msg("Creating or updating configuration file") - //log.Println("[DEBUG] configuration file path: ", configPath) - - if len(profile) == 0 { - log.Debug().Msg("profile is empty, setting to default") - profile = "default" - } - //check if configPath exists - if configPath == "" { - defaultDir, _ := os.UserHomeDir() - configPath = path.Join(defaultDir, ".keyfactor", DefaultConfigFileName) - //log.Println("[WARN] no config path provided. Using '" + configPath + "'.") - log.Debug().Str("configPath", configPath).Msg("no config path provided using default") - } - confFileExists, fileErr := os.Stat(configPath) - if fileErr != nil { - //log.Println("[WARN] ", fileErr) - log.Error().Err(fileErr).Msg("error checking if config file exists") - } - - existingConfig, _ := loadConfigurationFile(configPath, true) - if len(existingConfig.Servers) > 0 { - // check if the config name already exists - if _, ok := existingConfig.Servers[profile]; ok { - //log.Println(fmt.Sprintf("[WARN] config name '%s' already exists. Overwriting existing config.", profile)) - log.Info(). - Str("profile", profile). - Msg("config profile already exists, overwriting existing config") - //log.Println(fmt.Sprintf("[DEBUG] existing config: %v", existingConfig.Servers[profile])) - log.Debug().Str("profile", profile). - Str("existingConfig", fmt.Sprintf("%+v", existingConfig.Servers[profile])). - Msg("existing config") - //log.Println(fmt.Sprintf("[DEBUG] new config: %v", cfgFile)) - // print out the diff between the two configs - diff := cmp.Diff(existingConfig.Servers[profile], cfgFile) - if len(diff) == 0 && confFileExists != nil { - //log.Println("[DEBUG] no configuration changes detected") - log.Debug().Msg("no configuration changes detected") - return nil - } - //log.Println(fmt.Sprintf("[DEBUG] diff: %s", diff)) - log.Debug().Str("diff", diff).Msg("config diff") - } - existingConfig.Servers[profile] = cfgFile - } else { - //log.Println(fmt.Sprintf("[INFO] adding new config name '%s'", profile)) - log.Info().Str("profile", profile).Msg("adding new profile") - existingConfig.Servers = make(map[string]ConfigurationFileEntry) - existingConfig.Servers[profile] = cfgFile - } - - //log.Println("[DEBUG] kfcfg entry: ", cfgFile) - log.Debug().Str("cfgFile", fmt.Sprintf("%+v", cfgFile)).Msg("kfcfg entry") - - f, fErr := os.OpenFile(fmt.Sprintf("%s", configPath), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0600) - defer f.Close() - if fErr != nil { - msg := fmt.Errorf("unable to create command configuration file %s: %s", configPath, fErr) - outputError(msg, false, outputFormat) - log.Error().Err(fErr).Msg("unable to create command configuration file") - return fErr - } - - // convert existingConfig to json - jsonData, jsErr := json.MarshalIndent(existingConfig, "", " ") - if jsErr != nil { - //fmt.Println("Unable to read kfcfg file due to invalid format. ", jsErr) - outputError(jsErr, false, outputFormat) - //log.Println("[ERROR] marshalling kfcfg file: ", jsErr) - log.Error().Err(jsErr).Msg("marshalling command config file") - return jsErr - } - _, enErr := f.Write(jsonData) - if enErr != nil { - //fmt.Println("Unable to read kfcfg file due to invalid format. ", enErr) - outputError(enErr, false, outputFormat) - //log.Println("[ERROR] encoding kfcfg file: ", enErr) - log.Error().Err(enErr).Msg("encoding command config file") - return enErr - } - return nil -} - -func loadConfigurationFile(filePath string, silent bool) (ConfigurationFile, error) { - log.Debug().Str("filePath", filePath).Msg("loadConfigurationFile() called") - //data := ConfigurationFile{Servers: make(map[string]ConfigurationFileEntry)} - data := ConfigurationFile{} - if filePath == "" { - log.Debug().Msg("filePath is empty, setting to default") - defaultDir, _ := os.UserHomeDir() - filePath = path.Join(defaultDir, ".keyfactor", DefaultConfigFileName) - } - log.Debug().Str("filePath", filePath).Msg("filePath set") - - // attempt to make the directory if it doesn't exist - dirPath := path.Dir(filePath) - if _, dirErr := os.Stat(dirPath); os.IsNotExist(dirErr) { - //log.Println("[DEBUG] config directory does not exist, creating: ", dirPath) - log.Debug().Str("dirPath", dirPath).Msg("config directory does not exist, creating") - err := os.MkdirAll(dirPath, 0700) - if err != nil { - //log.Println("[ERROR] creating config directory: ", err) - log.Error().Err(err).Msg("creating config directory") - return data, err - } - return data, nil // return empty data since the directory didn't exist the file won't exist - } - - // check if file exists - if _, fileErr := os.Stat(filePath); os.IsNotExist(fileErr) { - //log.Println("[DEBUG] config file does not exist: ", filePath) - log.Debug().Str("filePath", filePath).Msg("config file does not exist") - return data, nil // return empty data since the file doesn't exist - } - - f, rFErr := os.ReadFile(filePath) - if rFErr != nil { - if !silent { - //fmt.Println(fmt.Sprintf("Unable to read config file '%s'.", rFErr)) - outputError(rFErr, true, outputFormat) - //log.Fatal("[FATAL] Error reading config file: ", rFErr) - log.Error().Err(rFErr).Msg("error reading config file") - } - return data, rFErr - } - - // Try to unmarshal as a single entry first - var singleEntry ConfigurationFileEntry - sjErr := json.Unmarshal(f, &singleEntry) - if sjErr != nil { - //log.Println(fmt.Sprintf("[DEBUG] config file '%s' is a not single entry, will attempt to parse as v1 config file", filePath)) - log.Debug().Str("filePath", filePath).Msg("config file is not a single entry, will attempt to parse as v1 config file") - } else if (singleEntry != ConfigurationFileEntry{}) { - // if we successfully unmarshalled a single entry, add it to the map as the default entry - //log.Println(fmt.Sprintf("[DEBUG] config file '%s' is a single entry, adding to map", filePath)) - log.Debug().Str("filePath", filePath).Msg("config file is a single entry, adding to map") - data.Servers = make(map[string]ConfigurationFileEntry) - data.Servers["default"] = singleEntry - return data, nil - } - - jErr := json.Unmarshal(f, &data) - if jErr != nil { - //fmt.Println("Unable to read config file due to invalid format. ", jErr) - //log.Println("[ERROR] decoding config file: ", jErr) - log.Error().Err(jErr).Msg("decoding config file") - return data, jErr - } - - return data, nil -} - -func createAuthConfigFromParams(hostname string, username string, password string, domain string, apiPath string) *api.AuthConfig { - output := api.AuthConfig{ - Hostname: hostname, - Username: username, - Password: password, - Domain: domain, - APIPath: apiPath, - } - return &output -} diff --git a/cmd/login_test.go b/cmd/login_test.go index 6fda933..c9dad5d 100644 --- a/cmd/login_test.go +++ b/cmd/login_test.go @@ -17,12 +17,15 @@ package cmd import ( "encoding/json" "fmt" - "github.com/joho/godotenv" - "github.com/stretchr/testify/assert" "os" - "os/user" + "path" "path/filepath" + "strings" "testing" + + "github.com/Keyfactor/keyfactor-auth-client-go/auth_providers" + "github.com/joho/godotenv" + "github.com/stretchr/testify/assert" ) func Test_LoginHelpCmd(t *testing.T) { @@ -49,30 +52,87 @@ func Test_LoginHelpCmd(t *testing.T) { } } -func Test_LoginCmdNoPrompt(t *testing.T) { - // Get the current user's information - currentUser, err := user.Current() - if err != nil { - fmt.Println("Error:", err) - return +func Test_LoginCmdEnvOnly(t *testing.T) { + homeDir, _ := os.UserHomeDir() + // Define the path to the file in the user's home directory + configFilePath := filepath.Join(homeDir, auth_providers.DefaultConfigFilePath) + testEnvCredsOnly(t, configFilePath, false) + +} + +func Test_LoginFileNoPrompt(t *testing.T) { + homeDir, _ := os.UserHomeDir() + configFilePath := filepath.Join(homeDir, auth_providers.DefaultConfigFilePath) + // Test logging in w/o args and w/o prompt + username, password, domain := exportBasicEnvVariables() + clientId, clientSecret, tokenUrl := exportOAuthEnvVariables() + envUsername, envPassword, envDomain := exportBasicEnvVariables() + envClientId, envClientSecret, envTokenUrl := exportOAuthEnvVariables() + os.Setenv(auth_providers.EnvKeyfactorSkipVerify, "true") + if (envUsername == "" || envPassword == "" || envDomain == "") && (envClientId == "" || envClientSecret == "" || envTokenUrl == "") { + t.Errorf("Environment variables are not set") + t.FailNow() + } + existingConfig, exErr := auth_providers.ReadConfigFromJSON(configFilePath) + if exErr != nil { + t.Errorf("Error reading existing config: %s", exErr) + t.FailNow() } + defer func() { + //restore config file + if existingConfig != nil { + wErr := auth_providers.WriteConfigToJSON(configFilePath, existingConfig) + if wErr != nil { + t.Errorf("Error writing existing config: %s", wErr) + t.FailNow() + } + } + }() + t.Run( + fmt.Sprintf("login no prompt from file"), func(t *testing.T) { + unsetOAuthEnvVariables() + unsetBasicEnvVariables() + defer setOAuthEnvVariables(clientId, clientSecret, tokenUrl) + defer setBasicEnvVariables(username, password, domain) - // Define the path to the file in the user's home directory - filePath := filepath.Join(currentUser.HomeDir, ".keyfactor/command_config.json") - testEnvCredsOnly(t, filePath, false) - testLoginNoPrompt(t, filePath) + npfCmd := RootCmd + npfCmd.SetArgs([]string{"login", "--no-prompt"}) + + output := captureOutput( + func() { + noPromptErr := npfCmd.Execute() + if noPromptErr != nil { + t.Errorf(noPromptErr.Error()) + t.FailNow() + } + }, + ) + t.Logf("output: %s", output) + assert.Contains(t, output, "Login successful to") + testConfigExists(t, configFilePath, true) + testConfigValid(t) + //testLogout(t) + }, + ) } func Test_LoginCmdConfigParams(t *testing.T) { testCmd := RootCmd // test - testCmd.SetArgs([]string{"stores", "list", "--exp", "--config", "$HOME/.keyfactor/extra_config.json"}) - output := captureOutput(func() { - err := testCmd.Execute() - assert.NoError(t, err) - }) + testCmd.SetArgs( + []string{ + "stores", "list", "--exp", "--config", "$HOME/.keyfactor/extra_config.json", "--profile", + "oauth", + }, + ) + output := captureOutput( + func() { + err := testCmd.Execute() + assert.NoError(t, err) + }, + ) t.Logf("output: %s", output) - var stores []string + var stores []map[string]interface{} if err := json.Unmarshal([]byte(output), &stores); err != nil { t.Fatalf("Error unmarshalling JSON: %v", err) } @@ -81,35 +141,48 @@ func Test_LoginCmdConfigParams(t *testing.T) { assert.True(t, len(stores) >= 0, "Expected non-empty list of stores") } -func testLogout(t *testing.T) { - t.Run(fmt.Sprintf("Logout"), func(t *testing.T) { - testCmd := RootCmd - // test - testCmd.SetArgs([]string{"logout"}) - output := captureOutput(func() { - err := testCmd.Execute() - assert.NoError(t, err) - }) - t.Logf("output: %s", output) - - assert.Contains(t, output, "Logged out successfully!") - - // Get the current user's information - currentUser, err := user.Current() - if err != nil { - fmt.Println("Error:", err) - return - } +func testLogout(t *testing.T, configFilePath string, restoreConfig bool) { + t.Run( + fmt.Sprintf("Logout"), func(t *testing.T) { + testCmd := RootCmd + //store current config in memory + if restoreConfig { + homeDir, _ := os.UserHomeDir() + configFilePath := path.Join(homeDir, auth_providers.DefaultConfigFilePath) + existingConfig, exErr := auth_providers.ReadConfigFromJSON(configFilePath) + defer func() { + //restore config file + if existingConfig != nil { + wErr := auth_providers.WriteConfigToJSON(configFilePath, existingConfig) + if wErr != nil { + t.Errorf("Error writing existing config: %s", wErr) + t.FailNow() + } + } + }() + if exErr != nil { + t.Errorf("Error reading existing config: %s", exErr) + t.FailNow() + } + } + testCmd.SetArgs([]string{"logout"}) + output := captureOutput( + func() { + err := testCmd.Execute() + assert.NoError(t, err) + }, + ) + t.Logf("output: %s", output) - // Define the path to the file in the user's home directory - filePath := filepath.Join(currentUser.HomeDir, ".keyfactor/command_config.json") - _, err = os.Stat(filePath) + assert.Contains(t, output, "Logged out successfully!") - // Test that the config file does not exist - if _, fErr := os.Stat(filePath); !os.IsNotExist(fErr) { - t.Errorf("Config file %s still exists, please remove", filePath) - } - }) + // Test that the config file does not exist + if _, fErr := os.Stat(configFile); !os.IsNotExist(fErr) { + t.Errorf("Config file %s still exists, please remove", configFilePath) + t.FailNow() + } + }, + ) } @@ -120,26 +193,36 @@ func testConfigValid(t *testing.T) { //t.Logf("envUsername: %s", envUsername) //t.Logf("envPassword: %s", envPassword) t.Logf("Attempting to run `store-types list`") - t.Run(fmt.Sprintf("List store types"), func(t *testing.T) { - testCmd := RootCmd - t.Log("Setting args") - testCmd.SetArgs([]string{"store-types", "list"}) - t.Logf("args: %v", testCmd.Args) - t.Log("Capturing output") - output := captureOutput(func() { - tErr := testCmd.Execute() - assert.NoError(t, tErr) - }) - t.Logf("output: %s", output) - - var storeTypes []map[string]interface{} - if err := json.Unmarshal([]byte(output), &storeTypes); err != nil { - t.Fatalf("Error unmarshalling JSON: %v", err) - } + t.Run( + fmt.Sprintf("List store types"), func(t *testing.T) { + skipVerify := os.Getenv(auth_providers.EnvKeyfactorSkipVerify) + t.Logf("skipVerify: %s", skipVerify) + testCmd := RootCmd + t.Log("Setting args") + testCmd.SetArgs([]string{"store-types", "list"}) + t.Logf("args: %v", testCmd.Args) + t.Log("Capturing output") + output := captureOutput( + func() { + tErr := testCmd.Execute() + assert.NoError(t, tErr) + if tErr != nil { + t.Errorf("Error running command: %s", tErr) + t.FailNow() + } + }, + ) + t.Logf("output: %s", output) + + var storeTypes []map[string]interface{} + if err := json.Unmarshal([]byte(output), &storeTypes); err != nil { + t.Fatalf("Error unmarshalling JSON: %v", err) + } - // Verify that the length of the response is greater than 0 - assert.True(t, len(storeTypes) >= 0, "Expected non-empty list of store types") - }) + // Verify that the length of the response is greater than 0 + assert.True(t, len(storeTypes) >= 0, "Expected non-empty list of store types") + }, + ) } func testConfigExists(t *testing.T, filePath string, allowExist bool) { @@ -149,77 +232,173 @@ func testConfigExists(t *testing.T, filePath string, allowExist bool) { } else { testName = "Config file does not exist" } - t.Run(fmt.Sprintf(testName), func(t *testing.T) { - _, fErr := os.Stat(filePath) - if allowExist { - assert.True(t, allowExist && fErr == nil) - // Load the config file from JSON to map[string]interface{} - fileConfigJSON := make(map[string]interface{}) - file, _ := os.Open(filePath) - defer file.Close() - decoder := json.NewDecoder(file) - err := decoder.Decode(&fileConfigJSON) - if err != nil { - t.Errorf("Error decoding config file: %s", err) - } - // Verify that the config file has the correct keys - assert.Contains(t, fileConfigJSON, "servers") - kfcServers, ok := fileConfigJSON["servers"].(map[string]interface{}) - if !ok { - t.Errorf("Error decoding config file: %s", err) - assert.False(t, ok, "Error decoding config file") - return + t.Run( + fmt.Sprintf(testName), func(t *testing.T) { + _, fErr := os.Stat(filePath) + if allowExist { + assert.True(t, allowExist && fErr == nil) + // Load the config file from JSON to map[string]interface{} + fileConfigJSON := make(map[string]interface{}) + file, _ := os.Open(filePath) + defer file.Close() + decoder := json.NewDecoder(file) + err := decoder.Decode(&fileConfigJSON) + if err != nil { + t.Errorf("Error decoding config file: %s", err) + } + // Verify that the config file has the correct keys + assert.Contains(t, fileConfigJSON, "servers") + kfcServers, ok := fileConfigJSON["servers"].(map[string]interface{}) + if !ok { + t.Errorf("Error decoding config file: %s", err) + assert.False(t, ok, "Error decoding config file") + return + } + assert.Contains(t, kfcServers, "default") + defaultServer := kfcServers["default"].(map[string]interface{}) + assert.Contains(t, defaultServer, "host") + confUsername, uOk := defaultServer["username"] + confPassword, pOk := defaultServer["password"] + confDomain, _ := defaultServer["domain"] + confClientID, cOk := defaultServer["client_id"] + confClientSecret, sOk := defaultServer["client_secret"] + confTokenUrl, tOk := defaultServer["token_url"] + t.Logf("confUsername: %s", confUsername) + t.Logf("confPassword: %s", hashSecretValue(fmt.Sprintf("%v", confPassword))) + t.Logf("confDomain: %s", confDomain) + t.Logf("confClientID: %s", confClientID) + t.Logf("confClientSecret: %s", hashSecretValue(fmt.Sprintf("%v", confClientSecret))) + t.Logf("confTokenUrl: %s", confTokenUrl) + + if (uOk && pOk) || (cOk && sOk && tOk) { + assert.True(t, uOk && pOk || cOk && sOk && tOk) + } else { + t.Errorf("Config file does not contain valid credentials") + } + } else { + assert.True(t, !allowExist && os.IsNotExist(fErr)) } - assert.Contains(t, kfcServers, "default") - defaultServer := kfcServers["default"].(map[string]interface{}) - assert.Contains(t, defaultServer, "host") - assert.Contains(t, defaultServer, "kfcUsername") - assert.Contains(t, defaultServer, "kfcPassword") - } else { - assert.True(t, !allowExist && os.IsNotExist(fErr)) - } - }) + }, + ) } -func testEnvCredsOnly(t *testing.T, filePath string, allowExist bool) { - t.Run(fmt.Sprintf("Auth w/ env ONLY"), func(t *testing.T) { - // Load .env file - err := godotenv.Load("../.env_1040") - if err != nil { - t.Errorf("Error loading .env file") - } - testLogout(t) - testConfigExists(t, filePath, false) - testConfigValid(t) - }) +func testEnvCredsOnly(t *testing.T, configFilePath string, allowExist bool) { + t.Run( + fmt.Sprintf("Auth w/ env ONLY"), func(t *testing.T) { + envUsername, envPassword, envDomain := exportBasicEnvVariables() + envClientId, envClientSecret, envTokenUrl := exportOAuthEnvVariables() + os.Setenv(auth_providers.EnvKeyfactorSkipVerify, "true") + if (envUsername == "" || envPassword == "" || envDomain == "") && (envClientId == "" || envClientSecret == "" || envTokenUrl == "") { + t.Errorf("Environment variables are not set") + t.FailNow() + } + if configFilePath == "" { + homeDir, _ := os.UserHomeDir() + configFilePath = path.Join(homeDir, auth_providers.DefaultConfigFilePath) + } + + existingConfig, exErr := auth_providers.ReadConfigFromJSON(configFilePath) + if exErr != nil { + t.Errorf("Error reading existing config: %s", exErr) + t.FailNow() + } + defer func() { + //restore config file + if existingConfig != nil { + wErr := auth_providers.WriteConfigToJSON(configFilePath, existingConfig) + if wErr != nil { + t.Errorf("Error writing existing config: %s", wErr) + t.FailNow() + } + } + }() + testLogout(t, configFilePath, false) + testConfigExists(t, configFilePath, false) + testConfigValid(t) + }, + ) } func testEnvCredsToFile(t *testing.T, filePath string, allowExist bool) { - t.Run(fmt.Sprintf("Auth w/ env ONLY"), func(t *testing.T) { - // Load .env file - err := godotenv.Load("../.env_1040") - if err != nil { - t.Errorf("Error loading .env file") - } - testLogout(t) - testConfigExists(t, filePath, false) - testConfigValid(t) - }) + t.Run( + fmt.Sprintf("Auth w/ env ONLY"), func(t *testing.T) { + // Load .env file + err := godotenv.Load("../.env_1040") + if err != nil { + t.Errorf("Error loading .env file") + } + testLogout(t, filePath, false) + testConfigExists(t, filePath, false) + testConfigValid(t) + }, + ) } -func testLoginNoPrompt(t *testing.T, filePath string) { - // Test logging in w/o args and w/o prompt - t.Run(fmt.Sprintf("login no prompt"), func(t *testing.T) { - testCmd := RootCmd - testCmd.SetArgs([]string{"login", "--no-prompt"}) - noPromptErr := testCmd.Execute() - if noPromptErr != nil { - t.Errorf("RootCmd() = %v, shouldNotPass %v", noPromptErr, true) +// setOAuthEnvVariables sets the oAuth environment variables +func setOAuthEnvVariables(clientId, clientSecret, tokenUrl string) { + os.Setenv(auth_providers.EnvKeyfactorClientID, clientId) + os.Setenv(auth_providers.EnvKeyfactorClientSecret, clientSecret) + os.Setenv(auth_providers.EnvKeyfactorAuthTokenURL, tokenUrl) +} + +func exportEnvVarsWithPrefix(prefix string) map[string]string { + result := make(map[string]string) + for _, env := range os.Environ() { + // Each environment variable is in the format "KEY=VALUE" + pair := strings.SplitN(env, "=", 2) + key := pair[0] + value := pair[1] + + if strings.HasPrefix(key, prefix) { + result[key] = value } - testConfigExists(t, filePath, true) - os.Unsetenv("KEYFACTOR_USERNAME") - os.Unsetenv("KEYFACTOR_PASSWORD") - testConfigValid(t) - //testLogout(t) - }) + } + return result +} + +// exportOAuthEnvVariables sets the oAuth environment variables +func exportOAuthEnvVariables() (string, string, string) { + clientId := os.Getenv(auth_providers.EnvKeyfactorClientID) + clientSecret := os.Getenv(auth_providers.EnvKeyfactorClientSecret) + tokenUrl := os.Getenv(auth_providers.EnvKeyfactorAuthTokenURL) + return clientId, clientSecret, tokenUrl +} + +// unsetOAuthEnvVariables unsets the oAuth environment variables +func unsetOAuthEnvVariables() { + os.Unsetenv(auth_providers.EnvKeyfactorClientID) + os.Unsetenv(auth_providers.EnvKeyfactorClientSecret) + os.Unsetenv(auth_providers.EnvKeyfactorAuthTokenURL) + //os.Unsetenv(auth_providers.EnvKeyfactorSkipVerify) + //os.Unsetenv(auth_providers.EnvKeyfactorConfigFile) + //os.Unsetenv(auth_providers.EnvKeyfactorAuthProfile) + //os.Unsetenv(auth_providers.EnvKeyfactorCACert) + //os.Unsetenv(auth_providers.EnvAuthCACert) + //os.Unsetenv(auth_providers.EnvKeyfactorHostName) + //os.Unsetenv(auth_providers.EnvKeyfactorUsername) + //os.Unsetenv(auth_providers.EnvKeyfactorPassword) + //os.Unsetenv(auth_providers.EnvKeyfactorDomain) + +} + +// setBasicEnvVariables sets the basic environment variables +func setBasicEnvVariables(username, password, domain string) { + os.Setenv(auth_providers.EnvKeyfactorUsername, username) + os.Setenv(auth_providers.EnvKeyfactorPassword, password) + os.Setenv(auth_providers.EnvKeyfactorDomain, domain) +} + +// exportBasicEnvVariables sets the basic environment variables +func exportBasicEnvVariables() (string, string, string) { + username := os.Getenv(auth_providers.EnvKeyfactorUsername) + password := os.Getenv(auth_providers.EnvKeyfactorPassword) + domain := os.Getenv(auth_providers.EnvKeyfactorDomain) + return username, password, domain +} + +// unsetBasicEnvVariables unsets the basic environment variables +func unsetBasicEnvVariables() { + os.Unsetenv(auth_providers.EnvKeyfactorUsername) + os.Unsetenv(auth_providers.EnvKeyfactorPassword) + os.Unsetenv(auth_providers.EnvKeyfactorDomain) } diff --git a/cmd/logout.go b/cmd/logout.go index 21c9ec3..f203824 100644 --- a/cmd/logout.go +++ b/cmd/logout.go @@ -16,40 +16,276 @@ package cmd import ( "fmt" - "log" + "io" + stdlog "log" "os" + "github.com/Keyfactor/keyfactor-auth-client-go/auth_providers" + "github.com/rs/zerolog/log" "github.com/spf13/cobra" ) // logoutCmd represents the logout command var logoutCmd = &cobra.Command{ Use: "logout", - Short: "Removes the credentials file '$HOME/.keyfactor/command_config.json'.", - Long: `Removes the credentials file '$HOME/.keyfactor/command_config.json'.`, - Run: func(cmd *cobra.Command, args []string) { - // Global flags - debugFlag, _ := cmd.Flags().GetBool("debugFlag") - //configFile, _ := cmd.Flags().GetString("config") - //noPrompt, _ := cmd.Flags().GetBool("no-prompt") - //profile, _ := cmd.Flags().GetString("profile") - - debugModeEnabled := checkDebug(debugFlag) - log.Println("Debug mode enabled: ", debugModeEnabled) - err := os.Remove(fmt.Sprintf("%s/.keyfactor/%s", os.Getenv("HOME"), DefaultConfigFileName)) - if err != nil { - if os.IsNotExist(err) { - fmt.Println("Config file does not exist.") - fmt.Println("Logged out successfully!") - return + Short: "Unsets environment variables and removes the stored credentials file.", + Long: `Unsets environment variables and removes the stored credentials file.`, + RunE: func(cmd *cobra.Command, args []string) error { + log.Info().Msg("Running logout command") + cmd.SilenceUsage = true + // expEnabled checks + isExperimental := false + debugErr := warnExperimentalFeature(expEnabled, isExperimental) + if debugErr != nil { + return debugErr + } + stdlog.SetOutput(io.Discard) + informDebug(debugFlag) + + logGlobals() + + var configFilePath string + if configFile == "" { + // check if environment variables for config file is set + if os.Getenv(auth_providers.EnvKeyfactorConfigFile) != "" { + configFilePath = os.Getenv(auth_providers.EnvKeyfactorConfigFile) + } else { + userHomeDir, err := os.UserHomeDir() + if err != nil { + userHomeDir, err = os.Getwd() + if err != nil { + userHomeDir = "." + } + } + configFilePath = fmt.Sprintf("%s/%s", userHomeDir, auth_providers.DefaultConfigFilePath) + } + } else { + configFilePath = configFile + } + + //log.Info().Msg("Running logout command for environment variables") + //envLogout() + + if profile != "" { + pErr := logoutProfile(profile, configFilePath) + if pErr != nil { + log.Error(). + Err(pErr). + Str("profile", profile). + Msg("unable to logout profile") + return pErr } - fmt.Println("Error removing config file: ", err) - //log.Fatal("[ERROR] removing config file: ", err) + fmt.Printf( + "Logged out successfully, removed profile '%s' from config file '%s'.", + profile, + configFilePath, + ) + return nil } - fmt.Println("Logged out successfully!") + + logoutFileErr := logoutFile(configFilePath) + if logoutFileErr != nil { + log.Error(). + Err(logoutFileErr). + Str("configFilePath", configFilePath). + Msg("unable to logout") + return logoutFileErr + } + return nil }, } +// logoutFile removes the config file +func logoutFile(f string) error { + log.Info(). + Str("configFilePath", f). + Msg("Running logout command for config file") + + var performLogout bool + if !noPrompt { + performLogout = promptForInteractiveYesNo( + fmt.Sprintf( + "Are you sure you want to remove the config file '%s'?", + f, + ), + ) + if !performLogout { + log.Info().Msg("Logout file cancelled") + fmt.Println(fmt.Sprintf("Logout file '%s' cancelled.", f)) + return nil + } + } + + log.Debug(). + Str("configFilePath", f). + Msg("Removing config file") + err := os.Remove(f) + + if err != nil { + if os.IsNotExist(err) { + log.Error(). + Err(err). + Msg("config file does not exist, unable to logout") + return err + } + log.Error(). + Err(err). + Msg("unable to remove config file, logout failed") + return err + } + log.Info(). + Str("configFilePath", f). + Msg("Config file removed successfully") + fmt.Println(fmt.Sprintf("Logged out successfully, removed config file '%s'", f)) + return nil +} + +// envLogout unsets environment variables +func envLogout() { + + if !noPrompt { + performLogout := promptForInteractiveYesNo("Are you sure you want to unset environment variables?") + if !performLogout { + log.Info().Msg("Logout environment variables cancelled") + fmt.Println("Logout environment variables cancelled.") + return + } + } + + log.Debug().Msg("Running logout command for environment variables") + + log.Debug().Msg("Unsetting base environment variables") + + log.Trace().Str("EnvKeyfactorHostName", auth_providers.EnvKeyfactorHostName).Msg("Unsetting") + os.Unsetenv(auth_providers.EnvKeyfactorHostName) + + log.Trace().Str("EnvKeyfactorPort", auth_providers.EnvKeyfactorPort).Msg("Unsetting") + os.Unsetenv(auth_providers.EnvKeyfactorPort) + + log.Trace().Str("EnvKeyfactorAPIPath", auth_providers.EnvKeyfactorAPIPath).Msg("Unsetting") + os.Unsetenv(auth_providers.EnvKeyfactorAPIPath) + + log.Trace().Str("EnvAuthCACert", auth_providers.EnvAuthCACert).Msg("Unsetting") + os.Unsetenv(auth_providers.EnvAuthCACert) + + log.Trace().Str("EnvKeyfactorSkipVerify", auth_providers.EnvKeyfactorSkipVerify).Msg("Unsetting") + os.Unsetenv(auth_providers.EnvKeyfactorSkipVerify) + + log.Trace().Str("EnvKeyfactorClientTimeout", auth_providers.EnvKeyfactorClientTimeout).Msg("Unsetting") + os.Unsetenv(auth_providers.EnvKeyfactorClientTimeout) + + log.Debug().Msg("Unsetting kfutil environment variables") + log.Trace().Str("EnvKeyfactorAuthProfile", auth_providers.EnvKeyfactorAuthProfile).Msg("Unsetting") + os.Unsetenv(auth_providers.EnvKeyfactorAuthProfile) + + log.Trace().Str("EnvKeyfactorConfigFile", auth_providers.EnvKeyfactorConfigFile).Msg("Unsetting") + os.Unsetenv(auth_providers.EnvKeyfactorConfigFile) + + log.Debug().Msg("Unsetting command basic auth environment variables") + log.Trace().Str("EnvKeyfactorUsername", auth_providers.EnvKeyfactorUsername).Msg("Unsetting") + os.Unsetenv(auth_providers.EnvKeyfactorUsername) + + log.Trace().Str("EnvKeyfactorPassword", auth_providers.EnvKeyfactorPassword).Msg("Unsetting") + os.Unsetenv(auth_providers.EnvKeyfactorPassword) + + log.Trace().Str("EnvKeyfactorDomain", auth_providers.EnvKeyfactorDomain).Msg("Unsetting") + os.Unsetenv(auth_providers.EnvKeyfactorDomain) + + log.Debug().Msg("Unsetting command oauth2 environment variables") + log.Trace().Str("EnvKeyfactorClientID", auth_providers.EnvKeyfactorClientID).Msg("Unsetting") + os.Unsetenv(auth_providers.EnvKeyfactorClientID) + + log.Trace().Str("EnvKeyfactorClientSecret", auth_providers.EnvKeyfactorClientSecret).Msg("Unsetting") + os.Unsetenv(auth_providers.EnvKeyfactorClientSecret) + + log.Trace().Str("EnvKeyfactorAccessToken", auth_providers.EnvKeyfactorAccessToken).Msg("Unsetting") + os.Unsetenv(auth_providers.EnvKeyfactorAccessToken) + + log.Trace().Str("EnvKeyfactorAuthTokenURL", auth_providers.EnvKeyfactorAuthTokenURL).Msg("Unsetting") + os.Unsetenv(auth_providers.EnvKeyfactorAuthTokenURL) + + log.Trace().Str("EnvKeyfactorAuthScopes", auth_providers.EnvKeyfactorAuthScopes).Msg("Unsetting") + os.Unsetenv(auth_providers.EnvKeyfactorAuthScopes) + + log.Trace().Str("EnvKeyfactorAuthAudience", auth_providers.EnvKeyfactorAuthAudience).Msg("Unsetting") + os.Unsetenv(auth_providers.EnvKeyfactorAuthAudience) + + log.Debug().Msg("Unsetting command azure environment variables") + log.Trace().Str("EnvKeyfactorAuthProvider", auth_providers.EnvKeyfactorAuthProvider).Msg("Unsetting") + os.Unsetenv(auth_providers.EnvKeyfactorAuthProvider) + + log.Trace().Str("EnvAzureSecretName", auth_providers.EnvAzureSecretName).Msg("Unsetting") + os.Unsetenv(auth_providers.EnvAzureSecretName) + + log.Trace().Str("EnvAzureVaultName", auth_providers.EnvAzureVaultName).Msg("Unsetting") + os.Unsetenv(auth_providers.EnvAzureVaultName) + +} + +// logoutProfile removes the profile from the config file +func logoutProfile(p string, f string) error { + log.Info(). + Str("profile", p). + Str("configFilePath", f). + Msg("Running logout command for profile") + + var performLogout bool + if !noPrompt { + performLogout = promptForInteractiveYesNo( + fmt.Sprintf( + "Are you sure you want to remove profile '%s' from '%s?", p, f, + ), + ) + if !performLogout { + log.Info().Msg("Logout profile cancelled") + fmt.Println(fmt.Sprintf("Logout profile '%s' in '%s' cancelled.", p, f)) + return nil + } + } + + log.Debug(). + Str("configFilePath", f). + Msg("Reading config file") + config, err := auth_providers.ReadConfigFromJSON(f) + if err != nil { + log.Error(). + Err(err). + Str("configFilePath", f). + Str("profile", p). + Msg("unable to read config file, logout failed") + return err + } else if config == nil { + log.Error(). + Str("configFilePath", f). + Str("profile", p). + Msg("config file is empty, unable to logout") + return fmt.Errorf("config file is empty, unable to logout profile '%s'", p) + } + + // check if profile exists + if _, ok := config.Servers[p]; !ok { + log.Error(). + Str("profile", p). + Msg("profile does not exist, unable to logout") + return fmt.Errorf("profile '%s' does not exist, unable to logout", p) + } + delete(config.Servers, p) + wErr := auth_providers.WriteConfigToJSON(f, config) + if wErr != nil { + log.Error(). + Err(wErr). + Str("configFilePath", f). + Str("profile", p). + Msg("unable to write config file, logout failed") + return wErr + } + log.Info(). + Str("configFilePath", f). + Str("profile", p). + Msg("Profile removed successfully") + return nil +} + func init() { RootCmd.AddCommand(logoutCmd) } diff --git a/cmd/orchs.go b/cmd/orchs.go index 47d85ff..324ec21 100644 --- a/cmd/orchs.go +++ b/cmd/orchs.go @@ -36,8 +36,6 @@ var getOrchestratorCmd = &cobra.Command{ Short: "Get orchestrator by machine/client name.", Long: `Get orchestrator by machine/client name.`, Run: func(cmd *cobra.Command, args []string) { - - authConfig := createAuthConfigFromParams(kfcHostName, kfcUsername, kfcPassword, kfcDomain, kfcAPIPath) isExperimental := true _, expErr := isExperimentalFeatureEnabled(expEnabled, isExperimental) @@ -49,7 +47,7 @@ var getOrchestratorCmd = &cobra.Command{ debugModeEnabled := checkDebug(debugFlag) log.Println("Debug mode enabled: ", debugModeEnabled) client := cmd.Flag("client").Value.String() - kfClient, _ := initClient(configFile, profile, "", "", noPrompt, authConfig, false) + kfClient, _ := initClient(false) agents, aErr := kfClient.GetAgent(client) if aErr != nil { fmt.Printf("Error, unable to get orchestrator %s. %s\n", client, aErr) @@ -70,8 +68,6 @@ var approveOrchestratorCmd = &cobra.Command{ Short: "Approve orchestrator by machine/client name.", Long: `Approve orchestrator by machine/client name.`, Run: func(cmd *cobra.Command, args []string) { - - authConfig := createAuthConfigFromParams(kfcHostName, kfcUsername, kfcPassword, kfcDomain, kfcAPIPath) isExperimental := true _, expErr := isExperimentalFeatureEnabled(expEnabled, isExperimental) @@ -83,7 +79,7 @@ var approveOrchestratorCmd = &cobra.Command{ debugModeEnabled := checkDebug(debugFlag) log.Println("Debug mode enabled: ", debugModeEnabled) client := cmd.Flag("client").Value.String() - kfClient, cErr := initClient(configFile, profile, "", "", noPrompt, authConfig, false) + kfClient, cErr := initClient(false) if cErr != nil { fmt.Println("Error, unable to connect to Keyfactor.") log.Fatalf("Error: %s", cErr) @@ -110,7 +106,6 @@ var disapproveOrchestratorCmd = &cobra.Command{ Long: `Disapprove orchestrator by machine/client name.`, Run: func(cmd *cobra.Command, args []string) { - authConfig := createAuthConfigFromParams(kfcHostName, kfcUsername, kfcPassword, kfcDomain, kfcAPIPath) isExperimental := true _, expErr := isExperimentalFeatureEnabled(expEnabled, isExperimental) @@ -122,7 +117,7 @@ var disapproveOrchestratorCmd = &cobra.Command{ debugModeEnabled := checkDebug(debugFlag) log.Println("Debug mode enabled: ", debugModeEnabled) client := cmd.Flag("client").Value.String() - kfClient, cErr := initClient(configFile, profile, "", "", noPrompt, authConfig, false) + kfClient, cErr := initClient(false) if cErr != nil { fmt.Println("Error, unable to connect to Keyfactor.") log.Fatalf("Error: %s", cErr) @@ -158,8 +153,6 @@ var getLogsOrchestratorCmd = &cobra.Command{ Short: "Get orchestrator logs by machine/client name.", Long: `Get orchestrator logs by machine/client name.`, Run: func(cmd *cobra.Command, args []string) { - - authConfig := createAuthConfigFromParams(kfcHostName, kfcUsername, kfcPassword, kfcDomain, kfcAPIPath) isExperimental := true _, expErr := isExperimentalFeatureEnabled(expEnabled, isExperimental) @@ -172,7 +165,7 @@ var getLogsOrchestratorCmd = &cobra.Command{ log.Println("Debug mode enabled: ", debugModeEnabled) client := cmd.Flag("client").Value.String() - kfClient, cErr := initClient(configFile, profile, "", "", noPrompt, authConfig, false) + kfClient, cErr := initClient(false) if cErr != nil { fmt.Println("Error, unable to connect to Keyfactor.") log.Fatalf("Error: %s", cErr) @@ -198,8 +191,6 @@ var listOrchestratorsCmd = &cobra.Command{ Short: "List orchestrators.", Long: `Returns a JSON list of Keyfactor orchestrators.`, Run: func(cmd *cobra.Command, args []string) { - - authConfig := createAuthConfigFromParams(kfcHostName, kfcUsername, kfcPassword, kfcDomain, kfcAPIPath) isExperimental := true _, expErr := isExperimentalFeatureEnabled(expEnabled, isExperimental) @@ -210,7 +201,7 @@ var listOrchestratorsCmd = &cobra.Command{ debugModeEnabled := checkDebug(debugFlag) log.Println("Debug mode enabled: ", debugModeEnabled) - kfClient, _ := initClient(configFile, profile, "", "", noPrompt, authConfig, false) + kfClient, _ := initClient(false) agents, aErr := kfClient.GetAgentList() if aErr != nil { fmt.Printf("Error, unable to get orchestrators list. %s\n", aErr) @@ -237,7 +228,13 @@ func init() { orchsCmd.AddCommand(listOrchestratorsCmd) // GET orchestrator command orchsCmd.AddCommand(getOrchestratorCmd) - getOrchestratorCmd.Flags().StringVarP(&client, "client", "c", "", "Get a specific orchestrator by machine or client name.") + getOrchestratorCmd.Flags().StringVarP( + &client, + "client", + "c", + "", + "Get a specific orchestrator by machine or client name.", + ) getOrchestratorCmd.MarkFlagRequired("client") // CREATE orchestrator command //orchsCmd.AddCommand(createOrchestratorCmd) @@ -247,19 +244,43 @@ func init() { //orchsCmd.AddCommand(deleteOrchestratorCmd) // APPROVE orchestrator command orchsCmd.AddCommand(approveOrchestratorCmd) - approveOrchestratorCmd.Flags().StringVarP(&client, "client", "c", "", "Approve a specific orchestrator by machine or client name.") + approveOrchestratorCmd.Flags().StringVarP( + &client, + "client", + "c", + "", + "Approve a specific orchestrator by machine or client name.", + ) approveOrchestratorCmd.MarkFlagRequired("client") // DISAPPROVE orchestrator command orchsCmd.AddCommand(disapproveOrchestratorCmd) - disapproveOrchestratorCmd.Flags().StringVarP(&client, "client", "c", "", "Disapprove a specific orchestrator by machine or client name.") + disapproveOrchestratorCmd.Flags().StringVarP( + &client, + "client", + "c", + "", + "Disapprove a specific orchestrator by machine or client name.", + ) disapproveOrchestratorCmd.MarkFlagRequired("client") // RESET orchestrator command orchsCmd.AddCommand(resetOrchestratorCmd) - resetOrchestratorCmd.Flags().StringVarP(&client, "client", "c", "", "Reset a specific orchestrator by machine or client name.") + resetOrchestratorCmd.Flags().StringVarP( + &client, + "client", + "c", + "", + "Reset a specific orchestrator by machine or client name.", + ) resetOrchestratorCmd.MarkFlagRequired("client") // GET orchestrator logs command orchsCmd.AddCommand(getLogsOrchestratorCmd) - getLogsOrchestratorCmd.Flags().StringVarP(&client, "client", "c", "", "Get logs for a specific orchestrator by machine or client name.") + getLogsOrchestratorCmd.Flags().StringVarP( + &client, + "client", + "c", + "", + "Get logs for a specific orchestrator by machine or client name.", + ) getLogsOrchestratorCmd.MarkFlagRequired("client") // SET orchestrator auth certificate reenrollment command //orchsCmd.AddCommand(setOrchestratorAuthCertReenrollCmd) diff --git a/cmd/pam.go b/cmd/pam.go index 2b3e6ff..a15400f 100644 --- a/cmd/pam.go +++ b/cmd/pam.go @@ -18,12 +18,15 @@ import ( "context" "encoding/json" "fmt" - "github.com/Keyfactor/keyfactor-go-client-sdk/api/keyfactor" - "github.com/rs/zerolog/log" - "github.com/spf13/cobra" "io" "net/http" "os" + "strconv" + "strings" + + "github.com/Keyfactor/keyfactor-go-client-sdk/v2/api/keyfactor" + "github.com/rs/zerolog/log" + "github.com/spf13/cobra" ) type JSONImportableObject interface { @@ -61,8 +64,7 @@ var pamTypesListCmd = &cobra.Command{ log.Info().Msg("list PAM Provider Types") // Authenticate - authConfig := createAuthConfigFromParams(kfcHostName, kfcUsername, kfcPassword, kfcDomain, kfcAPIPath) - sdkClient, clientErr := initGenClient(configFile, profile, noPrompt, authConfig, false) + sdkClient, clientErr := initGenClient(false) if clientErr != nil { return clientErr } @@ -135,9 +137,11 @@ https://github.com/Keyfactor/hashicorp-vault-pam/blob/main/integration-manifest. Msg("create PAM Provider Type") // Authenticate - authConfig := createAuthConfigFromParams(kfcHostName, kfcUsername, kfcPassword, kfcDomain, kfcAPIPath) //kfClient, _ := initClient(configFile, profile, providerType, providerProfile, noPrompt, authConfig, false) - sdkClient, _ := initGenClient(configFile, profile, noPrompt, authConfig, false) + sdkClient, cErr := initGenClient(false) + if cErr != nil { + return cErr + } // Check required flags if pamConfigFile == "" && repoName == "" { @@ -229,9 +233,11 @@ var pamProvidersListCmd = &cobra.Command{ log.Info().Msg("list PAM Providers") // Authenticate - authConfig := createAuthConfigFromParams(kfcHostName, kfcUsername, kfcPassword, kfcDomain, kfcAPIPath) //kfClient, _ := initClient(configFile, profile, providerType, providerProfile, noPrompt, authConfig, false) - sdkClient, _ := initGenClient(configFile, profile, noPrompt, authConfig, false) + sdkClient, cErr := initGenClient(false) + if cErr != nil { + return cErr + } // CLI Logic log.Debug().Msg("call: PAMProviderGetPamProviders()") @@ -281,13 +287,18 @@ var pamProvidersGetCmd = &cobra.Command{ Msg("get PAM Provider") // Authenticate - authConfig := createAuthConfigFromParams(kfcHostName, kfcUsername, kfcPassword, kfcDomain, kfcAPIPath) //kfClient, _ := initClient(configFile, profile, providerType, providerProfile, noPrompt, authConfig, false) - sdkClient, _ := initGenClient(configFile, profile, noPrompt, authConfig, false) + sdkClient, cErr := initGenClient(false) + if cErr != nil { + return cErr + } // CLI Logic log.Debug().Msg("call: PAMProviderGetPamProvider()") - pamProvider, httpResponse, err := sdkClient.PAMProviderApi.PAMProviderGetPamProvider(context.Background(), pamProviderId). + pamProvider, httpResponse, err := sdkClient.PAMProviderApi.PAMProviderGetPamProvider( + context.Background(), + pamProviderId, + ). XKeyfactorRequestedWith(XKeyfactorRequestedWith).XKeyfactorApiVersion(XKeyfactorApiVersion). Execute() log.Debug().Msg("returned: PAMProviderGetPamProvider()") @@ -311,6 +322,30 @@ var pamProvidersGetCmd = &cobra.Command{ }, } +func checkBug63171(cmdResp *http.Response, operation string) error { + if cmdResp != nil && cmdResp.StatusCode == 200 { + defer cmdResp.Body.Close() + // .\Admin + productVersion := cmdResp.Header.Get("X-Keyfactor-Product-Version") + log.Debug().Str("productVersion", productVersion).Msg("Keyfactor Command Version") + majorVersionStr := strings.Split(productVersion, ".")[0] + // Try to convert to int + majorVersion, err := strconv.Atoi(majorVersionStr) + if err == nil && majorVersion >= 12 { + // TODO: Pending resolution of this bug: https://dev.azure.com/Keyfactor/Engineering/_workitems/edit/63171 + errMsg := fmt.Sprintf( + "PAM Provider %s is not supported in Keyfactor Command version 12 and later, "+ + "please use the Keyfactor Command UI to create PAM Providers", operation, + ) + oErr := fmt.Errorf(errMsg) + log.Error().Err(oErr).Send() + outputError(oErr, true, outputFormat) + return oErr + } + } + return nil +} + var pamProvidersCreateCmd = &cobra.Command{ Use: "create", Short: "Create a new PAM Provider, currently only supported from file.", @@ -334,9 +369,22 @@ var pamProvidersCreateCmd = &cobra.Command{ Msg("create PAM Provider from file") // Authenticate - authConfig := createAuthConfigFromParams(kfcHostName, kfcUsername, kfcPassword, kfcDomain, kfcAPIPath) // kfClient, _ := initClient(configFile, profile, providerType, providerProfile, noPrompt, authConfig, false) - sdkClient, _ := initGenClient(configFile, profile, noPrompt, authConfig, false) + sdkClient, cErr := initGenClient(false) + + _, cmdResp, sErr := sdkClient.StatusApi.StatusGetEndpoints(context.Background()).Execute() + if sErr != nil { + log.Error().Err(sErr).Msg("failed to get Keyfactor Command version") + } else { + bug63171 := checkBug63171(cmdResp, "CREATE") + if bug63171 != nil { + return bug63171 + } + } + + if cErr != nil { + return cErr + } // CLI Logic var pamProvider *keyfactor.CSSCMSDataModelModelsProvider @@ -398,9 +446,21 @@ var pamProvidersUpdateCmd = &cobra.Command{ Msg("update PAM Provider from file") // Authenticate - authConfig := createAuthConfigFromParams(kfcHostName, kfcUsername, kfcPassword, kfcDomain, kfcAPIPath) //kfClient, _ := initClient(configFile, profile, providerType, providerProfile, noPrompt, authConfig, false) - sdkClient, _ := initGenClient(configFile, profile, noPrompt, authConfig, false) + sdkClient, cErr := initGenClient(false) + if cErr != nil { + return cErr + } + + _, cmdResp, sErr := sdkClient.StatusApi.StatusGetEndpoints(context.Background()).Execute() + if sErr != nil { + log.Error().Err(sErr).Msg("failed to get Keyfactor Command version") + } else { + bug63171 := checkBug63171(cmdResp, "UPDATE") + if bug63171 != nil { + return bug63171 + } + } // CLI Logic var pamProvider *keyfactor.CSSCMSDataModelModelsProvider @@ -422,7 +482,7 @@ var pamProvidersUpdateCmd = &cobra.Command{ log.Debug().Msg("returned: PAMProviderUpdatePamProvider()") log.Trace().Interface("httpResponse", httpResponse).Msg("PAMProviderUpdatePamProvider") if err != nil { - returnHttpErr(httpResponse, err) + return returnHttpErr(httpResponse, err) } log.Debug().Msg(convertResponseMsg) @@ -465,9 +525,11 @@ var pamProvidersDeleteCmd = &cobra.Command{ Msg("delete PAM Provider") // Authenticate - authConfig := createAuthConfigFromParams(kfcHostName, kfcUsername, kfcPassword, kfcDomain, kfcAPIPath) //kfClient, _ := initClient(configFile, profile, providerType, providerProfile, noPrompt, authConfig, false) - sdkClient, _ := initGenClient(configFile, profile, noPrompt, authConfig, false) + sdkClient, cErr := initGenClient(false) + if cErr != nil { + return cErr + } // CLI Logic log.Debug(). @@ -500,7 +562,11 @@ func GetPAMTypeInternet(providerName string, repo string, branch string) (interf branch = "main" } - providerUrl := fmt.Sprintf("https://raw.githubusercontent.com/Keyfactor/%s/%s/integration-manifest.json", repo, branch) + providerUrl := fmt.Sprintf( + "https://raw.githubusercontent.com/Keyfactor/%s/%s/integration-manifest.json", + repo, + branch, + ) log.Debug().Str("providerUrl", providerUrl). Msg("Getting PAM Type from Internet") response, err := http.Get(providerUrl) @@ -558,7 +624,10 @@ func GetPAMTypeInternet(providerName string, repo string, branch string) (interf return pamTypeJson, nil } -func GetTypeFromInternet[T JSONImportableObject](providerName string, repo string, branch string, returnType *T) (*T, error) { +func GetTypeFromInternet[T JSONImportableObject](providerName string, repo string, branch string, returnType *T) ( + *T, + error, +) { log.Debug().Str("providerName", providerName). Str("repo", repo). Str("branch", branch). @@ -629,10 +698,22 @@ func init() { // PAM Provider Types Create pamCmd.AddCommand(pamTypesCreateCmd) - pamTypesCreateCmd.Flags().StringVarP(&filePath, FlagFromFile, "f", "", "Path to a JSON file containing the PAM Type Object Data.") + pamTypesCreateCmd.Flags().StringVarP( + &filePath, + FlagFromFile, + "f", + "", + "Path to a JSON file containing the PAM Type Object Data.", + ) pamTypesCreateCmd.Flags().StringVarP(&name, "name", "n", "", "Name of the PAM Provider Type.") pamTypesCreateCmd.Flags().StringVarP(&repo, "repo", "r", "", "Keyfactor repository name of the PAM Provider Type.") - pamTypesCreateCmd.Flags().StringVarP(&branch, "branch", "b", "", "Branch name for the repository. Defaults to 'main'.") + pamTypesCreateCmd.Flags().StringVarP( + &branch, + "branch", + "b", + "", + "Branch name for the repository. Defaults to 'main'.", + ) // PAM Providers pamCmd.AddCommand(pamProvidersListCmd) @@ -641,11 +722,23 @@ func init() { pamProvidersGetCmd.MarkFlagRequired("id") pamCmd.AddCommand(pamProvidersCreateCmd) - pamProvidersCreateCmd.Flags().StringVarP(&filePath, FlagFromFile, "f", "", "Path to a JSON file containing the PAM Provider Object Data.") + pamProvidersCreateCmd.Flags().StringVarP( + &filePath, + FlagFromFile, + "f", + "", + "Path to a JSON file containing the PAM Provider Object Data.", + ) pamProvidersCreateCmd.MarkFlagRequired(FlagFromFile) pamCmd.AddCommand(pamProvidersUpdateCmd) - pamProvidersUpdateCmd.Flags().StringVarP(&filePath, FlagFromFile, "f", "", "Path to a JSON file containing the PAM Provider Object Data.") + pamProvidersUpdateCmd.Flags().StringVarP( + &filePath, + FlagFromFile, + "f", + "", + "Path to a JSON file containing the PAM Provider Object Data.", + ) pamProvidersUpdateCmd.MarkFlagRequired(FlagFromFile) pamCmd.AddCommand(pamProvidersDeleteCmd) diff --git a/cmd/pam_test.go b/cmd/pam_test.go index c377376..8df914b 100644 --- a/cmd/pam_test.go +++ b/cmd/pam_test.go @@ -17,13 +17,14 @@ package cmd import ( "encoding/json" "fmt" - "github.com/stretchr/testify/assert" "os" "path" "path/filepath" "strconv" "strings" "testing" + + "github.com/stretchr/testify/assert" ) func Test_PAMHelpCmd(t *testing.T) { @@ -70,10 +71,12 @@ func Test_PAMTypesListCmd(t *testing.T) { // test var err error testCmd.SetArgs([]string{"pam", "types-list"}) - output := captureOutput(func() { - err = testCmd.Execute() - assert.NoError(t, err) - }) + output := captureOutput( + func() { + err = testCmd.Execute() + assert.NoError(t, err) + }, + ) if err != nil { t.Errorf("failed to list PAM provider types: %v", err) @@ -157,10 +160,12 @@ func Test_PAMGetCmd(t *testing.T) { idInt := int(providerConfig["Id"].(float64)) idStr := strconv.Itoa(idInt) testCmd.SetArgs([]string{"pam", "get", "--id", idStr}) - output := captureOutput(func() { - err := testCmd.Execute() - assert.NoError(t, err) - }) + output := captureOutput( + func() { + err := testCmd.Execute() + assert.NoError(t, err) + }, + ) var pamProvider interface{} if err := json.Unmarshal([]byte(output), &pamProvider); err != nil { t.Fatalf("Error unmarshalling JSON: %v", err) @@ -184,10 +189,12 @@ func Test_PAMTypesCreateCmd(t *testing.T) { randomName := generateRandomUUID() t.Logf("randomName: %s", randomName) testCmd.SetArgs([]string{"pam", "types-create", "--repo", "hashicorp-vault-pam", "--name", randomName}) - output := captureOutput(func() { - err := testCmd.Execute() - assert.NoError(t, err) - }) + output := captureOutput( + func() { + err := testCmd.Execute() + assert.NoError(t, err) + }, + ) var createResponse interface{} if err := json.Unmarshal([]byte(output), &createResponse); err != nil { t.Log(output) @@ -203,7 +210,10 @@ func Test_PAMCreateCmd(t *testing.T) { // test // get current working dir - cwd, _ := os.Getwd() + cwd, wdErr := os.Getwd() + if wdErr != nil { + cwd = "./" + } t.Logf("cwd: %s", cwd) providerName := "Delinea-SecretServer-test" @@ -223,6 +233,9 @@ func Test_PAMCreateCmd(t *testing.T) { // Test valid config file createResponse, err := testCreatePamProvider(t, updatedFileName, providerName, false) + if err != nil && testCheckBug63171(err) { + t.Skip("PAM Provider creation is not supported in Keyfactor Command version 12 and later") + } assert.NoError(t, err) assert.NotNil(t, createResponse) if err != nil { @@ -261,7 +274,10 @@ func Test_PAMCreateCmd(t *testing.T) { func Test_PAMUpdateCmd(t *testing.T) { // test // get current working dir - cwd, _ := os.Getwd() + cwd, wdErr := os.Getwd() + if wdErr != nil { + cwd = "./" + } t.Logf("cwd: %s", cwd) providerName := "Delinea-SecretServer-test" @@ -291,10 +307,18 @@ func Test_PAMUpdateCmd(t *testing.T) { testCmd := RootCmd // test testCmd.SetArgs([]string{"pam", "update", "--from-file", updatedFileName}) - output := captureOutput(func() { - err := testCmd.Execute() - assert.NoError(t, err) - }) + output := captureOutput( + func() { + err := testCmd.Execute() + if err != nil && testCheckBug63171(err) { + t.Skip("Updating PAM Providers is not supported in Keyfactor Command version 12 and later") + } else if err != nil { + t.Errorf("failed to update a PAM provider: %v", err) + t.FailNow() + } + assert.NoError(t, err) + }, + ) var updateResponse interface{} if err := json.Unmarshal([]byte(output), &updateResponse); err != nil { @@ -328,7 +352,10 @@ func Test_PAMUpdateCmd(t *testing.T) { func Test_PAMDeleteCmd(t *testing.T) { // test // get current working dir - cwd, _ := os.Getwd() + cwd, wdErr := os.Getwd() + if wdErr != nil { + cwd = "./" + } t.Logf("cwd: %s", cwd) providerName := "Delinea-SecretServer-test" @@ -348,7 +375,10 @@ func Test_PAMDeleteCmd(t *testing.T) { return } // Create a provider to delete, doesn't matter if it fails, assume it exists then delete it - testCreatePamProvider(t, updatedFileName, providerName, true) + _, cErr := testCreatePamProvider(t, updatedFileName, providerName, true) + if cErr != nil && testCheckBug63171(cErr) { + t.Skip("PAM Provider creation is not supported in Keyfactor Command version 12 and later") + } // list providers providersList, err := testListPamProviders(t) @@ -386,51 +416,59 @@ func testListPamProviders(t *testing.T) ([]interface{}, error) { var pamProviders []interface{} var err error - t.Run("Listing PAM provider instances", func(t *testing.T) { - testCmd := RootCmd - // test - testCmd.SetArgs([]string{"pam", "list"}) - output = captureOutput(func() { - err = testCmd.Execute() - assert.NoError(t, err) - }) - - if err != nil { - t.Errorf("failed to list PAM providers: %v", err) - return - } - - if err = json.Unmarshal([]byte(output), &pamProviders); err != nil { - t.Fatalf("Error unmarshalling JSON: %v", err) - } + t.Run( + "Listing PAM provider instances", func(t *testing.T) { + testCmd := RootCmd + // test + testCmd.SetArgs([]string{"pam", "list"}) + output = captureOutput( + func() { + err = testCmd.Execute() + assert.NoError(t, err) + }, + ) + + if err != nil { + t.Errorf("failed to list PAM providers: %v", err) + return + } - // assert slice is len >= 0 - assert.GreaterOrEqual(t, len(pamProviders), 0) + if err = json.Unmarshal([]byte(output), &pamProviders); err != nil { + t.Fatalf("Error unmarshalling JSON: %v", err) + } - if len(pamProviders) > 0 { - for _, p := range pamProviders { - providerConfig := p.(map[string]interface{}) - // assert that each p has a name - assert.NotEmpty(t, providerConfig["Name"]) - // assert that each p has an ID - assert.NotEmpty(t, providerConfig["Id"]) - // assert that each p has a type - assert.NotEmpty(t, providerConfig["ProviderType"]) - - // Check params is a list of maps - pTypeParams := providerConfig["ProviderType"].(map[string]interface{})["ProviderTypeParams"].([]interface{}) - assert.NotEmpty(t, pTypeParams) - assert.GreaterOrEqual(t, len(pTypeParams), 0) - if len(pTypeParams) > 0 { - for _, param := range pTypeParams { - assert.NotEmpty(t, param.(map[string]interface{})["Id"]) - assert.NotEmpty(t, param.(map[string]interface{})["Name"]) - assert.NotEmpty(t, param.(map[string]interface{})["DataType"]) + // assert slice is len >= 0 + assert.GreaterOrEqual(t, len(pamProviders), 0) + + if len(pamProviders) > 0 { + for _, p := range pamProviders { + providerConfig := p.(map[string]interface{}) + // assert that each p has a name + assert.NotEmpty(t, providerConfig["Name"]) + // assert that each p has an ID + assert.NotEmpty(t, providerConfig["Id"]) + // assert that each p has a type + assert.NotEmpty(t, providerConfig["ProviderType"]) + + // Check params is a list of maps + pTypeParams := providerConfig["ProviderType"].(map[string]interface{})["ProviderTypeParams"].([]interface{}) + assert.NotEmpty(t, pTypeParams) + assert.GreaterOrEqual(t, len(pTypeParams), 0) + if len(pTypeParams) > 0 { + for _, param := range pTypeParams { + assert.NotEmpty(t, param.(map[string]interface{})["Id"]) + assert.NotEmpty(t, param.(map[string]interface{})["Name"]) + assert.NotEmpty(t, param.(map[string]interface{})["DataType"]) + } } } + } else { + t.Errorf("0 PAM providers found, cannot test list") + t.Fail() } - } - }) + + }, + ) if err != nil { t.Log(output) return nil, err @@ -447,36 +485,54 @@ func testCreatePamProvider(t *testing.T, fileName string, providerName string, a } else { testName = fmt.Sprintf("Create PAM provider '%s'", providerName) } - t.Run(testName, func(t *testing.T) { - testCmd := RootCmd + var bug63171 error + t.Run( + testName, func(t *testing.T) { + testCmd := RootCmd - args := []string{"pam", "create", "--from-file", fileName} - // log the args as a string - t.Logf("args: %s", args) - testCmd.SetArgs(args) - t.Logf("fileName: %s", fileName) - output := captureOutput(func() { - err = testCmd.Execute() - if !allowFail { - assert.NoError(t, err) + args := []string{"pam", "create", "--from-file", fileName} + // log the args as a string + t.Logf("args: %s", args) + testCmd.SetArgs(args) + t.Logf("fileName: %s", fileName) + + output := captureOutput( + func() { + err = testCmd.Execute() + if !allowFail { + if err != nil && testCheckBug63171(err) { + bug63171 = err + t.Skip("PAM Provider creation is not supported in Keyfactor Command version 12 and later") + } + assert.NoError(t, err) + } else if err != nil && !testCheckBug63171(err) { + bug63171 = err + } + }, + ) + + if jErr := json.Unmarshal([]byte(output), &createResponse); jErr != nil { + if allowFail { + t.Logf("Error unmarshalling JSON: %v", jErr) + } else { + t.Errorf("failed to create a PAM provider: %v", jErr) + t.FailNow() + } + return } - }) - if err = json.Unmarshal([]byte(output), &createResponse); err != nil { - if allowFail { - t.Logf("Error unmarshalling JSON: %v", err) - } else { - t.Errorf("failed to create a PAM provider: %v", err) + + if !allowFail { + assert.NotEmpty(t, createResponse.(map[string]interface{})["Id"]) + assert.NotEmpty(t, createResponse.(map[string]interface{})["Name"]) + assert.Equal(t, createResponse.(map[string]interface{})["Name"], providerName) + assert.NotEmpty(t, createResponse.(map[string]interface{})["ProviderType"]) } - return - } + }, + ) - if !allowFail { - assert.NotEmpty(t, createResponse.(map[string]interface{})["Id"]) - assert.NotEmpty(t, createResponse.(map[string]interface{})["Name"]) - assert.Equal(t, createResponse.(map[string]interface{})["Name"], providerName) - assert.NotEmpty(t, createResponse.(map[string]interface{})["ProviderType"]) - } - }) + if bug63171 != nil { + return createResponse, bug63171 + } return createResponse, err } @@ -484,20 +540,24 @@ func testCreatePamProvider(t *testing.T, fileName string, providerName string, a func testDeletePamProvider(t *testing.T, pID int, allowFail bool) error { var err error var output string - t.Run(fmt.Sprintf("Deleting PAM provider %d", pID), func(t *testing.T) { - testCmd := RootCmd - - testCmd.SetArgs([]string{"pam", "delete", "--id", strconv.Itoa(pID)}) - output = captureOutput(func() { - err = testCmd.Execute() + t.Run( + fmt.Sprintf("Deleting PAM provider %d", pID), func(t *testing.T) { + testCmd := RootCmd + + testCmd.SetArgs([]string{"pam", "delete", "--id", strconv.Itoa(pID)}) + output = captureOutput( + func() { + err = testCmd.Execute() + if !allowFail { + assert.NoError(t, err) + } + }, + ) if !allowFail { - assert.NoError(t, err) + assert.Contains(t, output, fmt.Sprintf("Deleted PAM provider with ID %d", pID)) } - }) - if !allowFail { - assert.Contains(t, output, fmt.Sprintf("Deleted PAM provider with ID %d", pID)) - } - }) + }, + ) if err != nil && !allowFail { t.Log(output) return err @@ -513,12 +573,14 @@ func testListPamProviderTypes(t *testing.T, name string, allowFail bool, allowEm testCmd := RootCmd // test testCmd.SetArgs([]string{"pam", "types-list"}) - output = captureOutput(func() { - err = testCmd.Execute() - if !allowFail { - assert.NoError(t, err) - } - }) + output = captureOutput( + func() { + err = testCmd.Execute() + if !allowFail { + assert.NoError(t, err) + } + }, + ) var pTypes []interface{} if err = json.Unmarshal([]byte(output), &pTypes); err != nil && !allowFail { t.Errorf("Error unmarshalling JSON: %v", err) @@ -632,7 +694,11 @@ func testFormatPamCreateConfig(t *testing.T, inputFileName string, providerName if oErr == nil { oErr = fmt.Errorf("failed to find PAM provider type '%s' unable to create PAM provider", cProviderTypeName) } else { - oErr = fmt.Errorf("failed to find PAM provider type '%s' unable to create PAM provider: %v", cProviderTypeName, oErr) + oErr = fmt.Errorf( + "failed to find PAM provider type '%s' unable to create PAM provider: %v", + cProviderTypeName, + oErr, + ) } t.Error(oErr) return "", oErr @@ -691,3 +757,10 @@ func testFormatPamCreateConfig(t *testing.T, inputFileName string, providerName } return updatedFileName, nil } + +func testCheckBug63171(err error) bool { + if err != nil && strings.Contains(err.Error(), "not supported in Keyfactor Command version 12 and later") { + return true + } + return false +} diff --git a/cmd/root.go b/cmd/root.go index 7029f29..ee21fc1 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -15,16 +15,20 @@ package cmd import ( + _ "embed" "fmt" - "github.com/Keyfactor/keyfactor-go-client-sdk/api/keyfactor" - "github.com/Keyfactor/keyfactor-go-client/v2/api" + "io" + stdlog "log" + "os" + "strings" + + "github.com/Keyfactor/keyfactor-auth-client-go/auth_providers" + "github.com/Keyfactor/keyfactor-go-client-sdk/v2/api/keyfactor" + "github.com/Keyfactor/keyfactor-go-client/v3/api" "github.com/rs/zerolog/log" "github.com/spf13/cobra" "github.com/spf13/cobra/doc" "golang.org/x/crypto/bcrypt" - "io" - stdlog "log" - "os" ) var ( @@ -33,24 +37,32 @@ var ( providerType string providerProfile string //providerConfig string - noPrompt bool - expEnabled bool - debugFlag bool - kfcUsername string - kfcHostName string - kfcPassword string - kfcDomain string - kfcAPIPath string - logInsecure bool - outputFormat string + noPrompt bool + expEnabled bool + debugFlag bool + skipVerifyFlag bool + kfcUsername string + kfcHostName string + kfcPassword string + kfcDomain string + kfcClientId string + kfcClientSecret string + kfcTokenUrl string + kfcAPIPath string + logInsecure bool + outputFormat string + offline bool ) +// hashSecretValue hashes the secret value using bcrypt func hashSecretValue(secretValue string) string { log.Debug().Msg("Enter hashSecretValue()") - if logInsecure { + if secretValue == "" { return secretValue } - log.Trace().Str("secretValue", secretValue).Send() + if !logInsecure { + return "*****************************" + } cost := 12 log.Debug().Int("cost", cost).Msg("call: bcrypt.GenerateFromPassword()") hashedPassword, err := bcrypt.GenerateFromPassword([]byte(secretValue), cost) @@ -63,206 +75,631 @@ func hashSecretValue(secretValue string) string { return string(hashedPassword) } -func initClient(flagConfigFile string, flagProfile string, flagAuthProviderType string, flagAuthProviderProfile string, noPrompt bool, authConfig *api.AuthConfig, saveConfig bool) (*api.Client, error) { - log.Debug().Msg("Enter initClient()") - var clientAuth api.AuthConfig - var commandConfig ConfigurationFile - - if providerType != "" { - return authViaProvider() - } - - log.Debug().Msg("call: authEnvVars()") - commandConfig, _ = authEnvVars(flagConfigFile, flagProfile, saveConfig) - - // check if commandConfig is empty - if commandConfig.Servers == nil || len(commandConfig.Servers) == 0 { - log.Debug().Msg("commandConfig is empty") - if flagConfigFile != "" || !validConfigFileEntry(commandConfig, flagProfile) { - log.Debug(). - Str("flagConfigFile", flagConfigFile). - Str("flagProfile", flagProfile). - Bool("noPrompt", noPrompt). - Bool("saveConfig", saveConfig). - Msg("call: authConfigFile()") - commandConfig, _ = authConfigFile(flagConfigFile, flagProfile, "", noPrompt, saveConfig) - log.Debug().Msg("complete: authConfigFile()") - } +// getServerConfigFromFile reads the configuration file and returns the server configuration +func getServerConfigFromFile(configFile string, profile string) (*auth_providers.Server, error) { + var commandConfig *auth_providers.Config + var serverConfig auth_providers.Server + + log.Debug(). + Str("configFile", configFile). + Str("profile", profile). + Msg("configFile or profile is not empty attempting to authenticate via config file") + if profile == "" { + profile = "default" + } + if configFile == "" { + homeDir, _ := os.UserHomeDir() + configFile = fmt.Sprintf("%s/%s", homeDir, auth_providers.DefaultConfigFilePath) + } + var cfgReadErr error + if strings.HasSuffix(configFile, ".yaml") || strings.HasSuffix(configFile, ".yml") { + log.Debug().Msg("call: auth_providers.ReadConfigFromYAML()") + //commandConfig, cfgReadErr = auth_providers.ReadConfigFromYAML(configFile) + commandConfig, cfgReadErr = auth_providers.ReadConfigFromJSON(configFile) } else { - log.Debug().Msg("commandConfig is not empty and is valid") - authProviderProfile, _ := os.LookupEnv("KUTIL_AUTH_PROVIDER_PROFILE") - log.Debug().Str("authProviderProfile", authProviderProfile).Send() - if authProviderProfile != "" { - flagProfile = authProviderProfile - } else if flagAuthProviderProfile != "" { - flagProfile = flagAuthProviderProfile - } + log.Debug().Msg("call: auth_providers.ReadConfigFromJSON()") + commandConfig, cfgReadErr = auth_providers.ReadConfigFromJSON(configFile) } - log.Debug().Str("flagProfile", flagProfile).Send() - if flagProfile == "" { - flagProfile = "default" + if cfgReadErr != nil { + log.Error().Err(cfgReadErr).Msg("unable to read config file") + return nil, fmt.Errorf("unable to read config file: %s", cfgReadErr) } - //Params from authConfig take precedence over everything else - if authConfig != nil { - // replace commandConfig with authConfig params that aren't null or empty - log.Debug().Str("flagProfile", flagProfile).Msg("Loading profile from authConfig") - configEntry := commandConfig.Servers[flagProfile] - if authConfig.Hostname != "" { - log.Debug().Str("authConfig.Hostname", authConfig.Hostname). - Str("configEntry.Hostname", configEntry.Hostname). - Str("flagProfile", flagProfile). - Msg("Config file profile file hostname is set") - configEntry.Hostname = authConfig.Hostname - } - if authConfig.Username != "" { - log.Debug().Str("authConfig.Username", authConfig.Username). - Str("configEntry.Username", configEntry.Username). - Str("flagProfile", flagProfile). - Msg("Config file profile file username is set") - configEntry.Username = authConfig.Username + // check if the profile exists in the config file + var ok bool + if serverConfig, ok = commandConfig.Servers[profile]; !ok { + log.Error().Str("profile", profile).Msg("invalid profile") + return nil, fmt.Errorf("invalid profile: %s", profile) + } + + if skipVerifyFlag { + serverConfig.SkipTLSVerify = true + } + + log.Debug().Msg("return: getServerConfigFromFile()") + return &serverConfig, nil +} + +// getServerConfigFromEnv reads the environment variables and returns the server configuration +func getServerConfigFromEnv() (*auth_providers.Server, error) { + log.Debug().Msg("Enter getServerConfigFromEnv()") + + oAuthNoParamsConfig := &auth_providers.CommandConfigOauth{} + basicAuthNoParamsConfig := &auth_providers.CommandAuthConfigBasic{} + + username, uOk := os.LookupEnv(auth_providers.EnvKeyfactorUsername) + password, pOk := os.LookupEnv(auth_providers.EnvKeyfactorPassword) + domain, dOk := os.LookupEnv(auth_providers.EnvKeyfactorDomain) + hostname, hOk := os.LookupEnv(auth_providers.EnvKeyfactorHostName) + apiPath, aOk := os.LookupEnv(auth_providers.EnvKeyfactorAPIPath) + clientId, cOk := os.LookupEnv(auth_providers.EnvKeyfactorClientID) + clientSecret, csOk := os.LookupEnv(auth_providers.EnvKeyfactorClientSecret) + tokenUrl, tOk := os.LookupEnv(auth_providers.EnvKeyfactorAuthTokenURL) + skipVerify, svOk := os.LookupEnv(auth_providers.EnvKeyfactorSkipVerify) + var skipVerifyBool bool + + isBasicAuth := uOk && pOk + isOAuth := cOk && csOk && tOk + + if skipVerifyFlag { + skipVerifyBool = true + } else if svOk { + //convert to bool + skipVerify = strings.ToLower(skipVerify) + skipVerifyBool = skipVerify == "true" || skipVerify == "1" || skipVerify == "yes" || skipVerify == "y" || skipVerify == "t" + log.Debug().Bool("skipVerifyBool", skipVerifyBool).Msg("skipVerifyBool") + } + if dOk { + log.Debug().Str("domain", domain).Msg("domain found in environment") + } + if hOk { + log.Debug().Str("hostname", hostname).Msg("hostname found in environment") + } + if aOk { + log.Debug().Str("apiPath", apiPath).Msg("apiPath found in environment") + } + + if isBasicAuth { + log.Debug(). + Str("username", username). + Str("password", hashSecretValue(password)). + Str("domain", domain). + Str("hostname", hostname). + Str("apiPath", apiPath). + Bool("skipVerify", skipVerifyBool). + Msg("call: basicAuthNoParamsConfig.Authenticate()") + basicAuthNoParamsConfig.WithCommandHostName(hostname). + WithCommandAPIPath(apiPath). + WithSkipVerify(skipVerifyBool) + + bErr := basicAuthNoParamsConfig. + WithUsername(username). + WithPassword(password). + WithDomain(domain). + Authenticate() + log.Debug().Msg("complete: basicAuthNoParamsConfig.Authenticate()") + if bErr != nil { + log.Error().Err(bErr).Msg("unable to authenticate with provided credentials") + return nil, bErr } - if authConfig.Password != "" { - log.Debug().Str("authConfig.Password", hashSecretValue(authConfig.Password)). - Str("configEntry.Password", hashSecretValue(configEntry.Password)). - Str("flagProfile", flagProfile). - Msg("Config file profile file password is set") - configEntry.Password = authConfig.Password + log.Debug().Msg("return: getServerConfigFromEnv()") + return basicAuthNoParamsConfig.GetServerConfig(), nil + } else if isOAuth { + log.Debug(). + Str("clientId", clientId). + Str("clientSecret", hashSecretValue(clientSecret)). + Str("tokenUrl", tokenUrl). + Str("hostname", hostname). + Str("apiPath", apiPath). + Bool("skipVerify", skipVerifyBool). + Msg("call: oAuthNoParamsConfig.Authenticate()") + _ = oAuthNoParamsConfig.CommandAuthConfig.WithCommandHostName(hostname). + WithCommandAPIPath(apiPath). + WithSkipVerify(skipVerifyBool) + oErr := oAuthNoParamsConfig.Authenticate() + log.Debug().Msg("complete: oAuthNoParamsConfig.Authenticate()") + if oErr != nil { + log.Error().Err(oErr).Msg("unable to authenticate with provided credentials") + return nil, oErr } - if authConfig.Domain != "" { - log.Debug().Str("authConfig.Domain", authConfig.Domain). - Str("configEntry.Domain", configEntry.Domain). - Str("flagProfile", flagProfile). - Msg("Config file profile file domain is set") - configEntry.Domain = authConfig.Domain - } else if authConfig.Username != "" { - log.Debug().Str("authConfig.Username", authConfig.Username). - Str("configEntry.Username", configEntry.Username). - Str("flagProfile", flagProfile). - Msg("Attempting to get domain from username") - tDomain := getDomainFromUsername(authConfig.Username) - if tDomain != "" { - log.Debug().Str("configEntry.Domain", tDomain). - Msg("domain set from username") - configEntry.Domain = tDomain + + log.Debug().Msg("return: getServerConfigFromEnv()") + return oAuthNoParamsConfig.GetServerConfig(), nil + + } + + log.Error().Msg("unable to authenticate with provided credentials") + return nil, fmt.Errorf("incomplete environment variable configuration") + +} + +// authViaConfigFile authenticates using the configuration file +func authViaConfigFile(cfgFile string, cfgProfile string) (*api.Client, error) { + var ( + c *api.Client + cErr error + ) + log.Debug().Msg("call: getServerConfigFromFile()") + conf, err := getServerConfigFromFile(cfgFile, cfgProfile) + log.Debug().Msg("complete: getServerConfigFromFile()") + if err != nil { + log.Error(). + Err(err). + Msg("unable to get server config from file") + return nil, err + } + if conf != nil { + if conf.AuthProvider.Type != "" { + switch conf.AuthProvider.Type { + case "azid", "azure", "az", "akv": + return authViaProvider(cfgFile, cfgProfile) } } - if authConfig.APIPath != "" && configEntry.APIPath == "" { - log.Debug().Str("authConfig.APIPath", authConfig.APIPath). - Str("configEntry.APIPath", configEntry.APIPath). - Str("flagProfile", flagProfile). - Msg("Config file profile file APIPath is set") - configEntry.APIPath = authConfig.APIPath + log.Debug().Msg("call: api.NewKeyfactorClient()") + c, cErr = api.NewKeyfactorClient(conf, nil) + log.Debug().Msg("complete: api.NewKeyfactorClient()") + if cErr != nil { + log.Error(). + Err(cErr). + Msg("unable to create Keyfactor client") + return nil, cErr } - log.Debug().Str("flagProfile", flagProfile).Msg("Setting configEntry") - commandConfig.Servers[flagProfile] = configEntry - } - - if !validConfigFileEntry(commandConfig, flagProfile) { - if !noPrompt { - // Auth user interactively - authConfigEntry := commandConfig.Servers[flagProfile] - commandConfig, _ = authInteractive(authConfigEntry.Hostname, authConfigEntry.Username, authConfigEntry.Password, authConfigEntry.Domain, authConfigEntry.APIPath, flagProfile, false, false, flagConfigFile) - } else { - //log.Fatalf("[ERROR] auth config profile: %s", flagProfile) - log.Error().Str("flagProfile", flagProfile).Msg("invalid auth config profile") - return nil, fmt.Errorf("invalid auth config profile: %s", flagProfile) + log.Debug().Msg("call: c.AuthClient.Authenticate()") + authErr := c.AuthClient.Authenticate() + log.Debug().Msg("complete: c.AuthClient.Authenticate()") + if authErr == nil { + return c, nil } + } + log.Error().Msg("unable to authenticate via config file") + return nil, fmt.Errorf("unable to authenticate via config file '%s' using profile '%s'", cfgFile, cfgProfile) +} - clientAuth.Username = commandConfig.Servers[flagProfile].Username - clientAuth.Password = commandConfig.Servers[flagProfile].Password - clientAuth.Domain = commandConfig.Servers[flagProfile].Domain - clientAuth.Hostname = commandConfig.Servers[flagProfile].Hostname - clientAuth.APIPath = commandConfig.Servers[flagProfile].APIPath +// authSdkViaConfigFile authenticates using the configuration file +func authSdkViaConfigFile(cfgFile string, cfgProfile string) (*keyfactor.APIClient, error) { + var ( + c *keyfactor.APIClient + cErr error + ) + log.Debug().Msg("call: getServerConfigFromFile()") + conf, err := getServerConfigFromFile(cfgFile, cfgProfile) + log.Debug().Msg("complete: getServerConfigFromFile()") + if err != nil { + log.Error(). + Err(err). + Msg("unable to get server config from file") + return nil, err + } + if conf != nil { + if conf.AuthProvider.Type != "" { + switch conf.AuthProvider.Type { + case "azid", "azure", "az", "akv": + log.Debug(). + Str("providerType", conf.AuthProvider.Type). + Str("providerProfile", conf.AuthProvider.Profile). + Str("cfgFile", cfgFile). + Str("cfgProfile", cfgProfile). + Msg("call: authSdkViaProvider()") + return authSdkViaProvider(cfgFile, cfgProfile) + } + } + log.Debug().Msg("call: keyfactor.NewAPIClient()") + c, cErr = keyfactor.NewAPIClient(conf) + log.Debug().Msg("complete: keyfactor.NewAPIClient()") + if cErr != nil { + log.Error(). + Err(cErr). + Msg("unable to create Keyfactor client") + return nil, cErr + } + log.Debug().Msg("call: c.AuthClient.Authenticate()") + authErr := c.AuthClient.Authenticate() + log.Debug().Msg("complete: c.AuthClient.Authenticate()") + if authErr == nil { + return c, nil + } - log.Debug().Str("clientAuth.Username", clientAuth.Username). - Str("clientAuth.Password", hashSecretValue(clientAuth.Password)). - Str("clientAuth.Domain", clientAuth.Domain). - Str("clientAuth.Hostname", clientAuth.Hostname). - Str("clientAuth.APIPath", clientAuth.APIPath). - Msg("Client authentication params") + } + log.Error().Msg("unable to authenticate via config file") + return nil, fmt.Errorf("unable to authenticate via config file '%s' using profile '%s'", cfgFile, cfgProfile) +} - log.Debug().Msg("call: api.NewKeyfactorClient()") - c, err := api.NewKeyfactorClient(&clientAuth) - log.Debug().Msg("complete: api.NewKeyfactorClient()") +// authViaEnvVars authenticates using the environment variables +func authViaEnvVars() (*api.Client, error) { + var ( + c *api.Client + cErr error + ) + log.Debug().Msg("enter: authViaEnvVars()") + log.Debug().Msg("call: getServerConfigFromEnv()") + conf, err := getServerConfigFromEnv() + log.Debug().Msg("complete: getServerConfigFromEnv()") + if err != nil { + log.Error().Err(err).Msg("unable to authenticate via environment variables") + log.Debug().Msg("return: authViaEnvVars()") + return nil, err + } + if conf != nil { + log.Debug().Msg("call: api.NewKeyfactorClient()") + c, cErr = api.NewKeyfactorClient(conf, nil) + log.Debug().Msg("complete: api.NewKeyfactorClient()") + if cErr != nil { + log.Error().Err(cErr).Msg("unable to create Keyfactor client") + log.Debug().Msg("return: authViaEnvVars()") + return nil, cErr + } + log.Debug().Msg("call: c.AuthClient.Authenticate()") + authErr := c.AuthClient.Authenticate() + log.Debug().Msg("complete: c.AuthClient.Authenticate()") + if authErr != nil { + log.Error().Err(authErr).Msg("unable to authenticate via environment variables") + return nil, authErr + } + log.Debug().Msg("return: authViaEnvVars()") + return c, nil + } + log.Error().Msg("unable to authenticate via environment variables") + log.Debug().Msg("return: authViaEnvVars()") + return nil, fmt.Errorf("unable to authenticate via environment variables") +} +// authSdkViaEnvVars authenticates using the environment variables +func authSdkViaEnvVars() (*keyfactor.APIClient, error) { + var ( + c *keyfactor.APIClient + cErr error + ) + log.Debug().Msg("enter: authViaEnvVars()") + log.Debug().Msg("call: getServerConfigFromEnv()") + conf, err := getServerConfigFromEnv() + log.Debug().Msg("complete: getServerConfigFromEnv()") if err != nil { - //fmt.Printf("Error connecting to Keyfactor: %s\n", err) - outputError(err, true, "text") - //log.Fatalf("[ERROR] creating Keyfactor client: %s", err) - return nil, fmt.Errorf("unable to create Keyfactor Command client: %s", err) + log.Error().Err(err).Msg("unable to authenticate via environment variables") + log.Debug().Msg("return: authViaEnvVars()") + return nil, err + } + if conf != nil { + log.Debug().Msg("call: api.NewKeyfactorClient()") + c, cErr = keyfactor.NewAPIClient(conf) + log.Debug().Msg("complete: api.NewKeyfactorClient()") + if cErr != nil { + log.Error().Err(cErr).Msg("unable to create Keyfactor client") + log.Debug().Msg("return: authViaEnvVars()") + return nil, cErr + } + log.Debug().Msg("call: c.AuthClient.Authenticate()") + authErr := c.AuthClient.Authenticate() + log.Debug().Msg("complete: c.AuthClient.Authenticate()") + if authErr != nil { + log.Error().Err(authErr).Msg("unable to authenticate via environment variables") + return nil, authErr + } + log.Debug().Msg("return: authViaEnvVars()") + return c, nil } - log.Info().Msg("Keyfactor Command client created") - return c, nil + log.Error().Msg("unable to authenticate via environment variables") + log.Debug().Msg("return: authViaEnvVars()") + return nil, fmt.Errorf("unable to authenticate via environment variables") } -func initGenClient(flagConfig string, flagProfile string, noPrompt bool, authConfig *api.AuthConfig, saveConfig bool) (*keyfactor.APIClient, error) { - var commandConfig ConfigurationFile +// authViaProvider authenticates using the provider +func authViaProvider(cfgFile string, cfgProfile string) (*api.Client, error) { + log.Debug(). + Str("providerType", providerType). + Str("providerProfile", providerProfile). + Str("cfgFile", cfgFile). + Str("cfgProfile", cfgProfile). + Msg("enter: authViaProvider()") + var ( + c *api.Client + cErr error + ) + + log.Debug().Msg("call: getServerConfigFromFile()") + conf, err := getServerConfigFromFile(cfgFile, cfgProfile) + log.Debug().Msg("complete: getServerConfigFromFile()") + if err != nil { + log.Error().Err(err).Msg("unable to authenticate via provider") + return nil, err + } - if providerType != "" { - return authViaProviderGenClient() + if providerType == "" { + providerType = conf.AuthProvider.Type } - commandConfig, _ = authEnvVars(flagConfig, "", saveConfig) + if providerType == "azid" || providerType == "azure" { + azConfig := &auth_providers.ConfigProviderAzureKeyVault{} + secretName, sOk := os.LookupEnv(auth_providers.EnvAzureSecretName) + vaultName, vOk := os.LookupEnv(auth_providers.EnvAzureVaultName) + if !sOk { + secretName, sOk = conf.AuthProvider.Parameters["secret_name"].(string) + } + if !vOk { + vaultName, vOk = conf.AuthProvider.Parameters["vault_name"].(string) + } + aErr := azConfig. + WithSecretName(secretName). + WithVaultName(vaultName). + Authenticate() + if aErr != nil { + log.Error().Err(aErr).Msg("unable to authenticate via provider") + return nil, aErr + } + cfg, cfgErr := azConfig.LoadConfigFromAzureKeyVault() + if cfgErr != nil { + log.Error().Err(cfgErr).Msg("unable to load config from Azure Key Vault") + return nil, cfgErr + } + log.Debug().Msg("call: api.NewKeyfactorClient()") + serverConfig, serOk := cfg.Servers[providerProfile] + if !serOk { + log.Error().Str("profile", providerProfile).Msg("invalid profile") + return nil, fmt.Errorf("invalid profile: %s", providerProfile) + } + c, cErr = api.NewKeyfactorClient(&serverConfig, nil) + log.Debug().Msg("complete: api.NewKeyfactorClient()") + if cErr != nil { + log.Error().Err(cErr).Msg("unable to create Keyfactor client") + return nil, cErr + } + log.Debug().Msg("call: c.AuthClient.Authenticate()") + authErr := c.AuthClient.Authenticate() + log.Debug().Msg("complete: c.AuthClient.Authenticate()") + if authErr != nil { + log.Error().Err(authErr).Msg("unable to authenticate via provider") + return nil, authErr + } + return c, nil + } + log.Error().Str("providerType", providerType).Msg("unsupported provider type") + return nil, fmt.Errorf("unsupported provider type: %s", providerType) +} - if flagConfig != "" || !validConfigFileEntry(commandConfig, flagProfile) { - commandConfig, _ = authConfigFile(flagConfig, flagProfile, "", noPrompt, saveConfig) +// authSdkViaProvider authenticates using the provider +func authSdkViaProvider(cfgFile string, cfgProfile string) (*keyfactor.APIClient, error) { + log.Debug(). + Str("providerType", providerType). + Str("providerProfile", providerProfile). + Str("cfgFile", cfgFile). + Str("cfgProfile", cfgProfile). + Msg("enter: authViaProvider()") + var ( + c *keyfactor.APIClient + cErr error + ) + + log.Debug().Msg("call: getServerConfigFromFile()") + conf, err := getServerConfigFromFile(cfgFile, cfgProfile) + log.Debug().Msg("complete: getServerConfigFromFile()") + if err != nil { + log.Error().Err(err).Msg("unable to authenticate via provider") + return nil, err } - if flagProfile == "" { - flagProfile = "default" + if providerType == "" { + providerType = conf.AuthProvider.Type } - //Params from authConfig take precedence over everything else - if authConfig != nil { - // replace commandConfig with authConfig params that aren't null or empty - configEntry := commandConfig.Servers[flagProfile] - if authConfig.Hostname != "" { - configEntry.Hostname = authConfig.Hostname + if providerType == "azid" || providerType == "azure" { + azConfig := &auth_providers.ConfigProviderAzureKeyVault{} + secretName, sOk := os.LookupEnv(auth_providers.EnvAzureSecretName) + vaultName, vOk := os.LookupEnv(auth_providers.EnvAzureVaultName) + if !sOk { + secretName, sOk = conf.AuthProvider.Parameters["secret_name"].(string) } - if authConfig.Username != "" { - configEntry.Username = authConfig.Username + if !vOk { + vaultName, vOk = conf.AuthProvider.Parameters["vault_name"].(string) } - if authConfig.Password != "" { - configEntry.Password = authConfig.Password + aErr := azConfig. + WithSecretName(secretName). + WithVaultName(vaultName). + Authenticate() + if aErr != nil { + log.Error().Err(aErr).Msg("unable to authenticate via provider") + return nil, aErr } - if authConfig.Domain != "" { - configEntry.Domain = authConfig.Domain - } else if authConfig.Username != "" { - tDomain := getDomainFromUsername(authConfig.Username) - if tDomain != "" { - configEntry.Domain = tDomain - } + cfg, cfgErr := azConfig.LoadConfigFromAzureKeyVault() + if cfgErr != nil { + log.Error().Err(cfgErr).Msg("unable to load config from Azure Key Vault") + return nil, cfgErr + } + + serverConfig, serOk := cfg.Servers[providerProfile] + if !serOk { + log.Error().Str("profile", providerProfile).Msg("invalid profile") + return nil, fmt.Errorf("invalid profile: %s", providerProfile) } - if authConfig.APIPath != "" { - configEntry.APIPath = authConfig.APIPath + log.Debug().Msg("call: keyfactor.NewAPIClient()") + c, cErr = keyfactor.NewAPIClient(&serverConfig) + log.Debug().Msg("complete: keyfactor.NewAPIClient()") + if cErr != nil { + log.Error().Err(cErr).Msg("unable to create Keyfactor client") + return nil, cErr } - commandConfig.Servers[flagProfile] = configEntry - } - - if !validConfigFileEntry(commandConfig, flagProfile) { - if !noPrompt { - // Auth user interactively - authConfigEntry := commandConfig.Servers[flagProfile] - commandConfig, _ = authInteractive(authConfigEntry.Hostname, authConfigEntry.Username, authConfigEntry.Password, authConfigEntry.Domain, authConfigEntry.APIPath, flagProfile, false, false, flagConfig) - } else { - //log.Fatalf("[ERROR] auth config profile: %s", flagProfile) - log.Error().Str("flagProfile", flagProfile).Msg("invalid auth config profile") - return nil, fmt.Errorf("auth config profile: %s", flagProfile) + log.Debug().Msg("call: c.AuthClient.Authenticate()") + authErr := c.AuthClient.Authenticate() + log.Debug().Msg("complete: c.AuthClient.Authenticate()") + if authErr != nil { + log.Error().Err(authErr).Msg("unable to authenticate via provider") + return nil, authErr } + return c, nil + } + log.Error().Str("providerType", providerType).Msg("unsupported provider type") + return nil, fmt.Errorf("unsupported provider type: %s", providerType) +} + +// initClient initializes the legacy Command API client +func initClient(saveConfig bool) (*api.Client, error) { + log.Debug(). + Str("configFile", configFile). + Str("profile", profile). + Str("providerType", providerType). + Str("providerProfile", providerProfile). + Bool("noPrompt", noPrompt). + Bool("saveConfig", saveConfig). + Bool("logInsecure", logInsecure). + Bool("skipVerifyFlag", skipVerifyFlag). + Str("hostname", kfcHostName). + Str("username", kfcUsername). + Str("password", hashSecretValue(kfcPassword)). + Str("domain", kfcDomain). + Str("clientId", kfcClientId). + Str("clientSecret", hashSecretValue(kfcClientSecret)). + Str("apiPath", kfcAPIPath). + Str("providerType", providerType). + Str("providerProfile", providerProfile). + Msg("enter: initClient()") + var ( + c *api.Client + envCfgErr error + cfgErr error + explicitCfgErr error + ) + + if providerType != "" { + log.Debug(). + Str("providerType", providerType). + Msg("call: authViaProvider()") + return authViaProvider("", "") + } + log.Debug(). + Msg("providerType is empty attempting to authenticate via params") + + if configFile != "" || profile != "" { + log.Info(). + Str("configFile", configFile). + Str("profile", profile). + Msg("authenticating via config file") + c, explicitCfgErr = authViaConfigFile(configFile, profile) + if explicitCfgErr == nil { + log.Info(). + Str("configFile", configFile). + Str("profile", profile). + Msgf("Authenticated via config file %s using profile %s", configFile, profile) + return c, nil + } + log.Error(). + Err(explicitCfgErr). + Str("configFile", configFile). + Str("profile", profile). + Msg("unable to authenticate using explicit config file and/or profile") + return nil, explicitCfgErr // return explicit error + } + + log.Info().Msg("authenticating via environment variables") + log.Debug().Msg("call: authViaEnvVars()") + c, envCfgErr = authViaEnvVars() + log.Debug().Msg("returned: authViaEnvVars()") + if envCfgErr == nil { + log.Info().Msg("Authenticated via environment variables") + return c, nil } - sdkClientConfig := make(map[string]string) - sdkClientConfig["host"] = commandConfig.Servers[flagProfile].Hostname - sdkClientConfig["username"] = commandConfig.Servers[flagProfile].Username - sdkClientConfig["password"] = commandConfig.Servers[flagProfile].Password - sdkClientConfig["domain"] = commandConfig.Servers[flagProfile].Domain + log.Info(). + Str("configFile", DefaultConfigFileName). + Str("profile", "default"). + Msg("implicit authenticating via config file using default profile") + log.Debug().Msg("call: authViaConfigFile()") + c, cfgErr = authViaConfigFile("", "") + if cfgErr == nil { + log.Info(). + Str("configFile", DefaultConfigFileName). + Str("profile", "default"). + Msgf("authenticated implictly via config file '%s' using 'default' profile", DefaultConfigFileName) + return c, nil + } - configuration := keyfactor.NewConfiguration(sdkClientConfig) - c := keyfactor.NewAPIClient(configuration) - return c, nil + log.Error(). + Err(cfgErr). + Err(envCfgErr). + Msg("unable to authenticate to Keyfactor Command") + log.Debug().Msg("return: initClient()") + return nil, cfgErr +} + +// initGenClient initializes the SDK Command API client +func initGenClient( + saveConfig bool, +) (*keyfactor.APIClient, error) { + log.Debug(). + Str("configFile", configFile). + Str("profile", profile). + Str("providerType", providerType). + Str("providerProfile", providerProfile). + Bool("noPrompt", noPrompt). + Bool("saveConfig", saveConfig). + Str("hostname", kfcHostName). + Str("username", kfcUsername). + Str("password", hashSecretValue(kfcPassword)). + Str("domain", kfcDomain). + Str("clientId", kfcClientId). + Str("clientSecret", hashSecretValue(kfcClientSecret)). + Str("apiPath", kfcAPIPath). + Str("providerType", providerType). + Str("providerProfile", providerProfile). + Msg("enter: initGenClient()") + + var ( + c *keyfactor.APIClient + envCErr error + cfErr error + ) + + if providerType != "" { + log.Debug(). + Str("providerType", providerType). + Msg("call: authSdkViaProvider()") + return authSdkViaProvider("", "") + } + log.Debug(). + Msg("providerType is empty attempting to authenticate via params") + + if configFile != "" || profile != "" { + log.Info(). + Str("configFile", configFile). + Str("profile", profile). + Msg("authenticating via config file") + c, cfErr = authSdkViaConfigFile(configFile, profile) + if cfErr == nil { + log.Info(). + Str("configFile", configFile). + Str("profile", profile). + Msgf("Authenticated via config file %s using profile %s", configFile, profile) + return c, nil + } + } + + log.Info().Msg("authenticating via environment variables") + log.Debug().Msg("call: authViaEnvVars()") + c, envCErr = authSdkViaEnvVars() + log.Debug().Msg("returned: authViaEnvVars()") + if envCErr == nil { + log.Info().Msg("authenticated via environment variables") + return c, nil + } + + log.Info(). + Str("configFile", DefaultConfigFileName). + Str("profile", "default"). + Msg("implicit authenticating via config file using default profile") + log.Debug().Msg("call: authViaConfigFile()") + c, cfErr = authSdkViaConfigFile("", "") + if cfErr == nil { + log.Info(). + Str("configFile", DefaultConfigFileName). + Str("profile", "default"). + Msgf("authenticated implictly via config file '%s' using 'default' profile", DefaultConfigFileName) + return c, nil + } + + log.Error(). + Err(cfErr). + Err(envCErr). + Msg("unable to authenticate") + return nil, fmt.Errorf("unable to authenticate to Keyfactor Command with provided credentials, please check your configuration") } var makeDocsCmd = &cobra.Command{ @@ -306,24 +743,127 @@ func init() { defaultConfigPath := fmt.Sprintf("$HOME/.keyfactor/%s", DefaultConfigFileName) - RootCmd.PersistentFlags().StringVarP(&configFile, "config", "", "", fmt.Sprintf("Full path to config file in JSON format. (default is %s)", defaultConfigPath)) - RootCmd.PersistentFlags().BoolVar(&noPrompt, "no-prompt", false, "Do not prompt for any user input and assume defaults or environmental variables are set.") - RootCmd.PersistentFlags().BoolVar(&expEnabled, "exp", false, "Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.)") + RootCmd.PersistentFlags().StringVarP( + &configFile, + "config", + "", + "", + fmt.Sprintf("Full path to config file in JSON format. (default is %s)", defaultConfigPath), + ) + RootCmd.PersistentFlags().BoolVar( + &noPrompt, + "no-prompt", + false, + "Do not prompt for any user input and assume defaults or environmental variables are set.", + ) + RootCmd.PersistentFlags().BoolVar( + &expEnabled, + "exp", + false, + "Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.)", + ) + RootCmd.PersistentFlags().BoolVar( + &offline, + "offline", + false, + "Will not attempt to connect to GitHub for latest release information and resources.", + ) RootCmd.PersistentFlags().BoolVar(&debugFlag, "debug", false, "Enable debugFlag logging.") - RootCmd.PersistentFlags().BoolVar(&logInsecure, "log-insecure", false, "Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.)") - RootCmd.PersistentFlags().StringVarP(&profile, "profile", "", "", "Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists.") - RootCmd.PersistentFlags().StringVar(&outputFormat, "format", "text", "How to format the CLI output. Currently only `text` is supported.") + RootCmd.PersistentFlags().BoolVar( + &skipVerifyFlag, "skip-tls-verify", false, + "Disable TLS verification for API requests to Keyfactor Command.", + ) + //RootCmd.PersistentFlags().BoolVar( + // &logInsecure, + // "log-insecure", + // false, + // "Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.)", + //) + RootCmd.PersistentFlags().StringVarP( + &profile, + "profile", + "", + "", + "Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists.", + ) + RootCmd.PersistentFlags().StringVar( + &outputFormat, + "format", + "text", + "How to format the CLI output. Currently only `text` is supported.", + ) RootCmd.PersistentFlags().StringVar(&providerType, "auth-provider-type", "", "Provider type choices: (azid)") // Validating the provider-type flag against the predefined choices RootCmd.PersistentFlags().SetAnnotation("auth-provider-type", cobra.BashCompCustom, ProviderTypeChoices) - RootCmd.PersistentFlags().StringVarP(&providerProfile, "auth-provider-profile", "", "default", "The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists.") - - RootCmd.PersistentFlags().StringVarP(&kfcUsername, "username", "", "", "Username to use for authenticating to Keyfactor Command.") - RootCmd.PersistentFlags().StringVarP(&kfcHostName, "hostname", "", "", "Hostname to use for authenticating to Keyfactor Command.") - RootCmd.PersistentFlags().StringVarP(&kfcPassword, "password", "", "", "Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text.") - RootCmd.PersistentFlags().StringVarP(&kfcDomain, "domain", "", "", "Domain to use for authenticating to Keyfactor Command.") - RootCmd.PersistentFlags().StringVarP(&kfcAPIPath, "api-path", "", "KeyfactorAPI", "API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI)") + RootCmd.PersistentFlags().StringVarP( + &providerProfile, + "auth-provider-profile", + "", + "default", + "The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists.", + ) + + RootCmd.PersistentFlags().StringVarP( + &kfcUsername, + "username", + "", + "", + "Username to use for authenticating to Keyfactor Command.", + ) + + RootCmd.PersistentFlags().StringVarP( + &kfcClientId, + "client-id", + "", + "", + "OAuth2 client-id to use for authenticating to Keyfactor Command.", + ) + + RootCmd.PersistentFlags().StringVarP( + &kfcClientSecret, + "client-secret", + "", + "", + "OAuth2 client-secret to use for authenticating to Keyfactor Command.", + ) + + RootCmd.PersistentFlags().StringVarP( + &kfcClientId, + "token-url", + "", + "", + "OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command.", + ) + + RootCmd.PersistentFlags().StringVarP( + &kfcHostName, + "hostname", + "", + "", + "Hostname to use for authenticating to Keyfactor Command.", + ) + RootCmd.PersistentFlags().StringVarP( + &kfcPassword, + "password", + "", + "", + "Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text.", + ) + RootCmd.PersistentFlags().StringVarP( + &kfcDomain, + "domain", + "", + "", + "Domain to use for authenticating to Keyfactor Command.", + ) + RootCmd.PersistentFlags().StringVarP( + &kfcAPIPath, + "api-path", + "", + "KeyfactorAPI", + "API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI)", + ) // Cobra also supports local flags, which will only run // when this action is called directly. diff --git a/cmd/rot.go b/cmd/rot.go index 897e65f..2a7478c 100644 --- a/cmd/rot.go +++ b/cmd/rot.go @@ -14,1193 +14,1406 @@ package cmd -import ( - "bufio" - "encoding/csv" - "encoding/json" - "errors" - "fmt" - "github.com/Keyfactor/keyfactor-go-client/v2/api" - "github.com/spf13/cobra" - "log" - "os" - "strconv" - "strings" -) - -type templateType string -type StoreCSVEntry struct { - ID string `json:"id"` - Type string `json:"type"` - Machine string `json:"address"` - Path string `json:"path"` - Thumbprints map[string]bool `json:"thumbprints,omitempty"` - Serials map[string]bool `json:"serials,omitempty"` - Ids map[int]bool `json:"ids,omitempty"` -} -type ROTCert struct { - ID int `json:"id,omitempty"` - ThumbPrint string `json:"thumbprint,omitempty"` - CN string `json:"cn,omitempty"` - Locations []api.CertificateLocations `json:"locations,omitempty"` -} -type ROTAction struct { - StoreID string `json:"store_id,omitempty"` - StoreType string `json:"store_type,omitempty"` - StorePath string `json:"store_path,omitempty"` - Thumbprint string `json:"thumbprint,omitempty"` - CertID int `json:"cert_id,omitempty" mapstructure:"CertID,omitempty"` - AddCert bool `json:"add,omitempty" mapstructure:"AddCert,omitempty"` - RemoveCert bool `json:"remove,omitempty" mapstructure:"RemoveCert,omitempty"` -} - -const ( - tTypeCerts templateType = "certs" - reconcileDefaultFileName string = "rot_audit.csv" -) - -var ( - AuditHeader = []string{"Thumbprint", "CertID", "SubjectName", "Issuer", "StoreID", "StoreType", "Machine", "Path", "AddCert", "RemoveCert", "Deployed", "AuditDate"} - ReconciledAuditHeader = []string{"Thumbprint", "CertID", "SubjectName", "Issuer", "StoreID", "StoreType", "Machine", "Path", "AddCert", "RemoveCert", "Deployed", "ReconciledDate"} - StoreHeader = []string{"StoreID", "StoreType", "StoreMachine", "StorePath", "ContainerId", "ContainerName", "LastQueriedDate"} - CertHeader = []string{"Thumbprint", "SubjectName", "Issuer", "CertID", "Locations", "LastQueriedDate"} -) - -// String is used both by fmt.Print and by Cobra in help text -func (e *templateType) String() string { - return string(*e) -} - -// Set must have pointer receiver, so it doesn't change the value of a copy -func (e *templateType) Set(v string) error { - switch v { - case "certs", "stores", "actions": - *e = templateType(v) - return nil - default: - return errors.New(`must be one of "certs", "stores", or "actions"`) - } -} - -// Type is only used in help text -func (e *templateType) Type() string { - return "string" -} - -func templateTypeCompletion(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { - return []string{ - "certs\tGenerates template CSV for certificate input to be used w/ `--add-certs` or `--remove-certs`", - "stores\tGenerates template CSV for certificate input to be used w/ `--stores`", - "actions\tGenerates template CSV for certificate input to be used w/ `--actions`", - }, cobra.ShellCompDirectiveDefault -} - -func generateAuditReport(addCerts map[string]string, removeCerts map[string]string, stores map[string]StoreCSVEntry, outpath string, kfClient *api.Client) ([][]string, map[string][]ROTAction, error) { - log.Println("[DEBUG] generateAuditReport called") - var ( - data [][]string - ) - - data = append(data, AuditHeader) - var csvFile *os.File - var fErr error - if outpath == "" { - csvFile, fErr = os.Create(reconcileDefaultFileName) - outpath = reconcileDefaultFileName - } else { - csvFile, fErr = os.Create(outpath) - } - - if fErr != nil { - fmt.Printf("%s", fErr) - log.Fatalf("[ERROR] creating audit file: %s", fErr) - } - csvWriter := csv.NewWriter(csvFile) - cErr := csvWriter.Write(AuditHeader) - if cErr != nil { - fmt.Printf("%s", cErr) - log.Fatalf("[ERROR] writing audit header: %s", cErr) - } - actions := make(map[string][]ROTAction) - - for _, cert := range addCerts { - certLookupReq := api.GetCertificateContextArgs{ - IncludeMetadata: boolToPointer(true), - IncludeLocations: boolToPointer(true), - CollectionId: nil, - Thumbprint: cert, - Id: 0, - } - certLookup, err := kfClient.GetCertificateContext(&certLookupReq) - if err != nil { - fmt.Printf("[ERROR] looking up certificate %s: %s\n", cert, err) - log.Printf("[ERROR] looking up cert: %s\n%v", cert, err) - continue - } - certID := certLookup.Id - certIDStr := strconv.Itoa(certID) - for _, store := range stores { - if _, ok := store.Thumbprints[cert]; ok { - // Cert is already in the store do nothing - row := []string{cert, certIDStr, certLookup.IssuedDN, certLookup.IssuerDN, store.ID, store.Type, store.Machine, store.Path, "false", "false", "true", getCurrentTime("")} - data = append(data, row) - wErr := csvWriter.Write(row) - if wErr != nil { - fmt.Printf("[ERROR] writing audit file row: %s\n", wErr) - log.Printf("[ERROR] writing audit row: %s", wErr) - } - } else { - // Cert is not deployed to this store and will need to be added - row := []string{cert, certIDStr, certLookup.IssuedDN, certLookup.IssuerDN, store.ID, store.Type, store.Machine, store.Path, "true", "false", "false", getCurrentTime("")} - data = append(data, row) - wErr := csvWriter.Write(row) - if wErr != nil { - fmt.Printf("[ERROR] writing audit file row: %s\n", wErr) - log.Printf("[ERROR] writing audit row: %s", wErr) - } - actions[cert] = append(actions[cert], ROTAction{ - Thumbprint: cert, - CertID: certID, - StoreID: store.ID, - StoreType: store.Type, - StorePath: store.Path, - AddCert: true, - RemoveCert: false, - }) - } - } - } - for _, cert := range removeCerts { - certLookupReq := api.GetCertificateContextArgs{ - IncludeMetadata: boolToPointer(true), - IncludeLocations: boolToPointer(true), - CollectionId: nil, - Thumbprint: cert, - Id: 0, - } - certLookup, err := kfClient.GetCertificateContext(&certLookupReq) - if err != nil { - log.Printf("[ERROR] looking up cert: %s", err) - continue - } - certID := certLookup.Id - certIDStr := strconv.Itoa(certID) - for _, store := range stores { - if _, ok := store.Thumbprints[cert]; ok { - // Cert is deployed to this store and will need to be removed - row := []string{cert, certIDStr, certLookup.IssuedDN, certLookup.IssuerDN, store.ID, store.Type, store.Machine, store.Path, "false", "true", "true", getCurrentTime("")} - data = append(data, row) - wErr := csvWriter.Write(row) - if wErr != nil { - fmt.Printf("%s", wErr) - log.Printf("[ERROR] writing row to CSV: %s", wErr) - } - actions[cert] = append(actions[cert], ROTAction{ - Thumbprint: cert, - CertID: certID, - StoreID: store.ID, - StoreType: store.Type, - StorePath: store.Path, - AddCert: false, - RemoveCert: true, - }) - } else { - // Cert is not deployed to this store do nothing - row := []string{cert, certIDStr, certLookup.IssuedDN, certLookup.IssuerDN, store.ID, store.Type, store.Machine, store.Path, "false", "false", "false", getCurrentTime("")} - data = append(data, row) - wErr := csvWriter.Write(row) - if wErr != nil { - fmt.Printf("%s", wErr) - log.Printf("[ERROR] writing row to CSV: %s", wErr) - } - } - } - } - csvWriter.Flush() - ioErr := csvFile.Close() - if ioErr != nil { - fmt.Println(ioErr) - log.Printf("[ERROR] closing audit file: %s", ioErr) - } - fmt.Printf("Audit report written to %s\n", outpath) - return data, actions, nil -} - -func reconcileRoots(actions map[string][]ROTAction, kfClient *api.Client, reportFile string, dryRun bool) error { - log.Printf("[DEBUG] Reconciling roots") - if len(actions) == 0 { - log.Printf("[INFO] No actions to take, roots are up-to-date.") - return nil - } - rFileName := fmt.Sprintf("%s_reconciled.csv", strings.Split(reportFile, ".csv")[0]) - csvFile, fErr := os.Create(rFileName) - if fErr != nil { - fmt.Printf("[ERROR] creating reconciled report file: %s", fErr) - } - csvWriter := csv.NewWriter(csvFile) - cErr := csvWriter.Write(ReconciledAuditHeader) - if cErr != nil { - fmt.Printf("%s", cErr) - log.Fatalf("[ERROR] writing audit header: %s", cErr) - } - for thumbprint, action := range actions { - - for _, a := range action { - if a.AddCert { - log.Printf("[INFO] Adding cert %s to store %s(%s)", thumbprint, a.StoreID, a.StorePath) - if !dryRun { - cStore := api.CertificateStore{ - CertificateStoreId: a.StoreID, - Overwrite: true, - } - var stores []api.CertificateStore - stores = append(stores, cStore) - schedule := &api.InventorySchedule{ - Immediate: boolToPointer(true), - } - addReq := api.AddCertificateToStore{ - CertificateId: a.CertID, - CertificateStores: &stores, - InventorySchedule: schedule, - } - log.Printf("[DEBUG] Adding cert %s to store %s", thumbprint, a.StoreID) - log.Printf("[TRACE] Add request: %+v", addReq) - addReqJSON, _ := json.Marshal(addReq) - log.Printf("[TRACE] Add request JSON: %s", addReqJSON) - _, err := kfClient.AddCertificateToStores(&addReq) - if err != nil { - fmt.Printf("[ERROR] adding cert %s (%d) to store %s (%s): %s\n", a.Thumbprint, a.CertID, a.StoreID, a.StorePath, err) - continue - } - } else { - log.Printf("[INFO] DRY RUN: Would have added cert %s from store %s", thumbprint, a.StoreID) - } - } else if a.RemoveCert { - if !dryRun { - log.Printf("[INFO] Removing cert from store %s", a.StoreID) - cStore := api.CertificateStore{ - CertificateStoreId: a.StoreID, - Alias: a.Thumbprint, - } - var stores []api.CertificateStore - stores = append(stores, cStore) - schedule := &api.InventorySchedule{ - Immediate: boolToPointer(true), - } - removeReq := api.RemoveCertificateFromStore{ - CertificateId: a.CertID, - CertificateStores: &stores, - InventorySchedule: schedule, - } - _, err := kfClient.RemoveCertificateFromStores(&removeReq) - if err != nil { - fmt.Printf("[ERROR] removing cert %s (ID: %d) from store %s (%s): %s\n", a.Thumbprint, a.CertID, a.StoreID, a.StorePath, err) - } - } else { - fmt.Printf("DRY RUN: Would have removed cert %s from store %s\n", thumbprint, a.StoreID) - log.Printf("[INFO] DRY RUN: Would have removed cert %s from store %s", thumbprint, a.StoreID) - } - } - } - } - return nil -} - -func readCertsFile(certsFilePath string, kfclient *api.Client) (map[string]string, error) { - // Read in the cert CSV - csvFile, _ := os.Open(certsFilePath) - reader := csv.NewReader(bufio.NewReader(csvFile)) - certEntries, _ := reader.ReadAll() - var certs = make(map[string]string) - for _, entry := range certEntries { - switch entry[0] { - case "CertID", "thumbprint", "id", "CertId", "Thumbprint": - continue // Skip header - } - certs[entry[0]] = entry[0] - } - return certs, nil -} - -func isRootStore(st *api.GetCertificateStoreResponse, invs *[]api.CertStoreInventoryV1, minCerts int, maxKeys int, maxLeaf int) bool { - leafCount := 0 - keyCount := 0 - certCount := 0 - for _, inv := range *invs { - log.Printf("[DEBUG] inv: %v", inv) - certCount += len(inv.Certificates) - - for _, cert := range inv.Certificates { - if cert.IssuedDN != cert.IssuerDN { - leafCount++ - } - if inv.Parameters["PrivateKeyEntry"] == "Yes" { - keyCount++ - } - } - } - if certCount < minCerts && minCerts >= 0 { - log.Printf("[DEBUG] Store %s has %d certs, less than the required count of %d", st.Id, certCount, minCerts) - return false - } - if leafCount > maxLeaf && maxLeaf >= 0 { - log.Printf("[DEBUG] Store %s has too many leaf certs", st.Id) - return false - } - - if keyCount > maxKeys && maxKeys >= 0 { - log.Printf("[DEBUG] Store %s has too many keys", st.Id) - return false - } - - return true -} - -var ( - rotCmd = &cobra.Command{ - Use: "rot", - Short: "Root of trust utility", - Long: `Root of trust allows you to manage your trusted roots using Keyfactor certificate stores. -For example if you wish to add a list of "root" certs to a list of certificate stores you would simply generate and fill -out the template CSV file. These template files can be generated with the following commands: -kfutil stores rot generate-template --type certs -kfutil stores rot generate-template --type stores -Once those files are filled out you can use the following command to add the certs to the stores: -kfutil stores rot audit --certs-file --stores-file -Will generate a CSV report file 'rot_audit.csv' of what actions will be taken. If those actions are correct you can run -the following command to actually perform the actions: -kfutil stores rot reconcile --certs-file --stores-file -OR if you want to use the audit report file generated you can run this command: -kfutil stores rot reconcile --import-csv -`, - } - rotAuditCmd = &cobra.Command{ - Use: "audit", - Aliases: nil, - SuggestFor: nil, - Short: "Audit generates a CSV report of what actions will be taken based on input CSV files.", - Long: `Root of Trust Audit: Will read and parse inputs to generate a report of certs that need to be added or removed from the "root of trust" stores.`, - Example: "", - ValidArgs: nil, - ValidArgsFunction: nil, - Args: nil, - ArgAliases: nil, - BashCompletionFunction: "", - Deprecated: "", - Annotations: nil, - Version: "", - PersistentPreRun: nil, - PersistentPreRunE: nil, - PreRun: nil, - PreRunE: nil, - Run: func(cmd *cobra.Command, args []string) { - // Global flags - debugFlag, _ := cmd.Flags().GetBool("debugFlag") - configFile, _ := cmd.Flags().GetString("config") - noPrompt, _ := cmd.Flags().GetBool("no-prompt") - profile, _ := cmd.Flags().GetString("profile") - - kfcUsername, _ := cmd.Flags().GetString("kfcUsername") - kfcPassword, _ := cmd.Flags().GetString("kfcPassword") - kfcDomain, _ := cmd.Flags().GetString("kfcDomain") - - authConfig := createAuthConfigFromParams(kfcHostName, kfcUsername, kfcPassword, kfcDomain, kfcAPIPath) - - debugModeEnabled := checkDebug(debugFlag) - log.Println("Debug mode enabled: ", debugModeEnabled) - var lookupFailures []string - kfClient, _ := initClient(configFile, profile, "", "", noPrompt, authConfig, false) - storesFile, _ := cmd.Flags().GetString("stores") - addRootsFile, _ := cmd.Flags().GetString("add-certs") - removeRootsFile, _ := cmd.Flags().GetString("remove-certs") - minCerts, _ := cmd.Flags().GetInt("min-certs") - maxLeaves, _ := cmd.Flags().GetInt("max-leaf-certs") - maxKeys, _ := cmd.Flags().GetInt("max-keys") - dryRun, _ := cmd.Flags().GetBool("dry-run") - outpath, _ := cmd.Flags().GetString("outpath") - // Read in the stores CSV - log.Printf("[DEBUG] storesFile: %s", storesFile) - log.Printf("[DEBUG] addRootsFile: %s", addRootsFile) - log.Printf("[DEBUG] removeRootsFile: %s", removeRootsFile) - log.Printf("[DEBUG] dryRun: %t", dryRun) - // Read in the stores CSV - csvFile, _ := os.Open(storesFile) - reader := csv.NewReader(bufio.NewReader(csvFile)) - storeEntries, _ := reader.ReadAll() - var stores = make(map[string]StoreCSVEntry) - validHeader := false - for _, entry := range storeEntries { - if strings.EqualFold(strings.Join(entry, ","), strings.Join(StoreHeader, ",")) { - validHeader = true - continue // Skip header - } - if !validHeader { - fmt.Printf("[ERROR] Invalid header in stores file. Expected: %s", strings.Join(StoreHeader, ",")) - log.Fatalf("[ERROR] Stores CSV file is missing a valid header") - } - apiResp, err := kfClient.GetCertificateStoreByID(entry[0]) - if err != nil { - log.Printf("[ERROR] getting cert store: %s", err) - _ = append(lookupFailures, strings.Join(entry, ",")) - continue - } - - inventory, invErr := kfClient.GetCertStoreInventoryV1(entry[0]) - if invErr != nil { - log.Printf("[ERROR] getting cert store inventory for: %s\n%s", entry[0], invErr) - } - - if !isRootStore(apiResp, inventory, minCerts, maxLeaves, maxKeys) { - fmt.Printf("Store %s is not a root store, skipping.\n", entry[0]) - log.Printf("[WARN] Store %s is not a root store", apiResp.Id) - continue - } else { - log.Printf("[INFO] Store %s is a root store", apiResp.Id) - } - - stores[entry[0]] = StoreCSVEntry{ - ID: entry[0], - Type: entry[1], - Machine: entry[2], - Path: entry[3], - Thumbprints: make(map[string]bool), - Serials: make(map[string]bool), - Ids: make(map[int]bool), - } - for _, cert := range *inventory { - thumb := cert.Thumbprints - for t, v := range thumb { - stores[entry[0]].Thumbprints[t] = v - } - for t, v := range cert.Serials { - stores[entry[0]].Serials[t] = v - } - for t, v := range cert.Ids { - stores[entry[0]].Ids[t] = v - } - } - - } - - // Read in the add addCerts CSV - var certsToAdd = make(map[string]string) - if addRootsFile != "" { - var rcfErr error - certsToAdd, rcfErr = readCertsFile(addRootsFile, kfClient) - if rcfErr != nil { - fmt.Printf("[ERROR] reading certs file %s: %s", addRootsFile, rcfErr) - log.Fatalf("[ERROR] reading addCerts file: %s", rcfErr) - } - addCertsJSON, _ := json.Marshal(certsToAdd) - log.Printf("[DEBUG] add certs JSON: %s", string(addCertsJSON)) - log.Println("[DEBUG] AddCert ROT called") - } else { - log.Printf("[DEBUG] No addCerts file specified") - log.Printf("[DEBUG] No addCerts = %s", certsToAdd) - } - - // Read in the remove removeCerts CSV - var certsToRemove = make(map[string]string) - if removeRootsFile != "" { - var rcfErr error - certsToRemove, rcfErr = readCertsFile(removeRootsFile, kfClient) - if rcfErr != nil { - fmt.Printf("[ERROR] reading removeCerts file %s: %s", removeRootsFile, rcfErr) - log.Fatalf("[ERROR] reading removeCerts file: %s", rcfErr) - } - removeCertsJSON, _ := json.Marshal(certsToRemove) - log.Printf("[DEBUG] remove certs JSON: %s", string(removeCertsJSON)) - } else { - log.Printf("[DEBUG] No removeCerts file specified") - log.Printf("[DEBUG] No removeCerts = %s", certsToRemove) - } - _, _, gErr := generateAuditReport(certsToAdd, certsToRemove, stores, outpath, kfClient) - if gErr != nil { - log.Fatalf("[ERROR] generating audit report: %s", gErr) - } - }, - RunE: nil, - PostRun: nil, - PostRunE: nil, - PersistentPostRun: nil, - PersistentPostRunE: nil, - FParseErrWhitelist: cobra.FParseErrWhitelist{}, - CompletionOptions: cobra.CompletionOptions{}, - TraverseChildren: false, - Hidden: false, - SilenceErrors: false, - SilenceUsage: false, - DisableFlagParsing: false, - DisableAutoGenTag: false, - DisableFlagsInUseLine: false, - DisableSuggestions: false, - SuggestionsMinimumDistance: 0, - } - rotReconcileCmd = &cobra.Command{ - Use: "reconcile", - Aliases: nil, - SuggestFor: nil, - Short: "Reconcile either takes in or will generate an audit report and then add/remove certs as needed.", - Long: `Root of Trust (rot): Will parse either a combination of CSV files that define certs to -add and/or certs to remove with a CSV of certificate stores or an audit CSV file. If an audit CSV file is provided, the -add and remove actions defined in the audit file will be immediately executed. If a combination of CSV files are provided, -the utility will first generate an audit report and then execute the add/remove actions defined in the audit report.`, - Example: "", - ValidArgs: nil, - ValidArgsFunction: nil, - Args: nil, - ArgAliases: nil, - BashCompletionFunction: "", - Deprecated: "", - Annotations: nil, - Version: "", - PersistentPreRun: nil, - PersistentPreRunE: nil, - PreRun: nil, - PreRunE: nil, - Run: func(cmd *cobra.Command, args []string) { - // Global flags - debugFlag, _ := cmd.Flags().GetBool("debugFlag") - configFile, _ := cmd.Flags().GetString("config") - noPrompt, _ := cmd.Flags().GetBool("no-prompt") - profile, _ := cmd.Flags().GetString("profile") - - kfcUsername, _ := cmd.Flags().GetString("kfcUsername") - kfcPassword, _ := cmd.Flags().GetString("kfcPassword") - kfcDomain, _ := cmd.Flags().GetString("kfcDomain") - - authConfig := createAuthConfigFromParams(kfcHostName, kfcUsername, kfcPassword, kfcDomain, kfcAPIPath) - - debugModeEnabled := checkDebug(debugFlag) - - log.Println("Debug mode enabled: ", debugModeEnabled) - - var lookupFailures []string - kfClient, _ := initClient(configFile, profile, "", "", noPrompt, authConfig, false) - storesFile, _ := cmd.Flags().GetString("stores") - addRootsFile, _ := cmd.Flags().GetString("add-certs") - isCSV, _ := cmd.Flags().GetBool("import-csv") - reportFile, _ := cmd.Flags().GetString("input-file") - removeRootsFile, _ := cmd.Flags().GetString("remove-certs") - minCerts, _ := cmd.Flags().GetInt("min-certs") - maxLeaves, _ := cmd.Flags().GetInt("max-leaf-certs") - maxKeys, _ := cmd.Flags().GetInt("max-keys") - dryRun, _ := cmd.Flags().GetBool("dry-run") - outpath, _ := cmd.Flags().GetString("outpath") - - log.Printf("[DEBUG] configFile: %s", configFile) - log.Printf("[DEBUG] storesFile: %s", storesFile) - log.Printf("[DEBUG] addRootsFile: %s", addRootsFile) - log.Printf("[DEBUG] removeRootsFile: %s", removeRootsFile) - log.Printf("[DEBUG] dryRun: %t", dryRun) - - // Parse existing audit report - if isCSV && reportFile != "" { - log.Printf("[DEBUG] isCSV: %t", isCSV) - log.Printf("[DEBUG] reportFile: %s", reportFile) - // Read in the CSV - csvFile, err := os.Open(reportFile) - if err != nil { - fmt.Printf("[ERROR] opening file: %s", err) - log.Fatalf("[ERROR] opening CSV file: %s", err) - } - validHeader := false - - aCSV := csv.NewReader(csvFile) - aCSV.FieldsPerRecord = -1 - inFile, cErr := aCSV.ReadAll() - if cErr != nil { - fmt.Printf("[ERROR] reading CSV file: %s", cErr) - log.Fatalf("[ERROR] reading CSV file: %s", cErr) - } - actions := make(map[string][]ROTAction) - fieldMap := make(map[int]string) - for i, field := range AuditHeader { - fieldMap[i] = field - } - for ri, row := range inFile { - if strings.EqualFold(strings.Join(row, ","), strings.Join(AuditHeader, ",")) { - validHeader = true - continue // Skip header - } - if !validHeader { - fmt.Printf("[ERROR] Invalid header in stores file. Expected: %s", strings.Join(AuditHeader, ",")) - log.Fatalf("[ERROR] Stores CSV file is missing a valid header") - } - action := make(map[string]interface{}) - - for i, field := range row { - fieldInt, iErr := strconv.Atoi(field) - if iErr != nil { - log.Printf("[DEBUG] Field %s is not an int", field) - action[fieldMap[i]] = field - } else { - action[fieldMap[i]] = fieldInt - } - - } - - addCertStr, aOk := action["AddCert"].(string) - if !aOk { - addCertStr = "" - } - addCert, acErr := strconv.ParseBool(addCertStr) - if acErr != nil { - addCert = false - } - - removeCertStr, rOk := action["RemoveCert"].(string) - if !rOk { - removeCertStr = "" - } - removeCert, rcErr := strconv.ParseBool(removeCertStr) - if rcErr != nil { - removeCert = false - } - - sType, sOk := action["StoreType"].(string) - if !sOk { - sType = "" - } - - sPath, pOk := action["Path"].(string) - if !pOk { - sPath = "" - } - - tp, tpOk := action["Thumbprint"].(string) - if !tpOk { - tp = "" - } - cid, cidOk := action["CertID"].(int) - if !cidOk { - cid = -1 - } - - if !tpOk && !cidOk { - fmt.Printf("[ERROR] Missing Thumbprint or CertID for row %d in report file %s", ri, reportFile) - log.Printf("[ERROR] Invalid action: %v", action) - continue - } - - sId, sIdOk := action["StoreID"].(string) - if !sIdOk { - fmt.Printf("[ERROR] Missing StoreID for row %d in report file %s", ri, reportFile) - log.Printf("[ERROR] Invalid action: %v", action) - continue - } - if cid == -1 && tp != "" { - certLookupReq := api.GetCertificateContextArgs{ - IncludeMetadata: boolToPointer(true), - IncludeLocations: boolToPointer(true), - CollectionId: nil, - Thumbprint: tp, - Id: 0, - } - certLookup, err := kfClient.GetCertificateContext(&certLookupReq) - if err != nil { - fmt.Printf("[ERROR] looking up certificate %s: %s\n", tp, err) - log.Printf("[ERROR] looking up cert: %s\n%v", tp, err) - continue - } - cid = certLookup.Id - } - - a := ROTAction{ - StoreID: sId, - StoreType: sType, - StorePath: sPath, - Thumbprint: tp, - CertID: cid, - AddCert: addCert, - RemoveCert: removeCert, - } - - actions[a.Thumbprint] = append(actions[a.Thumbprint], a) - } - if len(actions) == 0 { - fmt.Println("No reconciliation actions to take, root stores are up-to-date. Exiting.") - return - } - rErr := reconcileRoots(actions, kfClient, reportFile, dryRun) - if rErr != nil { - fmt.Printf("[ERROR] reconciling roots: %s", rErr) - log.Fatalf("[ERROR] reconciling roots: %s", rErr) - } - defer csvFile.Close() - - orchsURL := fmt.Sprintf("https://%s/Keyfactor/Portal/AgentJobStatus/Index", kfClient.Hostname) - - fmt.Println(fmt.Sprintf("Reconciliation completed. Check orchestrator jobs for details. %s", orchsURL)) - } else { - // Read in the stores CSV - csvFile, _ := os.Open(storesFile) - reader := csv.NewReader(bufio.NewReader(csvFile)) - storeEntries, _ := reader.ReadAll() - var stores = make(map[string]StoreCSVEntry) - for i, entry := range storeEntries { - if entry[0] == "StoreID" || entry[0] == "StoreId" || i == 0 { - continue // Skip header - } - apiResp, err := kfClient.GetCertificateStoreByID(entry[0]) - if err != nil { - log.Printf("[ERROR] getting cert store: %s", err) - lookupFailures = append(lookupFailures, entry[0]) - continue - } - inventory, invErr := kfClient.GetCertStoreInventoryV1(entry[0]) - if invErr != nil { - log.Fatalf("[ERROR] getting cert store inventory: %s", invErr) - } - - if !isRootStore(apiResp, inventory, minCerts, maxLeaves, maxKeys) { - log.Printf("[WARN] Store %s is not a root store", apiResp.Id) - continue - } else { - log.Printf("[INFO] Store %s is a root store", apiResp.Id) - } - - stores[entry[0]] = StoreCSVEntry{ - ID: entry[0], - Type: entry[1], - Machine: entry[2], - Path: entry[3], - Thumbprints: make(map[string]bool), - Serials: make(map[string]bool), - Ids: make(map[int]bool), - } - for _, cert := range *inventory { - thumb := cert.Thumbprints - for t, v := range thumb { - stores[entry[0]].Thumbprints[t] = v - } - for t, v := range cert.Serials { - stores[entry[0]].Serials[t] = v - } - for t, v := range cert.Ids { - stores[entry[0]].Ids[t] = v - } - } - - } - if len(lookupFailures) > 0 { - fmt.Printf("[ERROR] the following stores were not found: %s", strings.Join(lookupFailures, ",")) - log.Fatalf("[ERROR] the following stores were not found: %s", strings.Join(lookupFailures, ",")) - } - if len(stores) == 0 { - fmt.Println("[ERROR] no root stores found. Exiting.") - log.Fatalf("[ERROR] No root stores found. Exiting.") - } - // Read in the add addCerts CSV - var certsToAdd = make(map[string]string) - if addRootsFile != "" { - certsToAdd, _ = readCertsFile(addRootsFile, kfClient) - log.Printf("[DEBUG] ROT add certs called") - } else { - log.Printf("[INFO] No addCerts file specified") - } - - // Read in the remove removeCerts CSV - var certsToRemove = make(map[string]string) - if removeRootsFile != "" { - certsToRemove, _ = readCertsFile(removeRootsFile, kfClient) - log.Printf("[DEBUG] ROT remove certs called") - } else { - log.Printf("[DEBUG] No removeCerts file specified") - } - _, actions, err := generateAuditReport(certsToAdd, certsToRemove, stores, outpath, kfClient) - if err != nil { - log.Fatalf("[ERROR] generating audit report: %s", err) - } - if len(actions) == 0 { - fmt.Println("No reconciliation actions to take, root stores are up-to-date. Exiting.") - return - } - rErr := reconcileRoots(actions, kfClient, reportFile, dryRun) - if rErr != nil { - fmt.Printf("[ERROR] reconciling roots: %s", rErr) - log.Fatalf("[ERROR] reconciling roots: %s", rErr) - } - if lookupFailures != nil { - fmt.Printf("The following stores could not be found: %s", strings.Join(lookupFailures, ",")) - } - orchsURL := fmt.Sprintf("https://%s/Keyfactor/Portal/AgentJobStatus/Index", kfClient.Hostname) - - fmt.Println(fmt.Sprintf("Reconciliation completed. Check orchestrator jobs for details. %s", orchsURL)) - } - - }, - RunE: nil, - PostRun: nil, - PostRunE: nil, - PersistentPostRun: nil, - PersistentPostRunE: nil, - FParseErrWhitelist: cobra.FParseErrWhitelist{}, - CompletionOptions: cobra.CompletionOptions{}, - TraverseChildren: false, - Hidden: false, - SilenceErrors: false, - SilenceUsage: false, - DisableFlagParsing: false, - DisableAutoGenTag: false, - DisableFlagsInUseLine: false, - DisableSuggestions: false, - SuggestionsMinimumDistance: 0, - } - rotGenStoreTemplateCmd = &cobra.Command{ - Use: "generate-template", - Aliases: nil, - SuggestFor: nil, - Short: "For generating Root Of Trust template(s)", - Long: `Root Of Trust: Will parse a CSV and attempt to deploy a cert or set of certs into a list of cert stores.`, - Example: "", - ValidArgs: nil, - ValidArgsFunction: nil, - Args: nil, - ArgAliases: nil, - BashCompletionFunction: "", - Deprecated: "", - Annotations: nil, - Version: "", - PersistentPreRun: nil, - PersistentPreRunE: nil, - PreRun: nil, - PreRunE: nil, - Run: func(cmd *cobra.Command, args []string) { - // Global flags - debugFlag, _ := cmd.Flags().GetBool("debugFlag") - configFile, _ := cmd.Flags().GetString("config") - noPrompt, _ := cmd.Flags().GetBool("no-prompt") - profile, _ := cmd.Flags().GetString("profile") - - kfcUsername, _ := cmd.Flags().GetString("kfcUsername") - kfcPassword, _ := cmd.Flags().GetString("kfcPassword") - kfcDomain, _ := cmd.Flags().GetString("kfcDomain") - - authConfig := createAuthConfigFromParams(kfcHostName, kfcUsername, kfcPassword, kfcDomain, kfcAPIPath) - - debugModeEnabled := checkDebug(debugFlag) - log.Println("Debug mode enabled: ", debugModeEnabled) - - templateType, _ := cmd.Flags().GetString("type") - format, _ := cmd.Flags().GetString("format") - outPath, _ := cmd.Flags().GetString("outpath") - storeType, _ := cmd.Flags().GetStringSlice("store-type") - containerName, _ := cmd.Flags().GetStringSlice("container-name") - collection, _ := cmd.Flags().GetStringSlice("collection") - subjectName, _ := cmd.Flags().GetStringSlice("cn") - stID := -1 - var storeData []api.GetCertificateStoreResponse - var csvStoreData [][]string - var csvCertData [][]string - var rowLookup = make(map[string]bool) - kfClient, cErr := initClient(configFile, profile, "", "", noPrompt, authConfig, false) - if len(storeType) != 0 { - for _, s := range storeType { - if cErr != nil { - log.Fatalf("[ERROR] creating client: %s", cErr) - } - var sType *api.CertificateStoreType - var stErr error - if s == "all" { - sType = &api.CertificateStoreType{ - Name: "", - ShortName: "", - Capability: "", - StoreType: 0, - ImportType: 0, - LocalStore: false, - SupportedOperations: nil, - Properties: nil, - EntryParameters: nil, - PasswordOptions: nil, - StorePathType: "", - StorePathValue: "", - PrivateKeyAllowed: "", - JobProperties: nil, - ServerRequired: false, - PowerShell: false, - BlueprintAllowed: false, - CustomAliasAllowed: "", - ServerRegistration: 0, - InventoryEndpoint: "", - InventoryJobType: "", - ManagementJobType: "", - DiscoveryJobType: "", - EnrollmentJobType: "", - } - } else { - // check if s is an int - sInt, err := strconv.Atoi(s) - if err == nil { - sType, stErr = kfClient.GetCertificateStoreTypeById(sInt) - } else { - sType, stErr = kfClient.GetCertificateStoreTypeByName(s) - } - if stErr != nil { - fmt.Printf("[ERROR] getting store type '%s'. %s\n", s, stErr) - continue - } - stID = sType.StoreType // This is the template type ID - } - - if stID >= 0 || s == "all" { - log.Printf("[DEBUG] Store type ID: %d\n", stID) - params := make(map[string]interface{}) - stores, sErr := kfClient.ListCertificateStores(¶ms) - if sErr != nil { - fmt.Printf("[ERROR] getting certificate stores of type '%s': %s\n", s, sErr) - log.Fatalf("[ERROR] getting certificate stores of type '%s': %s", s, sErr) - } - for _, store := range *stores { - if store.CertStoreType == stID || s == "all" { - storeData = append(storeData, store) - if !rowLookup[store.Id] { - lineData := []string{ - //"StoreID", "StoreType", "StoreMachine", "StorePath", "ContainerId" - store.Id, fmt.Sprintf("%s", sType.ShortName), store.ClientMachine, store.StorePath, fmt.Sprintf("%d", store.ContainerId), store.ContainerName, getCurrentTime(""), - } - csvStoreData = append(csvStoreData, lineData) - rowLookup[store.Id] = true - } - } - } - } - } - fmt.Println("Done") - } - if len(containerName) != 0 { - for _, c := range containerName { - - if cErr != nil { - log.Fatalf("[ERROR] creating client: %s", cErr) - } - cStoresResp, scErr := kfClient.GetCertificateStoreByContainerID(c) - if scErr != nil { - fmt.Printf("[ERROR] getting store container: %s\n", scErr) - } - if cStoresResp != nil { - for _, store := range *cStoresResp { - sType, stErr := kfClient.GetCertificateStoreType(store.CertStoreType) - if stErr != nil { - fmt.Printf("[ERROR] getting store type: %s\n", stErr) - continue - } - storeData = append(storeData, store) - if !rowLookup[store.Id] { - lineData := []string{ - // "StoreID", "StoreType", "StoreMachine", "StorePath", "ContainerId" - store.Id, sType.ShortName, store.ClientMachine, store.StorePath, fmt.Sprintf("%d", store.ContainerId), store.ContainerName, getCurrentTime(""), - } - csvStoreData = append(csvStoreData, lineData) - rowLookup[store.Id] = true - } - } - - } - } - } - if len(collection) != 0 { - for _, c := range collection { - if cErr != nil { - fmt.Println("[ERROR] connecting to Keyfactor. Please check your configuration and try again.") - log.Fatalf("[ERROR] creating client: %s", cErr) - } - q := make(map[string]string) - q["collection"] = c - certsResp, scErr := kfClient.ListCertificates(q) - if scErr != nil { - fmt.Printf("No certificates found in collection: %s\n", scErr) - } - if certsResp != nil { - for _, cert := range certsResp { - if !rowLookup[cert.Thumbprint] { - lineData := []string{ - // "Thumbprint", "SubjectName", "Issuer", "CertID", "Locations", "LastQueriedDate" - cert.Thumbprint, cert.IssuedCN, cert.IssuerDN, fmt.Sprintf("%d", cert.Id), fmt.Sprintf("%v", cert.Locations), getCurrentTime(""), - } - csvCertData = append(csvCertData, lineData) - rowLookup[cert.Thumbprint] = true - } - } - - } - } - } - if len(subjectName) != 0 { - for _, s := range subjectName { - if cErr != nil { - fmt.Println("[ERROR] connecting to Keyfactor. Please check your configuration and try again.") - log.Fatalf("[ERROR] creating client: %s", cErr) - } - q := make(map[string]string) - q["subject"] = s - certsResp, scErr := kfClient.ListCertificates(q) - if scErr != nil { - fmt.Printf("No certificates found with CN: %s\n", scErr) - } - if certsResp != nil { - for _, cert := range certsResp { - if !rowLookup[cert.Thumbprint] { - locationsFormatted := "" - for _, loc := range cert.Locations { - locationsFormatted += fmt.Sprintf("%s:%s\n", loc.StoreMachine, loc.StorePath) - } - lineData := []string{ - // "Thumbprint", "SubjectName", "Issuer", "CertID", "Locations", "LastQueriedDate" - cert.Thumbprint, cert.IssuedCN, cert.IssuerDN, fmt.Sprintf("%d", cert.Id), locationsFormatted, getCurrentTime(""), - } - csvCertData = append(csvCertData, lineData) - rowLookup[cert.Thumbprint] = true - } - } - - } - } - } - // Create CSV template file - - var filePath string - if outPath != "" { - filePath = outPath - } else { - filePath = fmt.Sprintf("%s_template.%s", templateType, format) - } - file, err := os.Create(filePath) - if err != nil { - fmt.Printf("[ERROR] creating file: %s", err) - log.Fatal("Cannot create file", err) - } - - switch format { - case "csv": - writer := csv.NewWriter(file) - var data [][]string - switch templateType { - case "stores": - data = append(data, StoreHeader) - if len(csvStoreData) != 0 { - data = append(data, csvStoreData...) - } - case "certs": - data = append(data, CertHeader) - if len(csvCertData) != 0 { - data = append(data, csvCertData...) - } - case "actions": - data = append(data, AuditHeader) - } - csvErr := writer.WriteAll(data) - if csvErr != nil { - fmt.Println(csvErr) - } - defer file.Close() - - case "json": - writer := bufio.NewWriter(file) - _, err := writer.WriteString("StoreID,StoreType,StoreMachine,StorePath") - if err != nil { - log.Fatal("Cannot write to file", err) - } - } - fmt.Printf("Template file created at %s.\n", filePath) - }, - RunE: nil, - PostRun: nil, - PostRunE: nil, - PersistentPostRun: nil, - PersistentPostRunE: nil, - FParseErrWhitelist: cobra.FParseErrWhitelist{}, - CompletionOptions: cobra.CompletionOptions{}, - TraverseChildren: false, - Hidden: false, - SilenceErrors: false, - SilenceUsage: false, - DisableFlagParsing: false, - DisableAutoGenTag: false, - DisableFlagsInUseLine: false, - DisableSuggestions: false, - SuggestionsMinimumDistance: 0, - } -) - -func init() { - log.SetFlags(log.LstdFlags | log.Lshortfile) - log.SetOutput(os.Stdout) - var ( - stores string - addCerts string - removeCerts string - minCertsInStore int - maxPrivateKeys int - maxLeaves int - tType = tTypeCerts - outPath string - outputFormat string - inputFile string - storeTypes []string - containerNames []string - collections []string - subjectNames []string - ) - - storesCmd.AddCommand(rotCmd) - - // Root of trust `audit` command - rotCmd.AddCommand(rotAuditCmd) - rotAuditCmd.Flags().StringVarP(&stores, "stores", "s", "", "CSV file containing cert stores to enroll into") - rotAuditCmd.Flags().StringVarP(&addCerts, "add-certs", "a", "", - "CSV file containing cert(s) to enroll into the defined cert stores") - rotAuditCmd.Flags().StringVarP(&removeCerts, "remove-certs", "r", "", - "CSV file containing cert(s) to remove from the defined cert stores") - rotAuditCmd.Flags().IntVarP(&minCertsInStore, "min-certs", "m", -1, - "The minimum number of certs that should be in a store to be considered a 'root' store. If set to `-1` then all stores will be considered.") - rotAuditCmd.Flags().IntVarP(&maxPrivateKeys, "max-keys", "k", -1, - "The max number of private keys that should be in a store to be considered a 'root' store. If set to `-1` then all stores will be considered.") - rotAuditCmd.Flags().IntVarP(&maxLeaves, "max-leaf-certs", "l", -1, - "The max number of non-root-certs that should be in a store to be considered a 'root' store. If set to `-1` then all stores will be considered.") - rotAuditCmd.Flags().BoolP("dry-run", "d", false, "Dry run mode") - rotAuditCmd.Flags().StringVarP(&outPath, "outpath", "o", "", - "Path to write the audit report file to. If not specified, the file will be written to the current directory.") - - // Root of trust `reconcile` command - rotCmd.AddCommand(rotReconcileCmd) - rotReconcileCmd.Flags().StringVarP(&stores, "stores", "s", "", "CSV file containing cert stores to enroll into") - rotReconcileCmd.Flags().StringVarP(&addCerts, "add-certs", "a", "", - "CSV file containing cert(s) to enroll into the defined cert stores") - rotReconcileCmd.Flags().StringVarP(&removeCerts, "remove-certs", "r", "", - "CSV file containing cert(s) to remove from the defined cert stores") - rotReconcileCmd.Flags().IntVarP(&minCertsInStore, "min-certs", "m", -1, - "The minimum number of certs that should be in a store to be considered a 'root' store. If set to `-1` then all stores will be considered.") - rotReconcileCmd.Flags().IntVarP(&maxPrivateKeys, "max-keys", "k", -1, - "The max number of private keys that should be in a store to be considered a 'root' store. If set to `-1` then all stores will be considered.") - rotReconcileCmd.Flags().IntVarP(&maxLeaves, "max-leaf-certs", "l", -1, - "The max number of non-root-certs that should be in a store to be considered a 'root' store. If set to `-1` then all stores will be considered.") - rotReconcileCmd.Flags().BoolP("dry-run", "d", false, "Dry run mode") - rotReconcileCmd.Flags().BoolP("import-csv", "v", false, "Import an audit report file in CSV format.") - rotReconcileCmd.Flags().StringVarP(&inputFile, "input-file", "i", reconcileDefaultFileName, - "Path to a file generated by 'stores rot audit' command.") - rotReconcileCmd.Flags().StringVarP(&outPath, "outpath", "o", "", - "Path to write the audit report file to. If not specified, the file will be written to the current directory.") - //rotReconcileCmd.MarkFlagsRequiredTogether("add-certs", "stores") - //rotReconcileCmd.MarkFlagsRequiredTogether("remove-certs", "stores") - rotReconcileCmd.MarkFlagsMutuallyExclusive("add-certs", "import-csv") - rotReconcileCmd.MarkFlagsMutuallyExclusive("remove-certs", "import-csv") - rotReconcileCmd.MarkFlagsMutuallyExclusive("stores", "import-csv") - - // Root of trust `generate` command - rotCmd.AddCommand(rotGenStoreTemplateCmd) - rotGenStoreTemplateCmd.Flags().StringVarP(&outPath, "outpath", "o", "", - "Path to write the template file to. If not specified, the file will be written to the current directory.") - rotGenStoreTemplateCmd.Flags().StringVarP(&outputFormat, "format", "f", "csv", - "The type of template to generate. Only `csv` is supported at this time.") - rotGenStoreTemplateCmd.Flags().Var(&tType, "type", - `The type of template to generate. Only "certs|stores|actions" are supported at this time.`) - rotGenStoreTemplateCmd.Flags().StringSliceVar(&storeTypes, "store-type", []string{}, "Multi value flag. Attempt to pre-populate the stores template with the certificate stores matching specified store types. If not specified, the template will be empty.") - rotGenStoreTemplateCmd.Flags().StringSliceVar(&containerNames, "container-name", []string{}, "Multi value flag. Attempt to pre-populate the stores template with the certificate stores matching specified container types. If not specified, the template will be empty.") - rotGenStoreTemplateCmd.Flags().StringSliceVar(&subjectNames, "cn", []string{}, "Subject name(s) to pre-populate the 'certs' template with. If not specified, the template will be empty. Does not work with SANs.") - rotGenStoreTemplateCmd.Flags().StringSliceVar(&collections, "collection", []string{}, "Certificate collection name(s) to pre-populate the stores template with. If not specified, the template will be empty.") - - rotGenStoreTemplateCmd.RegisterFlagCompletionFunc("type", templateTypeCompletion) - rotGenStoreTemplateCmd.MarkFlagRequired("type") -} +//import ( +// "bufio" +// "encoding/csv" +// "encoding/json" +// "errors" +// "fmt" +// "log" +// "os" +// "strconv" +// "strings" +// +// "github.com/Keyfactor/keyfactor-go-client/v3/api" +// "github.com/spf13/cobra" +//) +// +//type templateType string +//type StoreCSVEntry struct { +// ID string `json:"id"` +// Type string `json:"type"` +// Machine string `json:"address"` +// Path string `json:"path"` +// Thumbprints map[string]bool `json:"thumbprints,omitempty"` +// Serials map[string]bool `json:"serials,omitempty"` +// Ids map[int]bool `json:"ids,omitempty"` +//} +//type ROTCert struct { +// ID int `json:"id,omitempty"` +// ThumbPrint string `json:"thumbprint,omitempty"` +// CN string `json:"cn,omitempty"` +// Locations []api.CertificateLocations `json:"locations,omitempty"` +//} +//type ROTAction struct { +// StoreID string `json:"store_id,omitempty"` +// StoreType string `json:"store_type,omitempty"` +// StorePath string `json:"store_path,omitempty"` +// Thumbprint string `json:"thumbprint,omitempty"` +// CertID int `json:"cert_id,omitempty" mapstructure:"CertID,omitempty"` +// AddCert bool `json:"add,omitempty" mapstructure:"AddCert,omitempty"` +// RemoveCert bool `json:"remove,omitempty" mapstructure:"RemoveCert,omitempty"` +//} +// +//const ( +// tTypeCerts templateType = "certs" +// reconcileDefaultFileName string = "rot_audit.csv" +//) +// +//var ( +// AuditHeader = []string{ +// "Thumbprint", +// "CertID", +// "SubjectName", +// "Issuer", +// "StoreID", +// "StoreType", +// "Machine", +// "Path", +// "AddCert", +// "RemoveCert", +// "Deployed", +// "AuditDate", +// } +// ReconciledAuditHeader = []string{ +// "Thumbprint", +// "CertID", +// "SubjectName", +// "Issuer", +// "StoreID", +// "StoreType", +// "Machine", +// "Path", +// "AddCert", +// "RemoveCert", +// "Deployed", +// "ReconciledDate", +// } +// StoreHeader = []string{ +// "StoreID", +// "StoreType", +// "StoreMachine", +// "StorePath", +// "ContainerId", +// "ContainerName", +// "LastQueriedDate", +// } +// CertHeader = []string{"Thumbprint", "SubjectName", "Issuer", "CertID", "Locations", "LastQueriedDate"} +//) +// +//// String is used both by fmt.Print and by Cobra in help text +//func (e *templateType) String() string { +// return string(*e) +//} +// +//// Set must have pointer receiver, so it doesn't change the value of a copy +//func (e *templateType) Set(v string) error { +// switch v { +// case "certs", "stores", "actions": +// *e = templateType(v) +// return nil +// default: +// return errors.New(`must be one of "certs", "stores", or "actions"`) +// } +//} +// +//// Type is only used in help text +//func (e *templateType) Type() string { +// return "string" +//} +// +//func templateTypeCompletion(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { +// return []string{ +// "certs\tGenerates template CSV for certificate input to be used w/ `--add-certs` or `--remove-certs`", +// "stores\tGenerates template CSV for certificate input to be used w/ `--stores`", +// "actions\tGenerates template CSV for certificate input to be used w/ `--actions`", +// }, cobra.ShellCompDirectiveDefault +//} +// +//func generateAuditReport( +// addCerts map[string]string, +// removeCerts map[string]string, +// stores map[string]StoreCSVEntry, +// outpath string, +// kfClient *api.Client, +//) ([][]string, map[string][]ROTAction, error) { +// log.Println("[DEBUG] generateAuditReport called") +// var ( +// data [][]string +// ) +// +// data = append(data, AuditHeader) +// var csvFile *os.File +// var fErr error +// if outpath == "" { +// csvFile, fErr = os.Create(reconcileDefaultFileName) +// outpath = reconcileDefaultFileName +// } else { +// csvFile, fErr = os.Create(outpath) +// } +// +// if fErr != nil { +// fmt.Printf("%s", fErr) +// log.Fatalf("[ERROR] creating audit file: %s", fErr) +// } +// csvWriter := csv.NewWriter(csvFile) +// cErr := csvWriter.Write(AuditHeader) +// if cErr != nil { +// fmt.Printf("%s", cErr) +// log.Fatalf("[ERROR] writing audit header: %s", cErr) +// } +// actions := make(map[string][]ROTAction) +// +// for _, cert := range addCerts { +// certLookupReq := api.GetCertificateContextArgs{ +// IncludeMetadata: boolToPointer(true), +// IncludeLocations: boolToPointer(true), +// CollectionId: nil, +// Thumbprint: cert, +// Id: 0, +// } +// certLookup, err := kfClient.GetCertificateContext(&certLookupReq) +// if err != nil { +// fmt.Printf("[ERROR] looking up certificate %s: %s\n", cert, err) +// log.Printf("[ERROR] looking up cert: %s\n%v", cert, err) +// continue +// } +// certID := certLookup.Id +// certIDStr := strconv.Itoa(certID) +// for _, store := range stores { +// if _, ok := store.Thumbprints[cert]; ok { +// // Cert is already in the store do nothing +// row := []string{ +// cert, +// certIDStr, +// certLookup.IssuedDN, +// certLookup.IssuerDN, +// store.ID, +// store.Type, +// store.Machine, +// store.Path, +// "false", +// "false", +// "true", +// getCurrentTime(""), +// } +// data = append(data, row) +// wErr := csvWriter.Write(row) +// if wErr != nil { +// fmt.Printf("[ERROR] writing audit file row: %s\n", wErr) +// log.Printf("[ERROR] writing audit row: %s", wErr) +// } +// } else { +// // Cert is not deployed to this store and will need to be added +// row := []string{ +// cert, +// certIDStr, +// certLookup.IssuedDN, +// certLookup.IssuerDN, +// store.ID, +// store.Type, +// store.Machine, +// store.Path, +// "true", +// "false", +// "false", +// getCurrentTime(""), +// } +// data = append(data, row) +// wErr := csvWriter.Write(row) +// if wErr != nil { +// fmt.Printf("[ERROR] writing audit file row: %s\n", wErr) +// log.Printf("[ERROR] writing audit row: %s", wErr) +// } +// actions[cert] = append( +// actions[cert], ROTAction{ +// Thumbprint: cert, +// CertID: certID, +// StoreID: store.ID, +// StoreType: store.Type, +// StorePath: store.Path, +// AddCert: true, +// RemoveCert: false, +// }, +// ) +// } +// } +// } +// for _, cert := range removeCerts { +// certLookupReq := api.GetCertificateContextArgs{ +// IncludeMetadata: boolToPointer(true), +// IncludeLocations: boolToPointer(true), +// CollectionId: nil, +// Thumbprint: cert, +// Id: 0, +// } +// certLookup, err := kfClient.GetCertificateContext(&certLookupReq) +// if err != nil { +// log.Printf("[ERROR] looking up cert: %s", err) +// continue +// } +// certID := certLookup.Id +// certIDStr := strconv.Itoa(certID) +// for _, store := range stores { +// if _, ok := store.Thumbprints[cert]; ok { +// // Cert is deployed to this store and will need to be removed +// row := []string{ +// cert, +// certIDStr, +// certLookup.IssuedDN, +// certLookup.IssuerDN, +// store.ID, +// store.Type, +// store.Machine, +// store.Path, +// "false", +// "true", +// "true", +// getCurrentTime(""), +// } +// data = append(data, row) +// wErr := csvWriter.Write(row) +// if wErr != nil { +// fmt.Printf("%s", wErr) +// log.Printf("[ERROR] writing row to CSV: %s", wErr) +// } +// actions[cert] = append( +// actions[cert], ROTAction{ +// Thumbprint: cert, +// CertID: certID, +// StoreID: store.ID, +// StoreType: store.Type, +// StorePath: store.Path, +// AddCert: false, +// RemoveCert: true, +// }, +// ) +// } else { +// // Cert is not deployed to this store do nothing +// row := []string{ +// cert, +// certIDStr, +// certLookup.IssuedDN, +// certLookup.IssuerDN, +// store.ID, +// store.Type, +// store.Machine, +// store.Path, +// "false", +// "false", +// "false", +// getCurrentTime(""), +// } +// data = append(data, row) +// wErr := csvWriter.Write(row) +// if wErr != nil { +// fmt.Printf("%s", wErr) +// log.Printf("[ERROR] writing row to CSV: %s", wErr) +// } +// } +// } +// } +// csvWriter.Flush() +// ioErr := csvFile.Close() +// if ioErr != nil { +// fmt.Println(ioErr) +// log.Printf("[ERROR] closing audit file: %s", ioErr) +// } +// fmt.Printf("Audit report written to %s\n", outpath) +// return data, actions, nil +//} +// +//func reconcileRoots(actions map[string][]ROTAction, kfClient *api.Client, reportFile string, dryRun bool) error { +// log.Printf("[DEBUG] Reconciling roots") +// if len(actions) == 0 { +// log.Printf("[INFO] No actions to take, roots are up-to-date.") +// return nil +// } +// rFileName := fmt.Sprintf("%s_reconciled.csv", strings.Split(reportFile, ".csv")[0]) +// csvFile, fErr := os.Create(rFileName) +// if fErr != nil { +// fmt.Printf("[ERROR] creating reconciled report file: %s", fErr) +// } +// csvWriter := csv.NewWriter(csvFile) +// cErr := csvWriter.Write(ReconciledAuditHeader) +// if cErr != nil { +// fmt.Printf("%s", cErr) +// log.Fatalf("[ERROR] writing audit header: %s", cErr) +// } +// for thumbprint, action := range actions { +// +// for _, a := range action { +// if a.AddCert { +// log.Printf("[INFO] Adding cert %s to store %s(%s)", thumbprint, a.StoreID, a.StorePath) +// if !dryRun { +// cStore := api.CertificateStore{ +// CertificateStoreId: a.StoreID, +// Overwrite: true, +// } +// var stores []api.CertificateStore +// stores = append(stores, cStore) +// schedule := &api.InventorySchedule{ +// Immediate: boolToPointer(true), +// } +// addReq := api.AddCertificateToStore{ +// CertificateId: a.CertID, +// CertificateStores: &stores, +// InventorySchedule: schedule, +// } +// log.Printf("[DEBUG] Adding cert %s to store %s", thumbprint, a.StoreID) +// log.Printf("[TRACE] Add request: %+v", addReq) +// addReqJSON, _ := json.Marshal(addReq) +// log.Printf("[TRACE] Add request JSON: %s", addReqJSON) +// _, err := kfClient.AddCertificateToStores(&addReq) +// if err != nil { +// fmt.Printf( +// "[ERROR] adding cert %s (%d) to store %s (%s): %s\n", +// a.Thumbprint, +// a.CertID, +// a.StoreID, +// a.StorePath, +// err, +// ) +// continue +// } +// } else { +// log.Printf("[INFO] DRY RUN: Would have added cert %s from store %s", thumbprint, a.StoreID) +// } +// } else if a.RemoveCert { +// if !dryRun { +// log.Printf("[INFO] Removing cert from store %s", a.StoreID) +// cStore := api.CertificateStore{ +// CertificateStoreId: a.StoreID, +// Alias: a.Thumbprint, +// } +// var stores []api.CertificateStore +// stores = append(stores, cStore) +// schedule := &api.InventorySchedule{ +// Immediate: boolToPointer(true), +// } +// removeReq := api.RemoveCertificateFromStore{ +// CertificateId: a.CertID, +// CertificateStores: &stores, +// InventorySchedule: schedule, +// } +// _, err := kfClient.RemoveCertificateFromStores(&removeReq) +// if err != nil { +// fmt.Printf( +// "[ERROR] removing cert %s (ID: %d) from store %s (%s): %s\n", +// a.Thumbprint, +// a.CertID, +// a.StoreID, +// a.StorePath, +// err, +// ) +// } +// } else { +// fmt.Printf("DRY RUN: Would have removed cert %s from store %s\n", thumbprint, a.StoreID) +// log.Printf("[INFO] DRY RUN: Would have removed cert %s from store %s", thumbprint, a.StoreID) +// } +// } +// } +// } +// return nil +//} +// +//func readCertsFile(certsFilePath string, kfclient *api.Client) (map[string]string, error) { +// // Read in the cert CSV +// csvFile, _ := os.Open(certsFilePath) +// reader := csv.NewReader(bufio.NewReader(csvFile)) +// certEntries, _ := reader.ReadAll() +// var certs = make(map[string]string) +// for _, entry := range certEntries { +// switch entry[0] { +// case "CertID", "thumbprint", "id", "CertId", "Thumbprint": +// continue // Skip header +// } +// certs[entry[0]] = entry[0] +// } +// return certs, nil +//} +// +//func isRootStore( +// st *api.GetCertificateStoreResponse, +// invs *[]api.CertStoreInventoryV1, +// minCerts int, +// maxKeys int, +// maxLeaf int, +//) bool { +// leafCount := 0 +// keyCount := 0 +// certCount := 0 +// for _, inv := range *invs { +// log.Printf("[DEBUG] inv: %v", inv) +// certCount += len(inv.Certificates) +// +// for _, cert := range inv.Certificates { +// if cert.IssuedDN != cert.IssuerDN { +// leafCount++ +// } +// if inv.Parameters["PrivateKeyEntry"] == "Yes" { +// keyCount++ +// } +// } +// } +// if certCount < minCerts && minCerts >= 0 { +// log.Printf("[DEBUG] Store %s has %d certs, less than the required count of %d", st.Id, certCount, minCerts) +// return false +// } +// if leafCount > maxLeaf && maxLeaf >= 0 { +// log.Printf("[DEBUG] Store %s has too many leaf certs", st.Id) +// return false +// } +// +// if keyCount > maxKeys && maxKeys >= 0 { +// log.Printf("[DEBUG] Store %s has too many keys", st.Id) +// return false +// } +// +// return true +//} +// +//var ( +// rotCmd = &cobra.Command{ +// Use: "rot", +// Short: "Root of trust utility", +// Long: `Root of trust allows you to manage your trusted roots using Keyfactor certificate stores. +//For example if you wish to add a list of "root" certs to a list of certificate stores you would simply generate and fill +//out the template CSV file. These template files can be generated with the following commands: +//kfutil stores rot generate-template --type certs +//kfutil stores rot generate-template --type stores +//Once those files are filled out you can use the following command to add the certs to the stores: +//kfutil stores rot audit --certs-file --stores-file +//Will generate a CSV report file 'rot_audit.csv' of what actions will be taken. If those actions are correct you can run +//the following command to actually perform the actions: +//kfutil stores rot reconcile --certs-file --stores-file +//OR if you want to use the audit report file generated you can run this command: +//kfutil stores rot reconcile --import-csv +//`, +// } +// rotAuditCmd = &cobra.Command{ +// Use: "audit", +// Aliases: nil, +// SuggestFor: nil, +// Short: "Audit generates a CSV report of what actions will be taken based on input CSV files.", +// Long: `Root of Trust Audit: Will read and parse inputs to generate a report of certs that need to be added or removed from the "root of trust" stores.`, +// Example: "", +// ValidArgs: nil, +// ValidArgsFunction: nil, +// Args: nil, +// ArgAliases: nil, +// BashCompletionFunction: "", +// Deprecated: "", +// Annotations: nil, +// Version: "", +// PersistentPreRun: nil, +// PersistentPreRunE: nil, +// PreRun: nil, +// PreRunE: nil, +// Run: func(cmd *cobra.Command, args []string) { +// // Global flags +// debugFlag, _ := cmd.Flags().GetBool("debugFlag") +// configFile, _ := cmd.Flags().GetString("config") +// noPrompt, _ := cmd.Flags().GetBool("no-prompt") +// profile, _ := cmd.Flags().GetString("profile") +// +// kfcUsername, _ := cmd.Flags().GetString("kfcUsername") +// kfcPassword, _ := cmd.Flags().GetString("kfcPassword") +// kfcDomain, _ := cmd.Flags().GetString("kfcDomain") +// +// +// +// debugModeEnabled := checkDebug(debugFlag) +// log.Println("Debug mode enabled: ", debugModeEnabled) +// var lookupFailures []string +// kfClient, _ := initClient(false) +// storesFile, _ := cmd.Flags().GetString("stores") +// addRootsFile, _ := cmd.Flags().GetString("add-certs") +// removeRootsFile, _ := cmd.Flags().GetString("remove-certs") +// minCerts, _ := cmd.Flags().GetInt("min-certs") +// maxLeaves, _ := cmd.Flags().GetInt("max-leaf-certs") +// maxKeys, _ := cmd.Flags().GetInt("max-keys") +// dryRun, _ := cmd.Flags().GetBool("dry-run") +// outpath, _ := cmd.Flags().GetString("outpath") +// // Read in the stores CSV +// log.Printf("[DEBUG] storesFile: %s", storesFile) +// log.Printf("[DEBUG] addRootsFile: %s", addRootsFile) +// log.Printf("[DEBUG] removeRootsFile: %s", removeRootsFile) +// log.Printf("[DEBUG] dryRun: %t", dryRun) +// // Read in the stores CSV +// csvFile, _ := os.Open(storesFile) +// reader := csv.NewReader(bufio.NewReader(csvFile)) +// storeEntries, _ := reader.ReadAll() +// var stores = make(map[string]StoreCSVEntry) +// validHeader := false +// for _, entry := range storeEntries { +// if strings.EqualFold(strings.Join(entry, ","), strings.Join(StoreHeader, ",")) { +// validHeader = true +// continue // Skip header +// } +// if !validHeader { +// fmt.Printf("[ERROR] Invalid header in stores file. Expected: %s", strings.Join(StoreHeader, ",")) +// log.Fatalf("[ERROR] Stores CSV file is missing a valid header") +// } +// apiResp, err := kfClient.GetCertificateStoreByID(entry[0]) +// if err != nil { +// log.Printf("[ERROR] getting cert store: %s", err) +// _ = append(lookupFailures, strings.Join(entry, ",")) +// continue +// } +// +// //inventory, invErr := kfClient.GetCertStoreInventoryV1(entry[0]) +// //if invErr != nil { +// // log.Printf("[ERROR] getting cert store inventory for: %s\n%s", entry[0], invErr) +// //} +// var inventory []api.CertStoreInventoryV1 //TODO: Update this to use SDK inventory +// +// if !isRootStore(apiResp, &inventory, minCerts, maxLeaves, maxKeys) { +// fmt.Printf("Store %s is not a root store, skipping.\n", entry[0]) +// log.Printf("[WARN] Store %s is not a root store", apiResp.Id) +// continue +// } else { +// log.Printf("[INFO] Store %s is a root store", apiResp.Id) +// } +// +// stores[entry[0]] = StoreCSVEntry{ +// ID: entry[0], +// Type: entry[1], +// Machine: entry[2], +// Path: entry[3], +// Thumbprints: make(map[string]bool), +// Serials: make(map[string]bool), +// Ids: make(map[int]bool), +// } +// for _, cert := range inventory { +// thumb := cert.Thumbprints +// for t, v := range thumb { +// stores[entry[0]].Thumbprints[t] = v +// } +// for t, v := range cert.Serials { +// stores[entry[0]].Serials[t] = v +// } +// for t, v := range cert.Ids { +// stores[entry[0]].Ids[t] = v +// } +// } +// +// } +// +// // Read in the add addCerts CSV +// var certsToAdd = make(map[string]string) +// if addRootsFile != "" { +// var rcfErr error +// certsToAdd, rcfErr = readCertsFile(addRootsFile, kfClient) +// if rcfErr != nil { +// fmt.Printf("[ERROR] reading certs file %s: %s", addRootsFile, rcfErr) +// log.Fatalf("[ERROR] reading addCerts file: %s", rcfErr) +// } +// addCertsJSON, _ := json.Marshal(certsToAdd) +// log.Printf("[DEBUG] add certs JSON: %s", string(addCertsJSON)) +// log.Println("[DEBUG] AddCert ROT called") +// } else { +// log.Printf("[DEBUG] No addCerts file specified") +// log.Printf("[DEBUG] No addCerts = %s", certsToAdd) +// } +// +// // Read in the remove removeCerts CSV +// var certsToRemove = make(map[string]string) +// if removeRootsFile != "" { +// var rcfErr error +// certsToRemove, rcfErr = readCertsFile(removeRootsFile, kfClient) +// if rcfErr != nil { +// fmt.Printf("[ERROR] reading removeCerts file %s: %s", removeRootsFile, rcfErr) +// log.Fatalf("[ERROR] reading removeCerts file: %s", rcfErr) +// } +// removeCertsJSON, _ := json.Marshal(certsToRemove) +// log.Printf("[DEBUG] remove certs JSON: %s", string(removeCertsJSON)) +// } else { +// log.Printf("[DEBUG] No removeCerts file specified") +// log.Printf("[DEBUG] No removeCerts = %s", certsToRemove) +// } +// _, _, gErr := generateAuditReport(certsToAdd, certsToRemove, stores, outpath, kfClient) +// if gErr != nil { +// log.Fatalf("[ERROR] generating audit report: %s", gErr) +// } +// }, +// RunE: nil, +// PostRun: nil, +// PostRunE: nil, +// PersistentPostRun: nil, +// PersistentPostRunE: nil, +// FParseErrWhitelist: cobra.FParseErrWhitelist{}, +// CompletionOptions: cobra.CompletionOptions{}, +// TraverseChildren: false, +// Hidden: false, +// SilenceErrors: false, +// SilenceUsage: false, +// DisableFlagParsing: false, +// DisableAutoGenTag: false, +// DisableFlagsInUseLine: false, +// DisableSuggestions: false, +// SuggestionsMinimumDistance: 0, +// } +// rotReconcileCmd = &cobra.Command{ +// Use: "reconcile", +// Aliases: nil, +// SuggestFor: nil, +// Short: "Reconcile either takes in or will generate an audit report and then add/remove certs as needed.", +// Long: `Root of Trust (rot): Will parse either a combination of CSV files that define certs to +//add and/or certs to remove with a CSV of certificate stores or an audit CSV file. If an audit CSV file is provided, the +//add and remove actions defined in the audit file will be immediately executed. If a combination of CSV files are provided, +//the utility will first generate an audit report and then execute the add/remove actions defined in the audit report.`, +// Example: "", +// ValidArgs: nil, +// ValidArgsFunction: nil, +// Args: nil, +// ArgAliases: nil, +// BashCompletionFunction: "", +// Deprecated: "", +// Annotations: nil, +// Version: "", +// PersistentPreRun: nil, +// PersistentPreRunE: nil, +// PreRun: nil, +// PreRunE: nil, +// Run: func(cmd *cobra.Command, args []string) { +// // Global flags +// debugFlag, _ := cmd.Flags().GetBool("debugFlag") +// configFile, _ := cmd.Flags().GetString("config") +// noPrompt, _ := cmd.Flags().GetBool("no-prompt") +// profile, _ := cmd.Flags().GetString("profile") +// +// kfcUsername, _ := cmd.Flags().GetString("kfcUsername") +// kfcPassword, _ := cmd.Flags().GetString("kfcPassword") +// kfcDomain, _ := cmd.Flags().GetString("kfcDomain") +// +// +// +// debugModeEnabled := checkDebug(debugFlag) +// +// log.Println("Debug mode enabled: ", debugModeEnabled) +// +// var lookupFailures []string +// kfClient, _ := initClient(false) +// storesFile, _ := cmd.Flags().GetString("stores") +// addRootsFile, _ := cmd.Flags().GetString("add-certs") +// isCSV, _ := cmd.Flags().GetBool("import-csv") +// reportFile, _ := cmd.Flags().GetString("input-file") +// removeRootsFile, _ := cmd.Flags().GetString("remove-certs") +// minCerts, _ := cmd.Flags().GetInt("min-certs") +// maxLeaves, _ := cmd.Flags().GetInt("max-leaf-certs") +// maxKeys, _ := cmd.Flags().GetInt("max-keys") +// dryRun, _ := cmd.Flags().GetBool("dry-run") +// outpath, _ := cmd.Flags().GetString("outpath") +// +// log.Printf("[DEBUG] configFile: %s", configFile) +// log.Printf("[DEBUG] storesFile: %s", storesFile) +// log.Printf("[DEBUG] addRootsFile: %s", addRootsFile) +// log.Printf("[DEBUG] removeRootsFile: %s", removeRootsFile) +// log.Printf("[DEBUG] dryRun: %t", dryRun) +// +// // Parse existing audit report +// if isCSV && reportFile != "" { +// log.Printf("[DEBUG] isCSV: %t", isCSV) +// log.Printf("[DEBUG] reportFile: %s", reportFile) +// // Read in the CSV +// csvFile, err := os.Open(reportFile) +// if err != nil { +// fmt.Printf("[ERROR] opening file: %s", err) +// log.Fatalf("[ERROR] opening CSV file: %s", err) +// } +// validHeader := false +// +// aCSV := csv.NewReader(csvFile) +// aCSV.FieldsPerRecord = -1 +// inFile, cErr := aCSV.ReadAll() +// if cErr != nil { +// fmt.Printf("[ERROR] reading CSV file: %s", cErr) +// log.Fatalf("[ERROR] reading CSV file: %s", cErr) +// } +// actions := make(map[string][]ROTAction) +// fieldMap := make(map[int]string) +// for i, field := range AuditHeader { +// fieldMap[i] = field +// } +// for ri, row := range inFile { +// if strings.EqualFold(strings.Join(row, ","), strings.Join(AuditHeader, ",")) { +// validHeader = true +// continue // Skip header +// } +// if !validHeader { +// fmt.Printf( +// "[ERROR] Invalid header in stores file. Expected: %s", +// strings.Join(AuditHeader, ","), +// ) +// log.Fatalf("[ERROR] Stores CSV file is missing a valid header") +// } +// action := make(map[string]interface{}) +// +// for i, field := range row { +// fieldInt, iErr := strconv.Atoi(field) +// if iErr != nil { +// log.Printf("[DEBUG] Field %s is not an int", field) +// action[fieldMap[i]] = field +// } else { +// action[fieldMap[i]] = fieldInt +// } +// +// } +// +// addCertStr, aOk := action["AddCert"].(string) +// if !aOk { +// addCertStr = "" +// } +// addCert, acErr := strconv.ParseBool(addCertStr) +// if acErr != nil { +// addCert = false +// } +// +// removeCertStr, rOk := action["RemoveCert"].(string) +// if !rOk { +// removeCertStr = "" +// } +// removeCert, rcErr := strconv.ParseBool(removeCertStr) +// if rcErr != nil { +// removeCert = false +// } +// +// sType, sOk := action["StoreType"].(string) +// if !sOk { +// sType = "" +// } +// +// sPath, pOk := action["Path"].(string) +// if !pOk { +// sPath = "" +// } +// +// tp, tpOk := action["Thumbprint"].(string) +// if !tpOk { +// tp = "" +// } +// cid, cidOk := action["CertID"].(int) +// if !cidOk { +// cid = -1 +// } +// +// if !tpOk && !cidOk { +// fmt.Printf("[ERROR] Missing Thumbprint or CertID for row %d in report file %s", ri, reportFile) +// log.Printf("[ERROR] Invalid action: %v", action) +// continue +// } +// +// sId, sIdOk := action["StoreID"].(string) +// if !sIdOk { +// fmt.Printf("[ERROR] Missing StoreID for row %d in report file %s", ri, reportFile) +// log.Printf("[ERROR] Invalid action: %v", action) +// continue +// } +// if cid == -1 && tp != "" { +// certLookupReq := api.GetCertificateContextArgs{ +// IncludeMetadata: boolToPointer(true), +// IncludeLocations: boolToPointer(true), +// CollectionId: nil, +// Thumbprint: tp, +// Id: 0, +// } +// certLookup, err := kfClient.GetCertificateContext(&certLookupReq) +// if err != nil { +// fmt.Printf("[ERROR] looking up certificate %s: %s\n", tp, err) +// log.Printf("[ERROR] looking up cert: %s\n%v", tp, err) +// continue +// } +// cid = certLookup.Id +// } +// +// a := ROTAction{ +// StoreID: sId, +// StoreType: sType, +// StorePath: sPath, +// Thumbprint: tp, +// CertID: cid, +// AddCert: addCert, +// RemoveCert: removeCert, +// } +// +// actions[a.Thumbprint] = append(actions[a.Thumbprint], a) +// } +// if len(actions) == 0 { +// fmt.Println("No reconciliation actions to take, root stores are up-to-date. Exiting.") +// return +// } +// rErr := reconcileRoots(actions, kfClient, reportFile, dryRun) +// if rErr != nil { +// fmt.Printf("[ERROR] reconciling roots: %s", rErr) +// log.Fatalf("[ERROR] reconciling roots: %s", rErr) +// } +// defer csvFile.Close() +// +// orchsURL := fmt.Sprintf("https://%s/Keyfactor/Portal/AgentJobStatus/Index", kfClient.Hostname) +// +// fmt.Println(fmt.Sprintf("Reconciliation completed. Check orchestrator jobs for details. %s", orchsURL)) +// } else { +// // Read in the stores CSV +// csvFile, _ := os.Open(storesFile) +// reader := csv.NewReader(bufio.NewReader(csvFile)) +// storeEntries, _ := reader.ReadAll() +// var stores = make(map[string]StoreCSVEntry) +// for i, entry := range storeEntries { +// if entry[0] == "StoreID" || entry[0] == "StoreId" || i == 0 { +// continue // Skip header +// } +// apiResp, err := kfClient.GetCertificateStoreByID(entry[0]) +// if err != nil { +// log.Printf("[ERROR] getting cert store: %s", err) +// lookupFailures = append(lookupFailures, entry[0]) +// continue +// } +// inventory, invErr := kfClient.GetCertStoreInventoryV1(entry[0]) +// if invErr != nil { +// log.Fatalf("[ERROR] getting cert store inventory: %s", invErr) +// } +// +// if !isRootStore(apiResp, inventory, minCerts, maxLeaves, maxKeys) { +// log.Printf("[WARN] Store %s is not a root store", apiResp.Id) +// continue +// } else { +// log.Printf("[INFO] Store %s is a root store", apiResp.Id) +// } +// +// stores[entry[0]] = StoreCSVEntry{ +// ID: entry[0], +// Type: entry[1], +// Machine: entry[2], +// Path: entry[3], +// Thumbprints: make(map[string]bool), +// Serials: make(map[string]bool), +// Ids: make(map[int]bool), +// } +// for _, cert := range *inventory { +// thumb := cert.Thumbprints +// for t, v := range thumb { +// stores[entry[0]].Thumbprints[t] = v +// } +// for t, v := range cert.Serials { +// stores[entry[0]].Serials[t] = v +// } +// for t, v := range cert.Ids { +// stores[entry[0]].Ids[t] = v +// } +// } +// +// } +// if len(lookupFailures) > 0 { +// fmt.Printf("[ERROR] the following stores were not found: %s", strings.Join(lookupFailures, ",")) +// log.Fatalf("[ERROR] the following stores were not found: %s", strings.Join(lookupFailures, ",")) +// } +// if len(stores) == 0 { +// fmt.Println("[ERROR] no root stores found. Exiting.") +// log.Fatalf("[ERROR] No root stores found. Exiting.") +// } +// // Read in the add addCerts CSV +// var certsToAdd = make(map[string]string) +// if addRootsFile != "" { +// certsToAdd, _ = readCertsFile(addRootsFile, kfClient) +// log.Printf("[DEBUG] ROT add certs called") +// } else { +// log.Printf("[INFO] No addCerts file specified") +// } +// +// // Read in the remove removeCerts CSV +// var certsToRemove = make(map[string]string) +// if removeRootsFile != "" { +// certsToRemove, _ = readCertsFile(removeRootsFile, kfClient) +// log.Printf("[DEBUG] ROT remove certs called") +// } else { +// log.Printf("[DEBUG] No removeCerts file specified") +// } +// _, actions, err := generateAuditReport(certsToAdd, certsToRemove, stores, outpath, kfClient) +// if err != nil { +// log.Fatalf("[ERROR] generating audit report: %s", err) +// } +// if len(actions) == 0 { +// fmt.Println("No reconciliation actions to take, root stores are up-to-date. Exiting.") +// return +// } +// rErr := reconcileRoots(actions, kfClient, reportFile, dryRun) +// if rErr != nil { +// fmt.Printf("[ERROR] reconciling roots: %s", rErr) +// log.Fatalf("[ERROR] reconciling roots: %s", rErr) +// } +// if lookupFailures != nil { +// fmt.Printf("The following stores could not be found: %s", strings.Join(lookupFailures, ",")) +// } +// orchsURL := fmt.Sprintf("https://%s/Keyfactor/Portal/AgentJobStatus/Index", kfClient.Hostname) +// +// fmt.Println(fmt.Sprintf("Reconciliation completed. Check orchestrator jobs for details. %s", orchsURL)) +// } +// +// }, +// RunE: nil, +// PostRun: nil, +// PostRunE: nil, +// PersistentPostRun: nil, +// PersistentPostRunE: nil, +// FParseErrWhitelist: cobra.FParseErrWhitelist{}, +// CompletionOptions: cobra.CompletionOptions{}, +// TraverseChildren: false, +// Hidden: false, +// SilenceErrors: false, +// SilenceUsage: false, +// DisableFlagParsing: false, +// DisableAutoGenTag: false, +// DisableFlagsInUseLine: false, +// DisableSuggestions: false, +// SuggestionsMinimumDistance: 0, +// } +// rotGenStoreTemplateCmd = &cobra.Command{ +// Use: "generate-template", +// Aliases: nil, +// SuggestFor: nil, +// Short: "For generating Root Of Trust template(s)", +// Long: `Root Of Trust: Will parse a CSV and attempt to deploy a cert or set of certs into a list of cert stores.`, +// Example: "", +// ValidArgs: nil, +// ValidArgsFunction: nil, +// Args: nil, +// ArgAliases: nil, +// BashCompletionFunction: "", +// Deprecated: "", +// Annotations: nil, +// Version: "", +// PersistentPreRun: nil, +// PersistentPreRunE: nil, +// PreRun: nil, +// PreRunE: nil, +// Run: func(cmd *cobra.Command, args []string) { +// // Global flags +// debugFlag, _ := cmd.Flags().GetBool("debugFlag") +// configFile, _ := cmd.Flags().GetString("config") +// noPrompt, _ := cmd.Flags().GetBool("no-prompt") +// profile, _ := cmd.Flags().GetString("profile") +// +// kfcUsername, _ := cmd.Flags().GetString("kfcUsername") +// kfcPassword, _ := cmd.Flags().GetString("kfcPassword") +// kfcDomain, _ := cmd.Flags().GetString("kfcDomain") +// +// +// +// debugModeEnabled := checkDebug(debugFlag) +// log.Println("Debug mode enabled: ", debugModeEnabled) +// +// templateType, _ := cmd.Flags().GetString("type") +// format, _ := cmd.Flags().GetString("format") +// outPath, _ := cmd.Flags().GetString("outpath") +// storeType, _ := cmd.Flags().GetStringSlice("store-type") +// containerName, _ := cmd.Flags().GetStringSlice("container-name") +// collection, _ := cmd.Flags().GetStringSlice("collection") +// subjectName, _ := cmd.Flags().GetStringSlice("cn") +// stID := -1 +// var storeData []api.GetCertificateStoreResponse +// var csvStoreData [][]string +// var csvCertData [][]string +// var rowLookup = make(map[string]bool) +// kfClient, cErr := initClient(false) +// if len(storeType) != 0 { +// for _, s := range storeType { +// if cErr != nil { +// log.Fatalf("[ERROR] creating client: %s", cErr) +// } +// var sType *api.CertificateStoreType +// var stErr error +// if s == "all" { +// sType = &api.CertificateStoreType{ +// Name: "", +// ShortName: "", +// Capability: "", +// StoreType: 0, +// ImportType: 0, +// LocalStore: false, +// SupportedOperations: nil, +// Properties: nil, +// EntryParameters: nil, +// PasswordOptions: nil, +// StorePathType: "", +// StorePathValue: "", +// PrivateKeyAllowed: "", +// JobProperties: nil, +// ServerRequired: false, +// PowerShell: false, +// BlueprintAllowed: false, +// CustomAliasAllowed: "", +// ServerRegistration: 0, +// InventoryEndpoint: "", +// InventoryJobType: "", +// ManagementJobType: "", +// DiscoveryJobType: "", +// EnrollmentJobType: "", +// } +// } else { +// // check if s is an int +// sInt, err := strconv.Atoi(s) +// if err == nil { +// sType, stErr = kfClient.GetCertificateStoreTypeById(sInt) +// } else { +// sType, stErr = kfClient.GetCertificateStoreTypeByName(s) +// } +// if stErr != nil { +// fmt.Printf("[ERROR] getting store type '%s'. %s\n", s, stErr) +// continue +// } +// stID = sType.StoreType // This is the template type ID +// } +// +// if stID >= 0 || s == "all" { +// log.Printf("[DEBUG] Store type ID: %d\n", stID) +// params := make(map[string]interface{}) +// stores, sErr := kfClient.ListCertificateStores(¶ms) +// if sErr != nil { +// fmt.Printf("[ERROR] getting certificate stores of type '%s': %s\n", s, sErr) +// log.Fatalf("[ERROR] getting certificate stores of type '%s': %s", s, sErr) +// } +// for _, store := range *stores { +// if store.CertStoreType == stID || s == "all" { +// storeData = append(storeData, store) +// if !rowLookup[store.Id] { +// lineData := []string{ +// //"StoreID", "StoreType", "StoreMachine", "StorePath", "ContainerId" +// store.Id, +// fmt.Sprintf("%s", sType.ShortName), +// store.ClientMachine, +// store.StorePath, +// fmt.Sprintf("%d", store.ContainerId), +// store.ContainerName, +// getCurrentTime(""), +// } +// csvStoreData = append(csvStoreData, lineData) +// rowLookup[store.Id] = true +// } +// } +// } +// } +// } +// fmt.Println("Done") +// } +// if len(containerName) != 0 { +// for _, c := range containerName { +// +// if cErr != nil { +// log.Fatalf("[ERROR] creating client: %s", cErr) +// } +// cStoresResp, scErr := kfClient.GetCertificateStoreByContainerID(c) +// if scErr != nil { +// fmt.Printf("[ERROR] getting store container: %s\n", scErr) +// } +// if cStoresResp != nil { +// for _, store := range *cStoresResp { +// sType, stErr := kfClient.GetCertificateStoreType(store.CertStoreType) +// if stErr != nil { +// fmt.Printf("[ERROR] getting store type: %s\n", stErr) +// continue +// } +// storeData = append(storeData, store) +// if !rowLookup[store.Id] { +// lineData := []string{ +// // "StoreID", "StoreType", "StoreMachine", "StorePath", "ContainerId" +// store.Id, +// sType.ShortName, +// store.ClientMachine, +// store.StorePath, +// fmt.Sprintf("%d", store.ContainerId), +// store.ContainerName, +// getCurrentTime(""), +// } +// csvStoreData = append(csvStoreData, lineData) +// rowLookup[store.Id] = true +// } +// } +// +// } +// } +// } +// if len(collection) != 0 { +// for _, c := range collection { +// if cErr != nil { +// fmt.Println("[ERROR] connecting to Keyfactor. Please check your configuration and try again.") +// log.Fatalf("[ERROR] creating client: %s", cErr) +// } +// q := make(map[string]string) +// q["collection"] = c +// certsResp, scErr := kfClient.ListCertificates(q) +// if scErr != nil { +// fmt.Printf("No certificates found in collection: %s\n", scErr) +// } +// if certsResp != nil { +// for _, cert := range certsResp { +// if !rowLookup[cert.Thumbprint] { +// lineData := []string{ +// // "Thumbprint", "SubjectName", "Issuer", "CertID", "Locations", "LastQueriedDate" +// cert.Thumbprint, +// cert.IssuedCN, +// cert.IssuerDN, +// fmt.Sprintf("%d", cert.Id), +// fmt.Sprintf("%v", cert.Locations), +// getCurrentTime(""), +// } +// csvCertData = append(csvCertData, lineData) +// rowLookup[cert.Thumbprint] = true +// } +// } +// +// } +// } +// } +// if len(subjectName) != 0 { +// for _, s := range subjectName { +// if cErr != nil { +// fmt.Println("[ERROR] connecting to Keyfactor. Please check your configuration and try again.") +// log.Fatalf("[ERROR] creating client: %s", cErr) +// } +// q := make(map[string]string) +// q["subject"] = s +// certsResp, scErr := kfClient.ListCertificates(q) +// if scErr != nil { +// fmt.Printf("No certificates found with CN: %s\n", scErr) +// } +// if certsResp != nil { +// for _, cert := range certsResp { +// if !rowLookup[cert.Thumbprint] { +// locationsFormatted := "" +// for _, loc := range cert.Locations { +// locationsFormatted += fmt.Sprintf("%s:%s\n", loc.StoreMachine, loc.StorePath) +// } +// lineData := []string{ +// // "Thumbprint", "SubjectName", "Issuer", "CertID", "Locations", "LastQueriedDate" +// cert.Thumbprint, +// cert.IssuedCN, +// cert.IssuerDN, +// fmt.Sprintf("%d", cert.Id), +// locationsFormatted, +// getCurrentTime(""), +// } +// csvCertData = append(csvCertData, lineData) +// rowLookup[cert.Thumbprint] = true +// } +// } +// +// } +// } +// } +// // Create CSV template file +// +// var filePath string +// if outPath != "" { +// filePath = outPath +// } else { +// filePath = fmt.Sprintf("%s_template.%s", templateType, format) +// } +// file, err := os.Create(filePath) +// if err != nil { +// fmt.Printf("[ERROR] creating file: %s", err) +// log.Fatal("Cannot create file", err) +// } +// +// switch format { +// case "csv": +// writer := csv.NewWriter(file) +// var data [][]string +// switch templateType { +// case "stores": +// data = append(data, StoreHeader) +// if len(csvStoreData) != 0 { +// data = append(data, csvStoreData...) +// } +// case "certs": +// data = append(data, CertHeader) +// if len(csvCertData) != 0 { +// data = append(data, csvCertData...) +// } +// case "actions": +// data = append(data, AuditHeader) +// } +// csvErr := writer.WriteAll(data) +// if csvErr != nil { +// fmt.Println(csvErr) +// } +// defer file.Close() +// +// case "json": +// writer := bufio.NewWriter(file) +// _, err := writer.WriteString("StoreID,StoreType,StoreMachine,StorePath") +// if err != nil { +// log.Fatal("Cannot write to file", err) +// } +// } +// fmt.Printf("Template file created at %s.\n", filePath) +// }, +// RunE: nil, +// PostRun: nil, +// PostRunE: nil, +// PersistentPostRun: nil, +// PersistentPostRunE: nil, +// FParseErrWhitelist: cobra.FParseErrWhitelist{}, +// CompletionOptions: cobra.CompletionOptions{}, +// TraverseChildren: false, +// Hidden: false, +// SilenceErrors: false, +// SilenceUsage: false, +// DisableFlagParsing: false, +// DisableAutoGenTag: false, +// DisableFlagsInUseLine: false, +// DisableSuggestions: false, +// SuggestionsMinimumDistance: 0, +// } +//) +// +//func init() { +// log.SetFlags(log.LstdFlags | log.Lshortfile) +// log.SetOutput(os.Stdout) +// var ( +// stores string +// addCerts string +// removeCerts string +// minCertsInStore int +// maxPrivateKeys int +// maxLeaves int +// tType = tTypeCerts +// outPath string +// outputFormat string +// inputFile string +// storeTypes []string +// containerNames []string +// collections []string +// subjectNames []string +// ) +// +// storesCmd.AddCommand(rotCmd) +// +// // Root of trust `audit` command +// rotCmd.AddCommand(rotAuditCmd) +// rotAuditCmd.Flags().StringVarP(&stores, "stores", "s", "", "CSV file containing cert stores to enroll into") +// rotAuditCmd.Flags().StringVarP( +// &addCerts, "add-certs", "a", "", +// "CSV file containing cert(s) to enroll into the defined cert stores", +// ) +// rotAuditCmd.Flags().StringVarP( +// &removeCerts, "remove-certs", "r", "", +// "CSV file containing cert(s) to remove from the defined cert stores", +// ) +// rotAuditCmd.Flags().IntVarP( +// &minCertsInStore, +// "min-certs", +// "m", +// -1, +// "The minimum number of certs that should be in a store to be considered a 'root' store. If set to `-1` then all stores will be considered.", +// ) +// rotAuditCmd.Flags().IntVarP( +// &maxPrivateKeys, +// "max-keys", +// "k", +// -1, +// "The max number of private keys that should be in a store to be considered a 'root' store. If set to `-1` then all stores will be considered.", +// ) +// rotAuditCmd.Flags().IntVarP( +// &maxLeaves, +// "max-leaf-certs", +// "l", +// -1, +// "The max number of non-root-certs that should be in a store to be considered a 'root' store. If set to `-1` then all stores will be considered.", +// ) +// rotAuditCmd.Flags().BoolP("dry-run", "d", false, "Dry run mode") +// rotAuditCmd.Flags().StringVarP( +// &outPath, "outpath", "o", "", +// "Path to write the audit report file to. If not specified, the file will be written to the current directory.", +// ) +// +// // Root of trust `reconcile` command +// rotCmd.AddCommand(rotReconcileCmd) +// rotReconcileCmd.Flags().StringVarP(&stores, "stores", "s", "", "CSV file containing cert stores to enroll into") +// rotReconcileCmd.Flags().StringVarP( +// &addCerts, "add-certs", "a", "", +// "CSV file containing cert(s) to enroll into the defined cert stores", +// ) +// rotReconcileCmd.Flags().StringVarP( +// &removeCerts, "remove-certs", "r", "", +// "CSV file containing cert(s) to remove from the defined cert stores", +// ) +// rotReconcileCmd.Flags().IntVarP( +// &minCertsInStore, +// "min-certs", +// "m", +// -1, +// "The minimum number of certs that should be in a store to be considered a 'root' store. If set to `-1` then all stores will be considered.", +// ) +// rotReconcileCmd.Flags().IntVarP( +// &maxPrivateKeys, +// "max-keys", +// "k", +// -1, +// "The max number of private keys that should be in a store to be considered a 'root' store. If set to `-1` then all stores will be considered.", +// ) +// rotReconcileCmd.Flags().IntVarP( +// &maxLeaves, +// "max-leaf-certs", +// "l", +// -1, +// "The max number of non-root-certs that should be in a store to be considered a 'root' store. If set to `-1` then all stores will be considered.", +// ) +// rotReconcileCmd.Flags().BoolP("dry-run", "d", false, "Dry run mode") +// rotReconcileCmd.Flags().BoolP("import-csv", "v", false, "Import an audit report file in CSV format.") +// rotReconcileCmd.Flags().StringVarP( +// &inputFile, "input-file", "i", reconcileDefaultFileName, +// "Path to a file generated by 'stores rot audit' command.", +// ) +// rotReconcileCmd.Flags().StringVarP( +// &outPath, "outpath", "o", "", +// "Path to write the audit report file to. If not specified, the file will be written to the current directory.", +// ) +// //rotReconcileCmd.MarkFlagsRequiredTogether("add-certs", "stores") +// //rotReconcileCmd.MarkFlagsRequiredTogether("remove-certs", "stores") +// rotReconcileCmd.MarkFlagsMutuallyExclusive("add-certs", "import-csv") +// rotReconcileCmd.MarkFlagsMutuallyExclusive("remove-certs", "import-csv") +// rotReconcileCmd.MarkFlagsMutuallyExclusive("stores", "import-csv") +// +// // Root of trust `generate` command +// rotCmd.AddCommand(rotGenStoreTemplateCmd) +// rotGenStoreTemplateCmd.Flags().StringVarP( +// &outPath, "outpath", "o", "", +// "Path to write the template file to. If not specified, the file will be written to the current directory.", +// ) +// rotGenStoreTemplateCmd.Flags().StringVarP( +// &outputFormat, "format", "f", "csv", +// "The type of template to generate. Only `csv` is supported at this time.", +// ) +// rotGenStoreTemplateCmd.Flags().Var( +// &tType, "type", +// `The type of template to generate. Only "certs|stores|actions" are supported at this time.`, +// ) +// rotGenStoreTemplateCmd.Flags().StringSliceVar( +// &storeTypes, +// "store-type", +// []string{}, +// "Multi value flag. Attempt to pre-populate the stores template with the certificate stores matching specified store types. If not specified, the template will be empty.", +// ) +// rotGenStoreTemplateCmd.Flags().StringSliceVar( +// &containerNames, +// "container-name", +// []string{}, +// "Multi value flag. Attempt to pre-populate the stores template with the certificate stores matching specified container types. If not specified, the template will be empty.", +// ) +// rotGenStoreTemplateCmd.Flags().StringSliceVar( +// &subjectNames, +// "cn", +// []string{}, +// "Subject name(s) to pre-populate the 'certs' template with. If not specified, the template will be empty. Does not work with SANs.", +// ) +// rotGenStoreTemplateCmd.Flags().StringSliceVar( +// &collections, +// "collection", +// []string{}, +// "Certificate collection name(s) to pre-populate the stores template with. If not specified, the template will be empty.", +// ) +// +// rotGenStoreTemplateCmd.RegisterFlagCompletionFunc("type", templateTypeCompletion) +// rotGenStoreTemplateCmd.MarkFlagRequired("type") +//} diff --git a/cmd/storeTypes.go b/cmd/storeTypes.go index 3d8b56b..20e43f0 100644 --- a/cmd/storeTypes.go +++ b/cmd/storeTypes.go @@ -15,19 +15,27 @@ package cmd import ( + _ "embed" "encoding/json" "fmt" - "github.com/AlecAivazis/survey/v2" - "github.com/Keyfactor/keyfactor-go-client/v2/api" - "github.com/rs/zerolog/log" - "github.com/spf13/cobra" "io" "net/http" "os" "sort" "strings" + "time" + + stdlog "log" + + "github.com/AlecAivazis/survey/v2" + "github.com/Keyfactor/keyfactor-go-client/v3/api" + "github.com/rs/zerolog/log" + "github.com/spf13/cobra" ) +//go:embed store_types.json +var EmbeddedStoreTypesJSON []byte + var storeTypesCmd = &cobra.Command{ Use: "store-types", Short: "Keyfactor certificate store types APIs and utilities.", @@ -46,13 +54,18 @@ var storesTypesListCmd = &cobra.Command{ if debugErr != nil { return debugErr } + stdlog.SetOutput(io.Discard) informDebug(debugFlag) // Authenticate - authConfig := createAuthConfigFromParams(kfcHostName, kfcUsername, kfcPassword, kfcDomain, kfcAPIPath) - kfClient, _ := initClient(configFile, profile, providerType, providerProfile, noPrompt, authConfig, false) + kfClient, cErr := initClient(false) + if cErr != nil { + log.Error().Err(cErr).Msg("unable to authenticate") + return cErr + } // CLI Logic + storeTypes, err := kfClient.ListCertificateStoreTypes() if err != nil { @@ -78,8 +91,9 @@ var storesTypeCreateCmd = &cobra.Command{ cmd.SilenceUsage = true // Specific flags gitRef, _ := cmd.Flags().GetString(FlagGitRef) + gitRepo, _ := cmd.Flags().GetString(FlagGitRepo) creatAll, _ := cmd.Flags().GetBool("all") - validStoreTypes := getValidStoreTypes("", gitRef) + validStoreTypes := getValidStoreTypes("", gitRef, gitRepo) storeType, _ := cmd.Flags().GetString("name") listTypes, _ := cmd.Flags().GetBool("list") storeTypeConfigFile, _ := cmd.Flags().GetString("from-file") @@ -93,15 +107,26 @@ var storesTypeCreateCmd = &cobra.Command{ informDebug(debugFlag) // Authenticate - authConfig := createAuthConfigFromParams(kfcHostName, kfcUsername, kfcPassword, kfcDomain, kfcAPIPath) - kfClient, _ := initClient(configFile, profile, providerType, providerProfile, noPrompt, authConfig, false) + kfClient, _ := initClient(false) // CLI Logic if gitRef == "" { - gitRef = "main" + gitRef = DefaultGitRef + } + if gitRepo == "" { + gitRepo = DefaultGitRepo } storeTypeIsValid := false + log.Debug().Str("storeType", storeType). + Bool("listTypes", listTypes). + Str("storeTypeConfigFile", storeTypeConfigFile). + Bool("creatAll", creatAll). + Str("gitRef", gitRef). + Str("gitRepo", gitRepo). + Strs("validStoreTypes", validStoreTypes). + Msg("create command flags") + if listTypes { fmt.Println("Available store types:") sort.Strings(validStoreTypes) @@ -113,13 +138,15 @@ var storesTypeCreateCmd = &cobra.Command{ } if storeTypeConfigFile != "" { - createdStore, err := createStoreFromFile(storeTypeConfigFile, kfClient) + createdStoreTypes, err := createStoreTypeFromFile(storeTypeConfigFile, kfClient) if err != nil { fmt.Printf("Failed to create store type from file \"%s\"", err) return err } - fmt.Printf("Created store type called \"%s\"\n", createdStore.Name) + for _, v := range createdStoreTypes { + fmt.Printf("Created store type \"%s\"\n", v.Name) + } return nil } @@ -138,12 +165,16 @@ var storesTypeCreateCmd = &cobra.Command{ } for _, v := range validStoreTypes { if strings.EqualFold(v, strings.ToUpper(storeType)) || creatAll { - log.Printf("[DEBUG] Valid store type: %s", storeType) + log.Debug().Str("storeType", storeType).Msg("Store type is valid") storeTypeIsValid = true break } } if !storeTypeIsValid { + log.Error(). + Str("storeType", storeType). + Bool("isValid", storeTypeIsValid). + Msg("Invalid store type") fmt.Printf("ERROR: Invalid store type: %s\nValid types are:\n", storeType) for _, st := range validStoreTypes { fmt.Println(fmt.Sprintf("\t%s", st)) @@ -157,7 +188,7 @@ var storesTypeCreateCmd = &cobra.Command{ } else { typesToCreate = validStoreTypes } - storeTypeConfig, stErr := readStoreTypesConfig("", gitRef) + storeTypeConfig, stErr := readStoreTypesConfig("", gitRef, gitRepo, offline) if stErr != nil { log.Error().Err(stErr).Send() return stErr @@ -226,11 +257,10 @@ var storesTypeDeleteCmd = &cobra.Command{ Msg("delete command flags") // Authenticate - authConfig := createAuthConfigFromParams(kfcHostName, kfcUsername, kfcPassword, kfcDomain, kfcAPIPath) if gitRef == "" { gitRef = "main" } - kfClient, _ := initClient(configFile, profile, providerType, providerProfile, noPrompt, authConfig, false) + kfClient, _ := initClient(false) var validStoreTypes []string var removeStoreTypes []interface{} @@ -238,7 +268,7 @@ var storesTypeDeleteCmd = &cobra.Command{ validStoreTypesResp, vstErr := kfClient.ListCertificateStoreTypes() if vstErr != nil { log.Error().Err(vstErr).Msg("unable to list certificate store types") - validStoreTypes = getValidStoreTypes("", gitRef) + validStoreTypes = getValidStoreTypes("", gitRef, DefaultGitRepo) } else { for _, v := range *validStoreTypesResp { validStoreTypes = append(validStoreTypes, v.ShortName) @@ -292,7 +322,10 @@ var storesTypeDeleteCmd = &cobra.Command{ } if dryRun { - outputResult(fmt.Sprintf("dry run delete called on certificate store type (%v) with ID: %d", st, id), outputFormat) + outputResult( + fmt.Sprintf("dry run delete called on certificate store type (%v) with ID: %d", st, id), + outputFormat, + ) } else { log.Debug().Interface("storeType", st). Int("id", id). @@ -332,6 +365,7 @@ var fetchStoreTypesCmd = &cobra.Command{ cmd.SilenceUsage = true // Specific flags gitRef, _ := cmd.Flags().GetString(FlagGitRef) + gitRepo, _ := cmd.Flags().GetString(FlagGitRepo) // Debug + expEnabled checks isExperimental := false @@ -344,7 +378,7 @@ var fetchStoreTypesCmd = &cobra.Command{ if gitRef == "" { gitRef = "main" } - templates, err := readStoreTypesConfig("", gitRef) + templates, err := readStoreTypesConfig("", gitRef, gitRepo, offline) if err != nil { log.Error().Err(err).Send() return err @@ -383,34 +417,107 @@ var fetchStoreTypesCmd = &cobra.Command{ // return nil // }, // } -func createStoreFromFile(filename string, kfClient *api.Client) (*api.CertificateStoreType, error) { +func createStoreTypeFromFile(filename string, kfClient *api.Client) ([]api.CertificateStoreType, error) { // Read the file + log.Debug().Str("filename", filename).Msg("Reading store type from file") file, err := os.Open(filename) + defer file.Close() if err != nil { + log.Error(). + Str("filename", filename). + Err(err).Msg("unable to open file") return nil, err } // Compile JSON contents to a api.CertificateStoreType struct - var storeType api.CertificateStoreType + var sType api.CertificateStoreType + var sTypes []api.CertificateStoreType + + log.Debug().Msg("Decoding JSON file as single store type") decoder := json.NewDecoder(file) - err = decoder.Decode(&storeType) - if err != nil { - return nil, err + err = decoder.Decode(&sType) + if err != nil || (sType.ShortName == "" && sType.Capability == "") { + log.Warn().Err(err).Msg("Unable to decode JSON file, attempting to parse an integration manifest") + // Attempt to parse as an integration manifest + var manifest IntegrationManifest + log.Debug().Msg("Decoding JSON file as integration manifest") + // Reset the file pointer + _, err = file.Seek(0, 0) + decoder = json.NewDecoder(file) + mErr := decoder.Decode(&manifest) + if mErr != nil { + return nil, err + } + log.Debug().Msg("Decoded JSON file as integration manifest") + sTypes = manifest.About.Orchestrator.StoreTypes + } else { + log.Debug().Msg("Decoded JSON file as single store type") + sTypes = []api.CertificateStoreType{sType} } + for _, st := range sTypes { + log.Debug().Msgf("Creating certificate store type %s", st.Name) + createResp, cErr := kfClient.CreateStoreType(&st) + if cErr != nil { + log.Error(). + Str("storeType", st.Name). + Err(cErr).Msg("unable to create certificate store type") + return nil, cErr + } + log.Info().Msgf("Certificate store type %s created with ID: %d", st.Name, createResp.StoreType) + } // Use the Keyfactor client to create the store type - createResp, err := kfClient.CreateStoreType(&storeType) - if err != nil { - return nil, err + log.Debug().Msg("Store type created") + return sTypes, nil +} + +func formatStoreTypes(sTypesList *[]interface{}) (map[string]interface{}, error) { + + if sTypesList == nil || len(*sTypesList) == 0 { + return nil, fmt.Errorf("empty store types list") + } + + output := make(map[string]interface{}) + for _, v := range *sTypesList { + v2 := v.(map[string]interface{}) + output[v2["ShortName"].(string)] = v2 } - return createResp, nil + + return output, nil } -func getStoreTypesInternet(gitRef string) (map[string]interface{}, error) { +func getStoreTypesInternet(gitRef string, repo string) (map[string]interface{}, error) { //resp, err := http.Get("https://raw.githubusercontent.com/keyfactor/kfutil/main/store_types.json") //resp, err := http.Get("https://raw.githubusercontent.com/keyfactor/kfctl/master/storetypes/storetypes.json") - resp, rErr := http.Get(fmt.Sprintf("https://raw.githubusercontent.com/Keyfactor/kfutil/%s/store_types.json", gitRef)) + baseUrl := "https://raw.githubusercontent.com/Keyfactor/%s/%s/%s" + if gitRef == "" { + gitRef = DefaultGitRef + } + if repo == "" { + repo = DefaultGitRepo + } + + var fileName string + if repo == "kfutil" { + fileName = "store_types.json" + } else { + fileName = "integration-manifest.json" + } + + url := fmt.Sprintf(baseUrl, repo, gitRef, fileName) + log.Debug(). + Str("url", url). + Msg("Getting store types from internet") + + // Define the timeout duration + timeout := MinHttpTimeout * time.Second + + // Create a custom http.Client with the timeout + client := &http.Client{ + Timeout: timeout, + } + resp, rErr := client.Get(url) if rErr != nil { return nil, rErr } @@ -421,24 +528,52 @@ func getStoreTypesInternet(gitRef string) (map[string]interface{}, error) { } // read as list of interfaces var result []interface{} - json.Unmarshal(body, &result) - - // convert to map - var result2 map[string]interface{} - result2 = make(map[string]interface{}) - for _, v := range result { - v2 := v.(map[string]interface{}) - result2[v2["ShortName"].(string)] = v2 + jErr := json.Unmarshal(body, &result) + if jErr != nil { + log.Warn().Err(jErr).Msg("Unable to decode JSON file, attempting to parse an integration manifest") + // Attempt to parse as an integration manifest + var manifest IntegrationManifest + log.Debug().Msg("Decoding JSON file as integration manifest") + // Reset the file pointer + + mErr := json.Unmarshal(body, &manifest) + if mErr != nil { + return nil, jErr + } + log.Debug().Msg("Decoded JSON file as integration manifest") + sTypes := manifest.About.Orchestrator.StoreTypes + output := make(map[string]interface{}) + for _, st := range sTypes { + output[st.ShortName] = st + } + return output, nil + } + output, sErr := formatStoreTypes(&result) + if sErr != nil { + return nil, err + } else if output == nil { + return nil, fmt.Errorf("unable to fetch store types from %s", url) } + return output, nil - return result2, nil } -func getValidStoreTypes(fp string, gitRef string) []string { - validStoreTypes, rErr := readStoreTypesConfig(fp, gitRef) +func getValidStoreTypes(fp string, gitRef string, gitRepo string) []string { + log.Debug(). + Str("file", fp). + Str("gitRef", gitRef). + Str("gitRepo", gitRepo). + Bool("offline", offline). + Msg(DebugFuncEnter) + + log.Debug(). + Str("file", fp). + Str("gitRef", gitRef). + Str("gitRepo", gitRepo). + Msg("Reading store types config.") + validStoreTypes, rErr := readStoreTypesConfig(fp, gitRef, gitRepo, offline) if rErr != nil { - log.Printf("Error: %s", rErr) - fmt.Printf("Error: %s\n", rErr) + log.Error().Err(rErr).Msg("unable to read store types") return nil } validStoreTypesList := make([]string, 0, len(validStoreTypes)) @@ -449,47 +584,82 @@ func getValidStoreTypes(fp string, gitRef string) []string { return validStoreTypesList } -func readStoreTypesConfig(fp string, gitRef string) (map[string]interface{}, error) { - sTypes, stErr := getStoreTypesInternet(gitRef) - if stErr != nil { - log.Error().Err(stErr).Msg("unable to read store types from internet") +func readStoreTypesConfig(fp, gitRef string, gitRepo string, offline bool) (map[string]interface{}, error) { + log.Debug().Str("file", fp).Str("gitRef", gitRef).Msg("Entering readStoreTypesConfig") + + var ( + sTypes map[string]interface{} + stErr error + ) + if offline { + log.Debug().Msg("Reading store types config from file") + } else { + log.Debug().Msg("Reading store types config from internet") + sTypes, stErr = getStoreTypesInternet(gitRef, gitRepo) + } + + if stErr != nil || sTypes == nil || len(sTypes) == 0 { + log.Warn().Err(stErr).Msg("Using embedded store-type definitions") + var emStoreTypes []interface{} + if err := json.Unmarshal(EmbeddedStoreTypesJSON, &emStoreTypes); err != nil { + log.Error().Err(err).Msg("Unable to unmarshal embedded store type definitions") + return nil, err + } + sTypes, stErr = formatStoreTypes(&emStoreTypes) + if stErr != nil { + log.Error().Err(stErr).Msg("Unable to format store types") + return nil, stErr + } } var content []byte var err error if sTypes == nil { if fp == "" { - fp = "store_types.json" + fp = DefaultStoreTypesFileName } content, err = os.ReadFile(fp) - if err != nil { - return nil, err - } } else { content, err = json.Marshal(sTypes) - if err != nil { - return nil, err - } + } + if err != nil { + return nil, err } var d map[string]interface{} - err = json.Unmarshal(content, &d) - if err != nil { - log.Error().Err(err).Msg("unable to unmarshal store types") + if err = json.Unmarshal(content, &d); err != nil { + log.Error().Err(err).Msg("Unable to unmarshal store types") return nil, err } return d, nil } func init() { - defaultGitRef := "main" + offline = true // temporarily set to true as it runs before the flag is set + debugFlag = false // temporarily set to false as it runs before the flag is set var gitRef string - validTypesString := strings.Join(getValidStoreTypes("", defaultGitRef), ", ") + var gitRepo string + validTypesString := strings.Join(getValidStoreTypes("", DefaultGitRef, DefaultGitRepo), ", ") + offline = false //revert this so that flag is not set to true by default RootCmd.AddCommand(storeTypesCmd) // GET store type templates storeTypesCmd.AddCommand(fetchStoreTypesCmd) - fetchStoreTypesCmd.Flags().StringVarP(&gitRef, FlagGitRef, "b", "main", "The git branch or tag to reference when pulling store-types from the internet.") + fetchStoreTypesCmd.Flags().StringVarP( + &gitRef, + FlagGitRef, + "b", + "main", + "The git branch or tag to reference when pulling store-types from the internet.", + ) + + fetchStoreTypesCmd.Flags().StringVarP( + &gitRepo, + FlagGitRepo, + "r", + DefaultGitRepo, + "The repository to pull store-type definitions from.", + ) // LIST command storeTypesCmd.AddCommand(storesTypesListCmd) @@ -504,10 +674,36 @@ func init() { var storeTypeName string var storeTypeID int storeTypesCmd.AddCommand(storesTypeCreateCmd) - storesTypeCreateCmd.Flags().StringVarP(&storeTypeName, "name", "n", "", "Short name of the certificate store type to get. Valid choices are: "+validTypesString) + storesTypeCreateCmd.Flags().StringVarP( + &storeTypeName, + "name", + "n", + "", + "Short name of the certificate store type to get. Valid choices are: "+validTypesString, + ) storesTypeCreateCmd.Flags().BoolVarP(&listValidStoreTypes, "list", "l", false, "List valid store types.") - storesTypeCreateCmd.Flags().StringVarP(&filePath, "from-file", "f", "", "Path to a JSON file containing certificate store type data for a single store.") - storesTypeCreateCmd.Flags().StringVarP(&gitRef, FlagGitRef, "b", "main", "The git branch or tag to reference when pulling store-types from the internet.") + storesTypeCreateCmd.Flags().StringVarP( + &filePath, + "from-file", + "f", + "", + "Path to a JSON file containing certificate store type data for a single store.", + ) + storesTypeCreateCmd.Flags().StringVarP( + &gitRef, + FlagGitRef, + "b", + "main", + "The git branch or tag to reference when pulling store-types from the internet.", + ) + storesTypeCreateCmd.Flags().StringVarP( + &gitRepo, + FlagGitRepo, + "r", + DefaultGitRepo, + "The repository to pull store-types definitions from.", + ) + storesTypeCreateCmd.Flags().BoolVarP(&createAll, "all", "a", false, "Create all store types.") // UPDATE command @@ -519,7 +715,13 @@ func init() { var dryRun bool storeTypesCmd.AddCommand(storesTypeDeleteCmd) storesTypeDeleteCmd.Flags().IntVarP(&storeTypeID, "id", "i", -1, "ID of the certificate store type to delete.") - storesTypeDeleteCmd.Flags().StringVarP(&storeTypeName, "name", "n", "", "Name of the certificate store type to delete.") + storesTypeDeleteCmd.Flags().StringVarP( + &storeTypeName, + "name", + "n", + "", + "Name of the certificate store type to delete.", + ) storesTypeDeleteCmd.Flags().BoolVarP(&dryRun, "dry-run", "t", false, "Specifies whether to perform a dry run.") storesTypeDeleteCmd.MarkFlagsMutuallyExclusive("id", "name") storesTypeDeleteCmd.Flags().BoolVarP(&deleteAll, "all", "a", false, "Delete all store types.") diff --git a/cmd/storeTypes_get.go b/cmd/storeTypes_get.go index 74f8c85..8635ea3 100644 --- a/cmd/storeTypes_get.go +++ b/cmd/storeTypes_get.go @@ -19,8 +19,9 @@ package cmd import ( "encoding/json" "fmt" + "github.com/AlecAivazis/survey/v2" - "github.com/Keyfactor/keyfactor-go-client/v2/api" + "github.com/Keyfactor/keyfactor-go-client/v3/api" "github.com/rs/zerolog/log" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -65,9 +66,27 @@ func CreateStoreTypesGetFlags() *StoreTypesGetFlags { func (f *StoreTypesGetFlags) AddFlags(flags *pflag.FlagSet) { flags.IntVarP(f.storeTypeID, "id", "i", -1, "ID of the certificate store type to get.") flags.StringVarP(f.storeTypeName, "name", "n", "", "Name of the certificate store type to get.") - flags.BoolVarP(f.genericFormat, "generic", "g", false, "Output the store type in a generic format stripped of all fields specific to the Command instance.") - flags.StringVarP(f.gitRef, FlagGitRef, "b", "main", "The git branch or tag to reference when pulling store-types from the internet.") - flags.BoolVarP(f.outputToIntegrationManifest, "output-to-integration-manifest", "", false, "Update the integration manifest with the store type. It overrides the store type in the manifest if it already exists. If the integration manifest does not exist in the current directory, it will be created.") + flags.BoolVarP( + f.genericFormat, + "generic", + "g", + false, + "Output the store type in a generic format stripped of all fields specific to the Command instance.", + ) + flags.StringVarP( + f.gitRef, + FlagGitRef, + "b", + "main", + "The git branch or tag to reference when pulling store-types from the internet.", + ) + flags.BoolVarP( + f.outputToIntegrationManifest, + "output-to-integration-manifest", + "", + false, + "Update the integration manifest with the store type. It overrides the store type in the manifest if it already exists. If the integration manifest does not exist in the current directory, it will be created.", + ) } func CreateCmdStoreTypesGet() *cobra.Command { @@ -98,8 +117,7 @@ func CreateCmdStoreTypesGet() *cobra.Command { } // Authenticate - authConfig := createAuthConfigFromParams(kfcHostName, kfcUsername, kfcPassword, kfcDomain, kfcAPIPath) - kfClient, _ := initClient(configFile, profile, providerType, providerProfile, noPrompt, authConfig, false) + kfClient, _ := initClient(false) if kfClient == nil { return fmt.Errorf("failed to initialize Keyfactor client") @@ -107,7 +125,12 @@ func CreateCmdStoreTypesGet() *cobra.Command { storeTypes, err := kfClient.GetCertificateStoreType(options.storeTypeInterface) if err != nil { - log.Error().Err(err).Msg(fmt.Sprintf("unable to get certificate store type %s", options.storeTypeInterface)) + log.Error().Err(err).Msg( + fmt.Sprintf( + "unable to get certificate store type %s", + options.storeTypeInterface, + ), + ) return err } log.Trace().Msg(fmt.Sprintf("storeTypes: %+v", storeTypes)) @@ -136,7 +159,12 @@ func CreateCmdStoreTypesGet() *cobra.Command { return err } - _, err = cmd.OutOrStdout().Write([]byte(fmt.Sprintf("Successfully updated integration manifest with store type %s\n", options.storeTypeInterface))) + _, err = cmd.OutOrStdout().Write( + []byte(fmt.Sprintf( + "Successfully updated integration manifest with store type %s\n", + options.storeTypeInterface, + )), + ) } else { _, err = cmd.OutOrStdout().Write([]byte(output)) if err != nil { @@ -159,6 +187,7 @@ type StoreTypesGetOptions struct { storeTypeName string genericFormat bool gitRef string + gitRepo string storeTypeInterface interface{} outputType string outputToIntegrationManifest bool @@ -215,7 +244,7 @@ func (f *StoreTypesGetOptions) Validate() error { // Check inputs and prompt if necessary // The f.storeTypeInterface is used to pass the store type to the API if f.storeTypeID < 0 && f.storeTypeName == "" { - validStoreTypes := getValidStoreTypes("", f.gitRef) + validStoreTypes := getValidStoreTypes("", f.gitRef, DefaultGitRepo) prompt := &survey.Select{ Message: "Choose a store type:", Options: validStoreTypes, @@ -256,7 +285,10 @@ func (f *StoreTypesGetOptions) Validate() error { return nil } -func formatStoreTypeOutput(storeType *api.CertificateStoreType, outputFormat string, outputType string) (string, error) { +func formatStoreTypeOutput(storeType *api.CertificateStoreType, outputFormat string, outputType string) ( + string, + error, +) { var sOut interface{} sOut = storeType if outputType == "generic" { diff --git a/cmd/storeTypes_get_test.go b/cmd/storeTypes_get_test.go index 517aee9..baf8c3f 100644 --- a/cmd/storeTypes_get_test.go +++ b/cmd/storeTypes_get_test.go @@ -18,97 +18,112 @@ package cmd import ( "encoding/json" + "os" + "testing" + "github.com/stretchr/testify/assert" "kfutil/pkg/cmdtest" manifestv1 "kfutil/pkg/keyfactor/v1" - "os" - "testing" ) func Test_StoreTypesGet(t *testing.T) { - t.Run("WithName", func(t *testing.T) { - testCmd := RootCmd - - output, err := cmdtest.TestExecuteCommand(t, testCmd, []string{"store-types", "get", "--name", "PEM", "--debug"}...) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - var storeType map[string]interface{} - if err := json.Unmarshal([]byte(output), &storeType); err != nil { - t.Fatalf("Error unmarshalling JSON: %v", err) - } - - assert.NotNil(t, storeType["Name"], "Expected store type to have a name") - assert.NotNil(t, storeType["ShortName"], "Expected store type to have short name") - assert.NotNil(t, storeType["StoreType"], "Expected store type to have a store type") - - // verify that the store type is an integer - _, ok := storeType["StoreType"].(float64) - assert.True(t, ok, "Expected store type to be an integer") - // verify short name is a string - _, ok = storeType["ShortName"].(string) - assert.True(t, ok, "Expected short name to be a string") - // verify name is a string - _, ok = storeType["Name"].(string) - assert.True(t, ok, "Expected name to be a string") - // check that shortname == AWS - assert.Equal(t, storeType["ShortName"], "PEM", "Expected short name to be PEM") - }) - - t.Run("GenericOutput", func(t *testing.T) { - testCmd := RootCmd - output, err := cmdtest.TestExecuteCommand(t, testCmd, []string{"store-types", "get", "--name", "PEM", "-g", "--debug"}...) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - var storeType map[string]interface{} - if err := json.Unmarshal(output, &storeType); err != nil { - t.Fatalf("Error unmarshalling JSON: %v", err) - } - - assert.NotNil(t, storeType["Name"], "Expected store type to have a Name") - assert.NotNil(t, storeType["ShortName"], "Expected store type to have ShortName") - - assert.Nil(t, storeType["StoreType"], "Expected StoreType to to be nil") - assert.Nil(t, storeType["InventoryJobType"], "Expected InventoryJobType to be nil") - assert.Nil(t, storeType["InventoryEndpoint"], "Expected InventoryEndpoint to be nil") - assert.Nil(t, storeType["ManagementJobType"], "Expected ManagementJobType to be nil") - assert.Nil(t, storeType["DiscoveryJobType"], "Expected DiscoveryJobType to be nil") - assert.Nil(t, storeType["EnrollmentJobType"], "Expected EnrollmentJobType to be nil") - assert.Nil(t, storeType["ImportType"], "Expected ImportType to be nil") - - // verify short name is a string - _, ok := storeType["ShortName"].(string) - assert.True(t, ok, "Expected short name to be a string") - // verify name is a string - _, ok = storeType["Name"].(string) - assert.True(t, ok, "Expected name to be a string") - // check that shortname == PEM - assert.Equal(t, storeType["ShortName"], "PEM", "Expected short name to be PEM") - }) - - t.Run("OutputToManifest", func(t *testing.T) { - testCmd := RootCmd - _, err := cmdtest.TestExecuteCommand(t, testCmd, []string{"store-types", "get", "--name", "PEM", "--output-to-integration-manifest", "--debug"}...) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - - // Verify that integration-manifest.json was created - manifest := manifestv1.IntegrationManifest{} - err = manifest.LoadFromFilesystem() - if err != nil { - t.Fatalf("Error loading integration manifest: %v", err) - } - - if len(manifest.About.Orchestrator.StoreTypes) != 1 { - t.Fatalf("Expected 1 store type, got %d", len(manifest.About.Orchestrator.StoreTypes)) - } - - // Clean up - err = os.Remove("integration-manifest.json") - if err != nil { - t.Errorf("Error removing integration-manifest.json: %v", err) - } - }) + t.Run( + "WithName", func(t *testing.T) { + testCmd := RootCmd + + output, err := cmdtest.TestExecuteCommand(t, testCmd, []string{"store-types", "get", "--name", "PEM"}...) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + var storeType map[string]interface{} + if err := json.Unmarshal([]byte(output), &storeType); err != nil { + t.Fatalf("Error unmarshalling JSON: %v", err) + } + + assert.NotNil(t, storeType["Name"], "Expected store type to have a name") + assert.NotNil(t, storeType["ShortName"], "Expected store type to have short name") + assert.NotNil(t, storeType["StoreType"], "Expected store type to have a store type") + + // verify that the store type is an integer + _, ok := storeType["StoreType"].(float64) + assert.True(t, ok, "Expected store type to be an integer") + // verify short name is a string + _, ok = storeType["ShortName"].(string) + assert.True(t, ok, "Expected short name to be a string") + // verify name is a string + _, ok = storeType["Name"].(string) + assert.True(t, ok, "Expected name to be a string") + // check that shortname == AWS + assert.Equal(t, storeType["ShortName"], "PEM", "Expected short name to be PEM") + }, + ) + + t.Run( + "GenericOutput", func(t *testing.T) { + testCmd := RootCmd + output, err := cmdtest.TestExecuteCommand( + t, + testCmd, + []string{"store-types", "get", "--name", "PEM", "-g"}..., + ) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + var storeType map[string]interface{} + if err := json.Unmarshal(output, &storeType); err != nil { + t.Fatalf("Error unmarshalling JSON: %v", err) + } + + assert.NotNil(t, storeType["Name"], "Expected store type to have a Name") + assert.NotNil(t, storeType["ShortName"], "Expected store type to have ShortName") + + assert.Nil(t, storeType["StoreType"], "Expected StoreType to to be nil") + assert.Nil(t, storeType["InventoryJobType"], "Expected InventoryJobType to be nil") + assert.Nil(t, storeType["InventoryEndpoint"], "Expected InventoryEndpoint to be nil") + assert.Nil(t, storeType["ManagementJobType"], "Expected ManagementJobType to be nil") + assert.Nil(t, storeType["DiscoveryJobType"], "Expected DiscoveryJobType to be nil") + assert.Nil(t, storeType["EnrollmentJobType"], "Expected EnrollmentJobType to be nil") + assert.Nil(t, storeType["ImportType"], "Expected ImportType to be nil") + + // verify short name is a string + _, ok := storeType["ShortName"].(string) + assert.True(t, ok, "Expected short name to be a string") + // verify name is a string + _, ok = storeType["Name"].(string) + assert.True(t, ok, "Expected name to be a string") + // check that shortname == PEM + assert.Equal(t, storeType["ShortName"], "PEM", "Expected short name to be PEM") + }, + ) + + t.Run( + "OutputToManifest", func(t *testing.T) { + testCmd := RootCmd + _, err := cmdtest.TestExecuteCommand( + t, + testCmd, + []string{"store-types", "get", "--name", "PEM", "--output-to-integration-manifest"}..., + ) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } + + // Verify that integration-manifest.json was created + manifest := manifestv1.IntegrationManifest{} + err = manifest.LoadFromFilesystem() + if err != nil { + t.Fatalf("Error loading integration manifest: %v", err) + } + + if len(manifest.About.Orchestrator.StoreTypes) != 1 { + t.Fatalf("Expected 1 store type, got %d", len(manifest.About.Orchestrator.StoreTypes)) + } + + // Clean up + err = os.Remove("integration-manifest.json") + if err != nil { + t.Errorf("Error removing integration-manifest.json: %v", err) + } + }, + ) } diff --git a/cmd/storeTypes_test.go b/cmd/storeTypes_test.go index 6cc3e20..9098d7f 100644 --- a/cmd/storeTypes_test.go +++ b/cmd/storeTypes_test.go @@ -17,10 +17,36 @@ package cmd import ( "encoding/json" "fmt" - "github.com/stretchr/testify/assert" + "net/url" "os" "strings" "testing" + + "github.com/spf13/cobra" + "github.com/stretchr/testify/assert" +) + +var ( + UndeleteableExceptions = []string{ + "F5-CA-REST: Certificate Store Type with either short name 'F5-CA-REST' or name 'F5 CA Profiles REST' already exists.", + "F5-WS-REST: Certificate Store Type with either short name 'F5-WS-REST' or name 'F5 WS Profiles REST' already exists.", + "F5-SL-REST: Certificate Store Type with either short name 'F5-SL-REST' or name 'F5 SSL Profiles REST' already exists.", + "F5: Certificate Store Type with either short name 'F5' or name 'F5' already exists.", + "IIS: Certificate Store Type with either short name 'IIS' or name 'IIS' already exists.", + "JKS: Certificate Store Type with either short name 'JKS' or name 'JKS' already exists.", + "NS: Certificate Store Type with either short name 'NS' or name 'Netscaler' already exists.", + "PEM: Certificate Store Type with either short name 'PEM' or name 'PEM' already exists.", + } + UndeleteableTypes = []string{ + "F5-CA-REST", + "F5-WS-REST", + "F5-SL-REST", + "F5", + "IIS", + "JKS", + "NS", + "PEM", + } ) func Test_StoreTypesHelpCmd(t *testing.T) { @@ -51,12 +77,22 @@ func Test_StoreTypesListCmd(t *testing.T) { testCmd := RootCmd // test testCmd.SetArgs([]string{"store-types", "list"}) - output := captureOutput(func() { - err := testCmd.Execute() - assert.NoError(t, err) - }) + output := captureOutput( + func() { + err := testCmd.Execute() + assert.NoError(t, err) + }, + ) + // search output string for JSON and unmarshal it + //parsedOutput, pErr := findLastJSON(output) + //if pErr != nil { + // t.Log(output) + // t.Fatalf("Error parsing JSON from response: %v", pErr) + //} + var storeTypes []map[string]interface{} if err := json.Unmarshal([]byte(output), &storeTypes); err != nil { + t.Log(output) t.Fatalf("Error unmarshalling JSON: %v", err) } @@ -92,10 +128,12 @@ func Test_StoreTypesFetchTemplatesCmd(t *testing.T) { testCmd := RootCmd // test testCmd.SetArgs([]string{"store-types", "templates-fetch"}) - output := captureOutput(func() { - err := testCmd.Execute() - assert.NoError(t, err) - }) + output := captureOutput( + func() { + err := testCmd.Execute() + assert.NoError(t, err) + }, + ) var storeTypes map[string]interface{} if err := json.Unmarshal([]byte(output), &storeTypes); err != nil { t.Fatalf("Error unmarshalling JSON: %v", err) @@ -125,15 +163,19 @@ func Test_StoreTypesCreateFromTemplatesCmd(t *testing.T) { if isGhAction == "true" { ghBranch := os.Getenv("GITHUB_REF") ghBranch = strings.Replace(ghBranch, "refs/heads/", "", 1) - testArgs = append(testArgs, "--git-ref", ghBranch) + // url escape the branch name + ghBranch = url.QueryEscape(ghBranch) + testArgs = append(testArgs, "--git-ref", fmt.Sprintf("%s", ghBranch)) t.Log("GITHUB_REF: ", ghBranch) } t.Log("testArgs: ", testArgs) testCmd.SetArgs(testArgs) - templatesOutput := captureOutput(func() { - err := testCmd.Execute() - assert.NoError(t, err) - }) + templatesOutput := captureOutput( + func() { + err := testCmd.Execute() + assert.NoError(t, err) + }, + ) var storeTypes map[string]interface{} if err := json.Unmarshal([]byte(templatesOutput), &storeTypes); err != nil { t.Fatalf("Error unmarshalling JSON: %v", err) @@ -158,102 +200,202 @@ func Test_StoreTypesCreateFromTemplatesCmd(t *testing.T) { // Attempt to create the store type shortName := storeType["ShortName"].(string) - createStoreTypeTest(t, shortName) + createStoreTypeTest(t, shortName, false) } createAllStoreTypes(t, storeTypes) } -func createAllStoreTypes(t *testing.T, storeTypes map[string]interface{}) { - t.Run(fmt.Sprintf("Create ALL StoreTypes"), func(t *testing.T) { - testCmd := RootCmd - // check if I'm running inside a GitHub Action - testArgs := []string{"store-types", "create", "--all"} - isGhAction := os.Getenv("GITHUB_ACTIONS") - t.Log("GITHUB_ACTIONS: ", isGhAction) - if isGhAction == "true" { - ghBranch := os.Getenv("GITHUB_REF") - ghBranch = strings.Replace(ghBranch, "refs/heads/", "", 1) - testArgs = append(testArgs, "--git-ref", ghBranch) - t.Log("GITHUB_REF: ", ghBranch) +func testCreateStoreType( + t *testing.T, + testCmd *cobra.Command, + testArgs []string, + storeTypes map[string]interface{}, +) error { + isGhAction := os.Getenv("GITHUB_ACTIONS") + t.Log("GITHUB_ACTIONS: ", isGhAction) + if isGhAction == "true" { + ghBranch := os.Getenv("GITHUB_REF") + ghBranch = strings.Replace(ghBranch, "refs/heads/", "", 1) + // url escape the branch name + ghBranch = url.QueryEscape(ghBranch) + testArgs = append(testArgs, "--git-ref", fmt.Sprintf("%s", ghBranch)) + t.Log("GITHUB_REF: ", ghBranch) + } + t.Log("testArgs: ", testArgs) + allowFail := false + // Attempt to get the AWS store type because it comes with the product + testCmd.SetArgs(testArgs) + output := captureOutput( + func() { + err := testCmd.Execute() - } - t.Log("testArgs: ", testArgs) + if err != nil { + eMsg := err.Error() + eMsg = strings.Replace(eMsg, "while creating store types:", "", -1) + for _, exception := range UndeleteableExceptions { + eMsg = strings.Replace(eMsg, exception, "", -1) + } + eMsg = strings.TrimSpace(eMsg) + if eMsg == "" { + return + } + t.Error("Emsg: ", eMsg) + if !allowFail { + assert.NoError(t, err) + } + } + if !allowFail { + assert.NoError(t, err) + } + }, + ) - // Attempt to get the AWS store type because it comes with the product - testCmd.SetArgs(testArgs) - output := captureOutput(func() { - err := testCmd.Execute() - assert.NoError(t, err) - }) + if !allowFail { assert.NotNil(t, output, "No output returned from create all command") + } - // iterate over the store types and verify that each has a name shortname and storetype - for sType := range storeTypes { - storeType := storeTypes[sType].(map[string]interface{}) - assert.NotNil(t, storeType["Name"], "Expected store type to have a name") - assert.NotNil(t, storeType["ShortName"], "Expected store type to have short name") - - // verify short name is a string - _, ok := storeType["ShortName"].(string) - assert.True(t, ok, "Expected short name to be a string") - // verify name is a string - _, ok = storeType["Name"].(string) - assert.True(t, ok, "Expected name to be a string") + // iterate over the store types and verify that each has a name shortname and storetype + for sType := range storeTypes { + storeType := storeTypes[sType].(map[string]interface{}) + assert.NotNil(t, storeType["Name"], "Expected store type to have a name") + assert.NotNil(t, storeType["ShortName"], "Expected store type to have short name") - // Attempt to create the store type - shortName := storeType["ShortName"].(string) + // verify short name is a string + _, ok := storeType["ShortName"].(string) + assert.True(t, ok, "Expected short name to be a string") + // verify name is a string + _, ok = storeType["Name"].(string) + assert.True(t, ok, "Expected name to be a string") - assert.Contains(t, output, fmt.Sprintf("Certificate store type %s created with ID", shortName), "Expected output to contain store type created message") + // Attempt to create the store type + shortName := storeType["ShortName"].(string) + allowStoreTypeFail := false + if checkIsUnDeleteable(shortName) { + t.Logf("WARNING: Skipping check for un-deletable store-type: %s", shortName) + allowStoreTypeFail = true + } - // Delete again after create - deleteStoreTypeTest(t, shortName, true) + if !allowStoreTypeFail { + assert.Contains( + t, + output, + fmt.Sprintf("Certificate store type %s created with ID", shortName), + "Expected output to contain store type created message", + ) } - }) + + // Delete again after create + deleteStoreTypeTest(t, shortName, allowStoreTypeFail) + } + return nil +} + +func createAllStoreTypes(t *testing.T, storeTypes map[string]interface{}) { + t.Run( + fmt.Sprintf("ONLINE Create ALL StoreTypes"), func(t *testing.T) { + testCmd := RootCmd + // check if I'm running inside a GitHub Action + testArgs := []string{"store-types", "create", "--all"} + testCreateStoreType(t, testCmd, testArgs, storeTypes) + + }, + ) + t.Run( + fmt.Sprintf("OFFLINE Create ALL StoreTypes"), func(t *testing.T) { + testCmd := RootCmd + // check if I'm running inside a GitHub Action + testArgs := []string{"store-types", "create", "--all", "--offline"} + testCreateStoreType(t, testCmd, testArgs, storeTypes) + }, + ) } func deleteStoreTypeTest(t *testing.T, shortName string, allowFail bool) { - t.Run(fmt.Sprintf("Delete StoreType %s", shortName), func(t *testing.T) { - testCmd := RootCmd - testCmd.SetArgs([]string{"store-types", "delete", "--name", shortName}) - deleteStoreOutput := captureOutput(func() { - err := testCmd.Execute() + t.Run( + fmt.Sprintf("Delete StoreType %s", shortName), func(t *testing.T) { + testCmd := RootCmd + testCmd.SetArgs([]string{"store-types", "delete", "--name", shortName}) + deleteStoreOutput := captureOutput( + func() { + if checkIsUnDeleteable(shortName) { + allowFail = true + //t.Skip("Not processing un-deletable store-type: ", shortName) + //return + } + + err := testCmd.Execute() + if !allowFail { + assert.NoError(t, err) + } + }, + ) if !allowFail { - assert.NoError(t, err) + if strings.Contains(deleteStoreOutput, "does not exist") { + t.Errorf("Store type %s does not exist", shortName) + } + if strings.Contains(deleteStoreOutput, "cannot be deleted") { + assert.Fail(t, fmt.Sprintf("Store type %s already exists", shortName)) + } + if !strings.Contains(deleteStoreOutput, "deleted") { + assert.Fail(t, fmt.Sprintf("Store type %s was not deleted: %s", shortName, deleteStoreOutput)) + } + if strings.Contains(deleteStoreOutput, "error processing the request") { + assert.Fail(t, fmt.Sprintf("Store type %s was not deleted: %s", shortName, deleteStoreOutput)) + } } - }) - if !allowFail { - if strings.Contains(deleteStoreOutput, "does not exist") { - t.Errorf("Store type %s does not exist", shortName) - } - if strings.Contains(deleteStoreOutput, "cannot be deleted") { - assert.Fail(t, fmt.Sprintf("Store type %s already exists", shortName)) - } - if !strings.Contains(deleteStoreOutput, "deleted") { - assert.Fail(t, fmt.Sprintf("Store type %s was not deleted: %s", shortName, deleteStoreOutput)) - } - if strings.Contains(deleteStoreOutput, "error processing the request") { - assert.Fail(t, fmt.Sprintf("Store type %s was not deleted: %s", shortName, deleteStoreOutput)) - } - } - }) + }, + ) } -func createStoreTypeTest(t *testing.T, shortName string) { - t.Run(fmt.Sprintf("CreateStore %s", shortName), func(t *testing.T) { - testCmd := RootCmd - deleteStoreTypeTest(t, shortName, true) - testCmd.SetArgs([]string{"store-types", "create", "--name", shortName}) - createStoreOutput := captureOutput(func() { - err := testCmd.Execute() - assert.NoError(t, err) - }) +func checkIsUnDeleteable(shortName string) bool { - if strings.Contains(createStoreOutput, "already exists") { - assert.Fail(t, fmt.Sprintf("Store type %s already exists", shortName)) - } else if !strings.Contains(createStoreOutput, "created with ID") { - assert.Fail(t, fmt.Sprintf("Store type %s was not created: %s", shortName, createStoreOutput)) + for _, v := range UndeleteableTypes { + if v == shortName { + return true } - // Delete again after create - deleteStoreTypeTest(t, shortName, false) - }) + } + return false +} + +func createStoreTypeTest(t *testing.T, shortName string, allowFail bool) { + t.Run( + fmt.Sprintf("CreateStore %s", shortName), func(t *testing.T) { + testCmd := RootCmd + if checkIsUnDeleteable(shortName) { + t.Logf("WARNING: Allowing un-deletable store-type: %s to FAIL", shortName) + allowFail = true + } + deleteStoreTypeTest(t, shortName, true) + testCmd.SetArgs([]string{"store-types", "create", "--name", shortName}) + createStoreOutput := captureOutput( + func() { + err := testCmd.Execute() + if !allowFail { + assert.NoError(t, err) + } + }, + ) + + // check if any of the undeleteable_exceptions are in the output + for _, exception := range UndeleteableExceptions { + if strings.Contains(createStoreOutput, exception) { + t.Logf( + "WARNING: wxpected error encountered '%s' allowing un-deletable store-type: %s to FAIL", + exception, shortName, + ) + allowFail = true + } + } + + if !allowFail { + if strings.Contains(createStoreOutput, "already exists") { + assert.Fail(t, fmt.Sprintf("Store type %s already exists", shortName)) + } else if !strings.Contains(createStoreOutput, "created with ID") { + assert.Fail(t, fmt.Sprintf("Store type %s was not created: %s", shortName, createStoreOutput)) + } + } + // Delete again after create + deleteStoreTypeTest(t, shortName, allowFail) + }, + ) } diff --git a/cmd/store_types.json b/cmd/store_types.json new file mode 100644 index 0000000..caeb3df --- /dev/null +++ b/cmd/store_types.json @@ -0,0 +1,3956 @@ +[ + { + "Name": "Azure Keyvault", + "BlueprintAllowed": false, + "Capability": "AKV", + "CustomAliasAllowed": "Optional", + "EntryParameters": null, + "JobProperties": [], + "LocalStore": false, + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "PowerShell": false, + "PrivateKeyAllowed": "Optional", + "Properties": [ + { + "Name": "TenantId", + "DisplayName": "Tenant Id", + "Type": "String", + "DependsOn": "", + "DefaultValue": null, + "Required": false + }, + { + "Name": "SkuType", + "DisplayName": "SKU Type", + "Type": "MultipleChoice", + "DependsOn": "", + "DefaultValue": "standard,premium", + "Required": false + }, + { + "Name": "VaultRegion", + "DisplayName": "Vault Region", + "Type": "MultipleChoice", + "DependsOn": "", + "DefaultValue": "eastus,eastus2,westus2,westus3,westus", + "Required": false + }, + { + "Name": "AzureCloud", + "DisplayName": "Azure Cloud", + "Type": "MultipleChoice", + "DependsOn": "", + "DefaultValue": "public,china,germany,government", + "Required": false + }, + { + "Name": "PrivateEndpoint", + "DisplayName": "Private KeyVault Endpoint", + "Type": "String", + "DependsOn": "", + "DefaultValue": null, + "Required": false + } + ], + "ServerRequired": true, + "ShortName": "AKV", + "StorePathType": "", + "StorePathValue": "", + "SupportedOperations": { + "Add": true, + "Create": true, + "Discovery": true, + "Enrollment": false, + "Remove": true + } + }, + { + "Name": "AWS Certificate Manager", + "ShortName": "AWS-ACM", + "Capability": "AWS-ACM", + "LocalStore": false, + "SupportedOperations": { + "Add": true, + "Create": false, + "Discovery": false, + "Enrollment": false, + "Remove": true + }, + "Properties": [ + { + "Name": "UseOAuth", + "DisplayName": "Use OAuth 2.0 Provider", + "Type": "Bool", + "DependsOn": null, + "DefaultValue": "false", + "Required": true + }, + { + "Name": "UseIAM", + "DisplayName": "Use IAM User Auth", + "Type": "Bool", + "DependsOn": null, + "DefaultValue": "false", + "Required": true + }, + { + "Name": "OAuthScope", + "DisplayName": "OAuth Scope", + "Type": "String", + "DependsOn": "UseOAuth", + "DefaultValue": null, + "Required": false + }, + { + "Name": "OAuthGrantType", + "DisplayName": "OAuth Grant Type", + "Type": "String", + "DependsOn": "UseOAuth", + "DefaultValue": "client_credentials", + "Required": false + }, + { + "Name": "OAuthUrl", + "DisplayName": "OAuth Url", + "Type": "String", + "DependsOn": "UseOAuth", + "DefaultValue": "https://***/oauth2/default/v1/token", + "Required": false + }, + { + "Name": "IAMAssumeRole", + "DisplayName": "AWS Role to Assume (IAM)", + "Type": "String", + "DependsOn": "UseIAM", + "DefaultValue": null, + "Required": false + }, + { + "Name": "OAuthAssumeRole", + "DisplayName": "AWS Role to Assume (OAuth)", + "Type": "String", + "DependsOn": "UseOAuth", + "DefaultValue": null, + "Required": false + }, + { + "Name": "ServerUsername", + "DisplayName": "Server Username", + "Type": "Secret", + "DependsOn": null, + "DefaultValue": null, + "Required": false + }, + { + "Name": "ServerPassword", + "DisplayName": "Server Password", + "Type": "Secret", + "DependsOn": null, + "DefaultValue": null, + "Required": false + }, + { + "Name": "ServerUseSsl", + "DisplayName": "Use SSL", + "Type": "Bool", + "DependsOn": null, + "DefaultValue": "true", + "Required": true + } + ], + "EntryParameters": [ + { + "Name": "AWS Region", + "DisplayName": "AWS Region", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": true, + "OnRemove": false, + "OnReenrollment": false + } + } + ], + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "PrivateKeyAllowed": "Required", + "ServerRequired": true, + "PowerShell": false, + "BlueprintAllowed": true, + "CustomAliasAllowed": "Optional" + }, + { + "Name": "Akamai Certificate Provisioning Service", + "ShortName": "Akamai", + "Capability": "Akamai", + "LocalStore": false, + "SupportedOperations": { + "Add": false, + "Create": false, + "Discovery": false, + "Enrollment": true, + "Remove": false + }, + "Properties": [ + { + "StoreTypeId;omitempty": 0, + "Name": "access_token", + "DisplayName": "Access Token", + "Type": "Secret", + "DependsOn": null, + "DefaultValue": null, + "Required": true + }, + { + "StoreTypeId;omitempty": 0, + "Name": "client_token", + "DisplayName": "Client Token", + "Type": "Secret", + "DependsOn": null, + "DefaultValue": null, + "Required": true + }, + { + "StoreTypeId;omitempty": 0, + "Name": "client_secret", + "DisplayName": "Client Secret", + "Type": "Secret", + "DependsOn": null, + "DefaultValue": null, + "Required": true + } + ], + "EntryParameters": [ + { + "StoreTypeId;omitempty": 0, + "Name": "EnrollmentId", + "DisplayName": "Enrollment ID", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": false + } + }, + { + "StoreTypeId;omitempty": 0, + "Name": "ContractId", + "DisplayName": "Contract ID", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + }, + "DefaultValue": null + }, + { + "StoreTypeId;omitempty": 0, + "Name": "Sans", + "DisplayName": "SANs", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + } + }, + { + "StoreTypeId;omitempty": 0, + "Name": "admin-addressLineOne", + "DisplayName": "Admin - Address Line 1", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + }, + "DefaultValue": null + }, + { + "StoreTypeId;omitempty": 0, + "Name": "admin-addressLineTwo", + "DisplayName": "Admin - Address Line 2", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": false + } + }, + { + "StoreTypeId;omitempty": 0, + "Name": "admin-city", + "DisplayName": "Admin - City", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + }, + "DefaultValue": null + }, + { + "StoreTypeId;omitempty": 0, + "Name": "admin-country", + "DisplayName": "Admin - Country", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + }, + "DefaultValue": null + }, + { + "StoreTypeId;omitempty": 0, + "Name": "admin-email", + "DisplayName": "Admin - Email", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + }, + "DefaultValue": null + }, + { + "StoreTypeId;omitempty": 0, + "Name": "admin-firstName", + "DisplayName": "Admin - First Name", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + }, + "DefaultValue": null + }, + { + "StoreTypeId;omitempty": 0, + "Name": "admin-lastName", + "DisplayName": "Admin - Last Name", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + }, + "DefaultValue": null + }, + { + "StoreTypeId;omitempty": 0, + "Name": "admin-organizationName", + "DisplayName": "Admin - Organization Name", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + }, + "DefaultValue": null + }, + { + "StoreTypeId;omitempty": 0, + "Name": "admin-phone", + "DisplayName": "Admin - Phone", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + }, + "DefaultValue": null + }, + { + "StoreTypeId;omitempty": 0, + "Name": "admin-postalCode", + "DisplayName": "Admin - Postal Code", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + }, + "DefaultValue": null + }, + { + "StoreTypeId;omitempty": 0, + "Name": "admin-region", + "DisplayName": "Admin - Region", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + }, + "DefaultValue": null + }, + { + "StoreTypeId;omitempty": 0, + "Name": "admin-title", + "DisplayName": "Admin - Title", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + }, + "DefaultValue": null + }, + { + "StoreTypeId;omitempty": 0, + "Name": "org-addressLineOne", + "DisplayName": "Org - Address Line 1", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + }, + "DefaultValue": null + }, + { + "StoreTypeId;omitempty": 0, + "Name": "org-addressLineTwo", + "DisplayName": "Org - Address Line 2", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": false + } + }, + { + "StoreTypeId;omitempty": 0, + "Name": "org-city", + "DisplayName": "Org - City", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + }, + "DefaultValue": null + }, + { + "StoreTypeId;omitempty": 0, + "Name": "org-country", + "DisplayName": "Org - Country", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + }, + "DefaultValue": null + }, + { + "StoreTypeId;omitempty": 0, + "Name": "org-organizationName", + "DisplayName": "Org - Organization Name", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + }, + "DefaultValue": null + }, + { + "StoreTypeId;omitempty": 0, + "Name": "org-phone", + "DisplayName": "Org - Phone", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + }, + "DefaultValue": null + }, + { + "StoreTypeId;omitempty": 0, + "Name": "org-postalCode", + "DisplayName": "Org - Postal Code", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + }, + "DefaultValue": null + }, + { + "StoreTypeId;omitempty": 0, + "Name": "org-region", + "DisplayName": "Org - Region", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + }, + "DefaultValue": null + }, + { + "StoreTypeId;omitempty": 0, + "Name": "tech-addressLineOne", + "DisplayName": "Tech - Address Line 1", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + }, + "DefaultValue": null + }, + { + "StoreTypeId;omitempty": 0, + "Name": "tech-addressLineTwo", + "DisplayName": "Tech - Address Line 2", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": false + } + }, + { + "StoreTypeId;omitempty": 0, + "Name": "tech-city", + "DisplayName": "Tech - City", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + }, + "DefaultValue": null + }, + { + "StoreTypeId;omitempty": 0, + "Name": "tech-country", + "DisplayName": "Tech - Country", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + }, + "DefaultValue": null + }, + { + "StoreTypeId;omitempty": 0, + "Name": "tech-email", + "DisplayName": "Tech - Email", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + }, + "DefaultValue": null + }, + { + "StoreTypeId;omitempty": 0, + "Name": "tech-firstName", + "DisplayName": "Tech - First Name", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + }, + "DefaultValue": null + }, + { + "StoreTypeId;omitempty": 0, + "Name": "tech-lastName", + "DisplayName": "Tech - Last Name", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + }, + "DefaultValue": null + }, + { + "StoreTypeId;omitempty": 0, + "Name": "tech-organizationName", + "DisplayName": "Tech - Organization Name", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + }, + "DefaultValue": null + }, + { + "StoreTypeId;omitempty": 0, + "Name": "tech-phone", + "DisplayName": "Tech - Phone", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + }, + "DefaultValue": null + }, + { + "StoreTypeId;omitempty": 0, + "Name": "tech-postalCode", + "DisplayName": "Tech - Postal Code", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + }, + "DefaultValue": null + }, + { + "StoreTypeId;omitempty": 0, + "Name": "tech-region", + "DisplayName": "Tech - Region", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + }, + "DefaultValue": null + }, + { + "StoreTypeId;omitempty": 0, + "Name": "tech-title", + "DisplayName": "Tech - Title", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + }, + "DefaultValue": null + } + ], + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "StorePathType": "MultipleChoice", + "StorePathValue": "[\"Production\",\"Staging\"]", + "PrivateKeyAllowed": "Forbidden", + "ServerRequired": false, + "PowerShell": false, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Forbidden" + }, + { + "Name": "Azure Application Gateway Certificate Binding", + "ShortName": "AppGwBin", + "Capability": "AzureAppGwBin", + "LocalStore": false, + "ClientMachineDescription": "The Azure Tenant (directory) ID that owns the Service Principal.", + "StorePathDescription": "Azure resource ID of the application gateway, following the format: /subscriptions//resourceGroups//providers/Microsoft.Network/applicationGateways/.", + "SupportedOperations": { + "Add": true, + "Remove": false, + "Enrollment": false, + "Discovery": true, + "Inventory": false + }, + "Properties": [ + { + "Name": "ServerUsername", + "DisplayName": "Server Username", + "Type": "Secret", + "Description": "Application ID of the service principal, representing the identity used for managing the Application Gateway.", + "Required": false + }, + { + "Name": "ServerPassword", + "DisplayName": "Server Password", + "Type": "Secret", + "Description": "A Client Secret that the extension will use to authenticate with the Azure Resource Management API for managing Application Gateway certificates, OR the password that encrypts the private key in ClientCertificate", + "Required": false + }, + { + "Name": "ClientCertificate", + "DisplayName": "Client Certificate", + "Type": "Secret", + "Description": "The client certificate used to authenticate with Azure Resource Management API for managing Application Gateway certificates. See the [requirements](#client-certificate-or-client-secret) for more information.", + "Required": false + }, + { + "Name": "AzureCloud", + "DisplayName": "Azure Global Cloud Authority Host", + "Type": "MultipleChoice", + "DefaultValue": "public,china,germany,government", + "Description": "Specifies the Azure Cloud instance used by the organization.", + "Required": false + }, + { + "Name": "ServerUseSsl", + "DisplayName": "Use SSL", + "Type": "Bool", + "DefaultValue": "true", + "Description": "Specifies whether SSL should be used for communication with the server. Set to 'true' to enable SSL, and 'false' to disable it.", + "Required": true + } + ], + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "PrivateKeyAllowed": "Required", + "ServerRequired": true, + "PowerShell": false, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Required" + }, + { + "Name": "Azure App Registration (Application)", + "ShortName": "AzureApp", + "Capability": "AzureApp", + "LocalStore": false, + "ClientMachineDescription": "The Azure Tenant (directory) ID that owns the Service Principal.", + "StorePathDescription": "The Application ID of the target Application/Service Principal that will be managed by the Azure App Registration and Enterprise Application Orchestrator extension.", + "SupportedOperations": { + "Add": true, + "Remove": true, + "Enrollment": false, + "Discovery": true, + "Inventory": true + }, + "Properties": [ + { + "Name": "ServerUsername", + "DisplayName": "Server Username", + "Type": "Secret", + "Description": "The Application ID of the Service Principal used to authenticate with Microsoft Graph for managing Application/Service Principal certificates.", + "Required": true + }, + { + "Name": "ServerPassword", + "DisplayName": "Server Password", + "Type": "Secret", + "Description": "A Client Secret that the extension will use to authenticate with Microsoft Graph for managing Application/Service Principal certificates, OR the password that encrypts the private key in ClientCertificate. If Client Cert Auth is used _and_ the Client Certificate's private key is not encrypted, you **must** select 'No Value' for this field.", + "Required": false + }, + { + "Name": "ClientCertificate", + "DisplayName": "Client Certificate", + "Type": "Secret", + "Description": "The client certificate used to authenticate with Microsoft Graph for managing Application/Service Principal certificates. See the [requirements](#client-certificate-or-client-secret) for more information. If Client Certificate Auth is not used, you **must** select 'No Value' for this field.", + "Required": false + }, + { + "Name": "AzureCloud", + "DisplayName": "Azure Global Cloud Authority Host", + "Type": "MultipleChoice", + "DefaultValue": "public,china,germany,government", + "Description": "Specifies the Azure Cloud instance used by the organization.", + "Required": false + }, + { + "Name": "ServerUseSsl", + "DisplayName": "Use SSL", + "Type": "Bool", + "DefaultValue": "true", + "Description": "Specifies whether SSL should be used for communication with the server. Set to 'true' to enable SSL, and 'false' to disable it.", + "Required": true + } + ], + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "PrivateKeyAllowed": "Forbidden", + "ServerRequired": true, + "PowerShell": false, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Required" + }, + { + "Name": "Azure App Registration 2 (Application)", + "ShortName": "AzureApp2", + "Capability": "AzureApp2", + "LocalStore": false, + "ClientMachineDescription": "The Azure Tenant (directory) ID where the Application is instantiated", + "StorePathDescription": "The Object ID of the target Application/App Registration that will be managed by the Azure App Registration and Enterprise Application Orchestrator extension.", + "SupportedOperations": { + "Add": true, + "Remove": true, + "Enrollment": false, + "Discovery": true, + "Inventory": true + }, + "Properties": [ + { + "Name": "ServerUsername", + "DisplayName": "Server Username", + "Type": "Secret", + "Description": "The Application ID of the Service Principal used to authenticate with Microsoft Graph for managing Application/App Registration certificates.", + "Required": true + }, + { + "Name": "ServerPassword", + "DisplayName": "Server Password", + "DependsOn": "ServerUsername", + "Type": "Secret", + "Description": "A Client Secret that the extension will use to authenticate with Microsoft Graph for managing Application/App Registration certificates. If Client Certificate Auth is used, you **must** select 'No Value'.", + "Required": false + }, + { + "Name": "ClientCertificate", + "DisplayName": "Client Certificate", + "DependsOn": "ServerUsername", + "Type": "Secret", + "Description": "The client certificate used to authenticate with Microsoft Graph for managing Application/App Registrations certificates. See the [requirements](#client-certificate-or-client-secret) for more information. If Client Certificate Auth is not used, you **must** check 'No Value'.", + "Required": false + }, + { + "Name": "ClientCertificatePassword", + "DisplayName": "Client Certificate Password", + "DependsOn": "ClientCertificate", + "Type": "Secret", + "Description": "The (optional) password that encrypts the private key in ClientCertificate. If Client Certificate Auth is not used, you **must** check 'No Value'.", + "Required": false + }, + { + "Name": "AzureCloud", + "DisplayName": "Azure Global Cloud Authority Host", + "Type": "MultipleChoice", + "DefaultValue": "public,china,germany,government", + "Description": "Specifies the Azure Cloud instance used by the organization.", + "Required": false + } + ], + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "PrivateKeyAllowed": "Forbidden", + "ServerRequired": true, + "PowerShell": false, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Required" + }, + { + "Name": "Azure Application Gateway Certificate", + "ShortName": "AzureAppGw", + "Capability": "AzureAppGw", + "LocalStore": false, + "ClientMachineDescription": "The Azure Tenant (directory) ID that owns the Service Principal.", + "StorePathDescription": "Azure resource ID of the application gateway, following the format: /subscriptions//resourceGroups//providers/Microsoft.Network/applicationGateways/.", + "SupportedOperations": { + "Add": true, + "Remove": true, + "Enrollment": false, + "Discovery": true, + "Inventory": true + }, + "Properties": [ + { + "Name": "ServerUsername", + "DisplayName": "Server Username", + "Type": "Secret", + "Description": "Application ID of the service principal, representing the identity used for managing the Application Gateway.", + "Required": false + }, + { + "Name": "ServerPassword", + "DisplayName": "Server Password", + "Type": "Secret", + "Description": "A Client Secret that the extension will use to authenticate with the Azure Resource Management API for managing Application Gateway certificates, OR the password that encrypts the private key in ClientCertificate", + "Required": false + }, + { + "Name": "ClientCertificate", + "DisplayName": "Client Certificate", + "Type": "Secret", + "Description": "The client certificate used to authenticate with Azure Resource Management API for managing Application Gateway certificates. See the [requirements](#client-certificate-or-client-secret) for more information.", + "Required": false + }, + { + "Name": "AzureCloud", + "DisplayName": "Azure Global Cloud Authority Host", + "Type": "MultipleChoice", + "DefaultValue": "public,china,germany,government", + "Description": "Specifies the Azure Cloud instance used by the organization.", + "Required": false + }, + { + "Name": "ServerUseSsl", + "DisplayName": "Use SSL", + "Type": "Bool", + "DefaultValue": "true", + "Description": "Specifies whether SSL should be used for communication with the server. Set to 'true' to enable SSL, and 'false' to disable it.", + "Required": true + } + ], + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "PrivateKeyAllowed": "Required", + "ServerRequired": true, + "PowerShell": false, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Required" + }, + { + "Name": "Azure Enterprise Application (Service Principal)", + "ShortName": "AzureSP", + "Capability": "AzureSP", + "LocalStore": false, + "ClientMachineDescription": "The Azure Tenant (directory) ID that owns the Service Principal.", + "StorePathDescription": "The Application ID of the target Application/Service Principal that will be managed by the Azure App Registration and Enterprise Application Orchestrator extension.", + "SupportedOperations": { + "Add": true, + "Remove": true, + "Enrollment": false, + "Discovery": true, + "Inventory": true + }, + "Properties": [ + { + "Name": "ServerUsername", + "DisplayName": "Server Username", + "Type": "Secret", + "Description": "The Application ID of the Service Principal used to authenticate with Microsoft Graph for managing Application/Service Principal certificates.", + "Required": true + }, + { + "Name": "ServerPassword", + "DisplayName": "Server Password", + "Type": "Secret", + "Description": "A Client Secret that the extension will use to authenticate with Microsoft Graph for managing Application/Service Principal certificates, OR the password that encrypts the private key in ClientCertificate. If Client Cert Auth is used _and_ the Client Certificate's private key is not encrypted, you **must** select 'No Value' for this field.", + "Required": false + }, + { + "Name": "ClientCertificate", + "DisplayName": "Client Certificate", + "Type": "Secret", + "Description": "The client certificate used to authenticate with Microsoft Graph for managing Application/Service Principal certificates. See the [requirements](#client-certificate-or-client-secret) for more information. If Client Certificate Auth is not used, you **must** select 'No Value' for this field.", + "Required": false + }, + { + "Name": "AzureCloud", + "DisplayName": "Azure Global Cloud Authority Host", + "Type": "MultipleChoice", + "DefaultValue": "public,china,germany,government", + "Description": "Specifies the Azure Cloud instance used by the organization.", + "Required": false + }, + { + "Name": "ServerUseSsl", + "DisplayName": "Use SSL", + "Type": "Bool", + "DefaultValue": "true", + "Description": "Specifies whether SSL should be used for communication with the server. Set to 'true' to enable SSL, and 'false' to disable it.", + "Required": true + } + ], + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "PrivateKeyAllowed": "Required", + "ServerRequired": true, + "PowerShell": false, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Required" + }, + { + "Name": "Azure Enterprise Application 2 (Service Principal)", + "ShortName": "AzureSP2", + "Capability": "AzureSP2", + "LocalStore": false, + "ClientMachineDescription": "The Azure Tenant (directory) ID where the Service Principal is instantiated", + "StorePathDescription": "The Object ID of the target Service Principal/Enterprise Application that will be managed by the Azure App Registration and Enterprise Application Orchestrator extension.", + "SupportedOperations": { + "Add": true, + "Remove": true, + "Enrollment": false, + "Discovery": true, + "Inventory": true + }, + "Properties": [ + { + "Name": "ServerUsername", + "DisplayName": "Server Username", + "Type": "Secret", + "Description": "The Application ID of the Service Principal used to authenticate with Microsoft Graph for managing Service Principal/Enterprise Application certificates.", + "Required": true + }, + { + "Name": "ServerPassword", + "DisplayName": "Server Password", + "DependsOn": "ServerUsername", + "Type": "Secret", + "Description": "A Client Secret that the extension will use to authenticate with Microsoft Graph for managing Service Principal/Enterprise Application certificates. If Client Certificate Auth is used, you **must** check 'No Value'.", + "Required": false + }, + { + "Name": "ClientCertificate", + "DisplayName": "Client Certificate", + "DependsOn": "ServerUsername", + "Type": "Secret", + "Description": "The client certificate used to authenticate with Microsoft Graph for managing Service Principal/Enterprise Application certificates. See the [requirements](#client-certificate-or-client-secret) for more information. If Client Certificate Auth is not used, you **must** check 'No Value'.", + "Required": false + }, + { + "Name": "ClientCertificatePassword", + "DisplayName": "Client Certificate Password", + "DependsOn": "ClientCertificate", + "Type": "Secret", + "Description": "The (optional) password that encrypts the private key in ClientCertificate. If Client Certificate Auth is not used or the certificate's private key is not encrypted, you **must** check 'No Value'.", + "Required": false + }, + { + "Name": "AzureCloud", + "DisplayName": "Azure Global Cloud Authority Host", + "Type": "MultipleChoice", + "DefaultValue": "public,china,germany,government", + "Description": "Specifies the Azure Cloud instance used by the organization.", + "Required": false + } + ], + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "PrivateKeyAllowed": "Required", + "ServerRequired": true, + "PowerShell": false, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Required" + }, + { + "Name": "Bosch IP Camera", + "ShortName": "BIPCamera", + "Capability": "BIPCamera", + "LocalStore": false, + "SupportedOperations": { + "Add": true, + "Create": false, + "Discovery": false, + "Enrollment": true, + "Remove": true + }, + "Properties": [ + { + "Name": "ServerUsername", + "DisplayName": "Server Username", + "Type": "Secret", + "DependsOn": null, + "DefaultValue": null, + "Required": false + }, + { + "Name": "ServerPassword", + "DisplayName": "Server Password", + "Type": "Secret", + "DependsOn": null, + "DefaultValue": null, + "Required": false + }, + { + "Name": "ServerUseSsl", + "DisplayName": "Use SSL", + "Type": "Bool", + "DependsOn": null, + "DefaultValue": "true", + "Required": true + } + ], + "EntryParameters": [ + { + "Name": "CertificateUsage", + "DisplayName": "Certificate Usage", + "Type": "MultipleChoice", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": false + }, + "Options": ",HTTPS,EAP-TLS-client,TLS-DATE-client" + }, + { + "Name": "Name", + "DisplayName": "Name (Alias)", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + } + }, + { + "Name": "Overwrite", + "DisplayName": "Overwrite", + "Type": "Bool", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": false + }, + "DefaultValue": "false" + } + ], + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "PrivateKeyAllowed": "Optional", + "ServerRequired": true, + "PowerShell": false, + "BlueprintAllowed": true, + "CustomAliasAllowed": "Required" + }, + { + "Name": "CiscoAsa", + "ShortName": "CiscoAsa", + "Capability": "CiscoAsa", + "LocalStore": false, + "SupportedOperations": { + "Add": true, + "Create": false, + "Discovery": false, + "Enrollment": false, + "Remove": true + }, + "Properties": [ + { + "Name": "CommitToDisk", + "DisplayName": "Commit To Disk", + "Type": "Bool", + "DependsOn": null, + "DefaultValue": "false", + "Required": false + }, + { + "Name": "ServerUsername", + "DisplayName": "Server Username", + "Type": "Secret", + "DependsOn": null, + "DefaultValue": null, + "Required": false + }, + { + "Name": "ServerPassword", + "DisplayName": "Server Password", + "Type": "Secret", + "DependsOn": null, + "DefaultValue": null, + "Required": false + }, + { + "Name": "ServerUseSsl", + "DisplayName": "Use SSL", + "Type": "Bool", + "DependsOn": null, + "DefaultValue": "true", + "Required": true + } + ], + "EntryParameters": [ + { + "Name": "interfaces", + "DisplayName": "Interfaces Comma Separated", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": false + } + } + ], + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "PrivateKeyAllowed": "Required", + "ServerRequired": true, + "PowerShell": false, + "BlueprintAllowed": true, + "CustomAliasAllowed": "Required" + }, + { + "Name": "CitrixAdc", + "ShortName": "CitrixAdc", + "Capability": "CitrixAdc", + "LocalStore": false, + "SupportedOperations": { + "Add": true, + "Create": false, + "Discovery": false, + "Enrollment": false, + "Remove": true + }, + "Properties": [ + { + "Name": "ServerUsername", + "DisplayName": "Server Username", + "Type": "Secret", + "DependsOn": null, + "DefaultValue": null, + "Required": false + }, + { + "Name": "ServerPassword", + "DisplayName": "Server Password", + "Type": "Secret", + "DependsOn": null, + "DefaultValue": null, + "Required": false + }, + { + "Name": "ServerUseSsl", + "DisplayName": "Use SSL", + "Type": "Bool", + "DependsOn": null, + "DefaultValue": "true", + "Required": true + }, + { + "Name": "linkToIssuer", + "DisplayName": "Link To Issuer", + "Type": "Bool", + "DependsOn": null, + "DefaultValue": "false", + "Required": false + } + ], + "EntryParameters": [ + { + "Name": "virtualServerName", + "DisplayName": "Virtual Server Name", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": false + } + }, + { + "Name": "sniCert", + "DisplayName": "SNI Cert", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": true, + "OnRemove": false, + "OnReenrollment": false + }, + "DefaultValue": "FALSE" + } + ], + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "PrivateKeyAllowed": "Required", + "ServerRequired": true, + "PowerShell": false, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Required", + "InventoryEndpoint": "/AnyInventory/Update" + }, + { + "Name": "F5 Big IQ", + "ShortName": "F5-BigIQ", + "Capability": "F5-BigIQ", + "LocalStore": false, + "SupportedOperations": { + "Add": true, + "Create": false, + "Discovery": false, + "Enrollment": true, + "Remove": true + }, + "Properties": [ + { + "Name": "DeployCertificateOnRenewal", + "DisplayName": "Deploy Certificate to Linked Big IP on Renewal", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "false", + "Required": false + }, + { + "Name": "IgnoreSSLWarning", + "DisplayName": "Ignore SSL Warning", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "false", + "Required": false + }, + { + "Name": "UseTokenAuth", + "DisplayName": "Use Token Authentication", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "false", + "Required": false + }, + { + "Name": "LoginProviderName", + "DisplayName": "Authentication Provider Name", + "Type": "String", + "DependsOn": "UseTokenAuth", + "DefaultValue": "", + "Required": false + } + ], + "EntryParameters": [ + { + "Name": "Alias", + "DisplayName": "Alias (Reenrollment only)", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + }, + "DependsOn": "", + "DefaultValue": "", + "Options": "" + }, + { + "Name": "Overwrite", + "DisplayName": "Overwrite (Reenrollment only)", + "Type": "Bool", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + }, + "DependsOn": "", + "DefaultValue": "False", + "Options": "" + }, + { + "Name": "SANs", + "DisplayName": "SANs (Reenrollment only)", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": false + }, + "DependsOn": "", + "DefaultValue": "", + "Options": "" + } + ], + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "PrivateKeyAllowed": "Required", + "ServerRequired": true, + "PowerShell": false, + "BlueprintAllowed": true, + "CustomAliasAllowed": "Required" + }, + { + "Name": "F5 CA Profiles REST", + "ShortName": "F5-CA-REST", + "Capability": "F5-CA-REST", + "SupportedOperations": { + "Add": true, + "Create": false, + "Discovery": true, + "Enrollment": false, + "Remove": true + }, + "Properties": [ + { + "Name": "PrimaryNode", + "DisplayName": "Primary Node", + "Type": "String", + "DependsOn": "PrimaryNodeOnlineRequired", + "DefaultValue": "", + "Required": true + }, + { + "Name": "PrimaryNodeCheckRetryWaitSecs", + "DisplayName": "Primary Node Check Retry Wait Seconds", + "Type": "String", + "DependsOn": "PrimaryNodeOnlineRequired", + "DefaultValue": "120", + "Required": true + }, + { + "Name": "PrimaryNodeCheckRetryMax", + "DisplayName": "Primary Node Check Retry Maximum", + "Type": "String", + "DependsOn": "PrimaryNodeOnlineRequired", + "DefaultValue": "3", + "Required": true + }, + { + "Name": "F5Version", + "DisplayName": "Version of F5", + "Type": "MultipleChoice", + "DependsOn": "", + "DefaultValue": "v12,v13,v14,v15", + "Required": true + }, + { + "Name": "ServerUsername", + "DisplayName": "Server Username", + "Type": "Secret", + "DependsOn": "", + "DefaultValue": null, + "Required": false + }, + { + "Name": "ServerPassword", + "DisplayName": "Server Password", + "Type": "Secret", + "DependsOn": "", + "DefaultValue": null, + "Required": false + }, + { + "Name": "ServerUseSsl", + "DisplayName": "Use SSL", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "true", + "Required": true + }, + { + "Name": "PrimaryNodeOnlineRequired", + "DisplayName": "Primary Node Online Required", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "", + "Required": true + }, + { + "Name": "IgnoreSSLWarning", + "DisplayName": "Ignore SSL Warning", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "False", + "Required": true + }, + { + "Name": "UseTokenAuth", + "DisplayName": "Use Token Authentication", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "false", + "Required": true + } + ], + "EntryParameters": [], + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "PrivateKeyAllowed": "Forbidden", + "JobProperties": [], + "ServerRequired": true, + "PowerShell": false, + "BlueprintAllowed": true, + "CustomAliasAllowed": "Required" + }, + { + "Name": "F5 SSL Profiles REST", + "ShortName": "F5-SL-REST", + "Capability": "F5-SL-REST", + "SupportedOperations": { + "Add": true, + "Create": false, + "Discovery": true, + "Enrollment": false, + "Remove": true + }, + "Properties": [ + { + "Name": "PrimaryNode", + "DisplayName": "Primary Node", + "Type": "String", + "DependsOn": "PrimaryNodeOnlineRequired", + "DefaultValue": "", + "Required": true + }, + { + "Name": "PrimaryNodeCheckRetryWaitSecs", + "DisplayName": "Primary Node Check Retry Wait Seconds", + "Type": "String", + "DependsOn": "PrimaryNodeOnlineRequired", + "DefaultValue": "120", + "Required": true + }, + { + "Name": "PrimaryNodeCheckRetryMax", + "DisplayName": "Primary Node Check Retry Maximum", + "Type": "String", + "DependsOn": "PrimaryNodeOnlineRequired", + "DefaultValue": "3", + "Required": true + }, + { + "Name": "F5Version", + "DisplayName": "Version of F5", + "Type": "MultipleChoice", + "DependsOn": "", + "DefaultValue": "v12,v13,v14,v15", + "Required": true + }, + { + "Name": "ServerUsername", + "DisplayName": "Server Username", + "Type": "Secret", + "DependsOn": "", + "DefaultValue": null, + "Required": false + }, + { + "Name": "ServerPassword", + "DisplayName": "Server Password", + "Type": "Secret", + "DependsOn": "", + "DefaultValue": null, + "Required": false + }, + { + "Name": "ServerUseSsl", + "DisplayName": "Use SSL", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "true", + "Required": true + }, + { + "Name": "PrimaryNodeOnlineRequired", + "DisplayName": "Primary Node Online Required", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "", + "Required": true + }, + { + "Name": "IgnoreSSLWarning", + "DisplayName": "Ignore SSL Warning", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "False", + "Required": true + }, + { + "Name": "UseTokenAuth", + "DisplayName": "Use Token Authentication", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "false", + "Required": true + } + ], + "EntryParameters": [], + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": true, + "Style": "Default" + }, + "PrivateKeyAllowed": "Optional", + "JobProperties": [], + "ServerRequired": true, + "PowerShell": false, + "BlueprintAllowed": true, + "CustomAliasAllowed": "Required" + }, + { + "Name": "F5 WS Profiles REST", + "ShortName": "F5-WS-REST", + "Capability": "F5-WS-REST", + "SupportedOperations": { + "Add": true, + "Create": false, + "Discovery": false, + "Enrollment": false, + "Remove": false + }, + "Properties": [ + { + "Name": "PrimaryNode", + "DisplayName": "Primary Node", + "Type": "String", + "DependsOn": "PrimaryNodeOnlineRequired", + "DefaultValue": "", + "Required": true + }, + { + "Name": "PrimaryNodeCheckRetryWaitSecs", + "DisplayName": "Primary Node Check Retry Wait Seconds", + "Type": "String", + "DependsOn": "PrimaryNodeOnlineRequired", + "DefaultValue": "120", + "Required": true + }, + { + "Name": "PrimaryNodeCheckRetryMax", + "DisplayName": "Primary Node Check Retry Maximum", + "Type": "String", + "DependsOn": "PrimaryNodeOnlineRequired", + "DefaultValue": "3", + "Required": true + }, + { + "Name": "F5Version", + "DisplayName": "Version of F5", + "Type": "MultipleChoice", + "DependsOn": "", + "DefaultValue": "v12,v13,v14,v15", + "Required": true + }, + { + "Name": "ServerUsername", + "DisplayName": "Server Username", + "Type": "Secret", + "DependsOn": "", + "DefaultValue": null, + "Required": false + }, + { + "Name": "ServerPassword", + "DisplayName": "Server Password", + "Type": "Secret", + "DependsOn": "", + "DefaultValue": null, + "Required": false + }, + { + "Name": "ServerUseSsl", + "DisplayName": "Use SSL", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "true", + "Required": true + }, + { + "Name": "PrimaryNodeOnlineRequired", + "DisplayName": "Primary Node Online Required", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "", + "Required": true + }, + { + "Name": "IgnoreSSLWarning", + "DisplayName": "Ignore SSL Warning", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "False", + "Required": true + }, + { + "Name": "UseTokenAuth", + "DisplayName": "Use Token Authentication", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "false", + "Required": true + } + ], + "EntryParameters": [], + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "PrivateKeyAllowed": "Required", + "JobProperties": [], + "ServerRequired": true, + "PowerShell": false, + "BlueprintAllowed": true, + "CustomAliasAllowed": "Forbidden" + }, + { + "Name": "Fortigate", + "ShortName": "Fortigate", + "Capability": "Fortigate", + "ServerRequired": false, + "BlueprintAllowed": true, + "CustomAliasAllowed": "Required", + "PowerShell": false, + "PrivateKeyAllowed": "Required", + "SupportedOperations": { + "Add": true, + "Create": false, + "Discovery": true, + "Enrollment": false, + "Remove": true + }, + "PasswordOptions": { + "Style": "Default", + "EntrySupported": false, + "StoreRequired": true + }, + "Properties": [], + "EntryParameters": [] + }, + { + "Name": "GCP Load Balancer", + "ShortName": "GCPLoadBal", + "Capability": "GCPLoadBal", + "ServerRequired": false, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Optional", + "PowerShell": false, + "PrivateKeyAllowed": "Required", + "SupportedOperations": { + "Add": true, + "Create": false, + "Discovery": false, + "Enrollment": false, + "Remove": true + }, + "PasswordOptions": { + "Style": "Default", + "EntrySupported": false, + "StoreRequired": false + }, + "Properties": [ + { + "Name": "jsonKey", + "DisplayName": "Service Account Key", + "Required": true, + "DependsOn": "", + "Type": "Secret", + "DefaultValue": "" + } + ], + "EntryParameters": [] + }, + { + "Name": "GCP Certificate Manager", + "ShortName": "GcpCertMgr", + "Capability": "GcpCertMgr", + "ServerRequired": false, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Required", + "PowerShell": false, + "PrivateKeyAllowed": "Required", + "StorePathType": "", + "StorePathValue": "n/a", + "SupportedOperations": { + "Add": true, + "Create": true, + "Discovery": true, + "Enrollment": false, + "Remove": true + }, + "PasswordOptions": { + "Style": "Default", + "EntrySupported": false, + "StoreRequired": false + }, + "Properties": [ + { + "Name": "Location", + "DisplayName": "Location", + "Type": "String", + "DependsOn": "", + "DefaultValue": "global", + "Required": true + }, + { + "Name": "ServiceAccountKey", + "DisplayName": "Service Account Key File Path", + "Type": "String", + "DependsOn": "", + "DefaultValue": null, + "Required": false + } + ], + "EntryParameters": [] + }, + { + "Name": "Hashicorp Vault Key-Value", + "ShortName": "HCVKV", + "Capability": "HCVKV", + "LocalStore": false, + "SupportedOperations": { + "Add": true, + "Create": true, + "Discovery": true, + "Enrollment": false, + "Remove": true + }, + "Properties": [ + { + "Name": "MountPoint", + "DisplayName": "Mount Point", + "Type": "String", + "DependsOn": "", + "DefaultValue": null, + "Required": false + }, + { + "Name": "VaultToken", + "DisplayName": "Vault Token", + "Type": "String", + "DependsOn": "", + "DefaultValue": null, + "Required": false + }, + { + "Name": "VaultServerUrl", + "DisplayName": "Vault Server URL", + "Type": "String", + "DependsOn": "", + "DefaultValue": null, + "Required": false + }, + { + "Name": "SubfolderInventory", + "DisplayName": "Subfolder Inventory", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "false", + "Required": false + }, + { + "Name": "IncludeCertChain", + "DisplayName": "Include Cert Chain", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "true", + "Required": false + } + ], + "EntryParameters": null, + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "StorePathType": "", + "StorePathValue": "", + "PrivateKeyAllowed": "Optional", + "JobProperties": [], + "ServerRequired": false, + "PowerShell": false, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Optional" + }, + { + "Name": "Hashicorp Vault Key-Value JKS", + "ShortName": "HCVKVJKS", + "Capability": "HCVKVJKS", + "LocalStore": false, + "SupportedOperations": { + "Add": true, + "Create": true, + "Discovery": true, + "Enrollment": false, + "Remove": true + }, + "Properties": [ + { + "Name": "SubfolderInventory", + "DisplayName": "Sub-folder Inventory", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "false", + "Required": false + }, + { + "Name": "IncludeCertChain", + "DisplayName": "Include Certificate Chain", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "false", + "Required": false + }, + { + "Name": "MountPoint", + "DisplayName": "Mount Point", + "Type": "String", + "DependsOn": "", + "DefaultValue": null, + "Required": false + }, + { + "Name": "ServerUsername", + "DisplayName": "Server Username", + "Type": "Secret", + "DependsOn": "", + "DefaultValue": null, + "Required": false + }, + { + "Name": "ServerPassword", + "DisplayName": "Server Password", + "Type": "Secret", + "DependsOn": "", + "DefaultValue": null, + "Required": false + }, + { + "Name": "ServerUseSsl", + "DisplayName": "Use SSL", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "true", + "Required": true + } + ], + "EntryParameters": null, + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "StorePathType": "", + "StorePathValue": "", + "PrivateKeyAllowed": "Optional", + "JobProperties": [], + "ServerRequired": true, + "PowerShell": false, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Optional" + }, + { + "Name": "Hashicorp Vault Key-Value PKCS12", + "ShortName": "HCVKVP12", + "Capability": "HCVKVP12", + "LocalStore": false, + "SupportedOperations": { + "Add": true, + "Create": true, + "Discovery": true, + "Enrollment": false, + "Remove": true + }, + "Properties": [ + { + "Name": "SubfolderInventory", + "DisplayName": "Sub-folder Inventory", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "false", + "Required": false + }, + { + "Name": "MountPoint", + "DisplayName": "Mount Point", + "Type": "String", + "DependsOn": "", + "DefaultValue": null, + "Required": false + }, + { + "Name": "IncludeCertChain", + "DisplayName": "Include Certificate Chain", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "false", + "Required": false + }, + { + "Name": "ServerUsername", + "DisplayName": "Server Username", + "Type": "Secret", + "DependsOn": "", + "DefaultValue": null, + "Required": false + }, + { + "Name": "ServerPassword", + "DisplayName": "Server Password", + "Type": "Secret", + "DependsOn": "", + "DefaultValue": null, + "Required": false + }, + { + "Name": "ServerUseSsl", + "DisplayName": "Use SSL", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "true", + "Required": true + } + ], + "EntryParameters": null, + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "StorePathType": "", + "StorePathValue": "", + "PrivateKeyAllowed": "Optional", + "JobProperties": [], + "ServerRequired": true, + "PowerShell": false, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Optional" + }, + { + "Name": "Hashicorp Vault Key-Value PEM", + "ShortName": "HCVKVPEM", + "Capability": "HCVKVPEM", + "LocalStore": false, + "SupportedOperations": { + "Add": true, + "Create": true, + "Discovery": true, + "Enrollment": false, + "Remove": true + }, + "Properties": [ + { + "Name": "SubfolderInventory", + "DisplayName": "Subfolder Inventory", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "false", + "Required": false + }, + { + "Name": "IncludeCertChain", + "DisplayName": "Include Certificate Chain", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "false", + "Required": false + }, + { + "Name": "MountPoint", + "DisplayName": "Mount Point", + "Type": "String", + "DependsOn": "", + "DefaultValue": null, + "Required": false + }, + { + "Name": "ServerUsername", + "DisplayName": "Server Username", + "Type": "Secret", + "DependsOn": "", + "DefaultValue": null, + "Required": false + }, + { + "Name": "ServerPassword", + "DisplayName": "Server Password", + "Type": "Secret", + "DependsOn": "", + "DefaultValue": null, + "Required": false + }, + { + "Name": "ServerUseSsl", + "DisplayName": "Use SSL", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "true", + "Required": true + } + ], + "EntryParameters": null, + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "PrivateKeyAllowed": "Optional", + "JobProperties": [], + "ServerRequired": true, + "PowerShell": false, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Optional" + }, + { + "Name": "Hashicorp Vault Key-Value PFX", + "ShortName": "HCVKVPFX", + "Capability": "HCVKVPFX", + "LocalStore": false, + "SupportedOperations": { + "Add": true, + "Create": true, + "Discovery": true, + "Enrollment": false, + "Remove": true + }, + "Properties": [ + { + "Name": "SubfolderInventory", + "DisplayName": "Sub-folder Inventory", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "false", + "Required": false + }, + { + "Name": "IncludeCertChain", + "DisplayName": "Include Certificate Chain", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "false", + "Required": false + }, + { + "Name": "MountPoint", + "DisplayName": "Mount Point", + "Type": "String", + "DependsOn": "", + "DefaultValue": null, + "Required": false + }, + { + "Name": "ServerUsername", + "DisplayName": "Server Username", + "Type": "Secret", + "DependsOn": "", + "DefaultValue": null, + "Required": false + }, + { + "Name": "ServerPassword", + "DisplayName": "Server Password", + "Type": "Secret", + "DependsOn": "", + "DefaultValue": null, + "Required": false + }, + { + "Name": "ServerUseSsl", + "DisplayName": "Use SSL", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "true", + "Required": true + } + ], + "EntryParameters": null, + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "StorePathType": "", + "StorePathValue": "", + "PrivateKeyAllowed": "Optional", + "JobProperties": [], + "ServerRequired": true, + "PowerShell": false, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Optional" + }, + { + "Name": "Hashicorp Vault PKI", + "ShortName": "HCVPKI", + "Capability": "HCVPKI", + "LocalStore": false, + "SupportedOperations": { + "Add": false, + "Create": false, + "Discovery": false, + "Enrollment": false, + "Remove": false + }, + "Properties": [ + { + "Name": "MountPoint", + "DisplayName": "Mount Point", + "Type": "String", + "DependsOn": "", + "DefaultValue": null, + "Required": true + }, + { + "Name": "VaultToken", + "DisplayName": "VaultToken", + "Type": "String", + "DependsOn": "", + "DefaultValue": null, + "Required": true + }, + { + "Name": "VaultServerUrl", + "DisplayName": "Vault Server URL", + "Type": "String", + "DependsOn": "", + "DefaultValue": null, + "Required": false + } + ], + "EntryParameters": null, + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "PrivateKeyAllowed": "Optional", + "JobProperties": [], + "ServerRequired": false, + "PowerShell": false, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Optional" + }, + { + "Name": "IIS Bound Certificate", + "ShortName": "IISU", + "Capability": "IISU", + "LocalStore": false, + "SupportedOperations": { + "Add": true, + "Create": false, + "Discovery": false, + "Enrollment": true, + "Remove": true + }, + "Properties": [ + { + "Name": "spnwithport", + "DisplayName": "SPN With Port", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "false", + "Required": false + }, + { + "Name": "WinRm Protocol", + "DisplayName": "WinRm Protocol", + "Type": "MultipleChoice", + "DependsOn": "", + "DefaultValue": "https,http", + "Required": true + }, + { + "Name": "WinRm Port", + "DisplayName": "WinRm Port", + "Type": "String", + "DependsOn": "", + "DefaultValue": "5986", + "Required": true + }, + { + "Name": "ServerUsername", + "DisplayName": "Server Username", + "Type": "Secret", + "DependsOn": "", + "DefaultValue": null, + "Required": false + }, + { + "Name": "ServerPassword", + "DisplayName": "Server Password", + "Type": "Secret", + "DependsOn": "", + "DefaultValue": null, + "Required": false + }, + { + "Name": "ServerUseSsl", + "DisplayName": "Use SSL", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "true", + "Required": true + } + ], + "EntryParameters": [ + { + "Name": "Port", + "DisplayName": "Port", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": false + }, + "DependsOn": "", + "DefaultValue": "443", + "Options": "" + }, + { + "Name": "IPAddress", + "DisplayName": "IP Address", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": true, + "OnRemove": true, + "OnReenrollment": true + }, + "DependsOn": "", + "DefaultValue": "*", + "Options": "" + }, + { + "Name": "HostName", + "DisplayName": "Host Name", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": false + }, + "DependsOn": "", + "DefaultValue": "", + "Options": "" + }, + { + "Name": "SiteName", + "DisplayName": "IIS Site Name", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": true, + "OnRemove": true, + "OnReenrollment": true + }, + "DependsOn": "", + "DefaultValue": "Default Web Site", + "Options": "" + }, + { + "Name": "SniFlag", + "DisplayName": "SNI Support", + "Type": "MultipleChoice", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": false + }, + "DependsOn": "", + "DefaultValue": "0 - No SNI", + "Options": "0 - No SNI,1 - SNI Enabled,2 - Non SNI Binding,3 - SNI Binding" + }, + { + "Name": "Protocol", + "DisplayName": "Protocol", + "Type": "MultipleChoice", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": true, + "OnRemove": true, + "OnReenrollment": true + }, + "DependsOn": "", + "DefaultValue": "https", + "Options": "https,http" + }, + { + "Name": "ProviderName", + "DisplayName": "Crypto Provider Name", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": false + }, + "DependsOn": "", + "DefaultValue": "", + "Options": "" + }, + { + "Name": "SAN", + "DisplayName": "SAN", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + }, + "DependsOn": "", + "DefaultValue": "", + "Options": "" + } + ], + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "StorePathValue": "[\"My\",\"WebHosting\"]", + "PrivateKeyAllowed": "Required", + "ServerRequired": true, + "PowerShell": false, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Forbidden" + }, + { + "Name": "Imperva", + "ShortName": "Imperva", + "Capability": "Imperva", + "ServerRequired": false, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Required", + "PowerShell": false, + "PrivateKeyAllowed": "Required", + "SupportedOperations": { + "Add": true, + "Create": false, + "Discovery": false, + "Enrollment": false, + "Remove": true + }, + "PasswordOptions": { + "Style": "Default", + "EntrySupported": false, + "StoreRequired": true + }, + "Properties": [], + "EntryParameters": [] + }, + { + "Name": "K8SCert", + "ShortName": "K8SCert", + "Capability": "K8SCert", + "LocalStore": false, + "SupportedOperations": { + "Add": false, + "Create": false, + "Discovery": true, + "Enrollment": false, + "Remove": false + }, + "Properties": [ + { + "Name": "KubeNamespace", + "DisplayName": "KubeNamespace", + "Type": "String", + "DependsOn": "", + "DefaultValue": "default", + "Required": false + }, + { + "Name": "KubeSecretName", + "DisplayName": "KubeSecretName", + "Type": "String", + "DependsOn": "", + "DefaultValue": null, + "Required": false + }, + { + "Name": "KubeSecretType", + "DisplayName": "KubeSecretType", + "Type": "String", + "DependsOn": "", + "DefaultValue": "cert", + "Required": true + } + ], + "EntryParameters": null, + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "StorePathType": "", + "StorePathValue": "", + "PrivateKeyAllowed": "Forbidden", + "JobProperties": [], + "ServerRequired": true, + "PowerShell": false, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Forbidden" + }, + { + "Name": "K8SCluster", + "ShortName": "K8SCluster", + "Capability": "K8SCluster", + "LocalStore": false, + "SupportedOperations": { + "Add": true, + "Create": true, + "Discovery": false, + "Enrollment": false, + "Remove": true + }, + "Properties": [ + { + "Name": "SeparateChain", + "DisplayName": "Separate Certificate Chain", + "Type": "Bool", + "DefaultValue": "false", + "Required": false + }, + { + "Name": "IncludeCertChain", + "DisplayName": "Include Certificate Chain", + "Type": "Bool", + "DefaultValue": "true", + "Required": false + } + ], + "EntryParameters": null, + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "StorePathType": "", + "StorePathValue": "", + "PrivateKeyAllowed": "Optional", + "JobProperties": [], + "ServerRequired": true, + "PowerShell": false, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Required" + }, + { + "Name": "K8SJKS", + "ShortName": "K8SJKS", + "Capability": "K8SJKS", + "LocalStore": false, + "SupportedOperations": { + "Add": true, + "Create": true, + "Discovery": true, + "Enrollment": false, + "Remove": true + }, + "Properties": [ + { + "Name": "KubeNamespace", + "DisplayName": "KubeNamespace", + "Type": "String", + "DependsOn": "", + "DefaultValue": "default", + "Required": false + }, + { + "Name": "KubeSecretName", + "DisplayName": "KubeSecretName", + "Type": "String", + "DependsOn": "", + "DefaultValue": null, + "Required": false + }, + { + "Name": "KubeSecretType", + "DisplayName": "KubeSecretType", + "Type": "String", + "DependsOn": "", + "DefaultValue": "jks", + "Required": true + }, + { + "Name": "CertificateDataFieldName", + "DisplayName": "CertificateDataFieldName", + "Type": "String", + "DependsOn": "", + "DefaultValue": ".jks", + "Required": true + }, + { + "Name": "PasswordFieldName", + "DisplayName": "PasswordFieldName", + "Type": "String", + "DependsOn": "", + "DefaultValue": "password", + "Required": false + }, + { + "Name": "PasswordIsK8SSecret", + "DisplayName": "Password Is K8S Secret", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "false", + "Required": false + }, + { + "Name": "StorePasswordPath", + "DisplayName": "StorePasswordPath", + "Type": "String", + "DependsOn": "", + "DefaultValue": null, + "Required": false + } + ], + "EntryParameters": null, + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "StorePathType": "", + "StorePathValue": "", + "PrivateKeyAllowed": "Optional", + "JobProperties": [], + "ServerRequired": true, + "PowerShell": false, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Required" + }, + { + "Name": "K8SNS", + "ShortName": "K8SNS", + "Capability": "K8SNS", + "LocalStore": false, + "SupportedOperations": { + "Add": true, + "Create": true, + "Discovery": true, + "Enrollment": false, + "Remove": true + }, + "Properties": [ + { + "Name": "KubeNamespace", + "DisplayName": "Kube Namespace", + "Type": "String", + "DependsOn": "", + "DefaultValue": "default", + "Required": false + }, + { + "Name": "SeparateChain", + "DisplayName": "Separate Certificate Chain", + "Type": "Bool", + "DefaultValue": "false", + "Required": false + }, + { + "Name": "IncludeCertChain", + "DisplayName": "Include Certificate Chain", + "Type": "Bool", + "DefaultValue": "true", + "Required": false + } + ], + "EntryParameters": null, + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "StorePathType": "", + "StorePathValue": "", + "PrivateKeyAllowed": "Optional", + "JobProperties": [], + "ServerRequired": true, + "PowerShell": false, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Required" + }, + { + "Name": "K8SPKCS12", + "ShortName": "K8SPKCS12", + "Capability": "K8SPKCS12", + "LocalStore": false, + "SupportedOperations": { + "Add": true, + "Create": true, + "Discovery": true, + "Enrollment": false, + "Remove": true + }, + "Properties": [ + { + "Name": "KubeSecretType", + "DisplayName": "Kube Secret Type", + "Type": "String", + "DependsOn": "", + "DefaultValue": "pkcs12", + "Required": true + }, + { + "Name": "CertificateDataFieldName", + "DisplayName": "CertificateDataFieldName", + "Type": "String", + "DependsOn": "", + "DefaultValue": ".p12", + "Required": true + }, + { + "Name": "PasswordFieldName", + "DisplayName": "Password Field Name", + "Type": "String", + "DependsOn": "", + "DefaultValue": "password", + "Required": false + }, + { + "Name": "PasswordIsK8SSecret", + "DisplayName": "Password Is K8S Secret", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "false", + "Required": false + }, + { + "Name": "KubeNamespace", + "DisplayName": "Kube Namespace", + "Type": "String", + "DependsOn": "", + "DefaultValue": "default", + "Required": false + }, + { + "Name": "KubeSecretName", + "DisplayName": "Kube Secret Name", + "Type": "String", + "DependsOn": "", + "DefaultValue": null, + "Required": false + }, + { + "Name": "StorePasswordPath", + "DisplayName": "StorePasswordPath", + "Type": "String", + "DependsOn": "", + "DefaultValue": null, + "Required": false + } + ], + "EntryParameters": null, + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "StorePathType": "", + "StorePathValue": "", + "PrivateKeyAllowed": "Optional", + "JobProperties": [], + "ServerRequired": true, + "PowerShell": false, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Required" + }, + { + "Name": "K8SSecret", + "ShortName": "K8SSecret", + "Capability": "K8SSecret", + "LocalStore": false, + "SupportedOperations": { + "Add": true, + "Create": true, + "Discovery": true, + "Enrollment": false, + "Remove": true + }, + "Properties": [ + { + "Name": "KubeNamespace", + "DisplayName": "KubeNamespace", + "Type": "String", + "DependsOn": "", + "DefaultValue": null, + "Required": false + }, + { + "Name": "KubeSecretName", + "DisplayName": "KubeSecretName", + "Type": "String", + "DependsOn": "", + "DefaultValue": null, + "Required": false + }, + { + "Name": "KubeSecretType", + "DisplayName": "KubeSecretType", + "Type": "String", + "DependsOn": "", + "DefaultValue": "secret", + "Required": true + }, + { + "Name": "SeparateChain", + "DisplayName": "Separate Certificate Chain", + "Type": "Bool", + "DefaultValue": "false", + "Required": false + }, + { + "Name": "IncludeCertChain", + "DisplayName": "Include Certificate Chain", + "Type": "Bool", + "DefaultValue": "true", + "Required": false + } + ], + "EntryParameters": null, + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "StorePathType": "", + "StorePathValue": "", + "PrivateKeyAllowed": "Optional", + "JobProperties": [], + "ServerRequired": true, + "PowerShell": false, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Forbidden" + }, + { + "Name": "K8STLSSecr", + "ShortName": "K8STLSSecr", + "Capability": "K8STLSSecr", + "LocalStore": false, + "SupportedOperations": { + "Add": true, + "Create": true, + "Discovery": true, + "Enrollment": false, + "Remove": true + }, + "Properties": [ + { + "Name": "KubeNamespace", + "DisplayName": "KubeNamespace", + "Type": "String", + "DependsOn": "", + "DefaultValue": null, + "Required": false + }, + { + "Name": "KubeSecretName", + "DisplayName": "KubeSecretName", + "Type": "String", + "DependsOn": "", + "DefaultValue": null, + "Required": false + }, + { + "Name": "KubeSecretType", + "DisplayName": "KubeSecretType", + "Type": "String", + "DependsOn": "", + "DefaultValue": "tls_secret", + "Required": true + }, + { + "Name": "SeparateChain", + "DisplayName": "Separate Certificate Chain", + "Type": "Bool", + "DefaultValue": "false", + "Required": false + }, + { + "Name": "IncludeCertChain", + "DisplayName": "Include Certificate Chain", + "Type": "Bool", + "DefaultValue": "true", + "Required": false + } + ], + "EntryParameters": null, + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "StorePathType": "", + "StorePathValue": "", + "PrivateKeyAllowed": "Optional", + "JobProperties": [], + "ServerRequired": true, + "PowerShell": false, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Forbidden" + }, + { + "Name": "MyOrchestratorStoreType", + "ShortName": "MOST", + "Capability": "MOST", + "LocalStore": false, + "SupportedOperations": { + "Add": false, + "Create": false, + "Discovery": true, + "Enrollment": false, + "Remove": false + }, + "Properties": [ + { + "Name": "CustomField1", + "DisplayName": "CustomField1", + "Type": "String", + "DependsOn": "", + "DefaultValue": "default", + "Required": true + }, + { + "Name": "CustomField2", + "DisplayName": "CustomField2", + "Type": "String", + "DependsOn": "", + "DefaultValue": null, + "Required": true + } + ], + "EntryParameters": [], + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "StorePathType": "", + "StorePathValue": "", + "PrivateKeyAllowed": "Forbidden", + "JobProperties": [], + "ServerRequired": true, + "PowerShell": false, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Forbidden" + }, + { + "Name": "Nmap Orchestrator", + "ShortName": "Nmap", + "Capability": "Nmap", + "LocalStore": false, + "SupportedOperations": { + "Add": false, + "Create": false, + "Discovery": false, + "Enrollment": false, + "Inventory": true, + "Reenrollment": false, + "Remove": true + }, + "Properties": [], + "EntryParameters": [], + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "StorePathType": "Freeform", + "StorePathValue": "", + "PrivateKeyAllowed": "Forbidden", + "ServerRequired": false, + "PowerShell": false, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Optional" + }, + { + "Name": "PaloAlto", + "ShortName": "PaloAlto", + "Capability": "PaloAlto", + "LocalStore": false, + "SupportedOperations": { + "Add": true, + "Create": false, + "Discovery": false, + "Enrollment": false, + "Remove": true + }, + "Properties": [ + { + "Name": "ServerUsername", + "DisplayName": "Server Username", + "Type": "Secret", + "DependsOn": null, + "DefaultValue": null, + "Required": false + }, + { + "Name": "ServerPassword", + "DisplayName": "Server Password", + "Type": "Secret", + "DependsOn": null, + "DefaultValue": null, + "Required": false + }, + { + "Name": "ServerUseSsl", + "DisplayName": "Use SSL", + "Type": "Bool", + "DependsOn": null, + "DefaultValue": "true", + "Required": true + }, + { + "Name": "DeviceGroup", + "DisplayName": "Device Group", + "Type": "String", + "DependsOn": null, + "DefaultValue": null, + "Required": false + } + ], + "EntryParameters": [], + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "PrivateKeyAllowed": "Optional", + "JobProperties": [], + "ServerRequired": true, + "PowerShell": false, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Required" + }, + { + "Name": "RFDER", + "ShortName": "RFDER", + "Capability": "RFDER", + "ServerRequired": true, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Forbidden", + "PowerShell": false, + "PrivateKeyAllowed": "Optional", + "SupportedOperations": { + "Add": true, + "Create": true, + "Discovery": true, + "Enrollment": false, + "Remove": true + }, + "PasswordOptions": { + "Style": "Default", + "EntrySupported": false, + "StoreRequired": true, + "StorePassword": { + "Description": "Password used to secure the Certificate Store", + "IsPAMEligible": true + } + }, + "Properties": [ + { + "Name": "ServerUsername", + "DisplayName": "Server Username", + "Type": "Secret", + "DependsOn": "", + "DefaultValue": "", + "Required": false, + "IsPAMEligible": true, + "Description": "A username (or valid PAM key if the username is stored in a KF Command configured PAM integration). If acting as an *agent* using local file access, just check *No Value*" + }, + { + "Name": "ServerPassword", + "DisplayName": "Server Password", + "Type": "Secret", + "DependsOn": "", + "DefaultValue": "", + "Required": false, + "IsPAMEligible": true, + "Description": "A password (or valid PAM key if the password is stored in a KF Command configured PAM integration). The password can also be an SSH private key if connecting via SSH to a server using SSH private key authentication. If acting as an *agent* using local file access, just check *No Value*" + }, + { + "Name": "LinuxFilePermissionsOnStoreCreation", + "DisplayName": "Linux File Permissions on Store Creation", + "Required": false, + "DependsOn": "", + "Type": "String", + "DefaultValue": "", + "Description": "The LinuxFilePermissionsOnStoreCreation field should contain a three-digit value between 000 and 777 representing the Linux file permissions to be set for the certificate store upon creation. Example: '600' or '755'." + }, + { + "Name": "LinuxFileOwnerOnStoreCreation", + "DisplayName": "Linux File Owner on Store Creation", + "Required": false, + "DependsOn": "", + "Type": "String", + "DefaultValue": "", + "Description": "The LinuxFileOwnerOnStoreCreation field should contain a valid user ID recognized by the destination Linux server, optionally followed by a colon and a group ID if the group owner differs. Example: 'userID' or 'userID:groupID'." + }, + { + "Name": "SudoImpersonatingUser", + "DisplayName": "Sudo Impersonating User", + "Required": false, + "DependsOn": "", + "Type": "String", + "DefaultValue": "", + "Description": "The SudoImpersonatingUser field should contain a valid user ID to impersonate using sudo on the destination Linux server. Example: 'impersonatedUserID'." + }, + { + "Name": "SeparatePrivateKeyFilePath", + "DisplayName": "Separate Private Key File Location", + "Required": false, + "DependsOn": "", + "Type": "String", + "DefaultValue": "", + "Description": "The SeparatePrivateKeyFilePath field should contain the full path and file name where the separate private key file will be stored if it is to be kept outside the main certificate file. Example: '/path/to/privatekey.der'." + } + ], + "EntryParameters": [], + "ClientMachineDescription": "The Client Machine field should contain the DNS name or IP address of the remote orchestrated server for Linux orchestrated servers, formatted as a URL (protocol://dns-or-ip:port) for Windows orchestrated servers, or '1.1.1.1|LocalMachine' for local agents. Example: 'https://myserver.mydomain.com:5986' or '1.1.1.1|LocalMachine' for local access.", + "StorePathDescription": "The Store Path field should contain the full path and file name, including file extension if applicable, beginning with a forward slash (/) for Linux orchestrated servers or a drive letter (i.e., c:\\folder\\path\\storename.der) for Windows orchestrated servers. Example: '/folder/path/storename.der' or 'c:\\folder\\path\\storename.der'." + }, + { + "Name": "RFJKS", + "ShortName": "RFJKS", + "Capability": "RFJKS", + "ServerRequired": true, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Required", + "PowerShell": false, + "PrivateKeyAllowed": "Optional", + "SupportedOperations": { + "Add": true, + "Create": true, + "Discovery": true, + "Enrollment": false, + "Remove": true + }, + "PasswordOptions": { + "Style": "Default", + "EntrySupported": false, + "StoreRequired": true, + "StorePassword": { + "Description": "Password used to secure the Certificate Store", + "IsPAMEligible": true + } + }, + "Properties": [ + { + "Name": "ServerUsername", + "DisplayName": "Server Username", + "Type": "Secret", + "DependsOn": "", + "DefaultValue": "", + "Required": false, + "IsPAMEligible": true, + "Description": "A username (or valid PAM key if the username is stored in a KF Command configured PAM integration). If acting as an *agent* using local file access, just check *No Value*" + }, + { + "Name": "ServerPassword", + "DisplayName": "Server Password", + "Type": "Secret", + "DependsOn": "", + "DefaultValue": "", + "Required": false, + "IsPAMEligible": true, + "Description": "A password (or valid PAM key if the password is stored in a KF Command configured PAM integration). The password can also be an SSH private key if connecting via SSH to a server using SSH private key authentication. If acting as an *agent* using local file access, just check *No Value*" + }, + { + "Name": "LinuxFilePermissionsOnStoreCreation", + "DisplayName": "Linux File Permissions on Store Creation", + "Required": false, + "DependsOn": "", + "Type": "String", + "DefaultValue": "", + "Description": "The LinuxFilePermissionsOnStoreCreation field should contain a three-digit value between 000 and 777 representing the Linux file permissions to be set for the certificate store upon creation. Example: '600' or '755'." + }, + { + "Name": "LinuxFileOwnerOnStoreCreation", + "DisplayName": "Linux File Owner on Store Creation", + "Required": false, + "DependsOn": "", + "Type": "String", + "DefaultValue": "", + "Description": "The LinuxFileOwnerOnStoreCreation field should contain a valid user ID recognized by the destination Linux server, optionally followed by a colon and a group ID if the group owner differs. Example: 'userID' or 'userID:groupID'." + }, + { + "Name": "SudoImpersonatingUser", + "DisplayName": "Sudo Impersonating User", + "Required": false, + "DependsOn": "", + "Type": "String", + "DefaultValue": "", + "Description": "The SudoImpersonatingUser field should contain a valid user ID to impersonate using sudo on the destination Linux server. Example: 'impersonatedUserID'." + } + ], + "EntryParameters": [], + "ClientMachineDescription": "The IP address or DNS of the server hosting the certificate store. For more information, see [Client Machine ](#client-machine-instructions)", + "StorePathDescription": "The full path and file name, including file extension if one exists where the certificate store file is located. For Linux orchestrated servers, StorePath will begin with a forward slash (i.e. /folder/path/storename.ext). For Windows orchestrated servers, it should begin with a drive letter (i.e. c:\\folder\\path\\storename.ext)." + }, + { + "Name": "RFKDB", + "ShortName": "RFKDB", + "Capability": "RFKDB", + "ServerRequired": true, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Required", + "PowerShell": false, + "PrivateKeyAllowed": "Optional", + "SupportedOperations": { + "Add": true, + "Create": true, + "Discovery": true, + "Enrollment": false, + "Remove": true + }, + "PasswordOptions": { + "Style": "Default", + "EntrySupported": false, + "StoreRequired": true, + "StorePassword": { + "Description": "Password used to secure the Certificate Store", + "IsPAMEligible": true + } + }, + "Properties": [ + { + "Name": "ServerUsername", + "DisplayName": "Server Username", + "Type": "Secret", + "DependsOn": "", + "DefaultValue": "", + "Required": false, + "IsPAMEligible": true, + "Description": "A username (or valid PAM key if the username is stored in a KF Command configured PAM integration). If acting as an *agent* using local file access, just check *No Value*" + }, + { + "Name": "ServerPassword", + "DisplayName": "Server Password", + "Type": "Secret", + "DependsOn": "", + "DefaultValue": "", + "Required": false, + "IsPAMEligible": true, + "Description": "A password (or valid PAM key if the password is stored in a KF Command configured PAM integration). The password can also be an SSH private key if connecting via SSH to a server using SSH private key authentication. If acting as an *agent* using local file access, just check *No Value*" + }, + { + "Name": "LinuxFilePermissionsOnStoreCreation", + "DisplayName": "Linux File Permissions on Store Creation", + "Required": false, + "DependsOn": "", + "Type": "String", + "DefaultValue": "", + "Description": "The LinuxFilePermissionsOnStoreCreation field should contain a three-digit value between 000 and 777 representing the Linux file permissions to be set for the certificate store upon creation. Example: '600' or '755'." + }, + { + "Name": "LinuxFileOwnerOnStoreCreation", + "DisplayName": "Linux File Owner on Store Creation", + "Required": false, + "DependsOn": "", + "Type": "String", + "DefaultValue": "", + "Description": "The LinuxFileOwnerOnStoreCreation field should contain a valid user ID recognized by the destination Linux server, optionally followed by a colon and a group ID if the group owner differs. Example: 'userID' or 'userID:groupID'." + }, + { + "Name": "SudoImpersonatingUser", + "DisplayName": "Sudo Impersonating User", + "Required": false, + "DependsOn": "", + "Type": "String", + "DefaultValue": "", + "Description": "The SudoImpersonatingUser field should contain a valid user ID to impersonate using sudo on the destination Linux server. Example: 'impersonatedUserID'." + } + ], + "EntryParameters": [], + "ClientMachineDescription": "The Client Machine field should contain the DNS name or IP address of the remote orchestrated server for Linux orchestrated servers, formatted as a URL (protocol://dns-or-ip:port) for Windows orchestrated servers, or '1.1.1.1|LocalMachine' for local agents. Example: 'https://myserver.mydomain.com:5986' or '1.1.1.1|LocalMachine' for local access.", + "StorePathDescription": "The Store Path field should contain the full path and file name, including file extension if applicable, beginning with a forward slash (/) for Linux orchestrated servers or a drive letter (i.e., c:\\folder\\path\\storename.kdb) for Windows orchestrated servers. Example: '/folder/path/storename.kdb' or 'c:\\folder\\path\\storename.kdb'." + }, + { + "Name": "RFORA", + "ShortName": "RFORA", + "Capability": "RFORA", + "ServerRequired": true, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Required", + "PowerShell": false, + "PrivateKeyAllowed": "Optional", + "SupportedOperations": { + "Add": true, + "Create": true, + "Discovery": true, + "Enrollment": false, + "Remove": true + }, + "PasswordOptions": { + "Style": "Default", + "EntrySupported": false, + "StoreRequired": true, + "StorePassword": { + "Description": "Password used to secure the Certificate Store", + "IsPAMEligible": true + } + }, + "Properties": [ + { + "Name": "ServerUsername", + "DisplayName": "Server Username", + "Type": "Secret", + "DependsOn": "", + "DefaultValue": "", + "Required": false, + "IsPAMEligible": true, + "Description": "A username (or valid PAM key if the username is stored in a KF Command configured PAM integration). If acting as an *agent* using local file access, just check *No Value*" + }, + { + "Name": "ServerPassword", + "DisplayName": "Server Password", + "Type": "Secret", + "DependsOn": "", + "DefaultValue": "", + "Required": false, + "IsPAMEligible": true, + "Description": "A password (or valid PAM key if the password is stored in a KF Command configured PAM integration). The password can also be an SSH private key if connecting via SSH to a server using SSH private key authentication. If acting as an *agent* using local file access, just check *No Value*" + }, + { + "Name": "LinuxFilePermissionsOnStoreCreation", + "DisplayName": "Linux File Permissions on Store Creation", + "Required": false, + "DependsOn": "", + "Type": "String", + "DefaultValue": "", + "Description": "The LinuxFilePermissionsOnStoreCreation field should contain a three-digit value between 000 and 777 representing the Linux file permissions to be set for the certificate store upon creation. Example: '600' or '755'." + }, + { + "Name": "LinuxFileOwnerOnStoreCreation", + "DisplayName": "Linux File Owner on Store Creation", + "Required": false, + "DependsOn": "", + "Type": "String", + "DefaultValue": "", + "Description": "The LinuxFileOwnerOnStoreCreation field should contain a valid user ID recognized by the destination Linux server, optionally followed by a colon and a group ID if the group owner differs. Example: 'userID' or 'userID:groupID'." + }, + { + "Name": "SudoImpersonatingUser", + "DisplayName": "Sudo Impersonating User", + "Required": false, + "DependsOn": "", + "Type": "String", + "DefaultValue": "", + "Description": "The SudoImpersonatingUser field should contain a valid user ID to impersonate using sudo on the destination Linux server. Example: 'impersonatedUserID'." + }, + { + "Name": "WorkFolder", + "DisplayName": "Location to use for creation/removal of work files", + "Required": true, + "DependsOn": "", + "Type": "String", + "DefaultValue": "", + "Description": "The WorkFolder field should contain the path on the managed server where temporary work files can be created, modified, and deleted during Inventory and Management jobs. Example: '/path/to/workfolder'." + } + ], + "EntryParameters": [], + "ClientMachineDescription": "The Client Machine field should contain the DNS name or IP address of the remote orchestrated server for Linux orchestrated servers, formatted as a URL (protocol://dns-or-ip:port) for Windows orchestrated servers, or '1.1.1.1|LocalMachine' for local agents. Example: 'https://myserver.mydomain.com:5986' or '1.1.1.1|LocalMachine' for local access.", + "StorePathDescription": "The Store Path field should contain the full path and file name of the Oracle Wallet, including the 'eWallet.p12' file name by convention. Example: '/path/to/eWallet.p12' or 'c:\\path\\to\\eWallet.p12'." + }, + { + "Name": "RFPEM", + "ShortName": "RFPEM", + "Capability": "RFPEM", + "ServerRequired": true, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Forbidden", + "PowerShell": false, + "PrivateKeyAllowed": "Optional", + "SupportedOperations": { + "Add": true, + "Create": true, + "Discovery": true, + "Enrollment": false, + "Remove": true + }, + "PasswordOptions": { + "Style": "Default", + "EntrySupported": false, + "StoreRequired": true, + "StorePassword": { + "Description": "Password used to secure the Certificate Store. For stores with PKCS#8 private keys, set the password for encrypted private keys (BEGIN ENCRYPTED PRIVATE KEY) or 'No Value' for unencrypted private keys (BEGIN PRIVATE KEY). If managing a store with a PKCS#1 private key (BEGIN RSA PRIVATE KEY), this value MUST be set to 'No Value'", + "IsPAMEligible": true + } + }, + "Properties": [ + { + "Name": "ServerUsername", + "DisplayName": "Server Username", + "Type": "Secret", + "DependsOn": "", + "DefaultValue": "", + "Required": false, + "IsPAMEligible": true, + "Description": "A username (or valid PAM key if the username is stored in a KF Command configured PAM integration). If acting as an *agent* using local file access, just check *No Value*" + }, + { + "Name": "ServerPassword", + "DisplayName": "Server Password", + "Type": "Secret", + "DependsOn": "", + "DefaultValue": "", + "Required": false, + "IsPAMEligible": true, + "Description": "A password (or valid PAM key if the password is stored in a KF Command configured PAM integration). The password can also be an SSH private key if connecting via SSH to a server using SSH private key authentication. If acting as an *agent* using local file access, just check *No Value*" + }, + { + "Name": "LinuxFilePermissionsOnStoreCreation", + "DisplayName": "Linux File Permissions on Store Creation", + "Required": false, + "DependsOn": "", + "Type": "String", + "DefaultValue": "", + "Description": "The LinuxFilePermissionsOnStoreCreation field should contain a three-digit value between 000 and 777 representing the Linux file permissions to be set for the certificate store upon creation. Example: '600' or '755'." + }, + { + "Name": "LinuxFileOwnerOnStoreCreation", + "DisplayName": "Linux File Owner on Store Creation", + "Required": false, + "DependsOn": "", + "Type": "String", + "DefaultValue": "", + "Description": "The LinuxFileOwnerOnStoreCreation field should contain a valid user ID recognized by the destination Linux server, optionally followed by a colon and a group ID if the group owner differs. Example: 'userID' or 'userID:groupID'." + }, + { + "Name": "SudoImpersonatingUser", + "DisplayName": "Sudo Impersonating User", + "Required": false, + "DependsOn": "", + "Type": "String", + "DefaultValue": "", + "Description": "The SudoImpersonatingUser field should contain a valid user ID to impersonate using sudo on the destination Linux server. Example: 'impersonatedUserID'." + }, + { + "Name": "IsTrustStore", + "DisplayName": "Trust Store", + "Required": false, + "DependsOn": "", + "Type": "Bool", + "DefaultValue": "false", + "Description": "The IsTrustStore field should contain a boolean value ('true' or 'false') indicating whether the store will be identified as a trust store, which can hold multiple certificates without private keys. Example: 'true' for a trust store or 'false' for a store with a single certificate and private key." + }, + { + "Name": "IncludesChain", + "DisplayName": "Store Includes Chain", + "Required": false, + "DependsOn": "", + "Type": "Bool", + "DefaultValue": "false", + "Description": "The IncludesChain field should contain a boolean value ('true' or 'false') indicating whether the certificate store includes the full certificate chain along with the end entity certificate. Example: 'true' to include the full chain or 'false' to exclude it." + }, + { + "Name": "SeparatePrivateKeyFilePath", + "DisplayName": "Separate Private Key File Location", + "Required": false, + "DependsOn": "", + "Type": "String", + "DefaultValue": "", + "Description": "The SeparatePrivateKeyFilePath field should contain the full path and file name where the separate private key file will be stored if it is to be kept outside the main certificate file. Example: '/path/to/privatekey.pem'." + }, + { + "Name": "IgnorePrivateKeyOnInventory", + "DisplayName": "Ignore Private Key On Inventory", + "Required": false, + "DependsOn": "", + "Type": "Bool", + "DefaultValue": "false", + "Description": "The IgnorePrivateKeyOnInventory field should contain a boolean value ('true' or 'false') indicating whether to ignore the private key during inventory, which will make the store inventory-only and return all certificates without private key entries. Example: 'true' to ignore the private key or 'false' to include it." + } + ], + "EntryParameters": [], + "ClientMachineDescription": "The Client Machine field should contain the DNS name or IP address of the remote orchestrated server for Linux orchestrated servers, formatted as a URL (protocol://dns-or-ip:port) for Windows orchestrated servers, or '1.1.1.1|LocalMachine' for local agents. Example: 'https://myserver.mydomain.com:5986' or '1.1.1.1|LocalMachine' for local access.", + "StorePathDescription": "The Store Path field should contain the full path and file name, including file extension if applicable, beginning with a forward slash (/) for Linux orchestrated servers or a drive letter (i.e., c:\\folder\\path\\storename.ext) for Windows orchestrated servers. Example: '/folder/path/storename.pem' or 'c:\\folder\\path\\storename.pem'." + }, + { + "Name": "RFPkcs12", + "ShortName": "RFPkcs12", + "Capability": "RFPkcs12", + "ServerRequired": true, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Required", + "PowerShell": false, + "PrivateKeyAllowed": "Optional", + "SupportedOperations": { + "Add": true, + "Create": true, + "Discovery": true, + "Enrollment": false, + "Remove": true + }, + "PasswordOptions": { + "Style": "Default", + "EntrySupported": false, + "StoreRequired": true, + "StorePassword": { + "Description": "Password used to secure the Certificate Store", + "IsPAMEligible": true + } + }, + "Properties": [ + { + "Name": "ServerUsername", + "DisplayName": "Server Username", + "Type": "Secret", + "DependsOn": "", + "DefaultValue": "", + "Required": false, + "IsPAMEligible": true, + "Description": "A username (or valid PAM key if the username is stored in a KF Command configured PAM integration). If acting as an *agent* using local file access, just check *No Value*" + }, + { + "Name": "ServerPassword", + "DisplayName": "Server Password", + "Type": "Secret", + "DependsOn": "", + "DefaultValue": "", + "Required": false, + "IsPAMEligible": true, + "Description": "A password (or valid PAM key if the password is stored in a KF Command configured PAM integration). The password can also be an SSH private key if connecting via SSH to a server using SSH private key authentication. If acting as an *agent* using local file access, just check *No Value*" + }, + { + "Name": "LinuxFilePermissionsOnStoreCreation", + "DisplayName": "Linux File Permissions on Store Creation", + "Required": false, + "DependsOn": "", + "Type": "String", + "DefaultValue": "", + "Description": "The LinuxFilePermissionsOnStoreCreation field should contain a three-digit value between 000 and 777 representing the Linux file permissions to be set for the certificate store upon creation. Example: '600' or '755'." + }, + { + "Name": "LinuxFileOwnerOnStoreCreation", + "DisplayName": "Linux File Owner on Store Creation", + "Required": false, + "DependsOn": "", + "Type": "String", + "DefaultValue": "", + "Description": "The LinuxFileOwnerOnStoreCreation field should contain a valid user ID recognized by the destination Linux server, optionally followed by a colon and a group ID if the group owner differs. Example: 'userID' or 'userID:groupID'." + }, + { + "Name": "SudoImpersonatingUser", + "DisplayName": "Sudo Impersonating User", + "Required": false, + "DependsOn": "", + "Type": "String", + "DefaultValue": "", + "Description": "The SudoImpersonatingUser field should contain a valid user ID to impersonate using sudo on the destination Linux server. Example: 'impersonatedUserID'." + } + ], + "EntryParameters": [], + "ClientMachineDescription": "The Client Machine field should contain the DNS name or IP address of the remote orchestrated server for Linux orchestrated servers, formatted as a URL (protocol://dns-or-ip:port) for Windows orchestrated servers, or '1.1.1.1|LocalMachine' for local agents. Example: 'https://myserver.mydomain.com:5986' or '1.1.1.1|LocalMachine' for local access.", + "StorePathDescription": "The Store Path field should contain the full path and file name, including file extension if applicable, beginning with a forward slash (/) for Linux orchestrated servers or a drive letter (i.e., c:\\folder\\path\\storename.p12) for Windows orchestrated servers. Example: '/folder/path/storename.p12' or 'c:\\folder\\path\\storename.p12'." + }, + { + "Name": "SampleStoreType", + "ShortName": "SAMPLETYPE", + "Capability": "SAMPLETYPE", + "SupportedOperations": { + "Add": false, + "Create": false, + "Discovery": false, + "Enrollment": false, + "Remove": false + }, + "Properties": [], + "EntryParameters": [], + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "StorePathType": "", + "StorePathValue": "", + "PrivateKeyAllowed": "Forbidden", + "JobProperties": [], + "ServerRequired": false, + "PowerShell": false, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Forbidden" + }, + { + "Name": "Signum", + "ShortName": "Signum", + "Capability": "Signum", + "LocalStore": false, + "SupportedOperations": { + "Add": false, + "Create": false, + "Discovery": false, + "Enrollment": false, + "Remove": false + }, + "Properties": [], + "EntryParameters": [], + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "StorePathType": "", + "StorePathValue": "na", + "PrivateKeyAllowed": "Required", + "JobProperties": [], + "ServerRequired": true, + "PowerShell": false, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Required" + }, + { + "Name": "VMware-NSX", + "ShortName": "VMware-NSX", + "Capability": "VMware-NSX", + "LocalStore": false, + "SupportedOperations": { + "Add": true, + "Create": false, + "Discovery": false, + "Enrollment": false, + "Remove": true + }, + "Properties": [ + { + "Name": "ServerUsername", + "DisplayName": "Server Username", + "Type": "Secret", + "DependsOn": "", + "DefaultValue": null, + "Required": true + }, + { + "Name": "ServerPassword", + "DisplayName": "Server Password", + "Type": "Secret", + "DependsOn": "", + "DefaultValue": null, + "Required": true + }, + { + "Name": "ServerUseSsl", + "DisplayName": "Use SSL", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "true", + "Required": true + }, + { + "Name": "ApiVersion", + "DisplayName": "X-Avi-Version", + "Type": "String", + "DependsOn": "", + "DefaultValue": "20.1.1", + "Required": true + } + ], + "EntryParameters": null, + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "StorePathType": "MultipleChoice", + "StorePathValue": "[\"Application\",\"Controller\",\"CA\"]", + "PrivateKeyAllowed": "Optional", + "JobProperties": [], + "ServerRequired": true, + "PowerShell": false, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Required" + }, + { + "Name": "WinCerMgmt", + "ShortName": "WinCerMgmt", + "Capability": "WinCerMgmt", + "SupportedOperations": { + "Add": true, + "Create": false, + "Discovery": false, + "Enrollment": false, + "Remove": true + }, + "Properties": [ + { + "Name": "spnwithport", + "DisplayName": "spnwithport", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "false", + "Required": false + } + ], + "EntryParameters": [], + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "StorePathType": "", + "StorePathValue": "", + "PrivateKeyAllowed": "Optional", + "JobProperties": [], + "ServerRequired": true, + "PowerShell": false, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Forbidden" + }, + { + "Name": "Windows Certificate", + "ShortName": "WinCert", + "Capability": "WinCert", + "LocalStore": false, + "SupportedOperations": { + "Add": true, + "Create": false, + "Discovery": false, + "Enrollment": true, + "Remove": true + }, + "Properties": [ + { + "Name": "spnwithport", + "DisplayName": "SPN With Port", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "false", + "Required": false + }, + { + "Name": "WinRM Protocol", + "DisplayName": "WinRM Protocol", + "Type": "MultipleChoice", + "DependsOn": "", + "DefaultValue": "https,http", + "Required": true + }, + { + "Name": "WinRM Port", + "DisplayName": "WinRM Port", + "Type": "String", + "DependsOn": "", + "DefaultValue": "5986", + "Required": true + }, + { + "Name": "ServerUsername", + "DisplayName": "Server Username", + "Type": "Secret", + "DependsOn": "", + "DefaultValue": null, + "Required": false + }, + { + "Name": "ServerPassword", + "DisplayName": "Server Password", + "Type": "Secret", + "DependsOn": "", + "DefaultValue": null, + "Required": false + }, + { + "Name": "ServerUseSsl", + "DisplayName": "Use SSL", + "Type": "Bool", + "DependsOn": "", + "DefaultValue": "true", + "Required": true + } + ], + "EntryParameters": [ + { + "Name": "ProviderName", + "DisplayName": "Crypto Provider Name", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": false + }, + "DependsOn": "", + "DefaultValue": "", + "Options": "" + }, + { + "Name": "SAN", + "DisplayName": "SAN", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + }, + "DependsOn": "", + "DefaultValue": "", + "Options": "" + } + ], + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "StorePathValue": "", + "PrivateKeyAllowed": "Optional", + "ServerRequired": true, + "PowerShell": false, + "BlueprintAllowed": false, + "CustomAliasAllowed": "Forbidden" + }, + { + "Name": "WinSql", + "ShortName": "WinSql", + "Capability": "WinSql", + "LocalStore": false, + "SupportedOperations": { + "Add": true, + "Create": false, + "Discovery": false, + "Enrollment": false, + "Remove": true + }, + "Properties": [ + { + "Name": "WinRm Protocol", + "DisplayName": "WinRm Protocol", + "Type": "MultipleChoice", + "DependsOn": null, + "DefaultValue": "https,http", + "Required": true + }, + { + "Name": "WinRm Port", + "DisplayName": "WinRm Port", + "Type": "String", + "DependsOn": null, + "DefaultValue": "5986", + "Required": true + }, + { + "Name": "ServerUsername", + "DisplayName": "Server Username", + "Type": "Secret", + "DependsOn": null, + "DefaultValue": null, + "Required": false + }, + { + "Name": "ServerPassword", + "DisplayName": "Server Password", + "Type": "Secret", + "DependsOn": null, + "DefaultValue": null, + "Required": false + }, + { + "Name": "ServerUseSsl", + "DisplayName": "Use SSL", + "Type": "Bool", + "DependsOn": null, + "DefaultValue": "true", + "Required": true + }, + { + "Name": "RestartService", + "DisplayName": "Restart SQL Service After Cert Installed", + "Type": "Bool", + "DependsOn": null, + "DefaultValue": "false", + "Required": true + } + ], + "EntryParameters": [ + { + "Name": "InstanceName", + "DisplayName": "Instance Name", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": false + } + }, + { + "Name": "ProviderName", + "DisplayName": "Crypto Provider Name", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": false + }, + "DependsOn": "", + "DefaultValue": "", + "Options": "" + }, + { + "Name": "SAN", + "DisplayName": "SAN", + "Type": "String", + "RequiredWhen": { + "HasPrivateKey": false, + "OnAdd": false, + "OnRemove": false, + "OnReenrollment": true + }, + "DependsOn": "", + "DefaultValue": "", + "Options": "" + } + ], + "PasswordOptions": { + "EntrySupported": false, + "StoreRequired": false, + "Style": "Default" + }, + "StorePathValue": "My", + "PrivateKeyAllowed": "Optional", + "ServerRequired": true, + "PowerShell": false, + "BlueprintAllowed": true, + "CustomAliasAllowed": "Forbidden" + } +] \ No newline at end of file diff --git a/cmd/stores.go b/cmd/stores.go index bad34f7..0b2b195 100644 --- a/cmd/stores.go +++ b/cmd/stores.go @@ -18,10 +18,11 @@ import ( "encoding/csv" "encoding/json" "fmt" + "os" + "github.com/AlecAivazis/survey/v2" "github.com/rs/zerolog/log" "github.com/spf13/cobra" - "os" ) // storesCmd represents the stores command @@ -50,12 +51,19 @@ var storesListCmd = &cobra.Command{ informDebug(debugFlag) // Authenticate - authConfig := createAuthConfigFromParams(kfcHostName, kfcUsername, kfcPassword, kfcDomain, kfcAPIPath) - kfClient, _ := initClient(configFile, profile, providerType, providerProfile, noPrompt, authConfig, false) + kfClient, cErr := initClient(false) + if cErr != nil { + log.Error().Err(cErr).Send() + return cErr + } // CLI Logic params := make(map[string]interface{}) + log.Debug(). + Str("params", fmt.Sprintf("%v", params)). + Msg("Calling ListCertificateStores") stores, err := kfClient.ListCertificateStores(¶ms) + log.Debug().Str("stores", fmt.Sprintf("%v", stores)).Msg("Stores returned") if err != nil { log.Error().Err(err).Send() @@ -66,6 +74,7 @@ var storesListCmd = &cobra.Command{ log.Error().Err(jErr).Send() return jErr } + outputResult(output, outputFormat) return nil }, @@ -89,8 +98,7 @@ var storesGetCmd = &cobra.Command{ informDebug(debugFlag) // Authenticate - authConfig := createAuthConfigFromParams(kfcHostName, kfcUsername, kfcPassword, kfcDomain, kfcAPIPath) - kfClient, _ := initClient(configFile, profile, providerType, providerProfile, noPrompt, authConfig, false) + kfClient, _ := initClient(false) // CLI Logic stores, err := kfClient.GetCertificateStoreByID(storeID) @@ -129,8 +137,7 @@ var storesDeleteCmd = &cobra.Command{ informDebug(debugFlag) // Authenticate - authConfig := createAuthConfigFromParams(kfcHostName, kfcUsername, kfcPassword, kfcDomain, kfcAPIPath) - kfClient, _ := initClient(configFile, profile, providerType, providerProfile, noPrompt, authConfig, false) + kfClient, _ := initClient(false) // CLI Logic log.Info().Str("storeID", storeID).Msg("Deleting certificate store") @@ -298,7 +305,13 @@ func init() { // delete cmd storesDeleteCmd.Flags().StringVarP(&storeID, "id", "i", "", "ID of the certificate store to delete.") - storesDeleteCmd.Flags().StringVarP(&inputFile, "file", "f", "", "The path to a CSV file containing the Ids of the stores to delete.") + storesDeleteCmd.Flags().StringVarP( + &inputFile, + "file", + "f", + "", + "The path to a CSV file containing the Ids of the stores to delete.", + ) storesDeleteCmd.Flags().BoolVarP(&deleteAll, "all", "a", false, "Attempt to delete ALL stores.") storesDeleteCmd.MarkFlagsMutuallyExclusive("id", "all") diff --git a/cmd/storesBulkOperations.go b/cmd/storesBulkOperations.go index 216ed45..45d61fa 100644 --- a/cmd/storesBulkOperations.go +++ b/cmd/storesBulkOperations.go @@ -19,14 +19,15 @@ import ( "encoding/csv" "encoding/json" "fmt" + "os" + "strconv" + "strings" + "github.com/AlecAivazis/survey/v2" "github.com/Jeffail/gabs" - "github.com/Keyfactor/keyfactor-go-client/v2/api" + "github.com/Keyfactor/keyfactor-go-client/v3/api" "github.com/rs/zerolog/log" "github.com/spf13/cobra" - "os" - "strconv" - "strings" ) var ( @@ -46,6 +47,56 @@ var ( } ) +// formatProperties will iterate through the properties of a json object and convert any "int" values to strings +// this is required because the Keyfactor API expects all properties to be strings +func formatProperties(json *gabs.Container, reqPropertiesForStoreType []string) *gabs.Container { + // Iterate through required properties and add to JSON + for _, reqProp := range reqPropertiesForStoreType { + if json.ExistsP("Properties." + reqProp) { + log.Debug().Str("reqProp", reqProp).Msg("Property exists in json") + continue + } + json.Set("", "Properties", reqProp) // Correctly add the required property + } + + // Iterate through properties and convert any "int" values to strings + properties, _ := json.S("Properties").ChildrenMap() + for name, prop := range properties { + if prop.Data() == nil { + log.Debug().Str("name", name).Msg("Property is nil") + continue + } + if intValue, isInt := prop.Data().(int); isInt { + log.Debug().Str("name", name).Msg("Property is an int") + asStr := strconv.Itoa(intValue) + // Use gabs' Set method to update the property value + json.Set(asStr, "Properties", name) + } + } + return json +} + +func serializeStoreFromTypeDef(storeTypeName string, input string) (string, error) { + // check if storetypename is an integer + storeTypes, _ := readStoreTypesConfig("", DefaultGitRef, DefaultGitRepo, offline) + log.Debug(). + Str("storeTypeName", storeTypeName). + Msg("checking if storeTypeName is an integer") + sTypeId, err := strconv.Atoi(storeTypeName) + if err == nil { + log.Debug(). + Int("storeTypeId", sTypeId). + Msg("storeTypeName is an integer") + } + for _, st := range storeTypes { + log.Debug(). + Interface("st", st). + Msg("iterating through store types") + } + return "", nil + +} + var importStoresCmd = &cobra.Command{ Use: "import", Short: "Import a file with certificate store parameters and create them in keyfactor.", @@ -82,8 +133,11 @@ var storesCreateFromCSVCmd = &cobra.Command{ informDebug(debugFlag) // Authenticate - authConfig := createAuthConfigFromParams(kfcHostName, kfcUsername, kfcPassword, kfcDomain, kfcAPIPath) - kfClient, _ := initClient(configFile, profile, "", "", noPrompt, authConfig, false) + kfClient, cErr := initClient(false) + if cErr != nil { + log.Error().Err(cErr).Msg("Error initializing client") + return cErr + } // CLI Logic log.Info().Msg("Importing certificate stores") @@ -107,7 +161,7 @@ var storesCreateFromCSVCmd = &cobra.Command{ } // render list of store types as options for user to select var storeTypeOptions []string - for name, _ := range *sTypes { + for name := range *sTypes { storeTypeOptions = append(storeTypeOptions, fmt.Sprintf("%s", name)) } prompt := &survey.Select{ @@ -159,7 +213,11 @@ var storesCreateFromCSVCmd = &cobra.Command{ // check for minimum necessary required fields for creating certificate stores log.Info().Msgf("Checking for minimum required fields for creating certificate stores") - intID, reqPropertiesForStoreType := getRequiredProperties(st, *kfClient) + intID, reqPropertiesForStoreType, pErr := getRequiredProperties(st, *kfClient) + if pErr != nil { + cmd.SilenceUsage = true + return pErr + } // if not present in header, throw error. headerRow := inFile[0] @@ -212,6 +270,9 @@ var storesCreateFromCSVCmd = &cobra.Command{ continue } reqJson := getJsonForRequest(headerRow, row) + + reqJson = formatProperties(reqJson, reqPropertiesForStoreType) + reqJson.Set(intID, "CertStoreType") // cannot send in 0 as ContainerId, need to omit @@ -231,7 +292,10 @@ var storesCreateFromCSVCmd = &cobra.Command{ if conversionError != nil { //outputError(conversionError, true, outputFormat) - log.Error().Err(conversionError).Msgf("Unable to convert the json into the request parameters object. %s", conversionError.Error()) + log.Error().Err(conversionError).Msgf( + "Unable to convert the json into the request parameters object. %s", + conversionError.Error(), + ) return conversionError } @@ -289,7 +353,8 @@ var storesCreateFromCSVCmd = &cobra.Command{ //fmt.Printf("\nImport results written to %s\n\n", outPath) outputResult(fmt.Sprintf("Import results written to %s", outPath), outputFormat) return nil - }} + }, +} var storesCreateImportTemplateCmd = &cobra.Command{ Use: "generate-template --store-type-id --store-type-name --outpath ", @@ -319,8 +384,7 @@ Store type IDs can be found by running the "store-types" command.`, informDebug(debugFlag) // Authenticate - authConfig := createAuthConfigFromParams(kfcHostName, kfcUsername, kfcPassword, kfcDomain, kfcAPIPath) - kfClient, clientErr := initClient(configFile, profile, "", "", noPrompt, authConfig, false) + kfClient, clientErr := initClient(false) if clientErr != nil { log.Error().Err(clientErr).Msg("Error initializing client") return clientErr @@ -344,7 +408,7 @@ Store type IDs can be found by running the "store-types" command.`, validStoreTypesResp, vstErr := kfClient.ListCertificateStoreTypes() if vstErr != nil { log.Error().Err(vstErr).Msg("unable to list certificate store types") - validStoreTypes = getValidStoreTypes("", "main") + validStoreTypes = getValidStoreTypes("", DefaultGitRef, DefaultGitRepo) } else { for _, v := range *validStoreTypesResp { validStoreTypes = append(validStoreTypes, v.ShortName) @@ -421,7 +485,10 @@ Store type IDs can be found by running the "store-types" command.`, return csvWriteErr } log.Info().Str("filePath", filePath).Msg("Template file written") - outputResult(fmt.Sprintf("Template file for store type with id %d written to %s", intID, filePath), outputFormat) + outputResult( + fmt.Sprintf("Template file for store type with id %d written to %s", intID, filePath), + outputFormat, + ) return nil }, } @@ -456,8 +523,8 @@ var storesExportCmd = &cobra.Command{ informDebug(debugFlag) // Authenticate - authConfig := createAuthConfigFromParams(kfcHostName, kfcUsername, kfcPassword, kfcDomain, kfcAPIPath) - kfClient, _ := initClient(configFile, profile, "", "", noPrompt, authConfig, false) + + kfClient, _ := initClient(false) // CLI Logic log.Info(). @@ -504,7 +571,7 @@ var storesExportCmd = &cobra.Command{ } // render list of store types as options for user to select var storeTypeOptions []string - for name, _ := range *storeTypes { + for name := range *storeTypes { storeTypeOptions = append(storeTypeOptions, fmt.Sprintf("%s", name)) } prompt := &survey.Select{ @@ -625,6 +692,10 @@ var storesExportCmd = &cobra.Command{ log.Debug().Str("name", name). Interface("prop", prop). Msg("adding to properties CSV data") + //check if property is an int + if _, isInt := prop.(int); isInt { + prop = strconv.Itoa(prop.(int)) + } if name != "ServerUsername" && name != "ServerPassword" { // Don't add ServerUsername and ServerPassword to properties as they can't be exported via API csvData[store.Id]["Properties."+name] = prop } @@ -796,13 +867,17 @@ func getHeadersForStoreType(id interface{}, kfClient api.Client) (int64, string, return intId, shortName, csvHeaders } -func getRequiredProperties(id interface{}, kfClient api.Client) (int64, []string) { +func getRequiredProperties(id interface{}, kfClient api.Client) (int64, []string, error) { storeType, err := kfClient.GetCertificateStoreType(id) if err != nil { - log.Printf("Error: %s", err) - fmt.Printf("Error: %s\n", err) - panic("error retrieving store type") + log.Error(). + Interface("id", id). + Err(err).Msg("Error retrieving store type from Keyfactor Command") + return 0, nil, fmt.Errorf( + "error retrieving store type '%s' from Keyfactor Command, please ensure you're using `ShortName` or `Id`", + id, + ) } output, jErr := json.Marshal(storeType) @@ -828,7 +903,7 @@ func getRequiredProperties(id interface{}, kfClient api.Client) (int64, []string } intId, _ := jsonParsedObj.S("StoreType").Data().(json.Number).Int64() - return intId, reqProps + return intId, reqProps, nil } func unmarshalPropertiesString(properties string) map[string]interface{} { @@ -841,6 +916,13 @@ func unmarshalPropertiesString(properties string) map[string]interface{} { // Then, iterate through each key:value pair and serialize into map[string]string newMap := make(map[string]interface{}) for key, value := range tempInterface.(map[string]interface{}) { + // check if value is an int + if _, isInt := value.(int); isInt { + log.Debug(). + Str("key", key). + Int("value", value.(int)).Msg("converting int to string as Command does not accept int") + value = strconv.Itoa(value.(int)) + } newMap[key] = value } return newMap @@ -992,24 +1074,76 @@ func init() { importStoresCmd.AddCommand(storesCreateImportTemplateCmd) importStoresCmd.AddCommand(storesCreateFromCSVCmd) - storesCreateImportTemplateCmd.Flags().StringVarP(&storeTypeName, "store-type-name", "n", "", "The name of the cert store type for the template. Use if store-type-id is unknown.") - storesCreateImportTemplateCmd.Flags().IntVarP(&storeTypeId, "store-type-id", "i", -1, "The ID of the cert store type for the template.") - storesCreateImportTemplateCmd.Flags().StringVarP(&outPath, "outpath", "o", "", - "Path and name of the template file to generate.. If not specified, the file will be written to the current directory.") + storesCreateImportTemplateCmd.Flags().StringVarP( + &storeTypeName, + "store-type-name", + "n", + "", + "The name of the cert store type for the template. Use if store-type-id is unknown.", + ) + storesCreateImportTemplateCmd.Flags().IntVarP( + &storeTypeId, + "store-type-id", + "i", + -1, + "The ID of the cert store type for the template.", + ) + storesCreateImportTemplateCmd.Flags().StringVarP( + &outPath, + "outpath", + "o", + "", + "Path and name of the template file to generate.. If not specified, the file will be written to the current directory.", + ) storesCreateImportTemplateCmd.MarkFlagsMutuallyExclusive("store-type-name", "store-type-id") - storesCreateFromCSVCmd.Flags().StringVarP(&storeTypeName, "store-type-name", "n", "", "The name of the cert store type. Use if store-type-id is unknown.") - storesCreateFromCSVCmd.Flags().IntVarP(&storeTypeId, "store-type-id", "i", -1, "The ID of the cert store type for the stores.") + storesCreateFromCSVCmd.Flags().StringVarP( + &storeTypeName, + "store-type-name", + "n", + "", + "The name of the cert store type. Use if store-type-id is unknown.", + ) + storesCreateFromCSVCmd.Flags().IntVarP( + &storeTypeId, + "store-type-id", + "i", + -1, + "The ID of the cert store type for the stores.", + ) storesCreateFromCSVCmd.Flags().StringVarP(&file, "file", "f", "", "CSV file containing cert stores to create.") storesCreateFromCSVCmd.MarkFlagRequired("file") storesCreateFromCSVCmd.Flags().BoolP("dry-run", "d", false, "Do not import, just check for necessary fields.") - storesCreateFromCSVCmd.Flags().StringVarP(&resultsPath, "results-path", "o", "", "CSV file containing cert stores to create. defaults to _results.csv") + storesCreateFromCSVCmd.Flags().StringVarP( + &resultsPath, + "results-path", + "o", + "", + "CSV file containing cert stores to create. defaults to _results.csv", + ) storesExportCmd.Flags().BoolVarP(&exportAll, "all", "a", false, "Export all stores grouped by store-type.") - storesExportCmd.Flags().StringVarP(&storeTypeName, "store-type-name", "n", "", "The name of the cert store type for the template. Use if store-type-id is unknown.") - storesExportCmd.Flags().IntVarP(&storeTypeId, "store-type-id", "i", -1, "The ID of the cert store type for the template.") - storesExportCmd.Flags().StringVarP(&outPath, "outpath", "o", "", - "Path and name of the template file to generate.. If not specified, the file will be written to the current directory.") + storesExportCmd.Flags().StringVarP( + &storeTypeName, + "store-type-name", + "n", + "", + "The name of the cert store type for the template. Use if store-type-id is unknown.", + ) + storesExportCmd.Flags().IntVarP( + &storeTypeId, + "store-type-id", + "i", + -1, + "The ID of the cert store type for the template.", + ) + storesExportCmd.Flags().StringVarP( + &outPath, + "outpath", + "o", + "", + "Path and name of the template file to generate.. If not specified, the file will be written to the current directory.", + ) storesExportCmd.MarkFlagsMutuallyExclusive("store-type-name", "store-type-id") } diff --git a/cmd/stores_test.go b/cmd/stores_test.go index 81c3f4e..3e03ba8 100644 --- a/cmd/stores_test.go +++ b/cmd/stores_test.go @@ -17,11 +17,12 @@ package cmd import ( "encoding/json" "fmt" - "github.com/stretchr/testify/assert" "os" "path/filepath" "strings" "testing" + + "github.com/stretchr/testify/assert" ) func Test_Stores_HelpCmd(t *testing.T) { @@ -52,10 +53,12 @@ func Test_Stores_ListCmd(t *testing.T) { testCmd := RootCmd // test testCmd.SetArgs([]string{"stores", "list", "--exp"}) - output := captureOutput(func() { - err := testCmd.Execute() - assert.NoError(t, err) - }) + output := captureOutput( + func() { + err := testCmd.Execute() + assert.NoError(t, err) + }, + ) var stores []interface{} if err := json.Unmarshal([]byte(output), &stores); err != nil { t.Fatalf("Error unmarshalling JSON: %v", err) @@ -80,10 +83,12 @@ func Test_Stores_GetCmd(t *testing.T) { testCmd := RootCmd // test testCmd.SetArgs([]string{"stores", "list", "--exp"}) - output := captureOutput(func() { - err := testCmd.Execute() - assert.NoError(t, err) - }) + output := captureOutput( + func() { + err := testCmd.Execute() + assert.NoError(t, err) + }, + ) var stores []interface{} if err := json.Unmarshal([]byte(output), &stores); err != nil { t.Fatalf("Error unmarshalling JSON: %v", err) @@ -102,10 +107,12 @@ func Test_Stores_GetCmd(t *testing.T) { assert.NotEmpty(t, store.(map[string]interface{})["CertStoreType"]) testCmd.SetArgs([]string{"stores", "get", "--id", store.(map[string]interface{})["Id"].(string)}) - output = captureOutput(func() { - err := testCmd.Execute() - assert.NoError(t, err) - }) + output = captureOutput( + func() { + err := testCmd.Execute() + assert.NoError(t, err) + }, + ) var store map[string]interface{} if err := json.Unmarshal([]byte(output), &store); err != nil { @@ -183,11 +190,24 @@ func Test_Stores_ImportCmd(t *testing.T) { testCmd := RootCmd // test - testCmd.SetArgs([]string{"stores", "import", "csv", "--file", outFileName, "--store-type-name", "k8ssecret", "--exp"}) - output := captureOutput(func() { - err := testCmd.Execute() - assert.NoError(t, err) - }) + testCmd.SetArgs( + []string{ + "stores", + "import", + "csv", + "--file", + outFileName, + "--store-type-name", + "k8ssecret", + "--exp", + }, + ) + output := captureOutput( + func() { + err := testCmd.Execute() + assert.NoError(t, err) + }, + ) assert.Contains(t, output, "records processed") assert.Contains(t, output, "results written to") @@ -234,14 +254,16 @@ func Test_Stores_GenerateImportTemplateCmd(t *testing.T) { testCmd := RootCmd // test testCmd.SetArgs([]string{"stores", "import", "generate-template", "--store-type-name", "k8ssecret"}) - output := captureOutput(func() { - err := testCmd.Execute() - assert.NoError(t, err) - if err != nil { - t.Errorf("Error: %v", err) - return - } - }) + output := captureOutput( + func() { + err := testCmd.Execute() + assert.NoError(t, err) + if err != nil { + t.Errorf("Error: %v", err) + return + } + }, + ) assert.Contains(t, output, "Template file for store type with id") assert.Contains(t, output, "csv") @@ -312,105 +334,117 @@ func testExportStore(t *testing.T, storeTypeName string) (string, []string) { files []string err error ) - t.Run(fmt.Sprintf("Export Stores of type %s", storeTypeName), func(t *testing.T) { - testCmd := RootCmd - testCmd.SetArgs([]string{"stores", "export", "--store-type-name", storeTypeName}) - output = captureOutput(func() { - err := testCmd.Execute() - assert.NoError(t, err) - }) - - // assert that output is not empty - assert.NotEmpty(t, output) - - // assert that output is a string - assert.IsType(t, "", output) - - // assert that output does not contain 'error' - assert.NotContains(t, output, "error") - - // assert that output does not contain 'Error' - assert.NotContains(t, output, "Error") - - // assert that output does not contain 'ERROR' - assert.NotContains(t, output, "ERROR") - - // assert that contains "exported for store type with id" - assert.Contains(t, output, "exported for store type with id") - - // assert that contains .csv - assert.Contains(t, output, ".csv") - - // assert that a csv file was created in current working directory with a filename that contains 'export_store_*.csv' - files, err = findMatchingFiles("*stores_export*.csv") - assert.Nil(t, err) - assert.NotEmpty(t, files) - }) + t.Run( + fmt.Sprintf("Export Stores of type %s", storeTypeName), func(t *testing.T) { + testCmd := RootCmd + testCmd.SetArgs([]string{"stores", "export", "--store-type-name", storeTypeName}) + output = captureOutput( + func() { + err := testCmd.Execute() + assert.NoError(t, err) + }, + ) + + // assert that output is not empty + assert.NotEmpty(t, output) + + // assert that output is a string + assert.IsType(t, "", output) + + // assert that output does not contain 'error' + assert.NotContains(t, output, "error") + + // assert that output does not contain 'Error' + assert.NotContains(t, output, "Error") + + // assert that output does not contain 'ERROR' + assert.NotContains(t, output, "ERROR") + + // assert that contains "exported for store type with id" + assert.Contains(t, output, "exported for store type with id") + + // assert that contains .csv + assert.Contains(t, output, ".csv") + + // assert that a csv file was created in current working directory with a filename that contains 'export_store_*.csv' + files, err = findMatchingFiles("*stores_export*.csv") + assert.Nil(t, err) + assert.NotEmpty(t, files) + }, + ) return output, files } func deleteStoreTest(t *testing.T, storeID string, allowFail bool) { - t.Run(fmt.Sprintf("Delete Store %s", storeID), func(t *testing.T) { - testCmd := RootCmd - testCmd.SetArgs([]string{"stores", "delete", "--id", storeID}) - deleteStoreOutput := captureOutput(func() { - err := testCmd.Execute() + t.Run( + fmt.Sprintf("Delete Store %s", storeID), func(t *testing.T) { + testCmd := RootCmd + testCmd.SetArgs([]string{"stores", "delete", "--id", storeID}) + deleteStoreOutput := captureOutput( + func() { + err := testCmd.Execute() + if !allowFail { + assert.NoError(t, err) + } + }, + ) if !allowFail { - assert.NoError(t, err) - } - }) - if !allowFail { - if strings.Contains(deleteStoreOutput, "does not exist") { - t.Errorf("Store %s does not exist", storeID) - } - if strings.Contains(deleteStoreOutput, "cannot be deleted") { - assert.Fail(t, fmt.Sprintf("Store %s already exists", storeID)) - } - if strings.Contains(deleteStoreOutput, "error processing the request") { - assert.Fail(t, fmt.Sprintf("Store %s was not deleted: %s", storeID, deleteStoreOutput)) + if strings.Contains(deleteStoreOutput, "does not exist") { + t.Errorf("Store %s does not exist", storeID) + } + if strings.Contains(deleteStoreOutput, "cannot be deleted") { + assert.Fail(t, fmt.Sprintf("Store %s already exists", storeID)) + } + if strings.Contains(deleteStoreOutput, "error processing the request") { + assert.Fail(t, fmt.Sprintf("Store %s was not deleted: %s", storeID, deleteStoreOutput)) + } + assert.Contains(t, deleteStoreOutput, "deleted") + assert.Contains(t, deleteStoreOutput, storeID) } - assert.Contains(t, deleteStoreOutput, "deleted") - assert.Contains(t, deleteStoreOutput, storeID) - } - }) + }, + ) } func testValidateCSVHeader(t *testing.T, filename string, header []string, expected []string) { // iterate bulkStoreImportCSVHeader and verify that each header is in the csv header - t.Run(fmt.Sprintf("Validate CSV header %s", filename), func(t *testing.T) { - // Check that first col isn't empty - assert.NotEmpty(t, header[0], "First column of CSV is empty") - - for _, h := range expected { - if h != "Properties" { - assert.Contains(t, header, h) + t.Run( + fmt.Sprintf("Validate CSV header %s", filename), func(t *testing.T) { + // Check that first col isn't empty + assert.NotEmpty(t, header[0], "First column of CSV is empty") + + for _, h := range expected { + if h != "Properties" { + assert.Contains(t, header, h) + } } - } - var props []string - for _, h := range header { - if strings.Contains(h, "Properties") { - props = append(props, h) + var props []string + for _, h := range header { + if strings.Contains(h, "Properties") { + props = append(props, h) + } } - } - assert.NotEmpty(t, props) - }) + assert.NotEmpty(t, props) + }, + ) } func testDeleteImportResults(t *testing.T, fileName string) { // get filename from path fName := filepath.Base(fileName) - t.Run(fmt.Sprintf("Deleting imported stores %s", fName), func(t *testing.T) { - csvData, err := csvToMap(fileName) - assert.NoError(t, err) + t.Run( + fmt.Sprintf("Deleting imported stores %s", fName), func(t *testing.T) { + csvData, err := csvToMap(fileName) + assert.NoError(t, err) - for _, row := range csvData { - // assert that each row has an ID - //assert.NotEmpty(t, row[""]) - // delete store - if row["Id"] != "" && row["Id"] != "error" && row["Id"] != "Id" { - deleteStoreTest(t, row["Id"], false) + for _, row := range csvData { + // assert that each row has an ID + //assert.NotEmpty(t, row[""]) + // delete store + if row["Id"] != "" && row["Id"] != "error" && row["Id"] != "Id" { + deleteStoreTest(t, row["Id"], false) + } } - } - }) + }, + ) } diff --git a/cmd/test.go b/cmd/test.go index 941df87..30cdfb3 100644 --- a/cmd/test.go +++ b/cmd/test.go @@ -16,22 +16,47 @@ package cmd import ( "bytes" + "encoding/json" + "errors" "io" "os" + "regexp" ) func captureOutput(f func()) string { + // Save the original os.Stdout old := os.Stdout + // Create a pipe r, w, _ := os.Pipe() + // Set os.Stdout to the write end of the pipe os.Stdout = w + // Create a channel to signal when f() has completed + done := make(chan bool) + + // Buffer to store the output + var buf bytes.Buffer + + // Start a goroutine to copy from the read end of the pipe to the buffer + go func() { + io.Copy(&buf, r) + // Signal that the copying is done + done <- true + }() + + // Run the provided function f f() + // Close the write end of the pipe to signal EOF to the reader w.Close() + + // Wait for the goroutine to finish copying + <-done + + // Restore the original os.Stdout os.Stdout = old - var buf bytes.Buffer - io.Copy(&buf, r) + // Return the captured output as a string return buf.String() } @@ -73,3 +98,25 @@ func getTestEnv() (testEnv, error) { return testEnv, nil } + +// findLastJSON attempts to find the last valid JSON object or array in a string. +func findLastJSON(input string) (string, error) { + // Regular expression to match JSON objects and arrays + // This regex looks for the most complex JSON objects and arrays, allowing nested structures + re := regexp.MustCompile(`(\{(?:[^{}]*|\{[^{}]*\})*\}|\[(?:[^\[\]]*|\[[^\[\]]*\])*\])`) + matches := re.FindAllString(input, -1) + + // If no match is found, return an empty string + if len(matches) == 0 { + return "", errors.New("no JSON object or array found") + } + + // Validate that the last match is a valid JSON object or array + lastMatch := matches[len(matches)-1] + var js json.RawMessage + if err := json.Unmarshal([]byte(lastMatch), &js); err != nil { + return "", errors.New("invalid JSON object or array found") + } + + return lastMatch, nil +} diff --git a/docs/kfutil.md b/docs/kfutil.md index e201503..a7c1b0f 100644 --- a/docs/kfutil.md +++ b/docs/kfutil.md @@ -12,6 +12,8 @@ A CLI wrapper around the Keyfactor Platform API. --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. @@ -19,10 +21,12 @@ A CLI wrapper around the Keyfactor Platform API. --format text How to format the CLI output. Currently only text is supported. (default "text") -h, --help help for kfutil --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -34,7 +38,7 @@ A CLI wrapper around the Keyfactor Platform API. * [kfutil helm](kfutil_helm.md) - Helm utilities for configuring Keyfactor Helm charts * [kfutil import](kfutil_import.md) - Keyfactor instance import utilities. * [kfutil login](kfutil_login.md) - User interactive login to Keyfactor. Stores the credentials in the config file '$HOME/.keyfactor/command_config.json'. -* [kfutil logout](kfutil_logout.md) - Removes the credentials file '$HOME/.keyfactor/command_config.json'. +* [kfutil logout](kfutil_logout.md) - Unsets environment variables and removes the stored credentials file. * [kfutil orchs](kfutil_orchs.md) - Keyfactor agents/orchestrators APIs and utilities. * [kfutil pam](kfutil_pam.md) - Keyfactor PAM Provider APIs. * [kfutil status](kfutil_status.md) - List the status of Keyfactor services. @@ -42,4 +46,4 @@ A CLI wrapper around the Keyfactor Platform API. * [kfutil stores](kfutil_stores.md) - Keyfactor certificate stores APIs and utilities. * [kfutil version](kfutil_version.md) - Shows version of kfutil -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_completion.md b/docs/kfutil_completion.md index e4557d0..1fae919 100644 --- a/docs/kfutil_completion.md +++ b/docs/kfutil_completion.md @@ -20,16 +20,20 @@ See each sub-command's help for details on how to use the generated script. --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -41,4 +45,4 @@ See each sub-command's help for details on how to use the generated script. * [kfutil completion powershell](kfutil_completion_powershell.md) - Generate the autocompletion script for powershell * [kfutil completion zsh](kfutil_completion_zsh.md) - Generate the autocompletion script for zsh -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_completion_bash.md b/docs/kfutil_completion_bash.md index 81afccb..527ddf2 100644 --- a/docs/kfutil_completion_bash.md +++ b/docs/kfutil_completion_bash.md @@ -43,16 +43,20 @@ kfutil completion bash --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -60,4 +64,4 @@ kfutil completion bash * [kfutil completion](kfutil_completion.md) - Generate the autocompletion script for the specified shell -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_completion_fish.md b/docs/kfutil_completion_fish.md index 32b1b40..3a32698 100644 --- a/docs/kfutil_completion_fish.md +++ b/docs/kfutil_completion_fish.md @@ -34,16 +34,20 @@ kfutil completion fish [flags] --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -51,4 +55,4 @@ kfutil completion fish [flags] * [kfutil completion](kfutil_completion.md) - Generate the autocompletion script for the specified shell -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_completion_powershell.md b/docs/kfutil_completion_powershell.md index 5a19e9f..50e0b78 100644 --- a/docs/kfutil_completion_powershell.md +++ b/docs/kfutil_completion_powershell.md @@ -31,16 +31,20 @@ kfutil completion powershell [flags] --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -48,4 +52,4 @@ kfutil completion powershell [flags] * [kfutil completion](kfutil_completion.md) - Generate the autocompletion script for the specified shell -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_completion_zsh.md b/docs/kfutil_completion_zsh.md index 8865a54..97c7c54 100644 --- a/docs/kfutil_completion_zsh.md +++ b/docs/kfutil_completion_zsh.md @@ -45,16 +45,20 @@ kfutil completion zsh [flags] --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -62,4 +66,4 @@ kfutil completion zsh [flags] * [kfutil completion](kfutil_completion.md) - Generate the autocompletion script for the specified shell -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_containers.md b/docs/kfutil_containers.md index 745d5f6..f0624a1 100644 --- a/docs/kfutil_containers.md +++ b/docs/kfutil_containers.md @@ -18,16 +18,20 @@ A collections of APIs and utilities for interacting with Keyfactor certificate s --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -37,4 +41,4 @@ A collections of APIs and utilities for interacting with Keyfactor certificate s * [kfutil containers get](kfutil_containers_get.md) - Get certificate store container by ID or name. * [kfutil containers list](kfutil_containers_list.md) - List certificate store containers. -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_containers_get.md b/docs/kfutil_containers_get.md index 5838b6d..8b01da1 100644 --- a/docs/kfutil_containers_get.md +++ b/docs/kfutil_containers_get.md @@ -23,16 +23,20 @@ kfutil containers get [flags] --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -40,4 +44,4 @@ kfutil containers get [flags] * [kfutil containers](kfutil_containers.md) - Keyfactor certificate store container API and utilities. -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_containers_list.md b/docs/kfutil_containers_list.md index 73e5c1d..7a4d56d 100644 --- a/docs/kfutil_containers_list.md +++ b/docs/kfutil_containers_list.md @@ -22,16 +22,20 @@ kfutil containers list [flags] --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -39,4 +43,4 @@ kfutil containers list [flags] * [kfutil containers](kfutil_containers.md) - Keyfactor certificate store container API and utilities. -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_export.md b/docs/kfutil_export.md index 5445dc2..439e3bd 100644 --- a/docs/kfutil_export.md +++ b/docs/kfutil_export.md @@ -34,16 +34,20 @@ kfutil export [flags] --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -51,4 +55,4 @@ kfutil export [flags] * [kfutil](kfutil.md) - Keyfactor CLI utilities -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_helm.md b/docs/kfutil_helm.md index 55754d4..297fa81 100644 --- a/docs/kfutil_helm.md +++ b/docs/kfutil_helm.md @@ -24,16 +24,20 @@ kubectl helm uo | helm install -f - keyfactor-universal-orchestrator keyfactor/k --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -42,4 +46,4 @@ kubectl helm uo | helm install -f - keyfactor-universal-orchestrator keyfactor/k * [kfutil](kfutil.md) - Keyfactor CLI utilities * [kfutil helm uo](kfutil_helm_uo.md) - Configure the Keyfactor Universal Orchestrator Helm Chart -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_helm_uo.md b/docs/kfutil_helm_uo.md index d7d8f9c..2471540 100644 --- a/docs/kfutil_helm_uo.md +++ b/docs/kfutil_helm_uo.md @@ -29,16 +29,20 @@ kfutil helm uo [-t ] [-o ] [-f ] [-e ] [-o ] [-f ] [-e -e @,@ -o ./app/extension --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -56,4 +60,4 @@ ext -t -e @,@ -o ./app/extension * [kfutil orchs](kfutil_orchs.md) - Keyfactor agents/orchestrators APIs and utilities. -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_orchs_get.md b/docs/kfutil_orchs_get.md index 5011e1a..a1fdd35 100644 --- a/docs/kfutil_orchs_get.md +++ b/docs/kfutil_orchs_get.md @@ -23,16 +23,20 @@ kfutil orchs get [flags] --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -40,4 +44,4 @@ kfutil orchs get [flags] * [kfutil orchs](kfutil_orchs.md) - Keyfactor agents/orchestrators APIs and utilities. -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_orchs_list.md b/docs/kfutil_orchs_list.md index e41c8f3..7d28007 100644 --- a/docs/kfutil_orchs_list.md +++ b/docs/kfutil_orchs_list.md @@ -22,16 +22,20 @@ kfutil orchs list [flags] --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -39,4 +43,4 @@ kfutil orchs list [flags] * [kfutil orchs](kfutil_orchs.md) - Keyfactor agents/orchestrators APIs and utilities. -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_orchs_logs.md b/docs/kfutil_orchs_logs.md index 07c7e5d..f2eb557 100644 --- a/docs/kfutil_orchs_logs.md +++ b/docs/kfutil_orchs_logs.md @@ -23,16 +23,20 @@ kfutil orchs logs [flags] --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -40,4 +44,4 @@ kfutil orchs logs [flags] * [kfutil orchs](kfutil_orchs.md) - Keyfactor agents/orchestrators APIs and utilities. -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_orchs_reset.md b/docs/kfutil_orchs_reset.md index 0e46fd0..fd0ebdc 100644 --- a/docs/kfutil_orchs_reset.md +++ b/docs/kfutil_orchs_reset.md @@ -23,16 +23,20 @@ kfutil orchs reset [flags] --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -40,4 +44,4 @@ kfutil orchs reset [flags] * [kfutil orchs](kfutil_orchs.md) - Keyfactor agents/orchestrators APIs and utilities. -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_pam.md b/docs/kfutil_pam.md index 48ec53a..0ee4a58 100644 --- a/docs/kfutil_pam.md +++ b/docs/kfutil_pam.md @@ -20,16 +20,20 @@ programmatically create, delete, edit, and list PAM Providers. --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -44,4 +48,4 @@ programmatically create, delete, edit, and list PAM Providers. * [kfutil pam types-list](kfutil_pam_types-list.md) - Returns a list of all available PAM provider types. * [kfutil pam update](kfutil_pam_update.md) - Updates an existing PAM Provider, currently only supported from file. -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_pam_create.md b/docs/kfutil_pam_create.md index 752f914..f68938c 100644 --- a/docs/kfutil_pam_create.md +++ b/docs/kfutil_pam_create.md @@ -23,16 +23,20 @@ kfutil pam create [flags] --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -40,4 +44,4 @@ kfutil pam create [flags] * [kfutil pam](kfutil_pam.md) - Keyfactor PAM Provider APIs. -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_pam_delete.md b/docs/kfutil_pam_delete.md index 27d2410..dbf74c0 100644 --- a/docs/kfutil_pam_delete.md +++ b/docs/kfutil_pam_delete.md @@ -23,16 +23,20 @@ kfutil pam delete [flags] --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -40,4 +44,4 @@ kfutil pam delete [flags] * [kfutil pam](kfutil_pam.md) - Keyfactor PAM Provider APIs. -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_pam_get.md b/docs/kfutil_pam_get.md index 62f4b75..8bae60e 100644 --- a/docs/kfutil_pam_get.md +++ b/docs/kfutil_pam_get.md @@ -23,16 +23,20 @@ kfutil pam get [flags] --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -40,4 +44,4 @@ kfutil pam get [flags] * [kfutil pam](kfutil_pam.md) - Keyfactor PAM Provider APIs. -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_pam_list.md b/docs/kfutil_pam_list.md index af95f9b..e6006c7 100644 --- a/docs/kfutil_pam_list.md +++ b/docs/kfutil_pam_list.md @@ -22,16 +22,20 @@ kfutil pam list [flags] --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -39,4 +43,4 @@ kfutil pam list [flags] * [kfutil pam](kfutil_pam.md) - Keyfactor PAM Provider APIs. -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_pam_types-create.md b/docs/kfutil_pam_types-create.md index d6e2e49..32713bb 100644 --- a/docs/kfutil_pam_types-create.md +++ b/docs/kfutil_pam_types-create.md @@ -30,16 +30,20 @@ kfutil pam types-create [flags] --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -47,4 +51,4 @@ kfutil pam types-create [flags] * [kfutil pam](kfutil_pam.md) - Keyfactor PAM Provider APIs. -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_pam_types-list.md b/docs/kfutil_pam_types-list.md index 33fb9da..d9e88af 100644 --- a/docs/kfutil_pam_types-list.md +++ b/docs/kfutil_pam_types-list.md @@ -22,16 +22,20 @@ kfutil pam types-list [flags] --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -39,4 +43,4 @@ kfutil pam types-list [flags] * [kfutil pam](kfutil_pam.md) - Keyfactor PAM Provider APIs. -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_pam_update.md b/docs/kfutil_pam_update.md index c92d707..b3f7608 100644 --- a/docs/kfutil_pam_update.md +++ b/docs/kfutil_pam_update.md @@ -23,16 +23,20 @@ kfutil pam update [flags] --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -40,4 +44,4 @@ kfutil pam update [flags] * [kfutil pam](kfutil_pam.md) - Keyfactor PAM Provider APIs. -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_status.md b/docs/kfutil_status.md index 55a3647..87f0f2d 100644 --- a/docs/kfutil_status.md +++ b/docs/kfutil_status.md @@ -22,16 +22,20 @@ kfutil status [flags] --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -39,4 +43,4 @@ kfutil status [flags] * [kfutil](kfutil.md) - Keyfactor CLI utilities -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_store-types.md b/docs/kfutil_store-types.md index 6512726..a7e15b9 100644 --- a/docs/kfutil_store-types.md +++ b/docs/kfutil_store-types.md @@ -18,16 +18,20 @@ A collections of APIs and utilities for interacting with Keyfactor certificate s --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -40,4 +44,4 @@ A collections of APIs and utilities for interacting with Keyfactor certificate s * [kfutil store-types list](kfutil_store-types_list.md) - List certificate store types. * [kfutil store-types templates-fetch](kfutil_store-types_templates-fetch.md) - Fetches store type templates from Keyfactor's Github. -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_store-types_create.md b/docs/kfutil_store-types_create.md index e9cca52..79b7b77 100644 --- a/docs/kfutil_store-types_create.md +++ b/docs/kfutil_store-types_create.md @@ -18,7 +18,8 @@ kfutil store-types create [flags] -b, --git-ref string The git branch or tag to reference when pulling store-types from the internet. (default "main") -h, --help help for create -l, --list List valid store types. - -n, --name string Short name of the certificate store type to get. Valid choices are: AKV, AzureApp, AzureAppGW, AzureSP, Fortigate, HCVKV, HCVKVJKS, HCVKVP12, HCVKVPEM, HCVKVPFX, HCVPKI, IISU, K8SCert, K8SCluster, K8SJKS, K8SNS, K8SPKCS12, K8SSecret, K8STLSSecr, RFDER, RFJKS, RFKDB, RFORA, RFPEM, RFPkcs12, SAMPLETYPE, Signum, VMware-NSX, WinCerMgmt, WinCert + -n, --name string Short name of the certificate store type to get. Valid choices are: AKV, AWS-ACM, Akamai, AppGwBin, AzureApp, AzureApp2, AzureAppGw, AzureSP, AzureSP2, BIPCamera, CiscoAsa, CitrixAdc, F5-BigIQ, F5-CA-REST, F5-SL-REST, F5-WS-REST, Fortigate, GCPLoadBal, GcpCertMgr, HCVKV, HCVKVJKS, HCVKVP12, HCVKVPEM, HCVKVPFX, HCVPKI, IISU, Imperva, K8SCert, K8SCluster, K8SJKS, K8SNS, K8SPKCS12, K8SSecret, K8STLSSecr, MOST, Nmap, PaloAlto, RFDER, RFJKS, RFKDB, RFORA, RFPEM, RFPkcs12, SAMPLETYPE, Signum, VMware-NSX, WinCerMgmt, WinCert, WinSql + -r, --repo string The repository to pull store-types definitions from. (default "kfutil") ``` ### Options inherited from parent commands @@ -27,16 +28,20 @@ kfutil store-types create [flags] --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -44,4 +49,4 @@ kfutil store-types create [flags] * [kfutil store-types](kfutil_store-types.md) - Keyfactor certificate store types APIs and utilities. -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_store-types_delete.md b/docs/kfutil_store-types_delete.md index 2fd868d..cff57c8 100644 --- a/docs/kfutil_store-types_delete.md +++ b/docs/kfutil_store-types_delete.md @@ -26,16 +26,20 @@ kfutil store-types delete [flags] --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -43,4 +47,4 @@ kfutil store-types delete [flags] * [kfutil store-types](kfutil_store-types.md) - Keyfactor certificate store types APIs and utilities. -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_store-types_get.md b/docs/kfutil_store-types_get.md index f272b16..c1c54d8 100644 --- a/docs/kfutil_store-types_get.md +++ b/docs/kfutil_store-types_get.md @@ -27,16 +27,20 @@ kfutil store-types get [-i | -n ] [-b --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -44,4 +48,4 @@ kfutil store-types get [-i | -n ] [-b * [kfutil store-types](kfutil_store-types.md) - Keyfactor certificate store types APIs and utilities. -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_store-types_list.md b/docs/kfutil_store-types_list.md index 5e65ce9..1345bb1 100644 --- a/docs/kfutil_store-types_list.md +++ b/docs/kfutil_store-types_list.md @@ -22,16 +22,20 @@ kfutil store-types list [flags] --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -39,4 +43,4 @@ kfutil store-types list [flags] * [kfutil store-types](kfutil_store-types.md) - Keyfactor certificate store types APIs and utilities. -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_store-types_templates-fetch.md b/docs/kfutil_store-types_templates-fetch.md index 305f700..c888ca3 100644 --- a/docs/kfutil_store-types_templates-fetch.md +++ b/docs/kfutil_store-types_templates-fetch.md @@ -15,6 +15,7 @@ kfutil store-types templates-fetch [flags] ``` -b, --git-ref string The git branch or tag to reference when pulling store-types from the internet. (default "main") -h, --help help for templates-fetch + -r, --repo string The repository to pull store-type definitions from. (default "kfutil") ``` ### Options inherited from parent commands @@ -23,16 +24,20 @@ kfutil store-types templates-fetch [flags] --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -40,4 +45,4 @@ kfutil store-types templates-fetch [flags] * [kfutil store-types](kfutil_store-types.md) - Keyfactor certificate store types APIs and utilities. -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_stores.md b/docs/kfutil_stores.md index 895cb02..570b2db 100644 --- a/docs/kfutil_stores.md +++ b/docs/kfutil_stores.md @@ -18,16 +18,20 @@ A collections of APIs and utilities for interacting with Keyfactor certificate s --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -40,6 +44,5 @@ A collections of APIs and utilities for interacting with Keyfactor certificate s * [kfutil stores import](kfutil_stores_import.md) - Import a file with certificate store parameters and create them in keyfactor. * [kfutil stores inventory](kfutil_stores_inventory.md) - Commands related to certificate store inventory management * [kfutil stores list](kfutil_stores_list.md) - List certificate stores. -* [kfutil stores rot](kfutil_stores_rot.md) - Root of trust utility -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_stores_delete.md b/docs/kfutil_stores_delete.md index b1f6f10..731109b 100644 --- a/docs/kfutil_stores_delete.md +++ b/docs/kfutil_stores_delete.md @@ -25,16 +25,20 @@ kfutil stores delete [flags] --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -42,4 +46,4 @@ kfutil stores delete [flags] * [kfutil stores](kfutil_stores.md) - Keyfactor certificate stores APIs and utilities. -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_stores_export.md b/docs/kfutil_stores_export.md index 07a8a61..967bd76 100644 --- a/docs/kfutil_stores_export.md +++ b/docs/kfutil_stores_export.md @@ -26,16 +26,20 @@ kfutil stores export [flags] --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -43,4 +47,4 @@ kfutil stores export [flags] * [kfutil stores](kfutil_stores.md) - Keyfactor certificate stores APIs and utilities. -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_stores_get.md b/docs/kfutil_stores_get.md index 4592c8e..19beb3d 100644 --- a/docs/kfutil_stores_get.md +++ b/docs/kfutil_stores_get.md @@ -23,16 +23,20 @@ kfutil stores get [flags] --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -40,4 +44,4 @@ kfutil stores get [flags] * [kfutil stores](kfutil_stores.md) - Keyfactor certificate stores APIs and utilities. -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_stores_import.md b/docs/kfutil_stores_import.md index fe02d29..5616626 100644 --- a/docs/kfutil_stores_import.md +++ b/docs/kfutil_stores_import.md @@ -18,16 +18,20 @@ Tools for generating import templates and importing certificate stores --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -37,4 +41,4 @@ Tools for generating import templates and importing certificate stores * [kfutil stores import csv](kfutil_stores_import_csv.md) - Create certificate stores from CSV file. * [kfutil stores import generate-template](kfutil_stores_import_generate-template.md) - For generating a CSV template with headers for bulk store creation. -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_stores_import_csv.md b/docs/kfutil_stores_import_csv.md index 1766049..5e8237f 100644 --- a/docs/kfutil_stores_import_csv.md +++ b/docs/kfutil_stores_import_csv.md @@ -30,16 +30,20 @@ kfutil stores import csv --file --store-type-id --store-type-id --store-t --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -45,4 +49,4 @@ kfutil stores import generate-template --store-type-id --store-t * [kfutil stores import](kfutil_stores_import.md) - Import a file with certificate store parameters and create them in keyfactor. -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_stores_inventory.md b/docs/kfutil_stores_inventory.md index 39563a5..5445f58 100644 --- a/docs/kfutil_stores_inventory.md +++ b/docs/kfutil_stores_inventory.md @@ -18,16 +18,20 @@ Commands related to certificate store inventory management --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -38,4 +42,4 @@ Commands related to certificate store inventory management * [kfutil stores inventory remove](kfutil_stores_inventory_remove.md) - Removes a certificate from the certificate store inventory. * [kfutil stores inventory show](kfutil_stores_inventory_show.md) - Show the inventory of a certificate store. -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_stores_inventory_add.md b/docs/kfutil_stores_inventory_add.md index 84014d2..d783dd5 100644 --- a/docs/kfutil_stores_inventory_add.md +++ b/docs/kfutil_stores_inventory_add.md @@ -36,16 +36,20 @@ kfutil stores inventory add [flags] --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -53,4 +57,4 @@ kfutil stores inventory add [flags] * [kfutil stores inventory](kfutil_stores_inventory.md) - Commands related to certificate store inventory management -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_stores_inventory_remove.md b/docs/kfutil_stores_inventory_remove.md index 200d649..8c00d8c 100644 --- a/docs/kfutil_stores_inventory_remove.md +++ b/docs/kfutil_stores_inventory_remove.md @@ -32,16 +32,20 @@ kfutil stores inventory remove [flags] --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -49,4 +53,4 @@ kfutil stores inventory remove [flags] * [kfutil stores inventory](kfutil_stores_inventory.md) - Commands related to certificate store inventory management -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_stores_inventory_show.md b/docs/kfutil_stores_inventory_show.md index 76ec36d..5c337ea 100644 --- a/docs/kfutil_stores_inventory_show.md +++ b/docs/kfutil_stores_inventory_show.md @@ -26,16 +26,20 @@ kfutil stores inventory show [flags] --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -43,4 +47,4 @@ kfutil stores inventory show [flags] * [kfutil stores inventory](kfutil_stores_inventory.md) - Commands related to certificate store inventory management -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_stores_list.md b/docs/kfutil_stores_list.md index eb1eeb4..fc471da 100644 --- a/docs/kfutil_stores_list.md +++ b/docs/kfutil_stores_list.md @@ -22,16 +22,20 @@ kfutil stores list [flags] --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -39,4 +43,4 @@ kfutil stores list [flags] * [kfutil stores](kfutil_stores.md) - Keyfactor certificate stores APIs and utilities. -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/docs/kfutil_stores_rot.md b/docs/kfutil_stores_rot.md index d3d7d63..ff49440 100644 --- a/docs/kfutil_stores_rot.md +++ b/docs/kfutil_stores_rot.md @@ -38,6 +38,7 @@ kfutil stores rot reconcile --import-csv --hostname string Hostname to use for authenticating to Keyfactor Command. --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. --username string Username to use for authenticating to Keyfactor Command. @@ -50,4 +51,4 @@ kfutil stores rot reconcile --import-csv * [kfutil stores rot generate-template](kfutil_stores_rot_generate-template.md) - For generating Root Of Trust template(s) * [kfutil stores rot reconcile](kfutil_stores_rot_reconcile.md) - Reconcile either takes in or will generate an audit report and then add/remove certs as needed. -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 8-Sep-2024 diff --git a/docs/kfutil_stores_rot_audit.md b/docs/kfutil_stores_rot_audit.md index a03ce0d..7d8a321 100644 --- a/docs/kfutil_stores_rot_audit.md +++ b/docs/kfutil_stores_rot_audit.md @@ -38,6 +38,7 @@ kfutil stores rot audit [flags] --hostname string Hostname to use for authenticating to Keyfactor Command. --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. --username string Username to use for authenticating to Keyfactor Command. @@ -47,4 +48,4 @@ kfutil stores rot audit [flags] * [kfutil stores rot](kfutil_stores_rot.md) - Root of trust utility -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 8-Sep-2024 diff --git a/docs/kfutil_stores_rot_generate-template.md b/docs/kfutil_stores_rot_generate-template.md index 7c143a9..054ec02 100644 --- a/docs/kfutil_stores_rot_generate-template.md +++ b/docs/kfutil_stores_rot_generate-template.md @@ -36,6 +36,7 @@ kfutil stores rot generate-template [flags] --hostname string Hostname to use for authenticating to Keyfactor Command. --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. --username string Username to use for authenticating to Keyfactor Command. @@ -45,4 +46,4 @@ kfutil stores rot generate-template [flags] * [kfutil stores rot](kfutil_stores_rot.md) - Root of trust utility -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 8-Sep-2024 diff --git a/docs/kfutil_stores_rot_reconcile.md b/docs/kfutil_stores_rot_reconcile.md index 24c49be..6f45ed9 100644 --- a/docs/kfutil_stores_rot_reconcile.md +++ b/docs/kfutil_stores_rot_reconcile.md @@ -43,6 +43,7 @@ kfutil stores rot reconcile [flags] --hostname string Hostname to use for authenticating to Keyfactor Command. --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. --username string Username to use for authenticating to Keyfactor Command. @@ -52,4 +53,4 @@ kfutil stores rot reconcile [flags] * [kfutil stores rot](kfutil_stores_rot.md) - Root of trust utility -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 8-Sep-2024 diff --git a/docs/kfutil_version.md b/docs/kfutil_version.md index 45ebfb3..6df9ce0 100644 --- a/docs/kfutil_version.md +++ b/docs/kfutil_version.md @@ -22,16 +22,20 @@ kfutil version [flags] --api-path string API Path to use for authenticating to Keyfactor Command. (default is KeyfactorAPI) (default "KeyfactorAPI") --auth-provider-profile string The profile to use defined in the securely stored config. If not specified the config named 'default' will be used if it exists. (default "default") --auth-provider-type string Provider type choices: (azid) + --client-id string OAuth2 client-id to use for authenticating to Keyfactor Command. + --client-secret string OAuth2 client-secret to use for authenticating to Keyfactor Command. --config string Full path to config file in JSON format. (default is $HOME/.keyfactor/command_config.json) --debug Enable debugFlag logging. --domain string Domain to use for authenticating to Keyfactor Command. --exp Enable expEnabled features. (USE AT YOUR OWN RISK, these features are not supported and may change or be removed at any time.) --format text How to format the CLI output. Currently only text is supported. (default "text") --hostname string Hostname to use for authenticating to Keyfactor Command. - --log-insecure Log insecure API requests. (USE AT YOUR OWN RISK, this WILL log sensitive information to the console.) --no-prompt Do not prompt for any user input and assume defaults or environmental variables are set. + --offline Will not attempt to connect to GitHub for latest release information and resources. --password string Password to use for authenticating to Keyfactor Command. WARNING: Remember to delete your console history if providing kfcPassword here in plain text. --profile string Use a specific profile from your config file. If not specified the config named 'default' will be used if it exists. + --skip-tls-verify Disable TLS verification for API requests to Keyfactor Command. + --token-url string OAuth2 token endpoint full URL to use for authenticating to Keyfactor Command. --username string Username to use for authenticating to Keyfactor Command. ``` @@ -39,4 +43,4 @@ kfutil version [flags] * [kfutil](kfutil.md) - Keyfactor CLI utilities -###### Auto generated by spf13/cobra on 27-Feb-2024 +###### Auto generated by spf13/cobra on 12-Dec-2024 diff --git a/examples/auth/akv/akv_auth_v2.sh b/examples/auth/akv/akv_auth_v2.sh new file mode 100644 index 0000000..0399c74 --- /dev/null +++ b/examples/auth/akv/akv_auth_v2.sh @@ -0,0 +1,83 @@ +#!/usr/bin/env bash +set -e -o pipefail + +# Define the default values using environment variables +default_vault_name="${VAULT_NAME:-kfutil}" +default_secret_name="${SECRET_NAME:-integration-labs}" +echo "Default vault name: $default_vault_name" +echo "Default secret name: $default_secret_name" + +export METADATA_URL="http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://vault.azure.net" + +read_keyvault_secret_azure() { + local vault_name="$1" + local secret_name="$2" + + echo "Vault Name: $vault_name" + echo "Secret Name: $secret_name" + + # Make a request to the metadata endpoint + echo "Querying metadata endpoint for access token..." + echo "Metadata URL: $METADATA_URL" + token_json=$(curl -H "Metadata: true" $METADATA_URL) + + echo "Exporting access token to access_token variable..." + # Parse the access token from the response JSON + access_token=$(echo $token_json | jq -r .access_token) + + # Now you can use the $access_token to authenticate and access Azure Key Vault + echo "Access Token: $access_token" + + secret_url="https://${vault_name}.vault.azure.net/secrets/${secret_name}?api-version=7.0" + echo "Secret URL: $secret_url" + + # Get the secret value from Azure Key Vault + echo "Querying Azure Key Vault for secret value..." + secret_value=$(curl -H "Authorization: Bearer ${access_token}" "$secret_url" | jq -r .value) + + mkdir -p ~/.keyfactor + echo "${secret_value}" | jq -r . > "${secret_name}.json" + rm -f "${HOME}/.keyfactor/command_config.json" || true + echo "${secret_value}" | jq -r . > "${HOME}/.keyfactor/command_config.json" +} + +read_keyvault_secret_cli() { + local vault_name="$1" + local secret_name="$2" + + echo "Vault Name: $vault_name" + echo "Secret Name: $secret_name" + + # Check if the user is logged in to Azure CLI + if ! az account show &> /dev/null; then + echo "You are not logged in to Azure CLI. Please run 'az login' to continue." + exit 1 + fi + + # Get the secret value from Azure Key Vault using Azure CLI + echo "Querying Azure Key Vault for secret value using Azure CLI..." + secret_value=$(az keyvault secret show --vault-name "$vault_name" --name "$secret_name" --query value -o tsv) + + mkdir -p ~/.keyfactor + echo "${secret_value}" | jq -r . > "${secret_name}.json" + rm -f "${HOME}/.keyfactor/command_config.json" || true + echo "${secret_value}" | jq -r . > "${HOME}/.keyfactor/command_config.json" +} + +# Main script logic +if curl -H "Metadata: true" --max-time 5 $METADATA_URL &> /dev/null; then + # Running in Azure Cloud + read_keyvault_secret_azure "$default_vault_name" "$default_secret_name" +else + # Running on a workstation + if [[ $# -eq 0 ]]; then + # No arguments provided, use default values from environment variables + read_keyvault_secret_cli "$default_vault_name" "$default_secret_name" + elif [[ $# -eq 2 ]]; then + # Two arguments provided: vault_name and secret_name + read_keyvault_secret_cli "$1" "$2" + else + echo "Usage: $0 [vault_name secret_name]" + exit 1 + fi +fi \ No newline at end of file diff --git a/go.mod b/go.mod index eeaf731..e0f9465 100644 --- a/go.mod +++ b/go.mod @@ -1,48 +1,58 @@ module kfutil -go 1.21 +go 1.23 + +toolchain go1.23.2 require ( github.com/AlecAivazis/survey/v2 v2.3.7 - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.2 - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0 + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 github.com/Jeffail/gabs v1.4.0 - github.com/Keyfactor/keyfactor-go-client-sdk v1.0.2 - github.com/Keyfactor/keyfactor-go-client/v2 v2.2.7 + github.com/Keyfactor/keyfactor-auth-client-go v1.1.1-rc.0 + github.com/Keyfactor/keyfactor-go-client-sdk/v2 v2.0.0 + github.com/Keyfactor/keyfactor-go-client/v3 v3.0.0 github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 - github.com/creack/pty v1.1.21 + github.com/creack/pty v1.1.24 github.com/google/go-cmp v0.6.0 github.com/google/uuid v1.6.0 github.com/hinshun/vt10x v0.0.0-20220301184237-5011da428d02 github.com/joho/godotenv v1.5.1 - github.com/rs/zerolog v1.31.0 - github.com/spf13/cobra v1.8.0 + github.com/rs/zerolog v1.33.0 + github.com/spf13/cobra v1.8.1 github.com/spf13/pflag v1.0.5 - github.com/stretchr/testify v1.8.4 - golang.org/x/crypto v0.19.0 + github.com/stretchr/testify v1.10.0 + golang.org/x/crypto v0.30.0 + golang.org/x/term v0.27.0 gopkg.in/yaml.v3 v3.0.1 //github.com/google/go-cmp/cmp v0.5.9 ) require ( - github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 // indirect - github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.3 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.3.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/cpuguy83/go-md2man/v2 v2.0.4 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/golang-jwt/jwt/v5 v5.0.0 // indirect + github.com/fatih/color v1.13.0 // indirect + github.com/golang-jwt/jwt/v5 v5.2.1 // indirect + github.com/hashicorp/go-hclog v1.5.0 // indirect + github.com/hashicorp/terraform-plugin-log v0.9.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect - github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect + github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/spbsoluble/go-pkcs12 v0.3.3 // indirect - go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 // indirect - golang.org/x/net v0.20.0 // indirect - golang.org/x/sys v0.17.0 // indirect - golang.org/x/term v0.17.0 // indirect - golang.org/x/text v0.14.0 // indirect + go.mozilla.org/pkcs7 v0.9.0 // indirect + golang.org/x/net v0.32.0 // indirect + golang.org/x/oauth2 v0.24.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index f0537dc..8de6d9d 100644 --- a/go.sum +++ b/go.sum @@ -1,41 +1,57 @@ github.com/AlecAivazis/survey/v2 v2.3.7 h1:6I/u8FvytdGsgonrYsVn2t8t4QiRnh6QSTqkkhIiSjQ= github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.2 h1:c4k2FIYIh4xtwqrQwV0Ct1v5+ehlNXj5NI/MWVsiTkQ= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.2/go.mod h1:5FDJtLEO/GxwNgUxbwrY3LP0pEoThTQJtk2oysdXHxM= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0 h1:BMAjVKJM0U/CYF27gA0ZMmXGkOcvfFtD0oHVZ1TIPRI= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0/go.mod h1:1fXstnBMas5kzG+S3q8UoJcmyU6nUeunJcMDHcRYHhs= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 h1:LqbJ/WzJUwBf8UiaSzgX7aMclParm9/5Vgp+TY51uBQ= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2/go.mod h1:yInRyqWXAuaPrgI7p70+lDDgh3mlBohis29jGMISnmc= -github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1 h1:WpB/QDNLpMw72xHJc34BNNykqSOeEJDAWkhf0u12/Jk= -github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 h1:JZg6HRh6W6U4OLl6lk7BZ7BLisIzM9dG1R50zUk9C/M= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0/go.mod h1:YL1xnZ6QejvQHWJrX/AvhFl4WW4rqHVoKspWNVwFk0M= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 h1:B/dfvscEQtew9dVuoxqxrUKKv8Ih2f55PydknDamU+g= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0/go.mod h1:fiPSssYvltE08HJchL04dOy+RD4hgrjph0cwGGMntdI= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.0 h1:+m0M/LFxN43KvULkDNfdXOgrjtg6UYJPFBJyuEcRCAw= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.0/go.mod h1:PwOyop78lveYMRs6oCxjiVyBdyCgIYH6XHIVZO9/SFQ= +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/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/Jeffail/gabs v1.4.0 h1://5fYRRTq1edjfIrQGvdkcd22pkYUrHZ5YC/H2GJVAo= github.com/Jeffail/gabs v1.4.0/go.mod h1:6xMvQMK4k33lb7GUUpaAPh6nKMmemQeg5d4gn7/bOXc= -github.com/Keyfactor/keyfactor-go-client v1.4.3 h1:CmGvWcuIbDRFM0PfYOQH6UdtAgplvZBpU++KTU8iseg= -github.com/Keyfactor/keyfactor-go-client v1.4.3/go.mod h1:3ZymLNCaSazglcuYeNfm9nrzn22wcwLjIWURrnUygBo= -github.com/Keyfactor/keyfactor-go-client-sdk v1.0.2 h1:caLlzFCz2L4Dth/9wh+VlypFATmOMmCSQkCPKOKMxw8= -github.com/Keyfactor/keyfactor-go-client-sdk v1.0.2/go.mod h1:Z5pSk8YFGXHbKeQ1wTzVN8A4P/fZmtAwqu3NgBHbDOs= -github.com/Keyfactor/keyfactor-go-client/v2 v2.2.7 h1:fHZF5lDEWKQEI8QOPeseG/y9Bd4h2DhOiUWkNx+rKJU= -github.com/Keyfactor/keyfactor-go-client/v2 v2.2.7/go.mod h1:3mfxdcwntB532QIATokBEkBCH0eXN2G/cdMZtu9NwNg= +github.com/Keyfactor/keyfactor-auth-client-go v1.1.1-rc.0 h1:/N/7pBj/oTUM1cYga2NvKyA4q6nfE0acciJHZqKC9Ug= +github.com/Keyfactor/keyfactor-auth-client-go v1.1.1-rc.0/go.mod h1:yw92P9gSYVEyWkiUAJFsb7hjhXa8slN1+yTQgjSgovM= +github.com/Keyfactor/keyfactor-go-client-sdk/v2 v2.0.0 h1:ehk5crxEGVBwkC8yXsoQXcyITTDlgbxMEkANrl1dA2Q= +github.com/Keyfactor/keyfactor-go-client-sdk/v2 v2.0.0/go.mod h1:11WXGG9VVKSV0EPku1IswjHbGGpzHDKqD4pe2vD7vas= +github.com/Keyfactor/keyfactor-go-client/v3 v3.0.0 h1:yMChWRnnxmcgLt6kEQ3FZfteps05v/qot5KXLXxa6so= +github.com/Keyfactor/keyfactor-go-client/v3 v3.0.0/go.mod h1:HWb+S60YAALFVSfB8QuQ8ugjsjr+FHLQET0/4K7EVWw= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s= github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w= +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/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= -github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= -github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0= -github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= +github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= -github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= +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/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= -github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +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.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 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/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= +github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= +github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow= github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= github.com/hinshun/vt10x v0.0.0-20220301184237-5011da428d02 h1:AgcIVYPa6XJnU3phs104wLj8l5GEththEw6+F79YsIY= github.com/hinshun/vt10x v0.0.0-20220301184237-5011da428d02/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68= @@ -45,80 +61,105 @@ github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +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/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= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= -github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= -github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= +github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= +github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4= +github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA= +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/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A= -github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= +github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spbsoluble/go-pkcs12 v0.3.3 h1:3nh7IKn16RDpmrSMtOu1JvbB0XHYq1j+IsICdU1c7J4= github.com/spbsoluble/go-pkcs12 v0.3.3/go.mod h1:MAxKIUEIl/QVcua/I1L4Otyxl9UvLCCIktce2Tjz6Nw= -github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= -github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 h1:CCriYyAfq1Br1aIYettdHZTy8mBTIPo7We18TuO/bak= -go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= +go.mozilla.org/pkcs7 v0.9.0 h1:yM4/HS9dYv7ri2biPtxt8ikvB37a980dg69/pKmS+eI= +go.mozilla.org/pkcs7 v0.9.0/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY= +golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= +golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= +golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= +golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= -golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 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= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/install.sh b/install.sh index 240a661..43d16ad 100755 --- a/install.sh +++ b/install.sh @@ -146,6 +146,7 @@ getVersion() { printf "Could not retrieve the latest release tag information from %s: %s\n" "${remote_release_url}" "${releases_response}" exit 1 fi + echo "Latest release version is $VERSION" else # Clean up version if prefixed with 'v' VERSION=$(echo "$VERSION" | tr -d 'v') diff --git a/integration-manifest.json b/integration-manifest.json index 61ae251..11b12e4 100644 --- a/integration-manifest.json +++ b/integration-manifest.json @@ -6,6 +6,7 @@ "description": "`kfutil` is a go-lang CLI wrapper for Keyfactor Command API. It also includes other utility/helper functions around automating common Keyfactor Command operations.", "support_level": "kf-community", "link_github": false, - "update_catalog": false + "update_catalog": false, + "release_dir": "bin" } diff --git a/main.go b/main.go index 69f352b..15e0228 100644 --- a/main.go +++ b/main.go @@ -15,6 +15,9 @@ package main import ( + _ "embed" + + "github.com/spf13/cobra/doc" "kfutil/cmd" ) @@ -26,5 +29,10 @@ func main() { // docs() // os.Exit(0) //} + cmd.Execute() } + +func docs() { + doc.GenMarkdownTree(cmd.RootCmd, "./docs") +} diff --git a/pkg/version/version.go b/pkg/version/version.go index 354b351..3294bc0 100644 --- a/pkg/version/version.go +++ b/pkg/version/version.go @@ -14,4 +14,4 @@ package version -const VERSION = "1.4.0" +const VERSION = "1.6.0" diff --git a/readme_source.md b/readme_source.md index 0cc81d6..50a45c0 100644 --- a/readme_source.md +++ b/readme_source.md @@ -1,7 +1,9 @@ ## Quickstart ### Linux/MacOS + #### Prerequisites: + - [jq](https://stedolan.github.io/jq/download/) CLI tool, used to parse JSON output. - Either - [curl](https://curl.se/download.html) CLI tool, used to download the release files. @@ -11,15 +13,19 @@ - `$HOME/.local/bin` in your `$PATH` and exists if not running as root, else `/usr/local/bin` if running as root. #### Installation: + ```bash bash <(curl -s https://raw.githubusercontent.com/Keyfactor/kfutil/main/install.sh) ```` ### Windows + #### Prerequisites: + - Powershell 5.1 or later #### Installation: + ```powershell Invoke-WebRequest -Uri "https://raw.githubusercontent.com/Keyfactor/kfutil/main/install.ps1" -OutFile "install.ps1" # Install kfutil to $HOME/AppData/Local/Microsoft/WindowsApps. @@ -27,40 +33,106 @@ Invoke-WebRequest -Uri "https://raw.githubusercontent.com/Keyfactor/kfutil/main/ .\install.ps1 ``` -## Environmental Variables +## Environment Variables + +### Global + +| Name | Description | Default | +|-------------------------------|-----------------------------------------------------------------------------------------------------------------|----------------------------------------| +| KEYFACTOR_HOSTNAME | Keyfactor Command hostname without protocol and port | | +| KEYFACTOR_PORT | Keyfactor Command port | `443` | +| KEYFACTOR_API_PATH | Keyfactor Command API Path | `KeyfactorAPI` | +| KEYFACTOR_SKIP_VERIFY | Skip TLS verification when connecting to Keyfactor Command | `false` | +| KEYFACTOR_CA_CERT | Either a file path or PEM encoded string to a CA certificate to trust when communicating with Keyfactor Command | | +| KEYFACTOR_CLIENT_TIMEOUT | Timeout for HTTP client requests to Keyfactor Command | `60s` | +| KEYFACTOR_AUTH_CONFIG_FILE | Path to a JSON file containing the authentication configuration | `$HOME/.keyfactor/command_config.json` | +| KEYFACTOR_AUTH_CONFIG_PROFILE | Profile to use from the authentication configuration file | `default` | + +### Basic Auth + +Currently `Basic Authentication` via `Active Directory` is the *ONLY* supported method of `Basic Authentication`. + +| Name | Description | Default | +|--------------------|---------------------------------------------------------------------------------------------|---------| +| KEYFACTOR_USERNAME | Active Directory username to authenticate to Keyfactor Command API | | +| KEYFACTOR_PASSWORD | Password associated with Active Directory username to authenticate to Keyfactor Command API | | +| KEYFACTOR_DOMAIN | Active Directory domain of user. Can be implied from username if it contains `@` or `\\` | | + +### oAuth Credentials + +| Name | Description | Default | +|------------------------------|---------------------------------------------------------------------------------------------------------------------------------|----------| +| KEYFACTOR_AUTH_CLIENT_ID | Keyfactor Auth Client ID | | +| KEYFACTOR_AUTH_CLIENT_SECRET | Keyfactor Auth Client Secret | | +| KEYFACTOR_AUTH_TOKEN_URL | URL to request an access token from Keyfactor Auth | | +| KEYFACTOR_AUTH_SCOPES | Scopes to request when authenticating to Keyfactor Command API. Each scope MUST be separated by `,` | `openid` | +| KEYFACTOR_AUTH_AUDIENCE | Audience to request when authenticating to Keyfactor Command API | | +| KEYFACTOR_AUTH_ACCESS_TOKEN | Access token to use to authenticate to Keyfactor Command API. This can be supplied directly or generated via client credentials | | +| KEYFACTOR_AUTH_CA_CERT | Either a file path or PEM encoded string to a CA certificate to use when connecting to Keyfactor Auth | | + +### kfutil specific All the variables listed below need to be set in your environment. The `kfutil` command will look for these variables -and use them if they are set. If they are not set, the utility will fail to connect to Keyfactor. - -| Variable Name | Description | -|--------------------|------------------------------------------------------------------------------------------| -| KEYFACTOR_HOSTNAME | The hostname of your Keyfactor instance. ex: `my.domain.com` | -| KEYFACTOR_USERNAME | The username to use to connect to Keyfactor. Do not include the domain. ex: `myusername` | -| KEYFACTOR_PASSWORD | The password to use to connect to Keyfactor. ex: `mypassword` | -| KEYFACTOR_DOMAIN | The domain to use to connect to Keyfactor. ex: `mydomain` | -| KEYFACTOR_API_PATH | The path to the Keyfactor API. Defaults to `/KeyfactorAPI`. | -| KFUTIL_EXP | Set to `1` or `true` to enable experimental features. | -| KFUTIL_DEBUG | Set to `1` or `true` to enable debug logging. | +and use them if they are set. + +| Variable Name | Description | +|---------------|-------------------------------------------------------| +| KFUTIL_EXP | Set to `1` or `true` to enable experimental features. | +| KFUTIL_DEBUG | Set to `1` or `true` to enable debug logging. | ### Linux/MacOS: +Below are examples of setting the environment variables in Linux/MacOS to be used with `kfutil`. + +#### Active Directory Basic Authentication + +This is the minimum required configuration to authenticate to Keyfactor Command using Active Directory username, +password auth. ```bash export KEYFACTOR_HOSTNAME="" -export KEYFACTOR_USERNAME="" # Do not include domain +export KEYFACTOR_USERNAME="" export KEYFACTOR_PASSWORD="" -export KEYFACTOR_DOMAIN="" +export KEYFACTOR_DOMAIN="" # Optional if username contains domain ``` -Additional variables: +#### oAuth Client Credentials + +This is the minimum required configuration to authenticate to Keyfactor Command using oAuth client credentials. +```bash +export KEYFACTOR_HOSTNAME="" +export KEYFACTOR_AUTH_CLIENT_ID="