mirror of
https://github.com/mumble-voip/grumble.git
synced 2025-12-20 06:10:00 -08:00
169 lines
5 KiB
Go
169 lines
5 KiB
Go
// 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
|
|
}
|