cvc/src/loreal.com/dit/cmd/ceh-cs-portal/logic.gateway.upstream.toke...

151 lines
4.1 KiB
Go

package main
import (
"encoding/json"
"log"
"net/http"
"net/url"
"strings"
"sync"
"time"
)
//APIAccount - Upstream API Account
var APIAccount *UpstreamAccount
func (a *App) getAPIAccount() *UpstreamAccount {
if APIAccount != nil {
return APIAccount
}
APIAccount = a.NewUpstreamAccount()
return APIAccount
}
//UpstreamTokenStore - token store for upstream system
type UpstreamTokenStore struct {
Scope string `json:"scope,omitempty"`
TokenType string `json:"token_type,omitempty"`
AccessToken string `json:"access_token,omitempty"`
RefreshToken string `json:"refresh_token,omitempty"`
ExpiresIn int `json:"expires_in,omitempty"`
JTI string `json:"jti,omitempty"`
Error string `json:"error,omitempty"`
ErrorDescription string `json:"error_description,omitempty"`
RefreshAt time.Time `json:"-"`
}
//Reset - reset store
func (s *UpstreamTokenStore) Reset() {
s.Scope = ""
s.TokenType = ""
s.AccessToken = ""
s.RefreshToken = ""
s.ExpiresIn = 0
s.JTI = ""
s.Error = ""
s.ErrorDescription = ""
}
//UpstreamAccount - account for upstream system
type UpstreamAccount struct {
TokenURL string
ClientID string
ClientSecret string
UserName string
Password string
store *UpstreamTokenStore
mutex *sync.Mutex
}
//NewUpstreamAccount - create upstream account from config
func (a *App) NewUpstreamAccount() *UpstreamAccount {
const oauthPath = "oauth/token"
return &UpstreamAccount{
TokenURL: a.Config.UpstreamURL + oauthPath,
ClientID: a.Config.UpstreamClientID,
ClientSecret: a.Config.UpstreamClientSecret,
UserName: a.Config.UpstreamUserName,
Password: a.Config.UpstreamPassword,
store: &UpstreamTokenStore{},
mutex: &sync.Mutex{},
}
}
//GetToken - get or refresh Token
func (a *UpstreamAccount) GetToken(forced bool) string {
a.mutex.Lock()
defer a.mutex.Unlock()
if a.store.RefreshToken == "" {
return a.getToken()
}
expiresAt := a.store.RefreshAt.Add(time.Second * time.Duration(a.store.ExpiresIn-10))
if !forced && expiresAt.After(time.Now()) {
return a.store.AccessToken
}
token := a.refreshToken()
if token != "" {
return token
}
return a.getToken()
}
func (a *UpstreamAccount) getToken() string {
payload := url.Values{}
payload.Add("grant_type", "password")
payload.Add("username", a.UserName)
payload.Add("password", a.Password)
req, _ := http.NewRequest(http.MethodPost, a.TokenURL, strings.NewReader(payload.Encode()))
req.SetBasicAuth(a.ClientID, a.ClientSecret)
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
resp, err := httpClient.Do(req)
if err != nil {
log.Printf("[ERR] - [UpstreamTokenStore][getToken], http.do err: %v", err)
return ""
}
defer resp.Body.Close()
dec := json.NewDecoder(resp.Body)
if err := dec.Decode(&a.store); err != nil {
log.Printf("[ERR] - [UpstreamTokenStore][getToken], decode err: %v", err)
return ""
}
if a.store.Error != "" {
log.Printf("[ERR] - [UpstreamTokenStore][getToken], token err: %s, %s", a.store.Error, a.store.ErrorDescription)
//a.store.Reset()
return ""
}
a.store.RefreshAt = time.Now()
return a.store.AccessToken
}
func (a *UpstreamAccount) refreshToken() string {
payload := url.Values{}
payload.Add("grant_type", "refresh_token")
payload.Add("refresh_token", a.store.RefreshToken)
req, _ := http.NewRequest(http.MethodPost, a.TokenURL, strings.NewReader(payload.Encode()))
req.SetBasicAuth(a.ClientID, a.ClientSecret)
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
resp, err := httpClient.Do(req)
if err != nil {
log.Printf("[ERR] - [UpstreamTokenStore][refreshToken], http.do err: %v", err)
return ""
}
defer resp.Body.Close()
dec := json.NewDecoder(resp.Body)
if err := dec.Decode(&a.store); err != nil {
log.Printf("[ERR] - [UpstreamTokenStore][refreshToken], decode err: %v", err)
a.store.Reset()
return ""
}
if a.store.Error != "" {
log.Printf("[ERR] - [UpstreamTokenStore][refreshToken], token err: %s, %s", a.store.Error, a.store.ErrorDescription)
a.store.Reset()
return ""
}
a.store.RefreshAt = time.Now()
return a.store.AccessToken
}