Skip to content

Commit e02abd7

Browse files
authored
feat(keymanager): add algorithm field to allow custom key algorithms (#3403)
* feat(keymanager): add algorithm field to allow custom key algorithms * docs(keymanager): document algorithm parameter with examples * test(keymanager): add tests and cassettes for algorithm parameter * test(keymanager): add cassette for custom algorithm test * feat: allow explicit algorithm selection in Key Manager keys * fix: lint issues in Key Manager implementation * feat: use SDK enums for Key Manager algorithm validation * feat: refactor Key Manager to use usage + algorithm (AWS-like approach) * chore: go mod tidy * refactor: remove comments from functions
1 parent 302e25a commit e02abd7

9 files changed

+945
-219
lines changed

internal/services/keymanager/helpers.go

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,27 @@ import (
1313
"github.com/scaleway/terraform-provider-scaleway/v2/internal/types"
1414
)
1515

16+
const (
17+
usageSymmetricEncryption = "symmetric_encryption"
18+
usageAsymmetricEncryption = "asymmetric_encryption"
19+
usageAsymmetricSigning = "asymmetric_signing"
20+
)
21+
1622
func UsageToString(u *key_manager.KeyUsage) string {
1723
if u == nil {
1824
return ""
1925
}
2026

2127
if u.SymmetricEncryption != nil {
22-
return "symmetric_encryption"
28+
return usageSymmetricEncryption
2329
}
2430

2531
if u.AsymmetricEncryption != nil {
26-
return "asymmetric_encryption"
32+
return usageAsymmetricEncryption
2733
}
2834

2935
if u.AsymmetricSigning != nil {
30-
return "asymmetric_signing"
36+
return usageAsymmetricSigning
3137
}
3238

3339
return ""
@@ -55,17 +61,43 @@ func NewKeyManagerAPIWithRegionAndID(m any, id string) (*key_manager.API, scw.Re
5561
return client, region, keyID, nil
5662
}
5763

58-
func ExpandKeyUsage(usage string) *key_manager.KeyUsage {
64+
func ExpandKeyUsageFromFields(d *schema.ResourceData) *key_manager.KeyUsage {
65+
if v, ok := d.GetOk("usage_symmetric_encryption"); ok {
66+
alg := key_manager.KeyAlgorithmSymmetricEncryption(v.(string))
67+
68+
return &key_manager.KeyUsage{SymmetricEncryption: &alg}
69+
}
70+
71+
if v, ok := d.GetOk("usage_asymmetric_encryption"); ok {
72+
alg := key_manager.KeyAlgorithmAsymmetricEncryption(v.(string))
73+
74+
return &key_manager.KeyUsage{AsymmetricEncryption: &alg}
75+
}
76+
77+
if v, ok := d.GetOk("usage_asymmetric_signing"); ok {
78+
alg := key_manager.KeyAlgorithmAsymmetricSigning(v.(string))
79+
80+
return &key_manager.KeyUsage{AsymmetricSigning: &alg}
81+
}
82+
83+
if v, ok := d.GetOk("usage"); ok {
84+
return ExpandKeyUsageLegacy(v.(string))
85+
}
86+
87+
return nil
88+
}
89+
90+
func ExpandKeyUsageLegacy(usage string) *key_manager.KeyUsage {
5991
switch usage {
60-
case "symmetric_encryption":
92+
case usageSymmetricEncryption:
6193
alg := key_manager.KeyAlgorithmSymmetricEncryptionAes256Gcm
6294

6395
return &key_manager.KeyUsage{SymmetricEncryption: &alg}
64-
case "asymmetric_encryption":
96+
case usageAsymmetricEncryption:
6597
alg := key_manager.KeyAlgorithmAsymmetricEncryptionRsaOaep3072Sha256
6698

6799
return &key_manager.KeyUsage{AsymmetricEncryption: &alg}
68-
case "asymmetric_signing":
100+
case usageAsymmetricSigning:
69101
alg := key_manager.KeyAlgorithmAsymmetricSigningEcP256Sha256
70102

71103
return &key_manager.KeyUsage{AsymmetricSigning: &alg}
@@ -74,6 +106,26 @@ func ExpandKeyUsage(usage string) *key_manager.KeyUsage {
74106
}
75107
}
76108

109+
func AlgorithmFromKeyUsage(u *key_manager.KeyUsage) string {
110+
if u == nil {
111+
return ""
112+
}
113+
114+
if u.SymmetricEncryption != nil {
115+
return string(*u.SymmetricEncryption)
116+
}
117+
118+
if u.AsymmetricEncryption != nil {
119+
return string(*u.AsymmetricEncryption)
120+
}
121+
122+
if u.AsymmetricSigning != nil {
123+
return string(*u.AsymmetricSigning)
124+
}
125+
126+
return ""
127+
}
128+
77129
func ExpandKeyRotationPolicy(v any) (*key_manager.KeyRotationPolicy, error) {
78130
list, ok := v.([]any)
79131
if !ok || len(list) == 0 {
@@ -99,7 +151,6 @@ func ExpandKeyRotationPolicy(v any) (*key_manager.KeyRotationPolicy, error) {
99151
RotationPeriod: scw.NewDurationFromTimeDuration(period),
100152
}
101153

102-
// Handle next_rotation_at if provided
103154
if nextRotationStr, ok := m["next_rotation_at"].(string); ok && nextRotationStr != "" {
104155
nextRotation, err := time.Parse(time.RFC3339, nextRotationStr)
105156
if err != nil {

internal/services/keymanager/key_resource.go

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,19 @@ package keymanager
22

33
import (
44
"context"
5+
"fmt"
56

7+
"github.com/hashicorp/go-cty/cty"
68
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
9+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff"
710
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
811
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
912
key_manager "github.com/scaleway/scaleway-sdk-go/api/key_manager/v1alpha1"
1013
"github.com/scaleway/terraform-provider-scaleway/v2/internal/dsf"
1114
"github.com/scaleway/terraform-provider-scaleway/v2/internal/locality/regional"
1215
"github.com/scaleway/terraform-provider-scaleway/v2/internal/services/account"
1316
"github.com/scaleway/terraform-provider-scaleway/v2/internal/types"
17+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/verify"
1418
)
1519

1620
func ResourceKeyManagerKey() *schema.Resource {
@@ -19,6 +23,9 @@ func ResourceKeyManagerKey() *schema.Resource {
1923
ReadContext: resourceKeyManagerKeyRead,
2024
UpdateContext: resourceKeyManagerKeyUpdate,
2125
DeleteContext: resourceKeyManagerKeyDelete,
26+
CustomizeDiff: customdiff.All(
27+
validateUsageAlgorithmCombination(),
28+
),
2229
Schema: map[string]*schema.Schema{
2330
"name": {
2431
Type: schema.TypeString,
@@ -33,7 +40,32 @@ func ResourceKeyManagerKey() *schema.Resource {
3340
ValidateFunc: validation.StringInSlice([]string{
3441
"symmetric_encryption", "asymmetric_encryption", "asymmetric_signing",
3542
}, false),
36-
Description: "Key usage. Keys with a usage set to 'symmetric_encryption' can encrypt and decrypt data using the AES-256-GCM key algorithm. Possible values: symmetric_encryption, asymmetric_encryption, asymmetric_signing.",
43+
Description: "Key usage type. Possible values: symmetric_encryption, asymmetric_encryption, asymmetric_signing.",
44+
},
45+
"algorithm": {
46+
Type: schema.TypeString,
47+
Required: true,
48+
Description: "Algorithm to use for the key. The valid algorithms depend on the usage type.",
49+
ValidateDiagFunc: func(i any, p cty.Path) diag.Diagnostics {
50+
var allKnownAlgos []string
51+
52+
symAlgos := key_manager.KeyAlgorithmSymmetricEncryption("").Values()
53+
for _, algo := range symAlgos {
54+
allKnownAlgos = append(allKnownAlgos, string(algo))
55+
}
56+
57+
asymEncAlgos := key_manager.KeyAlgorithmAsymmetricEncryption("").Values()
58+
for _, algo := range asymEncAlgos {
59+
allKnownAlgos = append(allKnownAlgos, string(algo))
60+
}
61+
62+
asymSignAlgos := key_manager.KeyAlgorithmAsymmetricSigning("").Values()
63+
for _, algo := range asymSignAlgos {
64+
allKnownAlgos = append(allKnownAlgos, string(algo))
65+
}
66+
67+
return verify.ValidateStringInSliceWithWarning(allKnownAlgos, "algorithm")(i, p)
68+
},
3769
},
3870
"description": {
3971
Type: schema.TypeString,
@@ -116,7 +148,15 @@ func resourceKeyManagerKeyCreate(ctx context.Context, d *schema.ResourceData, m
116148
createReq.Origin = key_manager.KeyOrigin(v.(string))
117149
}
118150

119-
createReq.Usage = ExpandKeyUsage(d.Get("usage").(string))
151+
usage := d.Get("usage").(string)
152+
algorithm := d.Get("algorithm").(string)
153+
154+
keyUsage, err := expandUsageAlgorithm(usage, algorithm)
155+
if err != nil {
156+
return diag.FromErr(err)
157+
}
158+
159+
createReq.Usage = keyUsage
120160

121161
key, err := api.CreateKey(createReq)
122162
if err != nil {
@@ -145,7 +185,13 @@ func resourceKeyManagerKeyRead(ctx context.Context, d *schema.ResourceData, m an
145185
_ = d.Set("name", key.Name)
146186
_ = d.Set("project_id", key.ProjectID)
147187
_ = d.Set("region", key.Region.String())
148-
_ = d.Set("usage", UsageToString(key.Usage))
188+
189+
usageType := UsageToString(key.Usage)
190+
algorithm := AlgorithmFromKeyUsage(key.Usage)
191+
192+
_ = d.Set("usage", usageType)
193+
_ = d.Set("algorithm", algorithm)
194+
149195
_ = d.Set("description", key.Description)
150196
_ = d.Set("tags", key.Tags)
151197
_ = d.Set("rotation_count", int(key.RotationCount))
@@ -222,3 +268,31 @@ func resourceKeyManagerKeyDelete(ctx context.Context, d *schema.ResourceData, m
222268

223269
return nil
224270
}
271+
272+
func validateUsageAlgorithmCombination() schema.CustomizeDiffFunc {
273+
return func(ctx context.Context, diff *schema.ResourceDiff, _ any) error {
274+
return nil
275+
}
276+
}
277+
278+
func expandUsageAlgorithm(usage, algorithm string) (*key_manager.KeyUsage, error) {
279+
switch usage {
280+
case usageSymmetricEncryption:
281+
typedAlgo := key_manager.KeyAlgorithmSymmetricEncryption(algorithm)
282+
283+
return &key_manager.KeyUsage{SymmetricEncryption: &typedAlgo}, nil
284+
285+
case usageAsymmetricEncryption:
286+
typedAlgo := key_manager.KeyAlgorithmAsymmetricEncryption(algorithm)
287+
288+
return &key_manager.KeyUsage{AsymmetricEncryption: &typedAlgo}, nil
289+
290+
case usageAsymmetricSigning:
291+
typedAlgo := key_manager.KeyAlgorithmAsymmetricSigning(algorithm)
292+
293+
return &key_manager.KeyUsage{AsymmetricSigning: &typedAlgo}, nil
294+
295+
default:
296+
return nil, fmt.Errorf("unknown usage type: %s", usage)
297+
}
298+
}

0 commit comments

Comments
 (0)