@@ -5,17 +5,17 @@ package vaultradar
55
66import (
77 "context"
8- "errors"
98 "fmt"
9+ "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
10+ "github.com/hashicorp/terraform-plugin-framework/schema/validator"
1011 "maps"
1112 "net/http"
13+ "regexp"
1214 "slices"
1315 "strings"
1416
1517 "github.com/hashicorp/hcp-sdk-go/clients/cloud-resource-manager/stable/2019-12-10/client/resource_service"
1618 "github.com/hashicorp/hcp-sdk-go/clients/cloud-resource-manager/stable/2019-12-10/models"
17- rrs "github.com/hashicorp/hcp-sdk-go/clients/cloud-vault-radar/preview/2023-05-01/client/resource_service"
18- rrm "github.com/hashicorp/hcp-sdk-go/clients/cloud-vault-radar/preview/2023-05-01/models"
1919 "github.com/hashicorp/terraform-plugin-framework/diag"
2020 "github.com/hashicorp/terraform-plugin-framework/path"
2121 "github.com/hashicorp/terraform-plugin-framework/resource"
@@ -36,15 +36,25 @@ func radarResourceIAMSchema(binding bool) schema.Schema {
3636 d = "Updates the Vault Radar Resource IAM policy to bind a role to a new principal. Existing bindings are preserved."
3737 }
3838
39+ // Defined in: https://github.com/CyberAP/nanoid-dictionary#nolookalikessafe
40+ nanoidNolookalikesSafeAlphabet := "6789BCDFGHJKLMNPQRTWbcdfghjkmnpqrtwz"
41+ nanoidLength := 20
42+ resourceNameRegex := fmt .Sprintf (`^vault-radar/project/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/scan-target/[%s]{%d}$` , nanoidNolookalikesSafeAlphabet , nanoidLength )
43+
3944 return schema.Schema {
4045 MarkdownDescription : d ,
4146 Attributes : map [string ]schema.Attribute {
42- "resource_uri " : schema.StringAttribute {
47+ "resource_name " : schema.StringAttribute {
4348 Required : true ,
44- Description : "The project's Radar resource URI ." ,
49+ Description : "The HCP resource name associated with the Radar resource. This is the name of the resource in the format `vault-radar/project/<project_id>/scan-target/<scan_target_id>` ." ,
4550 PlanModifiers : []planmodifier.String {
4651 stringplanmodifier .RequiresReplace (),
4752 },
53+ Validators : []validator.String {
54+ stringvalidator .RegexMatches (regexp .MustCompile (resourceNameRegex ),
55+ "must match the format: " + resourceNameRegex ,
56+ ),
57+ },
4858 },
4959 },
5060 }
@@ -59,40 +69,35 @@ func NewRadarResourceIAMBindingResource() resource.Resource {
5969}
6070
6171type radarResourceIAMPolicyUpdater struct {
62- resourceURI string
63- client * clients.Client
64- d iampolicy.TerraformResourceData
72+ resourceName string
73+ client * clients.Client
74+ d iampolicy.TerraformResourceData
6575}
6676
6777func newRadarResourceIAMPolicyUpdater (
6878 ctx context.Context ,
6979 d iampolicy.TerraformResourceData ,
7080 clients * clients.Client ) (iampolicy.ResourceIamUpdater , diag.Diagnostics ) {
7181
72- var resourceURI types.String
73- diags := d .GetAttribute (ctx , path .Root ("resource_uri " ), & resourceURI )
82+ var resourceName types.String
83+ diags := d .GetAttribute (ctx , path .Root ("resource_name " ), & resourceName )
7484
7585 return & radarResourceIAMPolicyUpdater {
76- resourceURI : resourceURI .ValueString (),
77- client : clients ,
78- d : d ,
86+ resourceName : resourceName .ValueString (),
87+ client : clients ,
88+ d : d ,
7989 }, diags
8090}
8191
8292func (u * radarResourceIAMPolicyUpdater ) GetMutexKey () string {
83- return u .resourceURI
93+ return u .resourceName
8494}
8595
8696// GetResourceIamPolicy Fetch the existing IAM policy attached to a resource.
8797func (u * radarResourceIAMPolicyUpdater ) GetResourceIamPolicy (ctx context.Context ) (* models.HashicorpCloudResourcemanagerPolicy , diag.Diagnostics ) {
88- rr , lookupDiags := lookupRadarResourceByURI (ctx , u .client , u .resourceURI )
89- if lookupDiags .HasError () {
90- return nil , lookupDiags
91- }
92-
9398 var diags diag.Diagnostics
9499 params := resource_service .NewResourceServiceGetIamPolicyParams ()
95- params .ResourceName = & rr . HcpResourceName
100+ params .ResourceName = & u . resourceName
96101
97102 res , err := u .client .ResourceService .ResourceServiceGetIamPolicy (params , nil )
98103 if err != nil {
@@ -131,16 +136,11 @@ func (u *radarResourceIAMPolicyUpdater) SetResourceIamPolicy(ctx context.Context
131136 return nil , diags
132137 }
133138
134- rr , diags := lookupRadarResourceByURI (ctx , u .client , u .resourceURI )
135- if diags .HasError () {
136- return nil , diags
137- }
138-
139139 params := resource_service .NewResourceServiceSetIamPolicyParams ()
140140
141141 params .Body = & models.HashicorpCloudResourcemanagerResourceSetIamPolicyRequest {
142142 Policy : policy ,
143- ResourceName : rr . HcpResourceName ,
143+ ResourceName : u . resourceName ,
144144 }
145145
146146 res , err := u .client .ResourceService .ResourceServiceSetIamPolicy (params , nil )
@@ -161,84 +161,3 @@ var (
161161 _ iampolicy.NewResourceIamUpdaterFunc = newRadarResourceIAMPolicyUpdater
162162 _ iampolicy.ResourceIamUpdater = & radarResourceIAMPolicyUpdater {}
163163)
164-
165- func lookupRadarResourceByURI (ctx context.Context , client * clients.Client , resourceURI string ) (* rrm.VaultRadar20230501Resource , diag.Diagnostics ) {
166- projectID := client .Config .ProjectID
167-
168- // Filter to find the exact radar resources by URI.
169- filters := []* rrm.VaultRadar20230501Filter {
170- {
171- ID : "uri" ,
172- Op : rrm .NewFilterFilterOperation (rrm .FilterFilterOperationEQ ),
173- Value : []* rrm.VaultRadar20230501FilterValue {{StringValue : resourceURI }},
174- ExactMatch : true ,
175- },
176- {
177- ID : "state" ,
178- Op : rrm .NewFilterFilterOperation (rrm .FilterFilterOperationNEQNULLAWARE ),
179- Value : []* rrm.VaultRadar20230501FilterValue {{StringValue : "deleted" }},
180- ExactMatch : true ,
181- },
182- }
183-
184- body := rrs.ListResourcesBody {
185- Location : & rrs.ListResourcesParamsBodyLocation {
186- OrganizationID : client .Config .OrganizationID ,
187- },
188- Search : & rrm.VaultRadar20230501SearchSchema {
189- Limit : 1 , // we expect either 0 or 1
190- Page : 1 ,
191- Filters : filters ,
192- },
193- }
194-
195- var diags diag.Diagnostics
196- resp , err := clients .ListRadarResources (ctx , client , projectID , body )
197- if err != nil {
198- var srvErr * rrs.ListResourcesDefault
199- ok := errors .As (err , & srvErr )
200- if ! ok {
201- diags .AddError ("unexpected error while reading response from service." , err .Error ())
202- return nil , diags
203- }
204-
205- diags .Append (customdiags .NewErrorHTTPStatusCode ("failed to retrieve radar resource" , err .Error (), srvErr .Code ()))
206- return nil , diags
207- }
208-
209- resources := resp .GetPayload ().Resources
210- if len (resources ) == 0 {
211- diags .AddError ("unable to find radar resource" , "no radar resource could be found with the uri: " + resourceURI )
212- return nil , diags
213- }
214-
215- resource := resources [0 ]
216- diags .Append (validateResource (resource , projectID )... )
217-
218- return resources [0 ], diags
219- }
220-
221- func validateResource (resource * rrm.VaultRadar20230501Resource , projectID string ) diag.Diagnostics {
222- var diags diag.Diagnostics
223-
224- if projectID == "" {
225- diags .AddError ("hcp project_id unknown" , "the project_id where Vault Radar is located must be specified in the hcp provider config." )
226- return diags
227- }
228-
229- if resource .HcpResourceStatus != "registered" {
230- diags .AddError ("invalid radar resource status" , "policy cannot be applied to a resource with status: " + resource .HcpResourceStatus )
231- }
232-
233- if resource .State != "created" {
234- diags .AddError ("invalid radar resource state" , "policy cannot be applied to a resource with state: " + resource .State )
235- }
236-
237- // This should never happen, but we check it just in case.
238- prefix := "vault-radar/project/" + projectID + "/scan-target/"
239- if ! strings .HasPrefix (resource .HcpResourceName , prefix ) {
240- diags .AddError ("invalid radar resource name" , "got resource name: " + resource .HcpResourceName + ", expected it to start with: " + prefix )
241- }
242-
243- return diags
244- }
0 commit comments