Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 2 additions & 22 deletions internal/cmd/project/create/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,29 +148,9 @@ func parseInput(p *print.Printer, cmd *cobra.Command, _ []string) (*inputModel,
func buildRequest(ctx context.Context, model *inputModel, apiClient *resourcemanager.APIClient) (resourcemanager.ApiCreateProjectRequest, error) {
req := apiClient.DefaultAPI.CreateProject(ctx)

authFlow, err := auth.GetAuthFlow()
email, err := auth.GetAuthEmail()
if err != nil {
return req, fmt.Errorf("get authentication flow: %w", err)
}
var email string
switch authFlow {
case auth.AUTH_FLOW_SERVICE_ACCOUNT_TOKEN:
email, err = auth.GetAuthField(auth.SERVICE_ACCOUNT_EMAIL)
if err != nil {
return req, fmt.Errorf("get email of the service account that was used to authenticate: %w", err)
}
case auth.AUTH_FLOW_SERVICE_ACCOUNT_KEY:
email, err = auth.GetAuthField(auth.SERVICE_ACCOUNT_EMAIL)
if err != nil {
return req, fmt.Errorf("get email of the service account that was used to authenticate: %w", err)
}
case auth.AUTH_FLOW_USER_TOKEN:
email, err = auth.GetAuthField(auth.USER_EMAIL)
if err != nil {
return req, fmt.Errorf("get your user email from configuration: %w", err)
}
default:
return req, fmt.Errorf("the configured authentication flow (%s) is not supported, please report this issue", authFlow)
return req, fmt.Errorf("get email of authenticated user: %w", err)
}

if email == "" {
Expand Down
23 changes: 23 additions & 0 deletions internal/cmd/project/create/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package create

import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"testing"

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

// buildTestJWT creates an unsigned JWT token containing the given email claim.
// getEmailFromToken uses ParseUnverified, so no real signing key is needed.
func buildTestJWT(email string) string {
header, _ := json.Marshal(map[string]string{"alg": "HS256", "typ": "JWT"})
payload, _ := json.Marshal(map[string]string{"email": email})
enc := base64.RawURLEncoding
return fmt.Sprintf("%s.%s.fakesig", enc.EncodeToString(header), enc.EncodeToString(payload))
}

func fixtureFlagValues(mods ...func(flagValues map[string]string)) map[string]string {
flagValues := map[string]string{
parentIdFlag: testParentId,
Expand Down Expand Up @@ -193,6 +205,7 @@ func TestBuildRequest(t *testing.T) {
authFlow auth.AuthFlow
sa_email *string
user_email *string
accessToken *string
expectedRequest resourcemanager.ApiCreateProjectRequest
isValid bool
}{
Expand Down Expand Up @@ -220,6 +233,13 @@ func TestBuildRequest(t *testing.T) {
expectedRequest: fixtureRequest(),
isValid: true,
},
{
description: "access_token_env_var_no_stored_auth_flow",
model: fixtureInputModel(),
accessToken: utils.Ptr(buildTestJWT(testEmail)),
expectedRequest: fixtureRequest(),
isValid: true,
},
{
description: "missing_network_area_id sa_key",
model: fixtureInputModel(
Expand Down Expand Up @@ -296,6 +316,9 @@ func TestBuildRequest(t *testing.T) {
t.Fatalf("Failed to set user email in storage: %v", err)
}
}
if tt.accessToken != nil {
t.Setenv("STACKIT_ACCESS_TOKEN", *tt.accessToken)
}
request, err := buildRequest(testCtx, tt.model, testClient)
if err != nil {
if !tt.isValid {
Expand Down
3 changes: 3 additions & 0 deletions internal/pkg/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ func UserSessionExpired() (bool, error) {
}

func GetAccessToken() (string, error) {
if accessToken := os.Getenv(envAccessTokenName); accessToken != "" {
return accessToken, nil
}
accessToken, err := GetAuthField(ACCESS_TOKEN)
if err != nil {
return "", fmt.Errorf("get %s: %w", ACCESS_TOKEN, err)
Expand Down
65 changes: 65 additions & 0 deletions internal/pkg/auth/auth_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,3 +335,68 @@ func TestInitKeyFlow(t *testing.T) {
})
}
}

func TestGetAccessToken_EnvVar(t *testing.T) {
const envValue = "token-from-env"
const storedValue = "stored-token"

tests := []struct {
description string
envToken string
storedToken string
expectedToken string
isValid bool
}{
{
description: "env var set and no stored token",
envToken: envValue,
expectedToken: envValue,
isValid: true,
},
{
description: "env var set and stored token present",
envToken: envValue,
storedToken: storedValue,
expectedToken: envValue,
isValid: true,
},
{
description: "env var not set and stored token present",
storedToken: storedValue,
expectedToken: storedValue,
isValid: true,
},
{
description: "env var not set and no stored token",
isValid: false,
},
}

for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
keyring.MockInit()
if tt.envToken != "" {
t.Setenv(envAccessTokenName, tt.envToken)
}
if tt.storedToken != "" {
if err := SetAuthField(ACCESS_TOKEN, tt.storedToken); err != nil {
t.Fatalf("Failed to set stored token: %v", err)
}
if err := SetAuthFlow(AUTH_FLOW_SERVICE_ACCOUNT_TOKEN); err != nil {
t.Fatalf("Failed to set auth flow: %v", err)
}
}

got, err := GetAccessToken()
if err != nil {
if !tt.isValid {
return
}
t.Fatalf("unexpected error: %v", err)
}
if tt.expectedToken != got {
t.Errorf("expected token %q, got %q", tt.expectedToken, got)
}
})
}
}
Loading