rework of hubs

This commit is contained in:
2026-04-29 19:32:26 +02:00
parent 6378966267
commit 909d222a89
5 changed files with 80 additions and 334 deletions
+9 -9
View File
@@ -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")
}
+3 -3
View File
@@ -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:
+55 -283
View File
@@ -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
})
}
+2 -2
View File
@@ -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
}