[editor] Display clear warning when ext perms are missing

Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
Knut Ahlers 2023-12-14 00:02:14 +01:00
parent 1515536746
commit 5d0a5322a5
Signed by: luzifer
GPG key ID: D91C3E91E4CAD6F5
3 changed files with 69 additions and 15 deletions

View file

@ -21,6 +21,7 @@ type (
BotName *string `json:"bot_name,omitempty"` BotName *string `json:"bot_name,omitempty"`
Channels []string `json:"channels"` Channels []string `json:"channels"`
ChannelScopes map[string][]string `json:"channel_scopes"` 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) { 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() channels, err := accessService.ListPermittedChannels()
if err != nil { if err != nil {
@ -195,7 +201,12 @@ func configEditorHandleGeneralGet(w http.ResponseWriter, _ *http.Request) {
} }
for _, ch := range channels { 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) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
@ -206,13 +217,9 @@ func configEditorHandleGeneralGet(w http.ResponseWriter, _ *http.Request) {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
resp.BotName = &uName
if err = json.NewEncoder(w).Encode(configEditorGeneralConfig{ if err = json.NewEncoder(w).Encode(resp); err != nil {
BotEditors: config.BotEditors,
BotName: &uName,
Channels: config.Channels,
ChannelScopes: channelScopes,
}); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
} }
} }

View file

@ -212,6 +212,40 @@ func (s Service) HasPermissionsForChannel(channel string, scopes ...string) (boo
return true, nil 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) { func (s Service) ListPermittedChannels() (out []string, err error) {
var perms []extendedPermission var perms []extendedPermission
if err = helpers.Retry(func() error { if err = helpers.Retry(func() error {

View file

@ -25,7 +25,14 @@
{{ channel }} {{ channel }}
<span class="ml-auto mr-2"> <span class="ml-auto mr-2">
<font-awesome-icon <font-awesome-icon
v-if="!hasAllExtendedScopes(channel)" v-if="!generalConfig.channel_has_token[channel]"
:id="`channelPublicWarn${channel}`"
fixed-width
class="ml-1 text-danger"
:icon="['fas', 'exclamation-triangle']"
/>
<font-awesome-icon
v-else-if="!hasAllExtendedScopes(channel)"
:id="`channelPublicWarn${channel}`" :id="`channelPublicWarn${channel}`"
fixed-width fixed-width
class="ml-1 text-warning" class="ml-1 text-warning"
@ -35,8 +42,14 @@
:target="`channelPublicWarn${channel}`" :target="`channelPublicWarn${channel}`"
triggers="hover" triggers="hover"
> >
<template v-if="!generalConfig.channel_has_token[channel]">
Bot is not authorized to access Twitch on behalf of this channels owner (tokens are missing).
Click pencil to grant permissions.
</template>
<template v-else>
Channel is missing {{ missingExtendedScopes(channel).length }} extended permissions. Channel is missing {{ missingExtendedScopes(channel).length }} extended permissions.
Click pencil to change granted permissions. Click pencil to change granted permissions.
</template>
</b-tooltip> </b-tooltip>
</span> </span>
<b-button-group size="sm"> <b-button-group size="sm">