diff --git a/docs/resources/consumer_key_auth.md b/docs/resources/consumer_key_auth.md new file mode 100644 index 0000000..ff815d0 --- /dev/null +++ b/docs/resources/consumer_key_auth.md @@ -0,0 +1,28 @@ +# kong_consumer_key_auth + +Resource that allows you to configure the [Key Authentication](https://docs.konghq.com/hub/kong-inc/key-auth/) plugin for a consumer. + +## Example Usage + +```hcl +resource "kong_consumer" "my_consumer" { + username = "User1" + custom_id = "123" +} + +resource "kong_plugin" "key_auth_plugin" { + name = "key-auth" +} + +resource "kong_consumer_key_auth" "consumer_key_auth" { + consumer_id = kong_consumer.my_consumer.id + key = "secret" + tags = ["myTag", "anotherTag"] +} +``` + +## Argument Reference + +* `consumer_id` - (Required) the id of the consumer to associate the credentials to +* `key` - (Optional) Unique key to authenticate the client; if omitted the plugin will generate one +* `tags` - (Optional) A list of strings associated with the consumer key auth for grouping and filtering diff --git a/kong/provider.go b/kong/provider.go index 6b06d9c..d3dde40 100644 --- a/kong/provider.go +++ b/kong/provider.go @@ -71,6 +71,7 @@ func Provider() *schema.Provider { "kong_consumer": resourceKongConsumer(), "kong_consumer_acl": resourceKongConsumerACL(), "kong_consumer_basic_auth": resourceKongConsumerBasicAuth(), + "kong_consumer_key_auth": resourceKongConsumerKeyAuth(), "kong_plugin": resourceKongPlugin(), "kong_upstream": resourceKongUpstream(), "kong_target": resourceKongTarget(), diff --git a/kong/resource_kong_consumer.go b/kong/resource_kong_consumer.go index 2193a63..9e4515a 100644 --- a/kong/resource_kong_consumer.go +++ b/kong/resource_kong_consumer.go @@ -43,8 +43,8 @@ func resourceKongConsumer() *schema.Resource { func resourceKongConsumerCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { consumerRequest := &kong.Consumer{ - Username: NilString(d.Get("username").(string)), - CustomID: NilString(d.Get("custom_id").(string)), + Username: readStringPtrFromResource(d, "username"), + CustomID: readStringPtrFromResource(d, "custom_id"), Tags: readStringArrayPtrFromResource(d, "tags"), } diff --git a/kong/resource_kong_consumer_basic_auth.go b/kong/resource_kong_consumer_basic_auth.go index 78b08cb..d09962a 100644 --- a/kong/resource_kong_consumer_basic_auth.go +++ b/kong/resource_kong_consumer_basic_auth.go @@ -96,7 +96,7 @@ func resourceKongConsumerBasicAuthRead(ctx context.Context, d *schema.ResourceDa if kong.IsNotFoundErr(err) { d.SetId("") } else if err != nil { - return diag.FromErr(fmt.Errorf("could not find kong ACLGroup with id: %s error: %v", id, err)) + return diag.FromErr(fmt.Errorf("could not find kong basic auth with id: %s error: %v", id, err)) } if basicAuth == nil { diff --git a/kong/resource_kong_consumer_basic_auth_test.go b/kong/resource_kong_consumer_basic_auth_test.go index b0ae5b8..ce3ab59 100644 --- a/kong/resource_kong_consumer_basic_auth_test.go +++ b/kong/resource_kong_consumer_basic_auth_test.go @@ -57,7 +57,7 @@ func testAccCheckConsumerBasicAuthDestroy(state *terraform.State) error { } if ConsumerBasicAuth != nil { - return fmt.Errorf("jwt auth %s still exists, %+v", id.ID, ConsumerBasicAuth) + return fmt.Errorf("basic auth %s still exists, %+v", id.ID, ConsumerBasicAuth) } return nil diff --git a/kong/resource_kong_consumer_key_auth.go b/kong/resource_kong_consumer_key_auth.go new file mode 100644 index 0000000..febbd5d --- /dev/null +++ b/kong/resource_kong_consumer_key_auth.go @@ -0,0 +1,132 @@ +package kong + +import ( + "context" + "fmt" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/kong/go-kong/kong" +) + +func resourceKongConsumerKeyAuth() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceKongConsumerKeyAuthCreate, + ReadContext: resourceKongConsumerKeyAuthRead, + DeleteContext: resourceKongConsumerKeyAuthDelete, + UpdateContext: resourceKongConsumerKeyAuthUpdate, + Schema: map[string]*schema.Schema{ + "consumer_id": { + Type: schema.TypeString, + Required: true, + ForceNew: false, + }, + "key": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: false, + Sensitive: true, + }, + "tags": { + Type: schema.TypeList, + Optional: true, + ForceNew: false, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + }, + } +} + +func resourceKongConsumerKeyAuthCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + KeyAuthRequest := &kong.KeyAuth{ + Key: readStringPtrFromResource(d, "key"), + Tags: readStringArrayPtrFromResource(d, "tags"), + } + + consumerId := kong.String(d.Get("consumer_id").(string)) + + client := meta.(*config).adminClient.KeyAuths + keyAuth, err := client.Create(ctx, consumerId, KeyAuthRequest) + + if err != nil { + return diag.FromErr(fmt.Errorf("failed to create kong key auth: %v error: %v", KeyAuthRequest, err)) + } + + d.SetId(buildConsumerPairID(*keyAuth.ID, *consumerId)) + + return resourceKongConsumerKeyAuthRead(ctx, d, meta) +} + +func resourceKongConsumerKeyAuthUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + id, err := splitConsumerID(d.Id()) + + KeyAuthRequest := &kong.KeyAuth{ + ID: kong.String(id.ID), + Key: readStringPtrFromResource(d, "key"), + Tags: readStringArrayPtrFromResource(d, "tags"), + } + + consumerId := kong.String(d.Get("consumer_id").(string)) + + client := meta.(*config).adminClient.KeyAuths + _, err = client.Update(ctx, consumerId, KeyAuthRequest) + + if err != nil { + return diag.FromErr(fmt.Errorf("error updating kong key auth: %s", err)) + } + + return resourceKongConsumerKeyAuthRead(ctx, d, meta) +} + +func resourceKongConsumerKeyAuthRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + id, err := splitConsumerID(d.Id()) + if err != nil { + return diag.FromErr(err) + } + + client := meta.(*config).adminClient.KeyAuths + keyAuth, err := client.Get(ctx, kong.String(id.ConsumerID), kong.String(id.ID)) + + if kong.IsNotFoundErr(err) { + d.SetId("") + } else if err != nil { + return diag.FromErr(fmt.Errorf("could not find kong key auth with id: %s error: %v", id, err)) + } + + if keyAuth == nil { + d.SetId("") + } else { + err = d.Set("consumer_id", keyAuth.Consumer.ID) + if err != nil { + return diag.FromErr(err) + } + err = d.Set("key", keyAuth.Key) + if err != nil { + return diag.FromErr(err) + } + err = d.Set("tags", keyAuth.Tags) + if err != nil { + return diag.FromErr(err) + } + } + + return diags +} + +func resourceKongConsumerKeyAuthDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + id, err := splitConsumerID(d.Id()) + if err != nil { + return diag.FromErr(err) + } + client := meta.(*config).adminClient.KeyAuths + err = client.Delete(ctx, kong.String(id.ConsumerID), kong.String(id.ID)) + + if err != nil { + return diag.FromErr(fmt.Errorf("could not delete kong key auth: %v", err)) + } + + return diags +} diff --git a/kong/resource_kong_consumer_key_auth_test.go b/kong/resource_kong_consumer_key_auth_test.go new file mode 100644 index 0000000..09c71e0 --- /dev/null +++ b/kong/resource_kong_consumer_key_auth_test.go @@ -0,0 +1,171 @@ +package kong + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/kong/go-kong/kong" +) + +func TestAccConsumerKeyAuth(t *testing.T) { + + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + CheckDestroy: testAccCheckConsumerKeyAuthDestroy, + Steps: []resource.TestStep{ + { + Config: testCreateConsumerKeyAuthConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckConsumerKeyAuthExists("kong_consumer_key_auth.consumer_key_auth"), + resource.TestCheckResourceAttr("kong_consumer_key_auth.consumer_key_auth", "key", "foo"), + resource.TestCheckResourceAttr("kong_consumer_key_auth.consumer_key_auth", "tags.#", "1"), + resource.TestCheckResourceAttr("kong_consumer_key_auth.consumer_key_auth", "tags.0", "myTag"), + ), + }, + { + Config: testUpdateConsumerKeyAuthConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckConsumerKeyAuthExists("kong_consumer_key_auth.consumer_key_auth"), + resource.TestCheckResourceAttr("kong_consumer_key_auth.consumer_key_auth", "key", "foo_updated"), + resource.TestCheckResourceAttr("kong_consumer_key_auth.consumer_key_auth", "tags.#", "2"), + resource.TestCheckResourceAttr("kong_consumer_key_auth.consumer_key_auth", "tags.0", "myTag"), + resource.TestCheckResourceAttr("kong_consumer_key_auth.consumer_key_auth", "tags.1", "anotherTag"), + ), + }, + }, + }) +} + +func TestAccConsumerKeyAuthComputed(t *testing.T) { + + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + CheckDestroy: testAccCheckConsumerKeyAuthDestroy, + Steps: []resource.TestStep{ + { + Config: testCreateConsumerKeyAuthConfigKeyComputed, + Check: resource.ComposeTestCheckFunc( + testAccCheckConsumerKeyAuthExists("kong_consumer_key_auth.consumer_key_auth"), + resource.TestCheckResourceAttrSet("kong_consumer_key_auth.consumer_key_auth", "key"), + resource.TestCheckResourceAttr("kong_consumer_key_auth.consumer_key_auth", "tags.#", "1"), + resource.TestCheckResourceAttr("kong_consumer_key_auth.consumer_key_auth", "tags.0", "myTag"), + ), + }, + { + Config: testUpdateConsumerKeyAuthConfig, + Check: resource.ComposeTestCheckFunc( + testAccCheckConsumerKeyAuthExists("kong_consumer_key_auth.consumer_key_auth"), + resource.TestCheckResourceAttr("kong_consumer_key_auth.consumer_key_auth", "key", "foo_updated"), + resource.TestCheckResourceAttr("kong_consumer_key_auth.consumer_key_auth", "tags.#", "2"), + resource.TestCheckResourceAttr("kong_consumer_key_auth.consumer_key_auth", "tags.0", "myTag"), + resource.TestCheckResourceAttr("kong_consumer_key_auth.consumer_key_auth", "tags.1", "anotherTag"), + ), + }, + }, + }) +} + +func testAccCheckConsumerKeyAuthDestroy(state *terraform.State) error { + + client := testAccProvider.Meta().(*config).adminClient.KeyAuths + + resources := getResourcesByType("kong_consumer_key_auth", state) + + if len(resources) != 1 { + return fmt.Errorf("expecting only 1 consumer key auth resource found %v", len(resources)) + } + + id, err := splitConsumerID(resources[0].Primary.ID) + ConsumerKeyAuth, err := client.Get(context.Background(), kong.String(id.ConsumerID), kong.String(id.ID)) + + if !kong.IsNotFoundErr(err) && err != nil { + return fmt.Errorf("error calling get consumer auth by id: %v", err) + } + + if ConsumerKeyAuth != nil { + return fmt.Errorf("key auth %s still exists, %+v", id.ID, ConsumerKeyAuth) + } + + return nil +} + +func testAccCheckConsumerKeyAuthExists(resourceKey string) resource.TestCheckFunc { + + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceKey] + + if !ok { + return fmt.Errorf("not found: %s", resourceKey) + } + + if rs.Primary.ID == "" { + return fmt.Errorf("no ID is set") + } + + client := testAccProvider.Meta().(*config).adminClient.KeyAuths + id, err := splitConsumerID(rs.Primary.ID) + + ConsumerKeyAuth, err := client.Get(context.Background(), kong.String(id.ConsumerID), kong.String(id.ID)) + + if err != nil { + return err + } + + if ConsumerKeyAuth == nil { + return fmt.Errorf("ConsumerKeyAuth with id %v not found", id.ID) + } + + return nil + } +} + +const testCreateConsumerKeyAuthConfig = ` +resource "kong_consumer" "my_consumer" { + username = "User1" + custom_id = "123" +} + +resource "kong_plugin" "key_auth_plugin" { + name = "key-auth" +} + +resource "kong_consumer_key_auth" "consumer_key_auth" { + consumer_id = "${kong_consumer.my_consumer.id}" + key = "foo" + tags = ["myTag"] +} +` +const testUpdateConsumerKeyAuthConfig = ` +resource "kong_consumer" "my_consumer" { + username = "User1" + custom_id = "123" +} + +resource "kong_plugin" "key_auth_plugin" { + name = "key-auth" +} + +resource "kong_consumer_key_auth" "consumer_key_auth" { + consumer_id = "${kong_consumer.my_consumer.id}" + key = "foo_updated" + tags = ["myTag", "anotherTag"] +} +` +const testCreateConsumerKeyAuthConfigKeyComputed = ` +resource "kong_consumer" "my_consumer" { + username = "User1" + custom_id = "123" +} + +resource "kong_plugin" "key_auth_plugin" { + name = "key-auth" +} + +resource "kong_consumer_key_auth" "consumer_key_auth" { + consumer_id = "${kong_consumer.my_consumer.id}" + tags = ["myTag"] +} +` diff --git a/kong/utils.go b/kong/utils.go index f524adc..c9c24ca 100644 --- a/kong/utils.go +++ b/kong/utils.go @@ -154,13 +154,3 @@ func IDToString(v *string) string { } return *v } - -// NilString converts a string to a string pointer, -// or if empty returns nil. -func NilString(str string) *string { - if str == "" { - return nil - } else { - return kong.String(str) - } -}