Skip to content

Commit be300f0

Browse files
fix: fix auth flow when creating project with access token (#1521)
Relates to STACKITTPR-761
1 parent 1bfb7c5 commit be300f0

4 files changed

Lines changed: 93 additions & 22 deletions

File tree

internal/cmd/project/create/create.go

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -148,29 +148,9 @@ func parseInput(p *print.Printer, cmd *cobra.Command, _ []string) (*inputModel,
148148
func buildRequest(ctx context.Context, model *inputModel, apiClient *resourcemanager.APIClient) (resourcemanager.ApiCreateProjectRequest, error) {
149149
req := apiClient.DefaultAPI.CreateProject(ctx)
150150

151-
authFlow, err := auth.GetAuthFlow()
151+
email, err := auth.GetAuthEmail()
152152
if err != nil {
153-
return req, fmt.Errorf("get authentication flow: %w", err)
154-
}
155-
var email string
156-
switch authFlow {
157-
case auth.AUTH_FLOW_SERVICE_ACCOUNT_TOKEN:
158-
email, err = auth.GetAuthField(auth.SERVICE_ACCOUNT_EMAIL)
159-
if err != nil {
160-
return req, fmt.Errorf("get email of the service account that was used to authenticate: %w", err)
161-
}
162-
case auth.AUTH_FLOW_SERVICE_ACCOUNT_KEY:
163-
email, err = auth.GetAuthField(auth.SERVICE_ACCOUNT_EMAIL)
164-
if err != nil {
165-
return req, fmt.Errorf("get email of the service account that was used to authenticate: %w", err)
166-
}
167-
case auth.AUTH_FLOW_USER_TOKEN:
168-
email, err = auth.GetAuthField(auth.USER_EMAIL)
169-
if err != nil {
170-
return req, fmt.Errorf("get your user email from configuration: %w", err)
171-
}
172-
default:
173-
return req, fmt.Errorf("the configured authentication flow (%s) is not supported, please report this issue", authFlow)
153+
return req, fmt.Errorf("get email of authenticated user: %w", err)
174154
}
175155

176156
if email == "" {

internal/cmd/project/create/create_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ package create
22

33
import (
44
"context"
5+
"encoding/base64"
6+
"encoding/json"
7+
"fmt"
58
"testing"
69

710
"github.com/google/go-cmp/cmp"
@@ -25,6 +28,15 @@ var testParentId = uuid.NewString()
2528
var testNetworkAreaId = uuid.NewString()
2629
var testEmail = "email"
2730

31+
// buildTestJWT creates an unsigned JWT token containing the given email claim.
32+
// getEmailFromToken uses ParseUnverified, so no real signing key is needed.
33+
func buildTestJWT(email string) string {
34+
header, _ := json.Marshal(map[string]string{"alg": "HS256", "typ": "JWT"})
35+
payload, _ := json.Marshal(map[string]string{"email": email})
36+
enc := base64.RawURLEncoding
37+
return fmt.Sprintf("%s.%s.fakesig", enc.EncodeToString(header), enc.EncodeToString(payload))
38+
}
39+
2840
func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string {
2941
flagValues := map[string]string{
3042
parentIdFlag: testParentId,
@@ -193,6 +205,7 @@ func TestBuildRequest(t *testing.T) {
193205
authFlow auth.AuthFlow
194206
sa_email *string
195207
user_email *string
208+
accessToken *string
196209
expectedRequest resourcemanager.ApiCreateProjectRequest
197210
isValid bool
198211
}{
@@ -220,6 +233,13 @@ func TestBuildRequest(t *testing.T) {
220233
expectedRequest: fixtureRequest(),
221234
isValid: true,
222235
},
236+
{
237+
description: "access_token_env_var_no_stored_auth_flow",
238+
model: fixtureInputModel(),
239+
accessToken: utils.Ptr(buildTestJWT(testEmail)),
240+
expectedRequest: fixtureRequest(),
241+
isValid: true,
242+
},
223243
{
224244
description: "missing_network_area_id sa_key",
225245
model: fixtureInputModel(
@@ -296,6 +316,9 @@ func TestBuildRequest(t *testing.T) {
296316
t.Fatalf("Failed to set user email in storage: %v", err)
297317
}
298318
}
319+
if tt.accessToken != nil {
320+
t.Setenv("STACKIT_ACCESS_TOKEN", *tt.accessToken)
321+
}
299322
request, err := buildRequest(testCtx, tt.model, testClient)
300323
if err != nil {
301324
if !tt.isValid {

internal/pkg/auth/auth.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,9 @@ func UserSessionExpired() (bool, error) {
134134
}
135135

136136
func GetAccessToken() (string, error) {
137+
if accessToken := os.Getenv(envAccessTokenName); accessToken != "" {
138+
return accessToken, nil
139+
}
137140
accessToken, err := GetAuthField(ACCESS_TOKEN)
138141
if err != nil {
139142
return "", fmt.Errorf("get %s: %w", ACCESS_TOKEN, err)

internal/pkg/auth/auth_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,3 +335,68 @@ func TestInitKeyFlow(t *testing.T) {
335335
})
336336
}
337337
}
338+
339+
func TestGetAccessToken_EnvVar(t *testing.T) {
340+
const envValue = "token-from-env"
341+
const storedValue = "stored-token"
342+
343+
tests := []struct {
344+
description string
345+
envToken string
346+
storedToken string
347+
expectedToken string
348+
isValid bool
349+
}{
350+
{
351+
description: "env var set and no stored token",
352+
envToken: envValue,
353+
expectedToken: envValue,
354+
isValid: true,
355+
},
356+
{
357+
description: "env var set and stored token present",
358+
envToken: envValue,
359+
storedToken: storedValue,
360+
expectedToken: envValue,
361+
isValid: true,
362+
},
363+
{
364+
description: "env var not set and stored token present",
365+
storedToken: storedValue,
366+
expectedToken: storedValue,
367+
isValid: true,
368+
},
369+
{
370+
description: "env var not set and no stored token",
371+
isValid: false,
372+
},
373+
}
374+
375+
for _, tt := range tests {
376+
t.Run(tt.description, func(t *testing.T) {
377+
keyring.MockInit()
378+
if tt.envToken != "" {
379+
t.Setenv(envAccessTokenName, tt.envToken)
380+
}
381+
if tt.storedToken != "" {
382+
if err := SetAuthField(ACCESS_TOKEN, tt.storedToken); err != nil {
383+
t.Fatalf("Failed to set stored token: %v", err)
384+
}
385+
if err := SetAuthFlow(AUTH_FLOW_SERVICE_ACCOUNT_TOKEN); err != nil {
386+
t.Fatalf("Failed to set auth flow: %v", err)
387+
}
388+
}
389+
390+
got, err := GetAccessToken()
391+
if err != nil {
392+
if !tt.isValid {
393+
return
394+
}
395+
t.Fatalf("unexpected error: %v", err)
396+
}
397+
if tt.expectedToken != got {
398+
t.Errorf("expected token %q, got %q", tt.expectedToken, got)
399+
}
400+
})
401+
}
402+
}

0 commit comments

Comments
 (0)