func (t *tokenAuthenticator) CanAuthenticate(host string) error {
	client := httpclient.New()
	return testAuth(t, client, host, false)
}
CanAuthenticate makes a test request to CM 1.5 and returns nil if it is successful.
package rsapi
import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"time"
	"github.com/rightscale/rsc/httpclient"
)
type Authenticator interface {
	Sign(req *http.Request) error
	SetHost(host string)
	CanAuthenticate(host string) error
}
func NewBasicAuthenticator(username, password string, accountID int) Authenticator {
	builder := basicLoginRequestBuilder{username: username, password: password, accountID: accountID}
	return newCookieSigner(&builder, accountID)
}
func NewInstanceAuthenticator(token string, accountID int) Authenticator {
	builder := instanceLoginRequestBuilder{token: token, accountID: accountID}
	return newCookieSigner(&builder, accountID)
}
func NewOAuthAuthenticator(token string, accountID int, flexeraOne bool) Authenticator {
	return &oAuthSigner{
		refreshToken: token,
		refreshAt:    time.Now().Add(-2 * time.Minute),
		client:       httpclient.New(),
		accountID:    accountID,
		flexeraOne:   flexeraOne,
	}
}
func NewTokenAuthenticator(token string, accountID int) Authenticator {
	return &tokenAuthenticator{token: token, accountID: accountID}
}
func NewSSAuthenticator(auther Authenticator, accountID int) Authenticator {
	if _, ok := auther.(*ssAuthenticator); ok {
		return auther
	}
	return &ssAuthenticator{
		auther:    auther,
		accountID: accountID,
		refreshAt: time.Now().Add(-2 * time.Minute),
		client:    httpclient.NewNoRedirect(),
	}
}
func NewRL10Authenticator(secret string) Authenticator {
	return &rl10Authenticator{secret: secret}
}
type loginRequestBuilder interface {
	BuildLoginRequest(host string) (*http.Request, error)
}
type cookieSigner struct {
	builder   loginRequestBuilder
	accountID int
	cookies   []*http.Cookie
	host      string
	refreshAt time.Time
	client    httpclient.HTTPClient
}
func newCookieSigner(builder loginRequestBuilder, accountID int) Authenticator {
	return &cookieSigner{
		builder:   builder,
		accountID: accountID,
		refreshAt: time.Now().Add(-2 * time.Minute),
		client:    httpclient.NewNoRedirect(),
	}
}
func (s *cookieSigner) Sign(req *http.Request) error {
	if time.Now().After(s.refreshAt) {
		authReq, authErr := s.builder.BuildLoginRequest(s.host)
		if authErr != nil {
			return authErr
		}
		resp, err := s.client.DoHidden(authReq)
		if err != nil {
			return err
		}
		url, err := extractRedirectURL(resp)
		if err != nil {
			return err
		}
		if url != nil {
			authReq, authErr = s.builder.BuildLoginRequest(url.Host)
			if authErr != nil {
				return authErr
			}
			s.host = url.Host
			req.Host = url.Host
			req.URL.Host = url.Host
			resp, err = s.client.DoHidden(authReq)
		}
		if err != nil {
			return fmt.Errorf("Authentication failed: %s", err)
		}
		if err := s.refresh(resp); err != nil {
			return err
		}
	}
	for _, c := range s.cookies {
		req.AddCookie(c)
	}
	req.Header.Set("X-Account", strconv.Itoa(s.accountID))
	return nil
}
func (s *cookieSigner) SetHost(host string) {
	s.host = host
}
func (s *cookieSigner) CanAuthenticate(host string) error {
	_, instance := s.builder.(*instanceLoginRequestBuilder)
	return testAuth(s, s.client, host, instance)
}
func (s *cookieSigner) refresh(resp *http.Response) error {
	if resp.StatusCode != 204 {
		return fmt.Errorf("Authentication failed: %s", resp.Status)
	}
	s.cookies = resp.Cookies()
	s.refreshAt = time.Now().Add(time.Duration(2) * time.Hour)
	return nil
}
type oAuthSigner struct {
	refreshToken string
	accessToken  string
	host         string
	refreshAt    time.Time
	client       httpclient.HTTPClient
	accountID    int  
	flexeraOne   bool
}
func (s *oAuthSigner) Sign(req *http.Request) error {
	if time.Now().After(s.refreshAt) {
		jsonStr := fmt.Sprintf(`{"grant_type":"refresh_token","refresh_token":"%s"}`, s.refreshToken)
		host := s.host
		url := "api/oauth2"
		if s.flexeraOne {
			host = "login.flexera.com"
			url = "oidc/token"
		}
		authReq, err := http.NewRequest("POST", buildURL(host, url),
			bytes.NewBufferString(jsonStr))
		if err != nil {
			return fmt.Errorf("Authentication failed (failed to build request): %s", err)
		}
		authReq.Header.Set("X-API-Version", "1.5")
		authReq.Header.Set("Content-Type", "application/json")
		resp, err := s.client.DoHidden(authReq)
		if err != nil {
			return fmt.Errorf("Authentication failed: %s", err)  
		}
		defer resp.Body.Close()
		var session map[string]interface{}
		jsonBytes, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return fmt.Errorf("Authentication failed (failed to read response): %s", err)
		}
		if resp.StatusCode != 200 {
			body, err := ioutil.ReadAll(resp.Body)
			var msg string
			if err != nil {
				msg = " - <failed to read body>"
			}
			if len(body) > 0 {
				msg = " - " + string(body)
			}
			return fmt.Errorf("Authentication failed: %s%s", resp.Status, msg)
		}
		err = json.Unmarshal(jsonBytes, &session)
		if err != nil {
			return fmt.Errorf("Authentication failed (failed to load response JSON): %s", err)
		}
		accessToken, ok := session["access_token"].(string)
		if !ok {
			return fmt.Errorf("Unexpected auth response: %s", jsonBytes)
		}
		s.accessToken = accessToken
		d, err := time.ParseDuration(fmt.Sprintf("%vs", session["expires_in"]))
		if err != nil {
			return fmt.Errorf("Authentication failed (failed to parse token duration): %s", err)
		}
		s.refreshAt = time.Now().Add(d / 2)
	}
	req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", s.accessToken))
	if s.accountID != 0 {
		req.Header.Set("X-Account", strconv.Itoa(s.accountID))
	}
	return nil
}
func (s *oAuthSigner) SetHost(host string) {
	s.host = host
}
func (s *oAuthSigner) CanAuthenticate(host string) error {
	return testAuth(s, s.client, host, false)
}
type tokenAuthenticator struct {
	token     string
	host      string  
	accountID int     
}
func (t *tokenAuthenticator) Sign(r *http.Request) error {
	r.Header.Set("Authorization", "Bearer "+t.token)
	if t.accountID != 0 {
		r.Header.Set("X-Account", strconv.Itoa(t.accountID))
	}
	return nil
}
func (t *tokenAuthenticator) SetHost(h string) {
	t.host = h
}