freebox-exporter/fbx/session.go

175 lines
4.0 KiB
Go
Raw Normal View History

2021-02-25 09:04:39 +01:00
package fbx
import (
"crypto/hmac"
"crypto/sha1"
"encoding/hex"
"fmt"
"net/http"
2021-02-28 22:15:44 +01:00
"sync"
2021-02-25 09:04:39 +01:00
"time"
"github.com/trazfr/freebox-exporter/log"
)
2021-02-28 22:15:44 +01:00
type sessionInfo struct {
sessionToken string
challenge string
}
2021-02-25 09:04:39 +01:00
// FreeboxSession represents all the variables used in a session
type FreeboxSession struct {
client *FreeboxHttpClient
getSessionTokenURL string
getChallengeURL string
2021-02-28 22:15:44 +01:00
appToken string
sessionTokenLastUpdate time.Time
sessionTokenLock sync.Mutex
sessionInfo *sessionInfo
oldSessionInfo *sessionInfo // avoid deleting the sessionInfo too quickly
2021-02-25 09:04:39 +01:00
}
func GetAppToken(client *FreeboxHttpClient, apiVersion *FreeboxAPIVersion) (string, error) {
reqStruct := getFreeboxAuthorize()
postResponse := struct {
AppToken string `json:"app_token"`
TrackID int64 `json:"track_id"`
}{}
url, err := apiVersion.GetURL("login/authorize/")
if err != nil {
return "", err
}
if err := client.Post(url, reqStruct, &postResponse); err != nil {
return "", err
}
counter := 0
for {
counter++
status := struct {
Status string `json:"status"`
}{}
url, err := apiVersion.GetURL("login/authorize/%d", postResponse.TrackID)
if err != nil {
return "", err
}
client.Get(url, &status)
switch status.Status {
case "pending":
2021-02-28 22:15:44 +01:00
log.Info.Println(counter, "Please accept the login on the Freebox Server")
2021-02-25 09:04:39 +01:00
time.Sleep(10 * time.Second)
case "granted":
return postResponse.AppToken, nil
default:
return "", fmt.Errorf("Access is %s", status.Status)
}
}
}
func NewFreeboxSession(appToken string, client *FreeboxHttpClient, apiVersion *FreeboxAPIVersion) (*FreeboxSession, error) {
getChallengeURL, err := apiVersion.GetURL("login/")
if err != nil {
return nil, err
}
getSessionTokenURL, err := apiVersion.GetURL("login/session/")
if err != nil {
return nil, err
}
result := &FreeboxSession{
client: client,
getSessionTokenURL: getSessionTokenURL,
getChallengeURL: getChallengeURL,
appToken: appToken,
}
if err := result.Refresh(); err != nil {
return nil, err
}
return result, nil
}
func (f *FreeboxSession) IsValid() bool {
2021-02-28 22:15:44 +01:00
return f.sessionInfo != nil
2021-02-25 09:04:39 +01:00
}
func (f *FreeboxSession) AddHeader(req *http.Request) {
2021-02-28 22:15:44 +01:00
if f != nil && f.sessionInfo != nil {
req.Header.Set("X-Fbx-App-Auth", f.sessionInfo.sessionToken)
2021-02-25 09:04:39 +01:00
}
}
func (f *FreeboxSession) Refresh() error {
2021-02-28 22:15:44 +01:00
f.sessionTokenLock.Lock()
defer f.sessionTokenLock.Unlock()
if sinceLastUpdate := time.Now().Sub(f.sessionTokenLastUpdate); sinceLastUpdate < 5*time.Second {
log.Debug.Printf("Updated %v ago. Skipping", sinceLastUpdate)
return nil
}
2021-02-25 09:04:39 +01:00
challenge, err := f.getChallenge()
if err != nil {
return err
}
sessionToken, err := f.getSessionToken(challenge)
if err != nil {
return err
}
2021-02-28 22:15:44 +01:00
f.sessionTokenLastUpdate = time.Now()
f.oldSessionInfo = f.sessionInfo
f.sessionInfo = &sessionInfo{
challenge: challenge,
sessionToken: sessionToken,
}
2021-02-25 09:04:39 +01:00
return nil
}
func (f *FreeboxSession) getChallenge() (string, error) {
log.Debug.Println("GET challenge:", f.getChallengeURL)
resStruct := struct {
Challenge string `json:"challenge"`
}{}
if err := f.client.Get(f.getChallengeURL, &resStruct); err != nil {
return "", err
}
log.Debug.Println("Challenge:", resStruct.Challenge)
return resStruct.Challenge, nil
}
func (f *FreeboxSession) getSessionToken(challenge string) (string, error) {
log.Debug.Println("GET SessionToken:", f.getSessionTokenURL)
freeboxAuthorize := getFreeboxAuthorize()
hash := hmac.New(sha1.New, []byte(f.appToken))
hash.Write([]byte(challenge))
password := hex.EncodeToString(hash.Sum(nil))
reqStruct := struct {
AppID string `json:"app_id"`
Password string `json:"password"`
}{
AppID: freeboxAuthorize.AppID,
Password: password,
}
resStruct := struct {
SessionToken string `json:"session_token"`
}{}
if err := f.client.Post(f.getSessionTokenURL, &reqStruct, &resStruct); err != nil {
return "", err
}
log.Debug.Println("SessionToken:", resStruct.SessionToken)
return resStruct.SessionToken, nil
}