forked from External/grumble
Initial ACL and group support.
This commit is contained in:
parent
1e67afca46
commit
4903558e4a
7 changed files with 1047 additions and 34 deletions
4
Makefile
4
Makefile
|
|
@ -20,7 +20,9 @@ GOFILES = \
|
|||
tlsserver.go \
|
||||
server.go \
|
||||
client.go \
|
||||
channel.go
|
||||
channel.go \
|
||||
acl.go \
|
||||
group.go
|
||||
|
||||
.PHONY: grumble
|
||||
grumble: pkg
|
||||
|
|
|
|||
240
acl.go
Normal file
240
acl.go
Normal file
|
|
@ -0,0 +1,240 @@
|
|||
// Copyright (c) 2010 The Grumble Authors
|
||||
// The use of this source code is goverened by a BSD-style
|
||||
// license that can be found in the LICENSE-file.
|
||||
|
||||
package main
|
||||
|
||||
const (
|
||||
// Per-channel permissions
|
||||
NonePermission = 0x0
|
||||
WritePermission = 0x1
|
||||
TraversePermission = 0x2
|
||||
EnterPermission = 0x4
|
||||
SpeakPermission = 0x8
|
||||
MuteDeafenPermission = 0x10
|
||||
MovePermission = 0x20
|
||||
MakeChannelPermission = 0x40
|
||||
LinkChannelPermission = 0x80
|
||||
WhisperPermission = 0x100
|
||||
TextMessagePermission = 0x200
|
||||
TempChannelPermission = 0x400
|
||||
|
||||
// Root channel only
|
||||
KickPermission = 0x10000
|
||||
BanPermission = 0x20000
|
||||
RegisterPermission = 0x40000
|
||||
SelfRegisterPermission = 0x80000
|
||||
|
||||
// Extra flags
|
||||
CachedPermission = 0x8000000
|
||||
AllPermissions = 0xf07ff
|
||||
)
|
||||
|
||||
type Permission uint32
|
||||
|
||||
// Check whether the given flags are set on perm
|
||||
func (perm Permission) IsSet(check Permission) bool {
|
||||
return perm&check == check
|
||||
}
|
||||
|
||||
// Check whether the Permission is marked as cached
|
||||
// (i.e. that it was read from an ACLCache)
|
||||
func (perm Permission) IsCached() bool {
|
||||
return perm.IsSet(CachedPermission)
|
||||
}
|
||||
|
||||
// Clear a flag in the Permission
|
||||
func (perm Permission) ClearFlag(flag Permission) {
|
||||
perm &= ^flag
|
||||
}
|
||||
|
||||
// Clear the cache bit in the Permission
|
||||
func (perm Permission) ClearCacheBit() {
|
||||
perm.ClearFlag(CachedPermission)
|
||||
}
|
||||
|
||||
// A channel-to-permission mapping used in the ACLCache
|
||||
type ChannelCache map[int]Permission
|
||||
// The ACLCache maps a user id to a ChannelCache map.
|
||||
// The ChannelCache map maps a channel to its permissions.
|
||||
type ACLCache map[uint32]ChannelCache
|
||||
|
||||
// Creates a new ACLCache
|
||||
func NewACLCache() ACLCache {
|
||||
return make(map[uint32]ChannelCache)
|
||||
}
|
||||
|
||||
// Store a client's permissions for a particular channel. When the permissions are stored,
|
||||
// the permission will have the CachedPermission flag added to it.
|
||||
func (cache ACLCache) StorePermission(client *Client, channel *Channel, perm Permission) {
|
||||
chancache, ok := cache[client.Session]
|
||||
if !ok {
|
||||
chancache = make(map[int]Permission)
|
||||
cache[client.Session] = chancache
|
||||
}
|
||||
chancache[channel.Id] = perm | CachedPermission
|
||||
}
|
||||
|
||||
// Get a client's permissions for a partcular channel. NonePermission will be returned
|
||||
// on error. To determine whether the returned value was retrieved from the cache, the
|
||||
// caller must call IsCached() on the returned permission.
|
||||
func (cache ACLCache) GetPermission(client *Client, channel *Channel) (perm Permission) {
|
||||
chancache, ok := cache[client.Session]
|
||||
perm = Permission(NonePermission)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
perm, ok = chancache[channel.Id]
|
||||
if !ok {
|
||||
perm = Permission(NonePermission)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// An ACL as defined on a channel.
|
||||
// An ACL can be defined for either a user or a group.
|
||||
type ChannelACL struct {
|
||||
// The channel that the ChannelACL is defined on.
|
||||
Channel *Channel
|
||||
|
||||
// The user id that this ACL applied to. If this
|
||||
// field is -1, the ACL is a group ACL.
|
||||
UserId int
|
||||
// The group that this ACL applies to.
|
||||
Group string
|
||||
|
||||
// The ApplyHere flag determines whether the ACL
|
||||
// should apply to the current channel.
|
||||
ApplyHere bool
|
||||
// The ApplySubs flag determines whethr the ACL
|
||||
// should apply to subchannels.
|
||||
ApplySubs bool
|
||||
|
||||
// The allowed permission flags.
|
||||
Allow Permission
|
||||
// The allowed permission flags. The Deny flags override
|
||||
// permissions set in Allow.
|
||||
Deny Permission
|
||||
}
|
||||
|
||||
// Returns true if the ACL is defined on a user
|
||||
// (as opposed to a group)
|
||||
func (acl ChannelACL) IsUserACL() bool {
|
||||
return acl.UserId != -1
|
||||
}
|
||||
|
||||
// Returns true if the ACL is defined on a channel
|
||||
// (as opposed to a user)
|
||||
func (acl ChannelACL) IsChannelACL() bool {
|
||||
return !acl.IsUserACL()
|
||||
}
|
||||
|
||||
// Create a new ACL for channel. Does not add it to the channel's
|
||||
// ACL list. This must be done manually.
|
||||
func NewChannelACL(channel *Channel) *ChannelACL {
|
||||
return &ChannelACL{
|
||||
Channel: channel,
|
||||
UserId: -1,
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether client has permission perm on channel. Perm *must* be a single permission,
|
||||
// and not a combination of permissions.
|
||||
func (server *Server) HasPermission(client *Client, channel *Channel, perm Permission) bool {
|
||||
// SuperUser can't speak or whisper, but everything else is OK
|
||||
if client.UserId == 0 {
|
||||
if perm == SpeakPermission || perm == WhisperPermission {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// First, try to look in the server's ACLCache.
|
||||
granted := Permission(NonePermission)
|
||||
cached := server.aclcache.GetPermission(client, channel)
|
||||
if cached.IsCached() {
|
||||
granted = cached
|
||||
// The +write permission implies all permissions except for +speak and +whisper.
|
||||
// For more information regarding this check, please see the comment regarding a simmilar
|
||||
// check at the bottom of this function.
|
||||
if perm != SpeakPermission && perm != WhisperPermission {
|
||||
return (granted & (perm | WritePermission)) != NonePermission
|
||||
} else {
|
||||
return (granted & perm) != NonePermission
|
||||
}
|
||||
}
|
||||
|
||||
// Default permissions
|
||||
def := Permission(TraversePermission | EnterPermission | SpeakPermission | WhisperPermission | TextMessagePermission)
|
||||
granted = def
|
||||
|
||||
channels := []*Channel{}
|
||||
iter := channel
|
||||
for iter != nil {
|
||||
channels = append([]*Channel{iter}, channels...)
|
||||
iter = iter.parent
|
||||
}
|
||||
|
||||
traverse := true
|
||||
write := false
|
||||
|
||||
for _, iter := range channels {
|
||||
// If the channel does not inherit any ACLs, use the default permissions.
|
||||
if !iter.InheritACL {
|
||||
granted = def
|
||||
}
|
||||
// Iterate through ACLs that are defined on iter. Note: this does not include
|
||||
// ACLs that iter has inherited from a parent (unless there is also a group on
|
||||
// iter with the same name, that changes the permissions a bit!)
|
||||
for _, acl := range iter.ACL {
|
||||
// Determine whether the ACL applies to client. If it is
|
||||
// a user ACL and the user id of the ACL matches client, we're good to go.
|
||||
//
|
||||
// If it's a group ACL, we have to parse and interpret the group string in the
|
||||
// current context to determine membership. For that we use GroupMemberCheck.
|
||||
matchUser := acl.IsUserACL() && acl.UserId == client.UserId
|
||||
matchGroup := GroupMemberCheck(channel, iter, acl.Group, client)
|
||||
if matchUser || matchGroup {
|
||||
if acl.Allow.IsSet(TraversePermission) {
|
||||
traverse = true
|
||||
}
|
||||
if acl.Deny.IsSet(TraversePermission) {
|
||||
traverse = false
|
||||
}
|
||||
if acl.Allow.IsSet(WritePermission) {
|
||||
write = true
|
||||
}
|
||||
if acl.Deny.IsSet(WritePermission) {
|
||||
write = false
|
||||
}
|
||||
if (channel == iter && acl.ApplyHere) || (channel != iter && acl.ApplySubs) {
|
||||
granted |= acl.Allow
|
||||
granted &= ^acl.Deny
|
||||
}
|
||||
}
|
||||
// If traverse is not set and the user doesn't have write permissions
|
||||
// on the channel, the user will not have any permissions.
|
||||
// This is because -traverse removes all permissions, and +write grants
|
||||
// all permissions.
|
||||
if !traverse && !write {
|
||||
granted = NonePermission
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cache the result
|
||||
server.aclcache.StorePermission(client, channel, granted)
|
||||
|
||||
// The +write permission implies all permissions except for +speak and +whisper.
|
||||
// This means that if the user has WritePermission, we should return true for all
|
||||
// permissions exccept SpeakPermission and WhisperPermission.
|
||||
if perm != SpeakPermission && perm != WhisperPermission {
|
||||
return (granted & (perm | WritePermission)) != NonePermission
|
||||
} else {
|
||||
return (granted & perm) != NonePermission
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
19
channel.go
19
channel.go
|
|
@ -1,3 +1,7 @@
|
|||
// Copyright (c) 2010 The Grumble Authors
|
||||
// The use of this source code is goverened by a BSD-style
|
||||
// license that can be found in the LICENSE-file.
|
||||
|
||||
package main
|
||||
|
||||
// A Mumble channel
|
||||
|
|
@ -8,10 +12,17 @@ type Channel struct {
|
|||
Temporary bool
|
||||
Position int
|
||||
|
||||
clients map[uint32]*Client
|
||||
clients map[uint32]*Client
|
||||
|
||||
parent *Channel
|
||||
children map[int]*Channel
|
||||
parent *Channel
|
||||
children map[int]*Channel
|
||||
|
||||
// ACL
|
||||
ACL []*ChannelACL
|
||||
InheritACL bool
|
||||
|
||||
// Groups
|
||||
Groups map[string]*Group
|
||||
}
|
||||
|
||||
func NewChannel(id int, name string) (channel *Channel) {
|
||||
|
|
@ -20,6 +31,8 @@ func NewChannel(id int, name string) (channel *Channel) {
|
|||
channel.Name = name
|
||||
channel.clients = make(map[uint32]*Client)
|
||||
channel.children = make(map[int]*Channel)
|
||||
channel.ACL = []*ChannelACL{}
|
||||
channel.Groups = map[string]*Group{}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
50
client.go
50
client.go
|
|
@ -37,14 +37,17 @@ type Client struct {
|
|||
udp bool
|
||||
|
||||
// Personal
|
||||
UserId int
|
||||
Session uint32
|
||||
Username string
|
||||
Hash string
|
||||
Tokens []string
|
||||
Channel *Channel
|
||||
}
|
||||
|
||||
// Something invalid happened on the wire.
|
||||
func (client *Client) Panic(reason string) {
|
||||
log.Printf("Client panic: %s", reason)
|
||||
client.Disconnect()
|
||||
}
|
||||
|
||||
|
|
@ -54,6 +57,8 @@ func (client *Client) Disconnect() {
|
|||
close(client.udprecv)
|
||||
close(client.msgchan)
|
||||
|
||||
client.conn.Close()
|
||||
|
||||
client.server.RemoveClient(client)
|
||||
}
|
||||
}
|
||||
|
|
@ -108,6 +113,49 @@ func (c *Client) sendProtoMessage(kind uint16, msg interface{}) (err os.Error) {
|
|||
return
|
||||
}
|
||||
|
||||
// Send permission denied by type
|
||||
func (c *Client) sendPermissionDeniedType(kind string) {
|
||||
val, ok := mumbleproto.PermissionDenied_DenyType_value[kind]
|
||||
if ok {
|
||||
d, err := proto.Marshal(&mumbleproto.PermissionDenied{
|
||||
Type: mumbleproto.NewPermissionDenied_DenyType(val),
|
||||
})
|
||||
if err != nil {
|
||||
c.Panic(err.String())
|
||||
return
|
||||
}
|
||||
c.msgchan <- &Message{
|
||||
buf: d,
|
||||
kind: MessagePermissionDenied,
|
||||
}
|
||||
} else {
|
||||
log.Printf("Unknown permission denied type.")
|
||||
}
|
||||
}
|
||||
|
||||
// Send permission denied by who, what, where
|
||||
func (c *Client) sendPermissionDenied(who *Client, where *Channel, what Permission) {
|
||||
d, err := proto.Marshal(&mumbleproto.PermissionDenied{
|
||||
Permission: proto.Uint32(uint32(what)),
|
||||
ChannelId: proto.Uint32(uint32(where.Id)),
|
||||
Session: proto.Uint32(who.Session),
|
||||
Type: mumbleproto.NewPermissionDenied_DenyType(mumbleproto.PermissionDenied_Permission),
|
||||
})
|
||||
if err != nil {
|
||||
c.Panic(err.String())
|
||||
}
|
||||
c.msgchan <- &Message{
|
||||
buf: d,
|
||||
kind: MessagePermissionDenied,
|
||||
}
|
||||
}
|
||||
|
||||
// Send permission denied fallback
|
||||
func (c *Client) sendPermissionDeniedFallback(kind string, version uint32, text string) {
|
||||
// fixme(mkrautz): Do fallback kind of stuff...
|
||||
c.sendPermissionDeniedType(kind)
|
||||
}
|
||||
|
||||
// UDP receiver.
|
||||
func (client *Client) udpreceiver() {
|
||||
for buf := range client.udprecv {
|
||||
|
|
@ -152,7 +200,7 @@ func (client *Client) udpreceiver() {
|
|||
buf: outbuf[0 : 1+outgoing.Size()],
|
||||
target: target,
|
||||
}
|
||||
// Server loopback
|
||||
// Server loopback
|
||||
} else {
|
||||
client.sendUdp(&Message{
|
||||
buf: outbuf[0 : 1+outgoing.Size()],
|
||||
|
|
|
|||
356
group.go
Normal file
356
group.go
Normal file
|
|
@ -0,0 +1,356 @@
|
|||
// Copyright (c) 2010 The Grumble Authors
|
||||
// The use of this source code is goverened by a BSD-style
|
||||
// license that can be found in the LICENSE-file.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strings"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Group struct {
|
||||
// The channel that this group resides in
|
||||
Channel *Channel
|
||||
|
||||
// The name of this group
|
||||
Name string
|
||||
|
||||
// The inherit flag means that this group will inherit group
|
||||
// members from its parent.
|
||||
Inherit bool
|
||||
|
||||
// The inheritable flag means that subchannels can
|
||||
// inherit the members of this group.
|
||||
Inheritable bool
|
||||
|
||||
// Group adds permissions to these users
|
||||
Add map[int]bool
|
||||
// Group removes permissions from these users
|
||||
Remove map[int]bool
|
||||
// Temporary add (authenticators)
|
||||
Temporary map[int]bool
|
||||
}
|
||||
|
||||
// Create a new group for channel with name. Does not add it to the channels
|
||||
// group list.
|
||||
func NewGroup(channel *Channel, name string) *Group {
|
||||
grp := &Group{}
|
||||
grp.Channel = channel
|
||||
grp.Name = name
|
||||
grp.Add = make(map[int]bool)
|
||||
grp.Remove = make(map[int]bool)
|
||||
grp.Temporary = make(map[int]bool)
|
||||
return grp
|
||||
}
|
||||
|
||||
// Check whether the Add set contains id.
|
||||
func (group *Group) AddContains(id int) (ok bool) {
|
||||
_, ok = group.Add[id]
|
||||
return
|
||||
}
|
||||
|
||||
// Check whether the Remove set contains id.
|
||||
func (group *Group) RemoveContains(id int) (ok bool) {
|
||||
_, ok = group.Remove[id]
|
||||
return
|
||||
}
|
||||
|
||||
// Check whether the Temporary set contains id.
|
||||
func (group *Group) TemporaryContains(id int) (ok bool) {
|
||||
_, ok = group.Temporary[id]
|
||||
return
|
||||
}
|
||||
|
||||
// Get the set of user id's from the group. This includes group
|
||||
// members that have been inherited from an ancestor.
|
||||
func (group *Group) Members() map[int]bool {
|
||||
groups := []*Group{}
|
||||
members := map[int]bool{}
|
||||
|
||||
// The channel that the group is defined on.
|
||||
channel := group.Channel
|
||||
|
||||
// Walk a group's channel tree, starting with the channel the group
|
||||
// is defined on, followed by its parent channels.
|
||||
iter := group.Channel
|
||||
for iter != nil {
|
||||
curgroup := iter.Groups[group.Name]
|
||||
if curgroup != nil {
|
||||
// If the group is not inheritable, and we're looking at an
|
||||
// ancestor group, we've looked in all the groups we should.
|
||||
if iter != channel && !curgroup.Inheritable {
|
||||
break
|
||||
}
|
||||
// Add the group to the list of groups to be considered
|
||||
groups = append([]*Group{curgroup}, groups...)
|
||||
// If this group does not inherit from groups in its ancestors, stop looking
|
||||
// for more ancestor groups.
|
||||
if !curgroup.Inherit {
|
||||
break
|
||||
}
|
||||
}
|
||||
iter = iter.parent
|
||||
}
|
||||
|
||||
for _, curgroup := range groups {
|
||||
for uid, _ := range curgroup.Add {
|
||||
members[uid] = true
|
||||
}
|
||||
for uid, _ := range curgroup.Remove {
|
||||
members[uid] = false, false
|
||||
}
|
||||
}
|
||||
|
||||
return members
|
||||
}
|
||||
|
||||
// Checks whether a user is a member of the group as defined on channel.
|
||||
// The channel current is the channel that group membership is currently being evaluated for.
|
||||
// The channel aclchan is the channel that the group is defined on. This means that current inherits
|
||||
// the group from an acl in aclchan.
|
||||
//
|
||||
// The channel aclchan will always be either equal to current, or be an ancestor.
|
||||
func GroupMemberCheck(current *Channel, aclchan *Channel, name string, client *Client) bool {
|
||||
invert := false
|
||||
token := false
|
||||
hash := false
|
||||
|
||||
// Returns the 'correct' return value considering the value
|
||||
// of the invert flag.
|
||||
retvalify := func(in bool) bool {
|
||||
if invert {
|
||||
return !in
|
||||
}
|
||||
return in
|
||||
}
|
||||
|
||||
member := false
|
||||
channel := current
|
||||
|
||||
for {
|
||||
// Empty group name are not valid.
|
||||
if len(name) == 0 {
|
||||
return false
|
||||
}
|
||||
// Invert
|
||||
if name[0] == '!' {
|
||||
invert = true
|
||||
name = name[1:]
|
||||
continue
|
||||
}
|
||||
// Evaluate in ACL context (not current channel)
|
||||
if name[0] == '~' {
|
||||
channel = aclchan
|
||||
name = name[1:]
|
||||
continue
|
||||
}
|
||||
// Token
|
||||
if name[0] == '#' {
|
||||
token = true
|
||||
name = name[1:]
|
||||
continue
|
||||
}
|
||||
// Hash
|
||||
if name[0] == '$' {
|
||||
hash = true
|
||||
name = name[1:]
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// The user is part of this group if the remaining name is part of
|
||||
// his access token list.
|
||||
if token {
|
||||
log.Printf("GroupMemberCheck: Implement token matching")
|
||||
member = false // fixme(mkrautz)
|
||||
// The user is part of this group if the remaining name matches his
|
||||
// cert hash.
|
||||
} else if hash {
|
||||
log.Printf("GroupMemberCheck: Implement hash matching")
|
||||
member = false // fixme(mkrautz)
|
||||
// None
|
||||
} else if name == "none" {
|
||||
member = false
|
||||
// Everyone
|
||||
} else if name == "all" {
|
||||
member = true
|
||||
// The user is part of the auth group is he is authenticated. That is,
|
||||
// his UserId is >= 0.
|
||||
} else if name == "auth" {
|
||||
member = client.UserId >= 0
|
||||
// The user is part of the strong group if he is authenticated to the server
|
||||
// via a strong certificate (i.e. non-self-signed).
|
||||
} else if name == "strong" {
|
||||
log.Printf("GroupMemberCheck: Implement strong certificate matching")
|
||||
member = false // fixme(mkrautz)
|
||||
// Is the user in the currently evaluated channel?
|
||||
} else if name == "in" {
|
||||
member = client.Channel == channel
|
||||
// Is the user not in the currently evaluated channel?
|
||||
} else if name == "out" {
|
||||
member = client.Channel != channel
|
||||
// fixme(mkrautz): The sub group implementation below hasn't been thoroughly
|
||||
// tested yet. It might be a bit buggy!
|
||||
} else if name == "sub" {
|
||||
// Strip away the "sub," part of the name
|
||||
name = name[4:]
|
||||
|
||||
mindesc := 1
|
||||
maxdesc := 1000
|
||||
minpath := 0
|
||||
|
||||
// Parse the groupname to extract the values we should use
|
||||
// for minpath (first argument), mindesc (second argument),
|
||||
// and maxdesc (third argument).
|
||||
args := strings.Split(name, ",", 3)
|
||||
nargs := len(args)
|
||||
if nargs == 3 {
|
||||
if len(args[2]) > 0 {
|
||||
if result, err := strconv.Atoi(args[2]); err == nil {
|
||||
maxdesc = result
|
||||
}
|
||||
}
|
||||
}
|
||||
if nargs >= 2 {
|
||||
if len(args[1]) > 0 {
|
||||
if result, err := strconv.Atoi(args[1]); err == nil {
|
||||
mindesc = result
|
||||
}
|
||||
}
|
||||
}
|
||||
if nargs >= 1 {
|
||||
if len(args[0]) > 0 {
|
||||
if result, err := strconv.Atoi(args[0]); err == nil {
|
||||
minpath = result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build a chain of channels, starting from the client's current channel.
|
||||
playerChain := []*Channel{}
|
||||
iter := client.Channel
|
||||
for iter != nil {
|
||||
playerChain = append([]*Channel{iter}, playerChain...)
|
||||
iter = iter.parent
|
||||
}
|
||||
// Build a chain of channels, starting from the channel current. This is
|
||||
// the channel that group membership is checked against, notwithstanding
|
||||
// the ~ group operator.
|
||||
groupChain := []*Channel{}
|
||||
iter = current
|
||||
for iter != nil {
|
||||
groupChain = append([]*Channel{iter}, groupChain...)
|
||||
iter = iter.parent
|
||||
}
|
||||
|
||||
// Helper function that finds the given channel in the channels slice.
|
||||
// Returns -1 if the given channel was not found in the slice.
|
||||
indexOf := func(channels []*Channel, channel *Channel) int {
|
||||
for i, iter := range channels {
|
||||
if iter == channel {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Find the index of channel that the group is currently being evaluated on.
|
||||
// This can be either aclchan or current depending on the ~ group operator.
|
||||
cofs := indexOf(groupChain, channel)
|
||||
if cofs == -1 {
|
||||
log.Printf("Invalid chain")
|
||||
return false
|
||||
}
|
||||
|
||||
// Add the first parameter of our sub group to cofs to get our 'base' channel.
|
||||
cofs += minpath
|
||||
// Check that the minpath parameter that was given is a valid index for groupChain.
|
||||
if cofs >= len(groupChain) {
|
||||
return retvalify(false)
|
||||
} else if cofs < 0 {
|
||||
cofs = 0
|
||||
}
|
||||
|
||||
// If our 'base' channel is not in the playerChain, the group does not apply to the client.
|
||||
if indexOf(playerChain, groupChain[cofs]) == -1 {
|
||||
return retvalify(false)
|
||||
}
|
||||
|
||||
// Down here, we're certain that the playerChain includes the base channel
|
||||
// *somewhere*. We must now determine if the path depth makes the user a
|
||||
// member of the group.
|
||||
mindepth := cofs + mindesc
|
||||
maxdepth := cofs + maxdesc
|
||||
pdepth := len(playerChain) - 1
|
||||
member = pdepth >= mindepth && pdepth <= maxdepth
|
||||
|
||||
// Non-magic groups
|
||||
} else {
|
||||
groups := []*Group{}
|
||||
|
||||
iter := channel
|
||||
for iter != nil {
|
||||
if group, ok := iter.Groups[name]; ok {
|
||||
// Skip non-inheritable groups if we're in parents
|
||||
// of our evaluated channel.
|
||||
if iter != channel && !group.Inheritable {
|
||||
break
|
||||
}
|
||||
// Prepend group
|
||||
groups = append([]*Group{group}, groups...)
|
||||
// If this group does not inherit from groups in its ancestors, stop looking
|
||||
// for more ancestor groups.
|
||||
if !group.Inherit {
|
||||
break
|
||||
}
|
||||
}
|
||||
iter = iter.parent
|
||||
}
|
||||
|
||||
for _, group := range groups {
|
||||
if group.AddContains(client.UserId) || group.TemporaryContains(client.UserId) || group.TemporaryContains(-int(client.Session)) {
|
||||
member = true
|
||||
}
|
||||
if group.RemoveContains(client.UserId) {
|
||||
member = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retvalify(member)
|
||||
}
|
||||
|
||||
// Get the list of group names in a particular channel.
|
||||
// This function walks the through the channel and all its
|
||||
// parent channels to figure out all groups that affect
|
||||
// the channel while considering group inheritance.
|
||||
func (channel *Channel) GroupNames() map[string]bool {
|
||||
names := map[string]bool{}
|
||||
|
||||
// Construct a list of channels. Fartherst away ancestors
|
||||
// are put in front of the list, allowing us to linearly
|
||||
// iterate the list to determine inheritance.
|
||||
channels := []*Channel{}
|
||||
iter := channel
|
||||
for iter != nil {
|
||||
channels = append([]*Channel{iter}, channels...)
|
||||
iter = iter.parent
|
||||
}
|
||||
|
||||
// Walk through all channels and groups in them.
|
||||
for _, iter := range channels {
|
||||
for _, group := range iter.Groups {
|
||||
// A non-inheritable group in parent. Discard it.
|
||||
if channel != iter && !group.Inheritable {
|
||||
names[group.Name] = false, false
|
||||
// An inheritable group. Add it to the list.
|
||||
} else {
|
||||
names[group.Name] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return names
|
||||
}
|
||||
326
message.go
326
message.go
|
|
@ -1,4 +1,3 @@
|
|||
// Grumble - an implementation of Murmur in Go
|
||||
// Copyright (c) 2010 The Grumble Authors
|
||||
// The use of this source code is goverened by a BSD-style
|
||||
// license that can be found in the LICENSE-file.
|
||||
|
|
@ -161,24 +160,47 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
|
|||
// Has a channel ID
|
||||
if userstate.ChannelId != nil {
|
||||
// Destination channel
|
||||
dstChan := server.channels[int(*userstate.ChannelId)]
|
||||
log.Printf("dstChan = %v", dstChan)
|
||||
dstChan, ok := server.channels[int(*userstate.ChannelId)]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// If the user and the actor aren't the same, check whether the actor has the 'move' permission
|
||||
// on the user's channel to move.
|
||||
if actor != user && !server.HasPermission(actor, user.Channel, MovePermission) {
|
||||
client.sendPermissionDenied(actor, user.Channel, MovePermission)
|
||||
return
|
||||
}
|
||||
|
||||
// Check whether the actor has 'move' permissions on dstChan. Check whether user has 'enter'
|
||||
// permissions on dstChan.
|
||||
if !server.HasPermission(actor, dstChan, MovePermission) && !server.HasPermission(user, dstChan, EnterPermission) {
|
||||
client.sendPermissionDenied(user, dstChan, EnterPermission)
|
||||
return
|
||||
}
|
||||
|
||||
// Check whether the channel is full.
|
||||
// fixme(mkrautz): See above.
|
||||
}
|
||||
|
||||
if userstate.Mute != nil || userstate.Deaf != nil || userstate.Suppress != nil || userstate.PrioritySpeaker != nil {
|
||||
// Disallow for SuperUser
|
||||
if user.UserId == 0 {
|
||||
client.sendPermissionDeniedType("SuperUser")
|
||||
return
|
||||
}
|
||||
|
||||
// Check whether the actor has 'mutedeafen' permission on user's channel.
|
||||
if !server.HasPermission(actor, user.Channel, MuteDeafenPermission) {
|
||||
client.sendPermissionDenied(actor, user.Channel, MuteDeafenPermission)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if this was a suppress operation. Only the server can suppress users.
|
||||
if userstate.Suppress != nil {
|
||||
client.sendPermissionDenied(actor, user.Channel, MuteDeafenPermission)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Comment set/clear
|
||||
|
|
@ -190,8 +212,15 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
|
|||
if user != actor {
|
||||
// Check if actor has 'move' permissions on the root channel. It is needed
|
||||
// to clear another user's comment.
|
||||
if !server.HasPermission(actor, server.root, MovePermission) {
|
||||
client.sendPermissionDenied(actor, server.root, MovePermission)
|
||||
return
|
||||
}
|
||||
|
||||
// Only allow empty text.
|
||||
if len(comment) > 0 {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the text is allowed.
|
||||
|
|
@ -209,9 +238,20 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
|
|||
if userstate.UserId != nil {
|
||||
// If user == actor, check for 'selfregister' permission on root channel.
|
||||
// If user != actor, check for 'register' permission on root channel.
|
||||
permCheck := Permission(NonePermission)
|
||||
uid := *userstate.UserId
|
||||
if user == actor {
|
||||
permCheck = SelfRegisterPermission
|
||||
} else {
|
||||
permCheck = RegisterPermission
|
||||
}
|
||||
if uid >= 0 || !server.HasPermission(actor, server.root, SelfRegisterPermission) {
|
||||
client.sendPermissionDenied(actor, server.root, permCheck)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if the UserId in the message is >= 0. A registration attempt
|
||||
// must use a negative UserId.
|
||||
// If user's hash is empty, deny...
|
||||
// fixme(mkrautz)
|
||||
}
|
||||
|
||||
// Prevent self-targetting state changes to be applied to other users
|
||||
|
|
@ -226,7 +266,7 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
|
|||
if actor != user && (userstate.SelfDeaf != nil || userstate.SelfMute != nil ||
|
||||
userstate.Texture != nil || userstate.PluginContext != nil || userstate.PluginIdentity != nil ||
|
||||
userstate.Recording != nil) {
|
||||
return
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -234,6 +274,7 @@ func (server *Server) handleUserStateMessage(client *Client, msg *Message) {
|
|||
func (server *Server) handleBanListMessage(client *Client, msg *Message) {
|
||||
}
|
||||
|
||||
// Broadcast text messages
|
||||
func (server *Server) handleTextMessage(client *Client, msg *Message) {
|
||||
txtmsg := &mumbleproto.TextMessage{}
|
||||
err := proto.Unmarshal(msg.buf, txtmsg)
|
||||
|
|
@ -242,15 +283,50 @@ func (server *Server) handleTextMessage(client *Client, msg *Message) {
|
|||
return
|
||||
}
|
||||
|
||||
users := []*Client{}
|
||||
for i := 0; i < len(txtmsg.Session); i++ {
|
||||
user, ok := server.clients[txtmsg.Session[i]]
|
||||
if !ok {
|
||||
log.Panic("Could not look up client by session")
|
||||
// fixme(mkrautz): Check text message length.
|
||||
// fixme(mkrautz): Sanitize text as well.
|
||||
|
||||
users := make(map[uint32]*Client)
|
||||
|
||||
// Tree
|
||||
for _, chanid := range txtmsg.TreeId {
|
||||
if channel, ok := server.channels[int(chanid)]; ok {
|
||||
if !server.HasPermission(client, channel, TextMessagePermission) {
|
||||
client.sendPermissionDenied(client, channel, TextMessagePermission)
|
||||
}
|
||||
for _, user := range channel.clients {
|
||||
users[user.Session] = user
|
||||
}
|
||||
}
|
||||
users = append(users, user)
|
||||
}
|
||||
|
||||
// Direct-to-channel
|
||||
for _, chanid := range txtmsg.ChannelId {
|
||||
if channel, ok := server.channels[int(chanid)]; ok {
|
||||
if !server.HasPermission(client, channel, TextMessagePermission) {
|
||||
client.sendPermissionDenied(client, channel, TextMessagePermission)
|
||||
return
|
||||
}
|
||||
for _, user := range channel.clients {
|
||||
users[user.Session] = user
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Direct-to-users
|
||||
for _, session := range txtmsg.Session {
|
||||
if user, ok := server.clients[session]; ok {
|
||||
if !server.HasPermission(client, user.Channel, TextMessagePermission) {
|
||||
client.sendPermissionDenied(client, user.Channel, TextMessagePermission)
|
||||
return
|
||||
}
|
||||
users[session] = user
|
||||
}
|
||||
}
|
||||
|
||||
// Remove ourselves
|
||||
users[client.Session] = nil, false
|
||||
|
||||
for _, user := range users {
|
||||
user.sendProtoMessage(MessageTextMessage, &mumbleproto.TextMessage{
|
||||
Actor: proto.Uint32(client.Session),
|
||||
|
|
@ -259,7 +335,214 @@ func (server *Server) handleTextMessage(client *Client, msg *Message) {
|
|||
}
|
||||
}
|
||||
|
||||
// ACL set/query
|
||||
func (server *Server) handleAclMessage(client *Client, msg *Message) {
|
||||
acl := &mumbleproto.ACL{}
|
||||
err := proto.Unmarshal(msg.buf, acl)
|
||||
if err != nil {
|
||||
client.Panic(err.String())
|
||||
}
|
||||
|
||||
// Look up the channel this ACL message operates on.
|
||||
channel, ok := server.channels[int(*acl.ChannelId)]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
// Does the user have permission to update or look at ACLs?
|
||||
if !server.HasPermission(client, channel, WritePermission) && !(channel.parent != nil && server.HasPermission(client, channel.parent, WritePermission)) {
|
||||
client.sendPermissionDenied(client, channel, WritePermission)
|
||||
return
|
||||
}
|
||||
|
||||
reply := &mumbleproto.ACL{}
|
||||
reply.ChannelId = proto.Uint32(uint32(channel.Id))
|
||||
|
||||
channels := []*Channel{}
|
||||
users := map[int]bool{}
|
||||
|
||||
// Query the current ACL state for the channel
|
||||
if acl.Query != nil && *acl.Query != false {
|
||||
reply.InheritAcls = proto.Bool(channel.InheritACL)
|
||||
// Walk the channel tree to get all relevant channels.
|
||||
// (Stop if we reach a channel that doesn't have the InheritACL flag set)
|
||||
iter := channel
|
||||
for iter != nil {
|
||||
channels = append([]*Channel{iter}, channels...)
|
||||
if iter == channel || iter.InheritACL {
|
||||
iter = iter.parent
|
||||
} else {
|
||||
iter = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Construct the protobuf ChanACL objects corresponding to the ACLs defined
|
||||
// in our channel list.
|
||||
reply.Acls = []*mumbleproto.ACL_ChanACL{}
|
||||
for _, iter := range channels {
|
||||
for _, chanacl := range iter.ACL {
|
||||
if iter == channel || chanacl.ApplySubs {
|
||||
mpacl := &mumbleproto.ACL_ChanACL{}
|
||||
mpacl.Inherited = proto.Bool(iter != channel)
|
||||
mpacl.ApplyHere = proto.Bool(chanacl.ApplyHere)
|
||||
mpacl.ApplySubs = proto.Bool(chanacl.ApplySubs)
|
||||
if chanacl.UserId >= 0 {
|
||||
mpacl.UserId = proto.Uint32(uint32(chanacl.UserId))
|
||||
users[chanacl.UserId] = true
|
||||
} else {
|
||||
mpacl.Group = proto.String(chanacl.Group)
|
||||
}
|
||||
mpacl.Grant = proto.Uint32(uint32(chanacl.Allow))
|
||||
mpacl.Deny = proto.Uint32(uint32(chanacl.Deny))
|
||||
reply.Acls = append(reply.Acls, mpacl)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parent := channel.parent
|
||||
allnames := channel.GroupNames()
|
||||
|
||||
// Construct the protobuf ChanGroups that we send back to the client.
|
||||
// Also constructs a usermap that is a set user ids from the channel's groups.
|
||||
reply.Groups = []*mumbleproto.ACL_ChanGroup{}
|
||||
for name, _ := range allnames {
|
||||
group := channel.Groups[name]
|
||||
pgroup, ok := parent.Groups[name]
|
||||
if !ok {
|
||||
pgroup = nil
|
||||
}
|
||||
|
||||
mpgroup := &mumbleproto.ACL_ChanGroup{}
|
||||
mpgroup.Name = proto.String(name)
|
||||
|
||||
mpgroup.Inherit = proto.Bool(true)
|
||||
if group != nil {
|
||||
mpgroup.Inherit = proto.Bool(group.Inherit)
|
||||
}
|
||||
|
||||
mpgroup.Inheritable = proto.Bool(true)
|
||||
if group != nil {
|
||||
mpgroup.Inheritable = proto.Bool(group.Inheritable)
|
||||
}
|
||||
|
||||
mpgroup.Inherited = proto.Bool(pgroup != nil && pgroup.Inheritable)
|
||||
|
||||
// Add the set of user ids that this group affects to the user map.
|
||||
// This is used later on in this function to send the client a QueryUsers
|
||||
// message that maps user ids to usernames.
|
||||
if group != nil {
|
||||
toadd := map[int]bool{}
|
||||
for uid, _ := range group.Add {
|
||||
users[uid] = true
|
||||
toadd[uid] = true
|
||||
}
|
||||
for uid, _ := range group.Remove {
|
||||
users[uid] = true
|
||||
toadd[uid] = false, false
|
||||
}
|
||||
for uid, _ := range toadd {
|
||||
mpgroup.Add = append(mpgroup.Add, uint32(uid))
|
||||
}
|
||||
}
|
||||
if pgroup != nil {
|
||||
for uid, _ := range pgroup.Members() {
|
||||
users[uid] = true
|
||||
mpgroup.InheritedMembers = append(mpgroup.InheritedMembers, uint32(uid))
|
||||
}
|
||||
}
|
||||
|
||||
reply.Groups = append(reply.Groups, mpgroup)
|
||||
}
|
||||
|
||||
if err := client.sendProtoMessage(MessageACL, reply); err != nil {
|
||||
client.Panic(err.String())
|
||||
}
|
||||
|
||||
// Map the user ids in the user map to usernames of users.
|
||||
// fixme(mkrautz): This requires a persistent datastore, because it retrieves registered users.
|
||||
queryusers := &mumbleproto.QueryUsers{}
|
||||
for uid, _ := range users {
|
||||
queryusers.Ids = append(queryusers.Ids, uint32(uid))
|
||||
queryusers.Names = append(queryusers.Names, "Unknown")
|
||||
}
|
||||
if len(queryusers.Ids) > 0 {
|
||||
client.sendProtoMessage(MessageQueryUsers, reply)
|
||||
}
|
||||
|
||||
// Set new groups and ACLs
|
||||
} else {
|
||||
|
||||
// Get old temporary members
|
||||
oldtmp := map[string]map[int]bool{}
|
||||
for name, grp := range channel.Groups {
|
||||
oldtmp[name] = grp.Temporary
|
||||
}
|
||||
|
||||
// Clear current ACLs and groups
|
||||
channel.ACL = []*ChannelACL{}
|
||||
channel.Groups = map[string]*Group{}
|
||||
|
||||
// Add the received groups to the channel.
|
||||
channel.InheritACL = *acl.InheritAcls
|
||||
for _, pbgrp := range acl.Groups {
|
||||
changroup := NewGroup(channel, *pbgrp.Name)
|
||||
|
||||
changroup.Inherit = *pbgrp.Inherit
|
||||
changroup.Inheritable = *pbgrp.Inheritable
|
||||
for _, uid := range pbgrp.Add {
|
||||
changroup.Add[int(uid)] = true
|
||||
}
|
||||
for _, uid := range pbgrp.Remove {
|
||||
changroup.Remove[int(uid)] = true
|
||||
}
|
||||
if temp, ok := oldtmp[*pbgrp.Name]; ok {
|
||||
changroup.Temporary = temp
|
||||
}
|
||||
|
||||
channel.Groups[changroup.Name] = changroup
|
||||
}
|
||||
// Add the received ACLs to the channel.
|
||||
for _, pbacl := range acl.Acls {
|
||||
chanacl := NewChannelACL(channel)
|
||||
|
||||
chanacl.ApplyHere = *pbacl.ApplyHere
|
||||
chanacl.ApplySubs = *pbacl.ApplySubs
|
||||
if pbacl.UserId != nil {
|
||||
chanacl.UserId = int(*pbacl.UserId)
|
||||
} else {
|
||||
chanacl.Group = *pbacl.Group
|
||||
}
|
||||
chanacl.Deny = Permission(*pbacl.Deny & AllPermissions)
|
||||
chanacl.Allow = Permission(*pbacl.Grant & AllPermissions)
|
||||
|
||||
channel.ACL = append(channel.ACL, chanacl)
|
||||
}
|
||||
|
||||
// Clear the server's ACL cache
|
||||
server.ClearACLCache()
|
||||
|
||||
// Regular user?
|
||||
if (!server.HasPermission(client, channel, WritePermission) && client.UserId >= 0) || len(client.Hash) > 0 {
|
||||
chanacl := NewChannelACL(channel)
|
||||
|
||||
chanacl.ApplyHere = true
|
||||
chanacl.ApplySubs = false
|
||||
if client.UserId >= 0 {
|
||||
chanacl.UserId = client.UserId
|
||||
} else {
|
||||
chanacl.Group = "$" + client.Hash
|
||||
}
|
||||
chanacl.UserId = client.UserId
|
||||
chanacl.Deny = Permission(NonePermission)
|
||||
chanacl.Allow = Permission(WritePermission | TraversePermission)
|
||||
|
||||
channel.ACL = append(channel.ACL, chanacl)
|
||||
|
||||
server.ClearACLCache()
|
||||
}
|
||||
|
||||
// fixme(mkrautz): Sync channel to datastore
|
||||
}
|
||||
}
|
||||
|
||||
// User query
|
||||
|
|
@ -274,5 +557,22 @@ func (server *Server) handleUserStatsMessage(client *Client, msg *Message) {
|
|||
if err != nil {
|
||||
client.Panic(err.String())
|
||||
}
|
||||
log.Printf("UserStatsMessage")
|
||||
|
||||
log.Printf("UserStats")
|
||||
}
|
||||
|
||||
// Permission query
|
||||
func (server *Server) handlePermissionQuery(client *Client, msg *Message) {
|
||||
query := &mumbleproto.PermissionQuery{}
|
||||
err := proto.Unmarshal(msg.buf, query)
|
||||
if err != nil {
|
||||
client.Panic(err.String())
|
||||
}
|
||||
|
||||
if query.ChannelId == nil {
|
||||
return
|
||||
}
|
||||
|
||||
channel := server.channels[int(*query.ChannelId)]
|
||||
server.sendClientPermissions(client, channel)
|
||||
}
|
||||
|
|
|
|||
86
server.go
86
server.go
|
|
@ -63,9 +63,12 @@ type Server struct {
|
|||
PreferAlphaCodec bool
|
||||
|
||||
// Channels
|
||||
chanid int
|
||||
root *Channel
|
||||
channels map[int]*Channel
|
||||
chanid int
|
||||
root *Channel
|
||||
channels map[int]*Channel
|
||||
|
||||
// ACL cache
|
||||
aclcache ACLCache
|
||||
}
|
||||
|
||||
// Allocate a new Murmur instance
|
||||
|
|
@ -93,6 +96,8 @@ func NewServer(addr string, port int) (s *Server, err os.Error) {
|
|||
subChan := s.NewChannel("SubChannel")
|
||||
s.root.AddChild(subChan)
|
||||
|
||||
s.aclcache = NewACLCache()
|
||||
|
||||
go s.handler()
|
||||
|
||||
return
|
||||
|
|
@ -117,6 +122,8 @@ func (server *Server) NewClient(conn net.Conn) (err os.Error) {
|
|||
client.msgchan = make(chan *Message)
|
||||
client.udprecv = make(chan []byte)
|
||||
|
||||
client.UserId = -1
|
||||
|
||||
go client.receiver()
|
||||
go client.udpreceiver()
|
||||
go client.sender()
|
||||
|
|
@ -149,7 +156,7 @@ func (server *Server) RemoveClient(client *Client) {
|
|||
channel.RemoveClient(client)
|
||||
|
||||
err := server.broadcastProtoMessage(MessageUserRemove, &mumbleproto.UserRemove{
|
||||
Session: proto.Uint32(client.Session),
|
||||
Session: proto.Uint32(client.Session),
|
||||
})
|
||||
if err != nil {
|
||||
// server panic
|
||||
|
|
@ -166,7 +173,7 @@ func (server *Server) NewChannel(name string) (channel *Channel) {
|
|||
|
||||
// Remove a channel from the server.
|
||||
func (server *Server) RemoveChanel(channel *Channel) {
|
||||
if (channel.Id == 0) {
|
||||
if channel.Id == 0 {
|
||||
log.Printf("Attempted to remove root channel.")
|
||||
return
|
||||
}
|
||||
|
|
@ -284,22 +291,41 @@ func (server *Server) handleAuthenticate(client *Client, msg *Message) {
|
|||
|
||||
// Broadcast the the user entered a channel
|
||||
server.root.AddClient(client)
|
||||
err = server.broadcastProtoMessage(MessageUserState, &mumbleproto.UserState{
|
||||
|
||||
if client.Username == "SuperUser" {
|
||||
client.UserId = 0
|
||||
}
|
||||
|
||||
userstate := &mumbleproto.UserState{
|
||||
Session: proto.Uint32(client.Session),
|
||||
Name: proto.String(client.Username),
|
||||
ChannelId: proto.Uint32(0),
|
||||
})
|
||||
if err != nil {
|
||||
}
|
||||
if client.UserId >= 0 {
|
||||
userstate.UserId = proto.Uint32(uint32(client.UserId))
|
||||
}
|
||||
if err := client.sendProtoMessage(MessageUserState, userstate); err != nil {
|
||||
client.Panic(err.String())
|
||||
}
|
||||
|
||||
server.sendUserList(client)
|
||||
|
||||
err = client.sendProtoMessage(MessageServerSync, &mumbleproto.ServerSync{
|
||||
Session: proto.Uint32(client.Session),
|
||||
MaxBandwidth: proto.Uint32(server.MaxBandwidth),
|
||||
})
|
||||
if err != nil {
|
||||
sync := &mumbleproto.ServerSync{}
|
||||
sync.Session = proto.Uint32(client.Session)
|
||||
sync.MaxBandwidth = proto.Uint32(server.MaxBandwidth)
|
||||
if client.UserId == 0 {
|
||||
sync.Permissions = proto.Uint64(uint64(AllPermissions))
|
||||
} else {
|
||||
server.HasPermission(client, server.root, EnterPermission)
|
||||
perm := server.aclcache.GetPermission(client, server.root)
|
||||
if !perm.IsCached() {
|
||||
client.Panic("Corrupt ACL cache")
|
||||
return
|
||||
}
|
||||
perm.ClearCacheBit()
|
||||
sync.Permissions = proto.Uint64(uint64(perm))
|
||||
}
|
||||
if err = client.sendProtoMessage(MessageServerSync, sync); err != nil {
|
||||
client.Panic(err.String())
|
||||
return
|
||||
}
|
||||
|
|
@ -377,6 +403,9 @@ func (server *Server) sendUserList(client *Client) {
|
|||
if user.state != StateClientAuthenticated {
|
||||
continue
|
||||
}
|
||||
if user == client {
|
||||
continue
|
||||
}
|
||||
|
||||
err := client.sendProtoMessage(MessageUserState, &mumbleproto.UserState{
|
||||
Session: proto.Uint32(user.Session),
|
||||
|
|
@ -392,6 +421,26 @@ func (server *Server) sendUserList(client *Client) {
|
|||
|
||||
}
|
||||
|
||||
// Send a client its permissions for channel.
|
||||
func (server *Server) sendClientPermissions(client *Client, channel *Channel) {
|
||||
// No caching for SuperUser
|
||||
if client.UserId == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Update cache
|
||||
server.HasPermission(client, channel, EnterPermission)
|
||||
|
||||
perm := server.aclcache.GetPermission(client, channel)
|
||||
log.Printf("Permissions = 0x%x", perm)
|
||||
|
||||
// fixme(mkrautz): Cache which permissions we've already sent.
|
||||
client.sendProtoMessage(MessagePermissionQuery, &mumbleproto.PermissionQuery{
|
||||
ChannelId: proto.Uint32(uint32(channel.Id)),
|
||||
Permissions: proto.Uint32(uint32(perm)),
|
||||
})
|
||||
}
|
||||
|
||||
func (server *Server) broadcastProtoMessage(kind uint16, msg interface{}) (err os.Error) {
|
||||
for _, client := range server.clients {
|
||||
if client.state != StateClientAuthenticated {
|
||||
|
|
@ -438,7 +487,7 @@ func (server *Server) handleIncomingMessage(client *Client, msg *Message) {
|
|||
case MessageVoiceTarget:
|
||||
log.Printf("MessageVoiceTarget from client")
|
||||
case MessagePermissionQuery:
|
||||
log.Printf("MessagePermissionQuery from client")
|
||||
server.handlePermissionQuery(msg.client, msg)
|
||||
case MessageCodecVersion:
|
||||
log.Printf("MessageCodecVersion from client")
|
||||
case MessageUserStats:
|
||||
|
|
@ -470,11 +519,11 @@ func (s *Server) SendUDP() {
|
|||
crypted := make([]byte, len(msg.buf)+4)
|
||||
msg.client.crypt.Encrypt(crypted, msg.buf)
|
||||
s.udpconn.WriteTo(crypted, msg.client.udpaddr)
|
||||
// Non-encrypted
|
||||
// Non-encrypted
|
||||
} else if msg.address != nil {
|
||||
s.udpconn.WriteTo(msg.buf, msg.address)
|
||||
} else {
|
||||
// Skipping
|
||||
// Skipping
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -564,6 +613,11 @@ func (server *Server) ListenUDP() {
|
|||
}
|
||||
}
|
||||
|
||||
// Clear the ACL cache
|
||||
func (s *Server) ClearACLCache() {
|
||||
s.aclcache = NewACLCache()
|
||||
}
|
||||
|
||||
// The accept loop of the server.
|
||||
func (s *Server) ListenAndMurmur() {
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue