From ee3d9edeb90362fbb5f8e3a51fa42c8377a0f7c1 Mon Sep 17 00:00:00 2001 From: Daniel Edholm Date: Mon, 16 Jun 2025 23:13:13 +0200 Subject: [PATCH] Adds SSH resource support --- docs/resources/item.md | 9 +++- .../provider/onepassword_item_resource.go | 45 +++++++++++++++++++ .../onepassword_item_resource_test.go | 30 +++++++++++++ 3 files changed, 82 insertions(+), 2 deletions(-) diff --git a/docs/resources/item.md b/docs/resources/item.md index 5f812934..cbd5c012 100644 --- a/docs/resources/item.md +++ b/docs/resources/item.md @@ -45,13 +45,15 @@ resource "onepassword_item" "example" { ### Optional -- `category` (String) The category of the item. One of ["login" "password" "database" "secure_note"] +- `category` (String) The category of the item. One of ["login" "password" "database" "secure_note" "ssh_key"] - `database` (String) (Only applies to the database category) The name of the database. - `hostname` (String) (Only applies to the database category) The address where the database can be found - `note_value` (String, Sensitive) Secure Note value. - `password` (String, Sensitive) Password for this item. - `password_recipe` (Block List) The recipe used to generate a new value for a password. (see [below for nested schema](#nestedblock--password_recipe)) - `port` (String) (Only applies to the database category) The port the database is listening on. +- `private_key` (String, Sensitive) SSH Private Key for this item. +- `public_key` (String) SSH Public Key for this item. - `section` (Block List) A list of custom sections in an item (see [below for nested schema](#nestedblock--section)) - `tags` (List of String) An array of strings of the tags assigned to the item. - `title` (String) The title of the item. @@ -65,6 +67,7 @@ resource "onepassword_item" "example" { - `uuid` (String) The UUID of the item. Item identifiers are unique within a specific vault. + ### Nested Schema for `password_recipe` Optional: @@ -74,8 +77,8 @@ Optional: - `letters` (Boolean) Use letters [a-zA-Z] when generating the password. - `symbols` (Boolean) Use symbols [!@.-_*] when generating the password. - + ### Nested Schema for `section` Required: @@ -91,6 +94,7 @@ Read-Only: - `id` (String) A unique identifier for the section. + ### Nested Schema for `section.field` Required: @@ -106,6 +110,7 @@ Optional: - `value` (String, Sensitive) The value of the field. + ### Nested Schema for `section.field.password_recipe` Optional: diff --git a/internal/provider/onepassword_item_resource.go b/internal/provider/onepassword_item_resource.go index 3917894f..b4c43f60 100644 --- a/internal/provider/onepassword_item_resource.go +++ b/internal/provider/onepassword_item_resource.go @@ -63,6 +63,8 @@ type OnePasswordItemResourceModel struct { NoteValue types.String `tfsdk:"note_value"` Section []OnePasswordItemResourceSectionModel `tfsdk:"section"` Recipe []PasswordRecipeModel `tfsdk:"password_recipe"` + PrivateKey types.String `tfsdk:"private_key"` + PublicKey types.String `tfsdk:"public_key"` } type PasswordRecipeModel struct { @@ -223,6 +225,23 @@ func (r *OnePasswordItemResource) Schema(ctx context.Context, req resource.Schem Optional: true, Sensitive: true, }, + "private_key": schema.StringAttribute{ + MarkdownDescription: publicKeyDescription, + Computed: true, + Optional: true, + Sensitive: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, + "public_key": schema.StringAttribute{ + MarkdownDescription: privateKeyDescription, + Computed: true, + Optional: true, + PlanModifiers: []planmodifier.String{ + stringplanmodifier.UseStateForUnknown(), + }, + }, }, Blocks: map[string]schema.Block{ "section": schema.ListNestedBlock{ @@ -609,6 +628,13 @@ func itemToData(ctx context.Context, item *op.Item, data *OnePasswordItemResourc data.Password = types.StringNull() } + if data.PrivateKey.IsUnknown() { + data.PrivateKey = types.StringNull() + } + if data.PublicKey.IsUnknown() { + data.PublicKey = types.StringNull() + } + return nil } @@ -740,6 +766,25 @@ func dataToItem(ctx context.Context, data OnePasswordItemResourceModel) (*op.Ite Value: data.NoteValue.ValueString(), }, } + case "ssh_key": + item.Category = op.SSHKey + item.Fields = []*op.ItemField{} + if !data.PrivateKey.IsNull() && !data.PrivateKey.IsUnknown() { + item.Fields = append(item.Fields, &op.ItemField{ + ID: "private_key", + Label: "private key", + Type: op.FieldTypeConcealed, + Value: data.PrivateKey.ValueString(), + }) + } + if !data.PublicKey.IsNull() && !data.PublicKey.IsUnknown() { + item.Fields = append(item.Fields, &op.ItemField{ + ID: "public_key", + Label: "public key", + Type: op.FieldTypeString, + Value: data.PublicKey.ValueString(), + }) + } case "secure_note": item.Category = op.SecureNote item.Fields = []*op.ItemField{ diff --git a/internal/provider/onepassword_item_resource_test.go b/internal/provider/onepassword_item_resource_test.go index 896b77e3..bb1d1494 100644 --- a/internal/provider/onepassword_item_resource_test.go +++ b/internal/provider/onepassword_item_resource_test.go @@ -179,6 +179,36 @@ func TestAccItemResourceDocument(t *testing.T) { }) } +func TestAccItemResourceSSHKey(t *testing.T) { + expectedItem := generateSSHKeyItem() + expectedVault := op.Vault{ + ID: expectedItem.Vault.ID, + Name: "Name of the vault", + Description: "This vault will be retrieved", + } + + testServer := setupTestServer(expectedItem, expectedVault, t) + defer testServer.Close() + + resource.Test(t, resource.TestCase{ + ProtoV6ProviderFactories: testAccProtoV6ProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccProviderConfig(testServer.URL) + testAccItemDataSourceConfig(expectedItem.Vault.ID, expectedItem.ID), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("data.onepassword_item.test", "id", fmt.Sprintf("vaults/%s/items/%s", expectedVault.ID, expectedItem.ID)), + resource.TestCheckResourceAttr("data.onepassword_item.test", "vault", expectedVault.ID), + resource.TestCheckResourceAttr("data.onepassword_item.test", "title", expectedItem.Title), + resource.TestCheckResourceAttr("data.onepassword_item.test", "uuid", expectedItem.ID), + resource.TestCheckResourceAttr("data.onepassword_item.test", "category", strings.ToLower(string(expectedItem.Category))), + resource.TestCheckResourceAttr("data.onepassword_item.test", "private_key", expectedItem.Fields[0].Value), + resource.TestCheckResourceAttr("data.onepassword_item.test", "public_key", expectedItem.Fields[1].Value), + ), + }, + }, + }) +} + func testAccDataBaseResourceConfig(expectedItem *op.Item) string { return fmt.Sprintf(`