From 0416b1ed91dd90fd07ca3b90bcfea72da066283b Mon Sep 17 00:00:00 2001 From: Azareal Date: Wed, 17 Jan 2018 11:13:08 +0000 Subject: [PATCH] Fixed the Cosora Alerts CSS. Fixed the topic edit input CSS. You can now filter the time range of the routes list. You can now filter the time range of the agents list. Renamed operations.log to ops.log Moved the account routes into the new router. Log suspicious requests. Removed some duplicated code in the control panel routes. Fixed a bug in the User Editor where the Administrator group didn't show up. Users are now force logged out when an admin changes their password. --- common/pages.go | 2 + gen_router.go | 97 +++++++++++++++++++++++++-- main.go | 11 +-- member_routes.go | 16 ++++- panel_routes.go | 66 +++++++----------- router_gen/main.go | 14 ++++ router_gen/routes.go | 18 ++++- routes.go | 14 ++-- run.bat | 2 +- templates/panel_analytics_agents.html | 17 ++++- templates/panel_analytics_routes.html | 17 ++++- themes/cosora/public/main.css | 20 +++++- 12 files changed, 215 insertions(+), 79 deletions(-) diff --git a/common/pages.go b/common/pages.go index 8a55499a..a9634c4d 100644 --- a/common/pages.go +++ b/common/pages.go @@ -182,6 +182,7 @@ type PanelAnalyticsRoutesPage struct { Stats PanelStats Zone string ItemList []PanelAnalyticsRoutesItem + TimeRange string } type PanelAnalyticsAgentsItem struct { @@ -196,6 +197,7 @@ type PanelAnalyticsAgentsPage struct { Stats PanelStats Zone string ItemList []PanelAnalyticsAgentsItem + TimeRange string } type PanelAnalyticsRoutePage struct { diff --git a/gen_router.go b/gen_router.go index 0bee6d53..a0b880cb 100644 --- a/gen_router.go +++ b/gen_router.go @@ -97,6 +97,11 @@ var RouteMap = map[string]interface{}{ "routeProfileReplyCreateSubmit": routeProfileReplyCreateSubmit, "routeProfileReplyEditSubmit": routeProfileReplyEditSubmit, "routeProfileReplyDeleteSubmit": routeProfileReplyDeleteSubmit, + "routeLogin": routeLogin, + "routeRegister": routeRegister, + "routeLogout": routeLogout, + "routeLoginSubmit": routeLoginSubmit, + "routeRegisterSubmit": routeRegisterSubmit, "routeDynamic": routeDynamic, "routeUploads": routeUploads, } @@ -185,8 +190,13 @@ var routeMapEnum = map[string]int{ "routeProfileReplyCreateSubmit": 79, "routeProfileReplyEditSubmit": 80, "routeProfileReplyDeleteSubmit": 81, - "routeDynamic": 82, - "routeUploads": 83, + "routeLogin": 82, + "routeRegister": 83, + "routeLogout": 84, + "routeLoginSubmit": 85, + "routeRegisterSubmit": 86, + "routeDynamic": 87, + "routeUploads": 88, } var reverseRouteMapEnum = map[int]string{ 0: "routeAPI", @@ -271,8 +281,13 @@ var reverseRouteMapEnum = map[int]string{ 79: "routeProfileReplyCreateSubmit", 80: "routeProfileReplyEditSubmit", 81: "routeProfileReplyDeleteSubmit", - 82: "routeDynamic", - 83: "routeUploads", + 82: "routeLogin", + 83: "routeRegister", + 84: "routeLogout", + 85: "routeLoginSubmit", + 86: "routeRegisterSubmit", + 87: "routeDynamic", + 88: "routeUploads", } var agentMapEnum = map[string]int{ "unknown": 0, @@ -379,6 +394,19 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { var prefix, extraData string prefix = req.URL.Path[0:strings.IndexByte(req.URL.Path[1:],'/') + 1] if req.URL.Path[len(req.URL.Path) - 1] != '/' { + // TODO: Cover more suspicious strings and at a lower layer than this and more efficiently + if strings.Contains(req.URL.Path,"'") || strings.Contains(req.URL.Path,";") || strings.Contains(req.URL.Path,"\"") || strings.Contains(req.URL.Path,"`") || strings.Contains(req.URL.Path,"%") { + log.Print("Suspicious UA: ", req.UserAgent()) + log.Print("Method: ", req.Method) + for key, value := range req.Header { + for _, vvalue := range value { + log.Print("Header '" + key + "': " + vvalue + "!!") + } + } + log.Print("req.URL.Path: ", req.URL.Path) + log.Print("req.Referer(): ", req.Referer()) + log.Print("req.RemoteAddr: ", req.RemoteAddr) + } extraData = req.URL.Path[strings.LastIndexByte(req.URL.Path,'/') + 1:] req.URL.Path = req.URL.Path[:strings.LastIndexByte(req.URL.Path,'/') + 1] } @@ -775,9 +803,21 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { common.RouteViewCounter.Bump(36) err = routePanelAnalyticsViews(w,req,user) case "/panel/analytics/routes/": + err = common.ParseForm(w,req,user) + if err != nil { + router.handleError(err,w,req,user) + return + } + common.RouteViewCounter.Bump(37) err = routePanelAnalyticsRoutes(w,req,user) case "/panel/analytics/agents/": + err = common.ParseForm(w,req,user) + if err != nil { + router.handleError(err,w,req,user) + return + } + common.RouteViewCounter.Bump(38) err = routePanelAnalyticsAgents(w,req,user) case "/panel/analytics/route/": @@ -1298,6 +1338,51 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { if err != nil { router.handleError(err,w,req,user) } + case "/accounts": + switch(req.URL.Path) { + case "/accounts/login/": + common.RouteViewCounter.Bump(82) + err = routeLogin(w,req,user) + case "/accounts/create/": + common.RouteViewCounter.Bump(83) + err = routeRegister(w,req,user) + case "/accounts/logout/": + err = common.NoSessionMismatch(w,req,user) + if err != nil { + router.handleError(err,w,req,user) + return + } + + err = common.MemberOnly(w,req,user) + if err != nil { + router.handleError(err,w,req,user) + return + } + + common.RouteViewCounter.Bump(84) + err = routeLogout(w,req,user) + case "/accounts/login/submit/": + err = common.ParseForm(w,req,user) + if err != nil { + router.handleError(err,w,req,user) + return + } + + common.RouteViewCounter.Bump(85) + err = routeLoginSubmit(w,req,user) + case "/accounts/create/submit/": + err = common.ParseForm(w,req,user) + if err != nil { + router.handleError(err,w,req,user) + return + } + + common.RouteViewCounter.Bump(86) + err = routeRegisterSubmit(w,req,user) + } + if err != nil { + router.handleError(err,w,req,user) + } /*case "/sitemaps": // TODO: Count these views req.URL.Path += extraData err = sitemapSwitch(w,req) @@ -1309,7 +1394,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { common.NotFound(w,req) return } - common.RouteViewCounter.Bump(83) + common.RouteViewCounter.Bump(88) req.URL.Path += extraData // TODO: Find a way to propagate errors up from this? router.UploadHandler(w,req) // TODO: Count these views @@ -1353,7 +1438,7 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { router.RUnlock() if ok { - common.RouteViewCounter.Bump(82) // TODO: Be more specific about *which* dynamic route it is + common.RouteViewCounter.Bump(87) // TODO: Be more specific about *which* dynamic route it is req.URL.Path += extraData err = handle(w,req,user) if err != nil { diff --git a/main.go b/main.go index bcf60797..3dda626b 100644 --- a/main.go +++ b/main.go @@ -130,7 +130,7 @@ func main() { // TODO: Have a file for each run with the time/date the server started as the file name? // TODO: Log panics with recover() - f, err := os.OpenFile("./operations.log", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0755) + f, err := os.OpenFile("./ops.log", os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0755) if err != nil { log.Fatal(err) } @@ -303,15 +303,6 @@ func main() { // TODO: Move these routes into the new routes list log.Print("Initialising the router") router = NewGenRouter(http.FileServer(http.Dir("./uploads"))) - - // Accounts - router.HandleFunc("/accounts/login/", routeLogin) - router.HandleFunc("/accounts/create/", routeRegister) - router.HandleFunc("/accounts/logout/", routeLogout) - router.HandleFunc("/accounts/login/submit/", routeLoginSubmit) - router.HandleFunc("/accounts/create/submit/", routeRegisterSubmit) - //router.HandleFunc("/accounts/list/", routeLogin) // Redirect /accounts/ and /user/ to here.. // Get a list of all of the accounts on the forum - router.HandleFunc("/ws/", routeWebsockets) log.Print("Initialising the plugins") diff --git a/member_routes.go b/member_routes.go index da0ad5e5..db9810d5 100644 --- a/member_routes.go +++ b/member_routes.go @@ -16,11 +16,25 @@ import ( "./common" ) +// Experimenting +/*func memberRenderTemplate(tmplName string, themeName string, w http.ResponseWriter, r *http.Request, user common.User, pi interface{}) common.RouteError { + if common.PreRenderHooks["pre_render_"+tmplName] != nil { + if common.RunPreRenderHook("pre_render_"+tmplName, w, r, &user, pi) { + return nil + } + } + + err := common.RunThemeTemplate(themeName, tmplName, pi, w) + if err != nil { + return common.InternalError(err, w, r) + } + return nil +}*/ + // ? - Should we add a new permission or permission zone (like per-forum permissions) specifically for profile comment creation // ? - Should we allow banned users to make reports? How should we handle report abuse? // TODO: Add a permission to stop certain users from using custom avatars // ? - Log username changes and put restrictions on this? - func routeTopicCreate(w http.ResponseWriter, r *http.Request, user common.User, sfid string) common.RouteError { var fid int var err error diff --git a/panel_routes.go b/panel_routes.go index 7744f081..58570b7c 100644 --- a/panel_routes.go +++ b/panel_routes.go @@ -834,39 +834,9 @@ func routePanelAnalyticsPosts(w http.ResponseWriter, r *http.Request, user commo headerVars.Stylesheets = append(headerVars.Stylesheets, "chartist/chartist.min.css") headerVars.Scripts = append(headerVars.Scripts, "chartist/chartist.min.js") - var timeQuantity = 6 - var timeUnit = "hour" - var timeSlices = 12 - var sliceWidth = 60 * 30 - var timeRange = "six-hours" - - switch r.FormValue("timeRange") { - case "one-month": - timeQuantity = 30 - timeUnit = "day" - timeSlices = 30 - sliceWidth = 60 * 60 * 24 - timeRange = "one-month" - case "two-days": // Two days is experimental - timeQuantity = 2 - timeUnit = "day" - timeSlices = 24 - sliceWidth = 60 * 60 * 2 - timeRange = "two-days" - case "one-day": - timeQuantity = 1 - timeUnit = "day" - timeSlices = 24 - sliceWidth = 60 * 60 - timeRange = "one-day" - case "twelve-hours": - timeQuantity = 12 - timeSlices = 24 - timeRange = "twelve-hours" - case "six-hours", "": - timeRange = "six-hours" - default: - return common.LocalError("Unknown time range", w, r, user) + timeRange, err := panelAnalyticsTimeRange(r.FormValue("timeRange")) + if err != nil { + return common.LocalError(err.Error(), w, r, user) } var revLabelList []int64 @@ -874,8 +844,8 @@ func routePanelAnalyticsPosts(w http.ResponseWriter, r *http.Request, user commo var viewMap = make(map[int64]int64) var currentTime = time.Now().Unix() - for i := 1; i <= timeSlices; i++ { - var label = currentTime - int64(i*sliceWidth) + for i := 1; i <= timeRange.Slices; i++ { + var label = currentTime - int64(i*timeRange.SliceWidth) revLabelList = append(revLabelList, label) viewMap[label] = 0 } @@ -887,7 +857,7 @@ func routePanelAnalyticsPosts(w http.ResponseWriter, r *http.Request, user commo log.Print("in routePanelAnalyticsPosts") acc := qgen.Builder.Accumulator() - rows, err := acc.Select("postchunks").Columns("count, createdAt").DateCutoff("createdAt", timeQuantity, timeUnit).Query() + rows, err := acc.Select("postchunks").Columns("count, createdAt").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query() if err != nil && err != ErrNoRows { return common.InternalError(err, w, r) } @@ -925,7 +895,7 @@ func routePanelAnalyticsPosts(w http.ResponseWriter, r *http.Request, user commo graph := common.PanelTimeGraph{Series: viewList, Labels: labelList} log.Printf("graph: %+v\n", graph) - pi := common.PanelAnalyticsPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", graph, viewItems, timeRange} + pi := common.PanelAnalyticsPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", graph, viewItems, timeRange.Range} return panelRenderTemplate("panel_analytics_posts", w, r, user, &pi) } @@ -936,8 +906,13 @@ func routePanelAnalyticsRoutes(w http.ResponseWriter, r *http.Request, user comm } var routeMap = make(map[string]int) + timeRange, err := panelAnalyticsTimeRange(r.FormValue("timeRange")) + if err != nil { + return common.LocalError(err.Error(), w, r, user) + } + acc := qgen.Builder.Accumulator() - rows, err := acc.Select("viewchunks").Columns("count, route").Where("route != ''").DateCutoff("createdAt", 1, "day").Query() + rows, err := acc.Select("viewchunks").Columns("count, route").Where("route != ''").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query() if err != nil && err != ErrNoRows { return common.InternalError(err, w, r) } @@ -969,7 +944,7 @@ func routePanelAnalyticsRoutes(w http.ResponseWriter, r *http.Request, user comm }) } - pi := common.PanelAnalyticsRoutesPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", routeItems} + pi := common.PanelAnalyticsRoutesPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", routeItems, timeRange.Range} return panelRenderTemplate("panel_analytics_routes", w, r, user, &pi) } @@ -980,8 +955,13 @@ func routePanelAnalyticsAgents(w http.ResponseWriter, r *http.Request, user comm } var agentMap = make(map[string]int) + timeRange, err := panelAnalyticsTimeRange(r.FormValue("timeRange")) + if err != nil { + return common.LocalError(err.Error(), w, r, user) + } + acc := qgen.Builder.Accumulator() - rows, err := acc.Select("viewchunks_agents").Columns("count, browser").DateCutoff("createdAt", 1, "day").Query() + rows, err := acc.Select("viewchunks_agents").Columns("count, browser").DateCutoff("createdAt", timeRange.Quantity, timeRange.Unit).Query() if err != nil && err != ErrNoRows { return common.InternalError(err, w, r) } @@ -1013,7 +993,7 @@ func routePanelAnalyticsAgents(w http.ResponseWriter, r *http.Request, user comm }) } - pi := common.PanelAnalyticsAgentsPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", agentItems} + pi := common.PanelAnalyticsAgentsPage{common.GetTitlePhrase("panel_analytics"), user, headerVars, stats, "analytics", agentItems, timeRange.Range} return panelRenderTemplate("panel_analytics_agents", w, r, user, &pi) } @@ -1505,7 +1485,7 @@ func routePanelUsersEdit(w http.ResponseWriter, r *http.Request, user common.Use } var groupList []interface{} - for _, group := range groups[1:] { + for _, group := range groups { if !user.Perms.EditUserGroupAdmin && group.IsAdmin { continue } @@ -1600,6 +1580,8 @@ func routePanelUsersEditSubmit(w http.ResponseWriter, r *http.Request, user comm if newpassword != "" { common.SetPassword(targetUser.ID, newpassword) + // Log the user out as a safety precaution + common.Auth.ForceLogout(targetUser.ID) } targetUser.CacheRemove() diff --git a/router_gen/main.go b/router_gen/main.go index 25c8c51e..b69f569d 100644 --- a/router_gen/main.go +++ b/router_gen/main.go @@ -181,6 +181,7 @@ func main() { for id, agent := range tmplVars.AllAgentNames { tmplVars.AllAgentMap[agent] = id } + var graveSym = "`" var fileData = `// Code generated by. DO NOT EDIT. /* This file was automatically generated by the software. Please don't edit it as your changes may be overwritten at any moment. */ @@ -284,6 +285,19 @@ func (router *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { var prefix, extraData string prefix = req.URL.Path[0:strings.IndexByte(req.URL.Path[1:],'/') + 1] if req.URL.Path[len(req.URL.Path) - 1] != '/' { + // TODO: Cover more suspicious strings and at a lower layer than this and more efficiently + if strings.Contains(req.URL.Path,"'") || strings.Contains(req.URL.Path,";") || strings.Contains(req.URL.Path,"\"") || strings.Contains(req.URL.Path,"` + graveSym + `") || strings.Contains(req.URL.Path,"%") { + log.Print("Suspicious UA: ", req.UserAgent()) + log.Print("Method: ", req.Method) + for key, value := range req.Header { + for _, vvalue := range value { + log.Print("Header '" + key + "': " + vvalue + "!!") + } + } + log.Print("req.URL.Path: ", req.URL.Path) + log.Print("req.Referer(): ", req.Referer()) + log.Print("req.RemoteAddr: ", req.RemoteAddr) + } extraData = req.URL.Path[strings.LastIndexByte(req.URL.Path,'/') + 1:] req.URL.Path = req.URL.Path[:strings.LastIndexByte(req.URL.Path,'/') + 1] } diff --git a/router_gen/routes.go b/router_gen/routes.go index 5027c664..f83bd55a 100644 --- a/router_gen/routes.go +++ b/router_gen/routes.go @@ -29,6 +29,7 @@ func routes() { buildTopicRoutes() buildReplyRoutes() buildProfileReplyRoutes() + buildAccountRoutes() } // TODO: Test the email token route @@ -101,6 +102,19 @@ func buildProfileReplyRoutes() { addRouteGroup(pReplyGroup) } +func buildAccountRoutes() { + //router.HandleFunc("/accounts/list/", routeLogin) // Redirect /accounts/ and /user/ to here.. // Get a list of all of the accounts on the forum + accReplyGroup := newRouteGroup("/accounts/") + accReplyGroup.Routes( + View("routeLogin", "/accounts/login/"), + View("routeRegister", "/accounts/create/"), + Action("routeLogout", "/accounts/logout/"), + AnonAction("routeLoginSubmit", "/accounts/login/submit/"), // TODO: Guard this with a token, maybe the IP hashed with a rotated key? + AnonAction("routeRegisterSubmit", "/accounts/create/submit/"), + ) + addRouteGroup(accReplyGroup) +} + func buildPanelRoutes() { panelGroup := newRouteGroup("/panel/").Before("SuperModOnly") panelGroup.Routes( @@ -138,8 +152,8 @@ func buildPanelRoutes() { Action("routePanelUsersEditSubmit", "/panel/users/edit/submit/", "extraData"), View("routePanelAnalyticsViews", "/panel/analytics/views/").Before("ParseForm"), - View("routePanelAnalyticsRoutes", "/panel/analytics/routes/"), - View("routePanelAnalyticsAgents", "/panel/analytics/agents/"), + View("routePanelAnalyticsRoutes", "/panel/analytics/routes/").Before("ParseForm"), + View("routePanelAnalyticsAgents", "/panel/analytics/agents/").Before("ParseForm"), View("routePanelAnalyticsRouteViews", "/panel/analytics/route/", "extraData"), View("routePanelAnalyticsAgentViews", "/panel/analytics/agent/", "extraData"), View("routePanelAnalyticsPosts", "/panel/analytics/posts/").Before("ParseForm"), diff --git a/routes.go b/routes.go index c11491a4..53d54634 100644 --- a/routes.go +++ b/routes.go @@ -746,10 +746,6 @@ func routeLoginSubmit(w http.ResponseWriter, r *http.Request, user common.User) if user.Loggedin { return common.LocalError("You're already logged in.", w, r, user) } - err := r.ParseForm() - if err != nil { - return common.LocalError("Bad Form", w, r, user) - } username := html.EscapeString(strings.Replace(r.PostFormValue("username"), "\n", "", -1)) uid, err := common.Auth.Authenticate(username, r.PostFormValue("password")) @@ -807,10 +803,6 @@ func routeRegister(w http.ResponseWriter, r *http.Request, user common.User) com func routeRegisterSubmit(w http.ResponseWriter, r *http.Request, user common.User) common.RouteError { headerLite, _ := common.SimpleUserCheck(w, r, &user) - err := r.ParseForm() - if err != nil { - return common.LocalError("Bad Form", w, r, user) - } username := html.EscapeString(strings.Replace(r.PostFormValue("username"), "\n", "", -1)) if username == "" { return common.LocalError("You didn't put in a username.", w, r, user) @@ -831,13 +823,15 @@ func routeRegisterSubmit(w http.ResponseWriter, r *http.Request, user common.Use } // ? Move this into Create()? What if we want to programatically set weak passwords for tests? - err = common.WeakPassword(password) + err := common.WeakPassword(password) if err != nil { return common.LocalError(err.Error(), w, r, user) } confirmPassword := r.PostFormValue("confirm_password") - log.Print("Registration Attempt! common.Username: " + username) // TODO: Add more controls over what is logged when? + if common.Dev.DebugMode { + log.Print("Registration Attempt! Username: " + username) // TODO: Add more controls over what is logged when? + } // Do the two inputted passwords match..? if password != confirmPassword { diff --git a/run.bat b/run.bat index b777ecf7..85f1b775 100644 --- a/run.bat +++ b/run.bat @@ -34,5 +34,5 @@ if %errorlevel% neq 0 ( echo Running Gosora gosora.exe rem Or you could redirect the output to a file -rem gosora.exe > operations.log 2>&1 +rem gosora.exe > ops.log 2>&1 pause \ No newline at end of file diff --git a/templates/panel_analytics_agents.html b/templates/panel_analytics_agents.html index 7ad50e52..662f998d 100644 --- a/templates/panel_analytics_agents.html +++ b/templates/panel_analytics_agents.html @@ -2,9 +2,20 @@
{{template "panel-menu.html" . }}
- +
+
+
+ User Agents + +
+
+
{{range .ItemList}}
diff --git a/templates/panel_analytics_routes.html b/templates/panel_analytics_routes.html index 9293bec1..7f3b1361 100644 --- a/templates/panel_analytics_routes.html +++ b/templates/panel_analytics_routes.html @@ -2,9 +2,20 @@
{{template "panel-menu.html" . }}
- +
+
+
+ Routes + +
+
+
{{range .ItemList}}
diff --git a/themes/cosora/public/main.css b/themes/cosora/public/main.css index 8ce1bbf2..7a3d8d60 100644 --- a/themes/cosora/public/main.css +++ b/themes/cosora/public/main.css @@ -207,9 +207,12 @@ ul { font-size: 15px; display: flex; } +.alertItem.withAvatar .text { + margin-top: 8px; +} .alertItem.withAvatar:not(:last-child) .text { border-bottom: 1px solid var(--element-border-color); - margin-top: 8px; + padding-bottom: 16px; } .alertItem .bgsub { width: 32px; @@ -217,6 +220,9 @@ ul { border-radius: 30px; margin-right: 12px; } +.alertItem.withAvatar:not(:first-child) { + padding-top: 0px; +} .rowblock, .colstack_head { margin-bottom: 12px; @@ -820,6 +826,18 @@ textarea { margin-right: 8px; } +.topic_item { + display: flex; +} +.topic_item .topic_name_input { + width: 100%; + padding-left: 12px; + margin-right: 12px; +} +.topic_item .formbutton { + margin-top: 0px; +} + .post_item { display: flex; margin-bottom: 16px;