From 28db919ffae43d7ddb948824885aaeab7e259be1 Mon Sep 17 00:00:00 2001 From: kjubybot Date: Fri, 14 Jan 2022 18:04:31 +0300 Subject: [PATCH] Fixed JSON parsing --- decort/controller.go | 160 +++++++++++++++++++++---------------------- 1 file changed, 79 insertions(+), 81 deletions(-) diff --git a/decort/controller.go b/decort/controller.go index d623e78..31e7af0 100644 --- a/decort/controller.go +++ b/decort/controller.go @@ -16,16 +16,15 @@ limitations under the License. */ /* -This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration +This file is part of Terraform (by Hashicorp) provider for Digital Energy Cloud Orchestration Technology platfom. -Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates. +Visit https://github.com/rudecs/terraform-provider-decort for full source code package and updates. */ package decort import ( - "bytes" "crypto/tls" "fmt" @@ -34,6 +33,7 @@ import ( "net/url" "strconv" "strings" + // "time" log "github.com/sirupsen/logrus" @@ -42,42 +42,41 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/helper/schema" // "github.com/hashicorp/terraform-plugin-sdk/terraform" - ) -// enumerated constants that define authentication modes +// enumerated constants that define authentication modes const ( - MODE_UNDEF = iota // this is the invalid mode - it should never be seen - MODE_LEGACY = iota - MODE_OAUTH2 = iota - MODE_JWT = iota + MODE_UNDEF = iota // this is the invalid mode - it should never be seen + MODE_LEGACY = iota + MODE_OAUTH2 = iota + MODE_JWT = iota ) type ControllerCfg struct { - controller_url string // always required - auth_mode_code int // always required - auth_mode_txt string // always required, it is a text representation of auth mode - legacy_user string // required for legacy mode - legacy_password string // required for legacy mode - legacy_sid string // obtained from DECORT controller on successful login in legacy mode - jwt string // obtained from Outh2 provider on successful login in oauth2 mode, required in jwt mode - app_id string // required for oauth2 mode - app_secret string // required for oauth2 mode - oauth2_url string // always required - decort_username string // assigned to either legacy_user (legacy mode) or Oauth2 user (oauth2 mode) upon successful verification - cc_client *http.Client // assigned when all initial checks successfully passed + controller_url string // always required + auth_mode_code int // always required + auth_mode_txt string // always required, it is a text representation of auth mode + legacy_user string // required for legacy mode + legacy_password string // required for legacy mode + legacy_sid string // obtained from DECORT controller on successful login in legacy mode + jwt string // obtained from Outh2 provider on successful login in oauth2 mode, required in jwt mode + app_id string // required for oauth2 mode + app_secret string // required for oauth2 mode + oauth2_url string // always required + decort_username string // assigned to either legacy_user (legacy mode) or Oauth2 user (oauth2 mode) upon successful verification + cc_client *http.Client // assigned when all initial checks successfully passed } func ControllerConfigure(d *schema.ResourceData) (*ControllerCfg, error) { - // This function first will check that all required provider parameters for the + // This function first will check that all required provider parameters for the // selected authenticator mode are set correctly and initialize ControllerCfg structure // based on the provided parameters. // - // Next, it will check for validity of supplied credentials by initiating connection to the specified - // DECORT controller URL and, if succeeded, completes ControllerCfg structure with the rest of computed + // Next, it will check for validity of supplied credentials by initiating connection to the specified + // DECORT controller URL and, if succeeded, completes ControllerCfg structure with the rest of computed // parameters (e.g. JWT, session ID and Oauth2 user name). // - // The structure created by this function should be used with subsequent calls to decortAPICall() method, + // The structure created by this function should be used with subsequent calls to decortAPICall() method, // which is a DECORT authentication mode aware wrapper around standard HTTP requests. ret_config := &ControllerCfg{ @@ -90,7 +89,7 @@ func ControllerConfigure(d *schema.ResourceData) (*ControllerCfg, error) { app_id: d.Get("app_id").(string), app_secret: d.Get("app_secret").(string), oauth2_url: d.Get("oauth2_url").(string), - decort_username: "", + decort_username: "", } var allow_unverified_ssl bool @@ -137,10 +136,10 @@ func ControllerConfigure(d *schema.ResourceData) (*ControllerCfg, error) { if allow_unverified_ssl { log.Warn("ControllerConfigure: allow_unverified_ssl is set - will not check certificates!") - transCfg := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true},} + transCfg := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} ret_config.cc_client = &http.Client{ Transport: transCfg, - Timeout: Timeout180s, + Timeout: Timeout180s, } } else { ret_config.cc_client = &http.Client{ @@ -150,7 +149,7 @@ func ControllerConfigure(d *schema.ResourceData) (*ControllerCfg, error) { switch ret_config.auth_mode_code { case MODE_LEGACY: - ok, err := ret_config.validateLegacyUser() + ok, err := ret_config.validateLegacyUser() if !ok { return nil, err } @@ -162,13 +161,13 @@ func ControllerConfigure(d *schema.ResourceData) (*ControllerCfg, error) { return nil, err } case MODE_OAUTH2: - // on success getOAuth2JWT will set config.jwt to the obtained JWT, so there is no + // on success getOAuth2JWT will set config.jwt to the obtained JWT, so there is no // need to set it once again here _, err := ret_config.getOAuth2JWT() if err != nil { return nil, err } - // we are not verifying the JWT when parsing because actual verification is done on the + // we are not verifying the JWT when parsing because actual verification is done on the // OVC controller side. Here we do parsing solely to extract Oauth2 user name (claim "user") // and JWT issuer name (claim "iss") parser := jwt.Parser{} @@ -181,7 +180,7 @@ func ControllerConfigure(d *schema.ResourceData) (*ControllerCfg, error) { tbuf.WriteString(claims["username"].(string)) tbuf.WriteString("@") tbuf.WriteString(claims["iss"].(string)) - ret_config.decort_username = tbuf.String() + ret_config.decort_username = tbuf.String() } else { return nil, fmt.Errorf("Failed to extract user and iss fields from JWT token in oauth2 mode.") } @@ -195,7 +194,7 @@ func ControllerConfigure(d *schema.ResourceData) (*ControllerCfg, error) { return ret_config, nil } -func (config *ControllerCfg) getDecortUsername() (string) { +func (config *ControllerCfg) getDecortUsername() string { return config.decort_username } @@ -216,14 +215,14 @@ func (config *ControllerCfg) getOAuth2JWT() (string, error) { params.Add("validity", "3600") params_str := params.Encode() - req, err := http.NewRequest("POST", config.oauth2_url + "/v1/oauth/access_token", strings.NewReader(params_str)) + req, err := http.NewRequest("POST", config.oauth2_url+"/v1/oauth/access_token", strings.NewReader(params_str)) if err != nil { return "", err } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("Content-Length", strconv.Itoa(len(params_str))) - resp, err := config.cc_client.Do(req) + resp, err := config.cc_client.Do(req) if err != nil { return "", err } @@ -231,16 +230,16 @@ func (config *ControllerCfg) getOAuth2JWT() (string, error) { // fmt.Println("response Status:", resp.Status) // fmt.Println("response Headers:", resp.Header) // fmt.Println("response Headers:", req.URL) - return "", fmt.Errorf("getOauth2JWT: unexpected status code %d when obtaining JWT from %q for APP_ID %q, request Body %q", - resp.StatusCode, req.URL, config.app_id, params_str) + return "", fmt.Errorf("getOauth2JWT: unexpected status code %d when obtaining JWT from %q for APP_ID %q, request Body %q", + resp.StatusCode, req.URL, config.app_id, params_str) } defer resp.Body.Close() responseData, err := ioutil.ReadAll(resp.Body) - if err != nil { - return "", err - } - + if err != nil { + return "", err + } + // validation successful - store JWT in the corresponding field of the ControllerCfg structure config.jwt = strings.TrimSpace(string(responseData)) @@ -249,10 +248,10 @@ func (config *ControllerCfg) getOAuth2JWT() (string, error) { func (config *ControllerCfg) validateJWT(jwt string) (bool, error) { /* - Validate JWT against DECORT controller. JWT can be supplied as argument to this method. If empty string supplied as - argument, JWT will be taken from config attribute. - DECORT controller URL will always be taken from the config attribute assigned at instantiation. - Validation is accomplished by attempting API call that lists accounts for the invoking user. + Validate JWT against DECORT controller. JWT can be supplied as argument to this method. If empty string supplied as + argument, JWT will be taken from config attribute. + DECORT controller URL will always be taken from the config attribute assigned at instantiation. + Validation is accomplished by attempting API call that lists accounts for the invoking user. */ if jwt == "" { if config.jwt == "" { @@ -265,7 +264,7 @@ func (config *ControllerCfg) validateJWT(jwt string) (bool, error) { return false, fmt.Errorf("validateJWT method called, but no OAuth2 URL provided.") } - req, err := http.NewRequest("POST", config.controller_url + "/restmachine/cloudapi/accounts/list", nil) + req, err := http.NewRequest("POST", config.controller_url+"/restmachine/cloudapi/accounts/list", nil) if err != nil { return false, err } @@ -273,14 +272,14 @@ func (config *ControllerCfg) validateJWT(jwt string) (bool, error) { req.Header.Set("Authorization", fmt.Sprintf("bearer %s", jwt)) // req.Header.Set("Content-Type", "application/x-www-form-urlencoded") // req.Header.Set("Content-Length", strconv.Itoa(0)) - - resp, err := config.cc_client.Do(req) + + resp, err := config.cc_client.Do(req) if err != nil { return false, err } if resp.StatusCode != http.StatusOK { - return false, fmt.Errorf("validateJWT: unexpected status code %d when validating JWT against %q.", - resp.StatusCode, req.URL) + return false, fmt.Errorf("validateJWT: unexpected status code %d when validating JWT against %q.", + resp.StatusCode, req.URL) } defer resp.Body.Close() @@ -289,10 +288,10 @@ func (config *ControllerCfg) validateJWT(jwt string) (bool, error) { func (config *ControllerCfg) validateLegacyUser() (bool, error) { /* - Validate legacy user by obtaining a session key, which will be used for authenticating subsequent API calls - to DECORT controller. - If successful, the session key is stored in config.legacy_sid and true is returned. If unsuccessful for any - reason, the method will return false and error. + Validate legacy user by obtaining a session key, which will be used for authenticating subsequent API calls + to DECORT controller. + If successful, the session key is stored in config.legacy_sid and true is returned. If unsuccessful for any + reason, the method will return false and error. */ if config.auth_mode_code == MODE_UNDEF { return false, fmt.Errorf("validateLegacyUser method called for undefined authorization mode.") @@ -306,7 +305,7 @@ func (config *ControllerCfg) validateLegacyUser() (bool, error) { params.Add("password", config.legacy_password) params_str := params.Encode() - req, err := http.NewRequest("POST", config.controller_url + "/restmachine/cloudapi/users/authenticate", strings.NewReader(params_str)) + req, err := http.NewRequest("POST", config.controller_url+"/restmachine/cloudapi/users/authenticate", strings.NewReader(params_str)) if err != nil { return false, err } @@ -314,21 +313,21 @@ func (config *ControllerCfg) validateLegacyUser() (bool, error) { req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("Content-Length", strconv.Itoa(len(params_str))) - resp, err := config.cc_client.Do(req) + resp, err := config.cc_client.Do(req) if err != nil { return false, err } if resp.StatusCode != http.StatusOK { - return false, fmt.Errorf("validateLegacyUser: unexpected status code %d when validating legacy user %q against %q.", - resp.StatusCode, config.legacy_user, config.controller_url) + return false, fmt.Errorf("validateLegacyUser: unexpected status code %d when validating legacy user %q against %q.", + resp.StatusCode, config.legacy_user, config.controller_url) } defer resp.Body.Close() responseData, err := ioutil.ReadAll(resp.Body) - if err != nil { - log.Fatal(err) - } - + if err != nil { + log.Fatal(err) + } + // validation successful - keep session ID for future use config.legacy_sid = string(responseData) @@ -336,16 +335,16 @@ func (config *ControllerCfg) validateLegacyUser() (bool, error) { } func (config *ControllerCfg) decortAPICall(method string, api_name string, url_values *url.Values) (json_resp string, err error) { - // This is a convenience wrapper around standard HTTP request methods that is aware of the + // This is a convenience wrapper around standard HTTP request methods that is aware of the // authorization mode for which the provider was initialized and compiles request accordingly. if config.cc_client == nil { - // this should never happen if ClientConfig was properly called prior to decortAPICall + // this should never happen if ClientConfig was properly called prior to decortAPICall return "", fmt.Errorf("decortAPICall method called with unconfigured DECORT cloud controller HTTP client.") } // Example: to create api_params, one would generally do the following: - // + // // data := []byte(`{"machineId": "2638"}`) // api_params := bytes.NewBuffer(data)) // @@ -367,42 +366,41 @@ func (config *ControllerCfg) decortAPICall(method string, api_name string, url_v } params_str := url_values.Encode() - req, err := http.NewRequest(method, config.controller_url + api_name, strings.NewReader(params_str)) + req, err := http.NewRequest(method, config.controller_url+api_name, strings.NewReader(params_str)) if err != nil { return "", err } req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("Content-Length", strconv.Itoa(len(params_str))) + req.Header.Set("Accept", "application/json") if config.auth_mode_code == MODE_OAUTH2 || config.auth_mode_code == MODE_JWT { req.Header.Set("Authorization", fmt.Sprintf("bearer %s", config.jwt)) - } - + } + resp, err := config.cc_client.Do(req) if err != nil { return "", err } defer resp.Body.Close() - if resp.StatusCode == http.StatusOK { - tmp_body, err := ioutil.ReadAll(resp.Body) + if resp.StatusCode == http.StatusOK { + body, err := ioutil.ReadAll(resp.Body) if err != nil { return "", err - } - json_resp := Jo2JSON(string(tmp_body)) - log.Debugf("decortAPICall: %s %s\n %s", method, api_name, json_resp) - return json_resp, nil + } + log.Debugf("decortAPICall: %s %s\n %s", method, api_name, body) + return string(body), nil } else { - return "", fmt.Errorf("decortAPICall: unexpected status code %d when calling API %q with request Body %q", - resp.StatusCode, req.URL, params_str) + return "", fmt.Errorf("decortAPICall: unexpected status code %d when calling API %q with request Body %q", + resp.StatusCode, req.URL, params_str) } - + /* - if resp.StatusCode == StatusServiceUnavailable { - return nil, fmt.Errorf("decortAPICall method called for incompatible authorization mode %q.", config.auth_mode_txt) - } + if resp.StatusCode == StatusServiceUnavailable { + return nil, fmt.Errorf("decortAPICall method called for incompatible authorization mode %q.", config.auth_mode_txt) + } */ return "", err } -