Skip to content
Open
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
58 changes: 41 additions & 17 deletions pkg/connectors/keycloak_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"strings"

"github.com/microcks/microcks-cli/pkg/config"
"github.com/microcks/microcks-cli/pkg/errors"
"golang.org/x/oauth2"
)

Expand All @@ -50,6 +51,8 @@ func NewKeycloakClient(realmURL string, username string, password string) Keyclo

u, err := url.Parse(realmURL)
if err != nil {
// url.Parse only fails on a malformed URL; returning it needs a
// signature change, done with the RunE command migration.
panic(err)
}
kc.BaseURL = u
Expand Down Expand Up @@ -88,7 +91,7 @@ func (c *keycloakClient) ConnectAndGetToken() (string, error) {

resp, err := c.httpClient.Do(req)
if err != nil {
return "", err
return "", errors.Wrap(errors.KindConnection, err)
}
defer resp.Body.Close()

Expand All @@ -97,16 +100,23 @@ func (c *keycloakClient) ConnectAndGetToken() (string, error) {

body, err := io.ReadAll(resp.Body)
if err != nil {
panic(err.Error())
return "", errors.Wrap(errors.KindConnection, fmt.Errorf("reading Keycloak token response: %w", err))
}

if resp.StatusCode != http.StatusOK {
return "", errors.Wrapf(errors.KindAPI, "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 "", errors.Wrap(errors.KindAPI, fmt.Errorf("parsing Keycloak token response: %w", err))
}

accessToken := openIDResp["access_token"].(string)
return accessToken, err
accessToken, ok := openIDResp["access_token"].(string)
if !ok || accessToken == "" {
return "", errors.Wrapf(errors.KindAPI, "Keycloak token response missing access_token")
}
return accessToken, nil
}

func (c *keycloakClient) GetOIDCConfig() (*oauth2.Config, error) {
Expand All @@ -116,27 +126,34 @@ func (c *keycloakClient) GetOIDCConfig() (*oauth2.Config, error) {
// Create HTTP request
req, err := http.NewRequest("GET", u.String(), nil)
if err != nil {
fmt.Println("Error creating request:", err)
return nil, errors.Wrap(errors.KindGeneric, fmt.Errorf("creating Keycloak OIDC request: %w", err))
}

resp, err := c.httpClient.Do(req)
if err != nil {
return nil, err
return nil, errors.Wrap(errors.KindConnection, err)
}
defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)
if err != nil {
panic(err.Error())
return nil, errors.Wrap(errors.KindConnection, fmt.Errorf("reading Keycloak OIDC config: %w", err))
}

if resp.StatusCode != http.StatusOK {
return nil, errors.Wrapf(errors.KindAPI, "Keycloak returned HTTP %d for OIDC config: %s", resp.StatusCode, strings.TrimSpace(string(body)))
}

var openIDResp map[string]interface{}
if err := json.Unmarshal(body, &openIDResp); err != nil {
panic(err)
return nil, errors.Wrap(errors.KindAPI, fmt.Errorf("parsing Keycloak OIDC config: %w", err))
}

authURL := openIDResp["authorization_endpoint"].(string)
tokenURL := openIDResp["token_endpoint"].(string)
authURL, _ := openIDResp["authorization_endpoint"].(string)
tokenURL, _ := openIDResp["token_endpoint"].(string)
if authURL == "" || tokenURL == "" {
return nil, errors.Wrapf(errors.KindAPI, "Keycloak OIDC config missing authorization_endpoint or token_endpoint")
}

return &oauth2.Config{
Endpoint: oauth2.Endpoint{
Expand All @@ -160,30 +177,37 @@ func (c *keycloakClient) ConnectAndGetTokenAndRefreshToken(username, password st
// Create HTTP request
req, err := http.NewRequest("POST", u.String(), bytes.NewBufferString(data.Encode()))
if err != nil {
fmt.Println("Error creating request:", err)
return "", "", errors.Wrap(errors.KindGeneric, fmt.Errorf("creating Keycloak token request: %w", err))
}

// Set headers
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

resp, err := c.httpClient.Do(req)
if err != nil {
return "", "", err
return "", "", errors.Wrap(errors.KindConnection, err)
}
defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)
if err != nil {
panic(err.Error())
return "", "", errors.Wrap(errors.KindConnection, fmt.Errorf("reading Keycloak token response: %w", err))
}

if resp.StatusCode != http.StatusOK {
return "", "", errors.Wrapf(errors.KindAPI, "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 "", "", errors.Wrap(errors.KindAPI, fmt.Errorf("parsing Keycloak token response: %w", err))
}

authToken := openIDResp["access_token"].(string)
refreshToken := openIDResp["refresh_token"].(string)
authToken, _ := openIDResp["access_token"].(string)
refreshToken, _ := openIDResp["refresh_token"].(string)
if authToken == "" || refreshToken == "" {
return "", "", errors.Wrapf(errors.KindAPI, "Keycloak token response missing access_token or refresh_token")
}

return authToken, refreshToken, nil
}
67 changes: 41 additions & 26 deletions pkg/connectors/microcks_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ func NewClient(opts ClientOptions) (MicrocksClient, error) {

u, err := url.Parse(apiURL)
if err != nil {
panic(err)
return nil, errors.Wrap(errors.KindUsage, fmt.Errorf("invalid server URL %q: %w", apiURL, err))
}
c.APIURL = u

Expand Down Expand Up @@ -194,6 +194,8 @@ func NewMicrocksClient(apiURL string) MicrocksClient {

u, err := url.Parse(apiURL)
if err != nil {
// url.Parse only fails on a malformed URL; returning it needs a
// signature change, done with the RunE command migration.
panic(err)
}
mc.APIURL = u
Expand Down Expand Up @@ -230,7 +232,7 @@ func (c *microcksClient) GetKeycloakURL() (string, error) {

resp, err := c.httpClient.Do(req)
if err != nil {
return "", err
return "", errors.Wrap(errors.KindConnection, err)
}
defer resp.Body.Close()

Expand All @@ -239,24 +241,29 @@ func (c *microcksClient) GetKeycloakURL() (string, error) {

body, err := io.ReadAll(resp.Body)
if err != nil {
panic(err.Error())
return "", errors.Wrap(errors.KindConnection, fmt.Errorf("reading Keycloak config response: %w", err))
}

if resp.StatusCode != http.StatusOK {
return "", errors.Wrapf(errors.KindAPI, "Microcks returned HTTP %d for Keycloak config: %s", resp.StatusCode, strings.TrimSpace(string(body)))
}

var configResp map[string]interface{}
if err := json.Unmarshal(body, &configResp); err != nil {
panic(err)
return "", errors.Wrap(errors.KindAPI, fmt.Errorf("parsing Keycloak config response: %w", err))
}

// Retrieve auth server url and realm name.
enabled := configResp["enabled"].(bool)
authServerURL := configResp["auth-server-url"].(string)
realmName := configResp["realm"].(string)
// Return 'null' if Keycloak is disabled.
if enabled, _ := configResp["enabled"].(bool); !enabled {
return "null", nil
}

// Return a proper URL or 'null' if Keycloak is disables.
if enabled {
return authServerURL + "/realms/" + realmName + "/", nil
authServerURL, _ := configResp["auth-server-url"].(string)
realmName, _ := configResp["realm"].(string)
if authServerURL == "" || realmName == "" {
return "", errors.Wrapf(errors.KindAPI, "Keycloak config response missing auth-server-url or realm")
}
return "null", nil
return authServerURL + "/realms/" + realmName + "/", nil
}

func (c *microcksClient) refreshAuthToken(localCfg *config.LocalConfig, ctxName, configPath string) error {
Expand Down Expand Up @@ -304,10 +311,14 @@ func (c *microcksClient) refreshAuthToken(localCfg *config.LocalConfig, ctxName,

func (c *microcksClient) redeemRefreshToken(auth config.Auth) (string, string, error) {
keyCloakUrl, err := c.GetKeycloakURL()
errors.CheckError(err)
if err != nil {
return "", "", err
}
kc := NewKeycloakClient(keyCloakUrl, "", "")
oauth2Conf, err := kc.GetOIDCConfig()
errors.CheckError(err)
if err != nil {
return "", "", err
}
oauth2Conf.ClientID = auth.ClientId
oauth2Conf.ClientSecret = auth.ClientSecret

Expand Down Expand Up @@ -372,18 +383,22 @@ func (c *microcksClient) CreateTestResult(serviceID string, testEndpoint string,

resp, err := c.httpClient.Do(req)
if err != nil {
return "", err
return "", errors.Wrap(errors.KindConnection, err)
}
defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("failed to read response body: %w", err)
return "", errors.Wrap(errors.KindConnection, fmt.Errorf("reading test creation response: %w", err))
}

// Check HTTP status before attempting to parse.
if resp.StatusCode != 201 {
return "", fmt.Errorf("microcks returned HTTP %d: %s (is the service '%s' registered?)", resp.StatusCode, strings.TrimSpace(string(body)), serviceID)
kind := errors.KindAPI
if resp.StatusCode == http.StatusNotFound {
kind = errors.KindNotFound
}
return "", errors.Wrapf(kind, "Microcks returned HTTP %d: %s (is the service '%s' registered?)", resp.StatusCode, strings.TrimSpace(string(body)), serviceID)
}

var createTestResp map[string]interface{}
Expand Down Expand Up @@ -416,7 +431,7 @@ func (c *microcksClient) GetTestResult(testResultID string) (*TestResultSummary,

resp, err := c.httpClient.Do(req)
if err != nil {
return nil, err
return nil, errors.Wrap(errors.KindConnection, err)
}
defer resp.Body.Close()

Expand All @@ -425,7 +440,7 @@ func (c *microcksClient) GetTestResult(testResultID string) (*TestResultSummary,

body, err := io.ReadAll(resp.Body)
if err != nil {
panic(err.Error())
return nil, errors.Wrap(errors.KindConnection, fmt.Errorf("reading test result response: %w", err))
}

result := TestResultSummary{}
Expand All @@ -440,7 +455,7 @@ func (c *microcksClient) UploadArtifact(specificationFilePath string, mainArtifa
// Ensure file exists on fs.
file, err := os.Open(specificationFilePath)
if err != nil {
return "", err
return "", errors.Wrap(errors.KindUsage, fmt.Errorf("cannot read artifact %q: %w", specificationFilePath, err))
}
defer file.Close()

Expand Down Expand Up @@ -490,7 +505,7 @@ func (c *microcksClient) UploadArtifact(specificationFilePath string, mainArtifa

resp, err := c.httpClient.Do(req)
if err != nil {
return "", err
return "", errors.Wrap(errors.KindConnection, err)
}
defer resp.Body.Close()

Expand All @@ -509,7 +524,7 @@ func (c *microcksClient) UploadArtifact(specificationFilePath string, mainArtifa

// Raise exception if not created.
if resp.StatusCode != 201 {
return "", errs.New(string(respBody))
return "", errors.Wrap(errors.KindAPI, errs.New(strings.TrimSpace(string(respBody))))
}

return string(respBody), nil
Expand Down Expand Up @@ -549,7 +564,7 @@ func (c *microcksClient) DownloadArtifact(artifactURL string, mainArtifact bool,

resp, err := c.httpClient.Do(req)
if err != nil {
return "", err
return "", errors.Wrap(errors.KindConnection, err)
}
defer resp.Body.Close()

Expand All @@ -558,15 +573,15 @@ func (c *microcksClient) DownloadArtifact(artifactURL string, mainArtifact bool,

respBody, err := io.ReadAll(resp.Body)
if err != nil {
panic(err.Error())
return "", errors.Wrap(errors.KindConnection, fmt.Errorf("reading download response: %w", err))
}

// Raise exception if not created.
if resp.StatusCode != 201 {
return "", errs.New(string(respBody))
return "", errors.Wrap(errors.KindAPI, errs.New(strings.TrimSpace(string(respBody))))
}

return string(respBody), err
return string(respBody), nil
}

func ensureValidOperationsList(filteredOperations string) bool {
Expand Down
Loading
Loading