diff --git a/cmd/login.go b/cmd/login.go index 2180504..fa31524 100644 --- a/cmd/login.go +++ b/cmd/login.go @@ -120,7 +120,11 @@ microcks login http://localhost:8080 --sso --sso-launch-browser=false os.Exit(1) } //Perform login and retrive tokens - authToken, refreshToken = passwordLogin(keycloakUrl, clientID, clientSecret, username, password) + var loginErr error + authToken, refreshToken, loginErr = passwordLogin(keycloakUrl, clientID, clientSecret, username, password) + if loginErr != nil { + log.Fatal(loginErr) + } authCfg.ClientId = clientID authCfg.ClientSecret = clientSecret } else { @@ -308,17 +312,16 @@ func ssoAuthFlow(url string, ssoLaunchBrowser bool) { } } -func passwordLogin(keycloakURL, clientId, clientSecret, Username, Password string) (string, string) { +func passwordLogin(keycloakURL, clientId, clientSecret, Username, Password string) (string, string, error) { kc := connectors.NewKeycloakClient(keycloakURL, clientId, clientSecret) username, password := promptCredentials(Username, Password) authToken, refreshToken, err := kc.ConnectAndGetTokenAndRefreshToken(username, password) - if err != nil { - panic(err) + return "", "", err } - return authToken, refreshToken + return authToken, refreshToken, nil } func promptCredentials(username, password string) (string, string) { diff --git a/pkg/connectors/keycloak_client.go b/pkg/connectors/keycloak_client.go index 5d6aeed..a9be1a9 100644 --- a/pkg/connectors/keycloak_client.go +++ b/pkg/connectors/keycloak_client.go @@ -97,16 +97,23 @@ func (c *keycloakClient) ConnectAndGetToken() (string, error) { body, err := io.ReadAll(resp.Body) if err != nil { - panic(err.Error()) + return "", fmt.Errorf("failed to read response body: %w", err) + } + + if resp.StatusCode != http.StatusOK { + return "", fmt.Errorf("keycloak returned HTTP %d: %s", resp.StatusCode, strings.TrimSpace(string(body))) } var openIDResp map[string]interface{} if err := json.Unmarshal(body, &openIDResp); err != nil { - panic(err) + return "", fmt.Errorf("failed to parse token response: %w", err) } - accessToken := openIDResp["access_token"].(string) - return accessToken, err + accessToken, ok := openIDResp["access_token"].(string) + if !ok || accessToken == "" { + return "", fmt.Errorf("keycloak response missing 'access_token'") + } + return accessToken, nil } func (c *keycloakClient) GetOIDCConfig() (*oauth2.Config, error) { @@ -127,16 +134,23 @@ func (c *keycloakClient) GetOIDCConfig() (*oauth2.Config, error) { body, err := io.ReadAll(resp.Body) if err != nil { - panic(err.Error()) + return nil, fmt.Errorf("failed to read response body: %w", err) + } + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("keycloak returned HTTP %d: %s", resp.StatusCode, strings.TrimSpace(string(body))) } var openIDResp map[string]interface{} if err := json.Unmarshal(body, &openIDResp); err != nil { - panic(err) + return nil, fmt.Errorf("failed to parse OIDC config response: %w", err) } - authURL := openIDResp["authorization_endpoint"].(string) - tokenURL := openIDResp["token_endpoint"].(string) + authURL, ok1 := openIDResp["authorization_endpoint"].(string) + tokenURL, ok2 := openIDResp["token_endpoint"].(string) + if !ok1 || !ok2 { + return nil, fmt.Errorf("OIDC config response missing 'authorization_endpoint' or 'token_endpoint'") + } return &oauth2.Config{ Endpoint: oauth2.Endpoint{ @@ -174,16 +188,23 @@ func (c *keycloakClient) ConnectAndGetTokenAndRefreshToken(username, password st body, err := io.ReadAll(resp.Body) if err != nil { - panic(err.Error()) + return "", "", fmt.Errorf("failed to read response body: %w", err) + } + + if resp.StatusCode != http.StatusOK { + return "", "", fmt.Errorf("keycloak returned HTTP %d: %s", resp.StatusCode, strings.TrimSpace(string(body))) } var openIDResp map[string]interface{} if err := json.Unmarshal(body, &openIDResp); err != nil { - panic(err) + return "", "", fmt.Errorf("failed to parse token response: %w", err) } - authToken := openIDResp["access_token"].(string) - refreshToken := openIDResp["refresh_token"].(string) + authToken, ok1 := openIDResp["access_token"].(string) + refreshToken, ok2 := openIDResp["refresh_token"].(string) + if !ok1 || !ok2 { + return "", "", fmt.Errorf("keycloak response missing 'access_token' or 'refresh_token'") + } return authToken, refreshToken, nil } diff --git a/pkg/connectors/microcks_client.go b/pkg/connectors/microcks_client.go index 6021a3c..e04c4a2 100644 --- a/pkg/connectors/microcks_client.go +++ b/pkg/connectors/microcks_client.go @@ -239,21 +239,26 @@ func (c *microcksClient) GetKeycloakURL() (string, error) { body, err := io.ReadAll(resp.Body) if err != nil { - panic(err.Error()) + return "", fmt.Errorf("failed to read response body: %w", err) + } + + if resp.StatusCode != http.StatusOK { + return "", fmt.Errorf("microcks returned HTTP %d: %s", resp.StatusCode, strings.TrimSpace(string(body))) } var configResp map[string]interface{} if err := json.Unmarshal(body, &configResp); err != nil { - panic(err) + return "", fmt.Errorf("failed to parse keycloak config response (is the server URL correct?): %w", err) } // Retrieve auth server url and realm name. - enabled := configResp["enabled"].(bool) - authServerURL := configResp["auth-server-url"].(string) - realmName := configResp["realm"].(string) - - // Return a proper URL or 'null' if Keycloak is disables. + enabled, _ := configResp["enabled"].(bool) if enabled { + authServerURL, ok1 := configResp["auth-server-url"].(string) + realmName, ok2 := configResp["realm"].(string) + if !ok1 || !ok2 { + return "", fmt.Errorf("invalid keycloak config response (missing 'auth-server-url' or 'realm')") + } return authServerURL + "/realms/" + realmName + "/", nil } return "null", nil @@ -425,7 +430,11 @@ func (c *microcksClient) GetTestResult(testResultID string) (*TestResultSummary, body, err := io.ReadAll(resp.Body) if err != nil { - panic(err.Error()) + return nil, fmt.Errorf("failed to read response body: %w", err) + } + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("microcks returned HTTP %d: %s", resp.StatusCode, strings.TrimSpace(string(body))) } result := TestResultSummary{} @@ -558,7 +567,7 @@ func (c *microcksClient) DownloadArtifact(artifactURL string, mainArtifact bool, respBody, err := io.ReadAll(resp.Body) if err != nil { - panic(err.Error()) + return "", fmt.Errorf("failed to read response body: %w", err) } // Raise exception if not created. @@ -566,7 +575,7 @@ func (c *microcksClient) DownloadArtifact(artifactURL string, mainArtifact bool, return "", errs.New(string(respBody)) } - return string(respBody), err + return string(respBody), nil } func ensureValidOperationsList(filteredOperations string) bool { diff --git a/pkg/connectors/microcks_client_test.go b/pkg/connectors/microcks_client_test.go index 2fe423e..b5d3ed7 100644 --- a/pkg/connectors/microcks_client_test.go +++ b/pkg/connectors/microcks_client_test.go @@ -102,3 +102,38 @@ func TestDownloadArtifactReturnsResponseBody(t *testing.T) { t.Fatalf("expected response body %q, got %q", expectedBody, msg) } } + +func TestUnexpectedServerResponseHandling(t *testing.T) { + // Create a test server that returns HTML / Bad Gateway + const htmlError = `502 Bad Gateway` + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusBadGateway) + _, _ = w.Write([]byte(htmlError)) + })) + defer server.Close() + + client := NewMicrocksClient(server.URL) + + // Test GetKeycloakURL + _, err := client.GetKeycloakURL() + if err == nil { + t.Error("expected GetKeycloakURL to return error on Bad Gateway response, got nil") + } else if !strings.Contains(err.Error(), "502") { + t.Errorf("expected error message to contain HTTP status code '502', got: %v", err) + } + + // Test GetTestResult + _, err = client.GetTestResult("some-id") + if err == nil { + t.Error("expected GetTestResult to return error on Bad Gateway response, got nil") + } else if !strings.Contains(err.Error(), "502") { + t.Errorf("expected error message to contain HTTP status code '502', got: %v", err) + } + + // Test DownloadArtifact + _, err = client.DownloadArtifact("https://example.com/openapi.yaml", true, "") + if err == nil { + t.Error("expected DownloadArtifact to return error on Bad Gateway response, got nil") + } +} +