diff --git a/AniListUserFunctions.go b/AniListUserFunctions.go index e90d98d..f6fec56 100644 --- a/AniListUserFunctions.go +++ b/AniListUserFunctions.go @@ -19,18 +19,48 @@ import ( var aniListJwt AniListJWT -var aniRing, _ = keyring.Open(keyring.Config{ - ServiceName: "AniTrack", - KeychainName: "AniTrack", - KeychainSynchronizable: false, - KeychainTrustApplication: true, - KeychainAccessibleWhenUnlocked: true, -}) +var aniRing keyring.Keyring + +func init() { + var err error + aniRing, err = keyring.Open(keyring.Config{ + ServiceName: "AniTrack", + KeychainName: "AniTrack", + KeychainSynchronizable: false, + KeychainTrustApplication: true, + KeychainAccessibleWhenUnlocked: true, + }) + if err != nil { + log.Printf("anilist: secure storage unavailable: %s", err) + } +} + +func aniRingReady() bool { + if aniRing == nil { + log.Println("anilist: secure storage unavailable") + return false + } + return true +} + +func aniRingSet(key string, data []byte) error { + if !aniRingReady() { + return errors.New("anilist: secure storage unavailable") + } + if err := aniRing.Set(keyring.Item{Key: key, Data: data}); err != nil { + log.Printf("anilist: save %s failed: %s", key, err) + return err + } + return nil +} var aniCtxShutdown, aniCancel = context.WithCancel(context.Background()) func (a *App) CheckIfAniListLoggedIn() bool { if (AniListJWT{} == aniListJwt) { + if !aniRingReady() { + return false + } tokenType, tokenErr := aniRing.Get("anilistTokenType") expiresIn, expiresInErr := aniRing.Get("anilistTokenExpiresIn") refreshToken, refreshTokenErr := aniRing.Get("anilistRefreshToken") @@ -38,10 +68,15 @@ func (a *App) CheckIfAniListLoggedIn() bool { if (tokenErr != nil || expiresInErr != nil || refreshTokenErr != nil || accessTokenErr != nil) || len(accessToken.Data) == 0 { return false } else { + var expiresInConvertErr error aniListJwt.TokenType = string(tokenType.Data) aniListJwt.AccessToken = string(accessToken.Data) aniListJwt.RefreshToken = string(refreshToken.Data) - aniListJwt.ExpiresIn, _ = strconv.Atoi(string(expiresIn.Data)) + aniListJwt.ExpiresIn, expiresInConvertErr = strconv.Atoi(string(expiresIn.Data)) + if expiresInConvertErr != nil { + log.Printf("anilist: invalid expiresIn %q: %s", string(expiresIn.Data), expiresInConvertErr) + return false + } return true } } else { @@ -51,6 +86,10 @@ func (a *App) CheckIfAniListLoggedIn() bool { func (a *App) AniListLogin() { if (AniListJWT{} == aniListJwt) { + if !aniRingReady() { + log.Println("anilist: cannot check login, secure storage unavailable") + return + } tokenType, tokenErr := aniRing.Get("anilistTokenType") expiresIn, expiresInErr := aniRing.Get("anilistTokenExpiresIn") refreshToken, refreshTokenErr := aniRing.Get("anilistRefreshToken") @@ -64,10 +103,14 @@ func (a *App) AniListLogin() { a.handleAniListCallback(serverDone) serverDone.Wait() } else { + var expiresInConvertErr error aniListJwt.TokenType = string(tokenType.Data) aniListJwt.AccessToken = string(accessToken.Data) aniListJwt.RefreshToken = string(refreshToken.Data) - aniListJwt.ExpiresIn, _ = strconv.Atoi(string(expiresIn.Data)) + aniListJwt.ExpiresIn, expiresInConvertErr = strconv.Atoi(string(expiresIn.Data)) + if expiresInConvertErr != nil { + log.Printf("anilist: invalid expiresIn %q: %s", string(expiresIn.Data), expiresInConvertErr) + } } } } @@ -85,22 +128,10 @@ func (a *App) handleAniListCallback(wg *sync.WaitGroup) { content := r.FormValue("code") if content != "" { aniListJwt = getAniListAuthorizationToken(content) - _ = aniRing.Set(keyring.Item{ - Key: "anilistTokenType", - Data: []byte(aniListJwt.TokenType), - }) - _ = aniRing.Set(keyring.Item{ - Key: "anilistTokenExpiresIn", - Data: []byte(strconv.Itoa(aniListJwt.ExpiresIn)), - }) - _ = aniRing.Set(keyring.Item{ - Key: "anilistAccessToken", - Data: []byte(aniListJwt.AccessToken), - }) - _ = aniRing.Set(keyring.Item{ - Key: "anilistRefreshToken", - Data: []byte(aniListJwt.RefreshToken), - }) + _ = aniRingSet("anilistTokenType", []byte(aniListJwt.TokenType)) + _ = aniRingSet("anilistTokenExpiresIn", []byte(strconv.Itoa(aniListJwt.ExpiresIn))) + _ = aniRingSet("anilistAccessToken", []byte(aniListJwt.AccessToken)) + _ = aniRingSet("anilistRefreshToken", []byte(aniListJwt.RefreshToken)) _, err := runtime.MessageDialog(*wailsContext, runtime.MessageDialogOptions{ Title: "AniList Authorization", Message: "It is now safe to close your browser tab", @@ -208,12 +239,16 @@ func (a *App) GetAniListLoggedInUser() AniListUser { func (a *App) LogoutAniList() string { if (AniListJWT{} != aniListJwt) { + if !aniRingReady() { + aniListJwt = AniListJWT{} + return "AniList Logged Out Successfully" + } typeErr := aniRing.Remove("anilistTokenType") expiresInErr := aniRing.Remove("anilistTokenExpiresIn") accessTokenErr := aniRing.Remove("anilistAccessToken") refreshTokenErr := aniRing.Remove("anilistRefreshToken") if typeErr != nil || expiresInErr != nil || accessTokenErr != nil || refreshTokenErr != nil { - fmt.Println("AniList Logout Failed") + log.Printf("anilist: logout cleanup failed (type=%v expires=%v access=%v refresh=%v)", typeErr, expiresInErr, accessTokenErr, refreshTokenErr) } aniListJwt = AniListJWT{} } diff --git a/MALUserFunctions.go b/MALUserFunctions.go index 2191b38..0e18986 100644 --- a/MALUserFunctions.go +++ b/MALUserFunctions.go @@ -23,13 +23,40 @@ import ( var myAnimeListJwt MyAnimeListJWT -var myAnimeListRing, _ = keyring.Open(keyring.Config{ - ServiceName: "AniTrack", - KeychainName: "AniTrack", - KeychainSynchronizable: false, - KeychainTrustApplication: true, - KeychainAccessibleWhenUnlocked: true, -}) +var myAnimeListRing keyring.Keyring + +func init() { + var err error + myAnimeListRing, err = keyring.Open(keyring.Config{ + ServiceName: "AniTrack", + KeychainName: "AniTrack", + KeychainSynchronizable: false, + KeychainTrustApplication: true, + KeychainAccessibleWhenUnlocked: true, + }) + if err != nil { + log.Printf("mal: secure storage unavailable: %s", err) + } +} + +func malRingReady() bool { + if myAnimeListRing == nil { + log.Println("mal: secure storage unavailable") + return false + } + return true +} + +func malRingSet(key string, data []byte) error { + if !malRingReady() { + return errors.New("mal: secure storage unavailable") + } + if err := myAnimeListRing.Set(keyring.Item{Key: key, Data: data}); err != nil { + log.Printf("mal: save %s failed: %s", key, err) + return err + } + return nil +} var myAnimeListCtxShutdown, myAnimeListCancel = context.WithCancel(context.Background()) @@ -72,6 +99,9 @@ func (v *CodeVerifier) CodeChallengeS256() string { func (a *App) CheckIfMyAnimeListLoggedIn() bool { if (MyAnimeListJWT{} == myAnimeListJwt) { + if !malRingReady() { + return false + } tokenType, tokenErr := myAnimeListRing.Get("MyAnimeListTokenType") expiresIn, expiresInErr := myAnimeListRing.Get("MyAnimeListExpiresIn") accessToken, accessTokenErr := myAnimeListRing.Get("MyAnimeListAccessToken") @@ -83,7 +113,8 @@ func (a *App) CheckIfMyAnimeListLoggedIn() bool { myAnimeListJwt.TokenType = string(tokenType.Data) myAnimeListJwt.ExpiresIn, expiresInConvertErr = strconv.Atoi(string(expiresIn.Data)) if expiresInConvertErr != nil { - fmt.Println("unable to convert string to int") + log.Printf("mal: invalid expiresIn %q: %s", string(expiresIn.Data), expiresInConvertErr) + return false } myAnimeListJwt.AccessToken = string(accessToken.Data) myAnimeListJwt.RefreshToken = string(refreshToken.Data) @@ -97,6 +128,10 @@ func (a *App) CheckIfMyAnimeListLoggedIn() bool { func (a *App) MyAnimeListLogin() { if !a.CheckIfMyAnimeListLoggedIn() { fmt.Println("check logged in function failed") + if !malRingReady() { + log.Println("mal: cannot check login, secure storage unavailable") + return + } tokenType, tokenErr := myAnimeListRing.Get("MyAnimeListTokenType") expiresIn, expiresInErr := myAnimeListRing.Get("MyAnimeListExpiresIn") accessToken, accessTokenErr := myAnimeListRing.Get("MyAnimeListAccessToken") @@ -114,7 +149,7 @@ func (a *App) MyAnimeListLogin() { myAnimeListJwt.TokenType = string(tokenType.Data) myAnimeListJwt.ExpiresIn, expiresInConvertErr = strconv.Atoi(string(expiresIn.Data)) if expiresInConvertErr != nil { - fmt.Println("unable to convert string to int in Login function") + log.Printf("mal: invalid expiresIn %q: %s", string(expiresIn.Data), expiresInConvertErr) } myAnimeListJwt.AccessToken = string(accessToken.Data) myAnimeListJwt.RefreshToken = string(refreshToken.Data) @@ -136,22 +171,10 @@ func (a *App) handleMyAnimeListCallback(wg *sync.WaitGroup, verifier *CodeVerifi if content != "" { myAnimeListJwt = getMyAnimeListAuthorizationToken(content, verifier) - _ = myAnimeListRing.Set(keyring.Item{ - Key: "MyAnimeListTokenType", - Data: []byte(myAnimeListJwt.TokenType), - }) - _ = myAnimeListRing.Set(keyring.Item{ - Key: "MyAnimeListExpiresIn", - Data: []byte(strconv.Itoa(myAnimeListJwt.ExpiresIn)), - }) - _ = myAnimeListRing.Set(keyring.Item{ - Key: "MyAnimeListAccessToken", - Data: []byte(myAnimeListJwt.AccessToken), - }) - _ = myAnimeListRing.Set(keyring.Item{ - Key: "MyAnimeListRefreshToken", - Data: []byte(myAnimeListJwt.RefreshToken), - }) + _ = malRingSet("MyAnimeListTokenType", []byte(myAnimeListJwt.TokenType)) + _ = malRingSet("MyAnimeListExpiresIn", []byte(strconv.Itoa(myAnimeListJwt.ExpiresIn))) + _ = malRingSet("MyAnimeListAccessToken", []byte(myAnimeListJwt.AccessToken)) + _ = malRingSet("MyAnimeListRefreshToken", []byte(myAnimeListJwt.RefreshToken)) _, err := runtime.MessageDialog(*wailsContext, runtime.MessageDialogOptions{ Title: "MyAnimeList Authorization", Message: "It is now safe to close your browser tab", @@ -292,22 +315,10 @@ func refreshMyAnimeListAuthorizationToken() bool { myAnimeListJwt = refreshed - _ = myAnimeListRing.Set(keyring.Item{ - Key: "MyAnimeListTokenType", - Data: []byte(myAnimeListJwt.TokenType), - }) - _ = myAnimeListRing.Set(keyring.Item{ - Key: "MyAnimeListExpiresIn", - Data: []byte(strconv.Itoa(myAnimeListJwt.ExpiresIn)), - }) - _ = myAnimeListRing.Set(keyring.Item{ - Key: "MyAnimeListAccessToken", - Data: []byte(myAnimeListJwt.AccessToken), - }) - _ = myAnimeListRing.Set(keyring.Item{ - Key: "MyAnimeListRefreshToken", - Data: []byte(myAnimeListJwt.RefreshToken), - }) + _ = malRingSet("MyAnimeListTokenType", []byte(myAnimeListJwt.TokenType)) + _ = malRingSet("MyAnimeListExpiresIn", []byte(strconv.Itoa(myAnimeListJwt.ExpiresIn))) + _ = malRingSet("MyAnimeListAccessToken", []byte(myAnimeListJwt.AccessToken)) + _ = malRingSet("MyAnimeListRefreshToken", []byte(myAnimeListJwt.RefreshToken)) return true } @@ -367,12 +378,16 @@ func createUser() MyAnimeListUser { func (a *App) LogoutMyAnimeList() string { if (MyAnimeListJWT{} != myAnimeListJwt) { + if !malRingReady() { + myAnimeListJwt = MyAnimeListJWT{} + return "MAL Logged Out Successfully" + } typeErr := myAnimeListRing.Remove("MyAnimeListTokenType") expiresInErr := myAnimeListRing.Remove("MyAnimeListExpiresIn") accessTokenErr := myAnimeListRing.Remove("MyAnimeListAccessToken") refreshTokenErr := myAnimeListRing.Remove("MyAnimeListRefreshToken") if typeErr != nil || expiresInErr != nil || accessTokenErr != nil || refreshTokenErr != nil { - fmt.Println("MAL Logout Failed") + log.Printf("mal: logout cleanup failed (type=%v expires=%v access=%v refresh=%v)", typeErr, expiresInErr, accessTokenErr, refreshTokenErr) } myAnimeListJwt = MyAnimeListJWT{} } diff --git a/SimklUserFunctions.go b/SimklUserFunctions.go index 51b4333..f060145 100644 --- a/SimklUserFunctions.go +++ b/SimklUserFunctions.go @@ -17,18 +17,48 @@ import ( var simklJwt SimklJWT -var simklRing, _ = keyring.Open(keyring.Config{ - ServiceName: "AniTrack", - KeychainName: "AniTrack", - KeychainSynchronizable: false, - KeychainTrustApplication: true, - KeychainAccessibleWhenUnlocked: true, -}) +var simklRing keyring.Keyring + +func init() { + var err error + simklRing, err = keyring.Open(keyring.Config{ + ServiceName: "AniTrack", + KeychainName: "AniTrack", + KeychainSynchronizable: false, + KeychainTrustApplication: true, + KeychainAccessibleWhenUnlocked: true, + }) + if err != nil { + log.Printf("simkl: secure storage unavailable: %s", err) + } +} + +func simklRingReady() bool { + if simklRing == nil { + log.Println("simkl: secure storage unavailable") + return false + } + return true +} + +func simklRingSet(key string, data []byte) error { + if !simklRingReady() { + return errors.New("simkl: secure storage unavailable") + } + if err := simklRing.Set(keyring.Item{Key: key, Data: data}); err != nil { + log.Printf("simkl: save %s failed: %s", key, err) + return err + } + return nil +} var simklCtxShutdown, simklCancel = context.WithCancel(context.Background()) func (a *App) CheckIfSimklLoggedIn() bool { if (SimklJWT{} == simklJwt) { + if !simklRingReady() { + return false + } tokenType, tokenTypeErr := simklRing.Get("SimklTokenType") accessToken, accessTokenErr := simklRing.Get("SimklAccessToken") scope, scopeErr := simklRing.Get("SimklScope") @@ -47,6 +77,10 @@ func (a *App) CheckIfSimklLoggedIn() bool { func (a *App) SimklLogin() { if !a.CheckIfSimklLoggedIn() { + if !simklRingReady() { + log.Println("simkl: cannot check login, secure storage unavailable") + return + } tokenType, tokenTypeErr := simklRing.Get("SimklTokenType") accessToken, accessTokenErr := simklRing.Get("SimklAccessToken") scope, scopeErr := simklRing.Get("SimklScope") @@ -80,18 +114,9 @@ func (a *App) handleSimklCallback(wg *sync.WaitGroup) { if content != "" { simklJwt = getSimklAuthorizationToken(content) - _ = simklRing.Set(keyring.Item{ - Key: "SimklTokenType", - Data: []byte(simklJwt.TokenType), - }) - _ = simklRing.Set(keyring.Item{ - Key: "SimklAccessToken", - Data: []byte(simklJwt.AccessToken), - }) - _ = simklRing.Set(keyring.Item{ - Key: "SimklScope", - Data: []byte(simklJwt.Scope), - }) + _ = simklRingSet("SimklTokenType", []byte(simklJwt.TokenType)) + _ = simklRingSet("SimklAccessToken", []byte(simklJwt.AccessToken)) + _ = simklRingSet("SimklScope", []byte(simklJwt.Scope)) _, err := runtime.MessageDialog(*wailsContext, runtime.MessageDialogOptions{ Title: "Simkl Authorization", Message: "It is now safe to close your browser tab", @@ -218,12 +243,16 @@ func (a *App) GetSimklLoggedInUser() SimklUser { func (a *App) LogoutSimkl() string { if (SimklJWT{} != simklJwt) { + if !simklRingReady() { + simklJwt = SimklJWT{} + return "Simkl Logged Out Successfully" + } tokenTypeErr := simklRing.Remove("SimklTokenType") accessTokenErr := simklRing.Remove("SimklAccessToken") scopeErr := simklRing.Remove("SimklScope") if tokenTypeErr != nil || accessTokenErr != nil || scopeErr != nil { - fmt.Println("Simkl Logout Failed") + log.Printf("simkl: logout cleanup failed (type=%v access=%v scope=%v)", tokenTypeErr, accessTokenErr, scopeErr) } simklJwt = SimklJWT{} }