7
7
* and limitations under the License.
8
8
*/
9
9
10
- package keyfactor
10
+ package kfbackend
11
11
12
12
import (
13
13
"context"
14
- b64 "encoding/base64"
15
14
"encoding/json"
16
15
"errors"
17
16
"fmt"
18
17
"io"
19
- "io/ioutil"
20
18
"net/http"
21
19
"strings"
22
20
"sync"
@@ -27,7 +25,9 @@ import (
27
25
"github.com/hashicorp/vault/sdk/logical"
28
26
)
29
27
30
- //var config map[string]string
28
+ const (
29
+ operationPrefixKeyfactor string = "keyfactor"
30
+ )
31
31
32
32
// Factory configures and returns backend
33
33
func Factory (ctx context.Context , conf * logical.BackendConfig ) (logical.Backend , error ) {
@@ -42,7 +42,7 @@ func Factory(ctx context.Context, conf *logical.BackendConfig) (logical.Backend,
42
42
// // Store certificates by serial number
43
43
type keyfactorBackend struct {
44
44
* framework.Backend
45
- lock sync.RWMutex
45
+ configLock sync.RWMutex
46
46
cachedConfig * keyfactorConfig
47
47
client * keyfactorClient
48
48
}
@@ -68,19 +68,31 @@ func backend() *keyfactorBackend {
68
68
pathCA (& b ),
69
69
pathCerts (& b ),
70
70
),
71
- Secrets : []* framework.Secret {},
72
- BackendType : logical .TypeLogical ,
73
- Invalidate : b .invalidate ,
71
+ Secrets : []* framework.Secret {},
72
+ BackendType : logical .TypeLogical ,
73
+ Invalidate : b .invalidate ,
74
+ InitializeFunc : b .Initialize ,
74
75
}
75
76
return & b
76
77
}
77
78
78
79
// reset clears any client configuration for a new
79
80
// backend to be configured
80
81
func (b * keyfactorBackend ) reset () {
81
- b .lock .Lock ()
82
- defer b .lock .Unlock ()
82
+ b .configLock .RLock ()
83
+ defer b .configLock .RUnlock ()
84
+ b .cachedConfig = nil
83
85
b .client = nil
86
+
87
+ }
88
+
89
+ func (b * keyfactorBackend ) Initialize (ctx context.Context , req * logical.InitializationRequest ) error {
90
+ b .configLock .RLock ()
91
+ defer b .configLock .RUnlock ()
92
+ if req == nil {
93
+ return fmt .Errorf ("initialization request is nil" )
94
+ }
95
+ return nil
84
96
}
85
97
86
98
// invalidate clears an existing client configuration in
@@ -94,63 +106,73 @@ func (b *keyfactorBackend) invalidate(ctx context.Context, key string) {
94
106
// getClient locks the backend as it configures and creates a
95
107
// a new client for the target API
96
108
func (b * keyfactorBackend ) getClient (ctx context.Context , s logical.Storage ) (* keyfactorClient , error ) {
97
- b .lock .RLock ()
98
- unlockFunc := b .lock .RUnlock
99
- defer func () { unlockFunc () }()
109
+ b .configLock .RLock ()
110
+ defer b .configLock .RUnlock ()
100
111
101
112
if b .client != nil {
102
113
return b .client , nil
103
114
}
104
115
105
- b .lock .RUnlock ()
106
- b .lock .Lock ()
107
- unlockFunc = b .lock .Unlock
116
+ // get configuration
117
+ config , err := b .fetchConfig (ctx , s )
118
+ if err != nil {
119
+ return nil , err
120
+ }
121
+ if config == nil {
122
+ return nil , errors .New ("configuration is empty" )
123
+ }
108
124
109
- return nil , fmt .Errorf ("need to return client" )
125
+ b .client , err = newClient (config , b )
126
+ if err != nil {
127
+ return nil , err
128
+ }
129
+ return b .client , nil
110
130
}
111
131
112
132
// Handle interface with Keyfactor API to enroll a certificate with given content
113
- func (b * keyfactorBackend ) submitCSR (ctx context.Context , req * logical.Request , csr string , caName string , templateName string ) ([]string , string , error ) {
114
- config , err := b .config (ctx , req .Storage )
133
+ func (b * keyfactorBackend ) submitCSR (ctx context.Context , req * logical.Request , csr string , caName string , templateName string , metaDataJson string ) ([]string , string , error ) {
134
+ config , err := b .fetchConfig (ctx , req .Storage )
115
135
if err != nil {
116
136
return nil , "" , err
117
137
}
118
138
if config == nil {
119
139
return nil , "" , errors .New ("configuration is empty" )
120
140
}
121
141
122
- ca := config .CertAuthority
123
- template := config .CertTemplate
124
-
125
- creds := config .Username + ":" + config .Password
126
- encCreds := b64 .StdEncoding .EncodeToString ([]byte (creds ))
127
-
128
142
location , _ := time .LoadLocation ("UTC" )
129
143
t := time .Now ().In (location )
130
144
time := t .Format ("2006-01-02T15:04:05" )
131
145
132
- // This is only needed when running as a vault extension
146
+ // get client
147
+ client , err := b .getClient (ctx , req .Storage )
148
+ if err != nil {
149
+ return nil , "" , fmt .Errorf ("error getting client: %w" , err )
150
+ }
151
+
133
152
b .Logger ().Debug ("Closing idle connections" )
134
- http .DefaultClient .CloseIdleConnections ()
153
+ client .httpClient .CloseIdleConnections ()
154
+
155
+ // build request parameter structure
135
156
136
- // Build request
137
- url := config .KeyfactorUrl + "/KeyfactorAPI/Enrollment/CSR"
157
+ url := config .KeyfactorUrl + "/" + config .CommandAPIPath + "/Enrollment/CSR"
138
158
b .Logger ().Debug ("url: " + url )
139
- bodyContent := "{\" CSR\" : \" " + csr + "\" ,\" CertificateAuthority\" :\" " + ca + "\" ,\" IncludeChain\" : true, \" Metadata\" : {} , \" Timestamp\" : \" " + time + "\" ,\" Template\" : \" " + template + "\" ,\" SANs\" : {}}"
159
+ bodyContent := "{\" CSR\" : \" " + csr + "\" ,\" CertificateAuthority\" :\" " + caName + "\" ,\" IncludeChain\" : true, \" Metadata\" : " + metaDataJson + " , \" Timestamp\" : \" " + time + "\" ,\" Template\" : \" " + templateName + "\" ,\" SANs\" : {}}"
140
160
payload := strings .NewReader (bodyContent )
141
161
b .Logger ().Debug ("body: " + bodyContent )
142
162
httpReq , err := http .NewRequest ("POST" , url , payload )
163
+
143
164
if err != nil {
144
165
b .Logger ().Info ("Error forming request: {{err}}" , err )
145
166
}
167
+
146
168
httpReq .Header .Add ("x-keyfactor-requested-with" , "APIClient" )
147
169
httpReq .Header .Add ("content-type" , "application/json" )
148
- httpReq .Header .Add ("authorization" , "Basic " + encCreds )
149
170
httpReq .Header .Add ("x-certificateformat" , "PEM" )
150
171
151
172
// Send request and check status
173
+
152
174
b .Logger ().Debug ("About to connect to " + config .KeyfactorUrl + "for csr submission" )
153
- res , err := http . DefaultClient .Do (httpReq )
175
+ res , err := client . httpClient .Do (httpReq )
154
176
if err != nil {
155
177
b .Logger ().Info ("CSR Enrollment failed: {{err}}" , err .Error ())
156
178
return nil , "" , err
@@ -166,7 +188,7 @@ func (b *keyfactorBackend) submitCSR(ctx context.Context, req *logical.Request,
166
188
// Read response and return certificate and key
167
189
168
190
defer res .Body .Close ()
169
- body , err := ioutil .ReadAll (res .Body )
191
+ body , err := io .ReadAll (res .Body )
170
192
if err != nil {
171
193
b .Logger ().Error ("Error reading response: {{err}}" , err )
172
194
return nil , "" , err
@@ -233,3 +255,15 @@ func (b *keyfactorBackend) submitCSR(ctx context.Context, req *logical.Request,
233
255
const keyfactorHelp = `
234
256
The Keyfactor backend is a pki service that issues and manages certificates.
235
257
`
258
+
259
+ func (b * keyfactorBackend ) isValidJSON (str string ) bool {
260
+ var js json.RawMessage
261
+ err := json .Unmarshal ([]byte (str ), & js )
262
+ if err != nil {
263
+ b .Logger ().Debug (err .Error ())
264
+ return false
265
+ } else {
266
+ b .Logger ().Debug ("the metadata was able to be parsed as valid JSON" )
267
+ return true
268
+ }
269
+ }
0 commit comments