Remove CSRF from page, post, category delete routes (#56)

- Chapter delete swap target needs work
This commit is contained in:
Daniel J. Summers 2025-07-04 22:24:32 -04:00
parent 87fbb1a8c7
commit d1840f63e5
5 changed files with 50 additions and 58 deletions

View File

@ -218,7 +218,7 @@ let router : HttpHandler = choose [
]) ])
route "/upload/save" >=> Upload.save route "/upload/save" >=> Upload.save
] ]
DELETE >=> validateCsrf >=> choose [ DELETE >=> choose [
routef "/category/%s" Admin.Category.delete routef "/category/%s" Admin.Category.delete
subRoute "/page" (choose [ subRoute "/page" (choose [
routef "/%s" Page.delete routef "/%s" Page.delete

View File

@ -527,7 +527,7 @@ let manageRevisions (model: ManageRevisionsModel) app = [
span [ _class "text-muted" ] [ raw " • " ] span [ _class "text-muted" ] [ raw " • " ]
a [ _href revRestore; _hxPost revRestore ] [ raw "Restore as Current" ] a [ _href revRestore; _hxPost revRestore ] [ raw "Restore as Current" ]
span [ _class "text-muted" ] [ raw " • " ] span [ _class "text-muted" ] [ raw " • " ]
a [ _href revUrlPrefix; _hxDelete revUrlPrefix; _hxTarget $"#{asOfId}" a [ _href revUrlPrefix; _hxDelete revUrlPrefix; _hxTarget $"#{asOfId}"; _hxPushUrl "false"
_hxSwap HxSwap.OuterHtml; _class "text-danger" ] [ _hxSwap HxSwap.OuterHtml; _class "text-danger" ] [
raw "Delete" raw "Delete"
] ]

View File

@ -29,7 +29,7 @@ let pageEdit (model: EditPageModel) templates app = [
/// Display a list of pages for this web log /// Display a list of pages for this web log
let pageList (pages: DisplayPage list) pageNbr hasNext app = [ let pageList (pages: DisplayPage list) pageNbr hasNext app = [
h2 [ _class "my-3" ] [ raw app.PageTitle ] h2 [ _class "my-3" ] [ raw app.PageTitle ]
article [] [ article [ _class "container mb-3" ] [
a [ _href (relUrl app "admin/page/new/edit"); _class "btn btn-primary btn-sm mb-3" ] [ raw "Create a New Page" ] a [ _href (relUrl app "admin/page/new/edit"); _class "btn btn-primary btn-sm mb-3" ] [ raw "Create a New Page" ]
if pages.Length = 0 then if pages.Length = 0 then
p [ _class "text-muted fst-italic text-center" ] [ raw "This web log has no pages" ] p [ _class "text-muted fst-italic text-center" ] [ raw "This web log has no pages" ]
@ -37,50 +37,47 @@ let pageList (pages: DisplayPage list) pageNbr hasNext app = [
let titleCol = "col-12 col-md-5" let titleCol = "col-12 col-md-5"
let linkCol = "col-12 col-md-5" let linkCol = "col-12 col-md-5"
let upd8Col = "col-12 col-md-2" let upd8Col = "col-12 col-md-2"
form [ _method "post"; _class "container mb-3"; _hxTarget "body" ] [ div [ _class "row mwl-table-heading" ] [
antiCsrf app div [ _class titleCol ] [
div [ _class "row mwl-table-heading" ] [ span [ _class "d-none d-md-inline" ] [ raw "Title" ]; span [ _class "d-md-none" ] [ raw "Page" ]
div [ _class titleCol ] [
span [ _class "d-none d-md-inline" ] [ raw "Title" ]; span [ _class "d-md-none" ] [ raw "Page" ]
]
div [ _class $"{linkCol} d-none d-md-inline-block" ] [ raw "Permalink" ]
div [ _class $"{upd8Col} d-none d-md-inline-block" ] [ raw "Updated" ]
] ]
for pg in pages do div [ _class $"{linkCol} d-none d-md-inline-block" ] [ raw "Permalink" ]
let pageLink = if pg.IsDefault then "" else pg.Permalink div [ _class $"{upd8Col} d-none d-md-inline-block" ] [ raw "Updated" ]
div [ _class "row mwl-table-detail" ] [ ]
div [ _class titleCol ] [ for pg in pages do
txt pg.Title let pageLink = if pg.IsDefault then "" else pg.Permalink
if pg.IsDefault then div [ _class "row mwl-table-detail" ] [
raw "   "; span [ _class "badge bg-success" ] [ raw "HOME PAGE" ] div [ _class titleCol ] [
if pg.IsInPageList then txt pg.Title
raw "   "; span [ _class "badge bg-primary" ] [ raw "IN PAGE LIST" ] if pg.IsDefault then
br [] ; small [] [ raw "   "; span [ _class "badge bg-success" ] [ raw "HOME PAGE" ]
let adminUrl = relUrl app $"admin/page/{pg.Id}" if pg.IsInPageList then
a [ _href (relUrl app pageLink); _target "_blank" ] [ raw "View Page" ] raw "   "; span [ _class "badge bg-primary" ] [ raw "IN PAGE LIST" ]
if app.IsEditor || (app.IsAuthor && app.UserId.Value = WebLogUserId pg.AuthorId) then br [] ; small [] [
span [ _class "text-muted" ] [ raw " • " ] let adminUrl = relUrl app $"admin/page/{pg.Id}"
a [ _href $"{adminUrl}/edit" ] [ raw "Edit" ] a [ _href (relUrl app pageLink); _target "_blank" ] [ raw "View Page" ]
if app.IsWebLogAdmin then if app.IsEditor || (app.IsAuthor && app.UserId.Value = WebLogUserId pg.AuthorId) then
span [ _class "text-muted" ] [ raw " • " ] span [ _class "text-muted" ] [ raw " • " ]
a [ _href adminUrl; _hxDelete adminUrl; _class "text-danger" a [ _href $"{adminUrl}/edit" ] [ raw "Edit" ]
_hxConfirm $"Are you sure you want to delete the page “{pg.Title}”? This action cannot be undone." ] [ if app.IsWebLogAdmin then
raw "Delete" span [ _class "text-muted" ] [ raw " • " ]
] a [ _href adminUrl; _hxDelete adminUrl; _hxTarget "body"; _class "text-danger"
] _hxConfirm $"Are you sure you want to delete the page “{pg.Title}”? This action cannot be undone." ] [
] raw "Delete"
div [ _class linkCol ] [ ]
small [ _class "d-md-none" ] [ txt pageLink ]
span [ _class "d-none d-md-inline" ] [ txt pageLink ]
]
div [ _class upd8Col ] [
small [ _class "d-md-none text-muted" ] [
raw "Updated "; txt (pg.UpdatedOn.ToString "MMMM d, yyyy")
]
span [ _class "d-none d-md-inline" ] [ txt (pg.UpdatedOn.ToString "MMMM d, yyyy") ]
] ]
] ]
] div [ _class linkCol ] [
small [ _class "d-md-none" ] [ txt pageLink ]
span [ _class "d-none d-md-inline" ] [ txt pageLink ]
]
div [ _class upd8Col ] [
small [ _class "d-md-none text-muted" ] [
raw "Updated "; txt (pg.UpdatedOn.ToString "MMMM d, yyyy")
]
span [ _class "d-none d-md-inline" ] [ txt (pg.UpdatedOn.ToString "MMMM d, yyyy") ]
]
]
if pageNbr > 1 || hasNext then if pageNbr > 1 || hasNext then
div [ _class "d-flex justify-content-evenly mb-3" ] [ div [ _class "d-flex justify-content-evenly mb-3" ] [
div [] [ div [] [

View File

@ -77,7 +77,7 @@ let chapterEdit (model: EditChapterModel) app = [
em [ _class "form-text" ] [ em [ _class "form-text" ] [
raw "Optional; " raw "Optional; "
a [ _href "https://www.openstreetmap.org/"; _target "_blank"; _relNoOpener ] [ raw "get ID" ] a [ _href "https://www.openstreetmap.org/"; _target "_blank"; _relNoOpener ] [ raw "get ID" ]
raw ", " raw ", "
a [ _href "https://github.com/Podcastindex-org/podcast-namespace/blob/main/location/location.md#osm-recommended" a [ _href "https://github.com/Podcastindex-org/podcast-namespace/blob/main/location/location.md#osm-recommended"
_target "_blank"; _relNoOpener ] [ _target "_blank"; _relNoOpener ] [
raw "see spec" raw "see spec"
@ -102,9 +102,7 @@ let chapterEdit (model: EditChapterModel) app = [
/// Display a list of chapters /// Display a list of chapters
let chapterList withNew (model: ManageChaptersModel) app = let chapterList withNew (model: ManageChaptersModel) app =
form [ _method "post"; _id "chapter_list"; _class "container mb-3"; _hxTarget "this"; _hxSwap HxSwap.OuterHtml ] [ div [ _id "chapter_list"; _class "container mb-3"; _hxTarget "this"; _hxSwap HxSwap.OuterHtml ] [
antiCsrf app
input [ _type "hidden"; _name "Id"; _value model.Id ]
div [ _class "row mwl-table-heading" ] [ div [ _class "row mwl-table-heading" ] [
div [ _class "col-3 col-md-2" ] [ raw "Start" ] div [ _class "col-3 col-md-2" ] [ raw "Start" ]
div [ _class "col-3 col-md-6 col-lg-8" ] [ raw "Title" ] div [ _class "col-3 col-md-6 col-lg-8" ] [ raw "Title" ]
@ -355,7 +353,7 @@ let postEdit (model: EditPostModel) templates (ratings: MetaItem list) app = [
] ]
div [ _class "col-12 col-md-4 pb-3" ] [ div [ _class "col-12 col-md-4 pb-3" ] [
selectField [] (nameof model.Explicit) "Explicit Rating" model.Explicit ratings selectField [] (nameof model.Explicit) "Explicit Rating" model.Explicit ratings
(_.Name) (_.Value) [ _.Name _.Value [
div [ _class "form-text" ] [ raw "Optional; overrides podcast default" ] div [ _class "form-text" ] [ raw "Optional; overrides podcast default" ]
] ]
] ]

View File

@ -33,7 +33,7 @@ let categoryEdit (model: EditCategoryModel) app =
|> String.concat "" |> String.concat ""
{ Name = c.Id; Value = $"{parents}{c.Name}" }) { Name = c.Id; Value = $"{parents}{c.Name}" })
|> Seq.append [ { Name = ""; Value = "– None –" } ] |> Seq.append [ { Name = ""; Value = "– None –" } ]
selectField [] (nameof model.ParentId) "Parent Category" model.ParentId cats (_.Name) (_.Value) [] selectField [] (nameof model.ParentId) "Parent Category" model.ParentId cats _.Name _.Value []
] ]
div [ _class "col-12 col-xl-10 offset-xl-1 mb-3" ] [ div [ _class "col-12 col-xl-10 offset-xl-1 mb-3" ] [
textField [] (nameof model.Description) "Description" model.Description [] textField [] (nameof model.Description) "Description" model.Description []
@ -107,9 +107,6 @@ let categoryList includeNew app = [
div [ _class catCol ] [ raw "Category"; span [ _class "d-md-none" ] [ raw "; Description" ] ] div [ _class catCol ] [ raw "Category"; span [ _class "d-md-none" ] [ raw "; Description" ] ]
div [ _class $"{descCol} d-none d-md-inline-block" ] [ raw "Description" ] div [ _class $"{descCol} d-none d-md-inline-block" ] [ raw "Description" ]
] ]
]
form [ _method "post"; _class "container" ] [
antiCsrf app
div [ _class "row mwl-table-detail"; _id "cat_new" ] [ if includeNew then loadNew ] div [ _class "row mwl-table-detail"; _id "cat_new" ] [ if includeNew then loadNew ]
yield! app.Categories |> Seq.ofArray |> Seq.map categoryDetail |> List.ofSeq yield! app.Categories |> Seq.ofArray |> Seq.map categoryDetail |> List.ofSeq
] ]
@ -249,8 +246,8 @@ let feedEdit (model: EditCustomFeedModel) (ratings: MetaItem list) (mediums: Met
|> Seq.append [ { Name = ""; Value = "– Select Category –" } ] |> Seq.append [ { Name = ""; Value = "– Select Category –" } ]
selectField [ _id "SourceValueCat"; _required selectField [ _id "SourceValueCat"; _required
if model.SourceType = "tag" then _disabled ] if model.SourceType = "tag" then _disabled ]
(nameof model.SourceValue) "Category" model.SourceValue cats (_.Name) (nameof model.SourceValue) "Category" model.SourceValue cats _.Name _.Value
(_.Value) [] []
] ]
div [ _class "col-1 d-flex justify-content-end pb-3" ] [ div [ _class "col-1 d-flex justify-content-end pb-3" ] [
div [ _class "form-check form-check-inline me-0" ] [ div [ _class "form-check form-check-inline me-0" ] [
@ -305,7 +302,7 @@ let feedEdit (model: EditCustomFeedModel) (ratings: MetaItem list) (mediums: Met
] ]
div [ _class "col-12 col-md-3 col-lg-2 pb-3" ] [ div [ _class "col-12 col-md-3 col-lg-2 pb-3" ] [
selectField [ _required ] (nameof model.Explicit) "Explicit Rating" model.Explicit selectField [ _required ] (nameof model.Explicit) "Explicit Rating" model.Explicit
ratings (_.Name) (_.Value) [] ratings _.Name _.Value []
] ]
] ]
div [ _class "row" ] [ div [ _class "row" ] [
@ -380,7 +377,7 @@ let feedEdit (model: EditCustomFeedModel) (ratings: MetaItem list) (mediums: Met
] ]
] ]
div [ _class "col-4 col-lg-3 offset-lg-2 pb-3" ] [ div [ _class "col-4 col-lg-3 offset-lg-2 pb-3" ] [
selectField [] (nameof model.Medium) "Medium" model.Medium mediums (_.Name) (_.Value) [ selectField [] (nameof model.Medium) "Medium" model.Medium mediums _.Name _.Value [
span [ _class "form-text fst-italic" ] [ span [ _class "form-text fst-italic" ] [
raw "Optional; medium of the podcast content (" raw "Optional; medium of the podcast content ("
a [ _href "https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md#medium" a [ _href "https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md#medium"
@ -779,7 +776,7 @@ let webLogSettings
] ]
div [ _class "col-12 col-md-6 offset-md-1 col-xl-4 offset-xl-0 pb-3" ] [ div [ _class "col-12 col-md-6 offset-md-1 col-xl-4 offset-xl-0 pb-3" ] [
selectField [ _required ] (nameof model.DefaultPage) "Default Page" model.DefaultPage pages selectField [ _required ] (nameof model.DefaultPage) "Default Page" model.DefaultPage pages
(fun p -> string p.Id) (_.Title) [] (fun p -> string p.Id) _.Title []
] ]
div [ _class "col-12 col-md-4 col-xl-2 pb-3" ] [ div [ _class "col-12 col-md-4 col-xl-2 pb-3" ] [
numberField [ _required; _min "0"; _max "50" ] (nameof model.PostsPerPage) "Posts per Page" numberField [ _required; _min "0"; _max "50" ] (nameof model.PostsPerPage) "Posts per Page"