From 156730277ae1cf69dd4cefc1c0244e198dc739ea Mon Sep 17 00:00:00 2001 From: spbsoluble <1661003+spbsoluble@users.noreply.github.com> Date: Thu, 16 Jan 2025 13:21:35 -0800 Subject: [PATCH 01/16] chore(deps): Bump `keyfactor-auth-client-go` to `v1.1.2` --- v3/go.mod | 2 +- v3/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/v3/go.mod b/v3/go.mod index 9fa544d..372fe63 100644 --- a/v3/go.mod +++ b/v3/go.mod @@ -19,7 +19,7 @@ go 1.23 toolchain go1.23.2 require ( - github.com/Keyfactor/keyfactor-auth-client-go v1.1.1-rc.0 + github.com/Keyfactor/keyfactor-auth-client-go v1.1.2 github.com/hashicorp/terraform-plugin-log v0.9.0 github.com/spbsoluble/go-pkcs12 v0.3.3 go.mozilla.org/pkcs7 v0.9.0 diff --git a/v3/go.sum b/v3/go.sum index 67d7ee9..dd025be 100644 --- a/v3/go.sum +++ b/v3/go.sum @@ -14,8 +14,8 @@ github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJ github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 h1:kYRSnvJju5gYVyhkij+RTJ/VR6QIUaCfWeaFm2ycsjQ= github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= -github.com/Keyfactor/keyfactor-auth-client-go v1.1.1-rc.0 h1:/N/7pBj/oTUM1cYga2NvKyA4q6nfE0acciJHZqKC9Ug= -github.com/Keyfactor/keyfactor-auth-client-go v1.1.1-rc.0/go.mod h1:yw92P9gSYVEyWkiUAJFsb7hjhXa8slN1+yTQgjSgovM= +github.com/Keyfactor/keyfactor-auth-client-go v1.1.2 h1:o/BCmICXUErZcodteat3/jrMjzkh8hlNI5kzXB+wsSw= +github.com/Keyfactor/keyfactor-auth-client-go v1.1.2/go.mod h1:yw92P9gSYVEyWkiUAJFsb7hjhXa8slN1+yTQgjSgovM= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= From 278bfdcea981a0e2c0ebee409f9a5be51a04a429 Mon Sep 17 00:00:00 2001 From: spbsoluble <1661003+spbsoluble@users.noreply.github.com> Date: Thu, 16 Jan 2025 13:23:46 -0800 Subject: [PATCH 02/16] chore(deps): ``` go: upgraded github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 => v1.17.0 go: upgraded github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 => v1.8.1 go: upgraded github.com/fatih/color v1.13.0 => v1.18.0 go: upgraded github.com/hashicorp/go-hclog v1.5.0 => v1.6.3 go: upgraded github.com/mattn/go-colorable v0.1.13 => v0.1.14 go: upgraded github.com/mattn/go-isatty v0.0.19 => v0.0.20 go: upgraded golang.org/x/crypto v0.30.0 => v0.32.0 go: upgraded golang.org/x/net v0.32.0 => v0.34.0 go: upgraded golang.org/x/oauth2 v0.24.0 => v0.25.0 go: upgraded golang.org/x/sys v0.28.0 => v0.29.0 ``` --- v3/go.mod | 20 ++++++++++---------- v3/go.sum | 49 ++++++++++++++++++++++++------------------------- 2 files changed, 34 insertions(+), 35 deletions(-) diff --git a/v3/go.mod b/v3/go.mod index 372fe63..9dcc91a 100644 --- a/v3/go.mod +++ b/v3/go.mod @@ -26,24 +26,24 @@ require ( ) require ( - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.3.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.0 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 // indirect - github.com/fatih/color v1.13.0 // indirect + github.com/fatih/color v1.18.0 // indirect github.com/golang-jwt/jwt/v5 v5.2.1 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/hashicorp/go-hclog v1.5.0 // indirect + github.com/hashicorp/go-hclog v1.6.3 // indirect github.com/kylelemons/godebug v1.1.0 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect - golang.org/x/crypto v0.30.0 // indirect - golang.org/x/net v0.32.0 // indirect - golang.org/x/oauth2 v0.24.0 // indirect - golang.org/x/sys v0.28.0 // indirect + golang.org/x/crypto v0.32.0 // indirect + golang.org/x/net v0.34.0 // indirect + golang.org/x/oauth2 v0.25.0 // indirect + golang.org/x/sys v0.29.0 // indirect golang.org/x/text v0.21.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/v3/go.sum b/v3/go.sum index dd025be..c4f0ef2 100644 --- a/v3/go.sum +++ b/v3/go.sum @@ -1,9 +1,9 @@ -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0 h1:JZg6HRh6W6U4OLl6lk7BZ7BLisIzM9dG1R50zUk9C/M= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.16.0/go.mod h1:YL1xnZ6QejvQHWJrX/AvhFl4WW4rqHVoKspWNVwFk0M= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 h1:B/dfvscEQtew9dVuoxqxrUKKv8Ih2f55PydknDamU+g= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0/go.mod h1:fiPSssYvltE08HJchL04dOy+RD4hgrjph0cwGGMntdI= -github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.0 h1:+m0M/LFxN43KvULkDNfdXOgrjtg6UYJPFBJyuEcRCAw= -github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.0/go.mod h1:PwOyop78lveYMRs6oCxjiVyBdyCgIYH6XHIVZO9/SFQ= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 h1:g0EZJwz7xkXQiZAI5xi9f3WWFYBlX1CPTrR+NDToRkQ= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0/go.mod h1:XCW7KnZet0Opnr7HccfUw1PLc4CjHqpcaxW8DHklNkQ= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1 h1:1mvYtZfWQAnwNah/C+Z+Jb9rQH95LPE2vlmMuWAHJk8= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1/go.mod h1:75I/mXtme1JyWFtz8GocPHVFyH421IBoZErnO16dd0k= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.1 h1:Bk5uOhSAenHyR5P61D/NzeQCv+4fEVV8mOkJ82NqpWw= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.1/go.mod h1:QZ4pw3or1WPmRBxf0cHd1tknzrT54WPBOQoGutCPvSU= github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY= github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY= github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.3.0 h1:WLUIpeyv04H0RCcQHaA4TNoyrQ39Ox7V+re+iaqzTe0= @@ -23,16 +23,17 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= +github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= -github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow= github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs= @@ -45,21 +46,20 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4= -github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA= +github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= +github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/spbsoluble/go-pkcs12 v0.3.3 h1:3nh7IKn16RDpmrSMtOu1JvbB0XHYq1j+IsICdU1c7J4= @@ -70,22 +70,21 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= go.mozilla.org/pkcs7 v0.9.0 h1:yM4/HS9dYv7ri2biPtxt8ikvB37a980dg69/pKmS+eI= go.mozilla.org/pkcs7 v0.9.0/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= -golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY= -golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= -golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= -golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= -golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= +golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= +golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 0bac539dde14e76ed788c3c587abed57e14103cd Mon Sep 17 00:00:00 2001 From: spbsoluble <1661003+spbsoluble@users.noreply.github.com> Date: Thu, 16 Jan 2025 14:28:05 -0800 Subject: [PATCH 03/16] chore(deps): Bump `github.com/Keyfactor/keyfactor-auth-client-go` to `v1.2.0-rc.0` --- v3/go.mod | 2 +- v3/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/v3/go.mod b/v3/go.mod index 9dcc91a..9e4c372 100644 --- a/v3/go.mod +++ b/v3/go.mod @@ -19,7 +19,7 @@ go 1.23 toolchain go1.23.2 require ( - github.com/Keyfactor/keyfactor-auth-client-go v1.1.2 + github.com/Keyfactor/keyfactor-auth-client-go v1.2.0-rc.0 github.com/hashicorp/terraform-plugin-log v0.9.0 github.com/spbsoluble/go-pkcs12 v0.3.3 go.mozilla.org/pkcs7 v0.9.0 diff --git a/v3/go.sum b/v3/go.sum index c4f0ef2..49df6d3 100644 --- a/v3/go.sum +++ b/v3/go.sum @@ -14,8 +14,8 @@ github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJ github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 h1:kYRSnvJju5gYVyhkij+RTJ/VR6QIUaCfWeaFm2ycsjQ= github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= -github.com/Keyfactor/keyfactor-auth-client-go v1.1.2 h1:o/BCmICXUErZcodteat3/jrMjzkh8hlNI5kzXB+wsSw= -github.com/Keyfactor/keyfactor-auth-client-go v1.1.2/go.mod h1:yw92P9gSYVEyWkiUAJFsb7hjhXa8slN1+yTQgjSgovM= +github.com/Keyfactor/keyfactor-auth-client-go v1.2.0-rc.0 h1:mm5NpyEJhm4em9nn8gWNFtsk211F3IY4oAH8kfZSAqs= +github.com/Keyfactor/keyfactor-auth-client-go v1.2.0-rc.0/go.mod h1:8/msKJyHU7+GwW3n9aYQIyW7Lu5/njaOsFL9dZoQ+Ek= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= From d897eea8aa53cafb76d2df380718b2e99b67b4e2 Mon Sep 17 00:00:00 2001 From: spbsoluble <1661003+spbsoluble@users.noreply.github.com> Date: Fri, 17 Jan 2025 10:15:44 -0800 Subject: [PATCH 04/16] chore(deps): Bump `github.com/Keyfactor/keyfactor-auth-client-go` to `v1.2.0-rc.5` --- v3/go.mod | 2 +- v3/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/v3/go.mod b/v3/go.mod index 9e4c372..6a0ed78 100644 --- a/v3/go.mod +++ b/v3/go.mod @@ -19,7 +19,7 @@ go 1.23 toolchain go1.23.2 require ( - github.com/Keyfactor/keyfactor-auth-client-go v1.2.0-rc.0 + github.com/Keyfactor/keyfactor-auth-client-go v1.2.0-rc.5 github.com/hashicorp/terraform-plugin-log v0.9.0 github.com/spbsoluble/go-pkcs12 v0.3.3 go.mozilla.org/pkcs7 v0.9.0 diff --git a/v3/go.sum b/v3/go.sum index 49df6d3..5656f2b 100644 --- a/v3/go.sum +++ b/v3/go.sum @@ -14,8 +14,8 @@ github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJ github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 h1:kYRSnvJju5gYVyhkij+RTJ/VR6QIUaCfWeaFm2ycsjQ= github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= -github.com/Keyfactor/keyfactor-auth-client-go v1.2.0-rc.0 h1:mm5NpyEJhm4em9nn8gWNFtsk211F3IY4oAH8kfZSAqs= -github.com/Keyfactor/keyfactor-auth-client-go v1.2.0-rc.0/go.mod h1:8/msKJyHU7+GwW3n9aYQIyW7Lu5/njaOsFL9dZoQ+Ek= +github.com/Keyfactor/keyfactor-auth-client-go v1.2.0-rc.5 h1:3BpabiIcvdSszX7cx7OLBlpS8c7CnmvdquAANOD5+2o= +github.com/Keyfactor/keyfactor-auth-client-go v1.2.0-rc.5/go.mod h1:7htRcBIWn+X4fI5jaYBALSYwP84H/djN7d8y3n0ZDQ0= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= From f50e0a70fab058495ec422268eafb77f8de9357c Mon Sep 17 00:00:00 2001 From: spbsoluble <1661003+spbsoluble@users.noreply.github.com> Date: Mon, 3 Mar 2025 12:51:24 -0800 Subject: [PATCH 05/16] fix(certs): When creating subject escape any values that main contain commas. feat(certs): Add `collectionId` support for certificate downloads. --- v3/api/certificate.go | 36 ++++++++++++++++++++++++++++++------ v3/go.mod | 2 +- v3/go.sum | 4 ++-- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/v3/api/certificate.go b/v3/api/certificate.go index c734c65..95a7af8 100644 --- a/v3/api/certificate.go +++ b/v3/api/certificate.go @@ -196,6 +196,7 @@ func (c *Client) DownloadCertificate( thumbprint string, serialNumber string, issuerDn string, + collectionId int, ) (*x509.Certificate, []*x509.Certificate, error) { log.Println("[INFO] Downloading certificate") @@ -228,6 +229,19 @@ func (c *Client) DownloadCertificate( ChainOrder: "EndEntityFirst", } + query := apiQuery{ + Query: []StringTuple{}, + } + if collectionId > 0 { + log.Println("[DEBUG] RecoverCertificate: Collection ID:", collectionId) + query.Query = append( + query.Query, StringTuple{ + "collectionId", fmt.Sprintf("%d", collectionId), + }, + ) + log.Println("[DEBUG] RecoverCertificate: Query:", query) + } + // Set Keyfactor-specific headers headers := &apiHeaders{ Headers: []StringTuple{ @@ -242,6 +256,7 @@ func (c *Client) DownloadCertificate( Endpoint: "Certificates/Download", Headers: headers, Payload: payload, + Query: &query, } resp, err := c.sendRequest(keyfactorAPIStruct) @@ -754,30 +769,39 @@ func createSubject(cs CertificateSubject) (string, error) { var subject string if cs.SubjectCommonName != "" && cs.SubjectCommonName != "" { - subject = "CN=" + cs.SubjectCommonName + "," + subject = "CN=" + escapeDNValue(cs.SubjectCommonName) + "," } else { return "", errors.New("build subject: common name required") // Common name is required! } if cs.SubjectOrganizationalUnit != "" && cs.SubjectOrganizationalUnit != "" { - subject += "OU=" + cs.SubjectOrganizationalUnit + "," + subject += "OU=" + escapeDNValue(cs.SubjectOrganizationalUnit) + "," } if cs.SubjectOrganization != "" && cs.SubjectOrganization != "" { - subject += "O=" + cs.SubjectOrganization + "," + subject += "O=" + escapeDNValue(cs.SubjectOrganization) + "," } if cs.SubjectLocality != "" && cs.SubjectLocality != "" { - subject += "L=" + cs.SubjectLocality + "," + subject += "L=" + escapeDNValue(cs.SubjectLocality) + "," } if cs.SubjectState != "" && cs.SubjectState != "" { - subject += "ST=" + cs.SubjectState + "," + subject += "ST=" + escapeDNValue(cs.SubjectState) + "," } if cs.SubjectCountry != "" && cs.SubjectCountry != "" { - subject += "C=" + cs.SubjectCountry + "," + subject += "C=" + escapeDNValue(cs.SubjectCountry) + "," } subject = strings.TrimRight(subject, ",") // remove trailing comma log.Printf("[DEBUG] createSubject(): Certificate subject created: %s\n", subject) return subject, nil } +// escapeDNValue ensures that a value in a DN is properly escaped if it contains special characters. +func escapeDNValue(value string) string { + // If the value contains a comma, quote it + if strings.Contains(value, ",") { + return `"` + value + `"` + } + return value +} + // validateDeployPFXArgs validates the arguments required to deploy a PFX certificate. func validateDeployPFXArgs(dpfxa *DeployPFXArgs) error { if dpfxa.StoreIds == nil { diff --git a/v3/go.mod b/v3/go.mod index 6a0ed78..a2b12ff 100644 --- a/v3/go.mod +++ b/v3/go.mod @@ -19,7 +19,7 @@ go 1.23 toolchain go1.23.2 require ( - github.com/Keyfactor/keyfactor-auth-client-go v1.2.0-rc.5 + github.com/Keyfactor/keyfactor-auth-client-go v1.2.0 github.com/hashicorp/terraform-plugin-log v0.9.0 github.com/spbsoluble/go-pkcs12 v0.3.3 go.mozilla.org/pkcs7 v0.9.0 diff --git a/v3/go.sum b/v3/go.sum index 5656f2b..2b13c8a 100644 --- a/v3/go.sum +++ b/v3/go.sum @@ -14,8 +14,8 @@ github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJ github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 h1:kYRSnvJju5gYVyhkij+RTJ/VR6QIUaCfWeaFm2ycsjQ= github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= -github.com/Keyfactor/keyfactor-auth-client-go v1.2.0-rc.5 h1:3BpabiIcvdSszX7cx7OLBlpS8c7CnmvdquAANOD5+2o= -github.com/Keyfactor/keyfactor-auth-client-go v1.2.0-rc.5/go.mod h1:7htRcBIWn+X4fI5jaYBALSYwP84H/djN7d8y3n0ZDQ0= +github.com/Keyfactor/keyfactor-auth-client-go v1.2.0 h1:uNSlyOW5Bqpi0nsOGZtOYQzN0vP/h4S4J38jtQes+OI= +github.com/Keyfactor/keyfactor-auth-client-go v1.2.0/go.mod h1:7htRcBIWn+X4fI5jaYBALSYwP84H/djN7d8y3n0ZDQ0= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= From a6dff6fd81b29310dd8c671450a38bda37ff58ad Mon Sep 17 00:00:00 2001 From: Macey <11599974+doebrowsk@users.noreply.github.com> Date: Sun, 21 Sep 2025 16:12:17 -0500 Subject: [PATCH 06/16] Expand secret type (#47) --- v3/api/store_models.go | 83 +++++++++++++++++++++++++++++------------- v3/go.mod | 30 +++++++-------- v3/go.sum | 64 ++++++++++++++++---------------- 3 files changed, 104 insertions(+), 73 deletions(-) diff --git a/v3/api/store_models.go b/v3/api/store_models.go index 5938e6a..bf4db2c 100644 --- a/v3/api/store_models.go +++ b/v3/api/store_models.go @@ -51,14 +51,20 @@ type UpdateStoreFctArgs struct { // automatically populated by the CreateStore method. However, if configured, this field will be used. PropertiesString string `json:"Properties,omitempty"` // Mapped name-value pair field used to configure properties. - Properties map[string]interface{} `json:"-"` - AgentId string `json:"AgentId"` - AgentAssigned *bool `json:"AgentAssigned,omitempty"` - ContainerName *string `json:"ContainerName,omitempty"` - InventorySchedule *InventorySchedule `json:"InventorySchedule,omitempty"` - ReEnrollmentStatus *ReEnrollmnentConfig `json:"ReEnrollmentStatus,omitempty"` - SetNewPasswordAllowed *bool `json:"SetNewPasswordAllowed,omitempty"` - Password *StorePasswordConfig `json:"Password"` + Properties map[string]interface{} `json:"-"` + AgentId string `json:"AgentId"` + AgentAssigned *bool `json:"AgentAssigned,omitempty"` + ContainerName *string `json:"ContainerName,omitempty"` + InventorySchedule *InventorySchedule `json:"InventorySchedule,omitempty"` + ReEnrollmentStatus *ReEnrollmnentConfig `json:"ReEnrollmentStatus,omitempty"` + SetNewPasswordAllowed *bool `json:"SetNewPasswordAllowed,omitempty"` + Password *UpdateStorePasswordConfig `json:"Password"` +} + +type UpdateStorePasswordConfig struct { + SecretValue *string `json:"SecretValue"` // used for setting kf-secret value or No Value (null) + Parameters map[string]string `json:"Parameters"` + Provider int `json:"Provider"` } // InventorySchedule holds configuration data for creating an inventory schedule for a certificate store in Keyfactor @@ -94,34 +100,59 @@ type ReEnrollmnentConfig struct { } // StorePasswordConfig configures the password field for a new certificate store. +// TODO: make re-usable struct for Secret type fields type StorePasswordConfig struct { - Value *string `json:"SecretValue"` - SecretTypeGuid *string `json:"SecretTypeGuid,omitempty"` - InstanceId *string `json:"InstanceId,omitempty"` + Value *string `json:"SecretValue"` + SecretTypeGuid *string `json:"SecretTypeGuid,omitempty"` + InstanceId *string `json:"InstanceId,omitempty"` + InstanceGuid *string `json:"InstanceGuid,omitempty"` + ProvidererTypeParameterValues *[]ProviderTypeParameterValue `json:"ProviderTypeParameterValues"` + ProviderId int `json:"ProviderId"` + IsManaged bool `json:"IsManaged"` + HasValue bool `json:"HasValue"` } // ProviderTypeParameterValues - Not yet implemented // ProviderTypeParameterValues ProviderTypeParams - Not implemented /* Future non-critical functionality */ -type ProviderTypeParams struct { - Id string - Value string - InstanceId string - InstanceGuid string - Provider ProviderParams +type ProviderTypeParameterValue struct { + Id int `json:"Id"` + Value *string `json:"Value"` + ParameterId int `json:"ParameterId"` // defaults always to 0, likely deprecated + InstanceId *string `json:"InstanceId"` // defaults null, likely deprecated + InstanceGuid *string `json:"InstanceGuid"` + Provider *string `json:"Provider"` // defaults null, likely deprecated + ProviderTypeParam ProviderTypeParam `json:"ProviderTypeParam"` } -type ProviderParams struct { - Id int - Name string - Area int - ProviderType ProviderType +type ProviderTypeParam struct { + Id int `json:"Id"` + Name *string `json:"Name"` + DisplayName *string `json:"DisplayName"` + DataType int `json:"DataType"` + InstanceLevel bool `json:"InstanceLevel"` + ProviderType *string `json:"ProviderType"` //defaults null, likely deprecated } -type ProviderType struct { - Id string - Name string -} +// type ProviderTypeParams struct { +// Id string +// Value string +// InstanceId string +// InstanceGuid string +// Provider ProviderParams +// } + +// type ProviderParams struct { +// Id int +// Name string +// Area int +// ProviderType ProviderType +// } + +// type ProviderType struct { +// Id string +// Name string +// } // CertStoreTypeResponse contains the response elements returned from the GetCertificateStoreType method. type CertStoreTypeResponse struct { diff --git a/v3/go.mod b/v3/go.mod index 6a0ed78..d69aa62 100644 --- a/v3/go.mod +++ b/v3/go.mod @@ -14,36 +14,36 @@ module github.com/Keyfactor/keyfactor-go-client/v3 -go 1.23 +go 1.24 -toolchain go1.23.2 +toolchain go1.24.5 require ( - github.com/Keyfactor/keyfactor-auth-client-go v1.2.0-rc.5 + github.com/Keyfactor/keyfactor-auth-client-go v1.3.0 github.com/hashicorp/terraform-plugin-log v0.9.0 github.com/spbsoluble/go-pkcs12 v0.3.3 go.mozilla.org/pkcs7 v0.9.0 ) require ( - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1 // indirect - github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.3.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.0 // indirect - github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect + github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.3.1 // indirect + github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect github.com/fatih/color v1.18.0 // indirect - github.com/golang-jwt/jwt/v5 v5.2.1 // indirect + github.com/golang-jwt/jwt/v5 v5.2.2 // indirect github.com/google/uuid v1.6.0 // indirect github.com/hashicorp/go-hclog v1.6.3 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect - golang.org/x/crypto v0.32.0 // indirect - golang.org/x/net v0.34.0 // indirect - golang.org/x/oauth2 v0.25.0 // indirect - golang.org/x/sys v0.29.0 // indirect - golang.org/x/text v0.21.0 // indirect + golang.org/x/crypto v0.39.0 // indirect + golang.org/x/net v0.41.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect + golang.org/x/sys v0.33.0 // indirect + golang.org/x/text v0.26.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/v3/go.sum b/v3/go.sum index 5656f2b..2008907 100644 --- a/v3/go.sum +++ b/v3/go.sum @@ -1,21 +1,21 @@ -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 h1:g0EZJwz7xkXQiZAI5xi9f3WWFYBlX1CPTrR+NDToRkQ= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0/go.mod h1:XCW7KnZet0Opnr7HccfUw1PLc4CjHqpcaxW8DHklNkQ= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1 h1:1mvYtZfWQAnwNah/C+Z+Jb9rQH95LPE2vlmMuWAHJk8= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1/go.mod h1:75I/mXtme1JyWFtz8GocPHVFyH421IBoZErnO16dd0k= -github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.1 h1:Bk5uOhSAenHyR5P61D/NzeQCv+4fEVV8mOkJ82NqpWw= -github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.1/go.mod h1:QZ4pw3or1WPmRBxf0cHd1tknzrT54WPBOQoGutCPvSU= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.3.0 h1:WLUIpeyv04H0RCcQHaA4TNoyrQ39Ox7V+re+iaqzTe0= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.3.0/go.mod h1:hd8hTTIY3VmUVPRHNH7GVCHO3SHgXkJKZHReby/bnUQ= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.0 h1:eXnN9kaS8TiDwXjoie3hMRLuwdUBUMW9KRgOqB3mCaw= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.0/go.mod h1:XIpam8wumeZ5rVMuhdDQLMfIPDf1WO3IzrCRO3e3e3o= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 h1:B+blDbyVIG3WaikNxPnhPiJ1MThR03b3vKGtER95TP4= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1/go.mod h1:JdM5psgjfBf5fo2uWOZhflPWyDBZ/O/CNAH9CtsuZE4= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY= +github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.3.1 h1:mrkDCdkMsD4l9wjFGhofFHFrV43Y3c53RSLKOCJ5+Ow= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azsecrets v1.3.1/go.mod h1:hPv41DbqMmnxcGralanA/kVlfdH5jv3T4LxGku2E1BY= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1 h1:bFWuoEKg+gImo7pvkiQEFAc8ocibADgXeiLAxWhWmkI= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1/go.mod h1:Vih/3yc6yac2JzU4hzpaDupBJP0Flaia9rXXrU8xyww= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= -github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 h1:kYRSnvJju5gYVyhkij+RTJ/VR6QIUaCfWeaFm2ycsjQ= -github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= -github.com/Keyfactor/keyfactor-auth-client-go v1.2.0-rc.5 h1:3BpabiIcvdSszX7cx7OLBlpS8c7CnmvdquAANOD5+2o= -github.com/Keyfactor/keyfactor-auth-client-go v1.2.0-rc.5/go.mod h1:7htRcBIWn+X4fI5jaYBALSYwP84H/djN7d8y3n0ZDQ0= +github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs= +github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= +github.com/Keyfactor/keyfactor-auth-client-go v1.3.0 h1:otC213b6CYzqeN9b3CRlH1Qj1hTFIN5nqPA8gTlHdLg= +github.com/Keyfactor/keyfactor-auth-client-go v1.3.0/go.mod h1:97vCisBNkdCK0l2TuvOSdjlpvQa4+GHsMut1UTyv1jo= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -26,8 +26,8 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cu github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= -github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk= -github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= +github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -36,8 +36,8 @@ github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB1 github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= github.com/hashicorp/terraform-plugin-log v0.9.0 h1:i7hOA+vdAItN1/7UrfBqBwvYPQ9TFvymaRGZED3FCV0= github.com/hashicorp/terraform-plugin-log v0.9.0/go.mod h1:rKL8egZQ/eXSyDqzLUuwUYLVdlYeamldAHSxjUFADow= -github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs= -github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw= +github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU= +github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -58,8 +58,8 @@ github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmd github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= -github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= +github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI= +github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/spbsoluble/go-pkcs12 v0.3.3 h1:3nh7IKn16RDpmrSMtOu1JvbB0XHYq1j+IsICdU1c7J4= @@ -70,12 +70,12 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= go.mozilla.org/pkcs7 v0.9.0 h1:yM4/HS9dYv7ri2biPtxt8ikvB37a980dg69/pKmS+eI= go.mozilla.org/pkcs7 v0.9.0/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= -golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= -golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= -golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= -golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= -golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= +golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= +golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= +golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -83,10 +83,10 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= +golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= From 8dc5fd16f5559b083bcd5cf2b506b1c620fe03f3 Mon Sep 17 00:00:00 2001 From: spbsoluble <1661003+spbsoluble@users.noreply.github.com> Date: Tue, 23 Sep 2025 13:24:50 -0700 Subject: [PATCH 07/16] fix(certs): For V2 PFX enrollments don't require `subject` and update validation logic to require a `subject` or at least 1 `SAN` --- v3/api/certificate.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/v3/api/certificate.go b/v3/api/certificate.go index 95a7af8..8775a50 100644 --- a/v3/api/certificate.go +++ b/v3/api/certificate.go @@ -151,7 +151,11 @@ func (c *Client) EnrollPFXV2(ea *EnrollPFXFctArgsV2) (*EnrollResponseV2, error) } ea.SubjectString = subject } else { - return nil, fmt.Errorf("subject is required to use enrollpfx(). Please configure either SubjectString or Subject") + log.Println("[DEBUG] EnrollPFXV2: Subject is nil checks if there are SANs") + if ea.SANs == nil || (len(ea.SANs.DNS) == 0 && len(ea.SANs.URI) == 0 && len(ea.SANs.IP4) == 0 && + len(ea.SANs.IP6) == 0) { + return nil, fmt.Errorf("subject or subject alternative names are required to use enrollpfx(). Please configure either SubjectString or Subject or SANs") + } } } From de70b30f1a3d46e47b721a7afa2b6352c1386c11 Mon Sep 17 00:00:00 2001 From: spbsoluble <1661003+spbsoluble@users.noreply.github.com> Date: Tue, 23 Sep 2025 13:25:53 -0700 Subject: [PATCH 08/16] feat(certs): Add fields `AdditionalEnrollmentFields`, `AlternativeKeyType`, `AlternativeKeyLength` to `EnrollPFXFctArgsV2` --- v3/api/certificate_models.go | 38 ++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/v3/api/certificate_models.go b/v3/api/certificate_models.go index 98604f6..0f0e8e5 100644 --- a/v3/api/certificate_models.go +++ b/v3/api/certificate_models.go @@ -47,27 +47,35 @@ type EnrollPFXFctArgs struct { type EnrollPFXFctArgsV2 struct { Stores []CertificateStore `json:"Stores,omitempty"` CustomFriendlyName string `json:"CustomFriendlyName,omitempty"` - Password string `json:"Password"` + Password string `json:"Password,omitempty"` PopulateMissingValuesFromAD bool `json:"PopulateMissingValuesFromAD"` // Configure the SubjectString field as the full string subject for the certificate. For example, if you don't have // subject fields individually separated, and the subject is already in the format required by RFC5280, use the SubjectString field. - SubjectString string `json:"Subject"` // If the certificate subject is not already in the format required by RFC5280, configure the subject fields using a CertificateSubject // struct, and EnrollPFX will automatically compile this information into a proper subject. - Subject *CertificateSubject `json:"-"` - IncludeChain bool `json:"IncludeChain"` - RenewalCertificateId int `json:"RenewalCertificateId,omitempty"` - CertificateAuthority string `json:"CertificateAuthority"` - Timestamp string `json:"Timestamp"` - Template string `json:"Template"` - SANs *SANs `json:"SANs,omitempty"` - Metadata map[string]interface{} `json:"Metadata,omitempty"` - CertFormat string `json:"-"` - InstallIntoExistingCertificateStores bool `json:"InstallIntoExistingCertificateStores,omitempty"` - ChainOrder string `json:"ChainOrder,omitempty"` - KeyType string `json:"KeyType,omitempty"` - KeyLength int `json:"KeyLength,omitempty"` + Subject *CertificateSubject `json:"-"` + SubjectString string `json:"Subject,omitempty"` + IncludeChain bool `json:"IncludeChain"` + IncludeSubjectHeader bool `json:"IncludeSubjectHeader,omitempty"` + RenewalCertificateId int `json:"RenewalCertificateId,omitempty"` + CertificateAuthority string `json:"CertificateAuthority"` + Timestamp string `json:"Timestamp"` + Template string `json:"Template"` + SANs *SANs `json:"SANs,omitempty"` + Metadata map[string]interface{} `json:"Metadata,omitempty"` + AdditionalEnrollmentFields *map[string]interface{} `json:"AdditionalEnrollmentFields,omitempty"` + CertFormat string `json:"-"` // Needs to be passed as header X-Certificate-Format + InstallIntoExistingCertificateStores bool `json:"InstallIntoExistingCertificateStores,omitempty"` + ChainOrder string `json:"ChainOrder,omitempty"` + AlternativeKeyType string `json:"AlternativeKeyType,omitempty"` // Requires Command 25.0.0+ + KeyType string `json:"KeyType,omitempty"` + AlternativeKeyLength int `json:"AlternativeKeyLength,omitempty"` // Requires Command 25.0.0+ + KeyLength int `json:"KeyLength,omitempty"` + Curve string `json:"Curve,omitempty"` + EnrollmentPatternId int `json:"EnrollmentPatternId,omitempty"` // Requires Command 25.1.0+ + OwnerRoleId int `json:"OwnerRoleId,omitempty"` // Requires Command 12.3.0+ + OwnerRoleName string `json:"OwnerRoleName,omitempty"` // Requires Command 12.3.0+ } // EnrollCSRFctArgs holds the function arguments used for calling the EnrollCSR method. From 2612c4c37f3cf500d757a885a0b59192940a2886 Mon Sep 17 00:00:00 2001 From: spbsoluble <1661003+spbsoluble@users.noreply.github.com> Date: Tue, 23 Sep 2025 14:55:44 -0700 Subject: [PATCH 09/16] feat(enrollmentpatterns): Add `/EnrollmentPattern` models and endpoints --- v3/api/enrollment_patterns.go | 270 +++++++++++++++++++++++++++ v3/api/enrollment_patterns_models.go | 222 ++++++++++++++++++++++ 2 files changed, 492 insertions(+) create mode 100644 v3/api/enrollment_patterns.go create mode 100644 v3/api/enrollment_patterns_models.go diff --git a/v3/api/enrollment_patterns.go b/v3/api/enrollment_patterns.go new file mode 100644 index 0000000..574feb0 --- /dev/null +++ b/v3/api/enrollment_patterns.go @@ -0,0 +1,270 @@ +// Copyright 2024 Keyfactor +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package api + +import ( + "encoding/json" + "errors" + "fmt" + "log" + "strconv" + "strings" +) + +// CreateEnrollmentPattern creates a new enrollment pattern with the provided properties +func (c *Client) CreateEnrollmentPattern( + req *EnrollmentPatternCreateRequest, + forceTemplateDefault ...bool, +) (*EnrollmentPatternResponse, error) { + log.Println("[INFO] Creating enrollment pattern with Keyfactor") + + // Validate required fields + var missingFields []string + if req.Name == "" { + missingFields = append(missingFields, "Name") + } + if req.Template == 0 { + missingFields = append(missingFields, "Template") + } + + if len(missingFields) > 0 { + return nil, errors.New("Required field(s) missing: " + strings.Join(missingFields, ", ")) + } + + // Set Keyfactor-specific headers + headers := &apiHeaders{ + Headers: []StringTuple{ + {"x-keyfactor-api-version", "1"}, + {"x-keyfactor-requested-with", "APIClient"}, + {"Content-Type", "application/json"}, + }, + } + + // Build URL with query parameters + endpoint := "EnrollmentPatterns" + if len(forceTemplateDefault) > 0 && forceTemplateDefault[0] { + endpoint += "?forceTemplateDefault=true" + } + + keyfactorAPIStruct := &request{ + Method: "POST", + Endpoint: endpoint, + Headers: headers, + Payload: req, + } + + resp, err := c.sendRequest(keyfactorAPIStruct) + if err != nil { + return nil, err + } + + jsonResp := &EnrollmentPatternResponse{} + err = json.NewDecoder(resp.Body).Decode(&jsonResp) + if err != nil { + return nil, err + } + + return jsonResp, nil +} + +// GetEnrollmentPatterns returns all enrollment patterns according to the provided filter and output parameters +func (c *Client) GetEnrollmentPatterns(params ...*EnrollmentPatternsQueryParams) ([]EnrollmentPatternResponse, error) { + log.Println("[INFO] Fetching enrollment patterns from Keyfactor") + + // Set Keyfactor-specific headers + headers := &apiHeaders{ + Headers: []StringTuple{ + {"x-keyfactor-api-version", "1"}, + {"x-keyfactor-requested-with", "APIClient"}, + }, + } + + // Build URL with query parameters + endpoint := "EnrollmentPatterns" + var queryParams []string + + if len(params) > 0 && params[0] != nil { + param := params[0] + if param.QueryString != "" { + queryParams = append(queryParams, "QueryString="+param.QueryString) + } + if param.PageReturned > 0 { + queryParams = append(queryParams, "PageReturned="+strconv.Itoa(param.PageReturned)) + } + if param.ReturnLimit > 0 { + queryParams = append(queryParams, "ReturnLimit="+strconv.Itoa(param.ReturnLimit)) + } + if param.SortField != "" { + queryParams = append(queryParams, "SortField="+param.SortField) + } + if param.SortAscending != nil { + queryParams = append(queryParams, "SortAscending="+strconv.Itoa(*param.SortAscending)) + } + } + + if len(queryParams) > 0 { + endpoint += "?" + strings.Join(queryParams, "&") + } + + keyfactorAPIStruct := &request{ + Method: "GET", + Endpoint: endpoint, + Headers: headers, + } + + resp, err := c.sendRequest(keyfactorAPIStruct) + if err != nil { + return nil, err + } + + var jsonResp []EnrollmentPatternResponse + err = json.NewDecoder(resp.Body).Decode(&jsonResp) + if err != nil { + return nil, err + } + + return jsonResp, nil +} + +// GetEnrollmentPattern returns the enrollment pattern associated with the provided ID +func (c *Client) GetEnrollmentPattern(id int) (*EnrollmentPatternResponse, error) { + log.Printf("[INFO] Fetching enrollment pattern with ID %d from Keyfactor", id) + + if id <= 0 { + return nil, errors.New("ID must be a positive integer") + } + + // Set Keyfactor-specific headers + headers := &apiHeaders{ + Headers: []StringTuple{ + {"x-keyfactor-api-version", "1"}, + {"x-keyfactor-requested-with", "APIClient"}, + }, + } + + keyfactorAPIStruct := &request{ + Method: "GET", + Endpoint: fmt.Sprintf("EnrollmentPatterns/%d", id), + Headers: headers, + } + + resp, err := c.sendRequest(keyfactorAPIStruct) + if err != nil { + return nil, err + } + + var jsonResp EnrollmentPatternResponse + err = json.NewDecoder(resp.Body).Decode(&jsonResp) + if err != nil { + return nil, err + } + + return &jsonResp, nil +} + +// UpdateEnrollmentPattern updates an enrollment pattern according to the provided properties and Keyfactor identifier +func (c *Client) UpdateEnrollmentPattern( + id int, + req *EnrollmentPatternRequest, + forceTemplateDefault ...bool, +) (*EnrollmentPatternResponse, error) { + log.Printf("[INFO] Updating enrollment pattern with ID %d in Keyfactor", id) + + if id <= 0 { + return nil, errors.New("ID must be a positive integer") + } + + // Validate required fields + var missingFields []string + if req.Name == "" { + missingFields = append(missingFields, "Name") + } + + if len(missingFields) > 0 { + return nil, errors.New("Required field(s) missing: " + strings.Join(missingFields, ", ")) + } + + // Set Keyfactor-specific headers + headers := &apiHeaders{ + Headers: []StringTuple{ + {"x-keyfactor-api-version", "1"}, + {"x-keyfactor-requested-with", "APIClient"}, + {"Content-Type", "application/json"}, + }, + } + + // Build URL with query parameters + endpoint := fmt.Sprintf("EnrollmentPatterns/%d", id) + if len(forceTemplateDefault) > 0 && forceTemplateDefault[0] { + endpoint += "?forceTemplateDefault=true" + } + + keyfactorAPIStruct := &request{ + Method: "PUT", + Endpoint: endpoint, + Headers: headers, + Payload: req, + } + + resp, err := c.sendRequest(keyfactorAPIStruct) + if err != nil { + return nil, err + } + + var jsonResp EnrollmentPatternResponse + err = json.NewDecoder(resp.Body).Decode(&jsonResp) + if err != nil { + return nil, err + } + + return &jsonResp, nil +} + +// DeleteEnrollmentPattern deletes an enrollment pattern by ID +// Note: This method assumes DELETE is supported based on REST conventions, +// though it may not be explicitly defined in the provided schema +func (c *Client) DeleteEnrollmentPattern(id int) error { + log.Printf("[INFO] Deleting enrollment pattern with ID %d from Keyfactor", id) + + if id <= 0 { + return errors.New("ID must be a positive integer") + } + + // Set Keyfactor-specific headers + headers := &apiHeaders{ + Headers: []StringTuple{ + {"x-keyfactor-api-version", "1"}, + {"x-keyfactor-requested-with", "APIClient"}, + }, + } + + keyfactorAPIStruct := &request{ + Method: "DELETE", + Endpoint: fmt.Sprintf("EnrollmentPatterns/%d", id), + Headers: headers, + } + + resp, err := c.sendRequest(keyfactorAPIStruct) + if err != nil { + return err + } + + // Check if the response indicates success (2xx status codes) + if resp.StatusCode < 200 || resp.StatusCode >= 300 { + return fmt.Errorf("failed to delete enrollment pattern: HTTP %d", resp.StatusCode) + } + + return nil +} diff --git a/v3/api/enrollment_patterns_models.go b/v3/api/enrollment_patterns_models.go new file mode 100644 index 0000000..c8a2392 --- /dev/null +++ b/v3/api/enrollment_patterns_models.go @@ -0,0 +1,222 @@ +// Copyright 2024 Keyfactor +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package api + +// EnrollmentPatternCreateRequest represents the request structure for creating a new enrollment pattern +type EnrollmentPatternCreateRequest struct { + Template int `json:"Template"` + Name string `json:"Name"` + Description *string `json:"Description,omitempty"` + TemplateDefault bool `json:"TemplateDefault,omitempty"` + AssociatedRoles []string `json:"AssociatedRoles,omitempty"` + UseADPermissions bool `json:"UseADPermissions,omitempty"` + CertificateAuthorities []int `json:"CertificateAuthorities,omitempty"` + AllowedEnrollmentTypes int `json:"AllowedEnrollmentTypes,omitempty"` + Regexes []EnrollmentPatternRegexesRequest `json:"Regexes,omitempty"` + MetadataFields []EnrollmentPatternMetadataFieldRequest `json:"MetadataFields,omitempty"` + RestrictCAs bool `json:"RestrictCAs,omitempty"` + Policies EnrollmentPatternPolicyRequest `json:"Policies"` + Defaults []EnrollmentPatternDefaultRequest `json:"Defaults,omitempty"` + EnrollmentFields []EnrollmentPatternFieldRequest `json:"EnrollmentFields,omitempty"` +} + +// EnrollmentPatternRequest represents the request structure for updating an enrollment pattern +type EnrollmentPatternRequest struct { + Name string `json:"Name"` + Description *string `json:"Description,omitempty"` + TemplateDefault bool `json:"TemplateDefault,omitempty"` + AssociatedRoles []string `json:"AssociatedRoles,omitempty"` + UseADPermissions bool `json:"UseADPermissions,omitempty"` + CertificateAuthorities []int `json:"CertificateAuthorities,omitempty"` + AllowedEnrollmentTypes int `json:"AllowedEnrollmentTypes,omitempty"` + Regexes []EnrollmentPatternRegexesRequest `json:"Regexes,omitempty"` + MetadataFields []EnrollmentPatternMetadataFieldRequest `json:"MetadataFields,omitempty"` + RestrictCAs bool `json:"RestrictCAs,omitempty"` + Policies EnrollmentPatternPolicyRequest `json:"Policies"` + Defaults []EnrollmentPatternDefaultRequest `json:"Defaults,omitempty"` + EnrollmentFields []EnrollmentPatternFieldRequest `json:"EnrollmentFields,omitempty"` +} + +// EnrollmentPatternResponse represents the response structure for enrollment pattern operations +type EnrollmentPatternResponse struct { + ID int `json:"Id,omitempty"` + Name *string `json:"Name,omitempty"` + Description *string `json:"Description,omitempty"` + Template *EnrollmentPatternTemplateResponse `json:"Template,omitempty"` + TemplateDefault bool `json:"TemplateDefault,omitempty"` + UseADPermissions bool `json:"UseADPermissions,omitempty"` + AssociatedRoles []EnrollmentPatternAssociatedRoleResponse `json:"AssociatedRoles,omitempty"` + CertificateAuthorities []EnrollmentPatternCAResponse `json:"CertificateAuthorities,omitempty"` + AllowedEnrollmentTypes *int `json:"AllowedEnrollmentTypes,omitempty"` + Regexes []EnrollmentPatternRegexesResponse `json:"Regexes,omitempty"` + MetadataFields []EnrollmentPatternMetadataFieldResponse `json:"MetadataFields,omitempty"` + RestrictCAs bool `json:"RestrictCAs,omitempty"` + Policies *EnrollmentPatternPolicyResponse `json:"Policies,omitempty"` + Defaults []EnrollmentPatternDefaultResponse `json:"Defaults,omitempty"` + EnrollmentFields []EnrollmentPatternFieldResponse `json:"EnrollmentFields,omitempty"` +} + +// EnrollmentPatternRegexesRequest represents regex validation rules for enrollment patterns +type EnrollmentPatternRegexesRequest struct { + SubjectPart string `json:"SubjectPart"` + Regex string `json:"Regex,omitempty"` + Error string `json:"Error,omitempty"` + CaseSensitive bool `json:"CaseSensitive,omitempty"` +} + +// EnrollmentPatternRegexesResponse represents regex validation rules in responses +type EnrollmentPatternRegexesResponse struct { + SubjectPart *string `json:"SubjectPart,omitempty"` + Regex *string `json:"Regex,omitempty"` + Error *string `json:"Error,omitempty"` + CaseSensitive bool `json:"CaseSensitive,omitempty"` +} + +// EnrollmentPatternPolicyRequest represents policy settings for enrollment patterns +type EnrollmentPatternPolicyRequest struct { + AllowKeyReuse *bool `json:"AllowKeyReuse,omitempty"` + AllowWildcards *bool `json:"AllowWildcards,omitempty"` + RFCEnforcement *bool `json:"RFCEnforcement,omitempty"` + CertificateOwnerRole *int `json:"CertificateOwnerRole,omitempty"` + DefaultCertificateOwnerRoleId *int `json:"DefaultCertificateOwnerRoleId,omitempty"` + DefaultCertificateOwnerRoleName *string `json:"DefaultCertificateOwnerRoleName,omitempty"` + DefaultCertificateOwnerOverride bool `json:"DefaultCertificateOwnerOverride,omitempty"` + PrimaryKeyAlgorithms []AlgorithmDataRequestV2 `json:"PrimaryKeyAlgorithms,omitempty"` + AlternativeKeyAlgorithms []AlgorithmDataRequestV2 `json:"AlternativeKeyAlgorithms,omitempty"` +} + +// EnrollmentPatternPolicyResponse represents policy settings in responses +type EnrollmentPatternPolicyResponse struct { + AllowKeyReuse *bool `json:"AllowKeyReuse,omitempty"` + AllowWildcards *bool `json:"AllowWildcards,omitempty"` + RFCEnforcement *bool `json:"RFCEnforcement,omitempty"` + CertificateOwnerRole *int `json:"CertificateOwnerRole,omitempty"` + DefaultCertificateOwnerRoleId *int `json:"DefaultCertificateOwnerRoleId,omitempty"` + DefaultCertificateOwnerRoleName *string `json:"DefaultCertificateOwnerRoleName,omitempty"` + DefaultCertificateOwnerOverride bool `json:"DefaultCertificateOwnerOverride,omitempty"` + PrimaryKeyAlgorithms []AlgorithmDataResponse `json:"PrimaryKeyAlgorithms,omitempty"` + AlternativeKeyAlgorithms []AlgorithmDataResponse `json:"AlternativeKeyAlgorithms,omitempty"` +} + +// EnrollmentPatternMetadataFieldRequest represents metadata field configuration for requests +type EnrollmentPatternMetadataFieldRequest struct { + Id int `json:"Id,omitempty"` + DefaultValue string `json:"DefaultValue,omitempty"` + Validation string `json:"Validation,omitempty"` + Enrollment int `json:"Enrollment,omitempty"` + Message string `json:"Message,omitempty"` + Options string `json:"Options,omitempty"` + DependsOn string `json:"DependsOn,omitempty"` + DependsOnValue string `json:"DependsOnValue,omitempty"` +} + +// EnrollmentPatternMetadataFieldResponse represents metadata field configuration in responses +type EnrollmentPatternMetadataFieldResponse struct { + Id *int `json:"Id,omitempty"` + Name *string `json:"Name,omitempty"` + DefaultValue *string `json:"DefaultValue,omitempty"` + Validation *string `json:"Validation,omitempty"` + Enrollment *int `json:"Enrollment,omitempty"` + Message *string `json:"Message,omitempty"` + Options *string `json:"Options,omitempty"` + DependsOn *string `json:"DependsOn,omitempty"` + DependsOnValue *string `json:"DependsOnValue,omitempty"` + DataType *int `json:"DataType,omitempty"` + Hint *string `json:"Hint,omitempty"` +} + +// EnrollmentPatternDefaultRequest represents default value settings for requests +type EnrollmentPatternDefaultRequest struct { + SubjectPart string `json:"SubjectPart"` + DefaultValue string `json:"DefaultValue,omitempty"` +} + +// EnrollmentPatternDefaultResponse represents default value settings in responses +type EnrollmentPatternDefaultResponse struct { + SubjectPart *string `json:"SubjectPart,omitempty"` + DefaultValue *string `json:"DefaultValue,omitempty"` +} + +// EnrollmentPatternFieldRequest represents enrollment field configuration for requests +type EnrollmentPatternFieldRequest struct { + Id int `json:"Id,omitempty"` + DefaultValue string `json:"DefaultValue,omitempty"` + Validation string `json:"Validation,omitempty"` + Enrollment int `json:"Enrollment,omitempty"` + Message string `json:"Message,omitempty"` + Options string `json:"Options,omitempty"` + DependsOn string `json:"DependsOn,omitempty"` + DependsOnValue string `json:"DependsOnValue,omitempty"` +} + +// EnrollmentPatternFieldResponse represents enrollment field configuration in responses +type EnrollmentPatternFieldResponse struct { + Id *int `json:"Id,omitempty"` + Name *string `json:"Name,omitempty"` + DefaultValue *string `json:"DefaultValue,omitempty"` + Validation *string `json:"Validation,omitempty"` + Enrollment *int `json:"Enrollment,omitempty"` + Message *string `json:"Message,omitempty"` + Options *string `json:"Options,omitempty"` + DependsOn *string `json:"DependsOn,omitempty"` + DependsOnValue *string `json:"DependsOnValue,omitempty"` + DataType *int `json:"DataType,omitempty"` + Hint *string `json:"Hint,omitempty"` +} + +// EnrollmentPatternTemplateResponse represents template information in responses +type EnrollmentPatternTemplateResponse struct { + Id *int `json:"Id,omitempty"` + Name *string `json:"Name,omitempty"` + CommonName *string `json:"CommonName,omitempty"` + DisplayName *string `json:"DisplayName,omitempty"` +} + +// EnrollmentPatternAssociatedRoleResponse represents associated role information in responses +type EnrollmentPatternAssociatedRoleResponse struct { + Id *int `json:"Id,omitempty"` + Name *string `json:"Name,omitempty"` +} + +// EnrollmentPatternCAResponse represents certificate authority information in responses +type EnrollmentPatternCAResponse struct { + Id *int `json:"Id,omitempty"` + LogicalName *string `json:"LogicalName,omitempty"` + HostName *string `json:"HostName,omitempty"` + CAName *string `json:"CAName,omitempty"` +} + +// AlgorithmDataRequestV2 represents algorithm configuration for requests +type AlgorithmDataRequestV2 struct { + KeyType *string `json:"KeyType,omitempty"` + KeySize *int `json:"KeySize,omitempty"` + CurveName *string `json:"CurveName,omitempty"` +} + +// AlgorithmDataResponse represents algorithm configuration in responses +type AlgorithmDataResponse struct { + KeyType *string `json:"KeyType,omitempty"` + KeySize *int `json:"KeySize,omitempty"` + CurveName *string `json:"CurveName,omitempty"` +} + +// EnrollmentPatternsQueryParams represents query parameters for listing enrollment patterns +type EnrollmentPatternsQueryParams struct { + QueryString string `json:"queryString,omitempty"` + PageReturned int `json:"pageReturned,omitempty"` + ReturnLimit int `json:"returnLimit,omitempty"` + SortField string `json:"sortField,omitempty"` + SortAscending *int `json:"sortAscending,omitempty"` // 0=ascending, 1=descending +} From b1091890eebe5d90de61f3ee51a4eb8f654e4a77 Mon Sep 17 00:00:00 2001 From: spbsoluble <1661003+spbsoluble@users.noreply.github.com> Date: Wed, 24 Sep 2025 09:47:57 -0700 Subject: [PATCH 10/16] feat(certficates): Add `OwnerRoleId,OwnerRoleName,AltKeyAlgorithm,AltKeySizeInBits,AltKeyType,IssuedEmail,AltSigningAlgorithm,AltKeyTypeString,HasAltPrivateKey,CARecordId,Curve,EnrollmentPatternId` to `GetCertificateResponse` model --- v3/api/certificate_models.go | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/v3/api/certificate_models.go b/v3/api/certificate_models.go index 0f0e8e5..0dd8021 100644 --- a/v3/api/certificate_models.go +++ b/v3/api/certificate_models.go @@ -221,16 +221,25 @@ type GetCertificateResponse struct { NotAfter string `json:"NotAfter"` IssuerDN string `json:"IssuerDN"` PrincipalId string `json:"PrincipalId"` + OwnerRoleId int `json:"OwnerRoleId;omitempty"` // Requires Command 12.3.0+ + OwnerRoleName string `json:"OwnerRoleName,omitempty"` // Requires Command 12.3.0+ TemplateId int `json:"TemplateId"` CertState int `json:"CertState"` KeySizeInBits int `json:"KeySizeInBits"` KeyType int `json:"KeyType"` + KeyAlgorithm string `json:"KeyAlgorithm"` + AltKeyAlgorithm string `json:"AltKeyAlgorithm,omitempty"` // Requires Command 25.0.0+ + AltKeySizeInBits int `json:"AltKeySizeInBits,omitempty"` // Requires Command 25.0.0+ + AltKeyType int `json:"AltKeyType,omitempty"` // Requires Command 25.0.0+ RequesterId int `json:"RequesterId"` IssuedOU string `json:"IssuedOU"` + IssuedEmail string `json:"IssuedEmail"` KeyUsage int `json:"KeyUsage"` SigningAlgorithm string `json:"SigningAlgorithm"` + AltSigningAlgorithm string `json:"AltSigningAlgorithm,omitempty"` // Requires Command 25.0.0+ CertStateString string `json:"CertStateString"` KeyTypeString string `json:"KeyTypeString"` + AltKeyTypeString string `json:"AltKeyTypeString,omitempty"` // Requires Command 25.0.0+ RevocationEffDate string `json:"RevocationEffDate"` RevocationReason int `json:"RevocationReason"` RevocationComment string `json:"RevocationComment"` @@ -239,6 +248,7 @@ type GetCertificateResponse struct { TemplateName string `json:"TemplateName"` ArchivedKey bool `json:"ArchivedKey"` HasPrivateKey bool `json:"HasPrivateKey"` + HasAltPrivateKey bool `json:"HasAltPrivateKey,omitempty"` // Requires Command 25.0.0+ PrincipalName string `json:"PrincipalName"` CertRequestId int `json:"CertRequestId"` RequesterName string `json:"RequesterName"` @@ -252,8 +262,11 @@ type GetCertificateResponse struct { Metadata interface{} `json:"Metadata"` CertificateKeyId int `json:"CertificateKeyId"` CARowIndex int `json:"CARowIndex"` + CARecordId string `json:"CARecordId"` DetailedKeyUsage []DetailedKeyUsage `json:"detailed_key_usage"` KeyRecoverable bool `json:"KeyRecoverable"` + Curve string `json:"Curve,omitempty"` + EnrollmentPatternId int `json:"EnrollmentPatternId,omitempty"` // Requires Command 25.1.0+ } type ListCertificateResponse struct { From 5175bb8d3dfe554c5db9f15960cd2a4a10b7321a Mon Sep 17 00:00:00 2001 From: spbsoluble <1661003+spbsoluble@users.noreply.github.com> Date: Wed, 24 Sep 2025 09:48:57 -0700 Subject: [PATCH 11/16] fix(stores): Change `Password` type from `UpdateStorePasswordConfig` to `StorePasswordConfig` on `UpdateStoreFctArgs` --- v3/api/store_models.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/v3/api/store_models.go b/v3/api/store_models.go index bf4db2c..a210cad 100644 --- a/v3/api/store_models.go +++ b/v3/api/store_models.go @@ -51,14 +51,14 @@ type UpdateStoreFctArgs struct { // automatically populated by the CreateStore method. However, if configured, this field will be used. PropertiesString string `json:"Properties,omitempty"` // Mapped name-value pair field used to configure properties. - Properties map[string]interface{} `json:"-"` - AgentId string `json:"AgentId"` - AgentAssigned *bool `json:"AgentAssigned,omitempty"` - ContainerName *string `json:"ContainerName,omitempty"` - InventorySchedule *InventorySchedule `json:"InventorySchedule,omitempty"` - ReEnrollmentStatus *ReEnrollmnentConfig `json:"ReEnrollmentStatus,omitempty"` - SetNewPasswordAllowed *bool `json:"SetNewPasswordAllowed,omitempty"` - Password *UpdateStorePasswordConfig `json:"Password"` + Properties map[string]interface{} `json:"-"` + AgentId string `json:"AgentId"` + AgentAssigned *bool `json:"AgentAssigned,omitempty"` + ContainerName *string `json:"ContainerName,omitempty"` + InventorySchedule *InventorySchedule `json:"InventorySchedule,omitempty"` + ReEnrollmentStatus *ReEnrollmnentConfig `json:"ReEnrollmentStatus,omitempty"` + SetNewPasswordAllowed *bool `json:"SetNewPasswordAllowed,omitempty"` + Password *StorePasswordConfig `json:"Password"` } type UpdateStorePasswordConfig struct { From bb177bc5c58f3aade6c97b23babdb81b1ce6cf8c Mon Sep 17 00:00:00 2001 From: spbsoluble <1661003+spbsoluble@users.noreply.github.com> Date: Wed, 24 Sep 2025 12:04:49 -0700 Subject: [PATCH 12/16] fix(certs): Check that `Template` and `EnrollmentPattern` are not both empty for enrollments --- v3/api/certificate.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v3/api/certificate.go b/v3/api/certificate.go index 8775a50..eccb9e6 100644 --- a/v3/api/certificate.go +++ b/v3/api/certificate.go @@ -113,8 +113,8 @@ func (c *Client) EnrollPFXV2(ea *EnrollPFXFctArgsV2) (*EnrollResponseV2, error) var missingFields []string // TODO: Probably a better way to express these if blocks - if ea.Template == "" { - missingFields = append(missingFields, "Template") + if ea.Template == "" && ea.EnrollmentPatternId == 0 { + missingFields = append(missingFields, "Template or EnrollmentPatternId") } if ea.CertificateAuthority == "" { missingFields = append(missingFields, "CertificateAuthority") From 02ac064269c83b5e1f0ac4c0decf85f1f552629f Mon Sep 17 00:00:00 2001 From: spbsoluble <1661003+spbsoluble@users.noreply.github.com> Date: Wed, 24 Sep 2025 13:24:09 -0700 Subject: [PATCH 13/16] fix(certs): Add `ChangeCertificateOwnerRole` --- v3/api/certificate.go | 62 ++++++++++++++++++++++++++++++++++++ v3/api/certificate_models.go | 12 +++++++ 2 files changed, 74 insertions(+) diff --git a/v3/api/certificate.go b/v3/api/certificate.go index eccb9e6..a73794b 100644 --- a/v3/api/certificate.go +++ b/v3/api/certificate.go @@ -768,6 +768,68 @@ func (c *Client) RecoverCertificate( return priv, leaf, chain, nil } +// ChangeCertificateOwnerRole changes the certificate's owner. Users must be in the current owner's role and the new owner's role. +// If removing the owner, leave both NewRoleId and NewRoleName empty in the request. +// Calls PUT /Certificates/{id}/Owner endpoint. +func (c *Client) ChangeCertificateOwnerRole( + certificateId int, + req *OwnerRequest, + params ...*CertificateOwnerChangeParams, +) error { + log.Printf("[INFO] Changing owner of certificate with ID %d in Keyfactor", certificateId) + + // Validate certificate ID + if certificateId <= 0 { + return errors.New("certificate ID must be a positive integer") + } + + // Set Keyfactor-specific headers + headers := &apiHeaders{ + Headers: []StringTuple{ + {"x-keyfactor-api-version", "1"}, + {"x-keyfactor-requested-with", "APIClient"}, + {"Content-Type", "application/json"}, + }, + } + + // Build URL with query parameters + endpoint := fmt.Sprintf("Certificates/%d/Owner", certificateId) + var queryParams []string + + if len(params) > 0 && params[0] != nil { + param := params[0] + if param.CollectionId != nil { + queryParams = append(queryParams, fmt.Sprintf("collectionId=%d", *param.CollectionId)) + } + if param.ContainerId != nil { + queryParams = append(queryParams, fmt.Sprintf("containerId=%d", *param.ContainerId)) + } + } + + if len(queryParams) > 0 { + endpoint += "?" + strings.Join(queryParams, "&") + } + + keyfactorAPIStruct := &request{ + Method: "PUT", + Endpoint: endpoint, + Headers: headers, + Payload: req, + } + + resp, err := c.sendRequest(keyfactorAPIStruct) + if err != nil { + return err + } + + // Check if the response indicates success (204 No Content expected) + if resp.StatusCode != http.StatusNoContent { + return fmt.Errorf("failed to change certificate owner: HTTP %d", resp.StatusCode) + } + + return nil +} + // createSubject builds the certificate subject string from a passed CertificateSubject argument. func createSubject(cs CertificateSubject) (string, error) { var subject string diff --git a/v3/api/certificate_models.go b/v3/api/certificate_models.go index 0dd8021..e1c2b8b 100644 --- a/v3/api/certificate_models.go +++ b/v3/api/certificate_models.go @@ -340,3 +340,15 @@ type SubjectAltNameElements struct { type downloadCertificateResponse struct { Content string `json:"Content"` } + +// OwnerRequest represents the request structure for changing certificate ownership +type OwnerRequest struct { + NewRoleId *int `json:"NewRoleId,omitempty"` + NewRoleName *string `json:"NewRoleName,omitempty"` +} + +// CertificateOwnerChangeParams represents the parameters for changing certificate ownership +type CertificateOwnerChangeParams struct { + CollectionId *int `json:"collectionId,omitempty"` + ContainerId *int `json:"containerId,omitempty"` +} From c2ae7d3cca05b83cb15394e498ff602c4126e8b7 Mon Sep 17 00:00:00 2001 From: spbsoluble <1661003+spbsoluble@users.noreply.github.com> Date: Thu, 25 Sep 2025 11:21:08 -0700 Subject: [PATCH 14/16] feat(certs): Add the following fields to `EnrollCSRFctArgs`: `PrivateKey,RenewalCertificateId,AdditionalEnrollmentFields,EnrollmentPatternId,OwnerRoleId,OwnerRoleName,IncludeSubjectHeader` --- v3/api/certificate_models.go | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/v3/api/certificate_models.go b/v3/api/certificate_models.go index e1c2b8b..945ab65 100644 --- a/v3/api/certificate_models.go +++ b/v3/api/certificate_models.go @@ -80,14 +80,21 @@ type EnrollPFXFctArgsV2 struct { // EnrollCSRFctArgs holds the function arguments used for calling the EnrollCSR method. type EnrollCSRFctArgs struct { - CSR string - Timestamp string `json:"Timestamp"` - Template string `json:"Template"` - CertFormat string `json:"-"` - CertificateAuthority string `json:"CertificateAuthority"` - IncludeChain bool `json:"IncludeChain"` - SANs *SANs `json:"SANs"` - Metadata map[string]interface{} `json:"Metadata"` + CSR string `json:"CSR"` //required + PrivateKey string `json:"PrivateKey,omitempty"` + RenewalCertificateId int `json:"RenewalCertificateId,omitempty"` + CertificateAuthority string `json:"CertificateAuthority,omitempty"` + IncludeChain bool `json:"IncludeChain"` + IncludeSubjectHeader bool `json:"IncludeSubjectHeader,omitempty"` + Timestamp string `json:"Timestamp"` + Template string `json:"Template,omitempty"` + EnrollmentPatternId int `json:"EnrollmentPatternId,omitempty"` // Requires Command 25.1.0+ + CertFormat string `json:"-"` + SANs *SANs `json:"SANs,omitempty"` + Metadata map[string]interface{} `json:"Metadata,omitempty"` + AdditionalEnrollmentFields map[string]interface{} `json:"AdditionalEnrollmentFields,omitempty"` + OwnerRoleId int `json:"OwnerRoleId,omitempty"` // Requires Command 12.3.0+ + OwnerRoleName string `json:"OwnerRoleName,omitempty"` // Requires Command 12.3.0+ } // RevokeCertArgs holds the function arguments used for calling the RevokeCert method. From f82208c941177c623d56a3edfdd2dc4c635d8608 Mon Sep 17 00:00:00 2001 From: spbsoluble <1661003+spbsoluble@users.noreply.github.com> Date: Sun, 28 Sep 2025 13:25:20 -0700 Subject: [PATCH 15/16] feat(certs): Add base64 response from `DownloadCertificate` --- v3/api/certificate.go | 127 ++++++++++++++++++++++++++++++++---------- 1 file changed, 99 insertions(+), 28 deletions(-) diff --git a/v3/api/certificate.go b/v3/api/certificate.go index a73794b..bd01c40 100644 --- a/v3/api/certificate.go +++ b/v3/api/certificate.go @@ -195,13 +195,16 @@ func (c *Client) EnrollPFXV2(ea *EnrollPFXFctArgsV2) (*EnrollResponseV2, error) // Returns: // - Leaf certificate // - Certificate chain +// - Raw certificate data (as base64 string, if applicable) +// - Error func (c *Client) DownloadCertificate( certId int, thumbprint string, serialNumber string, issuerDn string, collectionId int, -) (*x509.Certificate, []*x509.Certificate, error) { + certificateFormat string, +) (*x509.Certificate, []*x509.Certificate, *string, error) { log.Println("[INFO] Downloading certificate") /* The download certificate endpoint requires one of the following to retrieve a cert: @@ -221,7 +224,7 @@ func (c *Client) DownloadCertificate( } if !validInput { - return nil, nil, fmt.Errorf("certID, thumbprint, or serial number AND issuer DN required to dowload certificate") + return nil, nil, nil, fmt.Errorf("certID, thumbprint, or serial number AND issuer DN required to dowload certificate") } payload := &downloadCertificateBody{ @@ -247,11 +250,19 @@ func (c *Client) DownloadCertificate( } // Set Keyfactor-specific headers + switch certificateFormat { + case "CER", "CRT", "DER", "PEM": + // do nothing these are valid formats + break + default: + // if not specified or invalid format then default to P7B + certificateFormat = "P7B" + } headers := &apiHeaders{ Headers: []StringTuple{ {"x-keyfactor-api-version", "1"}, {"x-keyfactor-requested-with", "APIClient"}, - {"x-certificateformat", "P7B"}, + {"x-certificateformat", certificateFormat}, }, } @@ -265,13 +276,13 @@ func (c *Client) DownloadCertificate( resp, err := c.sendRequest(keyfactorAPIStruct) if err != nil { - return nil, nil, err + return nil, nil, nil, err } jsonResp := &downloadCertificateResponse{} err = json.NewDecoder(resp.Body).Decode(&jsonResp) if err != nil { - return nil, nil, err + return nil, nil, nil, err } //buf, err := base64.StdEncoding.DecodeString(jsonResp.Content) //if err != nil { @@ -285,17 +296,17 @@ func (c *Client) DownloadCertificate( certs, p7bErr := ConvertBase64P7BtoCertificates(jsonResp.Content) if p7bErr != nil { - return nil, nil, p7bErr + return nil, nil, &jsonResp.Content, p7bErr } var leaf *x509.Certificate if len(certs) > 1 { //leaf is last cert in chain leaf = certs[0] // First cert in chain is the leaf - return leaf, certs, nil + return leaf, certs, &jsonResp.Content, nil } - return certs[0], nil, nil + return certs[0], nil, &jsonResp.Content, nil } // EnrollCSR takes arguments for EnrollCSRFctArgs to enroll a passed Certificate Signing @@ -663,7 +674,8 @@ func (c *Client) RecoverCertificate( issuerDn string, password string, collectionId int, -) (interface{}, *x509.Certificate, []*x509.Certificate, error) { + certificateFormat string, +) (interface{}, *x509.Certificate, []*x509.Certificate, *string, error) { log.Println("[DEBUG] Enter RecoverCertificate") log.Println("[INFO] Recovering certificate ID:", certId) /* The download certificate endpoint requires one of the following to retrieve a cert: @@ -673,6 +685,9 @@ func (c *Client) RecoverCertificate( Check for this input */ + if certificateFormat == "" { + certificateFormat = "PFX" + } validInput := false if certId != 0 { validInput = true @@ -684,12 +699,12 @@ func (c *Client) RecoverCertificate( if !validInput { log.Println("[ERROR] RecoverCertificate: certID, thumbprint, or serial number AND issuer DN required to download certificate") - return nil, nil, nil, fmt.Errorf("certID, thumbprint, or serial number AND issuer DN required to download certificate") + return nil, nil, nil, nil, fmt.Errorf("certID, thumbprint, or serial number AND issuer DN required to download certificate") } log.Println("[DEBUG] RecoverCertificate: Valid input") if password == "" { - return nil, nil, nil, fmt.Errorf("password required to recover private key with certificate") + return nil, nil, nil, nil, fmt.Errorf("password required to recover private key with certificate") } rca := &recoverCertArgs{ @@ -707,7 +722,7 @@ func (c *Client) RecoverCertificate( Headers: []StringTuple{ {"x-keyfactor-api-version", "1"}, {"x-keyfactor-requested-with", "APIClient"}, - {"x-certificateformat", "PFX"}, + {"x-certificateformat", certificateFormat}, }, } @@ -738,7 +753,7 @@ func (c *Client) RecoverCertificate( resp, err := c.sendRequest(keyfactorAPIStruct) if err != nil { log.Println("[ERROR] RecoverCertificate: Error recovering certificate from Keyfactor Command", err.Error()) - return nil, nil, nil, err + return nil, nil, nil, nil, err } jsonResp := &recoverCertResponse{} @@ -746,26 +761,82 @@ func (c *Client) RecoverCertificate( err = json.NewDecoder(resp.Body).Decode(&jsonResp) if err != nil { log.Println("[ERROR] RecoverCertificate: Error decoding response from Keyfactor Command", err.Error()) - return nil, nil, nil, err + return nil, nil, nil, nil, err } - log.Println("[DEBUG] RecoverCertificate: Decoding PFX") - pfxDer, err := base64.StdEncoding.DecodeString(jsonResp.PFX) - if err != nil { - log.Println("[ERROR] RecoverCertificate: Error decoding PFX", err.Error()) - return nil, nil, nil, err - } + switch certificateFormat { + case "PFX", "pfx", "pkcs12", "p12", "jks", "JKS": + log.Println("[DEBUG] RecoverCertificate: decoding `PFX` response field") + pfxDer := jsonResp.PFX + if pfxDer == "" { + log.Println("[ERROR] RecoverCertificate: Error decoding PFX", err.Error()) + return nil, nil, nil, &pfxDer, fmt.Errorf("pfx field in response is empty") + } + log.Println("[INFO] Recovered certificate successfully") + log.Println("[DEBUG] RecoverCertificate returning in PFX format") + return nil, nil, nil, &pfxDer, nil + case "PEM", "pem": + log.Println("[DEBUG] RecoverCertificate: Decoding PFX") + pfxDer, dErr := base64.StdEncoding.DecodeString(jsonResp.PFX) + if dErr != nil { + log.Println("[ERROR] RecoverCertificate: Error decoding PFX", dErr.Error()) + return nil, nil, nil, &jsonResp.PFX, dErr + } - log.Println("[DEBUG] RecoverCertificate: Decoding PFX chain") - priv, leaf, chain, err := pkcs12.DecodeChain(pfxDer, rca.Password) - if err != nil { - log.Println("[ERROR] RecoverCertificate: Error decoding PFX chain", err.Error()) - return nil, nil, nil, err + log.Println("[DEBUG] RecoverCertificate: Decoding PFX chain") + priv, leaf, chain, pErr := pkcs12.DecodeChain(pfxDer, rca.Password) + if pErr != nil { + log.Println("[ERROR] RecoverCertificate: Error decoding PFX chain", pErr.Error()) + return nil, nil, nil, &jsonResp.PFX, pErr + } + + log.Println("[INFO] Recovered certificate successfully") + log.Println("[DEBUG] RecoverCertificate: ", leaf, chain) + return priv, leaf, chain, &jsonResp.PFX, nil + default: + log.Println("[DEBUG] RecoverCertificate: Decoding PFX") + pfxDer, dErr := base64.StdEncoding.DecodeString(jsonResp.PFX) + if dErr != nil { + log.Println("[ERROR] RecoverCertificate: Error decoding PFX", dErr.Error()) + return nil, nil, nil, &jsonResp.PFX, dErr + } + + log.Println("[DEBUG] RecoverCertificate: Decoding PFX chain") + priv, leaf, chain, pErr := pkcs12.DecodeChain(pfxDer, rca.Password) + if pErr != nil { + log.Println("[ERROR] RecoverCertificate: Error decoding PFX chain", pErr.Error()) + return nil, nil, nil, &jsonResp.PFX, pErr + } + + log.Println("[INFO] Recovered certificate successfully") + log.Println("[DEBUG] RecoverCertificate returning in PEM format") + + var pemCerts []string + + // Encode leaf certificate to PEM + pemLeaf := pem.EncodeToMemory( + &pem.Block{ + Type: "CERTIFICATE", + Bytes: leaf.Raw, + }, + ) + pemCerts = append(pemCerts, string(pemLeaf)) + + // Encode chain certificates to PEM + for _, cert := range chain { + pemCert := pem.EncodeToMemory( + &pem.Block{ + Type: "CERTIFICATE", + Bytes: cert.Raw, + }, + ) + pemCerts = append(pemCerts, string(pemCert)) + } + + pemData := strings.Join(pemCerts, "\n") + return priv, leaf, chain, &pemData, nil } - log.Println("[INFO] Recovered certificate successfully") - log.Println("[DEBUG] RecoverCertificate: ", leaf, chain) - return priv, leaf, chain, nil } // ChangeCertificateOwnerRole changes the certificate's owner. Users must be in the current owner's role and the new owner's role. From 6868250fcac81776bcfb6b495f11a22ef25d2686 Mon Sep 17 00:00:00 2001 From: spbsoluble <1661003+spbsoluble@users.noreply.github.com> Date: Wed, 1 Oct 2025 11:00:48 -0700 Subject: [PATCH 16/16] fix(patterns): Fix enrollmentpattern models --- v3/api/enrollment_patterns_models.go | 97 ++++++++++++++-------------- 1 file changed, 47 insertions(+), 50 deletions(-) diff --git a/v3/api/enrollment_patterns_models.go b/v3/api/enrollment_patterns_models.go index c8a2392..0d2ac42 100644 --- a/v3/api/enrollment_patterns_models.go +++ b/v3/api/enrollment_patterns_models.go @@ -35,7 +35,7 @@ type EnrollmentPatternCreateRequest struct { // EnrollmentPatternRequest represents the request structure for updating an enrollment pattern type EnrollmentPatternRequest struct { Name string `json:"Name"` - Description *string `json:"Description,omitempty"` + Description string `json:"Description,omitempty"` TemplateDefault bool `json:"TemplateDefault,omitempty"` AssociatedRoles []string `json:"AssociatedRoles,omitempty"` UseADPermissions bool `json:"UseADPermissions,omitempty"` @@ -52,8 +52,8 @@ type EnrollmentPatternRequest struct { // EnrollmentPatternResponse represents the response structure for enrollment pattern operations type EnrollmentPatternResponse struct { ID int `json:"Id,omitempty"` - Name *string `json:"Name,omitempty"` - Description *string `json:"Description,omitempty"` + Name string `json:"Name,omitempty"` + Description string `json:"Description,omitempty"` Template *EnrollmentPatternTemplateResponse `json:"Template,omitempty"` TemplateDefault bool `json:"TemplateDefault,omitempty"` UseADPermissions bool `json:"UseADPermissions,omitempty"` @@ -78,10 +78,10 @@ type EnrollmentPatternRegexesRequest struct { // EnrollmentPatternRegexesResponse represents regex validation rules in responses type EnrollmentPatternRegexesResponse struct { - SubjectPart *string `json:"SubjectPart,omitempty"` - Regex *string `json:"Regex,omitempty"` - Error *string `json:"Error,omitempty"` - CaseSensitive bool `json:"CaseSensitive,omitempty"` + SubjectPart string `json:"SubjectPart,omitempty"` + Regex string `json:"Regex,omitempty"` + Error string `json:"Error,omitempty"` + CaseSensitive bool `json:"CaseSensitive,omitempty"` } // EnrollmentPatternPolicyRequest represents policy settings for enrollment patterns @@ -99,12 +99,12 @@ type EnrollmentPatternPolicyRequest struct { // EnrollmentPatternPolicyResponse represents policy settings in responses type EnrollmentPatternPolicyResponse struct { - AllowKeyReuse *bool `json:"AllowKeyReuse,omitempty"` - AllowWildcards *bool `json:"AllowWildcards,omitempty"` - RFCEnforcement *bool `json:"RFCEnforcement,omitempty"` - CertificateOwnerRole *int `json:"CertificateOwnerRole,omitempty"` - DefaultCertificateOwnerRoleId *int `json:"DefaultCertificateOwnerRoleId,omitempty"` - DefaultCertificateOwnerRoleName *string `json:"DefaultCertificateOwnerRoleName,omitempty"` + AllowKeyReuse bool `json:"AllowKeyReuse,omitempty"` + AllowWildcards bool `json:"AllowWildcards,omitempty"` + RFCEnforcement bool `json:"RFCEnforcement,omitempty"` + CertificateOwnerRole int `json:"CertificateOwnerRole,omitempty"` + DefaultCertificateOwnerRoleId int `json:"DefaultCertificateOwnerRoleId,omitempty"` + DefaultCertificateOwnerRoleName string `json:"DefaultCertificateOwnerRoleName,omitempty"` DefaultCertificateOwnerOverride bool `json:"DefaultCertificateOwnerOverride,omitempty"` PrimaryKeyAlgorithms []AlgorithmDataResponse `json:"PrimaryKeyAlgorithms,omitempty"` AlternativeKeyAlgorithms []AlgorithmDataResponse `json:"AlternativeKeyAlgorithms,omitempty"` @@ -124,17 +124,12 @@ type EnrollmentPatternMetadataFieldRequest struct { // EnrollmentPatternMetadataFieldResponse represents metadata field configuration in responses type EnrollmentPatternMetadataFieldResponse struct { - Id *int `json:"Id,omitempty"` - Name *string `json:"Name,omitempty"` - DefaultValue *string `json:"DefaultValue,omitempty"` - Validation *string `json:"Validation,omitempty"` - Enrollment *int `json:"Enrollment,omitempty"` - Message *string `json:"Message,omitempty"` - Options *string `json:"Options,omitempty"` - DependsOn *string `json:"DependsOn,omitempty"` - DependsOnValue *string `json:"DependsOnValue,omitempty"` - DataType *int `json:"DataType,omitempty"` - Hint *string `json:"Hint,omitempty"` + MetadataId int `json:"MetadataId,omitempty"` + DefaultValue string `json:"DefaultValue,omitempty"` + Validation string `json:"Validation,omitempty"` + Enrollment int `json:"Enrollment,omitempty"` + Message string `json:"Message,omitempty"` + CaseSensitive bool `json:"CaseSensitive,omitempty"` } // EnrollmentPatternDefaultRequest represents default value settings for requests @@ -145,8 +140,8 @@ type EnrollmentPatternDefaultRequest struct { // EnrollmentPatternDefaultResponse represents default value settings in responses type EnrollmentPatternDefaultResponse struct { - SubjectPart *string `json:"SubjectPart,omitempty"` - DefaultValue *string `json:"DefaultValue,omitempty"` + SubjectPart string `json:"SubjectPart,omitempty"` + Value string `json:"Value,omitempty"` } // EnrollmentPatternFieldRequest represents enrollment field configuration for requests @@ -163,39 +158,41 @@ type EnrollmentPatternFieldRequest struct { // EnrollmentPatternFieldResponse represents enrollment field configuration in responses type EnrollmentPatternFieldResponse struct { - Id *int `json:"Id,omitempty"` - Name *string `json:"Name,omitempty"` - DefaultValue *string `json:"DefaultValue,omitempty"` - Validation *string `json:"Validation,omitempty"` - Enrollment *int `json:"Enrollment,omitempty"` - Message *string `json:"Message,omitempty"` - Options *string `json:"Options,omitempty"` - DependsOn *string `json:"DependsOn,omitempty"` - DependsOnValue *string `json:"DependsOnValue,omitempty"` - DataType *int `json:"DataType,omitempty"` - Hint *string `json:"Hint,omitempty"` + Id int `json:"Id,omitempty"` + Name string `json:"Name,omitempty"` + DefaultValue string `json:"DefaultValue,omitempty"` + Validation string `json:"Validation,omitempty"` + Enrollment int `json:"Enrollment,omitempty"` + Message string `json:"Message,omitempty"` + Options []string `json:"Options,omitempty"` + DependsOn string `json:"DependsOn,omitempty"` + DependsOnValue string `json:"DependsOnValue,omitempty"` + DataType int `json:"DataType,omitempty"` + Hint string `json:"Hint,omitempty"` } // EnrollmentPatternTemplateResponse represents template information in responses type EnrollmentPatternTemplateResponse struct { - Id *int `json:"Id,omitempty"` - Name *string `json:"Name,omitempty"` - CommonName *string `json:"CommonName,omitempty"` - DisplayName *string `json:"DisplayName,omitempty"` + Id int `json:"Id,omitempty"` + TemplateName string `json:"TemplateName,omitempty"` + CommonName string `json:"CommonName,omitempty"` + ConfigurationTenant string `json:"ConfigurationTenant,omitempty"` + RequiresApproval bool `json:"RequiresApproval,omitempty"` + FriendlyName string `json:"FriendlyName,omitempty"` } // EnrollmentPatternAssociatedRoleResponse represents associated role information in responses type EnrollmentPatternAssociatedRoleResponse struct { - Id *int `json:"Id,omitempty"` - Name *string `json:"Name,omitempty"` + Id int `json:"Id,omitempty"` + Name string `json:"Name,omitempty"` } // EnrollmentPatternCAResponse represents certificate authority information in responses type EnrollmentPatternCAResponse struct { - Id *int `json:"Id,omitempty"` - LogicalName *string `json:"LogicalName,omitempty"` - HostName *string `json:"HostName,omitempty"` - CAName *string `json:"CAName,omitempty"` + Id int `json:"Id,omitempty"` + LogicalName string `json:"LogicalName,omitempty"` + HostName string `json:"HostName,omitempty"` + ConfigurationTenant string `json:"ConfigurationTenant,omitempty"` } // AlgorithmDataRequestV2 represents algorithm configuration for requests @@ -207,9 +204,9 @@ type AlgorithmDataRequestV2 struct { // AlgorithmDataResponse represents algorithm configuration in responses type AlgorithmDataResponse struct { - KeyType *string `json:"KeyType,omitempty"` - KeySize *int `json:"KeySize,omitempty"` - CurveName *string `json:"CurveName,omitempty"` + Name string `json:"Name,omitempty"` + BitLengths []int `json:"bit_lengths,omitempty"` + Curves []string `json:"curves,omitempty"` } // EnrollmentPatternsQueryParams represents query parameters for listing enrollment patterns