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:
|
2022-09-30 21:11:12 +02:00
|
|
|
return "", fmt.Errorf("access is %s", status.Status)
|
2021-02-25 09:04:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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()
|
|
|
|
|
2022-09-30 21:11:12 +02:00
|
|
|
if sinceLastUpdate := time.Since(f.sessionTokenLastUpdate); sinceLastUpdate < 5*time.Second {
|
2021-02-28 22:15:44 +01:00
|
|
|
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
|
|
|
|
}
|