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
114 changes: 114 additions & 0 deletions cli/cmd/api_key_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ package cmd_test
import (
"fmt"
"os"
"os/exec"
"strings"
"time"

. "github.com/onsi/ginkgo/v2"
Expand Down Expand Up @@ -217,4 +219,116 @@ var _ = Describe("API Key Integration Tests", func() {
Expect(err.Error()).To(ContainSubstring("invalid date format"))
})
})

Describe("Old API Key Detection and Warning", func() {
var (
cliPath string
)

BeforeEach(func() {
cliPath = "./oms-cli"

_, err := os.Stat(cliPath)
if err != nil {
Skip("OMS CLI not found at " + cliPath + ", please build it first with 'make build-cli'")
}
})

Context("when using a 22-character old API key format", func() {
It("should detect the old format and attempt to upgrade", func() {
cmd := exec.Command(cliPath, "version")
cmd.Env = append(os.Environ(),
"OMS_PORTAL_API_KEY=fakeapikeywith22charsa", // 22 characters
"OMS_PORTAL_API=http://localhost:3000/api",
)

output, _ := cmd.CombinedOutput()
outputStr := string(output)

Expect(outputStr).To(ContainSubstring("OMS CLI version"))
})
})

Context("when using a new long-format API key", func() {
It("should not show any warning", func() {
cmd := exec.Command(cliPath, "version")
cmd.Env = append(os.Environ(),
"OMS_PORTAL_API_KEY=fake-api-key",
"OMS_PORTAL_API=http://localhost:3000/api",
)

output, _ := cmd.CombinedOutput()
outputStr := string(output)

Expect(outputStr).To(ContainSubstring("OMS CLI version"))
Expect(outputStr).NotTo(ContainSubstring("old API key"))
Expect(outputStr).NotTo(ContainSubstring("Failed to upgrade"))
})
})

Context("when using a 22-character key with list api-keys command", func() {
It("should attempt the upgrade and handle the error gracefully", func() {
cmd := exec.Command(cliPath, "list", "api-keys")
cmd.Env = append(os.Environ(),
"OMS_PORTAL_API_KEY=fakeapikeywith22charsa", // 22 characters (old format)
"OMS_PORTAL_API=http://localhost:3000/api",
)

output, err := cmd.CombinedOutput()
outputStr := string(output)

Expect(err).To(HaveOccurred())

hasWarning := strings.Contains(outputStr, "old API key") ||
strings.Contains(outputStr, "Failed to upgrade") ||
strings.Contains(outputStr, "Unauthorized")

Expect(hasWarning).To(BeTrue(),
"Should contain warning about old key or auth failure. Got: "+outputStr)
})
})

Context("when checking key length detection", func() {
It("should correctly identify 22-character old format", func() {
oldKey := "fakeapikeywith22charsa"
Expect(len(oldKey)).To(Equal(22))
})

It("should correctly identify new long format", func() {
newKey := "4hBieJRj2pWeB9qKJ9wQGE3CrcldLnLwP8fz6qutMjkf1n1"
Expect(len(newKey)).NotTo(Equal(22))
Expect(len(newKey)).To(BeNumerically(">", 22))
})
})
})

Describe("PreRun Hook Execution", func() {
var (
cliPath string
)

BeforeEach(func() {
cliPath = "./oms-cli"

_, err := os.Stat(cliPath)
if err != nil {
Skip("OMS CLI not found at " + cliPath + ", please build it first with 'make build-cli'")
}
})

Context("when running any OMS command", func() {
It("should execute the PreRun hook", func() {
cmd := exec.Command(cliPath, "version")
cmd.Env = append(os.Environ(),
"OMS_PORTAL_API_KEY=valid-key-format-short",
"OMS_PORTAL_API=http://localhost:3000/api",
)

output, _ := cmd.CombinedOutput()
outputStr := string(output)

Expect(outputStr).To(ContainSubstring("OMS CLI version"))
})
})
})
})
108 changes: 108 additions & 0 deletions cli/cmd/cmd_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,18 @@
package cmd_test

import (
"bytes"
"encoding/json"
"io"
"net/http"
"os"
"testing"

"github.com/codesphere-cloud/oms/cli/cmd"
"github.com/codesphere-cloud/oms/internal/env"
"github.com/codesphere-cloud/oms/internal/portal"
"github.com/stretchr/testify/mock"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
Expand All @@ -14,3 +24,101 @@ func TestCmd(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Cmd Suite")
}

var _ = Describe("RootCmd", func() {
var (
mockEnv *env.MockEnv
mockHttpClient *portal.MockHttpClient
)

BeforeEach(func() {
mockEnv = env.NewMockEnv(GinkgoT())
mockHttpClient = portal.NewMockHttpClient(GinkgoT())
})

AfterEach(func() {
mockEnv.AssertExpectations(GinkgoT())
mockHttpClient.AssertExpectations(GinkgoT())
})

Describe("PreRun hook with old API key", func() {
Context("when API key is 22 characters (old format)", func() {
It("attempts to upgrade the key via GetApiKeyId", func() {
oldKey := "fakeapikeywith22charsa" // 22 characters
keyId := "test-key-id-12345"
expectedNewKey := keyId + oldKey

Expect(os.Setenv("OMS_PORTAL_API_KEY", oldKey)).NotTo(HaveOccurred())
Expect(os.Setenv("OMS_PORTAL_API", "http://test-portal.com/api")).NotTo(HaveOccurred())

mockEnv.EXPECT().GetOmsPortalApi().Return("http://test-portal.com/api")

mockHttpClient.EXPECT().Do(mock.Anything).RunAndReturn(
func(req *http.Request) (*http.Response, error) {
Expect(req.Header.Get("X-API-Key")).To(Equal(oldKey))
Expect(req.URL.Path).To(ContainSubstring("/key"))

response := map[string]string{
"keyId": keyId,
}
body, _ := json.Marshal(response)
return &http.Response{
StatusCode: http.StatusOK,
Body: io.NopCloser(bytes.NewReader(body)),
}, nil
})

portalClient := &portal.PortalClient{
Env: mockEnv,
HttpClient: mockHttpClient,
}

result, err := portalClient.GetApiKeyId(oldKey)
Expect(err).NotTo(HaveOccurred())
Expect(result).To(Equal(keyId))

// Concatenate on client side
newApiKey := result + oldKey
Expect(newApiKey).To(Equal(expectedNewKey))

Expect(os.Unsetenv("OMS_PORTAL_API_KEY")).NotTo(HaveOccurred())
Expect(os.Unsetenv("OMS_PORTAL_API")).NotTo(HaveOccurred())
})
})

Context("when API key is not 22 characters (new format)", func() {
It("does not attempt to upgrade the key", func() {
newKey := "new-long-api-key-format-very-long-string"

Expect(os.Setenv("OMS_PORTAL_API_KEY", newKey)).NotTo(HaveOccurred())
Expect(os.Setenv("OMS_PORTAL_API", "http://test-portal.com/api")).NotTo(HaveOccurred())

Expect(len(newKey)).NotTo(Equal(22))

Expect(os.Unsetenv("OMS_PORTAL_API_KEY")).NotTo(HaveOccurred())
Expect(os.Unsetenv("OMS_PORTAL_API")).NotTo(HaveOccurred())
})
})

Context("when API key is empty", func() {
It("does not attempt to upgrade", func() {
Expect(os.Setenv("OMS_PORTAL_API_KEY", "")).NotTo(HaveOccurred())
Expect(os.Setenv("OMS_PORTAL_API", "http://test-portal.com/api")).NotTo(HaveOccurred())

Expect(len(os.Getenv("OMS_PORTAL_API_KEY"))).To(Equal(0))

Expect(os.Unsetenv("OMS_PORTAL_API_KEY")).NotTo(HaveOccurred())
Expect(os.Unsetenv("OMS_PORTAL_API")).NotTo(HaveOccurred())
})
})
})

Describe("GetRootCmd", func() {
It("returns a valid root command", func() {
rootCmd := cmd.GetRootCmd()
Expect(rootCmd).NotTo(BeNil())
Expect(rootCmd.Use).To(Equal("oms"))
Expect(rootCmd.Short).To(Equal("Codesphere Operations Management System (OMS)"))
})
})
})
29 changes: 29 additions & 0 deletions cli/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
package cmd

import (
"fmt"
"log"
"os"

"github.com/codesphere-cloud/cs-go/pkg/io"
"github.com/codesphere-cloud/oms/internal/portal"
"github.com/spf13/cobra"
)

Expand All @@ -25,6 +27,33 @@ func GetRootCmd() *cobra.Command {

This command can be used to run common tasks related to managing codesphere installations,
like downloading new versions.`),
PersistentPreRun: func(cmd *cobra.Command, args []string) {
apiKey := os.Getenv("OMS_PORTAL_API_KEY")

if len(apiKey) == 22 {
fmt.Fprintf(os.Stderr, "Warning: You used an old API key format.\n")
fmt.Fprintf(os.Stderr, "Attempting to upgrade to the new format...\n\n")

portalClient := portal.NewPortalClient()
keyId, err := portalClient.GetApiKeyId(apiKey)

if err != nil {
fmt.Fprintf(os.Stderr, "Error: Failed to upgrade old API key: %v\n", err)
return
}

newApiKey := keyId + apiKey

if err := os.Setenv("OMS_PORTAL_API_KEY", newApiKey); err != nil {
fmt.Fprintf(os.Stderr, "Error: Failed to set environment variable: %v\n", err)
return
}
opts.OmsPortalApiKey = newApiKey

fmt.Fprintf(os.Stderr, "Please update your environment variable:\n\n")
fmt.Fprintf(os.Stderr, " export OMS_PORTAL_API_KEY='%s'\n\n", newApiKey)
}
},
}
// General commands
AddVersionCmd(rootCmd)
Expand Down
1 change: 1 addition & 0 deletions internal/portal/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,7 @@ var _ = Describe("HttpWrapper", func() {
})
})
})

})

// Helper types for testing
Expand Down
54 changes: 54 additions & 0 deletions internal/portal/mocks.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading