only lower id roles can be modfified, add new hub endpoints

This commit is contained in:
2026-05-01 14:28:07 +02:00
parent dc097ad52a
commit 7c4f326410
4 changed files with 227 additions and 55 deletions
+4
View File
@@ -73,3 +73,7 @@ func StringToUuidSlice(uuidStr string) ([]uuid.UUID, error) {
func StringToTimestamp(str string) (time.Time, error) {
return time.Parse(time.RFC3339, str)
}
func StringToBool(str string) bool {
return strings.ToLower(str) == "true"
}
+187 -52
View File
@@ -3,13 +3,14 @@ package httpRequest
import (
"context"
"encoding/json"
"go-socket/packages/convertions"
"maps"
"net/http"
"slices"
"strings"
"time"
"go-socket/packages/convertions"
"go-socket/packages/cache"
"go-socket/packages/types"
"go-socket/packages/wsServer"
@@ -17,7 +18,7 @@ import (
"github.com/google/uuid"
)
func haveHubUserPermission(u *types.HubUser, h *types.Hub, needed types.Permissions) bool {
func haveHubUserPermission(u *types.HubUser, h *types.Hub, needed types.Permissions) (id uint8, has bool) {
h.Mu.Lock()
defer h.Mu.Unlock()
for _, role := range h.Roles {
@@ -27,11 +28,30 @@ func haveHubUserPermission(u *types.HubUser, h *types.Hub, needed types.Permissi
if !u.Roles.Has(role.Id) {
continue
}
if (needed & role.Permissions) == needed {
return true
if (needed&role.Permissions) == needed && role.Id > id {
id = role.Id
has = true
}
}
return false
return id, has
}
func hubUserHighestRoleId(u *types.HubUser, h *types.Hub) (id uint8) {
h.Mu.Lock()
defer h.Mu.Unlock()
for _, role := range h.Roles {
if role == nil {
continue
}
if !u.Roles.Has(role.Id) {
continue
}
if role.Id > id {
id = role.Id
}
}
return id
}
func haveHubUserCachedPermissions(needed types.CachedUserPermissions, user *types.HubUser, channel *types.HubChannel) bool {
@@ -222,24 +242,27 @@ func HandleGetHubs(response http.ResponseWriter, request *http.Request) {
response.Write(converted)
}
func hubPermissionContext(response http.ResponseWriter, request *http.Request, rt bodyLimit, needed types.Permissions) (*types.HubUser, *types.Hub, context.Context, bool) {
func hubPermissionContext(response http.ResponseWriter, request *http.Request, rt bodyLimit, needed types.Permissions) (*types.HubUser, *types.Hub, context.Context, uint8, bool) {
if !validCheckWithResponseOnFail(response, request, rt) {
return nil, nil, nil, false
return nil, nil, nil, 0, false
}
ctx := request.Context()
_, hubUser, hub, err := getHubUserIfValidWithResponseOnFail(ctx, response, request)
if err != nil {
return nil, nil, nil, false
return nil, nil, nil, 0, false
}
if !haveHubUserPermission(hubUser, hub, needed) {
roleId, ok := haveHubUserPermission(hubUser, hub, needed)
if !ok {
http.Error(response, "", http.StatusForbidden)
return nil, nil, nil, false
return nil, nil, nil, 0, false
}
return hubUser, hub, ctx, true
return hubUser, hub, ctx, roleId, true
}
func HandleHubSetName(response http.ResponseWriter, request *http.Request) {
_, hub, _, ok := hubPermissionContext(response, request, normal, types.PermissionSetHubName)
_, hub, _, _, ok := hubPermissionContext(response, request, normal, types.PermissionSetHubName)
if !ok {
return
}
@@ -253,7 +276,7 @@ func HandleHubSetName(response http.ResponseWriter, request *http.Request) {
}
func HandleHubSetColor(response http.ResponseWriter, request *http.Request) {
_, hub, _, ok := hubPermissionContext(response, request, normal, types.PermissionSetHubColor)
_, hub, _, _, ok := hubPermissionContext(response, request, normal, types.PermissionSetHubColor)
if !ok {
return
}
@@ -267,7 +290,7 @@ func HandleHubSetColor(response http.ResponseWriter, request *http.Request) {
}
func HandleHubRemove(response http.ResponseWriter, request *http.Request) {
_, hub, _, ok := hubPermissionContext(response, request, normal, types.PermissionRemoveHub)
_, hub, _, _, ok := hubPermissionContext(response, request, normal, types.PermissionRemoveHub)
if !ok {
return
}
@@ -276,7 +299,7 @@ func HandleHubRemove(response http.ResponseWriter, request *http.Request) {
}
func HandleHubToggleUserColorAllowed(response http.ResponseWriter, request *http.Request) {
_, hub, _, ok := hubPermissionContext(response, request, normal, types.PermissionSetUserColorAllowed)
_, hub, _, _, ok := hubPermissionContext(response, request, normal, types.PermissionSetUserColorAllowed)
if !ok {
return
}
@@ -285,7 +308,7 @@ func HandleHubToggleUserColorAllowed(response http.ResponseWriter, request *http
}
func HandleHubUserRemove(response http.ResponseWriter, request *http.Request) {
_, hub, _, ok := hubPermissionContext(response, request, normal, types.PermissionRemoveUser)
_, hub, _, usedRoleId, ok := hubPermissionContext(response, request, normal, types.PermissionRemoveUser)
if !ok {
return
}
@@ -294,19 +317,27 @@ func HandleHubUserRemove(response http.ResponseWriter, request *http.Request) {
http.Error(response, "bad targetid", http.StatusBadRequest)
return
}
hub.Mu.Lock()
if _, exists := hub.Users[targetId]; !exists {
hub.Mu.RLock()
target, exists := hub.Users[targetId]
if !exists {
hub.Mu.Unlock()
http.Error(response, "user not found", http.StatusNotFound)
return
}
hub.Mu.RUnlock()
if usedRoleId <= hubUserHighestRoleId(target, hub) {
http.Error(response, "target higher in hierarchy", http.StatusForbidden)
return
}
hub.Mu.Lock()
delete(hub.Users, targetId)
hub.Mu.Unlock()
response.WriteHeader(http.StatusAccepted)
}
func HandleHubRenameUser(response http.ResponseWriter, request *http.Request) {
_, hub, _, ok := hubPermissionContext(response, request, normal, types.PermissionRenameUser)
_, hub, _, usedRoleId, ok := hubPermissionContext(response, request, normal, types.PermissionRenameUser)
if !ok {
return
}
@@ -317,10 +348,16 @@ func HandleHubRenameUser(response http.ResponseWriter, request *http.Request) {
return
}
hub.Mu.RLock()
target, ok := hub.Users[targetId]
target, exists := hub.Users[targetId]
if !exists {
hub.Mu.Unlock()
http.Error(response, "user not found", http.StatusNotFound)
return
}
hub.Mu.RUnlock()
if !ok {
http.Error(response, "no users found", http.StatusNotFound)
if usedRoleId <= hubUserHighestRoleId(target, hub) {
http.Error(response, "target higher in hierarchy", http.StatusForbidden)
return
}
target.Name = newName
@@ -329,16 +366,16 @@ func HandleHubRenameUser(response http.ResponseWriter, request *http.Request) {
}
func HandleHubRenameSelf(response http.ResponseWriter, request *http.Request) {
hubUser, _, _, ok := hubPermissionContext(response, request, normal, types.PermissionSelfRename)
requestor, _, _, _, ok := hubPermissionContext(response, request, normal, types.PermissionSelfRename)
if !ok {
return
}
hubUser.Name = request.FormValue("newname")
requestor.Name = request.FormValue("newname")
response.WriteHeader(http.StatusAccepted)
}
func HandleHubToggleMuteUser(response http.ResponseWriter, request *http.Request) {
_, hub, _, ok := hubPermissionContext(response, request, normal, types.PermissionMuteUser)
_, hub, _, usedRoleId, ok := hubPermissionContext(response, request, normal, types.PermissionMuteUser)
if !ok {
return
}
@@ -348,18 +385,25 @@ func HandleHubToggleMuteUser(response http.ResponseWriter, request *http.Request
return
}
hub.Mu.RLock()
target, ok := hub.Users[targetId]
hub.Mu.RUnlock()
if !ok {
target, exists := hub.Users[targetId]
if !exists {
hub.Mu.Unlock()
http.Error(response, "user not found", http.StatusNotFound)
return
}
hub.Mu.RUnlock()
if usedRoleId <= hubUserHighestRoleId(target, hub) {
http.Error(response, "target higher in hierarchy", http.StatusForbidden)
return
}
hub.Mu.RLock()
target.IsMuted = !target.IsMuted
response.WriteHeader(http.StatusAccepted)
}
func HandleHubUserAddRole(response http.ResponseWriter, request *http.Request) {
_, hub, _, ok := hubPermissionContext(response, request, normal, types.PermissionUserAddRole)
_, hub, _, usedRoleId, ok := hubPermissionContext(response, request, normal, types.PermissionUserAddRole)
if !ok {
return
}
@@ -373,6 +417,11 @@ func HandleHubUserAddRole(response http.ResponseWriter, request *http.Request) {
http.Error(response, "bad roleid", http.StatusBadRequest)
return
}
if roleId > usedRoleId {
http.Error(response, "target role higher in hierarchy", http.StatusForbidden)
return
}
hub.Mu.RLock()
target, ok := hub.Users[targetId]
role := hub.Roles[roleId]
@@ -390,7 +439,7 @@ func HandleHubUserAddRole(response http.ResponseWriter, request *http.Request) {
}
func HandleHubUserRemoveRole(response http.ResponseWriter, request *http.Request) {
_, hub, _, ok := hubPermissionContext(response, request, normal, types.PermissionUserRemoveRole)
_, hub, _, usedRoleId, ok := hubPermissionContext(response, request, normal, types.PermissionUserRemoveRole)
if !ok {
return
}
@@ -404,6 +453,11 @@ func HandleHubUserRemoveRole(response http.ResponseWriter, request *http.Request
http.Error(response, "bad roleid", http.StatusBadRequest)
return
}
if roleId > usedRoleId {
http.Error(response, "target role higher in hierarchy", http.StatusForbidden)
return
}
hub.Mu.RLock()
target, ok := hub.Users[targetId]
hub.Mu.RUnlock()
@@ -416,7 +470,7 @@ func HandleHubUserRemoveRole(response http.ResponseWriter, request *http.Request
}
func HandleHubCreateRole(response http.ResponseWriter, request *http.Request) {
_, hub, _, ok := hubPermissionContext(response, request, normal, types.PermissionCreateRole)
_, hub, _, usedRoleId, ok := hubPermissionContext(response, request, normal, types.PermissionCreateRole)
if !ok {
return
}
@@ -429,7 +483,7 @@ func HandleHubCreateRole(response http.ResponseWriter, request *http.Request) {
var freeId uint8
found := false
for i := uint8(1); i < types.HubBoundRolesMax; i++ {
if hub.Roles[i] == nil {
if hub.Roles[i] == nil && usedRoleId > i {
freeId = i
found = true
break
@@ -451,32 +505,37 @@ func HandleHubCreateRole(response http.ResponseWriter, request *http.Request) {
}
func HandleHubRemoveRole(response http.ResponseWriter, request *http.Request) {
_, hub, _, ok := hubPermissionContext(response, request, normal, types.PermissionRemoveRole)
_, hub, _, usedRoleId, ok := hubPermissionContext(response, request, normal, types.PermissionRemoveRole)
if !ok {
return
}
roleId, err := convertions.StringToUint8(request.FormValue("roleid"))
targetRoleId, err := convertions.StringToUint8(request.FormValue("roleid"))
if err != nil {
http.Error(response, "bad roleid", http.StatusBadRequest)
return
}
if roleId == 0 || roleId == types.HubBoundRolesMax {
http.Error(response, "cannot remove system role", http.StatusForbidden)
if targetRoleId == 0 {
http.Error(response, "cannot remove root role", http.StatusForbidden)
return
}
if targetRoleId > usedRoleId {
http.Error(response, "target role higher in hierarchy", http.StatusForbidden)
return
}
hub.Mu.Lock()
if hub.Roles[roleId] == nil {
if hub.Roles[targetRoleId] == nil {
hub.Mu.Unlock()
http.Error(response, "role not found", http.StatusNotFound)
return
}
hub.Roles[roleId] = nil
hub.Roles[targetRoleId] = nil
hub.Mu.Unlock()
response.WriteHeader(http.StatusAccepted)
}
func HandleHubSelfRoleRemove(response http.ResponseWriter, request *http.Request) {
hubUser, _, _, ok := hubPermissionContext(response, request, normal, types.PermissionSelfRoleRemove)
func HandlePermissionSetRoleName(response http.ResponseWriter, request *http.Request) {
_, hub, _, usedRoleId, ok := hubPermissionContext(response, request, normal, types.PermissionSetRoleName)
if !ok {
return
}
@@ -485,18 +544,8 @@ func HandleHubSelfRoleRemove(response http.ResponseWriter, request *http.Request
http.Error(response, "bad roleid", http.StatusBadRequest)
return
}
hubUser.Roles.Remove(roleId)
response.WriteHeader(http.StatusAccepted)
}
func PermissionSetRoleName(response http.ResponseWriter, request *http.Request) {
_, hub, _, ok := hubPermissionContext(response, request, normal, types.PermissionSetRoleName)
if !ok {
return
}
roleId, err := convertions.StringToUint8(request.FormValue("roleid"))
if err != nil {
http.Error(response, "bad roleid", http.StatusBadRequest)
if roleId > usedRoleId {
http.Error(response, "target role higher in hierarchy", http.StatusForbidden)
return
}
newName := request.FormValue("newname")
@@ -515,3 +564,89 @@ func PermissionSetRoleName(response http.ResponseWriter, request *http.Request)
hub.Mu.Unlock()
response.WriteHeader(http.StatusAccepted)
}
func HandlePermissionSetRoleColor(response http.ResponseWriter, request *http.Request) {
_, hub, _, usedRoleId, ok := hubPermissionContext(response, request, normal, types.PermissionSetRoleColor)
if !ok {
return
}
roleId, err := convertions.StringToUint8(request.FormValue("roleid"))
if err != nil {
http.Error(response, "bad roleid", http.StatusBadRequest)
return
}
if roleId > usedRoleId {
http.Error(response, "target role higher in hierarchy", http.StatusForbidden)
return
}
color, err := convertions.StringToRgba(request.FormValue("newcolor"))
if err != nil {
http.Error(response, "invalid newcolor", http.StatusBadRequest)
return
}
hub.Mu.Lock()
role := hub.Roles[roleId]
if role == nil {
http.Error(response, "no such role", http.StatusNotFound)
return
}
role.Color = color
hub.Mu.Unlock()
response.WriteHeader(http.StatusAccepted)
}
func HandlePermissionSetRolePermissions(response http.ResponseWriter, request *http.Request) {
requestor, hub, _, usedRoleId, ok := hubPermissionContext(response, request, normal, types.PermissionSetRolePermissions)
if !ok {
return
}
targetRoleId, err := convertions.StringToUint8(request.FormValue("roleid"))
if err != nil {
http.Error(response, "bad roleid", http.StatusBadRequest)
return
}
if targetRoleId > usedRoleId {
http.Error(response, "target role higher in hierarchy", http.StatusForbidden)
return
}
hub.Mu.Lock()
targetRole := hub.Roles[targetRoleId]
hub.Mu.Unlock()
if targetRole == nil {
http.Error(response, "no such role", http.StatusNotFound)
return
}
query := request.URL.Query()
for key, values := range query {
permission, ok := types.PermissionLookup(key)
if !ok {
continue
}
permRoleId, ok := haveHubUserPermission(requestor, hub, permission)
if !ok {
http.Error(response, "permission needed: "+key, http.StatusNotFound)
}
if permRoleId <= targetRoleId {
http.Error(response, "target role higher in hierarchy", http.StatusForbidden)
return
}
if convertions.StringToBool(values[0]) {
targetRole.Permissions.Grant(permission)
} else {
targetRole.Permissions.Revoke(permission)
}
}
response.WriteHeader(http.StatusAccepted)
}
func HandlePermissionSelfRoleRemove(response http.ResponseWriter, request *http.Request) {
requestor, _, _, usedRoleId, ok := hubPermissionContext(response, request, normal, types.PermissionUserSelfRoleRemove)
if !ok {
return
}
requestor.Roles.Remove(usedRoleId)
response.WriteHeader(http.StatusAccepted)
}
+35 -3
View File
@@ -157,6 +157,7 @@ const (
PermissionMuteUser
PermissionUserAddRole
PermissionUserRemoveRole
PermissionUserSelfRoleRemove
// Role permissions
PermissionCreateRole
@@ -165,9 +166,6 @@ const (
PermissionSetRoleColor
PermissionSetRolePermissions
PermissionSelfRoleRemove
PermissionOnlySelfRoleRemove
// Channel permissions
PermissionAddChannel
PermissionRemoveChannel
@@ -178,6 +176,40 @@ const (
PermissionSetChannelPermittedReadHistoryRoles
)
var permissionRegistry = map[string]Permissions{
"set_hub_name": PermissionSetHubName,
"set_hub_icon": PermissionSetHubIcon,
"set_hub_bg": PermissionSetHubBg,
"set_hub_color": PermissionSetHubColor,
"remove_hub": PermissionRemoveHub,
"set_user_color_allowed": PermissionSetUserColorAllowed,
"invite_user": PermissionInviteUser,
"remove_user": PermissionRemoveUser,
"rename_user": PermissionRenameUser,
"self_rename": PermissionSelfRename,
"mute_user": PermissionMuteUser,
"user_add_role": PermissionUserAddRole,
"user_remove_role": PermissionUserRemoveRole,
"create_role": PermissionCreateRole,
"remove_role": PermissionRemoveRole,
"set_role_name": PermissionSetRoleName,
"set_role_color": PermissionSetRoleColor,
"set_role_permissions": PermissionSetRolePermissions,
"self_role_remove": PermissionUserSelfRoleRemove,
"add_channel": PermissionAddChannel,
"remove_channel": PermissionRemoveChannel,
"set_channel_name": PermissionSetChannelName,
"set_channel_icon": PermissionSetChannelIcon,
"set_channel_permitted_visible": PermissionSetChannelPermittedVisibleRoles,
"set_channel_permitted_send": PermissionSetChannelPermittedSendMessageRoles,
"set_channel_permitted_read": PermissionSetChannelPermittedReadHistoryRoles,
}
func PermissionLookup(permission string) (Permissions, bool) {
p, ok := permissionRegistry[permission]
return p, ok
}
func (p *Permissions) Grant(permissions Permissions) {
*p |= permissions
}
+1
View File
@@ -3,6 +3,7 @@ when user not ws connected collect count of unread messages for each conn (add d
add hubs
setting avatar and profilebg
check when mutex needed
change api endpoints and body/url/header to follow same case
user banners