Skip to content

Commit 23bb334

Browse files
committed
review changes
Signed-off-by: Mauritz Uphoff <[email protected]>
1 parent 6641b0c commit 23bb334

File tree

7 files changed

+139
-205
lines changed

7 files changed

+139
-205
lines changed

stackit/internal/conversion/conversion.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,3 +183,18 @@ func ParseProviderData(ctx context.Context, providerData any, diags *diag.Diagno
183183
}
184184
return stackitProviderData, true
185185
}
186+
187+
// TODO: write tests
188+
func ParseEphemeralProviderData(ctx context.Context, providerData any, diags *diag.Diagnostics) (core.EphemeralProviderData, bool) {
189+
// Prevent panic if the provider has not been configured.
190+
if providerData == nil {
191+
return core.EphemeralProviderData{}, false
192+
}
193+
194+
stackitProviderData, ok := providerData.(core.EphemeralProviderData)
195+
if !ok {
196+
core.LogAndAddError(ctx, diags, "Error configuring API client", fmt.Sprintf("Expected configure type stackit.ProviderData, got %T", providerData))
197+
return core.EphemeralProviderData{}, false
198+
}
199+
return stackitProviderData, true
200+
}

stackit/internal/core/core.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,19 @@ const (
2525
DatasourceRegionFallbackDocstring = "Uses the `default_region` specified in the provider configuration as a fallback in case no `region` is defined on datasource level."
2626
)
2727

28-
type ProviderData struct {
29-
RoundTripper http.RoundTripper
30-
ServiceAccountEmail string // Deprecated: ServiceAccountEmail is not required and will be removed after 12th June 2025.
28+
type EphemeralProviderData struct {
29+
ProviderData
3130

3231
PrivateKey string
3332
PrivateKeyPath string
3433
ServiceAccountKey string
3534
ServiceAccountKeyPath string
35+
TokenCustomEndpoint string
36+
}
37+
38+
type ProviderData struct {
39+
RoundTripper http.RoundTripper
40+
ServiceAccountEmail string // Deprecated: ServiceAccountEmail is not required and will be removed after 12th June 2025.
3641

3742
// Deprecated: Use DefaultRegion instead
3843
Region string
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package access_token

stackit/internal/services/access_token/ephemeral_access_token.go

Lines changed: 0 additions & 200 deletions
This file was deleted.
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package access_token
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/hashicorp/terraform-plugin-framework/ephemeral"
8+
"github.com/hashicorp/terraform-plugin-framework/ephemeral/schema"
9+
"github.com/hashicorp/terraform-plugin-framework/types"
10+
"github.com/stackitcloud/stackit-sdk-go/core/auth"
11+
"github.com/stackitcloud/stackit-sdk-go/core/clients"
12+
"github.com/stackitcloud/stackit-sdk-go/core/config"
13+
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
14+
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
15+
)
16+
17+
var (
18+
_ ephemeral.EphemeralResource = &accessTokenEphemeralResource{}
19+
_ ephemeral.EphemeralResourceWithConfigure = &accessTokenEphemeralResource{}
20+
)
21+
22+
func NewAccessTokenEphemeralResource() ephemeral.EphemeralResource {
23+
return &accessTokenEphemeralResource{}
24+
}
25+
26+
type accessTokenEphemeralResource struct {
27+
serviceAccountKeyPath string
28+
serviceAccountKey string
29+
privateKeyPath string
30+
privateKey string
31+
tokenCustomEndpoint string
32+
}
33+
34+
func (e *accessTokenEphemeralResource) Configure(ctx context.Context, req ephemeral.ConfigureRequest, resp *ephemeral.ConfigureResponse) {
35+
providerData, ok := conversion.ParseEphemeralProviderData(ctx, req.ProviderData, &resp.Diagnostics)
36+
if !ok {
37+
return
38+
}
39+
40+
e.serviceAccountKey = providerData.ServiceAccountKey
41+
e.serviceAccountKeyPath = providerData.ServiceAccountKeyPath
42+
e.privateKey = providerData.PrivateKey
43+
e.privateKeyPath = providerData.PrivateKeyPath
44+
e.tokenCustomEndpoint = providerData.TokenCustomEndpoint
45+
}
46+
47+
type ephemeralTokenModel struct {
48+
AccessToken types.String `tfsdk:"access_token"`
49+
}
50+
51+
func (e *accessTokenEphemeralResource) Metadata(_ context.Context, req ephemeral.MetadataRequest, resp *ephemeral.MetadataResponse) {
52+
resp.TypeName = req.ProviderTypeName + "_access_token"
53+
}
54+
55+
func (e *accessTokenEphemeralResource) Schema(_ context.Context, _ ephemeral.SchemaRequest, resp *ephemeral.SchemaResponse) {
56+
resp.Schema = schema.Schema{
57+
Description: "STACKIT Access Token ephemeral resource schema.",
58+
Attributes: map[string]schema.Attribute{
59+
"access_token": schema.StringAttribute{
60+
Description: "JWT access token for STACKIT API authentication.",
61+
Computed: true,
62+
Sensitive: true,
63+
},
64+
},
65+
}
66+
}
67+
68+
func (e *accessTokenEphemeralResource) Open(ctx context.Context, req ephemeral.OpenRequest, resp *ephemeral.OpenResponse) {
69+
var model ephemeralTokenModel
70+
71+
resp.Diagnostics.Append(req.Config.Get(ctx, &model)...)
72+
if resp.Diagnostics.HasError() {
73+
return
74+
}
75+
76+
cfg := config.Configuration{
77+
ServiceAccountKey: e.serviceAccountKey,
78+
ServiceAccountKeyPath: e.serviceAccountKeyPath,
79+
PrivateKeyPath: e.privateKeyPath,
80+
PrivateKey: e.privateKey,
81+
TokenCustomUrl: e.tokenCustomEndpoint,
82+
}
83+
84+
rt, err := auth.KeyAuth(&cfg)
85+
if err != nil {
86+
core.LogAndAddError(ctx, &resp.Diagnostics, "Access token generation failed", fmt.Sprintf("Failed to initialize authentication: %v", err))
87+
return
88+
}
89+
90+
// Type assert to access token functionality
91+
client, ok := rt.(*clients.KeyFlow)
92+
if !ok {
93+
core.LogAndAddError(ctx, &resp.Diagnostics, "Access token generation failed", "Internal error: expected *clients.KeyFlow, but received a different implementation of http.RoundTripper")
94+
return
95+
}
96+
97+
// Retrieve the access token
98+
accessToken, err := client.GetAccessToken()
99+
if err != nil {
100+
core.LogAndAddError(
101+
ctx,
102+
&resp.Diagnostics,
103+
"Access token retrieval failed",
104+
fmt.Sprintf("Error obtaining access token: %v", err),
105+
)
106+
return
107+
}
108+
109+
model.AccessToken = types.StringValue(accessToken)
110+
resp.Diagnostics.Append(resp.Result.Set(ctx, model)...)
111+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package access_token

stackit/provider.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -467,12 +467,13 @@ func (p *Provider) Configure(ctx context.Context, req provider.ConfigureRequest,
467467
resp.DataSourceData = providerData
468468
resp.ResourceData = providerData
469469

470-
// Copy service account and private key credentials to support ephemeral access token generation
471-
ephemeralProviderData := providerData
470+
// Copy service account, private key credentials and custom-token endpoint to support ephemeral access token generation
471+
var ephemeralProviderData core.EphemeralProviderData
472472
setStringField(providerConfig.ServiceAccountKey, func(v string) { ephemeralProviderData.ServiceAccountKey = v })
473473
setStringField(providerConfig.ServiceAccountKeyPath, func(v string) { ephemeralProviderData.ServiceAccountKeyPath = v })
474474
setStringField(providerConfig.PrivateKey, func(v string) { ephemeralProviderData.PrivateKey = v })
475475
setStringField(providerConfig.PrivateKeyPath, func(v string) { ephemeralProviderData.PrivateKeyPath = v })
476+
setStringField(providerConfig.TokenCustomEndpoint, func(v string) { ephemeralProviderData.TokenCustomEndpoint = v })
476477
resp.EphemeralResourceData = ephemeralProviderData
477478

478479
providerData.Version = p.version

0 commit comments

Comments
 (0)