diff --git a/configEditor_general.go b/configEditor_general.go index 3a3d5d2..b98fc99 100644 --- a/configEditor_general.go +++ b/configEditor_general.go @@ -17,10 +17,11 @@ import ( type ( configEditorGeneralConfig struct { - BotEditors []string `json:"bot_editors"` - BotName *string `json:"bot_name,omitempty"` - Channels []string `json:"channels"` - ChannelScopes map[string][]string `json:"channel_scopes"` + BotEditors []string `json:"bot_editors"` + BotName *string `json:"bot_name,omitempty"` + Channels []string `json:"channels"` + ChannelScopes map[string][]string `json:"channel_scopes"` + ChannelHasToken map[string]bool `json:"channel_has_token"` } ) @@ -186,7 +187,12 @@ func configEditorHandleGeneralDeleteAuthToken(w http.ResponseWriter, r *http.Req } func configEditorHandleGeneralGet(w http.ResponseWriter, _ *http.Request) { - channelScopes := make(map[string][]string) + resp := configEditorGeneralConfig{ + BotEditors: config.BotEditors, + Channels: config.Channels, + ChannelHasToken: make(map[string]bool), + ChannelScopes: make(map[string][]string), + } channels, err := accessService.ListPermittedChannels() if err != nil { @@ -195,7 +201,12 @@ func configEditorHandleGeneralGet(w http.ResponseWriter, _ *http.Request) { } for _, ch := range channels { - if channelScopes[ch], err = accessService.GetChannelPermissions(ch); err != nil { + if resp.ChannelScopes[ch], err = accessService.GetChannelPermissions(ch); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + if resp.ChannelHasToken[ch], err = accessService.HasTokensForChannel(ch); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } @@ -206,13 +217,9 @@ func configEditorHandleGeneralGet(w http.ResponseWriter, _ *http.Request) { http.Error(w, err.Error(), http.StatusInternalServerError) return } + resp.BotName = &uName - if err = json.NewEncoder(w).Encode(configEditorGeneralConfig{ - BotEditors: config.BotEditors, - BotName: &uName, - Channels: config.Channels, - ChannelScopes: channelScopes, - }); err != nil { + if err = json.NewEncoder(w).Encode(resp); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } } diff --git a/internal/service/access/access.go b/internal/service/access/access.go index dfd9560..a92f6c1 100644 --- a/internal/service/access/access.go +++ b/internal/service/access/access.go @@ -212,6 +212,40 @@ func (s Service) HasPermissionsForChannel(channel string, scopes ...string) (boo return true, nil } +// HasTokensForChannel retrieves and decrypts stored access- and +// refresh-tokens to evaluate whether tokens are available. Those +// tokens are NOT validated in this request, it's just checked whether +// they are present +func (s Service) HasTokensForChannel(channel string) (bool, error) { + var ( + err error + perm extendedPermission + ) + + if err = helpers.Retry(func() error { + err = s.db.DB().First(&perm, "channel = ?", strings.TrimLeft(channel, "#")).Error + if errors.Is(err, gorm.ErrRecordNotFound) { + return backoff.NewErrCannotRetry(ErrChannelNotAuthorized) + } + return errors.Wrap(err, "getting twitch credential from database") + }); err != nil { + if errors.Is(err, ErrChannelNotAuthorized) { + return false, nil + } + return false, err + } + + if perm.AccessToken, err = s.db.DecryptField(perm.AccessToken); err != nil { + return false, errors.Wrap(err, "decrypting access token") + } + + if perm.RefreshToken, err = s.db.DecryptField(perm.RefreshToken); err != nil { + return false, errors.Wrap(err, "decrypting refresh token") + } + + return perm.AccessToken != "" && perm.RefreshToken != "", nil +} + func (s Service) ListPermittedChannels() (out []string, err error) { var perms []extendedPermission if err = helpers.Retry(func() error { diff --git a/src/generalConfig.vue b/src/generalConfig.vue index fe21e36..65c6998 100644 --- a/src/generalConfig.vue +++ b/src/generalConfig.vue @@ -25,7 +25,14 @@ {{ channel }} + - Channel is missing {{ missingExtendedScopes(channel).length }} extended permissions. - Click pencil to change granted permissions. + +