From 9e10be127b5825e39cba8573a4255cb964501147 Mon Sep 17 00:00:00 2001 From: Shawn Castrianni Date: Fri, 5 Jan 2024 21:00:13 -0600 Subject: [PATCH] Add consumer_jwt resource --- konnect/client/consumer_jwt.go | 27 +++++ konnect/provider.go | 1 + konnect/resource_consumer_jwt.go | 192 +++++++++++++++++++++++++++++++ test/main.tf | 6 + 4 files changed, 226 insertions(+) create mode 100644 konnect/client/consumer_jwt.go create mode 100644 konnect/resource_consumer_jwt.go diff --git a/konnect/client/consumer_jwt.go b/konnect/client/consumer_jwt.go new file mode 100644 index 0000000..842d751 --- /dev/null +++ b/konnect/client/consumer_jwt.go @@ -0,0 +1,27 @@ +package client + +import "strings" + +const ( + ConsumerJWTPath = ControlPlanePathGet + "/core-entities/consumers/%s/jwt" + ConsumerJWTPathGet = ConsumerJWTPath + "/%s" +) + +type ConsumerJWT struct { + ControlPlaneId string `json:"-"` + ConsumerId string `json:"-"` + Id string `json:"id"` + Key string `json:"key,omitempty"` + Algorithm string `json:"algorithm"` + RSAPublicKey string `json:"rsa_public_key,omitempty"` + Secret string `json:"secret,omitempty"` +} + +func (cj *ConsumerJWT) ConsumerJWTEncodeId() string { + return cj.ControlPlaneId + IdSeparator + cj.ConsumerId + IdSeparator + cj.Id +} + +func ConsumerJWTDecodeId(s string) (string, string, string) { + tokens := strings.Split(s, IdSeparator) + return tokens[0], tokens[1], tokens[2] +} diff --git a/konnect/provider.go b/konnect/provider.go index f611553..ef198fb 100644 --- a/konnect/provider.go +++ b/konnect/provider.go @@ -47,6 +47,7 @@ func Provider() *schema.Provider { "konnect_consumer_acl": resourceConsumerACL(), "konnect_consumer_basic": resourceConsumerBasic(), "konnect_consumer_hmac": resourceConsumerHMAC(), + "konnect_consumer_jwt": resourceConsumerJWT(), "konnect_plugin": resourcePlugin(), }, DataSourcesMap: map[string]*schema.Resource{ diff --git a/konnect/resource_consumer_jwt.go b/konnect/resource_consumer_jwt.go new file mode 100644 index 0000000..43b3259 --- /dev/null +++ b/konnect/resource_consumer_jwt.go @@ -0,0 +1,192 @@ +package konnect + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "github.com/go-http-utils/headers" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "github.com/scastria/terraform-provider-konnect/konnect/client" + "net/http" +) + +func resourceConsumerJWT() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceConsumerJWTCreate, + ReadContext: resourceConsumerJWTRead, + UpdateContext: resourceConsumerJWTUpdate, + DeleteContext: resourceConsumerJWTDelete, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + Schema: map[string]*schema.Schema{ + "control_plane_id": { + Type: schema.TypeString, + Required: true, + }, + "consumer_id": { + Type: schema.TypeString, + Required: true, + }, + "key": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "secret": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, + "algorithm": { + Type: schema.TypeString, + Optional: true, + Default: "HS256", + ValidateFunc: validation.StringInSlice([]string{"HS256", "HS384", "HS512", "RS256", "RS384", "RS512", "ES256", "ES384"}, false), + }, + "rsa_public_key": { + Type: schema.TypeString, + Optional: true, + }, + "jwt_id": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func fillConsumerJWT(c *client.ConsumerJWT, d *schema.ResourceData) { + c.ControlPlaneId = d.Get("control_plane_id").(string) + c.ConsumerId = d.Get("consumer_id").(string) + c.Algorithm = d.Get("algorithm").(string) + key, ok := d.GetOk("key") + if ok { + c.Key = key.(string) + } + secret, ok := d.GetOk("secret") + if ok { + c.Secret = secret.(string) + } + rsaPublicKey, ok := d.GetOk("rsa_public_key") + if ok { + c.RSAPublicKey = rsaPublicKey.(string) + } +} + +func fillResourceDataFromConsumerJWT(c *client.ConsumerJWT, d *schema.ResourceData) { + d.Set("control_plane_id", c.ControlPlaneId) + d.Set("consumer_id", c.ConsumerId) + d.Set("algorithm", c.Algorithm) + d.Set("key", c.Key) + d.Set("secret", c.Secret) + d.Set("rsa_public_key", c.RSAPublicKey) + d.Set("jwt_id", c.Id) +} + +func resourceConsumerJWTCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + var diags diag.Diagnostics + c := m.(*client.Client) + buf := bytes.Buffer{} + newConsumerJWT := client.ConsumerJWT{} + fillConsumerJWT(&newConsumerJWT, d) + err := json.NewEncoder(&buf).Encode(newConsumerJWT) + if err != nil { + d.SetId("") + return diag.FromErr(err) + } + requestPath := fmt.Sprintf(client.ConsumerJWTPath, newConsumerJWT.ControlPlaneId, newConsumerJWT.ConsumerId) + requestHeaders := http.Header{ + headers.ContentType: []string{client.ApplicationJson}, + } + body, err := c.HttpRequest(ctx, true, http.MethodPost, requestPath, nil, requestHeaders, &buf) + if err != nil { + d.SetId("") + return diag.FromErr(err) + } + retVal := &client.ConsumerJWT{} + err = json.NewDecoder(body).Decode(retVal) + if err != nil { + d.SetId("") + return diag.FromErr(err) + } + retVal.ControlPlaneId = newConsumerJWT.ControlPlaneId + retVal.ConsumerId = newConsumerJWT.ConsumerId + d.SetId(retVal.ConsumerJWTEncodeId()) + fillResourceDataFromConsumerJWT(retVal, d) + return diags +} + +func resourceConsumerJWTRead(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + var diags diag.Diagnostics + controlPlaneId, consumerId, id := client.ConsumerJWTDecodeId(d.Id()) + c := m.(*client.Client) + requestPath := fmt.Sprintf(client.ConsumerJWTPathGet, controlPlaneId, consumerId, id) + body, err := c.HttpRequest(ctx, true, http.MethodGet, requestPath, nil, nil, &bytes.Buffer{}) + if err != nil { + d.SetId("") + re := err.(*client.RequestError) + if re.StatusCode == http.StatusNotFound { + return diags + } + return diag.FromErr(err) + } + retVal := &client.ConsumerJWT{} + err = json.NewDecoder(body).Decode(retVal) + if err != nil { + d.SetId("") + return diag.FromErr(err) + } + retVal.ControlPlaneId = controlPlaneId + retVal.ConsumerId = consumerId + fillResourceDataFromConsumerJWT(retVal, d) + return diags +} + +func resourceConsumerJWTUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + var diags diag.Diagnostics + controlPlaneId, consumerId, id := client.ConsumerJWTDecodeId(d.Id()) + c := m.(*client.Client) + buf := bytes.Buffer{} + upConsumerJWT := client.ConsumerJWT{} + fillConsumerJWT(&upConsumerJWT, d) + // Hide non-updateable fields + //upTeam.IsPredefined = false + err := json.NewEncoder(&buf).Encode(upConsumerJWT) + if err != nil { + return diag.FromErr(err) + } + requestPath := fmt.Sprintf(client.ConsumerJWTPathGet, controlPlaneId, consumerId, id) + requestHeaders := http.Header{ + headers.ContentType: []string{client.ApplicationJson}, + } + body, err := c.HttpRequest(ctx, true, http.MethodPut, requestPath, nil, requestHeaders, &buf) + if err != nil { + return diag.FromErr(err) + } + retVal := &client.ConsumerJWT{} + err = json.NewDecoder(body).Decode(retVal) + if err != nil { + return diag.FromErr(err) + } + retVal.ControlPlaneId = controlPlaneId + retVal.ConsumerId = consumerId + fillResourceDataFromConsumerJWT(retVal, d) + return diags +} + +func resourceConsumerJWTDelete(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { + var diags diag.Diagnostics + controlPlaneId, consumerId, id := client.ConsumerJWTDecodeId(d.Id()) + c := m.(*client.Client) + requestPath := fmt.Sprintf(client.ConsumerJWTPathGet, controlPlaneId, consumerId, id) + _, err := c.HttpRequest(ctx, true, http.MethodDelete, requestPath, nil, nil, &bytes.Buffer{}) + if err != nil { + return diag.FromErr(err) + } + d.SetId("") + return diags +} diff --git a/test/main.tf b/test/main.tf index 7d19b47..a26f10e 100644 --- a/test/main.tf +++ b/test/main.tf @@ -163,3 +163,9 @@ data "konnect_consumer" "C" { # username = "shawn" ## secret = "pass2" #} +#resource "konnect_consumer_jwt" "CJWT" { +# control_plane_id = data.konnect_control_plane.RG.id +# consumer_id = data.konnect_consumer.C.consumer_id +# secret = "pass" +# key = "hello" +#}