diff --git a/CHANGELOG.md b/CHANGELOG.md index 208293f..4dc63c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,14 @@ - Support for Keyfactor Command REST API endpoints up to 25.1.1. [API Change Log](https://software.keyfactor.com/Core-OnPrem/Current/Content/WebAPI/ChangeLogs/25_1_1-APIChangeLog.htm) - Add [helper methods](https://github.com/Keyfactor/keyfactor-go-client-sdk/blob/feat/AB%2372090/sdk-version-25/v25/helpers.go) to support some common lookup patterns. +# v24.0.1 +## Chores +- Bump github.com/Keyfactor/keyfactor-auth-client-go dependency from v1.1.0-rc.8 to 1.3.0. + +## Fixes +- fix: Fix issue with the OAuth token flow with explicit access token provided to config runs into an error +- fix: Skip appending port 443 to request URL to avoid duplicate port in HTTPS connections + # v24.0.0 ## Features diff --git a/v24/api/keyfactor/v1/client.go b/v24/api/keyfactor/v1/client.go index 73112a9..ba208f4 100644 --- a/v24/api/keyfactor/v1/client.go +++ b/v24/api/keyfactor/v1/client.go @@ -346,6 +346,9 @@ func buildHttpClientV2(cfg *auth_providers.Server) (AuthConfig, error) { ClientID: cfg.ClientID, ClientSecret: cfg.ClientSecret, TokenURL: cfg.OAuthTokenUrl, + AccessToken: cfg.AccessToken, + Audience: cfg.Audience, + Scopes: cfg.Scopes, } aErr := oauthCfg.Authenticate() if aErr != nil { @@ -778,7 +781,7 @@ func (c *APIClient) prepareRequest( // Override request host, if applicable serverConfig := c.GetConfig() if serverConfig.Host != "" { - if serverConfig.Port > 0 && serverConfig.Port <= 65535 { + if serverConfig.Port > 0 && serverConfig.Port <= 65535 && serverConfig.Port != 443 { url.Host = fmt.Sprintf("%s:%d", serverConfig.Host, serverConfig.Port) } else { url.Host = serverConfig.Host diff --git a/v24/api/keyfactor/v1/client_test.go b/v24/api/keyfactor/v1/client_test.go new file mode 100644 index 0000000..fadf205 --- /dev/null +++ b/v24/api/keyfactor/v1/client_test.go @@ -0,0 +1,99 @@ +package v1 + +import ( + "reflect" + "testing" + + "github.com/Keyfactor/keyfactor-auth-client-go/auth_providers" +) + +// TestCommandConfigOauth_AccessTokenFieldPropagation is a compilation + correctness +// regression test for the v2.8.0 bug where AccessToken, Audience, and Scopes were +// silently dropped when constructing CommandConfigOauth from auth_providers.Server +// in buildHttpClientV2. If any of those three fields are ever removed from either +// struct, this test fails to compile. +func TestCommandConfigOauth_AccessTokenFieldPropagation(t *testing.T) { + srv := &auth_providers.Server{ + Host: "test.example.com", + AccessToken: "mytoken-abc123", + Audience: "https://my.audience.example.com", + Scopes: []string{"read", "write", "admin"}, + } + + // Step 1: Verify Server struct holds the fields correctly + if srv.AccessToken != "mytoken-abc123" { + t.Errorf("Server.AccessToken = %q, want %q", srv.AccessToken, "mytoken-abc123") + } + if srv.Audience != "https://my.audience.example.com" { + t.Errorf("Server.Audience = %q, want %q", srv.Audience, "https://my.audience.example.com") + } + if !reflect.DeepEqual(srv.Scopes, []string{"read", "write", "admin"}) { + t.Errorf("Server.Scopes = %v, want %v", srv.Scopes, []string{"read", "write", "admin"}) + } + + // Step 2: Construct CommandConfigOauth the same way buildHttpClientV2 does + // (minus the Authenticate() call which requires network). This mirrors lines + // 344-351 of client.go exactly. + baseConfig := auth_providers.CommandAuthConfig{ + CommandHostName: srv.Host, + CommandPort: srv.Port, + CommandAPIPath: srv.APIPath, + CommandCACert: srv.CACertPath, + SkipVerify: srv.SkipTLSVerify, + } + oauthCfg := auth_providers.CommandConfigOauth{ + CommandAuthConfig: baseConfig, + ClientID: srv.ClientID, + ClientSecret: srv.ClientSecret, + TokenURL: srv.OAuthTokenUrl, + AccessToken: srv.AccessToken, + Audience: srv.Audience, + Scopes: srv.Scopes, + } + + // Step 3: Verify the three fields that were missing in the v2.8.0 regression + if oauthCfg.AccessToken != "mytoken-abc123" { + t.Errorf("CommandConfigOauth.AccessToken = %q, want %q", oauthCfg.AccessToken, "mytoken-abc123") + } + if oauthCfg.Audience != "https://my.audience.example.com" { + t.Errorf("CommandConfigOauth.Audience = %q, want %q", oauthCfg.Audience, "https://my.audience.example.com") + } + if !reflect.DeepEqual(oauthCfg.Scopes, []string{"read", "write", "admin"}) { + t.Errorf("CommandConfigOauth.Scopes = %v, want %v", oauthCfg.Scopes, []string{"read", "write", "admin"}) + } + + // Step 4: Verify GetAuthType returns "oauth" for access_token-only config + authType := srv.GetAuthType() + if authType != "oauth" { + t.Errorf("Server.GetAuthType() = %q, want %q (access_token-only should be oauth)", authType, "oauth") + } +} + +// TestCommandConfigOauth_AccessTokenOnlyNoClientCreds verifies that a Server +// configured with only Host + AccessToken (no ClientID/ClientSecret/TokenURL) +// is classified as "oauth" auth type and the token propagates correctly. +func TestCommandConfigOauth_AccessTokenOnlyNoClientCreds(t *testing.T) { + srv := &auth_providers.Server{ + Host: "command.example.com", + AccessToken: "pre-fetched-bearer-token", + // Deliberately omitting ClientID, ClientSecret, OAuthTokenUrl + } + + if got := srv.GetAuthType(); got != "oauth" { + t.Fatalf("GetAuthType() = %q, want %q for access_token-only", got, "oauth") + } + + oauthCfg := auth_providers.CommandConfigOauth{ + AccessToken: srv.AccessToken, + } + + if oauthCfg.AccessToken != "pre-fetched-bearer-token" { + t.Errorf("AccessToken = %q, want %q", oauthCfg.AccessToken, "pre-fetched-bearer-token") + } + if oauthCfg.ClientID != "" { + t.Errorf("ClientID = %q, want empty", oauthCfg.ClientID) + } + if oauthCfg.ClientSecret != "" { + t.Errorf("ClientSecret = %q, want empty", oauthCfg.ClientSecret) + } +} diff --git a/v24/api/keyfactor/v1/model_certificate_authorities_certificate_authority_request.go b/v24/api/keyfactor/v1/model_certificate_authorities_certificate_authority_request.go index 40f00f0..361f84f 100644 --- a/v24/api/keyfactor/v1/model_certificate_authorities_certificate_authority_request.go +++ b/v24/api/keyfactor/v1/model_certificate_authorities_certificate_authority_request.go @@ -70,6 +70,11 @@ type CertificateAuthoritiesCertificateAuthorityRequest struct { ClientSecret *CSSCMSDataModelModelsKeyfactorAPISecret `json:"ClientSecret,omitempty"` Scope NullableString `json:"Scope,omitempty"` Audience NullableString `json:"Audience,omitempty"` + UseForEnrollment *bool `json:"UseForEnrollment,omitempty"` + CertificateCleanupEnabled NullableBool `json:"CertificateCleanupEnabled,omitempty"` + DeleteWithArchivedKey NullableBool `json:"DeleteWithArchivedKey,omitempty"` + TimeAfterExpiration NullableInt32 `json:"TimeAfterExpiration,omitempty"` + TimeAfterExpirationUnits *CSSCMSDataModelEnumsCertificateCleanupTimeUnits `json:"TimeAfterExpirationUnits,omitempty"` } // NewCertificateAuthoritiesCertificateAuthorityRequest instantiates a new CertificateAuthoritiesCertificateAuthorityRequest object @@ -1578,6 +1583,124 @@ func (o *CertificateAuthoritiesCertificateAuthorityRequest) UnsetAudience() { o.Audience.Unset() } +// GetUseForEnrollment returns the UseForEnrollment field value if set, zero value otherwise. +func (o *CertificateAuthoritiesCertificateAuthorityRequest) GetUseForEnrollment() bool { + if o == nil || isNil(o.UseForEnrollment) { + var ret bool + return ret + } + return *o.UseForEnrollment +} + +// GetUseForEnrollmentOk returns a tuple with the UseForEnrollment field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *CertificateAuthoritiesCertificateAuthorityRequest) GetUseForEnrollmentOk() (*bool, bool) { + if o == nil || isNil(o.UseForEnrollment) { + return nil, false + } + return o.UseForEnrollment, true +} + +// HasUseForEnrollment returns a boolean if a field has been set. +func (o *CertificateAuthoritiesCertificateAuthorityRequest) HasUseForEnrollment() bool { + if o != nil && !isNil(o.UseForEnrollment) { + return true + } + + return false +} + +// SetUseForEnrollment gets a reference to the given bool and assigns it to the UseForEnrollment field. +func (o *CertificateAuthoritiesCertificateAuthorityRequest) SetUseForEnrollment(v bool) { + o.UseForEnrollment = &v +} + +// GetCertificateCleanupEnabled returns the CertificateCleanupEnabled field value if set, zero value otherwise. +func (o *CertificateAuthoritiesCertificateAuthorityRequest) GetCertificateCleanupEnabled() bool { + if o == nil || !o.CertificateCleanupEnabled.IsSet() || o.CertificateCleanupEnabled.Get() == nil { + var ret bool + return ret + } + return *o.CertificateCleanupEnabled.Get() +} + +// SetCertificateCleanupEnabled gets a reference to the given NullableBool and assigns it to the CertificateCleanupEnabled field. +func (o *CertificateAuthoritiesCertificateAuthorityRequest) SetCertificateCleanupEnabled(v bool) { + o.CertificateCleanupEnabled.Set(&v) +} + +// SetCertificateCleanupEnabledNil sets the value for CertificateCleanupEnabled to be an explicit nil +func (o *CertificateAuthoritiesCertificateAuthorityRequest) SetCertificateCleanupEnabledNil() { + o.CertificateCleanupEnabled.Set(nil) +} + +// UnsetCertificateCleanupEnabled ensures that no value is present for CertificateCleanupEnabled, not even an explicit nil +func (o *CertificateAuthoritiesCertificateAuthorityRequest) UnsetCertificateCleanupEnabled() { + o.CertificateCleanupEnabled.Unset() +} + +// GetDeleteWithArchivedKey returns the DeleteWithArchivedKey field value if set, zero value otherwise. +func (o *CertificateAuthoritiesCertificateAuthorityRequest) GetDeleteWithArchivedKey() bool { + if o == nil || !o.DeleteWithArchivedKey.IsSet() || o.DeleteWithArchivedKey.Get() == nil { + var ret bool + return ret + } + return *o.DeleteWithArchivedKey.Get() +} + +// SetDeleteWithArchivedKey gets a reference to the given NullableBool and assigns it to the DeleteWithArchivedKey field. +func (o *CertificateAuthoritiesCertificateAuthorityRequest) SetDeleteWithArchivedKey(v bool) { + o.DeleteWithArchivedKey.Set(&v) +} + +// SetDeleteWithArchivedKeyNil sets the value for DeleteWithArchivedKey to be an explicit nil +func (o *CertificateAuthoritiesCertificateAuthorityRequest) SetDeleteWithArchivedKeyNil() { + o.DeleteWithArchivedKey.Set(nil) +} + +// UnsetDeleteWithArchivedKey ensures that no value is present for DeleteWithArchivedKey, not even an explicit nil +func (o *CertificateAuthoritiesCertificateAuthorityRequest) UnsetDeleteWithArchivedKey() { + o.DeleteWithArchivedKey.Unset() +} + +// GetTimeAfterExpiration returns the TimeAfterExpiration field value if set, zero value otherwise (both if not set or set to explicit null). +func (o *CertificateAuthoritiesCertificateAuthorityRequest) GetTimeAfterExpiration() int32 { + if o == nil || isNil(o.TimeAfterExpiration.Get()) { + var ret int32 + return ret + } + return *o.TimeAfterExpiration.Get() +} + +// SetTimeAfterExpiration gets a reference to the given NullableInt32 and assigns it to the TimeAfterExpiration field. +func (o *CertificateAuthoritiesCertificateAuthorityRequest) SetTimeAfterExpiration(v int32) { + o.TimeAfterExpiration.Set(&v) +} + +// SetTimeAfterExpirationNil sets the value for TimeAfterExpiration to be an explicit nil +func (o *CertificateAuthoritiesCertificateAuthorityRequest) SetTimeAfterExpirationNil() { + o.TimeAfterExpiration.Set(nil) +} + +// UnsetTimeAfterExpiration ensures that no value is present for TimeAfterExpiration, not even an explicit nil +func (o *CertificateAuthoritiesCertificateAuthorityRequest) UnsetTimeAfterExpiration() { + o.TimeAfterExpiration.Unset() +} + +// GetTimeAfterExpirationUnits returns the TimeAfterExpirationUnits field value if set, zero value otherwise. +func (o *CertificateAuthoritiesCertificateAuthorityRequest) GetTimeAfterExpirationUnits() CSSCMSDataModelEnumsCertificateCleanupTimeUnits { + if o == nil || isNil(o.TimeAfterExpirationUnits) { + var ret CSSCMSDataModelEnumsCertificateCleanupTimeUnits + return ret + } + return *o.TimeAfterExpirationUnits +} + +// SetTimeAfterExpirationUnits gets a reference to the given CSSCMSDataModelEnumsCertificateCleanupTimeUnits and assigns it to the TimeAfterExpirationUnits field. +func (o *CertificateAuthoritiesCertificateAuthorityRequest) SetTimeAfterExpirationUnits(v CSSCMSDataModelEnumsCertificateCleanupTimeUnits) { + o.TimeAfterExpirationUnits = &v +} + func (o CertificateAuthoritiesCertificateAuthorityRequest) MarshalJSON() ([]byte, error) { toSerialize, err := o.ToMap() if err != nil { @@ -1711,6 +1834,21 @@ func (o CertificateAuthoritiesCertificateAuthorityRequest) ToMap() (map[string]i if o.Audience.IsSet() { toSerialize["Audience"] = o.Audience.Get() } + if !isNil(o.UseForEnrollment) { + toSerialize["UseForEnrollment"] = o.UseForEnrollment + } + if o.CertificateCleanupEnabled.IsSet() { + toSerialize["CertificateCleanupEnabled"] = o.CertificateCleanupEnabled.Get() + } + if o.DeleteWithArchivedKey.IsSet() { + toSerialize["DeleteWithArchivedKey"] = o.DeleteWithArchivedKey.Get() + } + if o.TimeAfterExpiration.IsSet() { + toSerialize["TimeAfterExpiration"] = o.TimeAfterExpiration.Get() + } + if !isNil(o.TimeAfterExpirationUnits) { + toSerialize["TimeAfterExpirationUnits"] = o.TimeAfterExpirationUnits + } return toSerialize, nil } diff --git a/v24/api/keyfactor/v1/model_certificate_authorities_certificate_authority_response.go b/v24/api/keyfactor/v1/model_certificate_authorities_certificate_authority_response.go index e165c86..91c746b 100644 --- a/v24/api/keyfactor/v1/model_certificate_authorities_certificate_authority_response.go +++ b/v24/api/keyfactor/v1/model_certificate_authorities_certificate_authority_response.go @@ -73,6 +73,11 @@ type CertificateAuthoritiesCertificateAuthorityResponse struct { Audience NullableString `json:"Audience,omitempty"` ClientSecret *CSSCMSDataModelModelsKeyfactorAPISecret `json:"ClientSecret,omitempty"` LastScan NullableString `json:"LastScan,omitempty"` + UseForEnrollment *bool `json:"UseForEnrollment,omitempty"` + CertificateCleanupEnabled NullableBool `json:"CertificateCleanupEnabled,omitempty"` + DeleteWithArchivedKey NullableBool `json:"DeleteWithArchivedKey,omitempty"` + TimeAfterExpiration NullableInt32 `json:"TimeAfterExpiration,omitempty"` + TimeAfterExpirationUnits *CSSCMSDataModelEnumsCertificateCleanupTimeUnits `json:"TimeAfterExpirationUnits,omitempty"` } // NewCertificateAuthoritiesCertificateAuthorityResponse instantiates a new CertificateAuthoritiesCertificateAuthorityResponse object @@ -1721,6 +1726,199 @@ func (o *CertificateAuthoritiesCertificateAuthorityResponse) UnsetLastScan() { o.LastScan.Unset() } +// GetUseForEnrollment returns the UseForEnrollment field value if set, zero value otherwise. +func (o *CertificateAuthoritiesCertificateAuthorityResponse) GetUseForEnrollment() bool { + if o == nil || isNil(o.UseForEnrollment) { + var ret bool + return ret + } + return *o.UseForEnrollment +} + +// GetUseForEnrollmentOk returns a tuple with the UseForEnrollment field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *CertificateAuthoritiesCertificateAuthorityResponse) GetUseForEnrollmentOk() (*bool, bool) { + if o == nil || isNil(o.UseForEnrollment) { + return nil, false + } + return o.UseForEnrollment, true +} + +// HasUseForEnrollment returns a boolean if a field has been set. +func (o *CertificateAuthoritiesCertificateAuthorityResponse) HasUseForEnrollment() bool { + if o != nil && !isNil(o.UseForEnrollment) { + return true + } + + return false +} + +// SetUseForEnrollment gets a reference to the given bool and assigns it to the UseForEnrollment field. +func (o *CertificateAuthoritiesCertificateAuthorityResponse) SetUseForEnrollment(v bool) { + o.UseForEnrollment = &v +} + +// GetCertificateCleanupEnabled returns the CertificateCleanupEnabled field value if set, zero value otherwise. +func (o *CertificateAuthoritiesCertificateAuthorityResponse) GetCertificateCleanupEnabled() bool { + if o == nil || !o.CertificateCleanupEnabled.IsSet() || o.CertificateCleanupEnabled.Get() == nil { + var ret bool + return ret + } + return *o.CertificateCleanupEnabled.Get() +} + +// GetCertificateCleanupEnabledOk returns a tuple with the CertificateCleanupEnabled field value if set, nil otherwise +// and a boolean to check if the value has been set. +// NOTE: If the value is an explicit nil, `nil, true` will be returned +func (o *CertificateAuthoritiesCertificateAuthorityResponse) GetCertificateCleanupEnabledOk() (*bool, bool) { + if o == nil { + return nil, false + } + return o.CertificateCleanupEnabled.Get(), o.CertificateCleanupEnabled.IsSet() +} + +// HasCertificateCleanupEnabled returns a boolean if a field has been set. +func (o *CertificateAuthoritiesCertificateAuthorityResponse) HasCertificateCleanupEnabled() bool { + if o != nil && o.CertificateCleanupEnabled.IsSet() { + return true + } + + return false +} + +// SetCertificateCleanupEnabled gets a reference to the given NullableBool and assigns it to the CertificateCleanupEnabled field. +func (o *CertificateAuthoritiesCertificateAuthorityResponse) SetCertificateCleanupEnabled(v bool) { + o.CertificateCleanupEnabled.Set(&v) +} + +// SetCertificateCleanupEnabledNil sets the value for CertificateCleanupEnabled to be an explicit nil +func (o *CertificateAuthoritiesCertificateAuthorityResponse) SetCertificateCleanupEnabledNil() { + o.CertificateCleanupEnabled.Set(nil) +} + +// UnsetCertificateCleanupEnabled ensures that no value is present for CertificateCleanupEnabled, not even an explicit nil +func (o *CertificateAuthoritiesCertificateAuthorityResponse) UnsetCertificateCleanupEnabled() { + o.CertificateCleanupEnabled.Unset() +} + +// GetDeleteWithArchivedKey returns the DeleteWithArchivedKey field value if set, zero value otherwise. +func (o *CertificateAuthoritiesCertificateAuthorityResponse) GetDeleteWithArchivedKey() bool { + if o == nil || !o.DeleteWithArchivedKey.IsSet() || o.DeleteWithArchivedKey.Get() == nil { + var ret bool + return ret + } + return *o.DeleteWithArchivedKey.Get() +} + +// GetDeleteWithArchivedKeyOk returns a tuple with the DeleteWithArchivedKey field value if set, nil otherwise +// and a boolean to check if the value has been set. +// NOTE: If the value is an explicit nil, `nil, true` will be returned +func (o *CertificateAuthoritiesCertificateAuthorityResponse) GetDeleteWithArchivedKeyOk() (*bool, bool) { + if o == nil { + return nil, false + } + return o.DeleteWithArchivedKey.Get(), o.DeleteWithArchivedKey.IsSet() +} + +// HasDeleteWithArchivedKey returns a boolean if a field has been set. +func (o *CertificateAuthoritiesCertificateAuthorityResponse) HasDeleteWithArchivedKey() bool { + if o != nil && o.DeleteWithArchivedKey.IsSet() { + return true + } + + return false +} + +// SetDeleteWithArchivedKey gets a reference to the given NullableBool and assigns it to the DeleteWithArchivedKey field. +func (o *CertificateAuthoritiesCertificateAuthorityResponse) SetDeleteWithArchivedKey(v bool) { + o.DeleteWithArchivedKey.Set(&v) +} + +// SetDeleteWithArchivedKeyNil sets the value for DeleteWithArchivedKey to be an explicit nil +func (o *CertificateAuthoritiesCertificateAuthorityResponse) SetDeleteWithArchivedKeyNil() { + o.DeleteWithArchivedKey.Set(nil) +} + +// UnsetDeleteWithArchivedKey ensures that no value is present for DeleteWithArchivedKey, not even an explicit nil +func (o *CertificateAuthoritiesCertificateAuthorityResponse) UnsetDeleteWithArchivedKey() { + o.DeleteWithArchivedKey.Unset() +} + +// GetTimeAfterExpiration returns the TimeAfterExpiration field value if set, zero value otherwise (both if not set or set to explicit null). +func (o *CertificateAuthoritiesCertificateAuthorityResponse) GetTimeAfterExpiration() int32 { + if o == nil || isNil(o.TimeAfterExpiration.Get()) { + var ret int32 + return ret + } + return *o.TimeAfterExpiration.Get() +} + +// GetTimeAfterExpirationOk returns a tuple with the TimeAfterExpiration field value if set, nil otherwise +// and a boolean to check if the value has been set. +// NOTE: If the value is an explicit nil, `nil, true` will be returned +func (o *CertificateAuthoritiesCertificateAuthorityResponse) GetTimeAfterExpirationOk() (*int32, bool) { + if o == nil { + return nil, false + } + return o.TimeAfterExpiration.Get(), o.TimeAfterExpiration.IsSet() +} + +// HasTimeAfterExpiration returns a boolean if a field has been set. +func (o *CertificateAuthoritiesCertificateAuthorityResponse) HasTimeAfterExpiration() bool { + if o != nil && o.TimeAfterExpiration.IsSet() { + return true + } + + return false +} + +// SetTimeAfterExpiration gets a reference to the given NullableInt32 and assigns it to the TimeAfterExpiration field. +func (o *CertificateAuthoritiesCertificateAuthorityResponse) SetTimeAfterExpiration(v int32) { + o.TimeAfterExpiration.Set(&v) +} + +// SetTimeAfterExpirationNil sets the value for TimeAfterExpiration to be an explicit nil +func (o *CertificateAuthoritiesCertificateAuthorityResponse) SetTimeAfterExpirationNil() { + o.TimeAfterExpiration.Set(nil) +} + +// UnsetTimeAfterExpiration ensures that no value is present for TimeAfterExpiration, not even an explicit nil +func (o *CertificateAuthoritiesCertificateAuthorityResponse) UnsetTimeAfterExpiration() { + o.TimeAfterExpiration.Unset() +} + +// GetTimeAfterExpirationUnits returns the TimeAfterExpirationUnits field value if set, zero value otherwise. +func (o *CertificateAuthoritiesCertificateAuthorityResponse) GetTimeAfterExpirationUnits() CSSCMSDataModelEnumsCertificateCleanupTimeUnits { + if o == nil || isNil(o.TimeAfterExpirationUnits) { + var ret CSSCMSDataModelEnumsCertificateCleanupTimeUnits + return ret + } + return *o.TimeAfterExpirationUnits +} + +// GetTimeAfterExpirationUnitsOk returns a tuple with the TimeAfterExpirationUnits field value if set, nil otherwise +// and a boolean to check if the value has been set. +func (o *CertificateAuthoritiesCertificateAuthorityResponse) GetTimeAfterExpirationUnitsOk() (*CSSCMSDataModelEnumsCertificateCleanupTimeUnits, bool) { + if o == nil || isNil(o.TimeAfterExpirationUnits) { + return nil, false + } + return o.TimeAfterExpirationUnits, true +} + +// HasTimeAfterExpirationUnits returns a boolean if a field has been set. +func (o *CertificateAuthoritiesCertificateAuthorityResponse) HasTimeAfterExpirationUnits() bool { + if o != nil && !isNil(o.TimeAfterExpirationUnits) { + return true + } + + return false +} + +// SetTimeAfterExpirationUnits gets a reference to the given CSSCMSDataModelEnumsCertificateCleanupTimeUnits and assigns it to the TimeAfterExpirationUnits field. +func (o *CertificateAuthoritiesCertificateAuthorityResponse) SetTimeAfterExpirationUnits(v CSSCMSDataModelEnumsCertificateCleanupTimeUnits) { + o.TimeAfterExpirationUnits = &v +} + func (o CertificateAuthoritiesCertificateAuthorityResponse) MarshalJSON() ([]byte, error) { toSerialize, err := o.ToMap() if err != nil { @@ -1863,6 +2061,21 @@ func (o CertificateAuthoritiesCertificateAuthorityResponse) ToMap() (map[string] if o.LastScan.IsSet() { toSerialize["LastScan"] = o.LastScan.Get() } + if !isNil(o.UseForEnrollment) { + toSerialize["UseForEnrollment"] = o.UseForEnrollment + } + if o.CertificateCleanupEnabled.IsSet() { + toSerialize["CertificateCleanupEnabled"] = o.CertificateCleanupEnabled.Get() + } + if o.DeleteWithArchivedKey.IsSet() { + toSerialize["DeleteWithArchivedKey"] = o.DeleteWithArchivedKey.Get() + } + if o.TimeAfterExpiration.IsSet() { + toSerialize["TimeAfterExpiration"] = o.TimeAfterExpiration.Get() + } + if !isNil(o.TimeAfterExpirationUnits) { + toSerialize["TimeAfterExpirationUnits"] = o.TimeAfterExpirationUnits + } return toSerialize, nil } diff --git a/v24/api/keyfactor/v1/model_certificate_authorities_test.go b/v24/api/keyfactor/v1/model_certificate_authorities_test.go new file mode 100644 index 0000000..2c2a010 --- /dev/null +++ b/v24/api/keyfactor/v1/model_certificate_authorities_test.go @@ -0,0 +1,264 @@ +/* +Copyright 2025 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 v1 + +import ( + "testing" +) + +// TestCARequestFields_CleanupAndEnrollment verifies that the 5 new fields +// (UseForEnrollment, CertificateCleanupEnabled, DeleteWithArchivedKey, +// TimeAfterExpiration, TimeAfterExpirationUnits) on the request struct can +// be set via setters and read back via getters. This is a compile-time + +// nil-safety regression test: if any field or method is removed, the test +// fails to compile. +func TestCARequestFields_CleanupAndEnrollment(t *testing.T) { + req := NewCertificateAuthoritiesCertificateAuthorityRequest() + + // UseForEnrollment — *bool (plain pointer) + req.SetUseForEnrollment(true) + if got := req.GetUseForEnrollment(); got != true { + t.Errorf("UseForEnrollment: expected true, got %v", got) + } + if !req.HasUseForEnrollment() { + t.Error("HasUseForEnrollment: expected true after Set") + } + + // CertificateCleanupEnabled — NullableBool + req.SetCertificateCleanupEnabled(true) + if got := req.GetCertificateCleanupEnabled(); got != true { + t.Errorf("CertificateCleanupEnabled: expected true, got %v", got) + } + // Verify IsSet via the underlying NullableBool (no Has* on request) + if !req.CertificateCleanupEnabled.IsSet() { + t.Error("CertificateCleanupEnabled.IsSet: expected true after Set") + } + + // DeleteWithArchivedKey — NullableBool + req.SetDeleteWithArchivedKey(false) + if got := req.GetDeleteWithArchivedKey(); got != false { + t.Errorf("DeleteWithArchivedKey: expected false, got %v", got) + } + if !req.DeleteWithArchivedKey.IsSet() { + t.Error("DeleteWithArchivedKey.IsSet: expected true after Set") + } + + // TimeAfterExpiration — NullableInt32 + req.SetTimeAfterExpiration(90) + if got := req.GetTimeAfterExpiration(); got != 90 { + t.Errorf("TimeAfterExpiration: expected 90, got %v", got) + } + if !req.TimeAfterExpiration.IsSet() { + t.Error("TimeAfterExpiration.IsSet: expected true after Set") + } + + // TimeAfterExpirationUnits — *CSSCMSDataModelEnumsCertificateCleanupTimeUnits + units := CSSCMSDATAMODELENUMSCERTIFICATECLEANUPTIMEUNITS__1 + req.SetTimeAfterExpirationUnits(units) + if got := req.GetTimeAfterExpirationUnits(); got != units { + t.Errorf("TimeAfterExpirationUnits: expected %v, got %v", units, got) + } +} + +// TestCAResponseFields_CleanupAndEnrollment verifies the same 5 fields on +// the response struct, including the Has* methods that exist on response +// but not on request. +func TestCAResponseFields_CleanupAndEnrollment(t *testing.T) { + resp := NewCertificateAuthoritiesCertificateAuthorityResponse() + + // UseForEnrollment — *bool + resp.SetUseForEnrollment(true) + if got := resp.GetUseForEnrollment(); got != true { + t.Errorf("UseForEnrollment: expected true, got %v", got) + } + if !resp.HasUseForEnrollment() { + t.Error("HasUseForEnrollment: expected true after Set") + } + if val, ok := resp.GetUseForEnrollmentOk(); !ok || val == nil || *val != true { + t.Error("GetUseForEnrollmentOk: expected (true, true)") + } + + // CertificateCleanupEnabled — NullableBool + resp.SetCertificateCleanupEnabled(true) + if got := resp.GetCertificateCleanupEnabled(); got != true { + t.Errorf("CertificateCleanupEnabled: expected true, got %v", got) + } + if !resp.HasCertificateCleanupEnabled() { + t.Error("HasCertificateCleanupEnabled: expected true after Set") + } + if val, ok := resp.GetCertificateCleanupEnabledOk(); !ok || val == nil || *val != true { + t.Error("GetCertificateCleanupEnabledOk: expected (true, true)") + } + + // DeleteWithArchivedKey — NullableBool + resp.SetDeleteWithArchivedKey(false) + if got := resp.GetDeleteWithArchivedKey(); got != false { + t.Errorf("DeleteWithArchivedKey: expected false, got %v", got) + } + if !resp.HasDeleteWithArchivedKey() { + t.Error("HasDeleteWithArchivedKey: expected true after Set") + } + if val, ok := resp.GetDeleteWithArchivedKeyOk(); !ok || val == nil || *val != false { + t.Error("GetDeleteWithArchivedKeyOk: expected (false, true)") + } + + // TimeAfterExpiration — NullableInt32 + resp.SetTimeAfterExpiration(30) + if got := resp.GetTimeAfterExpiration(); got != 30 { + t.Errorf("TimeAfterExpiration: expected 30, got %v", got) + } + if !resp.HasTimeAfterExpiration() { + t.Error("HasTimeAfterExpiration: expected true after Set") + } + if val, ok := resp.GetTimeAfterExpirationOk(); !ok || val == nil || *val != 30 { + t.Error("GetTimeAfterExpirationOk: expected (30, true)") + } + + // TimeAfterExpirationUnits — *CSSCMSDataModelEnumsCertificateCleanupTimeUnits + units := CSSCMSDATAMODELENUMSCERTIFICATECLEANUPTIMEUNITS__2 + resp.SetTimeAfterExpirationUnits(units) + if got := resp.GetTimeAfterExpirationUnits(); got != units { + t.Errorf("TimeAfterExpirationUnits: expected %v, got %v", units, got) + } + if !resp.HasTimeAfterExpirationUnits() { + t.Error("HasTimeAfterExpirationUnits: expected true after Set") + } + if val, ok := resp.GetTimeAfterExpirationUnitsOk(); !ok || val == nil || *val != units { + t.Error("GetTimeAfterExpirationUnitsOk: expected (units, true)") + } +} + +// TestCARequestFields_NilSafety verifies that calling getters on a +// zero-value request struct does not panic and returns Go zero values. +func TestCARequestFields_NilSafety(t *testing.T) { + req := CertificateAuthoritiesCertificateAuthorityRequest{} + + // UseForEnrollment — should return false (zero bool) + if got := req.GetUseForEnrollment(); got != false { + t.Errorf("UseForEnrollment zero: expected false, got %v", got) + } + if req.HasUseForEnrollment() { + t.Error("HasUseForEnrollment zero: expected false") + } + + // CertificateCleanupEnabled — NullableBool unset, should return false + if got := req.GetCertificateCleanupEnabled(); got != false { + t.Errorf("CertificateCleanupEnabled zero: expected false, got %v", got) + } + if req.CertificateCleanupEnabled.IsSet() { + t.Error("CertificateCleanupEnabled.IsSet zero: expected false") + } + + // DeleteWithArchivedKey — NullableBool unset, should return false + if got := req.GetDeleteWithArchivedKey(); got != false { + t.Errorf("DeleteWithArchivedKey zero: expected false, got %v", got) + } + if req.DeleteWithArchivedKey.IsSet() { + t.Error("DeleteWithArchivedKey.IsSet zero: expected false") + } + + // TimeAfterExpiration — NullableInt32 unset, should return 0 + if got := req.GetTimeAfterExpiration(); got != 0 { + t.Errorf("TimeAfterExpiration zero: expected 0, got %v", got) + } + if req.TimeAfterExpiration.IsSet() { + t.Error("TimeAfterExpiration.IsSet zero: expected false") + } + + // TimeAfterExpirationUnits — nil pointer, should return zero enum + if got := req.GetTimeAfterExpirationUnits(); got != 0 { + t.Errorf("TimeAfterExpirationUnits zero: expected 0, got %v", got) + } + + // Also test nil receiver safety + var nilReq *CertificateAuthoritiesCertificateAuthorityRequest + if got := nilReq.GetUseForEnrollment(); got != false { + t.Errorf("nil receiver UseForEnrollment: expected false, got %v", got) + } + if got := nilReq.GetCertificateCleanupEnabled(); got != false { + t.Errorf("nil receiver CertificateCleanupEnabled: expected false, got %v", got) + } + if got := nilReq.GetDeleteWithArchivedKey(); got != false { + t.Errorf("nil receiver DeleteWithArchivedKey: expected false, got %v", got) + } + if got := nilReq.GetTimeAfterExpiration(); got != 0 { + t.Errorf("nil receiver TimeAfterExpiration: expected 0, got %v", got) + } + if got := nilReq.GetTimeAfterExpirationUnits(); got != 0 { + t.Errorf("nil receiver TimeAfterExpirationUnits: expected 0, got %v", got) + } +} + +// TestCAResponseFields_NilSafety verifies that calling getters on a +// zero-value response struct does not panic and returns Go zero values. +func TestCAResponseFields_NilSafety(t *testing.T) { + resp := CertificateAuthoritiesCertificateAuthorityResponse{} + + // UseForEnrollment — should return false + if got := resp.GetUseForEnrollment(); got != false { + t.Errorf("UseForEnrollment zero: expected false, got %v", got) + } + if resp.HasUseForEnrollment() { + t.Error("HasUseForEnrollment zero: expected false") + } + + // CertificateCleanupEnabled — NullableBool unset + if got := resp.GetCertificateCleanupEnabled(); got != false { + t.Errorf("CertificateCleanupEnabled zero: expected false, got %v", got) + } + if resp.HasCertificateCleanupEnabled() { + t.Error("HasCertificateCleanupEnabled zero: expected false") + } + + // DeleteWithArchivedKey — NullableBool unset + if got := resp.GetDeleteWithArchivedKey(); got != false { + t.Errorf("DeleteWithArchivedKey zero: expected false, got %v", got) + } + if resp.HasDeleteWithArchivedKey() { + t.Error("HasDeleteWithArchivedKey zero: expected false") + } + + // TimeAfterExpiration — NullableInt32 unset + if got := resp.GetTimeAfterExpiration(); got != 0 { + t.Errorf("TimeAfterExpiration zero: expected 0, got %v", got) + } + if resp.HasTimeAfterExpiration() { + t.Error("HasTimeAfterExpiration zero: expected false") + } + + // TimeAfterExpirationUnits — nil pointer + if got := resp.GetTimeAfterExpirationUnits(); got != 0 { + t.Errorf("TimeAfterExpirationUnits zero: expected 0, got %v", got) + } + if resp.HasTimeAfterExpirationUnits() { + t.Error("HasTimeAfterExpirationUnits zero: expected false") + } + + // Also test nil receiver safety + var nilResp *CertificateAuthoritiesCertificateAuthorityResponse + if got := nilResp.GetUseForEnrollment(); got != false { + t.Errorf("nil receiver UseForEnrollment: expected false, got %v", got) + } + if got := nilResp.GetCertificateCleanupEnabled(); got != false { + t.Errorf("nil receiver CertificateCleanupEnabled: expected false, got %v", got) + } + if got := nilResp.GetDeleteWithArchivedKey(); got != false { + t.Errorf("nil receiver DeleteWithArchivedKey: expected false, got %v", got) + } + if got := nilResp.GetTimeAfterExpiration(); got != 0 { + t.Errorf("nil receiver TimeAfterExpiration: expected 0, got %v", got) + } + if got := nilResp.GetTimeAfterExpirationUnits(); got != 0 { + t.Errorf("nil receiver TimeAfterExpirationUnits: expected 0, got %v", got) + } +} diff --git a/v24/api/keyfactor/v2/client.go b/v24/api/keyfactor/v2/client.go index 473cef9..70f0a56 100644 --- a/v24/api/keyfactor/v2/client.go +++ b/v24/api/keyfactor/v2/client.go @@ -158,6 +158,9 @@ func buildHttpClientV2(cfg *auth_providers.Server) (AuthConfig, error) { ClientID: cfg.ClientID, ClientSecret: cfg.ClientSecret, TokenURL: cfg.OAuthTokenUrl, + AccessToken: cfg.AccessToken, + Audience: cfg.Audience, + Scopes: cfg.Scopes, } aErr := oauthCfg.Authenticate() if aErr != nil { @@ -590,7 +593,7 @@ func (c *APIClient) prepareRequest( // Override request host, if applicable serverConfig := c.GetConfig() if serverConfig.Host != "" { - if serverConfig.Port > 0 && serverConfig.Port <= 65535 { + if serverConfig.Port > 0 && serverConfig.Port <= 65535 && serverConfig.Port != 443 { url.Host = fmt.Sprintf("%s:%d", serverConfig.Host, serverConfig.Port) } else { url.Host = serverConfig.Host diff --git a/v24/api/keyfactor/v2/client_test.go b/v24/api/keyfactor/v2/client_test.go new file mode 100644 index 0000000..6a58a66 --- /dev/null +++ b/v24/api/keyfactor/v2/client_test.go @@ -0,0 +1,99 @@ +package v2 + +import ( + "reflect" + "testing" + + "github.com/Keyfactor/keyfactor-auth-client-go/auth_providers" +) + +// TestCommandConfigOauth_AccessTokenFieldPropagation is a compilation + correctness +// regression test for the v2.8.0 bug where AccessToken, Audience, and Scopes were +// silently dropped when constructing CommandConfigOauth from auth_providers.Server +// in buildHttpClientV2. If any of those three fields are ever removed from either +// struct, this test fails to compile. +func TestCommandConfigOauth_AccessTokenFieldPropagation(t *testing.T) { + srv := &auth_providers.Server{ + Host: "test.example.com", + AccessToken: "mytoken-abc123", + Audience: "https://my.audience.example.com", + Scopes: []string{"read", "write", "admin"}, + } + + // Step 1: Verify Server struct holds the fields correctly + if srv.AccessToken != "mytoken-abc123" { + t.Errorf("Server.AccessToken = %q, want %q", srv.AccessToken, "mytoken-abc123") + } + if srv.Audience != "https://my.audience.example.com" { + t.Errorf("Server.Audience = %q, want %q", srv.Audience, "https://my.audience.example.com") + } + if !reflect.DeepEqual(srv.Scopes, []string{"read", "write", "admin"}) { + t.Errorf("Server.Scopes = %v, want %v", srv.Scopes, []string{"read", "write", "admin"}) + } + + // Step 2: Construct CommandConfigOauth the same way buildHttpClientV2 does + // (minus the Authenticate() call which requires network). This mirrors lines + // 344-351 of client.go exactly. + baseConfig := auth_providers.CommandAuthConfig{ + CommandHostName: srv.Host, + CommandPort: srv.Port, + CommandAPIPath: srv.APIPath, + CommandCACert: srv.CACertPath, + SkipVerify: srv.SkipTLSVerify, + } + oauthCfg := auth_providers.CommandConfigOauth{ + CommandAuthConfig: baseConfig, + ClientID: srv.ClientID, + ClientSecret: srv.ClientSecret, + TokenURL: srv.OAuthTokenUrl, + AccessToken: srv.AccessToken, + Audience: srv.Audience, + Scopes: srv.Scopes, + } + + // Step 3: Verify the three fields that were missing in the v2.8.0 regression + if oauthCfg.AccessToken != "mytoken-abc123" { + t.Errorf("CommandConfigOauth.AccessToken = %q, want %q", oauthCfg.AccessToken, "mytoken-abc123") + } + if oauthCfg.Audience != "https://my.audience.example.com" { + t.Errorf("CommandConfigOauth.Audience = %q, want %q", oauthCfg.Audience, "https://my.audience.example.com") + } + if !reflect.DeepEqual(oauthCfg.Scopes, []string{"read", "write", "admin"}) { + t.Errorf("CommandConfigOauth.Scopes = %v, want %v", oauthCfg.Scopes, []string{"read", "write", "admin"}) + } + + // Step 4: Verify GetAuthType returns "oauth" for access_token-only config + authType := srv.GetAuthType() + if authType != "oauth" { + t.Errorf("Server.GetAuthType() = %q, want %q (access_token-only should be oauth)", authType, "oauth") + } +} + +// TestCommandConfigOauth_AccessTokenOnlyNoClientCreds verifies that a Server +// configured with only Host + AccessToken (no ClientID/ClientSecret/TokenURL) +// is classified as "oauth" auth type and the token propagates correctly. +func TestCommandConfigOauth_AccessTokenOnlyNoClientCreds(t *testing.T) { + srv := &auth_providers.Server{ + Host: "command.example.com", + AccessToken: "pre-fetched-bearer-token", + // Deliberately omitting ClientID, ClientSecret, OAuthTokenUrl + } + + if got := srv.GetAuthType(); got != "oauth" { + t.Fatalf("GetAuthType() = %q, want %q for access_token-only", got, "oauth") + } + + oauthCfg := auth_providers.CommandConfigOauth{ + AccessToken: srv.AccessToken, + } + + if oauthCfg.AccessToken != "pre-fetched-bearer-token" { + t.Errorf("AccessToken = %q, want %q", oauthCfg.AccessToken, "pre-fetched-bearer-token") + } + if oauthCfg.ClientID != "" { + t.Errorf("ClientID = %q, want empty", oauthCfg.ClientID) + } + if oauthCfg.ClientSecret != "" { + t.Errorf("ClientSecret = %q, want empty", oauthCfg.ClientSecret) + } +} diff --git a/v25/api/keyfactor/v1/client_test.go b/v25/api/keyfactor/v1/client_test.go new file mode 100644 index 0000000..fadf205 --- /dev/null +++ b/v25/api/keyfactor/v1/client_test.go @@ -0,0 +1,99 @@ +package v1 + +import ( + "reflect" + "testing" + + "github.com/Keyfactor/keyfactor-auth-client-go/auth_providers" +) + +// TestCommandConfigOauth_AccessTokenFieldPropagation is a compilation + correctness +// regression test for the v2.8.0 bug where AccessToken, Audience, and Scopes were +// silently dropped when constructing CommandConfigOauth from auth_providers.Server +// in buildHttpClientV2. If any of those three fields are ever removed from either +// struct, this test fails to compile. +func TestCommandConfigOauth_AccessTokenFieldPropagation(t *testing.T) { + srv := &auth_providers.Server{ + Host: "test.example.com", + AccessToken: "mytoken-abc123", + Audience: "https://my.audience.example.com", + Scopes: []string{"read", "write", "admin"}, + } + + // Step 1: Verify Server struct holds the fields correctly + if srv.AccessToken != "mytoken-abc123" { + t.Errorf("Server.AccessToken = %q, want %q", srv.AccessToken, "mytoken-abc123") + } + if srv.Audience != "https://my.audience.example.com" { + t.Errorf("Server.Audience = %q, want %q", srv.Audience, "https://my.audience.example.com") + } + if !reflect.DeepEqual(srv.Scopes, []string{"read", "write", "admin"}) { + t.Errorf("Server.Scopes = %v, want %v", srv.Scopes, []string{"read", "write", "admin"}) + } + + // Step 2: Construct CommandConfigOauth the same way buildHttpClientV2 does + // (minus the Authenticate() call which requires network). This mirrors lines + // 344-351 of client.go exactly. + baseConfig := auth_providers.CommandAuthConfig{ + CommandHostName: srv.Host, + CommandPort: srv.Port, + CommandAPIPath: srv.APIPath, + CommandCACert: srv.CACertPath, + SkipVerify: srv.SkipTLSVerify, + } + oauthCfg := auth_providers.CommandConfigOauth{ + CommandAuthConfig: baseConfig, + ClientID: srv.ClientID, + ClientSecret: srv.ClientSecret, + TokenURL: srv.OAuthTokenUrl, + AccessToken: srv.AccessToken, + Audience: srv.Audience, + Scopes: srv.Scopes, + } + + // Step 3: Verify the three fields that were missing in the v2.8.0 regression + if oauthCfg.AccessToken != "mytoken-abc123" { + t.Errorf("CommandConfigOauth.AccessToken = %q, want %q", oauthCfg.AccessToken, "mytoken-abc123") + } + if oauthCfg.Audience != "https://my.audience.example.com" { + t.Errorf("CommandConfigOauth.Audience = %q, want %q", oauthCfg.Audience, "https://my.audience.example.com") + } + if !reflect.DeepEqual(oauthCfg.Scopes, []string{"read", "write", "admin"}) { + t.Errorf("CommandConfigOauth.Scopes = %v, want %v", oauthCfg.Scopes, []string{"read", "write", "admin"}) + } + + // Step 4: Verify GetAuthType returns "oauth" for access_token-only config + authType := srv.GetAuthType() + if authType != "oauth" { + t.Errorf("Server.GetAuthType() = %q, want %q (access_token-only should be oauth)", authType, "oauth") + } +} + +// TestCommandConfigOauth_AccessTokenOnlyNoClientCreds verifies that a Server +// configured with only Host + AccessToken (no ClientID/ClientSecret/TokenURL) +// is classified as "oauth" auth type and the token propagates correctly. +func TestCommandConfigOauth_AccessTokenOnlyNoClientCreds(t *testing.T) { + srv := &auth_providers.Server{ + Host: "command.example.com", + AccessToken: "pre-fetched-bearer-token", + // Deliberately omitting ClientID, ClientSecret, OAuthTokenUrl + } + + if got := srv.GetAuthType(); got != "oauth" { + t.Fatalf("GetAuthType() = %q, want %q for access_token-only", got, "oauth") + } + + oauthCfg := auth_providers.CommandConfigOauth{ + AccessToken: srv.AccessToken, + } + + if oauthCfg.AccessToken != "pre-fetched-bearer-token" { + t.Errorf("AccessToken = %q, want %q", oauthCfg.AccessToken, "pre-fetched-bearer-token") + } + if oauthCfg.ClientID != "" { + t.Errorf("ClientID = %q, want empty", oauthCfg.ClientID) + } + if oauthCfg.ClientSecret != "" { + t.Errorf("ClientSecret = %q, want empty", oauthCfg.ClientSecret) + } +} diff --git a/v25/api/keyfactor/v1/model_certificate_authorities_test.go b/v25/api/keyfactor/v1/model_certificate_authorities_test.go new file mode 100644 index 0000000..2c2a010 --- /dev/null +++ b/v25/api/keyfactor/v1/model_certificate_authorities_test.go @@ -0,0 +1,264 @@ +/* +Copyright 2025 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 v1 + +import ( + "testing" +) + +// TestCARequestFields_CleanupAndEnrollment verifies that the 5 new fields +// (UseForEnrollment, CertificateCleanupEnabled, DeleteWithArchivedKey, +// TimeAfterExpiration, TimeAfterExpirationUnits) on the request struct can +// be set via setters and read back via getters. This is a compile-time + +// nil-safety regression test: if any field or method is removed, the test +// fails to compile. +func TestCARequestFields_CleanupAndEnrollment(t *testing.T) { + req := NewCertificateAuthoritiesCertificateAuthorityRequest() + + // UseForEnrollment — *bool (plain pointer) + req.SetUseForEnrollment(true) + if got := req.GetUseForEnrollment(); got != true { + t.Errorf("UseForEnrollment: expected true, got %v", got) + } + if !req.HasUseForEnrollment() { + t.Error("HasUseForEnrollment: expected true after Set") + } + + // CertificateCleanupEnabled — NullableBool + req.SetCertificateCleanupEnabled(true) + if got := req.GetCertificateCleanupEnabled(); got != true { + t.Errorf("CertificateCleanupEnabled: expected true, got %v", got) + } + // Verify IsSet via the underlying NullableBool (no Has* on request) + if !req.CertificateCleanupEnabled.IsSet() { + t.Error("CertificateCleanupEnabled.IsSet: expected true after Set") + } + + // DeleteWithArchivedKey — NullableBool + req.SetDeleteWithArchivedKey(false) + if got := req.GetDeleteWithArchivedKey(); got != false { + t.Errorf("DeleteWithArchivedKey: expected false, got %v", got) + } + if !req.DeleteWithArchivedKey.IsSet() { + t.Error("DeleteWithArchivedKey.IsSet: expected true after Set") + } + + // TimeAfterExpiration — NullableInt32 + req.SetTimeAfterExpiration(90) + if got := req.GetTimeAfterExpiration(); got != 90 { + t.Errorf("TimeAfterExpiration: expected 90, got %v", got) + } + if !req.TimeAfterExpiration.IsSet() { + t.Error("TimeAfterExpiration.IsSet: expected true after Set") + } + + // TimeAfterExpirationUnits — *CSSCMSDataModelEnumsCertificateCleanupTimeUnits + units := CSSCMSDATAMODELENUMSCERTIFICATECLEANUPTIMEUNITS__1 + req.SetTimeAfterExpirationUnits(units) + if got := req.GetTimeAfterExpirationUnits(); got != units { + t.Errorf("TimeAfterExpirationUnits: expected %v, got %v", units, got) + } +} + +// TestCAResponseFields_CleanupAndEnrollment verifies the same 5 fields on +// the response struct, including the Has* methods that exist on response +// but not on request. +func TestCAResponseFields_CleanupAndEnrollment(t *testing.T) { + resp := NewCertificateAuthoritiesCertificateAuthorityResponse() + + // UseForEnrollment — *bool + resp.SetUseForEnrollment(true) + if got := resp.GetUseForEnrollment(); got != true { + t.Errorf("UseForEnrollment: expected true, got %v", got) + } + if !resp.HasUseForEnrollment() { + t.Error("HasUseForEnrollment: expected true after Set") + } + if val, ok := resp.GetUseForEnrollmentOk(); !ok || val == nil || *val != true { + t.Error("GetUseForEnrollmentOk: expected (true, true)") + } + + // CertificateCleanupEnabled — NullableBool + resp.SetCertificateCleanupEnabled(true) + if got := resp.GetCertificateCleanupEnabled(); got != true { + t.Errorf("CertificateCleanupEnabled: expected true, got %v", got) + } + if !resp.HasCertificateCleanupEnabled() { + t.Error("HasCertificateCleanupEnabled: expected true after Set") + } + if val, ok := resp.GetCertificateCleanupEnabledOk(); !ok || val == nil || *val != true { + t.Error("GetCertificateCleanupEnabledOk: expected (true, true)") + } + + // DeleteWithArchivedKey — NullableBool + resp.SetDeleteWithArchivedKey(false) + if got := resp.GetDeleteWithArchivedKey(); got != false { + t.Errorf("DeleteWithArchivedKey: expected false, got %v", got) + } + if !resp.HasDeleteWithArchivedKey() { + t.Error("HasDeleteWithArchivedKey: expected true after Set") + } + if val, ok := resp.GetDeleteWithArchivedKeyOk(); !ok || val == nil || *val != false { + t.Error("GetDeleteWithArchivedKeyOk: expected (false, true)") + } + + // TimeAfterExpiration — NullableInt32 + resp.SetTimeAfterExpiration(30) + if got := resp.GetTimeAfterExpiration(); got != 30 { + t.Errorf("TimeAfterExpiration: expected 30, got %v", got) + } + if !resp.HasTimeAfterExpiration() { + t.Error("HasTimeAfterExpiration: expected true after Set") + } + if val, ok := resp.GetTimeAfterExpirationOk(); !ok || val == nil || *val != 30 { + t.Error("GetTimeAfterExpirationOk: expected (30, true)") + } + + // TimeAfterExpirationUnits — *CSSCMSDataModelEnumsCertificateCleanupTimeUnits + units := CSSCMSDATAMODELENUMSCERTIFICATECLEANUPTIMEUNITS__2 + resp.SetTimeAfterExpirationUnits(units) + if got := resp.GetTimeAfterExpirationUnits(); got != units { + t.Errorf("TimeAfterExpirationUnits: expected %v, got %v", units, got) + } + if !resp.HasTimeAfterExpirationUnits() { + t.Error("HasTimeAfterExpirationUnits: expected true after Set") + } + if val, ok := resp.GetTimeAfterExpirationUnitsOk(); !ok || val == nil || *val != units { + t.Error("GetTimeAfterExpirationUnitsOk: expected (units, true)") + } +} + +// TestCARequestFields_NilSafety verifies that calling getters on a +// zero-value request struct does not panic and returns Go zero values. +func TestCARequestFields_NilSafety(t *testing.T) { + req := CertificateAuthoritiesCertificateAuthorityRequest{} + + // UseForEnrollment — should return false (zero bool) + if got := req.GetUseForEnrollment(); got != false { + t.Errorf("UseForEnrollment zero: expected false, got %v", got) + } + if req.HasUseForEnrollment() { + t.Error("HasUseForEnrollment zero: expected false") + } + + // CertificateCleanupEnabled — NullableBool unset, should return false + if got := req.GetCertificateCleanupEnabled(); got != false { + t.Errorf("CertificateCleanupEnabled zero: expected false, got %v", got) + } + if req.CertificateCleanupEnabled.IsSet() { + t.Error("CertificateCleanupEnabled.IsSet zero: expected false") + } + + // DeleteWithArchivedKey — NullableBool unset, should return false + if got := req.GetDeleteWithArchivedKey(); got != false { + t.Errorf("DeleteWithArchivedKey zero: expected false, got %v", got) + } + if req.DeleteWithArchivedKey.IsSet() { + t.Error("DeleteWithArchivedKey.IsSet zero: expected false") + } + + // TimeAfterExpiration — NullableInt32 unset, should return 0 + if got := req.GetTimeAfterExpiration(); got != 0 { + t.Errorf("TimeAfterExpiration zero: expected 0, got %v", got) + } + if req.TimeAfterExpiration.IsSet() { + t.Error("TimeAfterExpiration.IsSet zero: expected false") + } + + // TimeAfterExpirationUnits — nil pointer, should return zero enum + if got := req.GetTimeAfterExpirationUnits(); got != 0 { + t.Errorf("TimeAfterExpirationUnits zero: expected 0, got %v", got) + } + + // Also test nil receiver safety + var nilReq *CertificateAuthoritiesCertificateAuthorityRequest + if got := nilReq.GetUseForEnrollment(); got != false { + t.Errorf("nil receiver UseForEnrollment: expected false, got %v", got) + } + if got := nilReq.GetCertificateCleanupEnabled(); got != false { + t.Errorf("nil receiver CertificateCleanupEnabled: expected false, got %v", got) + } + if got := nilReq.GetDeleteWithArchivedKey(); got != false { + t.Errorf("nil receiver DeleteWithArchivedKey: expected false, got %v", got) + } + if got := nilReq.GetTimeAfterExpiration(); got != 0 { + t.Errorf("nil receiver TimeAfterExpiration: expected 0, got %v", got) + } + if got := nilReq.GetTimeAfterExpirationUnits(); got != 0 { + t.Errorf("nil receiver TimeAfterExpirationUnits: expected 0, got %v", got) + } +} + +// TestCAResponseFields_NilSafety verifies that calling getters on a +// zero-value response struct does not panic and returns Go zero values. +func TestCAResponseFields_NilSafety(t *testing.T) { + resp := CertificateAuthoritiesCertificateAuthorityResponse{} + + // UseForEnrollment — should return false + if got := resp.GetUseForEnrollment(); got != false { + t.Errorf("UseForEnrollment zero: expected false, got %v", got) + } + if resp.HasUseForEnrollment() { + t.Error("HasUseForEnrollment zero: expected false") + } + + // CertificateCleanupEnabled — NullableBool unset + if got := resp.GetCertificateCleanupEnabled(); got != false { + t.Errorf("CertificateCleanupEnabled zero: expected false, got %v", got) + } + if resp.HasCertificateCleanupEnabled() { + t.Error("HasCertificateCleanupEnabled zero: expected false") + } + + // DeleteWithArchivedKey — NullableBool unset + if got := resp.GetDeleteWithArchivedKey(); got != false { + t.Errorf("DeleteWithArchivedKey zero: expected false, got %v", got) + } + if resp.HasDeleteWithArchivedKey() { + t.Error("HasDeleteWithArchivedKey zero: expected false") + } + + // TimeAfterExpiration — NullableInt32 unset + if got := resp.GetTimeAfterExpiration(); got != 0 { + t.Errorf("TimeAfterExpiration zero: expected 0, got %v", got) + } + if resp.HasTimeAfterExpiration() { + t.Error("HasTimeAfterExpiration zero: expected false") + } + + // TimeAfterExpirationUnits — nil pointer + if got := resp.GetTimeAfterExpirationUnits(); got != 0 { + t.Errorf("TimeAfterExpirationUnits zero: expected 0, got %v", got) + } + if resp.HasTimeAfterExpirationUnits() { + t.Error("HasTimeAfterExpirationUnits zero: expected false") + } + + // Also test nil receiver safety + var nilResp *CertificateAuthoritiesCertificateAuthorityResponse + if got := nilResp.GetUseForEnrollment(); got != false { + t.Errorf("nil receiver UseForEnrollment: expected false, got %v", got) + } + if got := nilResp.GetCertificateCleanupEnabled(); got != false { + t.Errorf("nil receiver CertificateCleanupEnabled: expected false, got %v", got) + } + if got := nilResp.GetDeleteWithArchivedKey(); got != false { + t.Errorf("nil receiver DeleteWithArchivedKey: expected false, got %v", got) + } + if got := nilResp.GetTimeAfterExpiration(); got != 0 { + t.Errorf("nil receiver TimeAfterExpiration: expected 0, got %v", got) + } + if got := nilResp.GetTimeAfterExpirationUnits(); got != 0 { + t.Errorf("nil receiver TimeAfterExpirationUnits: expected 0, got %v", got) + } +} diff --git a/v25/api/keyfactor/v2/client_test.go b/v25/api/keyfactor/v2/client_test.go new file mode 100644 index 0000000..6a58a66 --- /dev/null +++ b/v25/api/keyfactor/v2/client_test.go @@ -0,0 +1,99 @@ +package v2 + +import ( + "reflect" + "testing" + + "github.com/Keyfactor/keyfactor-auth-client-go/auth_providers" +) + +// TestCommandConfigOauth_AccessTokenFieldPropagation is a compilation + correctness +// regression test for the v2.8.0 bug where AccessToken, Audience, and Scopes were +// silently dropped when constructing CommandConfigOauth from auth_providers.Server +// in buildHttpClientV2. If any of those three fields are ever removed from either +// struct, this test fails to compile. +func TestCommandConfigOauth_AccessTokenFieldPropagation(t *testing.T) { + srv := &auth_providers.Server{ + Host: "test.example.com", + AccessToken: "mytoken-abc123", + Audience: "https://my.audience.example.com", + Scopes: []string{"read", "write", "admin"}, + } + + // Step 1: Verify Server struct holds the fields correctly + if srv.AccessToken != "mytoken-abc123" { + t.Errorf("Server.AccessToken = %q, want %q", srv.AccessToken, "mytoken-abc123") + } + if srv.Audience != "https://my.audience.example.com" { + t.Errorf("Server.Audience = %q, want %q", srv.Audience, "https://my.audience.example.com") + } + if !reflect.DeepEqual(srv.Scopes, []string{"read", "write", "admin"}) { + t.Errorf("Server.Scopes = %v, want %v", srv.Scopes, []string{"read", "write", "admin"}) + } + + // Step 2: Construct CommandConfigOauth the same way buildHttpClientV2 does + // (minus the Authenticate() call which requires network). This mirrors lines + // 344-351 of client.go exactly. + baseConfig := auth_providers.CommandAuthConfig{ + CommandHostName: srv.Host, + CommandPort: srv.Port, + CommandAPIPath: srv.APIPath, + CommandCACert: srv.CACertPath, + SkipVerify: srv.SkipTLSVerify, + } + oauthCfg := auth_providers.CommandConfigOauth{ + CommandAuthConfig: baseConfig, + ClientID: srv.ClientID, + ClientSecret: srv.ClientSecret, + TokenURL: srv.OAuthTokenUrl, + AccessToken: srv.AccessToken, + Audience: srv.Audience, + Scopes: srv.Scopes, + } + + // Step 3: Verify the three fields that were missing in the v2.8.0 regression + if oauthCfg.AccessToken != "mytoken-abc123" { + t.Errorf("CommandConfigOauth.AccessToken = %q, want %q", oauthCfg.AccessToken, "mytoken-abc123") + } + if oauthCfg.Audience != "https://my.audience.example.com" { + t.Errorf("CommandConfigOauth.Audience = %q, want %q", oauthCfg.Audience, "https://my.audience.example.com") + } + if !reflect.DeepEqual(oauthCfg.Scopes, []string{"read", "write", "admin"}) { + t.Errorf("CommandConfigOauth.Scopes = %v, want %v", oauthCfg.Scopes, []string{"read", "write", "admin"}) + } + + // Step 4: Verify GetAuthType returns "oauth" for access_token-only config + authType := srv.GetAuthType() + if authType != "oauth" { + t.Errorf("Server.GetAuthType() = %q, want %q (access_token-only should be oauth)", authType, "oauth") + } +} + +// TestCommandConfigOauth_AccessTokenOnlyNoClientCreds verifies that a Server +// configured with only Host + AccessToken (no ClientID/ClientSecret/TokenURL) +// is classified as "oauth" auth type and the token propagates correctly. +func TestCommandConfigOauth_AccessTokenOnlyNoClientCreds(t *testing.T) { + srv := &auth_providers.Server{ + Host: "command.example.com", + AccessToken: "pre-fetched-bearer-token", + // Deliberately omitting ClientID, ClientSecret, OAuthTokenUrl + } + + if got := srv.GetAuthType(); got != "oauth" { + t.Fatalf("GetAuthType() = %q, want %q for access_token-only", got, "oauth") + } + + oauthCfg := auth_providers.CommandConfigOauth{ + AccessToken: srv.AccessToken, + } + + if oauthCfg.AccessToken != "pre-fetched-bearer-token" { + t.Errorf("AccessToken = %q, want %q", oauthCfg.AccessToken, "pre-fetched-bearer-token") + } + if oauthCfg.ClientID != "" { + t.Errorf("ClientID = %q, want empty", oauthCfg.ClientID) + } + if oauthCfg.ClientSecret != "" { + t.Errorf("ClientSecret = %q, want empty", oauthCfg.ClientSecret) + } +}