// Package coreAPI Provides methods and types for interacting with the external coreapi located at https://coreapi.nsi.rackspace.com/ package coreAPI import ( "encoding/json" "errors" "fmt" "net/http" "strings" ) const ( // URLBase defines the base url used to reach the coreapi Rest service URLBase = "https://coreapi.nsi.rackspace.com" // URLStaging defines the staging url used to reach the coreapi Rest service URLStaging = "https://staging.nsi.rackspace.com" ) type ( // Client structure holds information required for interacting with the CoreAPI Client struct { StatusCode int // For tracking status code response from coreAPI Token *string SessionInfo SessionInfo } // SessionInfo contains data about the current session SessionInfo struct { ContactID int `json:"contact_id"` EmployeeNum int `json:"employee_number"` Username string `json:"username"` Valid bool `json:"valid"` Departments []string `json:"departments"` Error string `json:"error,omitempty"` } ) // New returns a new CoreAPI with sessionInfo if the token is valid, otherwise it return an error. // TODO: When coreAPI supports identity tokens for init, we can consolidate the New/ServiceToken/Identity funcs func New(token string) (client *Client, err error) { if token == "" { return nil, errors.New("coreAPI.New(): token cannot be empty") } client = new(Client) client.Token = &token res, err := http.Get(URLBase + "/core/session/" + *client.Token) if res != nil { defer func() { if defErr := res.Body.Close(); defErr != nil { err = fmt.Errorf("%s:%s", err, defErr) } }() } if err != nil { return nil, err } client.StatusCode = res.StatusCode if err := json.NewDecoder(res.Body).Decode(&client.SessionInfo); err != nil { return nil, err } if res.StatusCode != 200 { return client, fmt.Errorf("coreAPI: %s", client.SessionInfo.Error) } if !client.SessionInfo.Valid { client.StatusCode = http.StatusForbidden return client, errors.New("coreAPI: Invalid Token") } return } // ServiceToken will get the cached service token for netsec01. Using this prevents there from being multiple tokens // for netsec01 active at any given time. func ServiceToken(key string) (*Client, error) { if key == "" { return nil, errors.New("coreAPI.ServiceToken:key missing") } req, err := http.NewRequest(http.MethodGet, URLBase+"/core/serviceToken", nil) if err != nil { return nil, err } req.Header.Set("apikey", key) res, err := http.DefaultClient.Do(req) if res != nil { defer func() { if defErr := res.Body.Close(); defErr != nil { err = fmt.Errorf("%s:%s", err, defErr) } }() } if err != nil { return nil, err } if res.StatusCode != 200 { return nil, fmt.Errorf("coreAPI.ServiceToken:%d - %s", res.StatusCode, res.Status) } auth := struct { Authtoken string `json:"authtoken"` }{} if err := json.NewDecoder(res.Body).Decode(&auth); err != nil { return nil, fmt.Errorf("coreAPI.ServiceToken: %s", err) } return New(auth.Authtoken) } // IdentityServiceToken returns the cached token for the netsec01 user from coreAPI func IdentityServiceToken(key string) (string, error) { req, err := http.NewRequest(http.MethodGet, URLBase+"/identity/serviceToken", nil) if err != nil { return "", err } req.Header.Set("apikey", key) resp, err := http.DefaultClient.Do(req) if err != nil { if strings.Contains(err.Error(), "timed out") { return "", errors.New("coreAPI.IdentityServiceToken:Timeout connecting to coreapi") } return "", err } defer func() { _ = resp.Body.Close() }() if resp.StatusCode != 200 { return "", fmt.Errorf("coreAPI.IdentityServiceToken:%s", resp.Status) } token := struct { Authtoken string `json:"authtoken"` }{} if err := json.NewDecoder(resp.Body).Decode(&token); err != nil { return "", fmt.Errorf("coreAPI.IdentityServiceToken:DecodeError:%s", err) } return token.Authtoken, nil } func (c *Client) reqHeaders(req *http.Request) { for k, v := range map[string]string{ "X-Auth": *c.Token, "Content-Type": "application/json", "Accept": "application/json", "Connection": "close", } { req.Header.Set(k, v) } }