Add permission check on post editing
This commit is contained in:
parent
27b8157a89
commit
e9ba9b3dde
|
@ -0,0 +1,44 @@
|
|||
package migrations
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"git.handmade.network/hmn/hmn/src/migration/types"
|
||||
"git.handmade.network/hmn/hmn/src/oops"
|
||||
"github.com/jackc/pgx/v4"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registerMigration(DropSuperuserColumn{})
|
||||
}
|
||||
|
||||
type DropSuperuserColumn struct{}
|
||||
|
||||
func (m DropSuperuserColumn) Version() types.MigrationVersion {
|
||||
return types.MigrationVersion(time.Date(2021, 7, 22, 1, 59, 29, 0, time.UTC))
|
||||
}
|
||||
|
||||
func (m DropSuperuserColumn) Name() string {
|
||||
return "DropSuperuserColumn"
|
||||
}
|
||||
|
||||
func (m DropSuperuserColumn) Description() string {
|
||||
return "Drop the is_superuser column on users, in favor of is_staff"
|
||||
}
|
||||
|
||||
func (m DropSuperuserColumn) Up(ctx context.Context, tx pgx.Tx) error {
|
||||
_, err := tx.Exec(ctx, `
|
||||
ALTER TABLE auth_user
|
||||
DROP is_superuser;
|
||||
`)
|
||||
if err != nil {
|
||||
return oops.New(err, "failed to drop superuser column")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m DropSuperuserColumn) Down(ctx context.Context, tx pgx.Tx) error {
|
||||
panic("Implement me")
|
||||
}
|
|
@ -17,9 +17,8 @@ type User struct {
|
|||
DateJoined time.Time `db:"date_joined"`
|
||||
LastLogin *time.Time `db:"last_login"`
|
||||
|
||||
IsSuperuser bool `db:"is_superuser"`
|
||||
IsStaff bool `db:"is_staff"`
|
||||
IsActive bool `db:"is_active"`
|
||||
IsStaff bool `db:"is_staff"`
|
||||
IsActive bool `db:"is_active"`
|
||||
|
||||
Name string `db:"name"`
|
||||
Bio string `db:"bio"`
|
||||
|
|
|
@ -157,11 +157,10 @@ func UserToTemplate(u *models.User, currentTheme string) User {
|
|||
}
|
||||
|
||||
return User{
|
||||
ID: u.ID,
|
||||
Username: u.Username,
|
||||
Email: email,
|
||||
IsSuperuser: u.IsSuperuser,
|
||||
IsStaff: u.IsStaff,
|
||||
ID: u.ID,
|
||||
Username: u.Username,
|
||||
Email: email,
|
||||
IsStaff: u.IsStaff,
|
||||
|
||||
Name: UserDisplayName(u),
|
||||
Blurb: u.Blurb,
|
||||
|
|
|
@ -19,8 +19,8 @@
|
|||
|
||||
{{ define "content" }}
|
||||
<div class="content-block ph3 ph0-ns">
|
||||
{{ if .ThreadTitle }}
|
||||
<h2>{{ .ThreadTitle }}</h2>
|
||||
{{ if .Title }}
|
||||
<h2>{{ .Title }}</h2>
|
||||
{{ end }}
|
||||
<div class="flex flex-column flex-row-ns">
|
||||
<form id="form" action="{{ .SubmitUrl }}" method="post" class="flex-fair-ns">
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<header class="mb3">
|
||||
<div class="user-options flex justify-center justify-end-ns">
|
||||
{{ if .User }}
|
||||
{{ if .User.IsSuperuser }}
|
||||
{{ if .User.IsStaff }}
|
||||
<a class="admin-panel" href="{{ .Header.AdminUrl }}"><span class="icon-settings"> Admin</span></a>
|
||||
{{ end }}
|
||||
<a class="username settings" href="{{ .Header.UserSettingsUrl }}"><span class="icon-settings"></span> {{ .User.Username }}</a>
|
||||
|
|
|
@ -117,11 +117,10 @@ type Project struct {
|
|||
}
|
||||
|
||||
type User struct {
|
||||
ID int
|
||||
Username string
|
||||
Email string
|
||||
IsSuperuser bool
|
||||
IsStaff bool
|
||||
ID int
|
||||
Username string
|
||||
Email string
|
||||
IsStaff bool
|
||||
|
||||
Name string
|
||||
Blurb string
|
||||
|
|
|
@ -41,7 +41,7 @@ type forumSubcategoryData struct {
|
|||
type editorData struct {
|
||||
templates.BaseData
|
||||
SubmitUrl string
|
||||
ThreadTitle string
|
||||
Title string
|
||||
SubmitLabel string
|
||||
|
||||
IsEditing bool // false if new post, true if updating existing one
|
||||
|
@ -710,7 +710,7 @@ func ForumPostReply(c *RequestContext) ResponseData {
|
|||
SubmitUrl: hmnurl.BuildForumPostReply(c.CurrentProject.Slug, lineageBuilder.GetSubforumLineageSlugs(currentCatId), requestedThreadId, requestedPostId),
|
||||
SubmitLabel: "Submit Reply",
|
||||
|
||||
ThreadTitle: result.Thread.Title,
|
||||
Title: "Replying to post",
|
||||
PostReplyingTo: &templatePost,
|
||||
}, c.Perf)
|
||||
return res
|
||||
|
@ -820,6 +820,12 @@ func ForumPostEdit(c *RequestContext) ResponseData {
|
|||
}
|
||||
result := postQueryResult.(*postQuery)
|
||||
|
||||
// Ensure that the user is permitted to edit the post
|
||||
isPostAuthor := result.Author != nil && result.Author.ID == c.CurrentUser.ID
|
||||
if !(isPostAuthor || c.CurrentUser.IsStaff) {
|
||||
return FourOhFour(c)
|
||||
}
|
||||
|
||||
baseData := getBaseData(c)
|
||||
baseData.Title = fmt.Sprintf("Editing \"%s\" | %s", result.Thread.Title, *categoryTree[currentCatId].Name)
|
||||
baseData.MathjaxEnabled = true
|
||||
|
@ -832,7 +838,7 @@ func ForumPostEdit(c *RequestContext) ResponseData {
|
|||
res.MustWriteTemplate("editor.html", editorData{
|
||||
BaseData: baseData,
|
||||
SubmitUrl: hmnurl.BuildForumPostEdit(c.CurrentProject.Slug, lineageBuilder.GetSubforumLineageSlugs(currentCatId), requestedThreadId, requestedPostId),
|
||||
ThreadTitle: result.Thread.Title,
|
||||
Title: result.Thread.Title,
|
||||
SubmitLabel: "Submit Edited Post",
|
||||
|
||||
IsEditing: true,
|
||||
|
@ -868,8 +874,38 @@ func ForumPostEditSubmit(c *RequestContext) ResponseData {
|
|||
return FourOhFour(c)
|
||||
}
|
||||
|
||||
c.Req.ParseForm()
|
||||
// Ensure that the user is permitted to edit the post
|
||||
type postResult struct {
|
||||
AuthorID *int `db:"author.id"`
|
||||
}
|
||||
iresult, err := db.QueryOne(c.Context(), c.Conn, postResult{},
|
||||
`
|
||||
SELECT $columns
|
||||
FROM
|
||||
handmade_post AS post
|
||||
LEFT JOIN auth_user AS author ON post.author_id = author.id
|
||||
WHERE
|
||||
post.category_id = $1
|
||||
AND post.thread_id = $2
|
||||
AND post.id = $3
|
||||
AND NOT post.deleted
|
||||
ORDER BY postdate
|
||||
`,
|
||||
currentCatId,
|
||||
threadId,
|
||||
postId,
|
||||
)
|
||||
if err != nil && !errors.Is(err, db.ErrNoMatchingRows) {
|
||||
return ErrorResponse(http.StatusInternalServerError, oops.New(err, "failed to get author of post to delete"))
|
||||
}
|
||||
result := iresult.(*postResult)
|
||||
|
||||
isPostAuthor := result.AuthorID != nil && *result.AuthorID == c.CurrentUser.ID
|
||||
if !(isPostAuthor || c.CurrentUser.IsStaff) {
|
||||
return FourOhFour(c)
|
||||
}
|
||||
|
||||
c.Req.ParseForm()
|
||||
unparsed := c.Req.Form.Get("body")
|
||||
editReason := c.Req.Form.Get("editreason")
|
||||
|
||||
|
@ -888,8 +924,8 @@ func createNewForumPostAndVersion(ctx context.Context, tx pgx.Tx, catId, threadI
|
|||
// Create post
|
||||
err := tx.QueryRow(ctx,
|
||||
`
|
||||
INSERT INTO handmade_post (postdate, category_id, thread_id, current_id, author_id, category_kind, project_id, reply_id)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
INSERT INTO handmade_post (postdate, category_id, thread_id, current_id, author_id, category_kind, project_id, reply_id, preview)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
|
||||
RETURNING id
|
||||
`,
|
||||
time.Now(),
|
||||
|
@ -900,6 +936,7 @@ func createNewForumPostAndVersion(ctx context.Context, tx pgx.Tx, catId, threadI
|
|||
models.CatKindForum,
|
||||
projectId,
|
||||
replyId,
|
||||
"", // empty preview, will be updated later
|
||||
).Scan(&postId)
|
||||
if err != nil {
|
||||
panic(oops.New(err, "failed to create post"))
|
||||
|
|
|
@ -288,7 +288,7 @@ func ProjectHomepage(c *RequestContext) ResponseData {
|
|||
canView := false
|
||||
canEdit := false
|
||||
if c.CurrentUser != nil {
|
||||
if c.CurrentUser.IsSuperuser {
|
||||
if c.CurrentUser.IsStaff {
|
||||
canView = true
|
||||
canEdit = true
|
||||
} else {
|
||||
|
|
|
@ -162,6 +162,7 @@ func NewWebsiteRoutes(conn *pgxpool.Pool, perfCollector *perf.PerfCollector) htt
|
|||
mainRoutes.POST(hmnurl.RegexForumPostReply, authMiddleware(ForumPostReplySubmit))
|
||||
mainRoutes.GET(hmnurl.RegexForumPostEdit, authMiddleware(ForumPostEdit))
|
||||
mainRoutes.POST(hmnurl.RegexForumPostEdit, authMiddleware(ForumPostEditSubmit))
|
||||
// mainRoutes.GET(hmnurl.RegexForumPostDelete, authMiddleware(ForumPostDelete))
|
||||
|
||||
mainRoutes.GET(hmnurl.RegexProjectCSS, ProjectCSS)
|
||||
|
||||
|
|
|
@ -100,7 +100,7 @@ func UserProfile(c *RequestContext) ResponseData {
|
|||
AND ($2 OR (project.flags = 0 AND project.lifecycle = ANY ($3)))
|
||||
`,
|
||||
profileUser.ID,
|
||||
(c.CurrentUser != nil && (profileUser == c.CurrentUser || c.CurrentUser.IsSuperuser)),
|
||||
(c.CurrentUser != nil && (profileUser == c.CurrentUser || c.CurrentUser.IsStaff)),
|
||||
models.VisibleProjectLifecycles,
|
||||
)
|
||||
if err != nil {
|
||||
|
|
Loading…
Reference in New Issue