added the ability to assign multiple plugin administrators

This commit is contained in:
Дмитрий Пиченикин 2026-02-19 16:21:52 +03:00
parent 754304e4ca
commit 7c976233a7
6 changed files with 53 additions and 45 deletions

View File

@ -172,7 +172,7 @@ func (p *Plugin) getTypes(w http.ResponseWriter, r *http.Request, userID string)
resp := GetTypesResponse{ resp := GetTypesResponse{
Types: result, Types: result,
CanCreateType: canCreateType(u, p.badgeAdminUserID, false), CanCreateType: canCreateType(u, p.badgeAdminUserIDs, false),
} }
b, _ := json.Marshal(resp) b, _ := json.Marshal(resp)
@ -225,7 +225,7 @@ func (p *Plugin) apiCreateBadge(w http.ResponseWriter, r *http.Request, userID s
return return
} }
if !canCreateBadge(user, p.badgeAdminUserID, t) { if !canCreateBadge(user, p.badgeAdminUserIDs, t) {
p.writeAPIError(w, &APIErrorResponse{ p.writeAPIError(w, &APIErrorResponse{
ID: "no_permission", Message: "No permission to create badge of this type", StatusCode: http.StatusForbidden, ID: "no_permission", Message: "No permission to create badge of this type", StatusCode: http.StatusForbidden,
}) })
@ -281,7 +281,7 @@ func (p *Plugin) apiCreateType(w http.ResponseWriter, r *http.Request, userID st
return return
} }
if !canCreateType(user, p.badgeAdminUserID, false) { if !canCreateType(user, p.badgeAdminUserIDs, false) {
p.writeAPIError(w, &APIErrorResponse{ p.writeAPIError(w, &APIErrorResponse{
ID: "no_permission", Message: "No permission to create type", StatusCode: http.StatusForbidden, ID: "no_permission", Message: "No permission to create type", StatusCode: http.StatusForbidden,
}) })
@ -350,7 +350,7 @@ func (p *Plugin) apiUpdateBadge(w http.ResponseWriter, r *http.Request, userID s
return return
} }
if !canEditBadge(user, p.badgeAdminUserID, badge) { if !canEditBadge(user, p.badgeAdminUserIDs, badge) {
p.writeAPIError(w, &APIErrorResponse{ p.writeAPIError(w, &APIErrorResponse{
ID: "no_permission", Message: "No permission to edit this badge", StatusCode: http.StatusForbidden, ID: "no_permission", Message: "No permission to edit this badge", StatusCode: http.StatusForbidden,
}) })
@ -420,7 +420,7 @@ func (p *Plugin) apiDeleteBadge(w http.ResponseWriter, r *http.Request, userID s
return return
} }
if !canEditBadge(user, p.badgeAdminUserID, badge) { if !canEditBadge(user, p.badgeAdminUserIDs, badge) {
p.writeAPIError(w, &APIErrorResponse{ p.writeAPIError(w, &APIErrorResponse{
ID: "no_permission", Message: "No permission to delete this badge", StatusCode: http.StatusForbidden, ID: "no_permission", Message: "No permission to delete this badge", StatusCode: http.StatusForbidden,
}) })
@ -470,7 +470,7 @@ func (p *Plugin) apiDeleteType(w http.ResponseWriter, r *http.Request, userID st
return return
} }
if !canEditType(user, p.badgeAdminUserID, t) { if !canEditType(user, p.badgeAdminUserIDs, t) {
p.writeAPIError(w, &APIErrorResponse{ p.writeAPIError(w, &APIErrorResponse{
ID: "no_permission", Message: "No permission to delete this type", StatusCode: http.StatusForbidden, ID: "no_permission", Message: "No permission to delete this type", StatusCode: http.StatusForbidden,
}) })
@ -550,7 +550,7 @@ func (p *Plugin) dialogCreateBadge(w http.ResponseWriter, r *http.Request, userI
return return
} }
if !canCreateBadge(user, p.badgeAdminUserID, t) { if !canCreateBadge(user, p.badgeAdminUserIDs, t) {
dialogError(w, T("badges.api.no_permissions_create_badge", "У вас нет прав на создание этого значка"), nil) dialogError(w, T("badges.api.no_permissions_create_badge", "У вас нет прав на создание этого значка"), nil)
return return
} }
@ -586,7 +586,7 @@ func (p *Plugin) dialogCreateType(w http.ResponseWriter, r *http.Request, userID
} }
T := p.getT(u.Locale) T := p.getT(u.Locale)
if !canCreateType(u, p.badgeAdminUserID, false) { if !canCreateType(u, p.badgeAdminUserIDs, false) {
dialogError(w, T("badges.api.no_permissions_create_type", "У вас нет прав на создание типа"), nil) dialogError(w, T("badges.api.no_permissions_create_type", "У вас нет прав на создание типа"), nil)
return return
} }
@ -684,7 +684,7 @@ func (p *Plugin) dialogSelectType(w http.ResponseWriter, r *http.Request, userID
} }
T := p.getT(u.Locale) T := p.getT(u.Locale)
if !canEditType(u, p.badgeAdminUserID, t) { if !canEditType(u, p.badgeAdminUserIDs, t) {
dialogError(w, T("badges.api.cannot_edit_type", "Вы не можете редактировать этот тип"), nil) dialogError(w, T("badges.api.cannot_edit_type", "Вы не можете редактировать этот тип"), nil)
return return
} }
@ -723,7 +723,7 @@ func (p *Plugin) dialogEditType(w http.ResponseWriter, r *http.Request, userID s
return return
} }
if !canEditType(u, p.badgeAdminUserID, originalType) { if !canEditType(u, p.badgeAdminUserIDs, originalType) {
dialogError(w, T("badges.api.no_permissions_edit_type", "У вас нет прав на редактирование этого типа"), nil) dialogError(w, T("badges.api.no_permissions_edit_type", "У вас нет прав на редактирование этого типа"), nil)
return return
} }
@ -828,7 +828,7 @@ func (p *Plugin) dialogSelectBadge(w http.ResponseWriter, r *http.Request, userI
} }
T := p.getT(u.Locale) T := p.getT(u.Locale)
if !canEditBadge(u, p.badgeAdminUserID, b) { if !canEditBadge(u, p.badgeAdminUserIDs, b) {
dialogError(w, T("badges.api.cannot_edit_badge", "Вы не можете редактировать этот значок"), nil) dialogError(w, T("badges.api.cannot_edit_badge", "Вы не можете редактировать этот значок"), nil)
return return
} }
@ -867,7 +867,7 @@ func (p *Plugin) dialogEditBadge(w http.ResponseWriter, r *http.Request, userID
return return
} }
if !canEditBadge(u, p.badgeAdminUserID, originalBadge) { if !canEditBadge(u, p.badgeAdminUserIDs, originalBadge) {
dialogError(w, T("badges.api.no_permissions_edit_badge", "У вас нет прав на редактирование этого значка"), nil) dialogError(w, T("badges.api.no_permissions_edit_badge", "У вас нет прав на редактирование этого значка"), nil)
return return
} }
@ -970,7 +970,7 @@ func (p *Plugin) dialogGrant(w http.ResponseWriter, r *http.Request, userID stri
return return
} }
if !canGrantBadge(granter, p.badgeAdminUserID, badge, badgeType) { if !canGrantBadge(granter, p.badgeAdminUserIDs, badge, badgeType) {
dialogError(w, T("badges.api.no_permissions_grant", "У вас нет прав на выдачу этого значка"), nil) dialogError(w, T("badges.api.no_permissions_grant", "У вас нет прав на выдачу этого значка"), nil)
return return
} }
@ -1030,7 +1030,7 @@ func (p *Plugin) dialogCreateSubscription(w http.ResponseWriter, r *http.Request
} }
T := p.getT(u.Locale) T := p.getT(u.Locale)
if !canCreateSubscription(u, p.badgeAdminUserID, req.ChannelId) { if !canCreateSubscription(u, p.badgeAdminUserIDs, req.ChannelId) {
dialogError(w, T("badges.api.cannot_create_subscription", "Вы не можете создать подписку"), nil) dialogError(w, T("badges.api.cannot_create_subscription", "Вы не можете создать подписку"), nil)
return return
} }
@ -1070,7 +1070,7 @@ func (p *Plugin) dialogDeleteSubscription(w http.ResponseWriter, r *http.Request
} }
T := p.getT(u.Locale) T := p.getT(u.Locale)
if !canCreateSubscription(u, p.badgeAdminUserID, req.ChannelId) { if !canCreateSubscription(u, p.badgeAdminUserIDs, req.ChannelId) {
dialogError(w, T("badges.api.cannot_delete_subscription", "Вы не можете удалить подписку"), nil) dialogError(w, T("badges.api.cannot_delete_subscription", "Вы не можете удалить подписку"), nil)
return return
} }
@ -1162,7 +1162,7 @@ func (p *Plugin) grantBadge(w http.ResponseWriter, r *http.Request, pluginID str
return return
} }
if !canGrantBadge(granter, p.badgeAdminUserID, badge, badgeType) { if !canGrantBadge(granter, p.badgeAdminUserIDs, badge, badgeType) {
p.writeAPIError(w, &APIErrorResponse{ p.writeAPIError(w, &APIErrorResponse{
ID: "cannot grant badge", ID: "cannot grant badge",
Message: "you have no permissions to grant this badge", Message: "you have no permissions to grant this badge",

View File

@ -271,7 +271,7 @@ func (p *Plugin) runEditBadge(args []string, extra *model.CommandArgs) (bool, *m
return commandError(err.Error()) return commandError(err.Error())
} }
if !canEditBadge(u, p.badgeAdminUserID, badge) { if !canEditBadge(u, p.badgeAdminUserIDs, badge) {
return commandError(T("badges.error.cannot_edit_badge", "У вас нет прав на редактирование этого значка")) return commandError(T("badges.error.cannot_edit_badge", "У вас нет прав на редактирование этого значка"))
} }
@ -359,7 +359,7 @@ func (p *Plugin) runEditType(args []string, extra *model.CommandArgs) (bool, *mo
} }
T := p.getT(u.Locale) T := p.getT(u.Locale)
if !canCreateType(u, p.badgeAdminUserID, false) { if !canCreateType(u, p.badgeAdminUserIDs, false) {
return commandError(T("badges.error.no_permissions_edit_type", "У вас нет прав на редактирование типа значков.")) return commandError(T("badges.error.no_permissions_edit_type", "У вас нет прав на редактирование типа значков."))
} }
@ -379,7 +379,7 @@ func (p *Plugin) runEditType(args []string, extra *model.CommandArgs) (bool, *mo
return commandError(err.Error()) return commandError(err.Error())
} }
if !canEditType(u, p.badgeAdminUserID, typeDefinition) { if !canEditType(u, p.badgeAdminUserIDs, typeDefinition) {
return commandError(T("badges.error.cannot_edit_type", "У вас нет прав на редактирование этого типа")) return commandError(T("badges.error.cannot_edit_type", "У вас нет прав на редактирование этого типа"))
} }
@ -493,7 +493,7 @@ func (p *Plugin) runCreateType(args []string, extra *model.CommandArgs) (bool, *
} }
T := p.getT(u.Locale) T := p.getT(u.Locale)
if !canCreateType(u, p.badgeAdminUserID, false) { if !canCreateType(u, p.badgeAdminUserIDs, false) {
return commandError(T("badges.error.no_permissions_create_type", "У вас нет прав на создание типа значков.")) return commandError(T("badges.error.no_permissions_create_type", "У вас нет прав на создание типа значков."))
} }
@ -582,7 +582,7 @@ func (p *Plugin) runGrant(args []string, extra *model.CommandArgs) (bool, *model
return commandError(err.Error()) return commandError(err.Error())
} }
if !canGrantBadge(granter, p.badgeAdminUserID, badge, badgeType) { if !canGrantBadge(granter, p.badgeAdminUserIDs, badge, badgeType) {
return commandError(T("badges.error.no_permissions_grant", "У вас нет прав на выдачу этого значка")) return commandError(T("badges.error.no_permissions_grant", "У вас нет прав на выдачу этого значка"))
} }
@ -755,7 +755,7 @@ func (p *Plugin) runCreateSubscription(args []string, extra *model.CommandArgs)
} }
T := p.getT(actingUser.Locale) T := p.getT(actingUser.Locale)
if !canCreateSubscription(actingUser, p.badgeAdminUserID, extra.ChannelId) { if !canCreateSubscription(actingUser, p.badgeAdminUserIDs, extra.ChannelId) {
return commandError(T("badges.error.cannot_create_subscription", "Вы не можете создавать подписки")) return commandError(T("badges.error.cannot_create_subscription", "Вы не можете создавать подписки"))
} }
@ -818,7 +818,7 @@ func (p *Plugin) runDeleteSubscription(args []string, extra *model.CommandArgs)
} }
T := p.getT(actingUser.Locale) T := p.getT(actingUser.Locale)
if !canCreateSubscription(actingUser, p.badgeAdminUserID, extra.ChannelId) { if !canCreateSubscription(actingUser, p.badgeAdminUserIDs, extra.ChannelId) {
return commandError(T("badges.error.cannot_create_subscription", "Вы не можете создавать подписки")) return commandError(T("badges.error.cannot_create_subscription", "Вы не можете создавать подписки"))
} }

View File

@ -2,6 +2,7 @@ package main
import ( import (
"reflect" "reflect"
"strings"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -78,13 +79,20 @@ func (p *Plugin) OnConfigurationChange() error {
return errors.Wrap(err, "failed to load plugin configuration") return errors.Wrap(err, "failed to load plugin configuration")
} }
p.badgeAdminUserID = "" p.badgeAdminUserIDs = make(map[string]bool)
if configuration.BadgesAdmin != "" { if configuration.BadgesAdmin != "" {
u, err := p.API.GetUserByUsername(configuration.BadgesAdmin) for username := range strings.SplitSeq(configuration.BadgesAdmin, ",") {
if err != nil { username = strings.TrimSpace(username)
return errors.Wrap(err, "cannot get badge admin user") if username == "" {
continue
}
u, err := p.API.GetUserByUsername(username)
if err != nil {
p.API.LogWarn("Cannot find badge admin user", "username", username, "error", err.Error())
continue
}
p.badgeAdminUserIDs[u.Id] = true
} }
p.badgeAdminUserID = u.Id
} }
p.setConfiguration(configuration) p.setConfiguration(configuration)

View File

@ -28,7 +28,7 @@ type Plugin struct {
BotUserID string BotUserID string
store Store store Store
router *mux.Router router *mux.Router
badgeAdminUserID string badgeAdminUserIDs map[string]bool
i18nBundle *i18n.Bundle i18nBundle *i18n.Bundle
} }

View File

@ -23,7 +23,7 @@ func (p *Plugin) filterGrantBadges(user *model.User) ([]*badgesmodel.Badge, erro
p.mm.Log.Debug("Badge with missing type", "badge", b) p.mm.Log.Debug("Badge with missing type", "badge", b)
continue continue
} }
if canGrantBadge(user, p.badgeAdminUserID, b, badgeType) { if canGrantBadge(user, p.badgeAdminUserIDs, b, badgeType) {
out = append(out, b) out = append(out, b)
} }
} }
@ -39,7 +39,7 @@ func (p *Plugin) filterCreateBadgeTypes(user *model.User) (badgesmodel.BadgeType
out := badgesmodel.BadgeTypeList{} out := badgesmodel.BadgeTypeList{}
for _, t := range types { for _, t := range types {
if canCreateBadge(user, p.badgeAdminUserID, t) { if canCreateBadge(user, p.badgeAdminUserIDs, t) {
out = append(out, t) out = append(out, t)
} }
} }
@ -55,7 +55,7 @@ func (p *Plugin) filterEditTypes(user *model.User) (badgesmodel.BadgeTypeList, e
out := badgesmodel.BadgeTypeList{} out := badgesmodel.BadgeTypeList{}
for _, t := range types { for _, t := range types {
if canEditType(user, p.badgeAdminUserID, t) { if canEditType(user, p.badgeAdminUserIDs, t) {
out = append(out, t) out = append(out, t)
} }
} }
@ -71,7 +71,7 @@ func (p *Plugin) filterEditBadges(user *model.User) ([]*badgesmodel.Badge, error
out := []*badgesmodel.Badge{} out := []*badgesmodel.Badge{}
for _, b := range bb { for _, b := range bb {
if canEditBadge(user, p.badgeAdminUserID, b) { if canEditBadge(user, p.badgeAdminUserIDs, b) {
out = append(out, b) out = append(out, b)
} }
} }

View File

@ -23,8 +23,8 @@ func areRolesAllowed(userRoles []string, allowedRoles map[string]bool) bool {
return false return false
} }
func canGrantBadge(user *model.User, badgeAdminID string, badge *badgesmodel.Badge, badgeType *badgesmodel.BadgeTypeDefinition) bool { func canGrantBadge(user *model.User, badgeAdminIDs map[string]bool, badge *badgesmodel.Badge, badgeType *badgesmodel.BadgeTypeDefinition) bool {
if badgeAdminID != "" && user.Id == badgeAdminID { if badgeAdminIDs[user.Id] {
return true return true
} }
@ -57,8 +57,8 @@ func canGrantBadge(user *model.User, badgeAdminID string, badge *badgesmodel.Bad
return badgeType.CanGrant.Everyone return badgeType.CanGrant.Everyone
} }
func canCreateBadge(user *model.User, badgeAdminID string, badgeType *badgesmodel.BadgeTypeDefinition) bool { func canCreateBadge(user *model.User, badgeAdminIDs map[string]bool, badgeType *badgesmodel.BadgeTypeDefinition) bool {
if badgeAdminID != "" && user.Id == badgeAdminID { if badgeAdminIDs[user.Id] {
return true return true
} }
@ -87,36 +87,36 @@ func canCreateBadge(user *model.User, badgeAdminID string, badgeType *badgesmode
return badgeType.CanCreate.Everyone return badgeType.CanCreate.Everyone
} }
func canEditType(user *model.User, badgeAdminID string, badgeType *badgesmodel.BadgeTypeDefinition) bool { func canEditType(user *model.User, badgeAdminIDs map[string]bool, badgeType *badgesmodel.BadgeTypeDefinition) bool {
if badgeAdminID != "" && user.Id == badgeAdminID { if badgeAdminIDs[user.Id] {
return true return true
} }
return user.IsSystemAdmin() return user.IsSystemAdmin()
} }
func canEditBadge(user *model.User, badgeAdminID string, badge *badgesmodel.Badge) bool { func canEditBadge(user *model.User, badgeAdminIDs map[string]bool, badge *badgesmodel.Badge) bool {
if badgeAdminID != "" && user.Id == badgeAdminID { if badgeAdminIDs[user.Id] {
return true return true
} }
return user.IsSystemAdmin() || user.Id == badge.CreatedBy return user.IsSystemAdmin() || user.Id == badge.CreatedBy
} }
func canCreateType(user *model.User, badgeAdminID string, isPlugin bool) bool { func canCreateType(user *model.User, badgeAdminIDs map[string]bool, isPlugin bool) bool {
if isPlugin { if isPlugin {
return true return true
} }
if badgeAdminID != "" && user.Id == badgeAdminID { if badgeAdminIDs[user.Id] {
return true return true
} }
return user.IsSystemAdmin() return user.IsSystemAdmin()
} }
func canCreateSubscription(user *model.User, badgeAdminID string, channelID string) bool { func canCreateSubscription(user *model.User, badgeAdminIDs map[string]bool, channelID string) bool {
if badgeAdminID != "" && user.Id == badgeAdminID { if badgeAdminIDs[user.Id] {
return true return true
} }