-
Notifications
You must be signed in to change notification settings - Fork 13
/
apicauth.go
298 lines (248 loc) · 7.71 KB
/
apicauth.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
// Package auth implements the apic service account token management.
// Contributed by Xenon team
package auth
import (
"fmt"
"io"
"net/http"
"strings"
"time"
"github.com/Axway/agent-sdk/pkg/api"
"github.com/Axway/agent-sdk/pkg/authz/oauth"
"github.com/Axway/agent-sdk/pkg/config"
"github.com/Axway/agent-sdk/pkg/util/log"
)
func closeHelper(closer io.Closer) {
if err := closer.Close(); err != nil {
log.Warnf("Failed to close: %v", err)
}
}
// PlatformTokenGetter - Interface for token getter
type PlatformTokenGetter interface {
tokenGetterCloser
}
// ApicAuth provides authentication methods for calls against APIC Cloud services.
type ApicAuth struct {
tenantID string
tokenGetterCloser
}
// Authenticate applies the authentication headers
func (aa *ApicAuth) Authenticate(hs HeaderSetter) error {
token, err := aa.GetToken()
if err != nil {
return err
}
hs.SetHeader("Authorization", fmt.Sprintf("Bearer %s", token))
hs.SetHeader("X-Axway-Tenant-Id", aa.tenantID)
return nil
}
// AuthenticateNet applies the authentication headers
func (aa *ApicAuth) AuthenticateNet(req *http.Request) error {
return aa.Authenticate(NetHeaderSetter{req})
}
// NewWithStatic returns an ApicAuth that uses a fixed token
func NewWithStatic(tenantID, token string) *ApicAuth {
return &ApicAuth{
tenantID,
staticTokenGetter(token),
}
}
// NewWithFlow returns an ApicAuth that uses the axway authentication flow
func NewWithFlow(tenantID, privKey, publicKey, password, url, aud, clientID string, singleURL string, timeout time.Duration) *ApicAuth {
return &ApicAuth{
tenantID,
tokenGetterWithChannel(NewPlatformTokenGetter(privKey, publicKey, password, url, aud, clientID, singleURL, timeout)),
}
}
// NewPlatformTokenGetter returns a token getter for axway ID
func NewPlatformTokenGetter(privKey, publicKey, password, url, aud, clientID string, singleURL string, timeout time.Duration) PlatformTokenGetter {
cfg := config.NewCentralConfig(config.GenericService)
centralCfg, _ := cfg.(*config.CentralConfiguration)
centralCfg.SingleURL = singleURL
acfg := cfg.GetAuthConfig()
authCfg, _ := acfg.(*config.AuthConfiguration)
authCfg.ClientID = clientID
authCfg.PrivateKey = privKey
authCfg.PublicKey = publicKey
authCfg.KeyPwd = password
authCfg.URL = url
authCfg.Timeout = timeout
return NewPlatformTokenGetterWithCentralConfig(cfg)
}
// NewPlatformTokenGetterWithCentralConfig returns a token getter for axway ID
func NewPlatformTokenGetterWithCentralConfig(centralCfg config.CentralConfig) PlatformTokenGetter {
return &platformTokenGetter{
cfg: centralCfg,
keyReader: oauth.NewKeyReader(
centralCfg.GetAuthConfig().GetPrivateKey(),
centralCfg.GetAuthConfig().GetPublicKey(),
centralCfg.GetAuthConfig().GetKeyPassword()),
}
}
type funcTokenGetter func() (string, error)
// GetToken returns the fixed token.
func (f funcTokenGetter) GetToken() (string, error) {
return f()
}
func (f funcTokenGetter) Close() error {
return nil
}
// staticTokenGetter returns a token getter with a fixed token
func staticTokenGetter(token string) funcTokenGetter {
return funcTokenGetter(func() (string, error) { return token, nil })
}
// platformTokenGetter can get an access token from apic platform.
type platformTokenGetter struct {
cfg config.CentralConfig
keyReader oauth.KeyReader
axwayIDClient oauth.AuthClient
}
// Close a PlatformTokenGetter
func (ptp *platformTokenGetter) Close() error {
return nil
}
func (ptp *platformTokenGetter) initAxwayIDPClient() error {
privateKey, err := ptp.keyReader.GetPrivateKey()
if err != nil {
return err
}
publicKey, err := ptp.keyReader.GetPublicKey()
if err != nil {
return err
}
apiClient := api.NewClient(
ptp.cfg.GetTLSConfig(),
ptp.cfg.GetProxyURL(),
api.WithTimeout(ptp.cfg.GetAuthConfig().GetTimeout()),
api.WithSingleURL())
ptp.axwayIDClient, err = oauth.NewAuthClient(ptp.cfg.GetAuthConfig().GetTokenURL(), apiClient,
oauth.WithServerName("AxwayId"),
oauth.WithKeyPairAuth(
ptp.cfg.GetAuthConfig().GetClientID(),
"",
ptp.cfg.GetAuthConfig().GetAudience(),
privateKey,
publicKey, "", oauth.SigningMethodRS256))
return err
}
// GetToken returns a token from cache if not expired or fetches a new token
func (ptp *platformTokenGetter) GetToken() (string, error) {
if ptp.axwayIDClient == nil {
err := ptp.initAxwayIDPClient()
if err != nil {
return "", err
}
}
token, err := ptp.axwayIDClient.GetToken()
if err != nil && strings.Contains(err.Error(), "bad response") {
log.Debug("please check the value for CENTRAL_AUTH_URL: The Amplify login URL. Otherwise, possibly a clock syncing issue. Please check NTP daemon, if being used, that is up and running correctly.")
}
return token, err
}
// TokenGetter provides a bearer token to be used in api calls. Might block
type TokenGetter interface {
GetToken() (string, error)
}
// TokenGetterCloser can get a token and clean up resources if needed.
type tokenGetterCloser interface {
TokenGetter
Close() error
}
// NetHeaderSetter sets headers an a net/http request
type NetHeaderSetter struct {
*http.Request
}
// SetHeader sets a header on a net/http request
func (nhs NetHeaderSetter) SetHeader(key, value string) {
nhs.Header.Set(key, value)
}
// HeaderSetter sets a header for a request
type HeaderSetter interface {
// SetHeader sets a header on a http request
SetHeader(key, value string)
}
// channelTokenGetter uses a channel to ensure synchronized access to the wrapped token getter
type channelTokenGetter struct {
tokenGetter tokenGetterCloser
responses chan struct {
string
error
}
requests chan struct{}
}
// tokenGetterWithChannel wraps a token getter in a channelTokenGetter
func tokenGetterWithChannel(tokenGetter tokenGetterCloser) *channelTokenGetter {
requests := make(chan struct{})
responses := make(chan struct {
string
error
})
ctg := &channelTokenGetter{tokenGetter, responses, requests}
go ctg.loop()
return ctg
}
// loop reads requests and responds with token from the embedded token getter
func (ctg *channelTokenGetter) loop() {
defer close(ctg.responses)
defer closeHelper(ctg.tokenGetter)
for {
if _, ok := <-ctg.requests; !ok { // wait for a request
break // if input channel is closed, stop
}
t, err := ctg.tokenGetter.GetToken()
ctg.responses <- struct { // send back a response
string
error
}{t, err}
}
}
func (ctg *channelTokenGetter) GetToken() (string, error) {
ctg.requests <- struct{}{}
resp, ok := <-ctg.responses
if !ok {
return "", fmt.Errorf("[apicauth] channelTokenGetter closed")
}
return resp.string, resp.error
}
func (ctg *channelTokenGetter) Close() error {
close(ctg.requests)
return nil
}
// tokenAuth -
type tokenAuth struct {
tenantID string
tokenRequester TokenGetter
}
// Config the auth config
type Config struct {
PrivateKey string `mapstructure:"private_key"`
PublicKey string `mapstructure:"public_key"`
KeyPassword string `mapstructure:"key_password"`
URL string `mapstructure:"url"`
Audience string `mapstructure:"audience"`
ClientID string `mapstructure:"client_id"`
Timeout time.Duration `mapstructure:"timeout"`
}
// NewTokenAuth Create a new auth token requester
func NewTokenAuth(ac Config, tenantID string) TokenGetter {
instance := &tokenAuth{tenantID: tenantID}
tokenURL := ac.URL + "/realms/Broker/protocol/openid-connect/token"
aud := ac.URL + "/realms/Broker"
cfg := &config.CentralConfiguration{}
singleURL := cfg.GetSingleURL()
instance.tokenRequester = NewPlatformTokenGetter(
ac.PrivateKey,
ac.PublicKey,
ac.KeyPassword,
tokenURL,
aud,
ac.ClientID,
singleURL,
ac.Timeout,
)
return instance
}
// GetToken gets a token
func (t tokenAuth) GetToken() (string, error) {
return t.tokenRequester.GetToken()
}