forked from External/grumble
374 lines
9.8 KiB
Go
374 lines
9.8 KiB
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.
|
|
|
|
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
|
|
}
|
|
|
|
// Get the list of user ids in the Add set.
|
|
func (group *Group) AddUsers() []int {
|
|
users := []int{}
|
|
for uid, _ := range group.Add {
|
|
users = append(users, uid)
|
|
}
|
|
return users
|
|
}
|
|
|
|
// Check whether the Remove set contains id.
|
|
func (group *Group) RemoveContains(id int) (ok bool) {
|
|
_, ok = group.Remove[id]
|
|
return
|
|
}
|
|
|
|
// Get the list of user ids in the Remove set.
|
|
func (group *Group) RemoveUsers() []int {
|
|
users := []int{}
|
|
for uid, _ := range group.Remove {
|
|
users = append(users, uid)
|
|
}
|
|
return users
|
|
}
|
|
|
|
// 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.IsRegistered()
|
|
// 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
|
|
}
|