From 909d222a894903055b6c309c4b521487951dabf1 Mon Sep 17 00:00:00 2001 From: Sisi Date: Wed, 29 Apr 2026 19:32:26 +0200 Subject: [PATCH] rework of hubs --- packages/httpRequest/get.go | 18 +- packages/httpRequest/helper.go | 6 +- packages/httpRequest/hubs.go | 338 ++++++--------------------------- packages/httpRequest/user.go | 4 +- packages/types/types.go | 48 ++--- 5 files changed, 80 insertions(+), 334 deletions(-) diff --git a/packages/httpRequest/get.go b/packages/httpRequest/get.go index 93f492a..0067b2b 100644 --- a/packages/httpRequest/get.go +++ b/packages/httpRequest/get.go @@ -65,20 +65,20 @@ func getHubByIdStr(ctx context.Context, hubId string) (*types.Hub, error) { return hub, nil } -func getHubUserIfValidWithResponseOnFail(ctx context.Context, response http.ResponseWriter, token string, hubId string) ( +func getHubUserIfValidWithResponseOnFail(ctx context.Context, response http.ResponseWriter, request *http.Request) ( *types.User, *types.HubUser, *types.Hub, error) { - hub, err := getHubByIdStr(ctx, hubId) - if err != nil { - http.Error(response, "invalid hubid", http.StatusBadRequest) - return nil, nil, nil, errors.New("no such hub") - } - - user, err := getUserByToken(ctx, token) + user, err := getUserByToken(ctx, request.Header.Get("token")) if err != nil { http.Error(response, "invalid token", http.StatusBadRequest) return nil, nil, nil, errors.New("invalid token") } + hub, err := getHubByIdStr(ctx, request.Header.Get("hubid")) + if err != nil { + http.Error(response, "invalid hubid", http.StatusBadRequest) + return nil, nil, nil, errors.New("no such hub") + } + hub.Mu.RLock() hubUser, ok := hub.Users[user.Id] hub.Mu.RUnlock() @@ -103,7 +103,7 @@ func getHubChannelIfValidWithResponseOnFail(ctx context.Context, response http.R return nil, errors.New("invalid channelid") } - if !haveHubUserPermissionsOnChannel(types.CachedUserCanView, hubUser, channel) { + if !haveHubUserCachedPermissions(types.CachedUserCanView, hubUser, channel) { return nil, errors.New("invalid channelid") } diff --git a/packages/httpRequest/helper.go b/packages/httpRequest/helper.go index 415bed6..8a58994 100644 --- a/packages/httpRequest/helper.go +++ b/packages/httpRequest/helper.go @@ -8,16 +8,16 @@ import ( "go-socket/packages/config" ) -type postType uint8 +type requestType uint8 const ( - normal postType = iota + normal requestType = iota file avatar profileBg ) -func validCheckWithResponseOnFail(response *http.ResponseWriter, request *http.Request, pt postType) bool { +func validCheckWithResponseOnFail(response *http.ResponseWriter, request *http.Request, pt requestType) bool { var maxSize int64 switch pt { case file: diff --git a/packages/httpRequest/hubs.go b/packages/httpRequest/hubs.go index 178bf54..fe4c98b 100644 --- a/packages/httpRequest/hubs.go +++ b/packages/httpRequest/hubs.go @@ -2,7 +2,6 @@ package httpRequest import ( "encoding/json" - "go-socket/packages/convertions" "maps" "net/http" "slices" @@ -16,7 +15,24 @@ import ( "github.com/google/uuid" ) -func haveHubUserPermissionsOnChannel(needed types.CachedUserPermissions, user *types.HubUser, channel *types.HubChannel) bool { +func haveHubUserPermission(u *types.HubUser, h *types.Hub, needed types.Permissions) bool { + h.Mu.Lock() + defer h.Mu.Unlock() + for _, role := range h.Roles { + if role == nil { + continue + } + if !u.Roles.Has(role.Id) { + continue + } + if (needed & role.Permissions) == needed { + return true + } + } + return false +} + +func haveHubUserCachedPermissions(needed types.CachedUserPermissions, user *types.HubUser, channel *types.HubChannel) bool { channel.Mu.RLock() checkAgainst, ok := channel.UsersCachedPermissions[user.OriginalId] channel.Mu.RUnlock() @@ -26,61 +42,6 @@ func haveHubUserPermissionsOnChannel(needed types.CachedUserPermissions, user *t return true } -func haveUserPermissions(needed types.Permissions, scope uint8, hu *types.HubUser, h *types.Hub) bool { - h.Mu.RLock() - defer h.Mu.RUnlock() - - for _, role := range h.Roles { - if role == nil { - continue - } - if scope != 0 && role.BoundedGroup != scope { - continue - } - if !hu.Roles.Has(role.Id) { - continue - } - if (needed & role.Permissions) != needed { - return true - } - } - return true -} - -func addHubUserToPermissionCache(hub *types.Hub, user *types.HubUser) { - user.Mu.RLock() - roles := user.Roles - userId := user.OriginalId - user.Mu.RUnlock() - - for _, group := range hub.Groups { - if group == nil { - continue - } - if !roles.HasSameId(group.RolesCanView) { - continue - } - group.UsersCachedPermissions[userId] = types.CachedUserCanView - - for _, channel := range group.Channels { - var perms types.CachedUserPermissions - - if roles.HasSameId(channel.RolesCanView) { - perms |= types.CachedUserCanView - if roles.HasSameId(channel.RolesCanReadHistory) { - perms |= types.CachedUserCanReadHistory - } - if roles.HasSameId(channel.RolesCanMessage) { - perms |= types.CachedUserCanMessage - } - } - channel.Mu.Lock() - channel.UsersCachedPermissions[userId] = perms - channel.Mu.Unlock() - } - } -} - func HandleHubCreate(response http.ResponseWriter, request *http.Request) { if !validCheckWithResponseOnFail(&response, request, normal) { return @@ -132,31 +93,20 @@ func HandleHubCreate(response http.ResponseWriter, request *http.Request) { hub.Roles[memberRole.Id] = memberRole creator.Roles.Add(memberRole.Id) - rootGroup := types.NewHubGroup() - rootGroup.Name = "root" - rootGroup.Id = uint8(1) - rootGroup.Color = types.Rgba{}.GetRandom() - rootGroup.CreatedAt = hub.CreatedAt - rootGroup.RolesCanView.Add(rootRole.Id) - rootGroup.RolesCanView.Add(memberRole.Id) - hub.Groups[rootGroup.Id] = rootGroup - channel := types.NewHubChannel() channel.Name = "main channel" channel.Position = uint8(0) channel.Id = uuid.New() - channel.ParentId = rootGroup.Id channel.Description = "The fist channel!" channel.CreatedAt = hub.CreatedAt - channel.RolesCanMessage.Add(rootGroup.Id) + channel.RolesCanMessage.Add(rootRole.Id) channel.RolesCanMessage.Add(memberRole.Id) - channel.RolesCanView.Add(rootGroup.Id) + channel.RolesCanView.Add(rootRole.Id) channel.RolesCanView.Add(memberRole.Id) - channel.RolesCanReadHistory.Add(rootGroup.Id) + channel.RolesCanReadHistory.Add(rootRole.Id) channel.RolesCanReadHistory.Add(memberRole.Id) channel.UsersCachedPermissions[creator.OriginalId] = types.CachedUserPermissionsAll hub.Channels[channel.Id] = channel - rootGroup.Channels[channel.Id] = channel cache.SaveHub(hub) } @@ -182,7 +132,6 @@ func HandleHubJoin(response http.ResponseWriter, request *http.Request) { hubUser.Roles.Add(hub.JoinRole.Id) hubUser.CreatedAt = time.Now() hub.Users[hubUser.OriginalId] = hubUser - addHubUserToPermissionCache(hub, hubUser) } func HandleHubMessage(response http.ResponseWriter, request *http.Request) { @@ -190,7 +139,7 @@ func HandleHubMessage(response http.ResponseWriter, request *http.Request) { return } ctx := request.Context() - user, hubUser, hub, err := getHubUserIfValidWithResponseOnFail(ctx, response, request.Header.Get("token"), request.FormValue("hubid")) + user, hubUser, hub, err := getHubUserIfValidWithResponseOnFail(ctx, response, request) if err != nil { return } @@ -199,7 +148,7 @@ func HandleHubMessage(response http.ResponseWriter, request *http.Request) { return } - if hubUser.IsGlobalMuted { + if hubUser.IsMuted { http.Error(response, "muted", http.StatusForbidden) } @@ -269,215 +218,38 @@ func HandleGetHubs(response http.ResponseWriter, request *http.Request) { response.Write(converted) } +func HandleHubPermissionActionCore( + response http.ResponseWriter, request *http.Request, + takenVarName string, rt requestType, needed types.Permissions, + performedAction func(usr *types.HubUser, hub *types.Hub, takenVar *string)) { + if !validCheckWithResponseOnFail(&response, request, rt) { + return + } + ctx := request.Context() + _, hubUser, hub, err := getHubUserIfValidWithResponseOnFail(ctx, response, request) + if err != nil { + return + } + + if !haveHubUserPermission(hubUser, hub, needed) { + http.Error(response, "", http.StatusForbidden) + } + + var takenVar *string = nil + + if takenVarName != "" { + takenVar = new(request.FormValue(takenVarName)) + } + performedAction(hubUser, hub, takenVar) +} + func HandleHubSetName(response http.ResponseWriter, request *http.Request) { - if !validCheckWithResponseOnFail(&response, request, normal) { - return - } - ctx := request.Context() - _, hubUser, hub, err := getHubUserIfValidWithResponseOnFail(ctx, response, request.Header.Get("token"), request.FormValue("hubid")) - if err != nil { - return - } - - newName := request.FormValue("name") - if newName == "" { - http.Error(response, "empty name", http.StatusBadRequest) - return - } - if !haveUserPermissions(types.PermissionSetHubName, 0, hubUser, hub) { - http.Error(response, "no permission", http.StatusForbidden) - return - } - - hub.Name = newName - response.WriteHeader(http.StatusAccepted) -} - -func HandleHubSetColor(response http.ResponseWriter, request *http.Request) { - if !validCheckWithResponseOnFail(&response, request, normal) { - return - } - ctx := request.Context() - _, hubUser, hub, err := getHubUserIfValidWithResponseOnFail(ctx, response, request.Header.Get("token"), request.FormValue("hubid")) - if err != nil { - return - } - - newColor, err := convertions.StringToRgba(request.FormValue("color")) - if err != nil { - http.Error(response, "bad color", http.StatusBadRequest) - return - } - if !haveUserPermissions(types.PermissionSetHubColor, 0, hubUser, hub) { - http.Error(response, "no permission", http.StatusForbidden) - return - } - - hub.Color = newColor - response.WriteHeader(http.StatusAccepted) -} - -func HandleHubRemove(response http.ResponseWriter, request *http.Request) { - if !validCheckWithResponseOnFail(&response, request, normal) { - return - } - ctx := request.Context() - _, hubUser, hub, err := getHubUserIfValidWithResponseOnFail(ctx, response, request.Header.Get("token"), request.FormValue("hubid")) - if err != nil { - return - } - - if !haveUserPermissions(types.PermissionRemoveHub, 0, hubUser, hub) { - http.Error(response, "no permission", http.StatusForbidden) - return - } - cache.DeleteHub(hub) -} - -func HandleHubSetAllowUserColor(response http.ResponseWriter, request *http.Request) { - if !validCheckWithResponseOnFail(&response, request, normal) { - return - } - ctx := request.Context() - _, hubUser, hub, err := getHubUserIfValidWithResponseOnFail(ctx, response, request.Header.Get("token"), request.FormValue("hubid")) - if err != nil { - return - } - - if !haveUserPermissions(types.PermissionSetUserColorAllowed, 0, hubUser, hub) { - http.Error(response, "no permission", http.StatusForbidden) - return - } - cache.DeleteHub(hub) -} - -func HandleHubRemoveUser(response http.ResponseWriter, request *http.Request) { - if !validCheckWithResponseOnFail(&response, request, normal) { - return - } - ctx := request.Context() - _, hubUser, hub, err := getHubUserIfValidWithResponseOnFail(ctx, response, request.Header.Get("token"), request.FormValue("hubid")) - if err != nil { - return - } - - targetId, err := convertions.StringToUuid(request.FormValue("target")) - if err != nil { - http.Error(response, "invalid targetid", http.StatusBadRequest) - return - } - hub.Mu.RLock() - _, ok := hub.Users[targetId] - if !ok { - http.Error(response, "target not found", http.StatusNotFound) - return - } - - if !haveUserPermissions(types.PermissionRemoveUser, 0, hubUser, hub) { - http.Error(response, "no permission", http.StatusForbidden) - return - } - - hub.Mu.Lock() - delete(hub.Users, targetId) - hub.Mu.Unlock() - response.WriteHeader(http.StatusAccepted) -} - -func HandleHubMuteUserToggle(response http.ResponseWriter, request *http.Request) { - if !validCheckWithResponseOnFail(&response, request, normal) { - return - } - ctx := request.Context() - _, hubUser, hub, err := getHubUserIfValidWithResponseOnFail(ctx, response, request.Header.Get("token"), request.FormValue("hubid")) - if err != nil { - return - } - - targetId, err := convertions.StringToUuid(request.FormValue("target")) - if err != nil { - http.Error(response, "invalid targetid", http.StatusBadRequest) - return - } - - scope, err := convertions.StringToUint32(request.FormValue("scope")) - if err != nil { - http.Error(response, "invalid scope (set 0 for no scope)", http.StatusBadRequest) - return - } - - hub.Mu.RLock() - target, ok := hub.Users[targetId] - hub.Mu.RUnlock() - if !ok { - http.Error(response, "target not found", http.StatusNotFound) - return - } - - if !haveUserPermissions(types.PermissionMuteUser, uint8(scope), hubUser, hub) { - http.Error(response, "no permission", http.StatusForbidden) - return - } - - if scope == 0 { - target.IsGlobalMuted = !target.IsGlobalMuted - } else { - hub.Mu.Lock() - users := hub.Groups[scope].MutedUsers - _, ok = users[targetId] - if ok { - delete(users, targetId) - } else { - users[targetId] = struct{}{} + HandleHubPermissionActionCore(response, request, "newname", normal, types.PermissionSetHubName, func(usr *types.HubUser, hub *types.Hub, takenVar *string) { + newName := *takenVar + if newName == "" { + http.Error(response, "empty name", http.StatusBadRequest) + return } - } -} - -func HandleHubMuteUserToggle(response http.ResponseWriter, request *http.Request) { - if !validCheckWithResponseOnFail(&response, request, normal) { - return - } - ctx := request.Context() - _, hubUser, hub, err := getHubUserIfValidWithResponseOnFail(ctx, response, request.Header.Get("token"), request.FormValue("hubid")) - if err != nil { - return - } - - targetId, err := convertions.StringToUuid(request.FormValue("target")) - if err != nil { - http.Error(response, "invalid targetid", http.StatusBadRequest) - return - } - - scope, err := convertions.StringToUint32(request.FormValue("scope")) - if err != nil { - http.Error(response, "invalid scope (set 0 for no scope)", http.StatusBadRequest) - return - } - - hub.Mu.RLock() - target, ok := hub.Users[targetId] - hub.Mu.RUnlock() - if !ok { - http.Error(response, "target not found", http.StatusNotFound) - return - } - - if !haveUserPermissions(types.PermissionMuteUser, uint8(scope), hubUser, hub) { - http.Error(response, "no permission", http.StatusForbidden) - return - } - - if scope == 0 { - target.IsGlobalMuted = !target.IsGlobalMuted - } else { - hub.Mu.Lock() - users := hub.Groups[scope].MutedUsers - _, ok = users[targetId] - if ok { - delete(users, targetId) - } else { - users[targetId] = struct{}{} - } - } + hub.Name = newName + }) } diff --git a/packages/httpRequest/user.go b/packages/httpRequest/user.go index a900d26..18659e7 100644 --- a/packages/httpRequest/user.go +++ b/packages/httpRequest/user.go @@ -86,8 +86,8 @@ func HandleUserNew(response http.ResponseWriter, request *http.Request) { } username := request.FormValue("username") - if len(username) < 4 { - http.Error(response, "no or short username", http.StatusBadRequest) + if len(username) < 4 || len(username) > 30 { + http.Error(response, "no or short/too long username", http.StatusBadRequest) return } diff --git a/packages/types/types.go b/packages/types/types.go index 26f6075..01aa70c 100644 --- a/packages/types/types.go +++ b/packages/types/types.go @@ -152,25 +152,18 @@ const ( PermissionAddUser PermissionRemoveUser PermissionRenameUser + PermissionSelfRename PermissionMuteUser // Role permissions PermissionAddRole PermissionRemoveRole - PermissionChangeRoleName - PermissionChangeRoleColor - PermissionChangeRoleGlobals + PermissionSetRoleName + PermissionSetRoleColor - PermissionNoSelfRoleRemove + PermissionSelfRoleRemove PermissionOnlySelfRoleRemove - // Channel group permissions - PermissionAddChannelGroup - PermissionRemoveChannelGroup - PermissionSetChannelGroupName - PermissionSetChannelGroupColor - PermissionSetChannelGroupPermittedVisibleRoles - // Channel permissions PermissionAddChannel PermissionRemoveChannel @@ -238,7 +231,6 @@ type Hub struct { CreatedAt time.Time `json:"createdAt"` Roles [256]*HubRole `json:"-"` Users map[uuid.UUID]*HubUser `json:"-"` - Groups [256]*HubGroup `json:"-"` Channels map[uuid.UUID]*HubChannel `json:"-"` Name string `json:"name"` IconUrl string `json:"iconUrl"` @@ -264,6 +256,7 @@ type HubRole struct { Color Rgba `json:"color"` Id uint8 `json:"id"` BoundedGroup uint8 `json:"boundedGroup"` // BoundedGroup 0 for global + IsDeleted bool `json:"-"` } func (h *HubRole) GrantPermission(r Permissions) { @@ -277,36 +270,18 @@ func (h *HubRole) HasPermission(r Permissions) bool { } type HubUser struct { - Mu sync.RWMutex `json:"mu"` - CreatedAt time.Time `json:"createdAt"` - Roles HubBoundRoles `json:"-"` - Name string `json:"name"` // Name empty = original name - OriginalId uuid.UUID `json:"originalId"` - IsGlobalMuted bool `json:"isGlobalMuted"` + Mu sync.RWMutex `json:"mu"` + CreatedAt time.Time `json:"createdAt"` + Roles HubBoundRoles `json:"-"` + Name string `json:"name"` // Name empty = original name + OriginalId uuid.UUID `json:"originalId"` + IsMuted bool `json:"isMuted"` } func NewHubUser() *HubUser { return &HubUser{} } -type HubGroup struct { - Name string `json:"name"` - CreatedAt time.Time `json:"createdAt"` - Color Rgba `json:"color"` - RolesCanView HubBoundRoles `json:"rolesCanView"` - UsersCachedPermissions map[uuid.UUID]CachedUserPermissions `json:"-"` - Channels map[uuid.UUID]*HubChannel `json:"-"` - MutedUsers map[uuid.UUID]struct{} `json:"-"` - Id uint8 `json:"id"` // Id 0 for unused -} - -func NewHubGroup() *HubGroup { - return &HubGroup{ - UsersCachedPermissions: make(map[uuid.UUID]CachedUserPermissions), - Channels: make(map[uuid.UUID]*HubChannel), - } -} - type HubChannel struct { Mu sync.RWMutex `json:"-"` MessagesBuff []*Message `json:"-"` @@ -320,7 +295,6 @@ type HubChannel struct { UsersCachedPermissions map[uuid.UUID]CachedUserPermissions `json:"-"` NextBuffIdx uint32 `json:"-"` Id uuid.UUID `json:"id"` - ParentId uint8 `json:"parentId"` Position uint8 `json:"position"` HaveOverflowed bool `json:"-"` }