f56bb6be29
A terraform-plugin-framework provider for the vault-plugin-secrets-gpg engine, managing engine mounts and OpenPGP keys on Vault/OpenBao. - gpg_secret_backend resource: mount the engine (+ optional plugin catalog registration when a sha256 is given; deregisters on destroy). - gpg_key resource: create/configure a key (algorithm, identity, exportable, deletion_allowed, min_decryption_version); computed public_key/fingerprint/ key_id/latest_version; destroy auto-enables deletion; import <backend>/<name>. - gpg_key data source: read a key's metadata + armored public key. - Talks to Vault/OpenBao via hashicorp/vault/api; address/token fall back to VAULT_ADDR/VAULT_TOKEN. Unit tests plus an e2e running real terraform apply/destroy against a Vault dev server + the gpg plugin. Release publishes a zip to the artifactapi terraform-unkin registry on v* tags.
126 lines
4.6 KiB
Go
126 lines
4.6 KiB
Go
package provider
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/hashicorp/terraform-plugin-framework/datasource"
|
|
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
|
|
"github.com/hashicorp/terraform-plugin-framework/types"
|
|
)
|
|
|
|
var _ datasource.DataSource = &keyDataSource{}
|
|
|
|
type keyDataSource struct {
|
|
client *vaultClient
|
|
}
|
|
|
|
type keyDataSourceModel struct {
|
|
Backend types.String `tfsdk:"backend"`
|
|
Name types.String `tfsdk:"name"`
|
|
Algorithm types.String `tfsdk:"algorithm"`
|
|
Identity types.String `tfsdk:"identity"`
|
|
Exportable types.Bool `tfsdk:"exportable"`
|
|
DeletionAllowed types.Bool `tfsdk:"deletion_allowed"`
|
|
MinDecryptionVersion types.Int64 `tfsdk:"min_decryption_version"`
|
|
LatestVersion types.Int64 `tfsdk:"latest_version"`
|
|
Fingerprint types.String `tfsdk:"fingerprint"`
|
|
KeyID types.String `tfsdk:"key_id"`
|
|
PublicKey types.String `tfsdk:"public_key"`
|
|
}
|
|
|
|
func NewKeyDataSource() datasource.DataSource {
|
|
return &keyDataSource{}
|
|
}
|
|
|
|
func (d *keyDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
|
|
resp.TypeName = req.ProviderTypeName + "_key"
|
|
}
|
|
|
|
func (d *keyDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
|
|
resp.Schema = schema.Schema{
|
|
Description: "Reads an OpenPGP key's metadata and armored public key from a gpg secrets engine mount.",
|
|
Attributes: map[string]schema.Attribute{
|
|
"backend": schema.StringAttribute{
|
|
Description: "Mount path of the gpg secrets engine.",
|
|
Required: true,
|
|
},
|
|
"name": schema.StringAttribute{
|
|
Description: "Name of the key.",
|
|
Required: true,
|
|
},
|
|
"algorithm": schema.StringAttribute{Description: "Key algorithm.", Computed: true},
|
|
"identity": schema.StringAttribute{Description: "OpenPGP User ID.", Computed: true},
|
|
"exportable": schema.BoolAttribute{Description: "Whether the private key is exportable.", Computed: true},
|
|
"deletion_allowed": schema.BoolAttribute{Description: "Whether the key may be deleted.", Computed: true},
|
|
"min_decryption_version": schema.Int64Attribute{Description: "Minimum usable version for decryption/verification.", Computed: true},
|
|
"latest_version": schema.Int64Attribute{Description: "Current (highest) key version.", Computed: true},
|
|
"fingerprint": schema.StringAttribute{Description: "OpenPGP fingerprint of the latest version.", Computed: true},
|
|
"key_id": schema.StringAttribute{Description: "OpenPGP key ID of the latest version.", Computed: true},
|
|
"public_key": schema.StringAttribute{Description: "Armored public key of the latest version.", Computed: true},
|
|
},
|
|
}
|
|
}
|
|
|
|
func (d *keyDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
|
|
if req.ProviderData == nil {
|
|
return
|
|
}
|
|
client, ok := req.ProviderData.(*vaultClient)
|
|
if !ok {
|
|
resp.Diagnostics.AddError("unexpected provider data type", fmt.Sprintf("got %T", req.ProviderData))
|
|
return
|
|
}
|
|
d.client = client
|
|
}
|
|
|
|
func (d *keyDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
|
|
var config keyDataSourceModel
|
|
resp.Diagnostics.Append(req.Config.Get(ctx, &config)...)
|
|
if resp.Diagnostics.HasError() {
|
|
return
|
|
}
|
|
|
|
backend := strings.Trim(config.Backend.ValueString(), "/")
|
|
name := config.Name.ValueString()
|
|
|
|
data, err := d.client.readKey(ctx, backend, name)
|
|
if err != nil {
|
|
resp.Diagnostics.AddError("failed to read gpg key", err.Error())
|
|
return
|
|
}
|
|
if data == nil {
|
|
resp.Diagnostics.AddError("gpg key not found", fmt.Sprintf("no key %q in backend %q", name, backend))
|
|
return
|
|
}
|
|
|
|
if s, ok := toString(data["algorithm"]); ok {
|
|
config.Algorithm = types.StringValue(s)
|
|
}
|
|
if s, ok := toString(data["identity"]); ok {
|
|
config.Identity = types.StringValue(s)
|
|
}
|
|
if b, ok := toBool(data["exportable"]); ok {
|
|
config.Exportable = types.BoolValue(b)
|
|
}
|
|
if b, ok := toBool(data["deletion_allowed"]); ok {
|
|
config.DeletionAllowed = types.BoolValue(b)
|
|
}
|
|
if n, ok := toInt64(data["min_decryption_version"]); ok {
|
|
config.MinDecryptionVersion = types.Int64Value(n)
|
|
}
|
|
if n, ok := toInt64(data["latest_version"]); ok {
|
|
config.LatestVersion = types.Int64Value(n)
|
|
}
|
|
if s, ok := toString(data["fingerprint"]); ok {
|
|
config.Fingerprint = types.StringValue(s)
|
|
}
|
|
if s, ok := toString(data["public_key"]); ok {
|
|
config.PublicKey = types.StringValue(s)
|
|
}
|
|
config.KeyID = types.StringValue(keyIDForLatest(data))
|
|
|
|
resp.Diagnostics.Append(resp.State.Set(ctx, config)...)
|
|
}
|