Skip to content

Commit

Permalink
Pull request #10: Feature that allows loading cert/key/ca as strings,…
Browse files Browse the repository at this point in the history
… not only paths

Merge in ~HACHANDR/terraform-provider-kmi from feature/configure-provider-with-cert-content to main

* commit '8db5134e141ea2a1e84fea0f0694125b8bde3f02':
  Feature that allows loading cert/key/ca as strings, not only paths
  • Loading branch information
Patryk Kawa authored and 1hachandr committed Feb 5, 2024
2 parents 106ceae + 8db5134 commit 23ec28a
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 27 deletions.
3 changes: 3 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ provider "kmi" {
### Optional

- `akamai_ca` (String)
- `akamai_ca_path` (String)
- `api_crt` (String)
- `api_crt_path` (String)
- `api_key` (String, Sensitive)
- `api_key_path` (String, Sensitive)
- `host` (String)
23 changes: 22 additions & 1 deletion internal/kmi/accounts.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ type KMIRestClient struct {
httpclient *http.Client
}

func NewKMIRestClient(host string, apiKey string, apiCrt string, akamaiCA string) (*KMIRestClient, error) {
func NewKMIRestClientPath(host string, apiKey string, apiCrt string, akamaiCA string) (*KMIRestClient, error) {

cert, err := tls.LoadX509KeyPair(apiCrt, apiKey)
if err != nil {
Expand All @@ -45,6 +45,27 @@ func NewKMIRestClient(host string, apiKey string, apiCrt string, akamaiCA string
return &KMIRestClient{Host: host, ApiKey: apiKey, ApiCrt: apiCrt, AkamaiCA: akamaiCA, httpclient: client}, nil
}

func NewKMIRestClient(host string, apiKey string, apiCrt string, akamaiCA string) (*KMIRestClient, error) {

cert, err := tls.X509KeyPair([]byte(apiCrt), []byte(apiKey))
if err != nil {
return nil, err
}

caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM([]byte(akamaiCA))

// Setup HTTPS client
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
RootCAs: caCertPool,
}
transport := &http.Transport{TLSClientConfig: tlsConfig}
client := &http.Client{Transport: transport}

return &KMIRestClient{Host: host, ApiKey: apiKey, ApiCrt: apiCrt, AkamaiCA: akamaiCA, httpclient: client}, nil
}

// GetAccountDetails returns the account details for the given account.
func (client *KMIRestClient) GetAccountDetails(account string) (*Account, error) {

Expand Down
126 changes: 100 additions & 26 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,29 @@ func (p *kmiProvider) Schema(_ context.Context, _ provider.SchemaRequest, resp *
"akamai_ca": schema.StringAttribute{
Optional: true,
},
"api_key_path": schema.StringAttribute{
Optional: true,
Sensitive: true,
},
"api_crt_path": schema.StringAttribute{
Optional: true,
},
"akamai_ca_path": schema.StringAttribute{
Optional: true,
},
},
}
}

// kmiProviderModel maps provider schema data to a Go type.
type kmiProviderModel struct {
Host types.String `tfsdk:"host"`
ApiKey types.String `tfsdk:"api_key"`
ApiCrt types.String `tfsdk:"api_crt"`
AkamaiCA types.String `tfsdk:"akamai_ca"`
Host types.String `tfsdk:"host"`
ApiKey types.String `tfsdk:"api_key"`
ApiCrt types.String `tfsdk:"api_crt"`
AkamaiCA types.String `tfsdk:"akamai_ca"`
ApiKeyPath types.String `tfsdk:"api_key_path"`
ApiCrtPath types.String `tfsdk:"api_crt_path"`
AkamaiCAPath types.String `tfsdk:"akamai_ca_path"`
}

// Configure prepares a kmi API client for data sources and resources.
Expand Down Expand Up @@ -116,6 +129,33 @@ func (p *kmiProvider) Configure(ctx context.Context, req provider.ConfigureReque
)
}

if config.AkamaiCAPath.IsUnknown() {
resp.Diagnostics.AddAttributeError(
path.Root("akamai_ca_path"),
"Unknown KMI akamai_ca_path",
"The provider cannot create the KMI API client as there is an unknown configuration value for the KMI_AKAMAI_CA_PATH. "+
"Either target apply the source of the value first, set the value statically in the configuration, or use the KMI_AKAMAI_CA_PATH environment variable.",
)
}

if config.AkamaiCAPath.IsUnknown() {
resp.Diagnostics.AddAttributeError(
path.Root("api_crt_path"),
"Unknown KMI akamai_ca_path",
"The provider cannot create the KMI API client as there is an unknown configuration value for the KMI_API_CRT_PATH. "+
"Either target apply the source of the value first, set the value statically in the configuration, or use the KMI_API_CRT_PATH environment variable.",
)
}

if config.AkamaiCAPath.IsUnknown() {
resp.Diagnostics.AddAttributeError(
path.Root("api_key_path"),
"Unknown KMI akamai_ca_path",
"The provider cannot create the KMI API client as there is an unknown configuration value for the KMI_API_KEY_PATH. "+
"Either target apply the source of the value first, set the value statically in the configuration, or use the KMI_API_KEY_PATH environment variable.",
)
}

if resp.Diagnostics.HasError() {
return
}
Expand All @@ -124,6 +164,9 @@ func (p *kmiProvider) Configure(ctx context.Context, req provider.ConfigureReque
apikey := os.Getenv("KMI_API_KEY")
apicrt := os.Getenv("KMI_API_CRT")
akamaica := os.Getenv("KMI_AKAMAI_CA")
apikeyPath := os.Getenv("KMI_API_KEY_PATH")
apicrtPath := os.Getenv("KMI_API_CRT_PATH")
akamaicaPath := os.Getenv("KMI_AKAMAI_CA_PATH")

if !config.Host.IsNull() {
host = config.Host.ValueString()
Expand All @@ -141,6 +184,18 @@ func (p *kmiProvider) Configure(ctx context.Context, req provider.ConfigureReque
akamaica = config.AkamaiCA.ValueString()
}

if !config.AkamaiCAPath.IsNull() {
akamaicaPath = config.AkamaiCAPath.ValueString()
}

if !config.ApiKeyPath.IsNull() {
apikeyPath = config.ApiKeyPath.ValueString()
}

if !config.ApiCrtPath.IsNull() {
apicrtPath = config.ApiCrtPath.ValueString()
}

if host == "" {
resp.Diagnostics.AddAttributeError(
path.Root("host"),
Expand All @@ -151,32 +206,32 @@ func (p *kmiProvider) Configure(ctx context.Context, req provider.ConfigureReque
)
}

if apikey == "" {
if apikey == "" && apikeyPath == "" {
resp.Diagnostics.AddAttributeError(
path.Root("api_key"),
"Missing KMI API Key",
"The provider cannot create the KMI API client as there is a missing or empty value for the kmi API host. "+
"Set the host value in the configuration or use the KMI_API_KEY environment variable. "+
"Set the host value in the configuration or use the KMI_API_KEY/KMI_API_KEY_PATH environment variable. "+
"If either is already set, ensure the value is not empty.",
)
}

if apicrt == "" {
if apicrt == "" && apicrtPath == "" {
resp.Diagnostics.AddAttributeError(
path.Root("api_crt"),
"Missing KMI API Certificate",
"The provider cannot create the KMI API client as there is a missing or empty value for the kmi API host. "+
"Set the host value in the configuration or use the KMI_API_CRT environment variable. "+
"Set the host value in the configuration or use the KMI_API_CRT/KMI_API_CRT_PATH environment variable. "+
"If either is already set, ensure the value is not empty.",
)
}

if akamaica == "" {
if akamaica == "" && akamaicaPath == "" {
resp.Diagnostics.AddAttributeError(
path.Root("akamai_ca"),
"Missing KMI Certificate Authority",
"The provider cannot create the KMI API client as there is a missing or empty value for the kmi API host. "+
"Set the host value in the configuration or use the KMI_AKAMAI_CA environment variable. "+
"Set the host value in the configuration or use the KMI_AKAMAI_CA/KMI_AKAMAI_CA_PATH environment variable. "+
"If either is already set, ensure the value is not empty.",
)
}
Expand All @@ -189,27 +244,46 @@ func (p *kmiProvider) Configure(ctx context.Context, req provider.ConfigureReque
ctx = tflog.SetField(ctx, "kmi_key", apikey)
ctx = tflog.SetField(ctx, "kmi_crt", apicrt)
ctx = tflog.SetField(ctx, "kmi_ca", akamaica)
ctx = tflog.SetField(ctx, "kmi_key_path", apikeyPath)
ctx = tflog.SetField(ctx, "kmi_crt_path", apicrtPath)
ctx = tflog.SetField(ctx, "kmi_ca_path", akamaicaPath)

tflog.Debug(ctx, "Creating KMI client")

client, err := kmi.NewKMIRestClient(host, apikey, apicrt, akamaica)
tflog.Info(ctx, "Configured KMI client", map[string]any{"success": true})

if err != nil {
resp.Diagnostics.AddError(
"Unable to Create kmi API Client",
"An unexpected error occurred when creating the kmi API client. "+
"If the error is not clear, please contact the provider developers.\n\n"+
"kmi Client Error: "+err.Error(),
)
return
}
// setting a path variable takes a precedence over having it configured with the string
if apikeyPath != "" {
client, err := kmi.NewKMIRestClientPath(host, apikeyPath, apicrtPath, akamaicaPath)
tflog.Info(ctx, "Configured KMI client", map[string]any{"success": true})

if err != nil {
resp.Diagnostics.AddError(
"Unable to Create kmi API Client",
"An unexpected error occurred when creating the kmi API client. "+
"If the error is not clear, please contact the provider developers.\n\n"+
"kmi Client Error: "+err.Error(),
)
return
}

// Make the kmi client available during DataSource and Resource
// type Configure methods.
resp.DataSourceData = client
resp.ResourceData = client
resp.DataSourceData = client
resp.ResourceData = client
} else {
client, err := kmi.NewKMIRestClient(host, apikey, apicrt, akamaica)
tflog.Info(ctx, "Configured KMI client", map[string]any{"success": true})

if err != nil {
resp.Diagnostics.AddError(
"Unable to Create kmi API Client",
"An unexpected error occurred when creating the kmi API client. "+
"If the error is not clear, please contact the provider developers.\n\n"+
"kmi Client Error: "+err.Error(),
)
return
}

resp.DataSourceData = client
resp.ResourceData = client
}
}

// DataSources defines the data sources implemented in the provider.
Expand Down

0 comments on commit 23ec28a

Please sign in to comment.