Skip to content

Commit 0ec0d41

Browse files
adds the ability to provide custom http client and transports
1 parent fa1c27e commit 0ec0d41

File tree

10 files changed

+268
-117
lines changed

10 files changed

+268
-117
lines changed

edge-apis/client_base.go

Lines changed: 48 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,25 @@ const (
1818
TotpRequiredHeader = "totp-required"
1919
)
2020

21-
// AuthEnabledApi is used as a sentinel interface to detect APIs that support authentication and to work around a golang
22-
// limitation dealing with accessing field of generically typed fields.
21+
// AuthEnabledApi is a sentinel interface that detects APIs supporting authentication.
22+
// It provides methods for authenticating, managing sessions, and discovering controllers for high-availability.
2323
type AuthEnabledApi interface {
24-
//Authenticate will attempt to issue an authentication request using the provided credentials and http client.
25-
//These functions act as abstraction around the underlying go-swagger generated client and will use the default
26-
//http client if not provided.
24+
// Authenticate authenticates using the provided credentials and returns an ApiSession for subsequent authenticated requests.
2725
Authenticate(credentials Credentials, configTypes []string, httpClient *http.Client) (ApiSession, error)
26+
// SetUseOidc forces OIDC mode (true) or legacy mode (false).
2827
SetUseOidc(bool)
28+
// ListControllers returns the list of available controllers for HA failover.
2929
ListControllers() (*rest_model.ControllersList, error)
30+
// GetClientTransportPool returns the transport pool managing multiple controller endpoints.
3031
GetClientTransportPool() ClientTransportPool
32+
// SetClientTransportPool sets the transport pool.
3133
SetClientTransportPool(ClientTransportPool)
34+
// RefreshApiSession refreshes an existing session.
3235
RefreshApiSession(apiSession ApiSession, httpClient *http.Client) (ApiSession, error)
3336
}
3437

35-
// BaseClient implements the Client interface specifically for the types specified in the ApiType constraint. It
36-
// provides shared functionality that all ApiType types require.
38+
// BaseClient provides shared authentication and session management for OpenZiti API clients.
39+
// It handles credential-based authentication, TLS configuration, session storage, and controller failover.
3740
type BaseClient[A ApiType] struct {
3841
API *A
3942
AuthEnabledApi AuthEnabledApi
@@ -84,18 +87,19 @@ func (self *BaseClient[A]) SetAllowOidcDynamicallyEnabled(allow bool) {
8487
apiType.SetAllowOidcDynamicallyEnabled(allow)
8588
}
8689

87-
// Authenticate will attempt to use the provided credentials to authenticate via the underlying ApiType. On success
88-
// the API Session details will be returned and the current client will make authenticated requests on future
89-
// calls. On an error the API Session in use will be cleared and subsequent requests will become/continue to be
90-
// made in an unauthenticated fashion.
90+
// Authenticate authenticates using provided credentials, updating the TLS configuration based on the credential's CA pool.
91+
// On success, stores the session and processes controller endpoints for HA failover.
92+
// On failure, clears the session and credentials.
9193
func (self *BaseClient[A]) Authenticate(credentials Credentials, configTypesOverride []string) (ApiSession, error) {
9294
self.Credentials = nil
9395
self.ApiSession.Store(nil)
9496

97+
tlsClientConfig := self.TlsAwareTransport.GetTlsClientConfig()
98+
9599
if credCaPool := credentials.GetCaPool(); credCaPool != nil {
96-
self.HttpTransport.TLSClientConfig.RootCAs = credCaPool
100+
tlsClientConfig.RootCAs = credCaPool
97101
} else {
98-
self.HttpTransport.TLSClientConfig.RootCAs = self.CaPool
102+
tlsClientConfig.RootCAs = self.CaPool
99103
}
100104

101105
apiSession, err := self.AuthEnabledApi.Authenticate(credentials, configTypesOverride, self.HttpClient)
@@ -116,10 +120,11 @@ func (self *BaseClient[A]) AuthenticateWithPreviousSession(credentials Credentia
116120
self.Credentials = nil
117121
self.ApiSession.Store(nil)
118122

123+
tlsClientConfig := self.TlsAwareTransport.GetTlsClientConfig()
119124
if credCaPool := credentials.GetCaPool(); credCaPool != nil {
120-
self.HttpTransport.TLSClientConfig.RootCAs = credCaPool
125+
tlsClientConfig.RootCAs = credCaPool
121126
} else {
122-
self.HttpTransport.TLSClientConfig.RootCAs = self.CaPool
127+
tlsClientConfig.RootCAs = self.CaPool
123128
}
124129

125130
refreshedSession, refreshErr := self.AuthEnabledApi.RefreshApiSession(prevApiSession, self.HttpClient)
@@ -136,24 +141,46 @@ func (self *BaseClient[A]) AuthenticateWithPreviousSession(credentials Credentia
136141
return refreshedSession, nil
137142
}
138143

139-
// initializeComponents assembles the lower level components necessary for the go-swagger/openapi facilities.
144+
// initializeComponents assembles HTTP client infrastructure, either using provided Components or creating new ones.
145+
// If Components are provided with nil transport/client, they are initialized with warnings logged.
140146
func (self *BaseClient[A]) initializeComponents(config *ApiClientConfig) {
147+
if config.Components != nil {
148+
149+
if config.Components.TlsAwareTransport == nil {
150+
pfxlog.Logger().Warn("components were provided but the transport was nil, it is being initialized")
151+
config.Components.TlsAwareTransport = NewTlsAwareHttpTransport(nil)
152+
}
153+
154+
if config.Components.HttpClient == nil {
155+
pfxlog.Logger().Warn("components were provided but the http client was nil, it is being initialized")
156+
config.Components.HttpClient = NewHttpClient(config.Components.TlsAwareTransport)
157+
}
158+
159+
self.Components = *config.Components
160+
if config.Proxy != nil {
161+
pfxlog.Logger().Warn("components were provided along with a proxy function on the ApiClientConfig, it is being ignored, if needed properly set on components")
162+
}
163+
return
164+
}
165+
141166
components := NewComponentsWithConfig(&ComponentsConfig{
142167
Proxy: config.Proxy,
143168
})
144-
components.HttpTransport.TLSClientConfig.RootCAs = config.CaPool
169+
170+
tlsClientConfig := components.TlsAwareTransport.GetTlsClientConfig()
171+
tlsClientConfig.RootCAs = config.CaPool
145172
components.CaPool = config.CaPool
146173

147174
self.Components = *components
148175
}
149176

150-
// NewRuntime creates an OpenAPI runtime configured for the specified API endpoint.
177+
// NewRuntime creates an OpenAPI runtime for communicating with a controller endpoint. Used for HA failover to add multiple controller endpoints.
151178
func NewRuntime(apiUrl *url.URL, schemes []string, httpClient *http.Client) *openapiclient.Runtime {
152179
return openapiclient.NewWithClient(apiUrl.Host, apiUrl.Path, schemes, httpClient)
153180
}
154181

155-
// AuthenticateRequest implements the openapi runtime.ClientAuthInfoWriter interface from the OpenAPI libraries. It is used
156-
// to authenticate outgoing requests.
182+
// AuthenticateRequest authenticates outgoing API requests using the current session or credentials.
183+
// It implements the openapi runtime.ClientAuthInfoWriter interface.
157184
func (self *BaseClient[A]) AuthenticateRequest(request runtime.ClientRequest, registry strfmt.Registry) error {
158185
if self.AuthInfoWriter != nil {
159186
return self.AuthInfoWriter.AuthenticateRequest(request, registry)
@@ -184,8 +211,7 @@ func (self *BaseClient[A]) AuthenticateRequest(request runtime.ClientRequest, re
184211
return nil
185212
}
186213

187-
// ProcessControllers queries the authenticated controller for its list of peer controllers
188-
// and registers them for high-availability failover.
214+
// ProcessControllers discovers peer controllers and registers them for HA failover. Called after successful authentication.
189215
func (self *BaseClient[A]) ProcessControllers(authEnabledApi AuthEnabledApi) {
190216
list, err := authEnabledApi.ListControllers()
191217

edge-apis/client_edge_client.go

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ type ClientApiClient struct {
3737
// that have not been verified from an outside secret (such as an enrollment token).
3838
func NewClientApiClient(apiUrls []*url.URL, caPool *x509.CertPool, totpCallback func(chan string)) *ClientApiClient {
3939
return NewClientApiClientWithConfig(&ApiClientConfig{
40-
ApiUrls: apiUrls,
41-
CaPool: caPool,
42-
TotpCallback: totpCallback,
43-
Proxy: http.ProxyFromEnvironment,
40+
ApiUrls: apiUrls,
41+
CaPool: caPool,
42+
TotpCodeProvider: NewTotpCodeProviderFromChStringFunc(totpCallback),
43+
Proxy: http.ProxyFromEnvironment,
4444
})
4545
}
4646

@@ -65,7 +65,7 @@ func NewClientApiClientWithConfig(config *ApiClientConfig) *ClientApiClient {
6565
newApi := rest_client_api_client.New(transportPool, nil)
6666
api := ZitiEdgeClient{
6767
ZitiEdgeClient: newApi,
68-
TotpCallback: config.TotpCallback,
68+
TotpCodeProvider: config.TotpCodeProvider,
6969
ClientTransportPool: transportPool,
7070
}
7171
ret.API = &api
@@ -94,18 +94,21 @@ type ZitiEdgeClient struct {
9494
versionInfo *rest_model.Version
9595
versionOnce sync.Once
9696

97-
TotpCallback func(chan string)
97+
TotpCodeProvider TotpCodeProvider
9898
ClientTransportPool ClientTransportPool
9999
}
100100

101+
// GetClientTransportPool returns the transport pool managing multiple controller endpoints for failover.
101102
func (self *ZitiEdgeClient) GetClientTransportPool() ClientTransportPool {
102103
return self.ClientTransportPool
103104
}
104105

106+
// SetClientTransportPool sets the transport pool.
105107
func (self *ZitiEdgeClient) SetClientTransportPool(transportPool ClientTransportPool) {
106108
self.ClientTransportPool = transportPool
107109
}
108110

111+
// ListControllers returns the list of available controllers for high-availability failover.
109112
func (self *ZitiEdgeClient) ListControllers() (*rest_model.ControllersList, error) {
110113
params := clientControllers.NewListControllersParams()
111114
resp, err := self.Controllers.ListControllers(params, nil)
@@ -133,6 +136,7 @@ func (self *ZitiEdgeClient) Authenticate(credentials Credentials, configTypesOve
133136
return self.legacyAuth(credentials, configTypesOverrides, httpClient)
134137
}
135138

139+
// legacyAuth performs zt-session token based authentication.
136140
func (self *ZitiEdgeClient) legacyAuth(credentials Credentials, configTypes []string, httpClient *http.Client) (ApiSession, error) {
137141
params := clientAuth.NewAuthenticateParams()
138142
params.Auth = credentials.Payload()
@@ -145,8 +149,9 @@ func (self *ZitiEdgeClient) legacyAuth(credentials Credentials, configTypes []st
145149

146150
certs := credentials.TlsCerts()
147151
if len(certs) != 0 {
148-
if transport, ok := httpClient.Transport.(*http.Transport); ok {
149-
transport.TLSClientConfig.Certificates = certs
152+
if transport, ok := httpClient.Transport.(TlsAwareTransport); ok {
153+
tlsClientConf := transport.GetTlsClientConfig()
154+
tlsClientConf.Certificates = certs
150155
transport.CloseIdleConnections()
151156
}
152157
}
@@ -160,19 +165,23 @@ func (self *ZitiEdgeClient) legacyAuth(credentials Credentials, configTypes []st
160165
return &ApiSessionLegacy{Detail: resp.GetPayload().Data, RequestHeaders: credentials.GetRequestHeaders()}, err
161166
}
162167

168+
// oidcAuth performs OIDC OAuth flow based authentication.
163169
func (self *ZitiEdgeClient) oidcAuth(credentials Credentials, configTypeOverrides []string, httpClient *http.Client) (ApiSession, error) {
164-
return oidcAuth(self.ClientTransportPool, credentials, configTypeOverrides, httpClient, self.TotpCallback)
170+
return oidcAuth(self.ClientTransportPool, credentials, configTypeOverrides, httpClient, self.TotpCodeProvider)
165171
}
166172

173+
// SetUseOidc forces OIDC mode (true) or legacy mode (false), overriding automatic detection.
167174
func (self *ZitiEdgeClient) SetUseOidc(use bool) {
168175
self.useOidcExplicitlySet = true
169176
self.useOidc = use
170177
}
171178

179+
// SetAllowOidcDynamicallyEnabled enables automatic OIDC capability detection on the controller.
172180
func (self *ZitiEdgeClient) SetAllowOidcDynamicallyEnabled(allow bool) {
173181
self.oidcDynamicallyEnabled = allow
174182
}
175183

184+
// RefreshApiSession refreshes an existing API session (both legacy and OIDC types).
176185
func (self *ZitiEdgeClient) RefreshApiSession(apiSession ApiSession, httpClient *http.Client) (ApiSession, error) {
177186
switch s := apiSession.(type) {
178187
case *ApiSessionLegacy:
@@ -205,10 +214,12 @@ func (self *ZitiEdgeClient) RefreshApiSession(apiSession ApiSession, httpClient
205214
return nil, errors.New("api session is an unknown type")
206215
}
207216

217+
// ExchangeTokens exchanges OIDC tokens for refreshed tokens.
208218
func (self *ZitiEdgeClient) ExchangeTokens(curTokens *oidc.Tokens[*oidc.IDTokenClaims], httpClient *http.Client) (*oidc.Tokens[*oidc.IDTokenClaims], error) {
209219
return exchangeTokens(self.ClientTransportPool, curTokens, httpClient)
210220
}
211221

222+
// ControllerSupportsHa checks if the controller supports high-availability by inspecting its capabilities.
212223
func (self *ZitiEdgeClient) ControllerSupportsHa() bool {
213224
self.doOnceCacheVersionInfo()
214225

@@ -219,6 +230,7 @@ func (self *ZitiEdgeClient) ControllerSupportsHa() bool {
219230
return false
220231
}
221232

233+
// ControllerSupportsOidc checks if the controller supports OIDC authentication by inspecting its capabilities.
222234
func (self *ZitiEdgeClient) ControllerSupportsOidc() bool {
223235
self.doOnceCacheVersionInfo()
224236

@@ -229,6 +241,8 @@ func (self *ZitiEdgeClient) ControllerSupportsOidc() bool {
229241
return false
230242
}
231243

244+
// doOnceCacheVersionInfo caches the controller version information including capabilities on first call.
245+
// Subsequent calls are no-ops due to sync.Once synchronization.
232246
func (self *ZitiEdgeClient) doOnceCacheVersionInfo() {
233247
self.versionOnce.Do(func() {
234248
versionParams := clientInfo.NewListVersionParams()

edge-apis/client_edge_management.go

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ type ManagementApiClient struct {
3737
// that have not been verified from an outside secret (such as an enrollment token).
3838
func NewManagementApiClient(apiUrls []*url.URL, caPool *x509.CertPool, totpCallback func(chan string)) *ManagementApiClient {
3939
return NewManagementApiClientWithConfig(&ApiClientConfig{
40-
ApiUrls: apiUrls,
41-
CaPool: caPool,
42-
TotpCallback: totpCallback,
43-
Proxy: http.ProxyFromEnvironment,
40+
ApiUrls: apiUrls,
41+
CaPool: caPool,
42+
TotpCodeProvider: NewTotpCodeProviderFromChStringFunc(totpCallback),
43+
Proxy: http.ProxyFromEnvironment,
4444
})
4545
}
4646

@@ -51,6 +51,7 @@ func NewManagementApiClientWithConfig(config *ApiClientConfig) *ManagementApiCli
5151
ret.ApiBinding = "edge-management"
5252
ret.ApiVersion = "v1"
5353
ret.ApiUrls = config.ApiUrls
54+
5455
ret.initializeComponents(config)
5556

5657
transportPool := NewClientTransportPoolRandom()
@@ -64,7 +65,7 @@ func NewManagementApiClientWithConfig(config *ApiClientConfig) *ManagementApiCli
6465
newApi := rest_management_api_client.New(transportPool, nil)
6566
api := ZitiEdgeManagement{
6667
ZitiEdgeManagement: newApi,
67-
TotpCallback: config.TotpCallback,
68+
TotpCodeProvider: config.TotpCodeProvider,
6869
ClientTransportPool: transportPool,
6970
}
7071

@@ -94,18 +95,21 @@ type ZitiEdgeManagement struct {
9495
versionOnce sync.Once
9596
versionInfo *rest_model.Version
9697

97-
TotpCallback func(chan string)
98+
TotpCodeProvider TotpCodeProvider
9899
ClientTransportPool ClientTransportPool
99100
}
100101

102+
// SetClientTransportPool sets the transport pool.
101103
func (self *ZitiEdgeManagement) SetClientTransportPool(transportPool ClientTransportPool) {
102104
self.ClientTransportPool = transportPool
103105
}
104106

107+
// GetClientTransportPool returns the transport pool managing multiple controller endpoints for failover.
105108
func (self *ZitiEdgeManagement) GetClientTransportPool() ClientTransportPool {
106109
return self.ClientTransportPool
107110
}
108111

112+
// ListControllers returns the list of available controllers for high-availability failover.
109113
func (self *ZitiEdgeManagement) ListControllers() (*rest_model.ControllersList, error) {
110114
params := manControllers.NewListControllersParams()
111115
resp, err := self.Controllers.ListControllers(params, nil)
@@ -133,6 +137,7 @@ func (self *ZitiEdgeManagement) Authenticate(credentials Credentials, configType
133137
return self.legacyAuth(credentials, configTypes, httpClient)
134138
}
135139

140+
// legacyAuth performs zt-session token based authentication.
136141
func (self *ZitiEdgeManagement) legacyAuth(credentials Credentials, configTypes []string, httpClient *http.Client) (ApiSession, error) {
137142
params := manAuth.NewAuthenticateParams()
138143
params.Auth = credentials.Payload()
@@ -145,8 +150,9 @@ func (self *ZitiEdgeManagement) legacyAuth(credentials Credentials, configTypes
145150

146151
certs := credentials.TlsCerts()
147152
if len(certs) != 0 {
148-
if transport, ok := httpClient.Transport.(*http.Transport); ok {
149-
transport.TLSClientConfig.Certificates = certs
153+
if transport, ok := httpClient.Transport.(TlsAwareTransport); ok {
154+
tlsClientConf := transport.GetTlsClientConfig()
155+
tlsClientConf.Certificates = certs
150156
transport.CloseIdleConnections()
151157
}
152158
}
@@ -162,19 +168,23 @@ func (self *ZitiEdgeManagement) legacyAuth(credentials Credentials, configTypes
162168
RequestHeaders: credentials.GetRequestHeaders()}, err
163169
}
164170

171+
// oidcAuth performs OIDC OAuth flow based authentication.
165172
func (self *ZitiEdgeManagement) oidcAuth(credentials Credentials, configTypeOverrides []string, httpClient *http.Client) (ApiSession, error) {
166-
return oidcAuth(self.ClientTransportPool, credentials, configTypeOverrides, httpClient, self.TotpCallback)
173+
return oidcAuth(self.ClientTransportPool, credentials, configTypeOverrides, httpClient, self.TotpCodeProvider)
167174
}
168175

176+
// SetUseOidc forces OIDC mode (true) or legacy mode (false), overriding automatic detection.
169177
func (self *ZitiEdgeManagement) SetUseOidc(use bool) {
170178
self.useOidcExplicitlySet = true
171179
self.useOidc = use
172180
}
173181

182+
// SetAllowOidcDynamicallyEnabled enables automatic OIDC capability detection on the controller.
174183
func (self *ZitiEdgeManagement) SetAllowOidcDynamicallyEnabled(allow bool) {
175184
self.oidcDynamicallyEnabled = allow
176185
}
177186

187+
// RefreshApiSession refreshes an existing API session (both legacy and OIDC types).
178188
func (self *ZitiEdgeManagement) RefreshApiSession(apiSession ApiSession, httpClient *http.Client) (ApiSession, error) {
179189
switch s := apiSession.(type) {
180190
case *ApiSessionLegacy:
@@ -207,10 +217,12 @@ func (self *ZitiEdgeManagement) RefreshApiSession(apiSession ApiSession, httpCli
207217
return nil, errors.New("api session is an unknown type")
208218
}
209219

220+
// ExchangeTokens exchanges OIDC tokens for refreshed tokens.
210221
func (self *ZitiEdgeManagement) ExchangeTokens(curTokens *oidc.Tokens[*oidc.IDTokenClaims], httpClient *http.Client) (*oidc.Tokens[*oidc.IDTokenClaims], error) {
211222
return exchangeTokens(self.ClientTransportPool, curTokens, httpClient)
212223
}
213224

225+
// ControllerSupportsHa checks if the controller supports high-availability by inspecting its capabilities.
214226
func (self *ZitiEdgeManagement) ControllerSupportsHa() bool {
215227
self.doOnceCacheVersionInfo()
216228

@@ -221,6 +233,7 @@ func (self *ZitiEdgeManagement) ControllerSupportsHa() bool {
221233
return false
222234
}
223235

236+
// ControllerSupportsOidc checks if the controller supports OIDC authentication by inspecting its capabilities.
224237
func (self *ZitiEdgeManagement) ControllerSupportsOidc() bool {
225238
self.doOnceCacheVersionInfo()
226239

@@ -231,6 +244,8 @@ func (self *ZitiEdgeManagement) ControllerSupportsOidc() bool {
231244
return false
232245
}
233246

247+
// doOnceCacheVersionInfo caches the controller version information including capabilities on first call.
248+
// Subsequent calls are no-ops due to sync.Once synchronization.
234249
func (self *ZitiEdgeManagement) doOnceCacheVersionInfo() {
235250
self.versionOnce.Do(func() {
236251
versionParams := manInfo.NewListVersionParams()

0 commit comments

Comments
 (0)