forked from mumble-voip/grumble
-
Notifications
You must be signed in to change notification settings - Fork 0
/
acl.go
169 lines (149 loc) · 5.03 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
// Copyright (c) 2010-2013 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 acl
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
)
// Permission represents a permission in Mumble's ACL system.
type Permission uint32
// Check whether the given flags are set on perm
func (perm Permission) isSet(check Permission) bool {
return perm&check == check
}
// IsCached checks whether the ACL has its cache bit set,
// signalling that it was returned from an ACLCache.
func (perm Permission) IsCached() bool {
return perm.isSet(CachedPermission)
}
// Clean returns a Permission that has its cache bit cleared.
func (perm Permission) Clean() Permission {
return perm ^Permission(CachedPermission)
}
// An ACL as defined in an ACL context.
// An ACL can be defined for either a user or a group.
type ACL struct {
// 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
}
// IsUserACL returns true if the ACL is defined for a user,
// as opposed to a group.
func (acl *ACL) IsUserACL() bool {
return acl.UserId != -1
}
// IsChannelACL returns true if the ACL is defined for a group,
// as opposed to a user.
func (acl *ACL) IsChannelACL() bool {
return !acl.IsUserACL()
}
// HasPermission checks whether the given user has permission perm in the given context.
// The permission perm must be a single permission and not a combination of permissions.
func HasPermission(ctx *Context, user User, perm Permission) bool {
// We can't check permissions on a nil ctx.
if (ctx == nil) {
panic("acl: HasPermission got nil context")
}
// SuperUser can't speak or whisper, but everything else is OK
if user.UserId() == 0 {
if perm == SpeakPermission || perm == WhisperPermission {
return false
}
return true
}
// Default permissions
defaults := Permission(TraversePermission | EnterPermission | SpeakPermission | WhisperPermission | TextMessagePermission)
granted := defaults
contexts := buildChain(ctx)
origCtx := ctx
traverse := true
write := false
for _, ctx := range contexts {
// If the context does not inherit any ACLs, use the default permissions.
if !ctx.InheritACL {
granted = defaults
}
// Iterate through ACLs that are defined on ctx. 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 ctx.ACLs {
// Determine whether the ACL applies to user.
// If it is a user ACL and the user id of the ACL
// matches user's id, 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 == user.UserId()
matchGroup := GroupMemberCheck(origCtx, ctx, acl.Group, user)
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 (origCtx == ctx && acl.ApplyHere) || (origCtx != ctx && 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
}
}
// 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
}