From 299d1319aa61a69bd73e9a2bf48235ee442da63d Mon Sep 17 00:00:00 2001
From: Sean <1661003+spbsoluble@users.noreply.github.com>
Date: Thu, 7 Nov 2024 09:00:44 -0800
Subject: [PATCH 01/49] feat(auth): Add support for OAuth2 client credentials
(#224)
---
.github/config/.terraform.lock.hcl | 25 +
.github/config/MODULE.MD | 57 +
.github/config/Makefile | 26 +
.github/config/README.md | 107 +
.github/config/environments.tf | 96 +
.github/config/int1230c_ad.tf | 16 +
.github/config/int1230c_oauth.tf | 33 +
.github/config/providers.tf | 20 +
.github/config/repo.tf | 3 +
.github/config/variables.tf | 85 +
.github/workflows/container.yml | 332 +--
.../keyfactor-bootstrap-workflow.yml | 420 +--
.github/workflows/tests.yml | 634 ++--
.github/workflows/update-stores.yml | 394 +--
CHANGELOG.md | 6 +
README.md | 114 +-
cmd/certificates.go | 11 +-
cmd/containers.go | 12 +-
cmd/export.go | 41 +-
cmd/helm_uo.go | 27 +-
cmd/helpers.go | 3 +
cmd/import.go | 140 +-
cmd/inventory.go | 337 ++-
cmd/login.go | 723 +++--
cmd/login_test.go | 437 ++-
cmd/orchs.go | 59 +-
cmd/pam.go | 76 +-
cmd/root.go | 710 +++--
cmd/rot.go | 2593 +++++++++--------
cmd/storeTypes.go | 19 +-
cmd/storeTypes_get.go | 49 +-
cmd/stores.go | 9 +-
cmd/storesBulkOperations.go | 162 +-
docs/kfutil.md | 5 +-
docs/kfutil_completion.md | 5 +-
docs/kfutil_completion_bash.md | 5 +-
docs/kfutil_completion_fish.md | 5 +-
docs/kfutil_completion_powershell.md | 5 +-
docs/kfutil_completion_zsh.md | 5 +-
docs/kfutil_containers.md | 5 +-
docs/kfutil_containers_get.md | 5 +-
docs/kfutil_containers_list.md | 5 +-
docs/kfutil_export.md | 5 +-
docs/kfutil_helm.md | 5 +-
docs/kfutil_helm_uo.md | 5 +-
docs/kfutil_import.md | 5 +-
docs/kfutil_login.md | 17 +-
docs/kfutil_logout.md | 5 +-
docs/kfutil_orchs.md | 5 +-
docs/kfutil_orchs_approve.md | 5 +-
docs/kfutil_orchs_disapprove.md | 5 +-
docs/kfutil_orchs_ext.md | 5 +-
docs/kfutil_orchs_get.md | 5 +-
docs/kfutil_orchs_list.md | 5 +-
docs/kfutil_orchs_logs.md | 5 +-
docs/kfutil_orchs_reset.md | 5 +-
docs/kfutil_pam.md | 5 +-
docs/kfutil_pam_create.md | 5 +-
docs/kfutil_pam_delete.md | 5 +-
docs/kfutil_pam_get.md | 5 +-
docs/kfutil_pam_list.md | 5 +-
docs/kfutil_pam_types-create.md | 5 +-
docs/kfutil_pam_types-list.md | 5 +-
docs/kfutil_pam_update.md | 5 +-
docs/kfutil_status.md | 5 +-
docs/kfutil_store-types.md | 5 +-
docs/kfutil_store-types_create.md | 5 +-
docs/kfutil_store-types_delete.md | 5 +-
docs/kfutil_store-types_get.md | 5 +-
docs/kfutil_store-types_list.md | 5 +-
docs/kfutil_store-types_templates-fetch.md | 5 +-
docs/kfutil_stores.md | 6 +-
docs/kfutil_stores_delete.md | 5 +-
docs/kfutil_stores_export.md | 5 +-
docs/kfutil_stores_get.md | 5 +-
docs/kfutil_stores_import.md | 5 +-
docs/kfutil_stores_import_csv.md | 5 +-
.../kfutil_stores_import_generate-template.md | 5 +-
docs/kfutil_stores_inventory.md | 5 +-
docs/kfutil_stores_inventory_add.md | 5 +-
docs/kfutil_stores_inventory_remove.md | 5 +-
docs/kfutil_stores_inventory_show.md | 5 +-
docs/kfutil_stores_list.md | 5 +-
docs/kfutil_version.md | 5 +-
go.mod | 16 +-
go.sum | 38 +-
integration-manifest.json | 3 +-
pkg/version/version.go | 2 +-
readme_source.md | 92 +-
89 files changed, 5292 insertions(+), 2903 deletions(-)
create mode 100644 .github/config/.terraform.lock.hcl
create mode 100644 .github/config/MODULE.MD
create mode 100644 .github/config/Makefile
create mode 100644 .github/config/README.md
create mode 100644 .github/config/environments.tf
create mode 100644 .github/config/int1230c_ad.tf
create mode 100644 .github/config/int1230c_oauth.tf
create mode 100644 .github/config/providers.tf
create mode 100644 .github/config/repo.tf
create mode 100644 .github/config/variables.tf
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..4e1d569
--- /dev/null
+++ b/.github/config/MODULE.MD
@@ -0,0 +1,57 @@
+## 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\_hostname\_12\_3\_0\_OAUTH\_CLEAN](#input\_keyfactor\_hostname\_12\_3\_0\_OAUTH\_CLEAN) | 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 |
+
+## 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..3a92963
--- /dev/null
+++ b/.github/config/README.md
@@ -0,0 +1,107 @@
+# 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\_hostname\_12\_3\_0\_OAUTH\_CLEAN](#input\_keyfactor\_hostname\_12\_3\_0\_OAUTH\_CLEAN) | 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 |
+
+## 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..52b5bf3
--- /dev/null
+++ b/.github/config/environments.tf
@@ -0,0 +1,96 @@
+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"))
+}
+
+
+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.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
+ keyfactor_config_file = base64encode(file("${path.module}/command_config.json"))
+}
+
+
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..e429892 100644
--- a/.github/workflows/container.yml
+++ b/.github/workflows/container.yml
@@ -1,166 +1,166 @@
-name: Build and Release Container
-on:
- release:
- types: [released]
- push:
- branches:
- - '*'
-
-env:
- REGISTRY: ghcr.io
-
-jobs:
- build:
- name: Build Containers
- runs-on: ubuntu-latest
- strategy:
- fail-fast: false
- matrix:
- platform:
- - linux/386
- - linux/amd64
- - linux/arm/v6
- - linux/arm/v7
- - linux/arm64/v8
- - linux/ppc64le
- - linux/s390x
-
- permissions:
- contents: read
- packages: write
-
- steps:
-
- - name: Set IMAGE_NAME
- run: |
- echo "IMAGE_NAME=${GITHUB_REPOSITORY,,}" >>${GITHUB_ENV}
-
- # Checkout code
- # https://github.com/actions/checkout
- - name: Checkout code
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
-
- # Extract metadata (tags, labels) for Docker
- # If the pull request is not merged, do not include the edge tag and only include the sha tag.
- # https://github.com/docker/metadata-action
- - name: Extract Docker metadata
- uses: docker/metadata-action@31cebacef4805868f9ce9a0cb03ee36c32df2ac4 # v5.3.0
- with:
- images: |
- ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- tags: |
- type=semver,pattern=v{{version}}
- type=sha
-
- # Set up QEMU
- # https://github.com/docker/setup-qemu-action
- - name: Set up QEMU
- uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0
-
- # Set up BuildKit Docker container builder to be able to build
- # multi-platform images and export cache
- # https://github.com/docker/setup-buildx-action
- - name: Set up Docker Buildx
- uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
-
- # Login to Docker registry
- # https://github.com/docker/login-action
- - name: Log into registry ${{ env.REGISTRY }}
- uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
- with:
- registry: ${{ env.REGISTRY }}
- username: ${{ github.actor }}
- password: ${{ secrets.GITHUB_TOKEN }}
-
- # Build and push Docker image with Buildx
- # https://github.com/docker/build-push-action
- - name: Build and push Docker image
- id: build
- uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 # v5.1.0
- with:
- context: .
- platforms: ${{ matrix.platform }}
- labels: ${{ env.DOCKER_METADATA_OUTPUT_LABELS }}
- push: true
- outputs: type=image,name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true
-
- # Export digest
- - name: Export digest
- run: |
- mkdir -p /tmp/digests
- digest="${{ steps.build.outputs.digest }}"
- touch "/tmp/digests/${digest#sha256:}"
-
- # Upload digest
- - name: Upload digest
- uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
- with:
- name: digests
- path: /tmp/digests/*
- if-no-files-found: error
- retention-days: 1
-
- merge:
- name: Merge Container Manifests
- runs-on: ubuntu-latest
- needs:
- - build
- steps:
- - name: Set IMAGE_NAME
- run: |
- echo "IMAGE_NAME=${GITHUB_REPOSITORY,,}" >>${GITHUB_ENV}
-
- # Download digests
- # https://github.com/actions/download-artifact
- - name: Download digests
- uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
- with:
- name: digests
- path: /tmp/digests
-
- # Set up BuildKit Docker container builder to be able to build
- # multi-platform images and export cache
- # https://github.com/docker/setup-buildx-action
- - name: Set up Docker Buildx
- uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
-
- # Extract metadata (tags, labels) for Docker
- # If the pull request is not merged, do not include the edge tag and only include the sha tag.
- # https://github.com/docker/metadata-action
- - name: Extract Docker metadata
- uses: docker/metadata-action@31cebacef4805868f9ce9a0cb03ee36c32df2ac4 # v5.3.0
- with:
- images: |
- ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- tags: |
- type=semver,pattern=v{{version}}
- type=sha
-
- # Login to Docker registry
- # https://github.com/docker/login-action
- - name: Log into registry ${{ env.REGISTRY }}
- uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
- with:
- registry: ${{ env.REGISTRY }}
- username: ${{ github.actor }}
- password: ${{ secrets.GITHUB_TOKEN }}
-
- # Create manifest list and push
- - name: Create manifest list and push
- working-directory: /tmp/digests
- run: |
- # 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"
-
- - name: Inspect image
- run: |
- docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.DOCKER_METADATA_OUTPUT_VERSION }}
\ No newline at end of file
+#name: Build and Release Container
+#on:
+# release:
+# types: [released]
+# push:
+# branches:
+# - '*'
+#
+#env:
+# REGISTRY: ghcr.io
+#
+#jobs:
+# build:
+# name: Build Containers
+# runs-on: ubuntu-latest
+# strategy:
+# fail-fast: false
+# matrix:
+# platform:
+# - linux/386
+# - linux/amd64
+# - linux/arm/v6
+# - linux/arm/v7
+# - linux/arm64/v8
+# - linux/ppc64le
+# - linux/s390x
+#
+# permissions:
+# contents: read
+# packages: write
+#
+# steps:
+#
+# - name: Set IMAGE_NAME
+# run: |
+# echo "IMAGE_NAME=${GITHUB_REPOSITORY,,}" >>${GITHUB_ENV}
+#
+# # Checkout code
+# # https://github.com/actions/checkout
+# - name: Checkout code
+# uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
+#
+# # Extract metadata (tags, labels) for Docker
+# # If the pull request is not merged, do not include the edge tag and only include the sha tag.
+# # https://github.com/docker/metadata-action
+# - name: Extract Docker metadata
+# uses: docker/metadata-action@31cebacef4805868f9ce9a0cb03ee36c32df2ac4 # v5.3.0
+# with:
+# images: |
+# ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
+# tags: |
+# type=semver,pattern=v{{version}}
+# type=sha
+#
+# # Set up QEMU
+# # https://github.com/docker/setup-qemu-action
+# - name: Set up QEMU
+# uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0
+#
+# # Set up BuildKit Docker container builder to be able to build
+# # multi-platform images and export cache
+# # https://github.com/docker/setup-buildx-action
+# - name: Set up Docker Buildx
+# uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
+#
+# # Login to Docker registry
+# # https://github.com/docker/login-action
+# - name: Log into registry ${{ env.REGISTRY }}
+# uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
+# with:
+# registry: ${{ env.REGISTRY }}
+# username: ${{ github.actor }}
+# password: ${{ secrets.GITHUB_TOKEN }}
+#
+# # Build and push Docker image with Buildx
+# # https://github.com/docker/build-push-action
+# - name: Build and push Docker image
+# id: build
+# uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 # v5.1.0
+# with:
+# context: .
+# platforms: ${{ matrix.platform }}
+# labels: ${{ env.DOCKER_METADATA_OUTPUT_LABELS }}
+# push: true
+# outputs: type=image,name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true
+#
+# # Export digest
+# - name: Export digest
+# run: |
+# mkdir -p /tmp/digests
+# digest="${{ steps.build.outputs.digest }}"
+# touch "/tmp/digests/${digest#sha256:}"
+#
+# # Upload digest
+# - name: Upload digest
+# uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
+# with:
+# name: digests
+# path: /tmp/digests/*
+# if-no-files-found: error
+# retention-days: 1
+#
+# merge:
+# name: Merge Container Manifests
+# runs-on: ubuntu-latest
+# needs:
+# - build
+# steps:
+# - name: Set IMAGE_NAME
+# run: |
+# echo "IMAGE_NAME=${GITHUB_REPOSITORY,,}" >>${GITHUB_ENV}
+#
+# # Download digests
+# # https://github.com/actions/download-artifact
+# - name: Download digests
+# uses: actions/download-artifact@9bc31d5ccc31df68ecc42ccf4149144866c47d8a # v3.0.2
+# with:
+# name: digests
+# path: /tmp/digests
+#
+# # Set up BuildKit Docker container builder to be able to build
+# # multi-platform images and export cache
+# # https://github.com/docker/setup-buildx-action
+# - name: Set up Docker Buildx
+# uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
+#
+# # Extract metadata (tags, labels) for Docker
+# # If the pull request is not merged, do not include the edge tag and only include the sha tag.
+# # https://github.com/docker/metadata-action
+# - name: Extract Docker metadata
+# uses: docker/metadata-action@31cebacef4805868f9ce9a0cb03ee36c32df2ac4 # v5.3.0
+# with:
+# images: |
+# ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
+# tags: |
+# type=semver,pattern=v{{version}}
+# type=sha
+#
+# # Login to Docker registry
+# # https://github.com/docker/login-action
+# - name: Log into registry ${{ env.REGISTRY }}
+# uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
+# with:
+# registry: ${{ env.REGISTRY }}
+# username: ${{ github.actor }}
+# password: ${{ secrets.GITHUB_TOKEN }}
+#
+# # Create manifest list and push
+# - name: Create manifest list and push
+# working-directory: /tmp/digests
+# run: |
+# # 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"
+#
+# - name: Inspect image
+# run: |
+# docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.DOCKER_METADATA_OUTPUT_VERSION }}
\ No newline at end of file
diff --git a/.github/workflows/keyfactor-bootstrap-workflow.yml b/.github/workflows/keyfactor-bootstrap-workflow.yml
index a4c7eaf..78f5d45 100644
--- a/.github/workflows/keyfactor-bootstrap-workflow.yml
+++ b/.github/workflows/keyfactor-bootstrap-workflow.yml
@@ -1,226 +1,226 @@
-name: Keyfactor Bootstrap Workflow
-
-on:
- workflow_dispatch:
- pull_request:
- types: [ opened, closed, synchronize, edited, reopened ]
- push:
- create:
- branches:
- - 'release-*.*'
-
-jobs:
- get-versions:
- runs-on: ubuntu-latest
- outputs:
- PR_BASE_REF: ${{ steps.set-outputs.outputs.PR_BASE_REF }}
- PR_COMMIT_SHA: ${{ steps.set-outputs.outputs.PR_COMMIT_SHA }}
- GITHUB_SHA: ${{ steps.set-outputs.outputs.GITHUB_SHA }}
- PR_BASE_TAG: ${{ steps.set-outputs.outputs.PR_BASE_TAG }}
- IS_FULL_RELEASE: ${{ steps.set-outputs.outputs.IS_FULL_RELEASE }}
- IS_PRE_RELEASE: ${{ steps.set-outputs.outputs.IS_PRE_RELEASE }}
- INC_LEVEL: ${{ steps.set-outputs.outputs.INC_LEVEL }}
- IS_RELEASE_BRANCH: ${{ steps.set-outputs.outputs.IS_RELEASE_BRANCH }}
- IS_HOTFIX: ${{ steps.set-outputs.outputs.IS_HOTFIX }}
- LATEST_TAG: ${{ steps.set-outputs.outputs.LATEST_TAG }}
- NEXT_VERSION: ${{ steps.set-outputs.outputs.NEW_PKG_VERSION }}
-
- steps:
- - name: Check out the code
- uses: actions/checkout@v3
- with:
- token: ${{ secrets.V2BUILDTOKEN}}
-
- - name: Display base.ref from Pull Request
- if: github.event_name == 'pull_request'
- id: display-from-pr
- run: |
- echo "Event: ${{ github.event_name }}" | tee -a $GITHUB_STEP_SUMMARY
- echo "Event Action: ${{ github.event.action }}" | tee -a $GITHUB_STEP_SUMMARY
- echo "PR_BASE_REF=${{ github.event.pull_request.base.ref }}" | tee -a "$GITHUB_ENV" | tee -a $GITHUB_STEP_SUMMARY
- echo "PR_STATE=${{ github.event.pull_request.state }}" | tee -a "$GITHUB_ENV" | tee -a $GITHUB_STEP_SUMMARY
- echo "PR_MERGED=${{ github.event.pull_request.merged }}" | tee -a "$GITHUB_ENV" | tee -a $GITHUB_STEP_SUMMARY
- echo "PR_COMMIT_SHA=${{ github.event.pull_request.merge_commit_sha }}" | tee -a "$GITHUB_ENV" | tee -a $GITHUB_STEP_SUMMARY
- echo "GITHUB_SHA=${{ github.sha }}" | tee -a "$GITHUB_ENV" | tee -a $GITHUB_STEP_SUMMARY
- baseref="${{ github.event.pull_request.base.ref }}"
- basetag="${baseref#release-}"
- echo "PR_BASE_TAG=$basetag" | tee -a "$GITHUB_ENV" | tee -a $GITHUB_STEP_SUMMARY
-
- - name: Display base_ref from Push Event
- if: github.event_name == 'push'
- id: display-from-push
- run: |
- echo "Branch Ref: ${{ github.ref }}" | tee -a $GITHUB_STEP_SUMMARY
- echo "Event: ${{ github.event_name }}" | tee -a $GITHUB_STEP_SUMMARY
- echo "github.sha: ${{ github.sha }}" | tee -a $GITHUB_STEP_SUMMARY
-
- - name: Find Latest Tag
- if: github.event_name == 'pull_request'
- id: find-latest-tag
- run: |
- prbasetag="${{env.PR_BASE_TAG}}"
- git fetch --tags
- if [[ -n `git tag` ]]; then
- echo "Setting vars"
- allBranchTags=`git tag --sort=-v:refname | grep "^$prbasetag" || echo ""`
- allRepoTags=`git tag --sort=-v:refname`
- branchTagBase=`git tag --sort=-v:refname | grep "^$prbasetag" | grep -o '^[0-9.]*' | head -n 1 || echo ""`
- latestTagBase=`git tag --sort=-v:refname | grep -o '^[0-9.]*' | head -n 1`
- latestBranchTag=`git tag --sort=-v:refname | grep "^$prbasetag" | grep "^$branchTagBase" | head -n 1 || echo ""`
- latestReleasedTag=`git tag --sort=-v:refname | grep "^$prbasetag" | grep "^$branchTagBase$" | head -n 1 || echo ""`
-
- # If the *TagBase values are not found in the list of tags, it means no final release was produced, and the latest*Tag vars will be empty
- if [[ -z "$latestReleasedTag" ]]; then
- latestTag="$latestBranchTag"
- else
- latestTag="$latestReleasedTag"
- fi
- echo "LATEST_TAG=${latestTag}" | tee -a "$GITHUB_ENV"
-
- if [[ "$latestTagBase" == *"$branchTagBase" ]]; then
- hf="False"
- else
- hf="True"
- fi
-
- # 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
- echo "No tags exist in this repo"
- echo "LATEST_TAG=" | tee -a "$GITHUB_ENV"
- fi
- - name: Set Outputs
- id: set-outputs
- run: |
- echo "PR_BASE_REF=${{ env.PR_BASE_REF }}" | tee -a "$GITHUB_OUTPUT"
- echo "PR_STATE=${{ env.PR_STATE }}"
- echo "PR_MERGED=${{ env.PR_MERGED }}"
- if [[ "${{ env.PR_STATE }}" == "closed" && "${{ env.PR_MERGED }}" == "true" && "${{ env.PR_COMMIT_SHA }}" == "${{ env.GITHUB_SHA }}" ]]; then
- echo "IS_FULL_RELEASE=True" | tee -a "$GITHUB_OUTPUT"
- echo "INC_LEVEL=patch" | tee -a "$GITHUB_OUTPUT"
- fi
- if [[ "${{ env.PR_STATE }}" == "open" ]]; then
- echo "IS_PRE_RELEASE=True" | tee -a "$GITHUB_OUTPUT" | tee -a "$GITHUB_ENV"
- echo "INC_LEVEL=prerelease" | tee -a "$GITHUB_OUTPUT"
- fi
- if [[ "${{ env.PR_BASE_REF }}" == "release-"* ]]; then
- echo "IS_RELEASE_BRANCH=True" | tee -a "$GITHUB_OUTPUT" | tee -a "$GITHUB_ENV"
- fi
- echo "PR_COMMIT_SHA=${{ env.PR_COMMIT_SHA }}" | tee -a "$GITHUB_OUTPUT"
- echo "GITHUB_SHA=${{ env.GITHUB_SHA }}" | tee -a "$GITHUB_OUTPUT"
- echo "PR_BASE_TAG=${{ env.PR_BASE_TAG }}" | tee -a "$GITHUB_OUTPUT"
- 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 }}
+#name: Keyfactor Bootstrap Workflow
+#
+#on:
+# workflow_dispatch:
+# pull_request:
+# types: [ opened, closed, synchronize, edited, reopened ]
+# push:
+# create:
+# branches:
+# - 'release-*.*'
+#
+#jobs:
+# get-versions:
# runs-on: ubuntu-latest
+# outputs:
+# PR_BASE_REF: ${{ steps.set-outputs.outputs.PR_BASE_REF }}
+# PR_COMMIT_SHA: ${{ steps.set-outputs.outputs.PR_COMMIT_SHA }}
+# GITHUB_SHA: ${{ steps.set-outputs.outputs.GITHUB_SHA }}
+# PR_BASE_TAG: ${{ steps.set-outputs.outputs.PR_BASE_TAG }}
+# IS_FULL_RELEASE: ${{ steps.set-outputs.outputs.IS_FULL_RELEASE }}
+# IS_PRE_RELEASE: ${{ steps.set-outputs.outputs.IS_PRE_RELEASE }}
+# INC_LEVEL: ${{ steps.set-outputs.outputs.INC_LEVEL }}
+# IS_RELEASE_BRANCH: ${{ steps.set-outputs.outputs.IS_RELEASE_BRANCH }}
+# IS_HOTFIX: ${{ steps.set-outputs.outputs.IS_HOTFIX }}
+# LATEST_TAG: ${{ steps.set-outputs.outputs.LATEST_TAG }}
+# NEXT_VERSION: ${{ steps.set-outputs.outputs.NEW_PKG_VERSION }}
+#
# 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 != ''
+# token: ${{ secrets.V2BUILDTOKEN}}
+#
+# - name: Display base.ref from Pull Request
+# if: github.event_name == 'pull_request'
+# id: display-from-pr
# 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"
+# echo "Event: ${{ github.event_name }}" | tee -a $GITHUB_STEP_SUMMARY
+# echo "Event Action: ${{ github.event.action }}" | tee -a $GITHUB_STEP_SUMMARY
+# echo "PR_BASE_REF=${{ github.event.pull_request.base.ref }}" | tee -a "$GITHUB_ENV" | tee -a $GITHUB_STEP_SUMMARY
+# echo "PR_STATE=${{ github.event.pull_request.state }}" | tee -a "$GITHUB_ENV" | tee -a $GITHUB_STEP_SUMMARY
+# echo "PR_MERGED=${{ github.event.pull_request.merged }}" | tee -a "$GITHUB_ENV" | tee -a $GITHUB_STEP_SUMMARY
+# echo "PR_COMMIT_SHA=${{ github.event.pull_request.merge_commit_sha }}" | tee -a "$GITHUB_ENV" | tee -a $GITHUB_STEP_SUMMARY
+# echo "GITHUB_SHA=${{ github.sha }}" | tee -a "$GITHUB_ENV" | tee -a $GITHUB_STEP_SUMMARY
+# baseref="${{ github.event.pull_request.base.ref }}"
+# basetag="${baseref#release-}"
+# echo "PR_BASE_TAG=$basetag" | tee -a "$GITHUB_ENV" | tee -a $GITHUB_STEP_SUMMARY
#
-# - name: Get Package Version
-# id: get-pkg-version
+# - name: Display base_ref from Push Event
+# if: github.event_name == 'push'
+# id: display-from-push
# 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
+# echo "Branch Ref: ${{ github.ref }}" | tee -a $GITHUB_STEP_SUMMARY
+# echo "Event: ${{ github.event_name }}" | tee -a $GITHUB_STEP_SUMMARY
+# echo "github.sha: ${{ github.sha }}" | tee -a $GITHUB_STEP_SUMMARY
+#
+# - name: Find Latest Tag
+# if: github.event_name == 'pull_request'
+# id: find-latest-tag
# 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"
+# prbasetag="${{env.PR_BASE_TAG}}"
+# git fetch --tags
+# if [[ -n `git tag` ]]; then
+# echo "Setting vars"
+# allBranchTags=`git tag --sort=-v:refname | grep "^$prbasetag" || echo ""`
+# allRepoTags=`git tag --sort=-v:refname`
+# branchTagBase=`git tag --sort=-v:refname | grep "^$prbasetag" | grep -o '^[0-9.]*' | head -n 1 || echo ""`
+# latestTagBase=`git tag --sort=-v:refname | grep -o '^[0-9.]*' | head -n 1`
+# latestBranchTag=`git tag --sort=-v:refname | grep "^$prbasetag" | grep "^$branchTagBase" | head -n 1 || echo ""`
+# latestReleasedTag=`git tag --sort=-v:refname | grep "^$prbasetag" | grep "^$branchTagBase$" | head -n 1 || echo ""`
+#
+# # If the *TagBase values are not found in the list of tags, it means no final release was produced, and the latest*Tag vars will be empty
+# if [[ -z "$latestReleasedTag" ]]; then
+# latestTag="$latestBranchTag"
+# else
+# latestTag="$latestReleasedTag"
+# fi
+# echo "LATEST_TAG=${latestTag}" | tee -a "$GITHUB_ENV"
+#
+# if [[ "$latestTagBase" == *"$branchTagBase" ]]; then
+# hf="False"
+# else
+# hf="True"
+# fi
+#
+# # 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
-# 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
+# echo "No tags exist in this repo"
+# echo "LATEST_TAG=" | tee -a "$GITHUB_ENV"
# 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"
+# echo "PR_BASE_REF=${{ env.PR_BASE_REF }}" | tee -a "$GITHUB_OUTPUT"
+# echo "PR_STATE=${{ env.PR_STATE }}"
+# echo "PR_MERGED=${{ env.PR_MERGED }}"
+# if [[ "${{ env.PR_STATE }}" == "closed" && "${{ env.PR_MERGED }}" == "true" && "${{ env.PR_COMMIT_SHA }}" == "${{ env.GITHUB_SHA }}" ]]; then
+# echo "IS_FULL_RELEASE=True" | tee -a "$GITHUB_OUTPUT"
+# echo "INC_LEVEL=patch" | tee -a "$GITHUB_OUTPUT"
+# fi
+# if [[ "${{ env.PR_STATE }}" == "open" ]]; then
+# echo "IS_PRE_RELEASE=True" | tee -a "$GITHUB_OUTPUT" | tee -a "$GITHUB_ENV"
+# echo "INC_LEVEL=prerelease" | tee -a "$GITHUB_OUTPUT"
+# fi
+# if [[ "${{ env.PR_BASE_REF }}" == "release-"* ]]; then
+# echo "IS_RELEASE_BRANCH=True" | tee -a "$GITHUB_OUTPUT" | tee -a "$GITHUB_ENV"
+# fi
+# echo "PR_COMMIT_SHA=${{ env.PR_COMMIT_SHA }}" | tee -a "$GITHUB_OUTPUT"
+# echo "GITHUB_SHA=${{ env.GITHUB_SHA }}" | tee -a "$GITHUB_OUTPUT"
+# echo "PR_BASE_TAG=${{ env.PR_BASE_TAG }}" | tee -a "$GITHUB_OUTPUT"
+# echo "IS_HOTFIX=${{ env.IS_HOTFIX }}" | tee -a "$GITHUB_OUTPUT"
+# echo "LATEST_TAG=${{ env.LATEST_TAG }}" | tee -a "$GITHUB_OUTPUT"
#
-# update-pkg-version:
-# needs:
-# - check-package-version
-# runs-on: ubuntu-latest
+## 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
#
-# 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
- 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
+# call-starter-workflow:
+# uses: keyfactor/actions/.github/workflows/starter.yml@v2
+# 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
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index fddeab9..100807c 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -15,24 +15,29 @@ on:
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,19 +48,36 @@ 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
@@ -63,232 +85,460 @@ jobs:
### 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
+ 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_*"
### 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
+ 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: |
unset KFUTIL_DEBUG
go test -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"
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
cat $HOME/.keyfactor/command_config.json
+
- name: Install kfutil
run: |
make install
+
- name: Run tests
run: |
go test -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 -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 -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 -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 -v ./cmd -run "^Test_PAM*"
- ## KFC 11.x.x
- kf_11_x_x:
- runs-on: ubuntu-latest
+ ## KFC 12.x.x
+ kf_12_x_x:
+ runs-on: kfutil-runner-set
needs:
- build
steps:
- - name: Checkout code
+ - name: Check out 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: 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*"
+ - 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
- ### Store Tests
- Test_Stores_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: go test -v ./cmd -run "^Test_Stores_*"
+ - 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/"
- ### PAM Tests
- 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*"
+ run: echo "Running tests for KF 12.x.x"
+ ### Store Type Tests
+ # 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 -v ./cmd -run "^Test_StoreTypes*"
- ### PAM Tests AKV Auth Provider
- Test_AKV_PAM_KFC_11_1_2:
- runs-on: self-hosted
+ Test_StoreTypes_KFC_12_3_0_OAUTH:
+ runs-on: kfutil-runner-set
needs:
- - Test_PAM_KFC_11_1_2
+ - build
+ - kf_12_x_x
+ environment: "KFC_12_3_0_OAUTH_CLEAN"
env:
- SECRET_NAME: "command-config-1112-az"
+ 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.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
+ - 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/"
- ## KFC 12.x.x
- ### Store Type Tests
- Test_StoreTypes_KFC_12_2_0:
- runs-on: ubuntu-latest
- needs:
- - build
- - kf_11_x_x
- env:
- SECRET_NAME: "command-config-1220-clean"
- KEYFACTOR_HOSTNAME: "int1220-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*"
-
### Store Tests
- Test_Stores_KFC_12_2_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 -v ./cmd -run "^Test_Stores_*"
+ Test_Stores_KFC_12_3_0_OAUTH:
+ runs-on: kfutil-runner-set
needs:
- build
- - kf_11_x_x
- - Test_StoreTypes_KFC_12_2_0
+ - kf_12_x_x
+ - Test_StoreTypes_KFC_12_3_0_OAUTH
+ environment: "KFC_12_3_0_OAUTH"
env:
- SECRET_NAME: "command-config-1220"
- KEYFACTOR_HOSTNAME: "integrations1220-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_*"
### PAM Tests
- Test_PAM_KFC_12_2_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 -v ./cmd -run "^Test_PAM*"
+
+ Test_PAM_KFC_12_3_0_OAUTH:
+ runs-on: self-hosted
needs:
- build
- - kf_11_x_x
- - Test_StoreTypes_KFC_12_2_0
+ - kf_12_x_x
+ - Test_StoreTypes_KFC_12_3_0_OAUTH
+ environment: "KFC_12_3_0_OAUTH"
env:
- SECRET_NAME: "command-config-1220"
- KEYFACTOR_HOSTNAME: "integrations1220-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: |
unset KFUTIL_DEBUG
@@ -296,37 +546,86 @@ jobs:
### PAM Tests AKV Auth Provider
- Test_AKV_PAM_KFC_12_2_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 -v ./cmd -run "^Test_PAM*"
+
+ Test_AKV_PAM_KFC_12_3_0_OAUTH:
runs-on: self-hosted
needs:
- - Test_PAM_KFC_12_2_0
+ - Test_PAM_KFC_12_3_0_OAUTH
+ environment: "KFC_12_3_0_OAUTH"
env:
- SECRET_NAME: "command-config-1220-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"
+ 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 -v ./cmd -run "^Test_PAM*"
-
-
# Tester Install Script
Test_Install_Script:
- runs-on: ubuntu-latest
+ runs-on: kfutil-runner-set
steps:
- name: Test Quick Install Script
run: |
@@ -341,28 +640,29 @@ jobs:
# 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
diff --git a/.github/workflows/update-stores.yml b/.github/workflows/update-stores.yml
index 3d6bd4a..e15a993 100644
--- a/.github/workflows/update-stores.yml
+++ b/.github/workflows/update-stores.yml
@@ -1,197 +1,197 @@
-name: Create Cert Store Update Pull Request
-
-on:
- repository_dispatch:
- types: targetRepo-event
- workflow_dispatch:
- inputs:
- targetRepo:
- description: 'Target repository for workflow_dispatch'
- default: 'all'
- targetRef:
- description: 'Target ref for workflow_dispatch'
- default: 'latest'
-
-jobs:
- create_pull_request:
- runs-on: ubuntu-latest
- steps:
- - name: Set TARGET_REPO_BRANCH from workflow_dispatch input
- if: github.event_name == 'workflow_dispatch'
- id: set-local-env-vars
- run: |
- echo "TARGET_REPO_BRANCH=${{ inputs.targetRef }}" | tee -a $GITHUB_ENV
- echo "KFUTIL_ARG=${{ inputs.targetRepo }}" | tee -a $GITHUB_ENV
- - name: Set TARGET_REPO_BRANCH from repository_dispatch input
- if: github.event_name == 'repository_dispatch'
- id: set-env-vars-from-payload
- run: |
- echo "TARGET_REPO_BRANCH=${{ github.event.client_payload.targetRef }}" | tee -a $GITHUB_ENV
- echo "KFUTIL_ARG=${{ github.event.client_payload.targetRepo }}" | tee -a $GITHUB_ENV
- - name: Check Open PRs for Existing Branch
- id: check-branch
- uses: actions/github-script@v7
- with:
- script: |
- // Look for open pull requests
- const owner = context.repo.owner;
- const repo = context.repo.repo;
- const pulls = await github.rest.pulls.list({
- owner,
- repo,
- state: "open"
- });
- // Filter out ones matching the KFUTIL_ARG from payload (repository_dispatch) or input (workflow_dispatch)
- const filteredData = pulls.data.filter(item => item.head.ref === '${{ env.KFUTIL_ARG }}'); // Look for an existing branch with the orchestrator repo name
- const isBranch = (filteredData.length > 0)
- if (isBranch) {
- const {
- head: { ref: incomingBranch }, base: { ref: baseBranch }
- } = pulls.data[0]
- core.setOutput('PR_BRANCH', 'commit'); // Just commit since the branch exists
- console.log(`incomingBranch: ${incomingBranch}`)
- console.log(`baseBranch: ${baseBranch}`)
- } else {
- core.setOutput('PR_BRANCH', 'create') // No branch, create one
- }
- console.log(`Branch exists?`)
- console.log(filteredData.length > 0)
- console.log(`targetRepo: ${{env.KFUTIL_ARG}}`)
- - name: set env.PR_BRANCH value for 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
- - name: Check out existing repo merge branch
- if: env.PR_BRANCH == 'commit'
- uses: actions/checkout@v4
- with:
- repository: 'keyfactor/kfutil'
- sparse-checkout: |
- .github
- path: './merge-folder/'
- token: ${{ secrets.V2BUILDTOKEN }}
- ref: '${{env.KFUTIL_ARG}}'
-
-# 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
- with:
- repository: 'keyfactor/kfutil'
- sparse-checkout: |
- .github
- path: './merge-folder/'
- token: ${{ secrets.V2BUILDTOKEN }}
-
-# 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
- - name: Check out python merge tool repo
- uses: actions/checkout@v4
- with:
- repository: 'keyfactor/integration-tools'
- path: './tools/'
- token: ${{ secrets.V2BUILDTOKEN }}
-
- - name: Run Python Script
- working-directory: ./tools/store-type-merge
- run: |
- python main.py --repo-name ${{ env.KFUTIL_ARG }} --ref ${{ env.TARGET_REPO_BRANCH }}
- cat store_types.json
- env:
- GITHUB_TOKEN: ${{ secrets.V2BUILDTOKEN }}
-
- - name: Save Store Types JSON Artifact
- if: success()
- uses: actions/upload-artifact@v3
- with:
- name: store-types
- path: |
- ./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: Save logs directory
- if: success()
- uses: actions/upload-artifact@v3
- with:
- name: logs
- path: ./tools/store-type-merge/log
-
-# 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
- - 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;
- 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
- - 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
- env:
- GITHUB_TOKEN: ${{ secrets.SDK_SYNC_PAT }}
- with:
- add: store_types.json --force
- message: Update store_types.json for ${{env.KFUTIL_ARG}}:${{env.TARGET_REPO_BRANCH}}
- author_name: Keyfactor
- author_email: keyfactor@keyfactor.github.io
- cwd: './merge-folder/'
- new_branch: ${{env.KFUTIL_ARG}}
-
- - name: Add and Commit to existing branch
- if: ${{ env.UPDATE_FILE == 'T' && env.PR_BRANCH == 'commit' }}
- uses: Keyfactor/add-and-commit@v9.1.3
- env:
- GITHUB_TOKEN: ${{ secrets.SDK_SYNC_PAT }}
- with:
- add: store_types.json --force
- message: Update store_types.json for ${{env.KFUTIL_ARG}}:${{env.TARGET_REPO_BRANCH}}
- author_name: Keyfactor
- author_email: keyfactor@keyfactor.github.io
- cwd: './merge-folder/'
-
- - name: Create new PR for the newly created branch
- if: env.UPDATE_FILE == 'T' && env.PR_BRANCH == 'create'
- uses: actions/github-script@v7
- with:
- script: |
- console.log(`Created ${{env.KFUTIL_ARG}} `)
- 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 response = await github.rest.pulls.create({
- owner,
- repo,
- title: 'New Pull Request - ${{env.KFUTIL_ARG}}:${{env.TARGET_REPO_BRANCH}}',
- head: newBranch,
- base: baseBranch,
- body: 'The cert store update from ${{env.KFUTIL_ARG}}:${{env.TARGET_REPO_BRANCH}} needs to be verified and merged if correct.',
- });
- console.log(`Pull request created: ${{env.KFUTIL_ARG}}:${{env.TARGET_REPO_BRANCH}} : ${response.data.html_url}`);
- env:
- GITHUB_TOKEN: ${{ secrets.V2BUILDTOKEN }}
+#name: Create Cert Store Update Pull Request
+#
+#on:
+# repository_dispatch:
+# types: targetRepo-event
+# workflow_dispatch:
+# inputs:
+# targetRepo:
+# description: 'Target repository for workflow_dispatch'
+# default: 'all'
+# targetRef:
+# description: 'Target ref for workflow_dispatch'
+# default: 'latest'
+#
+#jobs:
+# create_pull_request:
+# runs-on: ubuntu-latest
+# steps:
+# - name: Set TARGET_REPO_BRANCH from workflow_dispatch input
+# if: github.event_name == 'workflow_dispatch'
+# id: set-local-env-vars
+# run: |
+# echo "TARGET_REPO_BRANCH=${{ inputs.targetRef }}" | tee -a $GITHUB_ENV
+# echo "KFUTIL_ARG=${{ inputs.targetRepo }}" | tee -a $GITHUB_ENV
+# - name: Set TARGET_REPO_BRANCH from repository_dispatch input
+# if: github.event_name == 'repository_dispatch'
+# id: set-env-vars-from-payload
+# run: |
+# echo "TARGET_REPO_BRANCH=${{ github.event.client_payload.targetRef }}" | tee -a $GITHUB_ENV
+# echo "KFUTIL_ARG=${{ github.event.client_payload.targetRepo }}" | tee -a $GITHUB_ENV
+# - name: Check Open PRs for Existing Branch
+# id: check-branch
+# uses: actions/github-script@v7
+# with:
+# script: |
+# // Look for open pull requests
+# const owner = context.repo.owner;
+# const repo = context.repo.repo;
+# const pulls = await github.rest.pulls.list({
+# owner,
+# repo,
+# state: "open"
+# });
+# // Filter out ones matching the KFUTIL_ARG from payload (repository_dispatch) or input (workflow_dispatch)
+# const filteredData = pulls.data.filter(item => item.head.ref === '${{ env.KFUTIL_ARG }}'); // Look for an existing branch with the orchestrator repo name
+# const isBranch = (filteredData.length > 0)
+# if (isBranch) {
+# const {
+# head: { ref: incomingBranch }, base: { ref: baseBranch }
+# } = pulls.data[0]
+# core.setOutput('PR_BRANCH', 'commit'); // Just commit since the branch exists
+# console.log(`incomingBranch: ${incomingBranch}`)
+# console.log(`baseBranch: ${baseBranch}`)
+# } else {
+# core.setOutput('PR_BRANCH', 'create') // No branch, create one
+# }
+# console.log(`Branch exists?`)
+# console.log(filteredData.length > 0)
+# console.log(`targetRepo: ${{env.KFUTIL_ARG}}`)
+# - name: set env.PR_BRANCH value for 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
+# - name: Check out existing repo merge branch
+# if: env.PR_BRANCH == 'commit'
+# uses: actions/checkout@v4
+# with:
+# repository: 'keyfactor/kfutil'
+# sparse-checkout: |
+# .github
+# path: './merge-folder/'
+# token: ${{ secrets.V2BUILDTOKEN }}
+# ref: '${{env.KFUTIL_ARG}}'
+#
+## 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
+# with:
+# repository: 'keyfactor/kfutil'
+# sparse-checkout: |
+# .github
+# path: './merge-folder/'
+# token: ${{ secrets.V2BUILDTOKEN }}
+#
+## 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
+# - name: Check out python merge tool repo
+# uses: actions/checkout@v4
+# with:
+# repository: 'keyfactor/integration-tools'
+# path: './tools/'
+# token: ${{ secrets.V2BUILDTOKEN }}
+#
+# - name: Run Python Script
+# working-directory: ./tools/store-type-merge
+# run: |
+# python main.py --repo-name ${{ env.KFUTIL_ARG }} --ref ${{ env.TARGET_REPO_BRANCH }}
+# cat store_types.json
+# env:
+# GITHUB_TOKEN: ${{ secrets.V2BUILDTOKEN }}
+#
+# - name: Save Store Types JSON Artifact
+# if: success()
+# uses: actions/upload-artifact@v3
+# with:
+# name: store-types
+# path: |
+# ./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: Save logs directory
+# if: success()
+# uses: actions/upload-artifact@v3
+# with:
+# name: logs
+# path: ./tools/store-type-merge/log
+#
+## 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
+# - 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;
+# 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
+# - 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
+# env:
+# GITHUB_TOKEN: ${{ secrets.SDK_SYNC_PAT }}
+# with:
+# add: store_types.json --force
+# message: Update store_types.json for ${{env.KFUTIL_ARG}}:${{env.TARGET_REPO_BRANCH}}
+# author_name: Keyfactor
+# author_email: keyfactor@keyfactor.github.io
+# cwd: './merge-folder/'
+# new_branch: ${{env.KFUTIL_ARG}}
+#
+# - name: Add and Commit to existing branch
+# if: ${{ env.UPDATE_FILE == 'T' && env.PR_BRANCH == 'commit' }}
+# uses: Keyfactor/add-and-commit@v9.1.3
+# env:
+# GITHUB_TOKEN: ${{ secrets.SDK_SYNC_PAT }}
+# with:
+# add: store_types.json --force
+# message: Update store_types.json for ${{env.KFUTIL_ARG}}:${{env.TARGET_REPO_BRANCH}}
+# author_name: Keyfactor
+# author_email: keyfactor@keyfactor.github.io
+# cwd: './merge-folder/'
+#
+# - name: Create new PR for the newly created branch
+# if: env.UPDATE_FILE == 'T' && env.PR_BRANCH == 'create'
+# uses: actions/github-script@v7
+# with:
+# script: |
+# console.log(`Created ${{env.KFUTIL_ARG}} `)
+# 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 response = await github.rest.pulls.create({
+# owner,
+# repo,
+# title: 'New Pull Request - ${{env.KFUTIL_ARG}}:${{env.TARGET_REPO_BRANCH}}',
+# head: newBranch,
+# base: baseBranch,
+# body: 'The cert store update from ${{env.KFUTIL_ARG}}:${{env.TARGET_REPO_BRANCH}} needs to be verified and merged if correct.',
+# });
+# console.log(`Pull request created: ${{env.KFUTIL_ARG}}:${{env.TARGET_REPO_BRANCH}} : ${response.data.html_url}`);
+# env:
+# GITHUB_TOKEN: ${{ secrets.V2BUILDTOKEN }}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6243e17..fea864b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+# 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
diff --git a/README.md b/README.md
index 08fc1b1..c4214ef 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,31 +55,72 @@ 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, only Active Directory `Basic` authentication is supported.
+
+| 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 Client 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 | `openid` |
+| KEYFACTOR_AUTH_ACCESS_TOKEN | Access token to use to authenticate to Keyfactor Command API. This can be supplied directly or generated via client credentials | |
+
+### 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:
+#### Active Directory Basic Authentication
+
```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
+
+```bash
+export KEYFACTOR_HOSTNAME=""
+export KEYFACTOR_AUTH_CLIENT_ID=""
$env:KEYFACTOR_USERNAME = "" # Do not include domain
@@ -92,7 +139,16 @@ $env:KEYFACTOR_PASSWORD = ""
$env:KEYFACTOR_DOMAIN = ""
```
-Additional variables:
+#### oAuth Client Credentials
+
+```powershell
+$env:KEYFACTOR_HOSTNAME = ""
+$env:KEYFACTOR_AUTH_CLIENT_ID = ""
+$env:KEYFACTOR_AUTH_CLIENT_SECRET = ""
+$env:KEYFACTOR_AUTH_TOKEN_URL = "/protocol/openid-connect/token"
+```
+
+#### Additional variables:
```bash
$env:KEYFACTOR_API_PATH="/KeyfactorAPI" # Defaults to /KeyfactorAPI if not set ex. my.domain.com/KeyfactorAPI
@@ -135,8 +191,6 @@ For full documentation on the `logout` command, see the [logout](docs/kfutil_log
kfutil logout
```
-## Commands
-
### Bulk operations
#### Bulk create cert stores
@@ -439,5 +493,3 @@ alternatively you can specify the parent command
```bash
cobra-cli add -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/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..e0145c5 100644
--- a/cmd/export.go
+++ b/cmd/export.go
@@ -18,12 +18,13 @@ import (
"context"
"encoding/json"
"fmt"
+ "os"
+ "strconv"
+
"github.com/Keyfactor/keyfactor-go-client-sdk/api/keyfactor"
- "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"
)
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..2060926 100644
--- a/cmd/helm_uo.go
+++ b/cmd/helm_uo.go
@@ -18,12 +18,13 @@ package cmd
import (
"fmt"
+ "log"
+
"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 +69,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 {
diff --git a/cmd/helpers.go b/cmd/helpers.go
index 74c79d0..743eba8 100644
--- a/cmd/helpers.go
+++ b/cmd/helpers.go
@@ -30,6 +30,8 @@ import (
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
+
+ stdlog "log"
)
func boolToPointer(b bool) *bool {
@@ -188,6 +190,7 @@ 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()
diff --git a/cmd/import.go b/cmd/import.go
index 5004a2e..e9a3bf7 100644
--- a/cmd/import.go
+++ b/cmd/import.go
@@ -18,12 +18,13 @@ import (
"context"
"encoding/json"
"fmt"
+ "io"
+ "os"
+
"github.com/Keyfactor/keyfactor-go-client-sdk/api/keyfactor"
- "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"
- "io"
- "os"
)
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/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..578990e 100644
--- a/cmd/login.go
+++ b/cmd/login.go
@@ -15,18 +15,21 @@
package cmd
import (
+ "bufio"
"encoding/json"
"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/Keyfactor/keyfactor-go-client/v3/api"
+ "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 +38,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 +64,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 +285,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,7 +329,14 @@ func getDomainFromUsername(username string) string {
return ""
}
-func createConfigFile(hostname string, username string, password string, domain string, apiPath string, profileName string) ConfigurationFile {
+func createConfigFile(
+ hostname string,
+ username string,
+ password string,
+ domain string,
+ apiPath string,
+ profileName string,
+) ConfigurationFile {
output := ConfigurationFile{
Servers: map[string]ConfigurationFileEntry{
profileName: {
@@ -238,28 +351,6 @@ func createConfigFile(hostname string, username string, password string, domain
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,
- }
- return output
-}
-
func promptForInteractiveParameter(parameterName string, defaultValue string) string {
var input string
fmt.Printf("Enter %s [%s]: \n", parameterName, defaultValue)
@@ -284,16 +375,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 = password[:len(password)-1]
+ }
+ if password == "" {
return defaultValue
}
- password := string(bytePassword)
- fmt.Println("")
return password
}
@@ -329,40 +443,102 @@ func saveConfigFile(configFile ConfigurationFile, configPath string, profileName
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 username == "" || forcePrompt {
- username = promptForInteractiveParameter("Keyfactor Command kfcUsername", username)
- }
- if password == "" || forcePrompt {
- password = promptForInteractivePassword("Keyfactor Command kfcPassword", password)
+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 domain == "" || forcePrompt {
- domain = getDomainFromUsername(username)
- if domain == "" {
- domain = promptForInteractiveParameter("Keyfactor Command AD kfcDomain", domain)
+ 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.ClientID == "" || forcePrompt {
+ serverConf.ClientID = promptForInteractiveParameter(
+ "Keyfactor Command OAuth Client ID",
+ serverConf.ClientID,
+ )
+ }
+ if serverConf.ClientSecret == "" || forcePrompt {
+ serverConf.ClientSecret = promptForInteractivePassword(
+ "Keyfactor Command OAuth Client Secret",
+ serverConf.ClientSecret,
+ )
+ }
+ if serverConf.OAuthTokenUrl == "" || forcePrompt {
+ serverConf.OAuthTokenUrl = promptForInteractiveParameter(
+ "Keyfactor Command OAuth Token URL",
+ serverConf.OAuthTokenUrl,
+ )
}
}
- if apiPath == "" || forcePrompt {
- apiPath = promptForInteractiveParameter("Keyfactor Command API path", apiPath)
+
+ if serverConf.APIPath == "" || forcePrompt {
+ serverConf.APIPath = promptForInteractiveParameter("Keyfactor Command API path", serverConf.APIPath)
+ }
+
+ if serverConf.CACertPath == "" || forcePrompt {
+ serverConf.CACertPath = promptForInteractiveParameter("Keyfactor Command CA Cert Path", serverConf.CACertPath)
}
if profileName == "" {
profileName = "default"
}
+ if configPath == "" {
+ userHomeDir, hErr := prepHomeDir()
+ if hErr != nil {
+ //log.Println("[ERROR] Unable to create home directory: ", hErr)
+ log.Error().Err(hErr)
+ return auth_providers.Config{}, hErr
+ }
+ configPath = path.Join(userHomeDir, DefaultConfigFileName)
+ }
- confFile := createConfigFile(hostname, username, password, domain, apiPath, profileName)
+ confFile := auth_providers.Config{
+ Servers: map[string]auth_providers.Server{},
+ }
+ confFile.Servers[profileName] = *serverConf
if saveConfig {
- savedConfigFile, saveErr := saveConfigFile(confFile, configPath, profileName)
+ 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
}
return confFile, nil
}
@@ -397,7 +573,12 @@ func prepHomeDir() (string, error) {
return userHomeDir, hErr
}
-func loadConfigFileData(profileName string, configPath string, noPrompt bool, configurationFile ConfigurationFile) (string, string, string, string, string) {
+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).
@@ -500,12 +681,15 @@ func loadConfigFileData(profileName string, configPath string, noPrompt bool, co
func authViaProvider() (*api.Client, error) {
var clientAuth api.AuthConfig
- var commandConfig ConfigurationFile
+ var commandConfig auth_providers.Config
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'")
+ log.Info().Str(
+ "providerProfile",
+ providerProfile,
+ ).Msg("auth provider profile not set, defaulting to 'default'")
providerProfile = "default"
}
@@ -565,22 +749,29 @@ func authViaProvider() (*api.Client, error) {
}
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).
+ //commandConfig = pvConfig //TODO: Handle this ab#55467
+ clientConfig := clientAuth.GetServerConfig()
+ clientConfig.Username = commandConfig.Servers[providerProfile].Username
+ clientConfig.Password = commandConfig.Servers[providerProfile].Password
+ clientConfig.Domain = commandConfig.Servers[providerProfile].Domain
+ clientConfig.Host = commandConfig.Servers[providerProfile].Host
+ clientConfig.ClientID = commandConfig.Servers[providerProfile].ClientID
+ clientConfig.ClientSecret = commandConfig.Servers[providerProfile].ClientSecret
+ clientConfig.OAuthTokenUrl = commandConfig.Servers[providerProfile].OAuthTokenUrl
+ clientConfig.APIPath = commandConfig.Servers[providerProfile].APIPath
+
+ log.Debug().
+ Str("clientAuth.Username", clientConfig.Username).
+ Str("clientAuth.Password", hashSecretValue(clientConfig.Password)).
+ Str("clientAuth.Domain", clientConfig.Domain).
+ Str("clientAuth.ClientID", clientConfig.ClientID).
+ Str("clientAuth.ClientSecret", hashSecretValue(clientConfig.ClientSecret)).
+ Str("clientAuth.Hostname", clientConfig.Host).
+ Str("clientAuth.APIPath", clientConfig.APIPath).
Msg("Client authentication params")
log.Debug().Msg("call: api.NewKeyfactorClient()")
- c, err := api.NewKeyfactorClient(&clientAuth)
+ c, err := api.NewKeyfactorClient(clientConfig, nil)
log.Debug().Msg("complete: api.NewKeyfactorClient()")
if err != nil {
@@ -598,100 +789,6 @@ func authViaProvider() (*api.Client, error) {
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
@@ -702,7 +799,11 @@ func authViaProviderParams(providerConfig *AuthProvider) (ConfigurationFile, err
// 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)
+ return ConfigurationFile{}, fmt.Errorf(
+ "invalid auth provider type '%s'. Valid auth providers are: %v",
+ pt,
+ ValidAuthProviders,
+ )
}
// Check if provider type matches requested provider type
@@ -734,13 +835,17 @@ func authViaProviderParams(providerConfig *AuthProvider) (ConfigurationFile, err
log.Error().Msg("invalid auth provider type")
break
}
- return ConfigurationFile{}, fmt.Errorf("invalid auth provider type '%s'. Valid auth providers are: %v", pt, ValidAuthProviders)
+ 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
+ return true // default to Username/Password
}
for _, validProvider := range ValidAuthProviders {
if validProvider == providerType {
@@ -752,7 +857,13 @@ func validAuthProvider(providerType string) bool {
return false
}
-func authConfigFile(configPath string, profileName string, authProviderProfile string, noPrompt bool, saveConfig bool) (ConfigurationFile, []error) {
+func authConfigFile(
+ configPath string,
+ profileName string,
+ authProviderProfile string,
+ noPrompt bool,
+ saveConfig bool,
+) (ConfigurationFile, []error) {
var configurationFile ConfigurationFile
var (
hostName string
@@ -793,7 +904,12 @@ func authConfigFile(configPath string, profileName string, authProviderProfile s
}
log.Debug().Msg("calling loadConfigFileData()")
- hostName, userName, password, domain, apiPath = loadConfigFileData(profileName, configPath, noPrompt, configurationFile)
+ hostName, userName, password, domain, apiPath = loadConfigFileData(
+ profileName,
+ configPath,
+ noPrompt,
+ configurationFile,
+ )
log.Debug().Msg("loadConfigFileData() returned")
log.Debug().Str("hostName", hostName).
@@ -832,7 +948,10 @@ func authConfigFile(configPath string, profileName string, authProviderProfile s
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")
+ 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")
@@ -918,7 +1037,12 @@ func authEnvProvider(authProvider *AuthProvider, configProfile string) (Configur
} 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)}
+ return ConfigurationFile{}, []error{
+ fmt.Errorf(
+ "profile '%s' not found in authProviderParams file",
+ configProfile,
+ ),
+ }
}
} else {
//check if provider params is an AuthProvider
@@ -956,7 +1080,10 @@ func authEnvProvider(authProvider *AuthProvider, configProfile string) (Configur
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")
+ 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)
@@ -1011,16 +1138,28 @@ func authEnvVars(configPath string, profileName string, saveConfig bool) (Config
var outputErr []error
if !hostSet {
- outputErr = append(outputErr, fmt.Errorf("KEYFACTOR_HOSTNAME environment variable not set. Please set the KEYFACTOR_HOSTNAME environment variable"))
+ 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"))
+ 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"))
+ 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"))
+ outputErr = append(
+ outputErr,
+ fmt.Errorf("KEYFACTOR_DOMAIN environment variable not set. Please set the KEYFACTOR_DOMAIN environment variable"),
+ )
}
if !apiPathSet {
apiPath = DefaultAPIPath
@@ -1182,7 +1321,10 @@ func loadConfigurationFile(filePath string, silent bool) (ConfigurationFile, err
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")
+ 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))
@@ -1202,14 +1344,3 @@ func loadConfigurationFile(filePath string, silent bool) (ConfigurationFile, err
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/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..1893081 100644
--- a/cmd/pam.go
+++ b/cmd/pam.go
@@ -18,12 +18,13 @@ 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"
+
+ "github.com/Keyfactor/keyfactor-go-client-sdk/api/keyfactor"
+ "github.com/rs/zerolog/log"
+ "github.com/spf13/cobra"
)
type JSONImportableObject interface {
@@ -61,8 +62,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 +135,8 @@ 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, _ := initGenClient(false)
// Check required flags
if pamConfigFile == "" && repoName == "" {
@@ -229,9 +228,8 @@ 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, _ := initGenClient(false)
// CLI Logic
log.Debug().Msg("call: PAMProviderGetPamProviders()")
@@ -281,13 +279,15 @@ 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, _ := initGenClient(false)
// 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()")
@@ -334,9 +334,8 @@ 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, _ := initGenClient(false)
// CLI Logic
var pamProvider *keyfactor.CSSCMSDataModelModelsProvider
@@ -398,9 +397,8 @@ 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, _ := initGenClient(false)
// CLI Logic
var pamProvider *keyfactor.CSSCMSDataModelModelsProvider
@@ -465,9 +463,8 @@ 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, _ := initGenClient(false)
// CLI Logic
log.Debug().
@@ -500,7 +497,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 +559,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 +633,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 +657,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/root.go b/cmd/root.go
index a356d55..c5d7e53 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -20,9 +20,11 @@ import (
"io"
stdlog "log"
"os"
+ "strings"
+ "github.com/Keyfactor/keyfactor-auth-client-go/auth_providers"
"github.com/Keyfactor/keyfactor-go-client-sdk/api/keyfactor"
- "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/cobra/doc"
@@ -35,17 +37,20 @@ 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
- offline bool
+ noPrompt bool
+ expEnabled bool
+ debugFlag bool
+ kfcUsername string
+ kfcHostName string
+ kfcPassword string
+ kfcDomain string
+ kfcClientId string
+ kfcClientSecret string
+ kfcTokenUrl string
+ kfcAPIPath string
+ logInsecure bool
+ outputFormat string
+ offline bool
)
func hashSecretValue(secretValue string) string {
@@ -66,242 +71,508 @@ 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
+func getServerConfigFromFile(configFile string, profile string) (*auth_providers.Server, error) {
+ var commandConfig *auth_providers.Config
+ var serverConfig auth_providers.Server
- if providerType != "" {
- return authViaProvider()
+ 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("call: auth_providers.ReadConfigFromJSON()")
+ commandConfig, cfgReadErr = auth_providers.ReadConfigFromJSON(configFile)
}
- 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()")
+ if cfgReadErr != nil {
+ log.Error().Err(cfgReadErr).Msg("unable to read config file")
+ return nil, fmt.Errorf("unable to read config file: %s", cfgReadErr)
+ }
+
+ // 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)
+ }
+
+ log.Debug().Msg("return: getServerConfigFromFile()")
+ return &serverConfig, nil
+}
+
+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 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
}
- } 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("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
}
+
+ log.Debug().Msg("return: getServerConfigFromEnv()")
+ return oAuthNoParamsConfig.GetServerConfig(), nil
+
}
- log.Debug().Str("flagProfile", flagProfile).Send()
- if flagProfile == "" {
- flagProfile = "default"
+ log.Error().Msg("unable to authenticate with provided credentials")
+ return nil, fmt.Errorf("incomplete environment variable configuration")
+
+}
+
+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 {
+ 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().Msg("call: c.AuthClient.Authenticate()")
+ authErr := c.AuthClient.Authenticate()
+ log.Debug().Msg("complete: c.AuthClient.Authenticate()")
+ if authErr == nil {
+ return c, nil
+ }
- //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
+ }
+ 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)
+}
+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 {
+ log.Debug().Msg("call: keyfactor.NewAPIClient()")
+ c = 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
}
- 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
+
+ }
+ 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)
+}
+
+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
}
- 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("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
}
- 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: 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")
+}
+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 {
+ 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 = 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
}
- 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: 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().Str("flagProfile", flagProfile).Msg("Setting configEntry")
- commandConfig.Servers[flagProfile] = configEntry
+ 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")
+}
- 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)
+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).
+ 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 (
+ authenticated bool
+ c *api.Client
+ cErr 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 != "" {
+ c, cErr = authViaConfigFile(configFile, profile)
+ if cErr == nil {
+ log.Info().
+ Str("configFile", configFile).
+ Str("profile", profile).
+ Msgf("Authenticated via config file %s using profile %s", configFile, profile)
+ authenticated = true
}
}
- 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
-
- 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")
+ if !authenticated {
+ log.Debug().Msg("call: authViaEnvVars()")
+ c, cErr = authViaEnvVars()
+ log.Debug().Msg("returned: authViaEnvVars()")
+ if cErr == nil {
+ log.Info().Msg("Authenticated via environment variables")
+ authenticated = true
+ }
+ }
- log.Debug().Msg("call: api.NewKeyfactorClient()")
- c, err := api.NewKeyfactorClient(&clientAuth)
- log.Debug().Msg("complete: api.NewKeyfactorClient()")
+ if !authenticated {
+ log.Debug().Msg("call: authViaConfigFile()")
+ c, cErr = authViaConfigFile("", "")
+ if cErr == nil {
+ log.Info().
+ Str("configFile", configFile).
+ Str("profile", profile).
+ Msgf("Authenticated via config file %s using profile %s", configFile, profile)
+ authenticated = true
+ }
+ }
- 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)
+ if !authenticated {
+ log.Error().Msg("unable to authenticate")
+ if cErr != nil {
+ log.Debug().Err(cErr).Msg("return: initClient()")
+ return nil, cErr
+ }
+ log.Debug().Msg("return: initClient()")
+ return nil, fmt.Errorf("unable to authenticate to Keyfactor Command")
}
+
log.Info().Msg("Keyfactor Command client created")
return c, nil
}
func initGenClient(
- flagConfig string,
- flagProfile string,
- noPrompt bool,
- authConfig *api.AuthConfig,
saveConfig bool,
) (*keyfactor.APIClient, error) {
- var commandConfig ConfigurationFile
+ 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 (
+ authenticated bool
+ c *keyfactor.APIClient
+ cErr error
+ )
if providerType != "" {
- return authViaProviderGenClient()
- }
-
- commandConfig, _ = authEnvVars(flagConfig, "", saveConfig)
-
- if flagConfig != "" || !validConfigFileEntry(commandConfig, flagProfile) {
- commandConfig, _ = authConfigFile(flagConfig, flagProfile, "", noPrompt, saveConfig)
- }
-
- if flagProfile == "" {
- flagProfile = "default"
+ log.Debug().
+ Str("providerType", providerType).
+ Msg("call: authViaProvider()")
+ //return authViaProvider()
+ return nil, fmt.Errorf("provider auth not supported using Keyfactor Command SDK")
}
-
- //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 authConfig.Username != "" {
- configEntry.Username = authConfig.Username
+ log.Debug().
+ Msg("providerType is empty attempting to authenticate via params")
+
+ if configFile != "" || profile != "" {
+ c, cErr = authSdkViaConfigFile(configFile, profile)
+ if cErr == nil {
+ log.Info().
+ Str("configFile", configFile).
+ Str("profile", profile).
+ Msgf("Authenticated via config file %s using profile %s", configFile, profile)
+ authenticated = true
}
- if authConfig.Password != "" {
- configEntry.Password = authConfig.Password
- }
- if authConfig.Domain != "" {
- configEntry.Domain = authConfig.Domain
- } else if authConfig.Username != "" {
- tDomain := getDomainFromUsername(authConfig.Username)
- if tDomain != "" {
- configEntry.Domain = tDomain
- }
- }
- if authConfig.APIPath != "" {
- configEntry.APIPath = authConfig.APIPath
- }
- 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)
+ if !authenticated {
+ log.Debug().Msg("call: authViaEnvVars()")
+ c, cErr = authSdkViaEnvVars()
+ log.Debug().Msg("returned: authViaEnvVars()")
+ if cErr == nil {
+ log.Info().Msg("Authenticated via environment variables")
+ authenticated = true
}
}
- 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
-
- configuration := keyfactor.NewConfiguration(sdkClientConfig)
- c := keyfactor.NewAPIClient(configuration)
+ log.Info().Msg("Keyfactor Command client created")
return c, nil
}
+//func initGenClientV1(
+// flagConfig string,
+// flagProfile string,
+// noPrompt bool,
+// authConfig *api.AuthConfig,
+// saveConfig bool,
+//) (*keyfactor.APIClient, error) {
+// var commandConfig ConfigurationFile
+//
+// if providerType != "" {
+// return authViaProviderGenClient()
+// }
+//
+// commandConfig, _ = authEnvVars(flagConfig, "", saveConfig)
+//
+// if flagConfig != "" || !validConfigFileEntry(commandConfig, flagProfile) {
+// commandConfig, _ = authConfigFile(flagConfig, flagProfile, "", noPrompt, saveConfig)
+// }
+//
+// if flagProfile == "" {
+// flagProfile = "default"
+// }
+//
+// //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 authConfig.Username != "" {
+// configEntry.Username = authConfig.Username
+// }
+// if authConfig.Password != "" {
+// configEntry.Password = authConfig.Password
+// }
+// if authConfig.Domain != "" {
+// configEntry.Domain = authConfig.Domain
+// } else if authConfig.Username != "" {
+// tDomain := getDomainFromUsername(authConfig.Username)
+// if tDomain != "" {
+// configEntry.Domain = tDomain
+// }
+// }
+// if authConfig.APIPath != "" {
+// configEntry.APIPath = authConfig.APIPath
+// }
+// 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)
+// }
+// }
+//
+// 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
+//
+// configuration := keyfactor.NewConfiguration(sdkClientConfig)
+// c := keyfactor.NewAPIClient(configuration)
+// return c, nil
+//}
+
var makeDocsCmd = &cobra.Command{
Use: "makedocs",
Short: "Generate markdown documentation for kfutil",
@@ -407,6 +678,31 @@ func init() {
"",
"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",
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 7bced1d..068e7ca 100644
--- a/cmd/storeTypes.go
+++ b/cmd/storeTypes.go
@@ -25,8 +25,10 @@ import (
"strings"
"time"
+ stdlog "log"
+
"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"
)
@@ -52,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 {
@@ -99,8 +106,7 @@ 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 == "" {
@@ -244,11 +250,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{}
diff --git a/cmd/storeTypes_get.go b/cmd/storeTypes_get.go
index 74f8c85..e630d11 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 {
@@ -256,7 +284,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/stores.go b/cmd/stores.go
index 9205d1f..9a0c19a 100644
--- a/cmd/stores.go
+++ b/cmd/stores.go
@@ -51,8 +51,7 @@ var storesListCmd = &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
params := make(map[string]interface{})
@@ -94,8 +93,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)
@@ -134,8 +132,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")
diff --git a/cmd/storesBulkOperations.go b/cmd/storesBulkOperations.go
index 216ed45..ada8184 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,55 @@ 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)
+ }
+
+ // 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 _, isInt := prop.Data().(int); isInt {
+ log.Debug().Str("name", name).Msg("Property is an int")
+ asStr := strconv.Itoa(prop.Data().(int))
+ json.Set(asStr, "Properties."+name)
+ }
+ }
+ return json
+}
+
+func serializeStoreFromTypeDef(storeTypeName string, input string) (string, error) {
+ // check if storetypename is an integer
+ storeTypes, _ := readStoreTypesConfig("", "", 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 +132,7 @@ var storesCreateFromCSVCmd = &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().Msg("Importing certificate stores")
@@ -107,7 +156,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{
@@ -212,6 +261,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 +283,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 +344,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