More refactoring. So much data o.o

Fixed the tests.
Added wrapper functions for the various logging levels.
Added the SetPreset method to *Forum.
Added the ReloadGroup to the ForumPermsStore.
This commit is contained in:
Azareal 2017-11-13 05:22:37 +00:00
parent 0572c3e048
commit b42181c0c6
20 changed files with 523 additions and 512 deletions

View File

@ -2,6 +2,7 @@ package common
import (
"database/sql"
"log"
"../query_gen/lib"
)
@ -82,3 +83,27 @@ func (inits dbInits) Run() error {
func (inits dbInits) Add(init ...func(acc *qgen.Accumulator) error) {
DbInits = dbInits(append(DbInits, init...))
}
func debugDetail(args ...interface{}) {
if Dev.SuperDebug {
log.Print(args...)
}
}
func debugDetailf(str string, args ...interface{}) {
if Dev.SuperDebug {
log.Printf(str, args...)
}
}
func debugLog(args ...interface{}) {
if Dev.DebugMode {
log.Print(args...)
}
}
func debugLogf(str string, args ...interface{}) {
if Dev.DebugMode {
log.Printf(str, args...)
}
}

View File

@ -3,6 +3,7 @@ package common
//import "fmt"
import (
"database/sql"
"errors"
"strconv"
"strings"
@ -48,7 +49,8 @@ type ForumSimple struct {
}
type ForumStmts struct {
update *sql.Stmt
update *sql.Stmt
setPreset *sql.Stmt
}
var forumStmts ForumStmts
@ -56,7 +58,8 @@ var forumStmts ForumStmts
func init() {
DbInits.Add(func(acc *qgen.Accumulator) error {
forumStmts = ForumStmts{
update: acc.Update("forums").Set("name = ?, desc = ?, active = ?, preset = ?").Where("fid = ?").Prepare(),
update: acc.Update("forums").Set("name = ?, desc = ?, active = ?, preset = ?").Where("fid = ?").Prepare(),
setPreset: acc.Update("forums").Set("preset = ?").Where("fid = ?").Prepare(),
}
return acc.FirstError()
})
@ -88,6 +91,39 @@ func (forum *Forum) Update(name string, desc string, active bool, preset string)
return nil
}
func (forum *Forum) SetPreset(preset string, gid int) error {
fperms, changed := GroupForumPresetToForumPerms(preset)
if changed {
return forum.setPreset(fperms, preset, gid)
}
return nil
}
// TODO: Refactor this
func (forum *Forum) setPreset(fperms *ForumPerms, preset string, gid int) (err error) {
err = ReplaceForumPermsForGroup(gid, map[int]string{forum.ID: preset}, map[int]*ForumPerms{forum.ID: fperms})
if err != nil {
LogError(err)
return errors.New("Unable to update the permissions")
}
// TODO: Add this and replaceForumPermsForGroup into a transaction?
_, err = forumStmts.setPreset.Exec("", forum.ID)
if err != nil {
LogError(err)
return errors.New("Unable to update the forum")
}
err = Fstore.Reload(forum.ID)
if err != nil {
return errors.New("Unable to reload forum")
}
err = Fpstore.ReloadGroup(forum.ID, gid)
if err != nil {
return errors.New("Unable to reload the forum permissions")
}
return nil
}
// TODO: Replace this sorting mechanism with something a lot more efficient
// ? - Use sort.Slice instead?
type SortForum []*Forum

307
common/forum_perms.go Normal file
View File

@ -0,0 +1,307 @@
package common
import (
"database/sql"
"encoding/json"
"../query_gen/lib"
)
// ? - 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",
}
/* Inherit from group permissions for ones we don't have */
type ForumPerms struct {
ViewTopic bool
//ViewOwnTopic 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 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 {
tx, err := qgen.Builder.Begin()
if err != nil {
return err
}
defer tx.Rollback()
deleteForumPermsByForumTx, err := qgen.Builder.SimpleDeleteTx(tx, "forums_permissions", "fid = ?")
if err != nil {
return err
}
_, err = deleteForumPermsByForumTx.Exec(fid)
if err != nil {
return err
}
perms, err := json.Marshal(permmap["admins"])
if err != nil {
return err
}
addForumPermsToForumAdminsTx, err := qgen.Builder.SimpleInsertSelectTx(tx,
qgen.DBInsert{"forums_permissions", "gid, fid, preset, permissions", ""},
qgen.DBSelect{"users_groups", "gid, ? AS fid, ? AS preset, ? AS permissions", "is_admin = 1", "", ""},
)
if err != nil {
return err
}
_, err = addForumPermsToForumAdminsTx.Exec(fid, "", perms)
if err != nil {
return err
}
perms, err = json.Marshal(permmap["staff"])
if err != nil {
return err
}
addForumPermsToForumStaffTx, err := qgen.Builder.SimpleInsertSelectTx(tx,
qgen.DBInsert{"forums_permissions", "gid, fid, preset, permissions", ""},
qgen.DBSelect{"users_groups", "gid, ? AS fid, ? AS preset, ? AS permissions", "is_admin = 0 AND is_mod = 1", "", ""},
)
if err != nil {
return err
}
_, err = addForumPermsToForumStaffTx.Exec(fid, "", perms)
if err != nil {
return err
}
perms, err = json.Marshal(permmap["members"])
if err != nil {
return err
}
addForumPermsToForumMembersTx, err := qgen.Builder.SimpleInsertSelectTx(tx,
qgen.DBInsert{"forums_permissions", "gid, fid, preset, permissions", ""},
qgen.DBSelect{"users_groups", "gid, ? AS fid, ? AS preset, ? AS permissions", "is_admin = 0 AND is_mod = 0 AND is_banned = 0", "", ""},
)
if err != nil {
return err
}
_, err = addForumPermsToForumMembersTx.Exec(fid, "", perms)
if err != nil {
return err
}
// 6 is the ID of the Not Loggedin Group
// TODO: Use a shared variable rather than a literal for the group ID
err = ReplaceForumPermsForGroupTx(tx, 6, map[int]string{fid: ""}, map[int]*ForumPerms{fid: permmap["guests"]})
if err != nil {
return err
}
err = tx.Commit()
if err != nil {
return err
}
return Fpstore.Reload(fid)
}
func ReplaceForumPermsForGroup(gid int, presetSet map[int]string, permSets map[int]*ForumPerms) error {
tx, err := qgen.Builder.Begin()
if err != nil {
return err
}
defer tx.Rollback()
err = ReplaceForumPermsForGroupTx(tx, gid, presetSet, permSets)
if err != nil {
return err
}
return tx.Commit()
}
func ReplaceForumPermsForGroupTx(tx *sql.Tx, gid int, presetSets map[int]string, permSets map[int]*ForumPerms) error {
deleteForumPermsForGroupTx, err := qgen.Builder.SimpleDeleteTx(tx, "forums_permissions", "gid = ? AND fid = ?")
if err != nil {
return err
}
addForumPermsToGroupTx, err := qgen.Builder.SimpleInsertTx(tx, "forums_permissions", "gid, fid, preset, permissions", "?,?,?,?")
if err != nil {
return err
}
for fid, permSet := range permSets {
permstr, err := json.Marshal(permSet)
if err != nil {
return err
}
_, err = deleteForumPermsForGroupTx.Exec(gid, fid)
if err != nil {
return err
}
_, err = addForumPermsToGroupTx.Exec(gid, fid, presetSets[fid], string(permstr))
if err != nil {
return err
}
}
return nil
}
// TODO: Refactor this and write tests for it
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
}
return fperms, false
}
func BlankForumPerms() *ForumPerms {
return &ForumPerms{ViewTopic: false}
}
func ReadWriteForumPerms() *ForumPerms {
return &ForumPerms{
ViewTopic: true,
LikeItem: true,
CreateTopic: true,
CreateReply: true,
Overrides: true,
ExtData: make(map[string]bool),
}
}
func ReadReplyForumPerms() *ForumPerms {
return &ForumPerms{
ViewTopic: true,
LikeItem: true,
CreateReply: true,
Overrides: true,
ExtData: make(map[string]bool),
}
}
func ReadForumPerms() *ForumPerms {
return &ForumPerms{
ViewTopic: true,
Overrides: true,
ExtData: make(map[string]bool),
}
}
// AllForumPerms is a set of forum local permissions with everything set to true
func AllForumPerms() *ForumPerms {
return &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),
}
}

View File

@ -4,6 +4,7 @@ import (
"database/sql"
"encoding/json"
"log"
"sync"
"../query_gen/lib"
)
@ -12,89 +13,90 @@ var Fpstore ForumPermsStore
type ForumPermsStore interface {
Init() error
Get(fid int, gid int) (fperms ForumPerms, err error)
Get(fid int, gid int) (fperms *ForumPerms, err error)
Reload(id int) error
ReloadGroup(fid int, gid int) error
}
type ForumPermsCache interface {
}
type MemoryForumPermsStore struct {
get *sql.Stmt
getByForum *sql.Stmt
get *sql.Stmt
getByForum *sql.Stmt
getByForumGroup *sql.Stmt
updateMutex sync.Mutex
}
func NewMemoryForumPermsStore() (*MemoryForumPermsStore, error) {
acc := qgen.Builder.Accumulator()
return &MemoryForumPermsStore{
get: acc.Select("forums_permissions").Columns("gid, fid, permissions").Orderby("gid ASC, fid ASC").Prepare(),
getByForum: acc.Select("forums_permissions").Columns("gid, permissions").Where("fid = ?").Orderby("gid ASC").Prepare(),
get: acc.Select("forums_permissions").Columns("gid, fid, permissions").Orderby("gid ASC, fid ASC").Prepare(),
getByForum: acc.Select("forums_permissions").Columns("gid, permissions").Where("fid = ?").Orderby("gid ASC").Prepare(),
getByForumGroup: acc.Select("forums_permissions").Columns("permissions").Where("fid = ? AND gid = ?").Prepare(),
}, acc.FirstError()
}
func (fps *MemoryForumPermsStore) Init() error {
fps.updateMutex.Lock()
defer fps.updateMutex.Unlock()
fids, err := Fstore.GetAllIDs()
if err != nil {
return err
}
if Dev.SuperDebug {
log.Print("fids: ", fids)
}
debugDetail("fids: ", fids)
rows, err := fps.get.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]")
}
}
debugLog("Adding the forum permissions")
debugDetail("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)
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)
pperms, err := fps.parseForumPerm(perms)
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] = make(map[int]*ForumPerms)
}
if Dev.SuperDebug {
log.Print("gid: ", gid)
log.Print("fid: ", fid)
log.Printf("perms: %+v\n", pperms)
}
debugDetail("gid: ", gid)
debugDetail("fid: ", fid)
debugDetailf("perms: %+v\n", pperms)
forumPerms[gid][fid] = pperms
}
return fps.cascadePermSetToGroups(forumPerms, fids)
}
func (fps *MemoryForumPermsStore) parseForumPerm(perms []byte) (pperms *ForumPerms, err error) {
debugDetail("perms: ", string(perms))
pperms = BlankForumPerms()
err = json.Unmarshal(perms, &pperms)
pperms.ExtData = make(map[string]bool)
pperms.Overrides = true
return pperms, err
}
// TODO: Need a more thread-safe way of doing this. Possibly with sync.Map?
func (fps *MemoryForumPermsStore) Reload(fid int) error {
if Dev.DebugMode {
log.Printf("Reloading the forum permissions for forum #%d", fid)
}
fps.updateMutex.Lock()
defer fps.updateMutex.Unlock()
debugLogf("Reloading the forum permissions for forum #%d", fid)
fids, err := Fstore.GetAllIDs()
if err != nil {
return err
@ -109,38 +111,56 @@ func (fps *MemoryForumPermsStore) Reload(fid int) error {
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)
pperms, err := fps.parseForumPerm(perms)
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] = make(map[int]*ForumPerms)
}
forumPerms[gid][fid] = pperms
}
return fps.cascadePermSetToGroups(forumPerms, fids)
}
func (fps *MemoryForumPermsStore) cascadePermSetToGroups(forumPerms map[int]map[int]ForumPerms, fids []int) error {
func (fps *MemoryForumPermsStore) ReloadGroup(fid int, gid int) (err error) {
fps.updateMutex.Lock()
defer fps.updateMutex.Unlock()
var perms []byte
err = fps.getByForumGroup.QueryRow(fid, gid).Scan(&perms)
if err != nil {
return err
}
fperms, err := fps.parseForumPerm(perms)
if err != nil {
return err
}
group, err := Gstore.Get(gid)
if err != nil {
return err
}
// TODO: Refactor this
group.Forums[fid] = fperms
return nil
}
func (fps *MemoryForumPermsStore) cascadePermSetToGroups(forumPerms map[int]map[int]*ForumPerms, fids []int) error {
groups, err := Gstore.GetAll()
if err != nil {
return err
}
for _, group := range groups {
if Dev.DebugMode {
log.Printf("Updating the forum permissions for Group #%d", group.ID)
}
group.Forums = []ForumPerms{BlankForumPerms}
debugLogf("Updating the forum permissions for Group #%d", group.ID)
group.Forums = []*ForumPerms{BlankForumPerms()}
group.CanSee = []int{}
fps.cascadePermSetToGroup(forumPerms, group, fids)
@ -152,18 +172,16 @@ func (fps *MemoryForumPermsStore) cascadePermSetToGroups(forumPerms map[int]map[
return nil
}
func (fps *MemoryForumPermsStore) cascadePermSetToGroup(forumPerms map[int]map[int]ForumPerms, group *Group, fids []int) {
func (fps *MemoryForumPermsStore) cascadePermSetToGroup(forumPerms map[int]map[int]*ForumPerms, group *Group, fids []int) {
for _, fid := range fids {
if Dev.SuperDebug {
log.Printf("Forum #%+v\n", fid)
}
debugDetailf("Forum #%+v\n", fid)
forumPerm, ok := forumPerms[group.ID][fid]
if ok {
//log.Printf("Overriding permissions for forum #%d",fid)
group.Forums = append(group.Forums, forumPerm)
} else {
//log.Printf("Inheriting from group defaults for forum #%d",fid)
forumPerm = BlankForumPerms
forumPerm = BlankForumPerms()
group.Forums = append(group.Forums, forumPerm)
}
if forumPerm.Overrides {
@ -174,16 +192,14 @@ func (fps *MemoryForumPermsStore) cascadePermSetToGroup(forumPerms map[int]map[i
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)
}
debugDetail("group.ID: ", group.ID)
debugDetailf("forumPerm: %+v\n", forumPerm)
debugDetail("group.CanSee: ", group.CanSee)
}
}
func (fps *MemoryForumPermsStore) Get(fid int, gid int) (fperms ForumPerms, err error) {
// TODO: Add a hook here and have plugin_guilds use it
// TODO: Add a hook here and have plugin_guilds use it
func (fps *MemoryForumPermsStore) Get(fid int, gid int) (fperms *ForumPerms, err error) {
group, err := Gstore.Get(gid)
if err != nil {
return fperms, ErrNoRows

View File

@ -17,9 +17,8 @@ import (
"../query_gen/lib"
)
var ForumUpdateMutex sync.Mutex
var forumCreateMutex sync.Mutex
var forumPerms map[int]map[int]ForumPerms // [gid][fid]Perms // TODO: Add an abstraction around this and make it more thread-safe
var forumPerms map[int]map[int]*ForumPerms // [gid][fid]*ForumPerms // TODO: Add an abstraction around this and make it more thread-safe
var Fstore ForumStore
// ForumStore is an interface for accessing the forums and the metadata stored on them
@ -110,9 +109,7 @@ func (mfs *MemoryForumStore) LoadForums() error {
}
if forum.Name == "" {
if Dev.DebugMode {
log.Print("Adding a placeholder forum")
}
debugLog("Adding a placeholder forum")
} else {
log.Printf("Adding the '%s' forum", forum.Name)
}
@ -272,11 +269,8 @@ func (mfs *MemoryForumStore) Delete(id int) error {
return errors.New("You cannot delete the Reports forum")
}
_, err := mfs.delete.Exec(id)
if err != nil {
return err
}
mfs.CacheDelete(id)
return nil
return err
}
func (mfs *MemoryForumStore) AddTopic(tid int, uid int, fid int) error {

View File

@ -26,7 +26,7 @@ type Group struct {
PermissionsText []byte
PluginPerms map[string]bool // Custom permissions defined by plugins. What if two plugins declare the same permission, but they handle them in incompatible ways? Very unlikely, we probably don't need to worry about this, the plugin authors should be aware of each other to some extent
PluginPermsText []byte
Forums []ForumPerms
Forums []*ForumPerms
CanSee []int // The IDs of the forums this group can see
}

View File

@ -86,9 +86,7 @@ func (mgs *MemoryGroupStore) LoadGroups() error {
}
mgs.groupCount = i
if Dev.DebugMode {
log.Print("Binding the Not Loggedin Group")
}
debugLog("Binding the Not Loggedin Group")
GuestPerms = mgs.dirtyGetUnsafe(6).Perms
return nil
}
@ -172,9 +170,7 @@ func (mgs *MemoryGroupStore) initGroup(group *Group) error {
log.Print("bad group plugin perms: ", group.PluginPermsText)
return err
}
if Dev.DebugMode {
log.Printf(group.Name+": %+v\n", group.PluginPerms)
}
debugLogf(group.Name+": %+v\n", group.PluginPerms)
//group.Perms.ExtData = make(map[string]bool)
// TODO: Can we optimise the bit where this cascades down to the user now?
@ -200,6 +196,7 @@ func (mgs *MemoryGroupStore) Exists(gid int) bool {
}
// ? Allow two groups with the same name?
// TODO: Refactor this
func (mgs *MemoryGroupStore) Create(name string, tag string, isAdmin bool, isMod bool, isBanned bool) (gid int, err error) {
var permstr = "{}"
tx, err := qgen.Builder.Begin()
@ -224,7 +221,7 @@ func (mgs *MemoryGroupStore) Create(name string, tag string, isAdmin bool, isMod
gid = int(gid64)
var perms = BlankPerms
var blankForums []ForumPerms
var blankForums []*ForumPerms
var blankIntList []int
var pluginPerms = make(map[string]bool)
var pluginPermsBytes = []byte("{}")
@ -239,18 +236,17 @@ func (mgs *MemoryGroupStore) Create(name string, tag string, isAdmin bool, isMod
}
var presetSet = make(map[int]string)
var permSet = make(map[int]ForumPerms)
PermUpdateMutex.Lock()
defer PermUpdateMutex.Unlock()
var permSet = make(map[int]*ForumPerms)
for _, forum := range fdata {
var thePreset string
if isAdmin {
switch {
case isAdmin:
thePreset = "admins"
} else if isMod {
case isMod:
thePreset = "staff"
} else if isBanned {
case isBanned:
thePreset = "banned"
} else {
default:
thePreset = "members"
}

View File

@ -1,46 +1,20 @@
package common
import (
"database/sql"
"encoding/json"
"log"
"sync"
"../query_gen/lib"
)
// TODO: Refactor the perms system
var PermUpdateMutex sync.Mutex
var BlankPerms Perms
var BlankForumPerms ForumPerms
var GuestPerms Perms
var ReadForumPerms ForumPerms
var ReadReplyForumPerms ForumPerms
var ReadWriteForumPerms ForumPerms
// AllPerms is a set of global permissions with everything set to true
var AllPerms Perms
// AllForumPerms is a set of forum local permissions with everything set to true
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",
@ -111,36 +85,11 @@ type Perms struct {
//ExtData map[string]bool
}
/* Inherit from group permissions for ones we don't have */
type ForumPerms struct {
ViewTopic bool
//ViewOwnTopic 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),
@ -183,266 +132,9 @@ func init() {
//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 {
tx, err := qgen.Builder.Begin()
if err != nil {
return err
}
defer tx.Rollback()
deleteForumPermsByForumTx, err := qgen.Builder.SimpleDeleteTx(tx, "forums_permissions", "fid = ?")
if err != nil {
return err
}
_, err = deleteForumPermsByForumTx.Exec(fid)
if err != nil {
return err
}
perms, err := json.Marshal(permmap["admins"])
if err != nil {
return err
}
addForumPermsToForumAdminsTx, err := qgen.Builder.SimpleInsertSelectTx(tx,
qgen.DBInsert{"forums_permissions", "gid, fid, preset, permissions", ""},
qgen.DBSelect{"users_groups", "gid, ? AS fid, ? AS preset, ? AS permissions", "is_admin = 1", "", ""},
)
if err != nil {
return err
}
_, err = addForumPermsToForumAdminsTx.Exec(fid, "", perms)
if err != nil {
return err
}
perms, err = json.Marshal(permmap["staff"])
if err != nil {
return err
}
addForumPermsToForumStaffTx, err := qgen.Builder.SimpleInsertSelectTx(tx,
qgen.DBInsert{"forums_permissions", "gid, fid, preset, permissions", ""},
qgen.DBSelect{"users_groups", "gid, ? AS fid, ? AS preset, ? AS permissions", "is_admin = 0 AND is_mod = 1", "", ""},
)
if err != nil {
return err
}
_, err = addForumPermsToForumStaffTx.Exec(fid, "", perms)
if err != nil {
return err
}
perms, err = json.Marshal(permmap["members"])
if err != nil {
return err
}
addForumPermsToForumMembersTx, err := qgen.Builder.SimpleInsertSelectTx(tx,
qgen.DBInsert{"forums_permissions", "gid, fid, preset, permissions", ""},
qgen.DBSelect{"users_groups", "gid, ? AS fid, ? AS preset, ? AS permissions", "is_admin = 0 AND is_mod = 0 AND is_banned = 0", "", ""},
)
if err != nil {
return err
}
_, err = addForumPermsToForumMembersTx.Exec(fid, "", perms)
if err != nil {
return err
}
// 6 is the ID of the Not Loggedin Group
// TODO: Use a shared variable rather than a literal for the group ID
err = ReplaceForumPermsForGroupTx(tx, 6, map[int]string{fid: ""}, map[int]ForumPerms{fid: permmap["guests"]})
if err != nil {
return err
}
err = tx.Commit()
if err != nil {
return err
}
PermUpdateMutex.Lock()
defer PermUpdateMutex.Unlock()
return Fpstore.Reload(fid)
}
func ReplaceForumPermsForGroup(gid int, presetSet map[int]string, permSets map[int]ForumPerms) error {
tx, err := qgen.Builder.Begin()
if err != nil {
return err
}
defer tx.Rollback()
err = ReplaceForumPermsForGroupTx(tx, gid, presetSet, permSets)
if err != nil {
return err
}
return tx.Commit()
}
func ReplaceForumPermsForGroupTx(tx *sql.Tx, gid int, presetSets map[int]string, permSets map[int]ForumPerms) error {
deleteForumPermsForGroupTx, err := qgen.Builder.SimpleDeleteTx(tx, "forums_permissions", "gid = ? AND fid = ?")
if err != nil {
return err
}
addForumPermsToGroupTx, err := qgen.Builder.SimpleInsertTx(tx, "forums_permissions", "gid, fid, preset, permissions", "?,?,?,?")
if err != nil {
return err
}
for fid, permSet := range permSets {
permstr, err := json.Marshal(permSet)
if err != nil {
return err
}
_, err = deleteForumPermsForGroupTx.Exec(gid, fid)
if err != nil {
return err
}
_, err = addForumPermsToGroupTx.Exec(gid, fid, presetSets[fid], string(permstr))
if err != nil {
return err
}
}
return nil
}
// TODO: Refactor this and write tests for it
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
debugLogf("Guest Perms: %+v\n", GuestPerms)
debugLogf("All Perms: %+v\n", AllPerms)
}
func StripInvalidGroupForumPreset(preset string) string {
@ -457,9 +149,8 @@ func StripInvalidPreset(preset string) string {
switch preset {
case "all", "announce", "members", "staff", "admins", "archive", "custom":
return preset
default:
return ""
}
return ""
}
// TODO: Move this into the phrase system?

View File

@ -101,7 +101,7 @@ func forumUserCheck(w http.ResponseWriter, r *http.Request, user *User, fid int)
}
// TODO: Put this on the user instance? Do we really want forum specific logic in there? Maybe, a method which spits a new pointer with the same contents as user?
func cascadeForumPerms(fperms ForumPerms, user *User) {
func cascadeForumPerms(fperms *ForumPerms, user *User) {
if fperms.Overrides && !user.IsSuperAdmin {
user.Perms.ViewTopic = fperms.ViewTopic
user.Perms.LikeItem = fperms.LikeItem

View File

@ -51,7 +51,6 @@ type Stmts struct {
updatePlugin *sql.Stmt
updatePluginInstall *sql.Stmt
updateTheme *sql.Stmt
updateForum *sql.Stmt
updateUser *sql.Stmt
updateGroupPerms *sql.Stmt
updateGroup *sql.Stmt
@ -380,13 +379,6 @@ func _gen_mssql() (err error) {
return err
}
log.Print("Preparing updateForum statement.")
stmts.updateForum, err = db.Prepare("UPDATE [forums] SET [name] = ?,[desc] = ?,[active] = ?,[preset] = ? WHERE [fid] = ?")
if err != nil {
log.Print("Bad Query: ","UPDATE [forums] SET [name] = ?,[desc] = ?,[active] = ?,[preset] = ? WHERE [fid] = ?")
return err
}
log.Print("Preparing updateUser statement.")
stmts.updateUser, err = db.Prepare("UPDATE [users] SET [name] = ?,[email] = ?,[group] = ? WHERE [uid] = ?")
if err != nil {

View File

@ -53,7 +53,6 @@ type Stmts struct {
updatePlugin *sql.Stmt
updatePluginInstall *sql.Stmt
updateTheme *sql.Stmt
updateForum *sql.Stmt
updateUser *sql.Stmt
updateGroupPerms *sql.Stmt
updateGroup *sql.Stmt
@ -340,12 +339,6 @@ func _gen_mysql() (err error) {
return err
}
log.Print("Preparing updateForum statement.")
stmts.updateForum, err = db.Prepare("UPDATE `forums` SET `name` = ?,`desc` = ?,`active` = ?,`preset` = ? WHERE `fid` = ?")
if err != nil {
return err
}
log.Print("Preparing updateUser statement.")
stmts.updateUser, err = db.Prepare("UPDATE `users` SET `name` = ?,`email` = ?,`group` = ? WHERE `uid` = ?")
if err != nil {

View File

@ -15,7 +15,6 @@ type Stmts struct {
updatePlugin *sql.Stmt
updatePluginInstall *sql.Stmt
updateTheme *sql.Stmt
updateForum *sql.Stmt
updateUser *sql.Stmt
updateGroupPerms *sql.Stmt
updateGroup *sql.Stmt
@ -80,12 +79,6 @@ func _gen_pgsql() (err error) {
return err
}
log.Print("Preparing updateForum statement.")
stmts.updateForum, err = db.Prepare("UPDATE `forums` SET `name` = ?,`desc` = ?,`active` = ?,`preset` = ? WHERE `fid` = ?")
if err != nil {
return err
}
log.Print("Preparing updateUser statement.")
stmts.updateUser, err = db.Prepare("UPDATE `users` SET `name` = ?,`email` = ?,`group` = ? WHERE `uid` = ?")
if err != nil {

View File

@ -50,6 +50,17 @@ func (ins *MysqlInstaller) DefaultPort() string {
return "3306"
}
func (ins *MysqlInstaller) dbExists(dbName string) (bool, error) {
var waste string
err := ins.db.QueryRow("SHOW DATABASES LIKE '" + dbName + "'").Scan(&waste)
if err != nil && err != sql.ErrNoRows {
return false, err
} else if err == sql.ErrNoRows {
return false, nil
}
return true, nil
}
func (ins *MysqlInstaller) InitDatabase() (err error) {
_dbPassword := ins.dbPassword
if _dbPassword != "" {
@ -67,13 +78,13 @@ func (ins *MysqlInstaller) InitDatabase() (err error) {
}
fmt.Println("Successfully connected to the database")
var waste string
err = db.QueryRow("SHOW DATABASES LIKE '" + ins.dbName + "'").Scan(&waste)
if err != nil && err != sql.ErrNoRows {
ins.db = db
ok, err := ins.dbExists(ins.dbName)
if err != nil {
return err
}
if err == sql.ErrNoRows {
if !ok {
fmt.Println("Unable to find the database. Attempting to create it")
_, err = db.Exec("CREATE DATABASE IF NOT EXISTS " + ins.dbName)
if err != nil {
@ -82,7 +93,7 @@ func (ins *MysqlInstaller) InitDatabase() (err error) {
fmt.Println("The database was successfully created")
}
fmt.Println("Switching to database " + ins.dbName)
fmt.Println("Switching to database ", ins.dbName)
_, err = db.Exec("USE " + ins.dbName)
if err != nil {
return err
@ -90,13 +101,7 @@ func (ins *MysqlInstaller) InitDatabase() (err error) {
// Ready the query builder
qgen.Builder.SetConn(db)
err = qgen.Builder.SetAdapter("mysql")
if err != nil {
return err
}
ins.db = db
return nil
return qgen.Builder.SetAdapter("mysql")
}
func (ins *MysqlInstaller) TableDefs() (err error) {
@ -122,7 +127,7 @@ func (ins *MysqlInstaller) TableDefs() (err error) {
return err
}
fmt.Println("Creating table '" + table + "'")
fmt.Printf("Creating table '%s'", table)
data, err := ioutil.ReadFile("./schema/mysql/" + f.Name())
if err != nil {
return err
@ -135,7 +140,6 @@ func (ins *MysqlInstaller) TableDefs() (err error) {
return err
}
}
//fmt.Println("Finished creating the tables")
return nil
}
@ -163,8 +167,6 @@ func (ins *MysqlInstaller) InitialData() error {
return err
}
}
//fmt.Println("Finished inserting the database data")
return nil
}

View File

@ -69,14 +69,9 @@ func (ins *PgsqlInstaller) InitDatabase() (err error) {
// TODO: Create the database, if it doesn't exist
// Ready the query builder
qgen.Builder.SetConn(db)
err = qgen.Builder.SetAdapter("pgsql")
if err != nil {
return err
}
ins.db = db
return nil
qgen.Builder.SetConn(db)
return qgen.Builder.SetAdapter("pgsql")
}
func (ins *PgsqlInstaller) TableDefs() (err error) {

View File

@ -607,8 +607,8 @@ func TestForumPermsStore(t *testing.T) {
if !gloinited {
gloinit()
}
if !pluginsInited {
initPlugins()
if !common.PluginsInited {
common.InitPlugins()
}
}
@ -617,8 +617,8 @@ func TestGroupStore(t *testing.T) {
if !gloinited {
gloinit()
}
if !pluginsInited {
initPlugins()
if !common.PluginsInited {
common.InitPlugins()
}
_, err := common.Gstore.Get(-1)
@ -728,8 +728,8 @@ func TestReplyStore(t *testing.T) {
if !gloinited {
gloinit()
}
if !pluginsInited {
initPlugins()
if !common.PluginsInited {
common.InitPlugins()
}
_, err := common.Rstore.Get(-1)
@ -770,8 +770,8 @@ func TestProfileReplyStore(t *testing.T) {
if !gloinited {
gloinit()
}
if !pluginsInited {
initPlugins()
if !common.PluginsInited {
common.InitPlugins()
}
_, err := common.Prstore.Get(-1)
@ -845,7 +845,7 @@ func TestAuth(t *testing.T) {
/* No extra salt tests, we might not need this extra salt, as bcrypt has it's own? */
realPassword = "Madame Cassandra's Mystic Orb"
t.Log("Set realPassword to '" + realPassword + "'")
t.Logf("Set realPassword to '%s'", realPassword)
t.Log("Hashing the real password")
hashedPassword, err = common.BcryptGeneratePasswordNoSalt(realPassword)
if err != nil {
@ -853,34 +853,31 @@ func TestAuth(t *testing.T) {
}
password = realPassword
t.Log("Testing password '" + password + "'")
t.Log("Testing salt '" + salt + "'")
t.Logf("Testing password '%s'", password)
t.Logf("Testing salt '%s'", salt)
err = common.CheckPassword(hashedPassword, password, salt)
if err == ErrMismatchedHashAndPassword {
if err == common.ErrMismatchedHashAndPassword {
t.Error("The two don't match")
} else if err == ErrPasswordTooLong {
} else if err == common.ErrPasswordTooLong {
t.Error("CheckPassword thinks the password is too long")
} else if err != nil {
t.Error(err)
}
password = "hahaha"
t.Log("Testing password '" + password + "'")
t.Log("Testing salt '" + salt + "'")
t.Logf("Testing password '%s'", password)
t.Logf("Testing salt '%s'", salt)
err = common.CheckPassword(hashedPassword, password, salt)
if err == ErrPasswordTooLong {
if err == common.ErrPasswordTooLong {
t.Error("CheckPassword thinks the password is too long")
} else if err == nil {
t.Error("The two shouldn't match!")
}
password = "Madame Cassandra's Mystic"
t.Log("Testing password '" + password + "'")
t.Log("Testing salt '" + salt + "'")
t.Logf("Testing password '%s'", password)
t.Logf("Testing salt '%s'", salt)
err = common.CheckPassword(hashedPassword, password, salt)
if err == ErrPasswordTooLong {
t.Error("CheckPassword thinks the password is too long")
} else if err == nil {
t.Error("The two shouldn't match!")
}
expect(t, err != common.ErrPasswordTooLong, "CheckPassword thinks the password is too long")
expect(t, err != nil, "The two shouldn't match!")
}

View File

@ -425,9 +425,6 @@ func routePanelForumsEditPermsSubmit(w http.ResponseWriter, r *http.Request, use
return common.LocalErrorJSQ("Invalid Group ID", w, r, user, isJs)
}
permPreset := common.StripInvalidGroupForumPreset(r.PostFormValue("perm_preset"))
fperms, changed := common.GroupForumPresetToForumPerms(permPreset)
forum, err := common.Fstore.Get(fid)
if err == ErrNoRows {
return common.LocalErrorJSQ("This forum doesn't exist", w, r, user, isJs)
@ -435,33 +432,10 @@ func routePanelForumsEditPermsSubmit(w http.ResponseWriter, r *http.Request, use
return common.InternalErrorJSQ(err, w, r, isJs)
}
// ! IMPORTANT
// TODO: Refactor this
common.ForumUpdateMutex.Lock()
defer common.ForumUpdateMutex.Unlock()
if changed {
common.PermUpdateMutex.Lock()
defer common.PermUpdateMutex.Unlock()
group, err := common.Gstore.Get(gid)
if err != nil {
return common.LocalError("The group whose permissions you're updating doesn't exist.", w, r, user)
}
group.Forums[fid] = fperms
err = common.ReplaceForumPermsForGroup(gid, map[int]string{fid: permPreset}, map[int]common.ForumPerms{fid: fperms})
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
// TODO: Add this and replaceForumPermsForGroup into a transaction?
_, err = stmts.updateForum.Exec(forum.Name, forum.Desc, forum.Active, "", fid)
if err != nil {
return common.InternalErrorJSQ(err, w, r, isJs)
}
err = common.Fstore.Reload(fid)
if err != nil {
return common.LocalErrorJSQ("Unable to reload forum", w, r, user, isJs)
}
permPreset := common.StripInvalidGroupForumPreset(r.PostFormValue("perm_preset"))
err = forum.SetPreset(permPreset, gid)
if err != nil {
return common.LocalErrorJSQ(err.Error(), w, r, user, isJs)
}
if !isJs {

View File

@ -181,15 +181,16 @@ func processWhere(wherestr string) (where []DBWhere) {
segment += ")"
for i := 0; i < len(segment); i++ {
char := segment[i]
if '0' <= char && char <= '9' {
switch {
case '0' <= char && char <= '9':
i = tmpWhere.parseNumber(segment, i)
} else if ('a' <= char && char <= 'z') || ('A' <= char && char <= 'Z') || char == '_' {
case ('a' <= char && char <= 'z') || ('A' <= char && char <= 'Z') || char == '_':
i = tmpWhere.parseColumn(segment, i)
} else if char == '\'' {
case char == '\'':
i = tmpWhere.parseString(segment, i)
} else if isOpByte(char) {
case isOpByte(char):
i = tmpWhere.parseOperator(segment, i)
} else if char == '?' {
case char == '?':
tmpWhere.Expr = append(tmpWhere.Expr, DBToken{"?", "substitute"})
}
}
@ -216,11 +217,12 @@ func (setter *DBSetter) parseColumn(segment string, i int) int {
var buffer string
for ; i < len(segment); i++ {
char := segment[i]
if ('a' <= char && char <= 'z') || ('A' <= char && char <= 'Z') || char == '_' {
switch {
case ('a' <= char && char <= 'z') || ('A' <= char && char <= 'Z') || char == '_':
buffer += string(char)
} else if char == '(' {
case char == '(':
return setter.parseFunction(segment, buffer, i)
} else {
default:
i--
setter.Expr = append(setter.Expr, DBToken{buffer, "column"})
return i
@ -303,15 +305,16 @@ func processSet(setstr string) (setter []DBSetter) {
for i := 0; i < len(segment); i++ {
char := segment[i]
if '0' <= char && char <= '9' {
switch {
case '0' <= char && char <= '9':
i = tmpSetter.parseNumber(segment, i)
} else if ('a' <= char && char <= 'z') || ('A' <= char && char <= 'Z') || char == '_' {
case ('a' <= char && char <= 'z') || ('A' <= char && char <= 'Z') || char == '_':
i = tmpSetter.parseColumn(segment, i)
} else if char == '\'' {
case char == '\'':
i = tmpSetter.parseString(segment, i)
} else if isOpByte(char) {
case isOpByte(char):
i = tmpSetter.parseOperator(segment, i)
} else if char == '?' {
case char == '?':
tmpSetter.Expr = append(tmpSetter.Expr, DBToken{"?", "substitute"})
}
}

View File

@ -352,8 +352,6 @@ func writeUpdates(adapter qgen.Adapter) error {
build.Update("updateTheme").Table("themes").Set("default = ?").Where("uname = ?").Parse()
build.Update("updateForum").Table("forums").Set("name = ?, desc = ?, active = ?, preset = ?").Where("fid = ?").Parse()
build.Update("updateUser").Table("users").Set("name = ?, email = ?, group = ?").Where("uid = ?").Parse()
build.Update("updateGroupPerms").Table("users_groups").Set("permissions = ?").Where("gid = ?").Parse()

View File

@ -849,13 +849,12 @@ func routeRegisterSubmit(w http.ResponseWriter, r *http.Request, user common.Use
}
password := r.PostFormValue("password")
if password == "" {
switch password {
case "":
return common.LocalError("You didn't put in a password.", w, r, user)
}
if password == username {
case username:
return common.LocalError("You can't use your username as your password.", w, r, user)
}
if password == email {
case email:
return common.LocalError("You can't use your email as your password.", w, r, user)
}

View File

@ -157,7 +157,7 @@ ul {
display: none;
}
.rowblock {
.rowblock, .colstack_item {
margin-bottom: 12px;
border: 1px solid var(--header-border-color);
border-bottom: 2px solid var(--header-border-color);