idk how to plan this out

This commit is contained in:
gitGnome
2026-04-24 15:34:58 +02:00
parent 9cb05e7155
commit 635139aad2
6 changed files with 140 additions and 359 deletions
+58
View File
@@ -0,0 +1,58 @@
package permission
type Global uint32
const (
// Hub permissions
GlobalSetHubName Global = 1 << iota
GlobalSetHubBg
GlobalSetHubColor
GlobalRemoveHub
GlobalSetUserColorAllowed
// User permissions
GlobalAddUser
GlobalRemoveUser
GlobalRenameUser
GlobalMuteUser
// Role permissions
GlobalAddRole
GlobalRemoveRole
GlobalChangeRoleName
GlobalChangeRoleColor
GlobalChangeRoleGlobals
GlobalOnlySelfRoleRemove
// Channel group permissions
GlobalAddChannelGroup
GlobalRemoveChannelGroup
GlobalSetChannelGroupName
GlobalSetChannelGroupColor
GlobalSetChannelGroupPermittedVisibleRoles
// Channel permissions
GlobalAddChannel
GlobalRemoveChannel
GlobalSetChannelName
GlobalSetChannelPermittedVisibleRoles
GlobalSetChannelPermittedSendMessageRoles
GlobalSetChannelPermittedReadHistoryRoles
)
type ChannelGroup uint16
const (
// Channel group permission
ChannelGroupSetName ChannelGroup = 1 << iota
ChannelGroupSetColor
ChannelGroupSetPermittedVisibleRoles
// Channel permission
ChannelGroupAddChannel
ChannelGroupRemoveChannel
ChannelGroupSetChannelName
ChannelGroupSetChannelPermittedVisibleRoles
ChannelGroupSetChannelPermittedSendMessageRoles
ChannelGroupSetChannelPermittedReadHistoryRoles
)
+1 -1
View File
@@ -241,7 +241,7 @@ func HandleUserNewConnection(response http.ResponseWriter, request *http.Request
} }
requestor.Mu.RUnlock() requestor.Mu.RUnlock()
connection := types.NewConnection() connection := types.CreateConn()
connection.CreatedAt = time.Now() connection.CreatedAt = time.Now()
connection.RequestorId = requestor.Id connection.RequestorId = requestor.Id
connection.RecipientId = recipient.Id connection.RecipientId = recipient.Id
+1 -1
View File
@@ -58,7 +58,7 @@ func getHubByIdStr(ctx context.Context, hubId string) (*types.Hub, error) {
hub, ok := cache.GetHubById(hubUuid) hub, ok := cache.GetHubById(hubUuid)
if !ok { if !ok {
hub = types.NewHub() hub = types.CreateHub()
hub.Id = hubUuid hub.Id = hubUuid
if err := postgresql.GetWholeHub(ctx, hub); err != nil { if err := postgresql.GetWholeHub(ctx, hub); err != nil {
return nil, err return nil, err
+24 -14
View File
@@ -4,6 +4,7 @@ import (
"net/http" "net/http"
"time" "time"
"go-socket/packages/Enums/permission"
"go-socket/packages/cache" "go-socket/packages/cache"
"go-socket/packages/postgresql" "go-socket/packages/postgresql"
"go-socket/packages/types" "go-socket/packages/types"
@@ -11,9 +12,18 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
) )
func hasHubUserPermission(user *types.HubUser, hub *types.Hub, permission types.RolePermission) bool { func hasHubUserGlobalPermission(user *types.HubUser, hub *types.Hub, perm permission.Global) bool {
for _, roleId := range user.Roles { for _, roleId := range user.GlobalRoles {
if role, ok := hub.Roles[roleId]; ok && role.HasPermission(permission) { if role, ok := hub.GlobalRoles[roleId]; ok && role.HasPermission(perm) {
return true
}
}
return false
}
func hasHubUserChannelGroupPermission(user *types.HubUser, hub *types.Hub, perm permission.ChannelGroup) bool {
for _, roleId := range user.ChannelGroupRoles {
if role, ok := hub.ChannelGroupRoles[roleId]; ok && role.HasPermission(perm) {
return true return true
} }
} }
@@ -38,7 +48,7 @@ func HandleHubCreate(response http.ResponseWriter, request *http.Request) {
return return
} }
hub := types.NewHub() hub := types.CreateHub()
hub.Name = hubName hub.Name = hubName
hub.Creator = user.Id hub.Creator = user.Id
hub.Color = types.Rgba{}.GetRandom() hub.Color = types.Rgba{}.GetRandom()
@@ -52,19 +62,19 @@ func HandleHubCreate(response http.ResponseWriter, request *http.Request) {
} }
hub.ChannelGroups[rootGrp.Id] = rootGrp hub.ChannelGroups[rootGrp.Id] = rootGrp
rootRole := &types.HubRole{ rootRole := &types.HubGlobalRole{
Id: 0, Id: 0,
Name: "root", Name: "root",
Color: types.Rgba{255, 0, 0, 255}, Color: types.Rgba{255, 0, 0, 255},
RolePermission: ^types.RolePermission(0), RolePermission: ^permission.Global(0),
} }
hub.Roles[rootRole.Id] = rootRole hub.GlobalRoles[rootRole.Id] = rootRole
rootUser := &types.HubUser{ rootUser := &types.HubUser{
Id: user.Id, Id: user.Id,
Username: user.Name, Username: user.Name,
Roles: []uint8{rootRole.Id}, GlobalRoles: []uint8{rootRole.Id},
CreatedAt: hub.CreatedAt, CreatedAt: hub.CreatedAt,
} }
hub.Users[rootUser.Id] = rootUser hub.Users[rootUser.Id] = rootUser
@@ -73,7 +83,7 @@ func HandleHubCreate(response http.ResponseWriter, request *http.Request) {
http.Error(response, "internal server error", http.StatusInternalServerError) http.Error(response, "internal server error", http.StatusInternalServerError)
return return
} }
if err = postgresql.HubRoleSave(ctx, hub.Id, rootRole); err != nil { if err = postgresql.HubGlobalRoleSave(ctx, hub.Id, rootRole); err != nil {
http.Error(response, "internal server error", http.StatusInternalServerError) http.Error(response, "internal server error", http.StatusInternalServerError)
return return
} }
@@ -85,7 +95,7 @@ func HandleHubCreate(response http.ResponseWriter, request *http.Request) {
http.Error(response, "internal server error", http.StatusInternalServerError) http.Error(response, "internal server error", http.StatusInternalServerError)
return return
} }
if err = postgresql.HubUserRoleAdd(ctx, hub.Id, user.Id, rootRole.Id); err != nil { if err = postgresql.HubUserGlobalRoleAdd(ctx, hub.Id, user.Id, rootRole.Id); err != nil {
http.Error(response, "internal server error", http.StatusInternalServerError) http.Error(response, "internal server error", http.StatusInternalServerError)
return return
} }
@@ -106,7 +116,7 @@ func HandleHubDelete(response http.ResponseWriter, request *http.Request) {
return return
} }
if !hasHubUserPermission(hubUser, hub, types.PermissionRemoveHub) { if !hasHubUserGlobalPermission(hubUser, hub, permission.GlobalRemoveHub) {
http.Error(response, "forbidden", http.StatusForbidden) http.Error(response, "forbidden", http.StatusForbidden)
return return
} }
+1 -260
View File
@@ -72,86 +72,6 @@ func Init(ctx context.Context) {
panic(err) panic(err)
} }
_, err = dbConn.Exec(ctx, `
CREATE TABLE IF NOT EXISTS hubs (
id UUID PRIMARY KEY,
creator_id UUID NOT NULL REFERENCES users(id),
name TEXT NOT NULL,
allow_user_color BOOLEAN NOT NULL DEFAULT TRUE,
rgba BIGINT NOT NULL DEFAULT 0 CHECK (rgba BETWEEN 0 AND 4294967295),
created_at TIMESTAMP NOT NULL DEFAULT NOW()
)
`)
if err != nil {
panic(err)
}
_, err = dbConn.Exec(ctx, `
CREATE TABLE IF NOT EXISTS hub_roles (
hub_id UUID NOT NULL REFERENCES hubs(id) ON DELETE CASCADE,
role_id SMALLINT NOT NULL,
name TEXT NOT NULL,
permissions INTEGER NOT NULL DEFAULT 0,
rgba BIGINT NOT NULL DEFAULT 0 CHECK (rgba BETWEEN 0 AND 4294967295),
PRIMARY KEY (hub_id, role_id)
)
`)
if err != nil {
panic(err)
}
_, err = dbConn.Exec(ctx, `
CREATE TABLE IF NOT EXISTS hub_channel_groups (
channel_group_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
hub_id UUID NOT NULL REFERENCES hubs(id) ON DELETE CASCADE,
name TEXT NOT NULL,
rgba BIGINT NOT NULL DEFAULT 0 CHECK (rgba BETWEEN 0 AND 4294967295),
position SMALLINT NOT NULL DEFAULT 0
)
`)
if err != nil {
panic(err)
}
_, err = dbConn.Exec(ctx, `
CREATE TABLE IF NOT EXISTS hub_channels (
channel_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
hub_id UUID NOT NULL REFERENCES hubs(id) ON DELETE CASCADE,
name TEXT NOT NULL,
parent_group_id UUID REFERENCES hub_channel_groups(channel_group_id) ON DELETE CASCADE,
position SMALLINT NOT NULL DEFAULT 0
)
`)
if err != nil {
panic(err)
}
_, err = dbConn.Exec(ctx, `
CREATE TABLE IF NOT EXISTS hub_users (
hub_id UUID NOT NULL REFERENCES hubs(id) ON DELETE CASCADE,
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
name TEXT NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
PRIMARY KEY (hub_id, user_id)
)
`)
if err != nil {
panic(err)
}
_, err = dbConn.Exec(ctx, `
CREATE TABLE IF NOT EXISTS hub_user_roles (
hub_id UUID NOT NULL,
user_id UUID NOT NULL,
role_id SMALLINT NOT NULL,
PRIMARY KEY (hub_id, user_id, role_id),
FOREIGN KEY (hub_id, user_id) REFERENCES hub_users(hub_id, user_id) ON DELETE CASCADE,
FOREIGN KEY (hub_id, role_id) REFERENCES hub_roles(hub_id, role_id) ON DELETE CASCADE
)
`)
if err != nil {
panic(err)
}
} }
func UserSave(ctx context.Context, user *types.User) error { func UserSave(ctx context.Context, user *types.User) error {
@@ -277,7 +197,7 @@ func ConnectionsGetBelongingToUser(ctx context.Context, user *types.User) error
} }
for rows.Next() { for rows.Next() {
conn := types.NewConnection() conn := types.CreateConn()
if err = rows.Scan(&conn.Id, &conn.RequestorId, &conn.RecipientId, &conn.State, &conn.CreatedAt); err != nil { if err = rows.Scan(&conn.Id, &conn.RequestorId, &conn.RecipientId, &conn.State, &conn.CreatedAt); err != nil {
return fmt.Errorf("scanning connection row: %w", err) return fmt.Errorf("scanning connection row: %w", err)
} }
@@ -335,182 +255,3 @@ func ConnectionGetMessagesBefore(ctx context.Context, before time.Time, connecti
return messages, rows.Err() return messages, rows.Err()
} }
func HubSave(ctx context.Context, hub *types.Hub) error {
_, err := dbConn.Exec(ctx, `
INSERT INTO hubs (id, creator_id, name, allow_user_color, rgba, created_at) VALUES ($1, $2, $3, $4, $5, $6)
`, hub.Id, hub.Creator, hub.Name, hub.AllowUserColor, convertions.RgbaToUint32(hub.Color), hub.CreatedAt)
return err
}
func HubDelete(ctx context.Context, hub *types.Hub) error {
_, err := dbConn.Exec(ctx, `DELETE FROM hubs WHERE id = $1`, hub.Id)
return err
}
func HubGet(ctx context.Context, hub *types.Hub) error {
var rgba uint32
err := dbConn.QueryRow(ctx, `
SELECT creator_id, name, allow_user_color, rgba, created_at FROM hubs WHERE id = $1
`, hub.Id).Scan(&hub.Creator, &hub.Name, &hub.AllowUserColor, &rgba, &hub.CreatedAt)
if err == nil {
hub.Color = convertions.Uint32ToRgba(rgba)
}
return err
}
func HubsGetBelongingToUser(ctx context.Context, userId uuid.UUID) ([]*types.Hub, error) {
rows, err := dbConn.Query(ctx, `
SELECT h.id, h.creator_id, h.name, h.allow_user_color, h.rgba, h.created_at
FROM hubs h
JOIN hub_users hu ON h.id = hu.hub_id
WHERE hu.user_id = $1
`, userId)
if err != nil {
return nil, err
}
defer rows.Close()
var hubs []*types.Hub
for rows.Next() {
hub := types.NewHub()
var rgba uint32
if err = rows.Scan(&hub.Id, &hub.Creator, &hub.Name, &hub.AllowUserColor, &rgba, &hub.CreatedAt); err != nil {
return nil, fmt.Errorf("scanning hub row: %w", err)
}
hub.Color = convertions.Uint32ToRgba(rgba)
hubs = append(hubs, hub)
}
return hubs, rows.Err()
}
func HubGetUsers(ctx context.Context, hub *types.Hub) error {
rows, err := dbConn.Query(ctx, `
SELECT hu.user_id, hu.name, hu.created_at, COALESCE(array_agg(hur.role_id) FILTER (WHERE hur.role_id IS NOT NULL), '{}')
FROM hub_users hu
LEFT JOIN hub_user_roles hur ON hu.hub_id = hur.hub_id AND hu.user_id = hur.user_id
WHERE hu.hub_id = $1
GROUP BY hu.user_id, hu.name, hu.created_at
`, hub.Id)
if err != nil {
return err
}
defer rows.Close()
for rows.Next() {
hu := &types.HubUser{}
var roles []int16
if err = rows.Scan(&hu.Id, &hu.Username, &hu.CreatedAt, &roles); err != nil {
return fmt.Errorf("scanning hub user row: %w", err)
}
hu.Roles = make([]uint8, len(roles))
for i, r := range roles {
hu.Roles[i] = uint8(r)
}
hub.Users[hu.Id] = hu
}
return rows.Err()
}
func HubGetRoles(ctx context.Context, hub *types.Hub) error {
rows, err := dbConn.Query(ctx, `
SELECT role_id, name, permissions, rgba FROM hub_roles WHERE hub_id = $1
`, hub.Id)
if err != nil {
return err
}
defer rows.Close()
for rows.Next() {
hr := &types.HubRole{}
var rgba uint32
if err = rows.Scan(&hr.Id, &hr.Name, &hr.RolePermission, &rgba); err != nil {
return fmt.Errorf("scanning hub role row: %w", err)
}
hr.Color = convertions.Uint32ToRgba(rgba)
hub.Roles[hr.Id] = hr
}
return rows.Err()
}
func GetWholeHub(ctx context.Context, hub *types.Hub) error {
if err := HubGet(ctx, hub); err != nil {
return err
}
if err := HubGetUsers(ctx, hub); err != nil {
return err
}
return HubGetRoles(ctx, hub)
}
func HubUserAdd(ctx context.Context, hubId uuid.UUID, hu *types.HubUser) error {
_, err := dbConn.Exec(ctx, `
INSERT INTO hub_users (hub_id, user_id, name, created_at) VALUES ($1, $2, $3, $4)
`, hubId, hu.Id, hu.Username, hu.CreatedAt)
return err
}
func HubUserRemove(ctx context.Context, hubId uuid.UUID, userId uuid.UUID) error {
_, err := dbConn.Exec(ctx, `
DELETE FROM hub_users WHERE hub_id = $1 AND user_id = $2
`, hubId, userId)
return err
}
func HubUserRoleAdd(ctx context.Context, hubId uuid.UUID, userId uuid.UUID, roleId uint8) error {
_, err := dbConn.Exec(ctx, `
INSERT INTO hub_user_roles (hub_id, user_id, role_id) VALUES ($1, $2, $3)
`, hubId, userId, roleId)
return err
}
func HubUserRoleRemove(ctx context.Context, hubId uuid.UUID, userId uuid.UUID, roleId uint8) error {
_, err := dbConn.Exec(ctx, `
DELETE FROM hub_user_roles WHERE hub_id = $1 AND user_id = $2 AND role_id = $3
`, hubId, userId, roleId)
return err
}
func HubRoleSave(ctx context.Context, hubId uuid.UUID, role *types.HubRole) error {
_, err := dbConn.Exec(ctx, `
INSERT INTO hub_roles (hub_id, role_id, name, permissions, rgba) VALUES ($1, $2, $3, $4, $5)
`, hubId, role.Id, role.Name, role.RolePermission, convertions.RgbaToUint32(role.Color))
return err
}
func HubRoleDelete(ctx context.Context, hubId uuid.UUID, roleId uint8) error {
_, err := dbConn.Exec(ctx, `
DELETE FROM hub_roles WHERE hub_id = $1 AND role_id = $2
`, hubId, roleId)
return err
}
func HubRoleUpdate(ctx context.Context, hubId uuid.UUID, role *types.HubRole) error {
_, err := dbConn.Exec(ctx, `
UPDATE hub_roles SET name = $1, permissions = $2, rgba = $3 WHERE hub_id = $4 AND role_id = $5
`, role.Name, role.RolePermission, convertions.RgbaToUint32(role.Color), hubId, role.Id)
return err
}
func HubChannelGroupSave(ctx context.Context, hubId uuid.UUID, cg *types.HubChannelGroup) error {
_, err := dbConn.Exec(ctx, `
INSERT INTO hub_channel_groups (channel_group_id, hub_id, name, rgba, position) VALUES ($1, $2, $3, $4, $5)
`, cg.Id, hubId, cg.Name, convertions.RgbaToUint32(cg.Color), cg.Position)
return err
}
func HubChannelGroupDelete(ctx context.Context, cgId uuid.UUID) error {
_, err := dbConn.Exec(ctx, `DELETE FROM hub_channel_groups WHERE channel_group_id = $1`, cgId)
return err
}
func HubChannelSave(ctx context.Context, hubId uuid.UUID, ch *types.HubChannel) error {
return dbConn.QueryRow(ctx, `
INSERT INTO hub_channels (hub_id, name, parent_group_id, position) VALUES ($1, $2, $3, $4)
RETURNING channel_id
`, hubId, ch.Name, ch.ParentGroupId, ch.Position).Scan(&ch.Id)
}
func HubChannelDelete(ctx context.Context, chId uuid.UUID) error {
_, err := dbConn.Exec(ctx, `DELETE FROM hub_channels WHERE channel_id = $1`, chId)
return err
}
+54 -82
View File
@@ -2,6 +2,7 @@ package types
import ( import (
"crypto/sha256" "crypto/sha256"
"go-socket/packages/Enums/permission"
"math/rand/v2" "math/rand/v2"
"sync" "sync"
"time" "time"
@@ -24,6 +25,11 @@ func (r Rgba) GetRandom() Rgba {
return r return r
} }
type pairUuidHubChannelGroupRole struct {
first uuid.UUID
second *HubChannelGroupRole
}
type User struct { type User struct {
Mu sync.RWMutex `json:"-"` Mu sync.RWMutex `json:"-"`
Name string `json:"name"` Name string `json:"name"`
@@ -60,7 +66,7 @@ type Connection struct {
State ConnectionState.ConnectionState `json:"state"` State ConnectionState.ConnectionState `json:"state"`
} }
func NewConnection() *Connection { func CreateConn() *Connection {
return &Connection{ return &Connection{
MessagesBuff: make([]*Message, config.MaxDirectMsgCache), MessagesBuff: make([]*Message, config.MaxDirectMsgCache),
} }
@@ -124,69 +130,27 @@ type WsAuthMessage struct {
Error string `json:"error"` Error string `json:"error"`
} }
type RolePermission uint32
const (
// Hub permissions
PermissionSetHubName RolePermission = 1 << iota
PermissionSetHubBg
PermissionSetHubColor
PermissionRemoveHub
PermissionSetUserColorAllowed
// User permissions
PermissionAddUser
PermissionRemoveUser
PermissionRenameUser
PermissionMuteUser
// Role permissions
PermissionAddRole
PermissionRemoveRole
PermissionChangeRoleName
PermissionChangeRoleColor
PermissionChangeRolePermissions
PermissionOnlySelfRoleRemove
// Channel group permissions
PermissionAddChannelGroup
PermissionRemoveChannelGroup
PermissionSetChannelGroupName
PermissionSetChannelGroupColor
PermissionSetChannelGroupPermittedVisibleRoles
// Channel permissions
PermissionAddChannel
PermissionRemoveChannel
PermissionSetChannelName
PermissionSetChannelPermittedVisibleRoles
PermissionSetChannelPermittedSendMessageRoles
PermissionSetChannelPermittedReadHistoryRoles
)
type Hub struct { type Hub struct {
Mu sync.RWMutex `json:"-"` Mu sync.RWMutex `json:"-"`
Id uuid.UUID `json:"id"` Id uuid.UUID `json:"id"`
CreatedAt time.Time `json:"createdAt"` CreatedAt time.Time `json:"createdAt"`
MessagesBuff []*Message `json:"-"` Users map[uuid.UUID]*HubUser `json:"-"`
NextBuffIdx uint32 `json:"-"` GlobalRoles map[uint8]*HubGlobalRole `json:"-"`
HaveOverflowed bool `json:"-"` ChannelGroupRoles map[uint8]*HubChannelGroupRole `json:"-"`
Users map[uuid.UUID]*HubUser `json:"-"` ChannelGroups map[uuid.UUID]*HubChannelGroup `json:"-"`
Roles map[uint8]*HubRole `json:"-"` Creator uuid.UUID `json:"creator"`
ChannelGroups map[uuid.UUID]*HubChannelGroup `json:"-"` Name string `json:"name"`
Creator uuid.UUID `json:"creator"` Color Rgba `json:"color"`
Name string `json:"name"` AllowUserColor bool `json:"allowUserColor"`
Color Rgba `json:"color"`
AllowUserColor bool `json:"allowUserColor"`
} }
func NewHub() *Hub { func CreateHub() *Hub {
return &Hub{ return &Hub{
Id: uuid.New(), Id: uuid.New(),
MessagesBuff: make([]*Message, config.MaxHubMsgCache), Users: make(map[uuid.UUID]*HubUser),
Users: make(map[uuid.UUID]*HubUser), GlobalRoles: make(map[uint8]*HubGlobalRole),
Roles: make(map[uint8]*HubRole), ChannelGroupRoles: make(map[uint8]*HubChannelGroupRole),
ChannelGroups: make(map[uuid.UUID]*HubChannelGroup), ChannelGroups: make(map[uuid.UUID]*HubChannelGroup),
} }
} }
@@ -205,35 +169,43 @@ type HubChannel struct {
} }
type HubUser struct { type HubUser struct {
Id uuid.UUID `json:"id"` Id uuid.UUID `json:"id"`
Username string `json:"username"` Username string `json:"username"`
Roles []uint8 `json:"roles"` GlobalRoles []uint8 `json:"globalRoles"`
CreatedAt time.Time `json:"createdAt"` ChannelGroupRoles map[uint8]pairUuidHubChannelGroupRole `json:"ChannelGroupRoles"`
CreatedAt time.Time `json:"createdAt"`
} }
type HubRole struct { type HubGlobalRole struct {
Name string `json:"role"` Name string `json:"role"`
Id uint8 `json:"id"` Id uint8 `json:"id"`
RolePermission RolePermission `json:"rolePermission"` RolePermission permission.Global `json:"rolePermission"`
Color Rgba `json:"color"` Color Rgba `json:"color"`
} }
func (h *HubRole) GrantPermission(r RolePermission) { func (h *HubGlobalRole) GrantPermission(r permission.Global) {
h.RolePermission |= r h.RolePermission |= r
} }
func (h *HubRole) GrantPermissions(rs []RolePermission) { func (h *HubGlobalRole) RevokePermission(r permission.Global) {
for _, r := range rs {
h.RolePermission |= r
}
}
func (h *HubRole) RevokePermission(r RolePermission) {
h.RolePermission &^= r h.RolePermission &^= r
} }
func (h *HubRole) RevokePermissions(rs []RolePermission) { func (h *HubGlobalRole) HasPermission(r permission.Global) bool {
for _, r := range rs { return h.RolePermission&r != 0
h.RolePermission &^= r }
}
} type HubChannelGroupRole struct {
func (h *HubRole) HasPermission(r RolePermission) bool { Name string `json:"role"`
Id uint8 `json:"id"`
RolePermission permission.ChannelGroup `json:"rolePermission"`
Color Rgba `json:"color"`
}
func (h *HubChannelGroupRole) GrantPermission(r permission.ChannelGroup) {
h.RolePermission |= r
}
func (h *HubChannelGroupRole) RevokePermission(r permission.ChannelGroup) {
h.RolePermission &^= r
}
func (h *HubChannelGroupRole) HasPermission(r permission.ChannelGroup) bool {
return h.RolePermission&r != 0 return h.RolePermission&r != 0
} }