forked from mumble-voip/grumble
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathacl.go
240 lines (211 loc) · 7.17 KB
/
acl.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
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.IsSuperUser() {
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
}