diff --git a/deploy.go b/deploy.go index 4e9d182..e488b34 100644 --- a/deploy.go +++ b/deploy.go @@ -9,15 +9,20 @@ import ( "errors" "fmt" "io" + "io/ioutil" "mime/multipart" "net/http" "net/url" "os" path "path/filepath" + "regexp" "strconv" "strings" + "github.com/dell/goscaleio/api" types "github.com/dell/goscaleio/types/v1" + log "github.com/sirupsen/logrus" + "gopkg.in/yaml.v3" ) var ( @@ -28,14 +33,16 @@ var ( // GatewayClient is client for Gateway server type GatewayClient struct { http *http.Client + api api.Client host string username string password string + token string + version string } // NewGateway returns a new gateway client. -func NewGateway( - host string, username, password string, insecure, useCerts bool) (*GatewayClient, error) { +func NewGateway(host string, username, password string, insecure, useCerts bool) (*GatewayClient, error) { if host == "" { return nil, errNewClient @@ -72,9 +79,115 @@ func NewGateway( } } + version, err := gc.GetVersion() + if err != nil { + return nil, err + } + + if version == "3.5" { + gc.version = version + //No need to create token + } else { + bodyData := map[string]interface{}{ + "username": username, + "password": password, + } + + body, _ := json.Marshal(bodyData) + + req, err := http.NewRequest("POST", host+"/rest/auth/login", bytes.NewBuffer(body)) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", "application/json") + + resp, err := gc.http.Do(req) + if err != nil { + return nil, err + } + + defer func() { + if err := resp.Body.Close(); err != nil { + doLog(log.WithError(err).Error, "") + } + }() + + // parse the response + switch { + case resp == nil: + return nil, errNilReponse + case !(resp.StatusCode >= 200 && resp.StatusCode <= 299): + return nil, gc.api.ParseJSONError(resp) + } + + bs, err := io.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + responseBody := string(bs) + + result := make(map[string]interface{}) + jsonErr := json.Unmarshal([]byte(responseBody), &result) + if err != nil { + return nil, fmt.Errorf("Error For Uploading Package: %s", jsonErr) + } + + token := result["access_token"].(string) + + gc.token = token + + version, err = gc.GetVersion() + if err != nil { + return nil, err + } + gc.version = version + } + return gc, nil } +// GetVersion returns version +func (gc *GatewayClient) GetVersion() (string, error) { + + req, httpError := http.NewRequest("GET", gc.host+"/api/version", nil) + if httpError != nil { + return "", httpError + } + + if gc.token != "" { + req.Header.Set("Authorization", "Bearer "+gc.token) + } + + req.Header.Set("Content-Type", "application/json") + + client := gc.http + resp, httpRespError := client.Do(req) + if httpRespError != nil { + return "", httpRespError + } + + // parse the response + switch { + case resp == nil: + return "", errNilReponse + case !(resp.StatusCode >= 200 && resp.StatusCode <= 299): + return "", nil + } + + version, err := extractString(resp) + if err != nil { + return "", err + } + + versionRX := regexp.MustCompile(`^(\d+?\.\d+?).*$`) + if m := versionRX.FindStringSubmatch(version); len(m) > 0 { + return m[1], nil + } + return version, nil +} + // UploadPackages used for upload packge to gateway server func (gc *GatewayClient) UploadPackages(filePaths []string) (*types.GatewayResponse, error) { var gatewayResponse types.GatewayResponse @@ -120,7 +233,13 @@ func (gc *GatewayClient) UploadPackages(filePaths []string) (*types.GatewayRespo return &gatewayResponse, httpError } req.Header.Set("Content-Type", writer.FormDataContentType()) - req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(gc.username+":"+gc.password))) + if gc.version == "4.0" { + req.Header.Set("Authorization", "Bearer "+gc.token) + + setCookie(req.Header, gc.host) + } else { + req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(gc.username+":"+gc.password))) + } client := gc.http response, httpRespError := client.Do(req) @@ -182,7 +301,13 @@ func (gc *GatewayClient) ParseCSV(filePath string) (*types.GatewayResponse, erro return &gatewayResponse, httpError } req.Header.Set("Content-Type", writer.FormDataContentType()) - req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(gc.username+":"+gc.password))) + if gc.version == "4.0" { + req.Header.Set("Authorization", "Bearer "+gc.token) + + setCookie(req.Header, gc.host) + } else { + req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(gc.username+":"+gc.password))) + } client := gc.http response, httpRespError := client.Do(req) @@ -234,7 +359,16 @@ func (gc *GatewayClient) GetPackageDetails() ([]*types.PackageDetails, error) { if httpError != nil { return packageParam, httpError } - req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(gc.username+":"+gc.password))) + + if gc.version == "4.0" { + req.Header.Set("Authorization", "Bearer "+gc.token) + + setCookie(req.Header, gc.host) + + } else { + req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(gc.username+":"+gc.password))) + } + req.Header.Set("Content-Type", "application/json") client := gc.http @@ -250,6 +384,10 @@ func (gc *GatewayClient) GetPackageDetails() ([]*types.PackageDetails, error) { if httpResp.StatusCode == 200 { + if gc.version == "4.0" { + storeCookie(httpResp.Header, gc.host) + } + err := json.Unmarshal([]byte(responseString), &packageParam) if err != nil { @@ -270,7 +408,13 @@ func (gc *GatewayClient) ValidateMDMDetails(mdmTopologyParam []byte) (*types.Gat if httpError != nil { return &gatewayResponse, httpError } - req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(gc.username+":"+gc.password))) + if gc.version == "4.0" { + req.Header.Set("Authorization", "Bearer "+gc.token) + + setCookie(req.Header, gc.host) + } else { + req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(gc.username+":"+gc.password))) + } req.Header.Set("Content-Type", "application/json") client := gc.http @@ -293,6 +437,14 @@ func (gc *GatewayClient) ValidateMDMDetails(mdmTopologyParam []byte) (*types.Gat } return &gatewayResponse, nil + } else if httpResp.StatusCode == 200 && responseString == "" { + gatewayResponse.Message = "Wrong Primary MDM IP, Please provide valid Primary MDM IP" + + return &gatewayResponse, fmt.Errorf("Wrong Primary MDM IP, Please provide valid Primary MDM IP") + } + + if gc.version == "4.0" { + storeCookie(httpResp.Header, gc.host) } var mdmTopologyDetails types.MDMTopologyDetails @@ -318,7 +470,13 @@ func (gc *GatewayClient) GetClusterDetails(mdmTopologyParam []byte, requireJSONO if httpError != nil { return &gatewayResponse, httpError } - req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(gc.username+":"+gc.password))) + if gc.version == "4.0" { + req.Header.Set("Authorization", "Bearer "+gc.token) + + setCookie(req.Header, gc.host) + } else { + req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(gc.username+":"+gc.password))) + } req.Header.Set("Content-Type", "application/json") client := gc.http @@ -347,6 +505,10 @@ func (gc *GatewayClient) GetClusterDetails(mdmTopologyParam []byte, requireJSONO return &gatewayResponse, fmt.Errorf("Error Getting Cluster Details") } + if gc.version == "4.0" { + storeCookie(httpResp.Header, gc.host) + } + if requireJSONOutput { gatewayResponse.StatusCode = 200 @@ -379,7 +541,13 @@ func (gc *GatewayClient) DeletePackage(packageName string) (*types.GatewayRespon if httpError != nil { return &gatewayResponse, httpError } - req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(gc.username+":"+gc.password))) + if gc.version == "4.0" { + req.Header.Set("Authorization", "Bearer "+gc.token) + + setCookie(req.Header, gc.host) + } else { + req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(gc.username+":"+gc.password))) + } req.Header.Set("Content-Type", "application/json") client := gc.http @@ -404,6 +572,10 @@ func (gc *GatewayClient) DeletePackage(packageName string) (*types.GatewayRespon return &gatewayResponse, nil } + if gc.version == "4.0" { + storeCookie(httpResp.Header, gc.host) + } + gatewayResponse.StatusCode = 200 return &gatewayResponse, nil @@ -435,11 +607,16 @@ func (gc *GatewayClient) BeginInstallation(jsonStr, mdmUsername, mdmPassword, li u, _ := url.Parse(gc.host + "/im/types/Configuration/actions/install") q := u.Query() - q.Set("noUpload", "false") - q.Set("noInstall", "false") - q.Set("noConfigure", "false") - q.Set("noLinuxDevValidation", "false") - q.Set("globalZeroPadPolicy", "false") + + if gc.version == "4.0" { + q.Set("noSecurityBootstrap", "false") + } else { + q.Set("noUpload", "false") + q.Set("noInstall", "false") + q.Set("noConfigure", "false") + q.Set("noLinuxDevValidation", "false") + q.Set("globalZeroPadPolicy", "false") + } if expansion { q.Set("extend", strconv.FormatBool(expansion)) @@ -451,7 +628,13 @@ func (gc *GatewayClient) BeginInstallation(jsonStr, mdmUsername, mdmPassword, li if httpError != nil { return &gatewayResponse, httpError } - req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(gc.username+":"+gc.password))) + if gc.version == "4.0" { + req.Header.Set("Authorization", "Bearer "+gc.token) + + setCookie(req.Header, gc.host) + } else { + req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(gc.username+":"+gc.password))) + } req.Header.Set("Content-Type", "application/json") client := gc.http @@ -491,7 +674,13 @@ func (gc *GatewayClient) MoveToNextPhase() (*types.GatewayResponse, error) { if httpError != nil { return &gatewayResponse, httpError } - req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(gc.username+":"+gc.password))) + if gc.version == "4.0" { + req.Header.Set("Authorization", "Bearer "+gc.token) + + setCookie(req.Header, gc.host) + } else { + req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(gc.username+":"+gc.password))) + } req.Header.Set("Content-Type", "application/json") client := gc.http @@ -517,6 +706,10 @@ func (gc *GatewayClient) MoveToNextPhase() (*types.GatewayResponse, error) { return &gatewayResponse, nil } + if gc.version == "4.0" { + storeCookie(httpResp.Header, gc.host) + } + gatewayResponse.StatusCode = 200 return &gatewayResponse, nil @@ -531,7 +724,13 @@ func (gc *GatewayClient) RetryPhase() (*types.GatewayResponse, error) { if httpError != nil { return &gatewayResponse, httpError } - req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(gc.username+":"+gc.password))) + if gc.version == "4.0" { + req.Header.Set("Authorization", "Bearer "+gc.token) + + setCookie(req.Header, gc.host) + } else { + req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(gc.username+":"+gc.password))) + } req.Header.Set("Content-Type", "application/json") client := gc.http @@ -557,6 +756,10 @@ func (gc *GatewayClient) RetryPhase() (*types.GatewayResponse, error) { return &gatewayResponse, nil } + if gc.version == "4.0" { + storeCookie(httpResp.Header, gc.host) + } + gatewayResponse.StatusCode = 200 return &gatewayResponse, nil @@ -571,7 +774,13 @@ func (gc *GatewayClient) AbortOperation() (*types.GatewayResponse, error) { if httpError != nil { return &gatewayResponse, httpError } - req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(gc.username+":"+gc.password))) + if gc.version == "4.0" { + req.Header.Set("Authorization", "Bearer "+gc.token) + + setCookie(req.Header, gc.host) + } else { + req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(gc.username+":"+gc.password))) + } req.Header.Set("Content-Type", "application/json") client := gc.http @@ -597,6 +806,10 @@ func (gc *GatewayClient) AbortOperation() (*types.GatewayResponse, error) { return &gatewayResponse, nil } + if gc.version == "4.0" { + storeCookie(httpResp.Header, gc.host) + } + gatewayResponse.StatusCode = 200 return &gatewayResponse, nil @@ -611,7 +824,13 @@ func (gc *GatewayClient) ClearQueueCommand() (*types.GatewayResponse, error) { if httpError != nil { return &gatewayResponse, httpError } - req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(gc.username+":"+gc.password))) + if gc.version == "4.0" { + req.Header.Set("Authorization", "Bearer "+gc.token) + + setCookie(req.Header, gc.host) + } else { + req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(gc.username+":"+gc.password))) + } req.Header.Set("Content-Type", "application/json") client := gc.http @@ -637,6 +856,10 @@ func (gc *GatewayClient) ClearQueueCommand() (*types.GatewayResponse, error) { return &gatewayResponse, nil } + if gc.version == "4.0" { + storeCookie(httpResp.Header, gc.host) + } + gatewayResponse.StatusCode = 200 return &gatewayResponse, nil @@ -651,7 +874,13 @@ func (gc *GatewayClient) MoveToIdlePhase() (*types.GatewayResponse, error) { if httpError != nil { return &gatewayResponse, httpError } - req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(gc.username+":"+gc.password))) + if gc.version == "4.0" { + req.Header.Set("Authorization", "Bearer "+gc.token) + + setCookie(req.Header, gc.host) + } else { + req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(gc.username+":"+gc.password))) + } req.Header.Set("Content-Type", "application/json") client := gc.http @@ -677,6 +906,10 @@ func (gc *GatewayClient) MoveToIdlePhase() (*types.GatewayResponse, error) { return &gatewayResponse, nil } + if gc.version == "4.0" { + storeCookie(httpResp.Header, gc.host) + } + gatewayResponse.StatusCode = 200 return &gatewayResponse, nil @@ -691,7 +924,13 @@ func (gc *GatewayClient) GetInQueueCommand() ([]types.MDMQueueCommandDetails, er if httpError != nil { return mdmQueueCommandDetails, httpError } - req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(gc.username+":"+gc.password))) + if gc.version == "4.0" { + req.Header.Set("Authorization", "Bearer "+gc.token) + + setCookie(req.Header, gc.host) + } else { + req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(gc.username+":"+gc.password))) + } req.Header.Set("Content-Type", "application/json") client := gc.http @@ -707,6 +946,10 @@ func (gc *GatewayClient) GetInQueueCommand() ([]types.MDMQueueCommandDetails, er if httpResp.StatusCode == 200 { + if gc.version == "4.0" { + storeCookie(httpResp.Header, gc.host) + } + var queueCommandDetails map[string][]interface{} err := json.Unmarshal([]byte(responseString), &queueCommandDetails) @@ -801,7 +1044,13 @@ func (gc *GatewayClient) UninstallCluster(jsonStr, mdmUsername, mdmPassword, lia if httpError != nil { return &gatewayResponse, httpError } - req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(gc.username+":"+gc.password))) + if gc.version == "4.0" { + req.Header.Set("Authorization", "Bearer "+gc.token) + + setCookie(req.Header, gc.host) + } else { + req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(gc.username+":"+gc.password))) + } req.Header.Set("Content-Type", "application/json") client := gc.http @@ -841,3 +1090,113 @@ func jsonToMap(jsonStr string) (map[string]interface{}, error) { } return result, nil } + +const configFile = "/home/.cookie_config.yaml" + +var globalCookie string + +// CookieConfig represents the YAML structure +type CookieConfig struct { + Hosts []Host `yaml:"hosts"` +} + +// Host represents individual hosts in the YAML structure +type Host struct { + Name string `yaml:"name"` + LegacyGWCookie string `yaml:"cookie"` +} + +func storeCookie(header http.Header, host string) error { + if header != nil && header["Set-Cookie"] != nil { + + newCookie := strings.Split(header["Set-Cookie"][0], ";")[0] + sanitizedCookie := strings.ReplaceAll(strings.Split(newCookie, "=")[1], "|", "_") + + // Load existing configuration + config, err := loadConfig() + if err != nil { + return err + } + + // Check if the host already exists, and update or add accordingly + found := false + for i, h := range config.Hosts { + if h.Name == host { + config.Hosts[i].LegacyGWCookie = sanitizedCookie + found = true + break + } + } + + // If the host is not found, add a new host + if !found { + config.Hosts = append(config.Hosts, Host{Name: host, LegacyGWCookie: sanitizedCookie}) + } + + // Update the global variable directly + globalCookie = sanitizedCookie + + err = writeConfig(config) + if err != nil { + return err + } + } + + return nil +} + +func setCookie(header http.Header, host string) error { + + if globalCookie != "" { + header.Set("Cookie", "LEGACYGWCOOKIE="+strings.ReplaceAll(globalCookie, "_", "|")) + } else { + config, err := loadConfig() + if err != nil { + return err + } + + // Check if the host already exists and set the globalCookie + for _, h := range config.Hosts { + if h.Name == host { + globalCookie = h.LegacyGWCookie + header.Set("Cookie", "LEGACYGWCOOKIE="+strings.ReplaceAll(globalCookie, "_", "|")) + break + } + } + } + + return nil +} + +func loadConfig() (*CookieConfig, error) { + if _, err := os.Stat(configFile); err == nil { + data, err := ioutil.ReadFile(configFile) + if err != nil { + return nil, err + } + + var config CookieConfig + err = yaml.Unmarshal(data, &config) + if err != nil { + return nil, err + } + + return &config, nil + } + + return &CookieConfig{}, nil +} + +func writeConfig(config *CookieConfig) error { + data, err := yaml.Marshal(&config) + if err != nil { + return err + } + + err = ioutil.WriteFile(configFile, data, 0644) + if err != nil { + return err + } + + return nil +}