634 lines
14 KiB
Go
634 lines
14 KiB
Go
package main
|
|
|
|
import "log"
|
|
import "sync"
|
|
import "strconv"
|
|
import "encoding/json"
|
|
|
|
var permUpdateMutex sync.Mutex
|
|
var BlankPerms Perms
|
|
var BlankForumPerms ForumPerms
|
|
var GuestPerms Perms
|
|
var ReadForumPerms ForumPerms
|
|
var ReadReplyForumPerms ForumPerms
|
|
var ReadWriteForumPerms ForumPerms
|
|
var AllPerms Perms
|
|
var AllForumPerms ForumPerms
|
|
var AllPluginPerms = make(map[string]bool)
|
|
|
|
// ? - Can we avoid duplicating the items in this list in a bunch of places?
|
|
|
|
var LocalPermList = []string{
|
|
"ViewTopic",
|
|
"LikeItem",
|
|
"CreateTopic",
|
|
"EditTopic",
|
|
"DeleteTopic",
|
|
"CreateReply",
|
|
"EditReply",
|
|
"DeleteReply",
|
|
"PinTopic",
|
|
"CloseTopic",
|
|
}
|
|
|
|
// ? - Can we avoid duplicating the items in this list in a bunch of places?
|
|
var GlobalPermList = []string{
|
|
"BanUsers",
|
|
"ActivateUsers",
|
|
"EditUser",
|
|
"EditUserEmail",
|
|
"EditUserPassword",
|
|
"EditUserGroup",
|
|
"EditUserGroupSuperMod",
|
|
"EditUserGroupAdmin",
|
|
"EditGroup",
|
|
"EditGroupLocalPerms",
|
|
"EditGroupGlobalPerms",
|
|
"EditGroupSuperMod",
|
|
"EditGroupAdmin",
|
|
"ManageForums",
|
|
"EditSettings",
|
|
"ManageThemes",
|
|
"ManagePlugins",
|
|
"ViewAdminLogs",
|
|
"ViewIPs",
|
|
"UploadFiles",
|
|
}
|
|
|
|
// Permission Structure: ActionComponent[Subcomponent]Flag
|
|
type Perms struct {
|
|
// Global Permissions
|
|
BanUsers bool
|
|
ActivateUsers bool
|
|
EditUser bool
|
|
EditUserEmail bool
|
|
EditUserPassword bool
|
|
EditUserGroup bool
|
|
EditUserGroupSuperMod bool
|
|
EditUserGroupAdmin bool
|
|
EditGroup bool
|
|
EditGroupLocalPerms bool
|
|
EditGroupGlobalPerms bool
|
|
EditGroupSuperMod bool
|
|
EditGroupAdmin bool
|
|
ManageForums bool // This could be local, albeit limited for per-forum managers?
|
|
EditSettings bool
|
|
ManageThemes bool
|
|
ManagePlugins bool
|
|
ViewAdminLogs bool
|
|
ViewIPs bool
|
|
|
|
// Global non-staff permissions
|
|
UploadFiles bool
|
|
// TODO: Add a permission for enabling avatars
|
|
|
|
// Forum permissions
|
|
ViewTopic bool
|
|
LikeItem bool
|
|
CreateTopic bool
|
|
EditTopic bool
|
|
DeleteTopic bool
|
|
CreateReply bool
|
|
//CreateReplyToOwn bool
|
|
EditReply bool
|
|
//EditOwnReply bool
|
|
DeleteReply bool
|
|
PinTopic bool
|
|
CloseTopic bool
|
|
//CloseOwnTopic bool
|
|
|
|
//ExtData map[string]bool
|
|
}
|
|
|
|
/* Inherit from group permissions for ones we don't have */
|
|
type ForumPerms struct {
|
|
ViewTopic bool
|
|
LikeItem bool
|
|
CreateTopic bool
|
|
EditTopic bool
|
|
DeleteTopic bool
|
|
CreateReply bool
|
|
//CreateReplyToOwn bool
|
|
EditReply bool
|
|
//EditOwnReply bool
|
|
DeleteReply bool
|
|
PinTopic bool
|
|
CloseTopic bool
|
|
//CloseOwnTopic bool
|
|
|
|
Overrides bool
|
|
ExtData map[string]bool
|
|
}
|
|
|
|
func init() {
|
|
BlankPerms = Perms{
|
|
//ExtData: make(map[string]bool),
|
|
}
|
|
|
|
BlankForumPerms = ForumPerms{
|
|
ExtData: make(map[string]bool),
|
|
}
|
|
|
|
GuestPerms = Perms{
|
|
ViewTopic: true,
|
|
//ExtData: make(map[string]bool),
|
|
}
|
|
|
|
AllPerms = Perms{
|
|
BanUsers: true,
|
|
ActivateUsers: true,
|
|
EditUser: true,
|
|
EditUserEmail: true,
|
|
EditUserPassword: true,
|
|
EditUserGroup: true,
|
|
EditUserGroupSuperMod: true,
|
|
EditUserGroupAdmin: true,
|
|
EditGroup: true,
|
|
EditGroupLocalPerms: true,
|
|
EditGroupGlobalPerms: true,
|
|
EditGroupSuperMod: true,
|
|
EditGroupAdmin: true,
|
|
ManageForums: true,
|
|
EditSettings: true,
|
|
ManageThemes: true,
|
|
ManagePlugins: true,
|
|
ViewAdminLogs: true,
|
|
ViewIPs: true,
|
|
|
|
UploadFiles: true,
|
|
|
|
ViewTopic: true,
|
|
LikeItem: true,
|
|
CreateTopic: true,
|
|
EditTopic: true,
|
|
DeleteTopic: true,
|
|
CreateReply: true,
|
|
EditReply: true,
|
|
DeleteReply: true,
|
|
PinTopic: true,
|
|
CloseTopic: true,
|
|
|
|
//ExtData: make(map[string]bool),
|
|
}
|
|
|
|
AllForumPerms = ForumPerms{
|
|
ViewTopic: true,
|
|
LikeItem: true,
|
|
CreateTopic: true,
|
|
EditTopic: true,
|
|
DeleteTopic: true,
|
|
CreateReply: true,
|
|
EditReply: true,
|
|
DeleteReply: true,
|
|
PinTopic: true,
|
|
CloseTopic: true,
|
|
|
|
Overrides: true,
|
|
ExtData: make(map[string]bool),
|
|
}
|
|
|
|
ReadWriteForumPerms = ForumPerms{
|
|
ViewTopic: true,
|
|
LikeItem: true,
|
|
CreateTopic: true,
|
|
CreateReply: true,
|
|
Overrides: true,
|
|
ExtData: make(map[string]bool),
|
|
}
|
|
|
|
ReadReplyForumPerms = ForumPerms{
|
|
ViewTopic: true,
|
|
LikeItem: true,
|
|
CreateReply: true,
|
|
Overrides: true,
|
|
ExtData: make(map[string]bool),
|
|
}
|
|
|
|
ReadForumPerms = ForumPerms{
|
|
ViewTopic: true,
|
|
Overrides: true,
|
|
ExtData: make(map[string]bool),
|
|
}
|
|
|
|
guestUser.Perms = GuestPerms
|
|
|
|
if dev.DebugMode {
|
|
log.Printf("Guest Perms: %+v\n", GuestPerms)
|
|
log.Printf("All Perms: %+v\n", AllPerms)
|
|
}
|
|
}
|
|
|
|
func presetToPermmap(preset string) (out map[string]ForumPerms) {
|
|
out = make(map[string]ForumPerms)
|
|
switch preset {
|
|
case "all":
|
|
out["guests"] = ReadForumPerms
|
|
out["members"] = ReadWriteForumPerms
|
|
out["staff"] = AllForumPerms
|
|
out["admins"] = AllForumPerms
|
|
case "announce":
|
|
out["guests"] = ReadForumPerms
|
|
out["members"] = ReadReplyForumPerms
|
|
out["staff"] = AllForumPerms
|
|
out["admins"] = AllForumPerms
|
|
case "members":
|
|
out["guests"] = BlankForumPerms
|
|
out["members"] = ReadWriteForumPerms
|
|
out["staff"] = AllForumPerms
|
|
out["admins"] = AllForumPerms
|
|
case "staff":
|
|
out["guests"] = BlankForumPerms
|
|
out["members"] = BlankForumPerms
|
|
out["staff"] = ReadWriteForumPerms
|
|
out["admins"] = AllForumPerms
|
|
case "admins":
|
|
out["guests"] = BlankForumPerms
|
|
out["members"] = BlankForumPerms
|
|
out["staff"] = BlankForumPerms
|
|
out["admins"] = AllForumPerms
|
|
case "archive":
|
|
out["guests"] = ReadForumPerms
|
|
out["members"] = ReadForumPerms
|
|
out["staff"] = ReadForumPerms
|
|
out["admins"] = ReadForumPerms //CurateForumPerms. Delete / Edit but no create?
|
|
default:
|
|
out["guests"] = BlankForumPerms
|
|
out["members"] = BlankForumPerms
|
|
out["staff"] = BlankForumPerms
|
|
out["admins"] = BlankForumPerms
|
|
}
|
|
return out
|
|
}
|
|
|
|
func permmapToQuery(permmap map[string]ForumPerms, fid int) error {
|
|
permUpdateMutex.Lock()
|
|
defer permUpdateMutex.Unlock()
|
|
|
|
_, err := deleteForumPermsByForumStmt.Exec(fid)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
perms, err := json.Marshal(permmap["admins"])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = addForumPermsToForumAdminsStmt.Exec(fid, "", perms)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
perms, err = json.Marshal(permmap["staff"])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = addForumPermsToForumStaffStmt.Exec(fid, "", perms)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
perms, err = json.Marshal(permmap["members"])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = addForumPermsToForumMembersStmt.Exec(fid, "", perms)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
perms, err = json.Marshal(permmap["guests"])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = addForumPermsToGroupStmt.Exec(6, fid, "", perms)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return rebuildForumPermissions(fid)
|
|
}
|
|
|
|
// TODO: Need a more thread-safe way of doing this. Possibly with sync.Map?
|
|
func rebuildForumPermissions(fid int) error {
|
|
if dev.DebugMode {
|
|
log.Print("Loading the forum permissions")
|
|
}
|
|
fids, err := fstore.GetAllIDs()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
rows, err := db.Query("select gid, permissions from forums_permissions where fid = ? order by gid asc", fid)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer rows.Close()
|
|
|
|
if dev.DebugMode {
|
|
log.Print("Updating the forum permissions")
|
|
}
|
|
for rows.Next() {
|
|
var gid int
|
|
var perms []byte
|
|
var pperms ForumPerms
|
|
err := rows.Scan(&gid, &perms)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = json.Unmarshal(perms, &pperms)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
pperms.ExtData = make(map[string]bool)
|
|
pperms.Overrides = true
|
|
_, ok := forumPerms[gid]
|
|
if !ok {
|
|
forumPerms[gid] = make(map[int]ForumPerms)
|
|
}
|
|
forumPerms[gid][fid] = pperms
|
|
}
|
|
|
|
groups, err := gstore.GetAll()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, group := range groups {
|
|
if dev.DebugMode {
|
|
log.Print("Updating the forum permissions for Group #" + strconv.Itoa(group.ID))
|
|
}
|
|
group.Forums = []ForumPerms{BlankForumPerms}
|
|
group.CanSee = []int{}
|
|
|
|
for _, ffid := range fids {
|
|
forumPerm, ok := forumPerms[group.ID][ffid]
|
|
if ok {
|
|
//log.Print("Overriding permissions for forum #" + strconv.Itoa(fid))
|
|
group.Forums = append(group.Forums, forumPerm)
|
|
} else {
|
|
//log.Print("Inheriting from default for forum #" + strconv.Itoa(fid))
|
|
forumPerm = BlankForumPerms
|
|
group.Forums = append(group.Forums, forumPerm)
|
|
}
|
|
if forumPerm.Overrides {
|
|
if forumPerm.ViewTopic {
|
|
group.CanSee = append(group.CanSee, ffid)
|
|
}
|
|
} else if group.Perms.ViewTopic {
|
|
group.CanSee = append(group.CanSee, ffid)
|
|
}
|
|
}
|
|
if dev.SuperDebug {
|
|
log.Printf("group.CanSee %+v\n", group.CanSee)
|
|
log.Printf("group.Forums %+v\n", group.Forums)
|
|
log.Print("len(group.CanSee)", len(group.CanSee))
|
|
log.Print("len(group.Forums)", len(group.Forums)) // This counts blank aka 0
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ? - We could have buildForumPermissions and rebuildForumPermissions call a third function containing common logic?
|
|
func buildForumPermissions() error {
|
|
fids, err := fstore.GetAllIDs()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if dev.SuperDebug {
|
|
log.Print("fids: ", fids)
|
|
}
|
|
|
|
rows, err := getForumsPermissionsStmt.Query()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer rows.Close()
|
|
|
|
if dev.DebugMode {
|
|
log.Print("Adding the forum permissions")
|
|
if dev.SuperDebug {
|
|
log.Print("forumPerms[gid][fid]")
|
|
}
|
|
}
|
|
// Temporarily store the forum perms in a map before transferring it to a much faster and thread-safe slice
|
|
forumPerms = make(map[int]map[int]ForumPerms)
|
|
for rows.Next() {
|
|
var gid, fid int
|
|
var perms []byte
|
|
var pperms ForumPerms
|
|
err = rows.Scan(&gid, &fid, &perms)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if dev.SuperDebug {
|
|
log.Print("perms: ", string(perms))
|
|
}
|
|
err = json.Unmarshal(perms, &pperms)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
pperms.ExtData = make(map[string]bool)
|
|
pperms.Overrides = true
|
|
_, ok := forumPerms[gid]
|
|
if !ok {
|
|
forumPerms[gid] = make(map[int]ForumPerms)
|
|
}
|
|
|
|
if dev.SuperDebug {
|
|
log.Print("gid: ", gid)
|
|
log.Print("fid: ", fid)
|
|
log.Printf("perms: %+v;", pperms)
|
|
}
|
|
forumPerms[gid][fid] = pperms
|
|
}
|
|
|
|
groups, err := gstore.GetAll()
|
|
if err != nil {
|
|
return err
|
|
|
|
}
|
|
for _, group := range groups {
|
|
if dev.DebugMode {
|
|
log.Print("Adding the forum permissions for Group #" + strconv.Itoa(group.ID) + " - " + group.Name)
|
|
}
|
|
group.Forums = []ForumPerms{BlankForumPerms}
|
|
group.CanSee = []int{}
|
|
for _, fid := range fids {
|
|
if dev.SuperDebug {
|
|
log.Printf("Forum #%+v\n", fid)
|
|
}
|
|
forumPerm, ok := forumPerms[group.ID][fid]
|
|
if ok {
|
|
// Override group perms
|
|
//log.Print("Overriding permissions for forum #" + strconv.Itoa(fid))
|
|
group.Forums = append(group.Forums, forumPerm)
|
|
} else {
|
|
// Inherit from Group
|
|
//log.Print("Inheriting from default for forum #" + strconv.Itoa(fid))
|
|
forumPerm = BlankForumPerms
|
|
group.Forums = append(group.Forums, forumPerm)
|
|
}
|
|
if forumPerm.Overrides {
|
|
if forumPerm.ViewTopic {
|
|
group.CanSee = append(group.CanSee, fid)
|
|
}
|
|
} else if group.Perms.ViewTopic {
|
|
group.CanSee = append(group.CanSee, fid)
|
|
}
|
|
|
|
if dev.SuperDebug {
|
|
log.Print("group.ID: ", group.ID)
|
|
log.Printf("forumPerm: %+v\n", forumPerm)
|
|
log.Print("group.CanSee: ", group.CanSee)
|
|
}
|
|
}
|
|
if dev.SuperDebug {
|
|
log.Printf("group.CanSee %+v\n", group.CanSee)
|
|
log.Printf("group.Forums %+v\n", group.Forums)
|
|
log.Print("len(group.CanSee)", len(group.CanSee))
|
|
log.Print("len(group.Forums)", len(group.Forums)) // This counts blank aka 0
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func forumPermsToGroupForumPreset(fperms ForumPerms) string {
|
|
if !fperms.Overrides {
|
|
return "default"
|
|
}
|
|
if !fperms.ViewTopic {
|
|
return "no_access"
|
|
}
|
|
var canPost = (fperms.LikeItem && fperms.CreateTopic && fperms.CreateReply)
|
|
var canModerate = (canPost && fperms.EditTopic && fperms.DeleteTopic && fperms.EditReply && fperms.DeleteReply && fperms.PinTopic && fperms.CloseTopic)
|
|
if canModerate {
|
|
return "can_moderate"
|
|
}
|
|
if fperms.EditTopic || fperms.DeleteTopic || fperms.EditReply || fperms.DeleteReply || fperms.PinTopic || fperms.CloseTopic {
|
|
if !canPost {
|
|
return "custom"
|
|
}
|
|
return "quasi_mod"
|
|
}
|
|
|
|
if canPost {
|
|
return "can_post"
|
|
}
|
|
if fperms.ViewTopic && !fperms.LikeItem && !fperms.CreateTopic && !fperms.CreateReply {
|
|
return "read_only"
|
|
}
|
|
return "custom"
|
|
}
|
|
|
|
func groupForumPresetToForumPerms(preset string) (fperms ForumPerms, changed bool) {
|
|
switch preset {
|
|
case "read_only":
|
|
return ReadForumPerms, true
|
|
case "can_post":
|
|
return ReadWriteForumPerms, true
|
|
case "can_moderate":
|
|
return AllForumPerms, true
|
|
case "no_access":
|
|
return ForumPerms{Overrides: true, ExtData: make(map[string]bool)}, true
|
|
case "default":
|
|
return BlankForumPerms, true
|
|
//case "custom": return fperms, false
|
|
}
|
|
return fperms, false
|
|
}
|
|
|
|
func stripInvalidGroupForumPreset(preset string) string {
|
|
switch preset {
|
|
case "read_only", "can_post", "can_moderate", "no_access", "default", "custom":
|
|
return preset
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func stripInvalidPreset(preset string) string {
|
|
switch preset {
|
|
case "all", "announce", "members", "staff", "admins", "archive", "custom":
|
|
return preset
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
|
|
// TODO: Move this into the phrase system?
|
|
func presetToLang(preset string) string {
|
|
switch preset {
|
|
case "all":
|
|
return "Public"
|
|
case "announce":
|
|
return "Announcements"
|
|
case "members":
|
|
return "Member Only"
|
|
case "staff":
|
|
return "Staff Only"
|
|
case "admins":
|
|
return "Admin Only"
|
|
case "archive":
|
|
return "Archive"
|
|
case "custom":
|
|
return "Custom"
|
|
default:
|
|
return ""
|
|
}
|
|
}
|
|
|
|
// TODO: Is this racey?
|
|
func rebuildGroupPermissions(gid int) error {
|
|
var permstr []byte
|
|
log.Print("Reloading a group")
|
|
err := db.QueryRow("select permissions from users_groups where gid = ?", gid).Scan(&permstr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
tmpPerms := Perms{
|
|
//ExtData: make(map[string]bool),
|
|
}
|
|
err = json.Unmarshal(permstr, &tmpPerms)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
group, err := gstore.Get(gid)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
group.Perms = tmpPerms
|
|
return nil
|
|
}
|
|
|
|
func overridePerms(perms *Perms, status bool) {
|
|
if status {
|
|
*perms = AllPerms
|
|
} else {
|
|
*perms = BlankPerms
|
|
}
|
|
}
|
|
|
|
// TODO: We need a better way of overriding forum perms rather than setting them one by one
|
|
func overrideForumPerms(perms *Perms, status bool) {
|
|
perms.ViewTopic = status
|
|
perms.LikeItem = status
|
|
perms.CreateTopic = status
|
|
perms.EditTopic = status
|
|
perms.DeleteTopic = status
|
|
perms.CreateReply = status
|
|
perms.EditReply = status
|
|
perms.DeleteReply = status
|
|
perms.PinTopic = status
|
|
perms.CloseTopic = status
|
|
}
|
|
|
|
func registerPluginPerm(name string) {
|
|
AllPluginPerms[name] = true
|
|
}
|
|
|
|
func deregisterPluginPerm(name string) {
|
|
delete(AllPluginPerms, name)
|
|
}
|