gosora/permissions.go
Azareal 22650aad27 Added Quick Topic.
Added Attachments.
Added Attachment Media Embeds.

Renamed a load of *Store and *Cache methods to reduce the amount of unneccesary typing.
Added petabytes as a unit and cleaned up a few of the friendly units.
Refactored the username change logic to make it easier to maintain.
Refactored the avatar change logic to make it easier to maintain.
Shadow now uses CSS Variables for most of it's colours. We have plans to transpile this to support older browsers later on!
Snuck some CSS Variables into Tempra Conflux.
Added the GroupCache interface to MemoryGroupStore.
Added the Length method to MemoryGroupStore.
Added support for a site short name.
Added the UploadFiles permission.
Renamed more functions.
Fixed the background for the left gutter on the postbit for Tempra Simple and Shadow.
Added support for if statements operating on int8, int16, int32, int32, int64, uint, uint8, uint16, uint32, uint64, float32, and float64 for the template compiler.
Added support for if statements operating on slices and maps for the template compiler.
Fixed a security exploit in reply editing.
Fixed a bug in the URL detector in the parser where it couldn't find URLs with non-standard ports.
Fixed buttons having blue outlines on focus on Shadow.
Refactored the topic creation logic to make it easier to maintain.

Made a few responsive fixes, but there's still more to do in the following commits!
2017-10-05 11:20:28 +01:00

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)
}