1
0
Fork 0
forked from External/grumble
grumble/pkg/acl/acl.go
Tim Cooper 20eba760e5 go fmt
2016-03-25 22:05:23 -03:00

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
}