diff --git a/db/models.go b/db/models.go index 3787f45c..1041a4f6 100644 --- a/db/models.go +++ b/db/models.go @@ -181,6 +181,7 @@ type Comment struct { ReportCount uint `gorm:"column:report_count" json:"-"` ReviewedByMod bool `gorm:"column:reviewed_by_mod" json:"-"` Edited bool `gorm:"column:edited" json:"edited"` + SchoolID uint `gorm:"column:school_id" json:"-"` } func (c *Comment) ObscureIfHidden() Comment { @@ -347,6 +348,7 @@ type Report struct { Post *Post `gorm:"foreignKey:PostID" json:"post,omitempty"` // Use "omitempty" here CommentID *uint `db:"comment_id" gorm:"default:NULL" json:"-"` Comment *Comment `gorm:"foreignKey:CommentID" json:"comment,omitempty"` // Use "omitempty" here + SchoolID uint `gorm:"column:school_id" json:"-"` } type CronJob struct { @@ -356,6 +358,20 @@ type CronJob struct { Type string `gorm:"column:type" json:"type"` } +func (RoleAssignmentLog) TableName() string { + return "role_assignment_logs" +} + +type RoleAssignmentLog struct { + ID int `gorm:"primary_key;column:id" json:"id"` + CreatedAt TimeMicros `gorm:"column:created_at;autoCreateTime" json:"created_at"` + ActionUserID string `gorm:"column:action_user_id" json:"action_user_id"` + AffectedUserID string `gorm:"column:affected_user_id" json:"affected_user_id"` + OldRoles string `gorm:"column:old_roles" json:"old_roles"` + NewRoles string `gorm:"column:new_roles" json:"new_roles"` + ActionType string `gorm:"column:action_type" json:"action_type"` +} + func (CronJob) TableName() string { return "cron_jobs" } diff --git a/features/admin/fetch_reports_for_comment_by_id.go b/features/admin/fetch_reports_for_comment_by_id.go index 33372b6e..51ee7913 100644 --- a/features/admin/fetch_reports_for_comment_by_id.go +++ b/features/admin/fetch_reports_for_comment_by_id.go @@ -18,13 +18,24 @@ func (h *handler) handleFetchReportForCommentById(c *gin.Context) { return } + userRoles, err := getUserRoles(c) + if err != nil { + response.New(http.StatusInternalServerError).Err(err.Error()).Send(c) + return + } + fetchResults := fetchResults{} - err = h.db. + query := h.db. Preload("ReportType"). Where(req.Next.Cursor("created_at >")). - Where("comment_id IS NOT NULL"). - Order("created_at ASC"). + Where("comment_id IS NOT NULL") + + if len(userRoles.SchoolMods) > 0 { + query.Where("school_id IN ?", userRoles.SchoolMods) + } + + err = query.Order("created_at ASC"). Find(&fetchResults.Reports). Limit(config.AdminViewAllReportsPerCommentId). Error diff --git a/features/admin/fetch_reports_for_post_by_id.go b/features/admin/fetch_reports_for_post_by_id.go index f5e45e81..909f1fa4 100644 --- a/features/admin/fetch_reports_for_post_by_id.go +++ b/features/admin/fetch_reports_for_post_by_id.go @@ -18,13 +18,24 @@ func (h *handler) handleFetchReportForPostById(c *gin.Context) { return } + userRoles, err := getUserRoles(c) + if err != nil { + response.New(http.StatusInternalServerError).Err(err.Error()).Send(c) + return + } + fetchResults := fetchResults{} - err = h.db. + query := h.db. Preload("ReportType"). Where(req.Next.Cursor("created_at >")). - Where("post_id IS NOT NULL"). - Order("created_at ASC"). + Where("post_id IS NOT NULL") + + if len(userRoles.SchoolMods) > 0 { + query.Where("school_id IN ?", userRoles.SchoolMods) + } + + err = query.Order("created_at ASC"). Find(&fetchResults.Reports). Limit(config.AdminViewAllReportsPerPostId). Error diff --git a/features/admin/get_report_by_id.go b/features/admin/get_report_by_id.go index 416762bc..4652e449 100644 --- a/features/admin/get_report_by_id.go +++ b/features/admin/get_report_by_id.go @@ -22,12 +22,25 @@ func (h *handler) handleGetReportById(c *gin.Context) { return } + userRoles, err := getUserRoles(c) + if err != nil { + response.New(http.StatusInternalServerError).Err(err.Error()).Send(c) + return + } + report := db.Report{} - err = h.db. + query := h.db. Preload("ReportType"). // preload the ReportType field of the Report - Where("id = ?", idNumeric). - First(&report). + Where("id = ?", idNumeric) + + if len(userRoles.SchoolMods) > 0 { + query.Where("school_id IN ?", userRoles.SchoolMods) + } + + err = query.First(&report). Error + + // If user does not have access to report or report does not exist, return 404 if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { response.New(http.StatusNotFound).Err(notFound.Error()).Send(c) diff --git a/features/admin/get_reports.go b/features/admin/get_reports.go index d4807a8f..5f8af306 100644 --- a/features/admin/get_reports.go +++ b/features/admin/get_reports.go @@ -24,11 +24,21 @@ func (h *handler) handleGetReports(c *gin.Context) { return } + userRoles, err := getUserRoles(c) + if err != nil { + response.New(http.StatusInternalServerError).Err(err.Error()).Send(c) + return + } + fetchResults := FetchedReports{} query := h.db. Where(req.Next.Cursor("created_at <")) + if len(userRoles.SchoolMods) > 0 { + query.Where("school_id IN ?", userRoles.SchoolMods) + } + if req.Type != "all" { query = query.Where("type = ?", req.Type) } diff --git a/features/admin/hide.go b/features/admin/hide.go index af32a62d..dd124382 100644 --- a/features/admin/hide.go +++ b/features/admin/hide.go @@ -15,6 +15,10 @@ import ( "github.com/gin-gonic/gin" ) +const ( + uniSpecific bool = false +) + type fcmTokenWithReportID struct { Token string `gorm:"column:token"` ReportID uint `gorm:"column:report_id"` @@ -51,6 +55,12 @@ func (h *handler) handleHideContent(c *gin.Context) { return } + userRoles, err := getUserRoles(c) + if err != nil { + response.New(http.StatusInternalServerError).Err(err.Error()).Send(c) + return + } + // start a transaction tx := h.db.Begin() // if something goes ary, rollback @@ -68,10 +78,19 @@ func (h *handler) handleHideContent(c *gin.Context) { } // update the "hidden" field on content. - result := tx. - Table(table). - Where("id = ?", req.ContentID). - Updates(updateData) + query := tx.Table(table).Where("id = ?", req.ContentID) + + if len(userRoles.SchoolMods) > 0 && table == "posts" { + query.Where("school_id IN ?", userRoles.SchoolMods) + post := db.Post{} + if query.First(&post).Error != nil { + tx.Rollback() + response.New(http.StatusForbidden).Err("missing school permissions").Send(c) + return + } + } + + result := query.Updates(updateData) if result.Error != nil { tx.Rollback() diff --git a/features/admin/ranked_comments_by_report.go b/features/admin/ranked_comments_by_report.go index 71edaec1..3d0a386a 100644 --- a/features/admin/ranked_comments_by_report.go +++ b/features/admin/ranked_comments_by_report.go @@ -33,6 +33,12 @@ func (h *handler) handleGetRankedCommentsByReport(c *gin.Context) { return } + userRoles, err := getUserRoles(c) + if err != nil { + response.New(http.StatusInternalServerError).Err(err.Error()).Send(c) + return + } + token, err := utils.UserTokenFromContext(c) if err != nil { response.New(http.StatusInternalServerError).Err(serverError.Error()).Send(c) @@ -73,10 +79,15 @@ func (h *handler) handleGetRankedCommentsByReport(c *gin.Context) { comments := []db.Comment{} // fetch comments - err = h.db. + query := h.db. Where("reviewed_by_mod = ?"+excludedIDQuery, req.ReviewedByMod). - Order("report_count DESC"). - Find(&comments). + Order("report_count DESC") + + if len(userRoles.SchoolMods) > 0 { + query.Where("school_id IN ?", userRoles.SchoolMods) + } + + err = query.Find(&comments). Limit(config.AdminCommentsSortedByReportsPageSize). Error if err != nil { diff --git a/features/admin/ranked_posts_by_report.go b/features/admin/ranked_posts_by_report.go index deb37681..85ee41ab 100644 --- a/features/admin/ranked_posts_by_report.go +++ b/features/admin/ranked_posts_by_report.go @@ -33,6 +33,12 @@ func (h *handler) handleGetRankedPostsByReport(c *gin.Context) { return } + userRoles, err := getUserRoles(c) + if err != nil { + response.New(http.StatusInternalServerError).Err(err.Error()).Send(c) + return + } + token, err := utils.UserTokenFromContext(c) if err != nil { response.New(http.StatusInternalServerError).Err(serverError.Error()).Send(c) @@ -73,13 +79,18 @@ func (h *handler) handleGetRankedPostsByReport(c *gin.Context) { posts := []db.Post{} // fetch comments - err = h.db. + query := h.db. Preload("Faculty"). Preload("School"). Preload("Category"). Preload("YearOfStudy"). - Where("reviewed_by_mod = ?"+excludedIDQuery, req.ReviewedByMod). - Order("report_count DESC"). + Where("reviewed_by_mod = ?"+excludedIDQuery, req.ReviewedByMod) + + if len(userRoles.SchoolMods) > 0 { + query.Where("school_id IN ?", userRoles.SchoolMods) + } + + err = query.Order("report_count DESC"). Find(&posts). Limit(config.AdminPostsSortedByReportsPageSize). Error diff --git a/features/admin/requests.http b/features/admin/requests.http index 83e4753a..ec6cc3de 100644 --- a/features/admin/requests.http +++ b/features/admin/requests.http @@ -2,11 +2,11 @@ PATCH http://127.0.0.1:8080/api/v1/admin/user-standing Content-Type: application/json -Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjYyM2YzNmM4MTZlZTNkZWQ2YzU0NTkyZTM4ZGFlZjcyZjE1YTBmMTMiLCJ0eXAiOiJKV1QifQ.eyJyb2xlcyI6W10sInN5bmMiOnRydWUsImlzcyI6Imh0dHBzOi8vc2VjdXJldG9rZW4uZ29vZ2xlLmNvbS9jb25mZXNpLXNlcnZlci1kZXYiLCJhdWQiOiJjb25mZXNpLXNlcnZlci1kZXYiLCJhdXRoX3RpbWUiOjE2OTEzMTc1NzksInVzZXJfaWQiOiJyQm5LcERKS3FpZ05kNTNTY1JBQktIbXR3VGoxIiwic3ViIjoickJuS3BESktxaWdOZDUzU2NSQUJLSG10d1RqMSIsImlhdCI6MTY5MTMxNzU3OSwiZXhwIjoxNjkxMzIxMTc5LCJlbWFpbCI6Im1hdHRoZXcucmwudHJlbnRAZ21haWwuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsibWF0dGhldy5ybC50cmVudEBnbWFpbC5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9fQ.iyPft2mx1tleTCEeOm9v8BercvOt9r6cGAcAiSiCBXrE38ez0PrZpsauRyIGjDNAKfi-nTGR9rkKfdsz6P_1ByQUKL2AdpXxGYVqqvCU2rE4Nk0Hv6TQtJUEdbkbkTGfgM2jRCjOmP4s_c6d5wP6RAL9XGkYHh1lgssbQFNntDNlUbJRnQnkMFVA2MOig1-ZaFWRuJe6LyL_BGXlhnPStSt3PzQbklC46hoGTH8jmcP8jt-DXUmBKiqIDe1zF4hkRE58zdp3saEiKEc2_lEHvjh2oF6B3Kk-0NeFRVeHpO3kTAc0fIzIlDIeO7FagiP6PzKM0OZpEc5V2TmNxfonLA +Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImNmM2I1YWRhM2NhMzkxNTQ4ZDM1OTJiMzU5MjkyM2UzNjAxMmI5MTQiLCJ0eXAiOiJKV1QifQ.eyJyb2xlcyI6WyJhZG1pbiIsIm1vZF8xMiIsIm1vZF8xMzIiLCJzX21vZCJdLCJzeW5jIjp0cnVlLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vY29uZmVzaS1zZXJ2ZXItZGV2IiwiYXVkIjoiY29uZmVzaS1zZXJ2ZXItZGV2IiwiYXV0aF90aW1lIjoxNjkxNDgxNjkxLCJ1c2VyX2lkIjoic1AxRDhNWkFRcFpzcGQxTzd0dWZMUEJGcTl2MSIsInN1YiI6InNQMUQ4TVpBUXBac3BkMU83dHVmTFBCRnE5djEiLCJpYXQiOjE2OTE0ODE2OTEsImV4cCI6MTY5MTQ4NTI5MSwiZW1haWwiOiJodWtjaHJpc3RvcGhlcjEyQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJmaXJlYmFzZSI6eyJpZGVudGl0aWVzIjp7ImVtYWlsIjpbImh1a2NocmlzdG9waGVyMTJAZ21haWwuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoicGFzc3dvcmQifX0.ghTo-inp_LHtpnEbA1C9mxwwkX_jVTXI9PqyKoDSRm_F86em2hu8mD6m7agdMemNGOUFOR2PYpxGbNGI4aJseAZEflgHXQPVOCP2YnMokvcz8lkcEEVl_OL1uRCa4xEnKw8rCspX-UT8TVdEpZYO3W8SfrrXgVKDbzVpa1bsW6yfbXt5YDs8LDK_msRc_-U3swbIVQxMvEvRoYeZLfNkKtNFDDbKgSA878Z-gMDA03DH2xYYNHR9ZcKCWZFFqBbiNvkJiNum2HXmheYWkG_rozNqOTJuvyJcZUZbwKcUf0Kg2oLjRS4vf28_KACWO2WdRrppGAvIFdbcYVYei0pwZA { - "standing": "banned", - "user_id": "eNLUvWgxIgZ79t2pZmM15DxXM6u2" + "standing": "limited", + "user_id": "bad_1id" } ### get user standing @@ -65,16 +65,16 @@ Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjYyM2YzNmM4MTZlZTNkZWQ2YzU0NT ### get report by id -GET http://127.0.0.1:8080/api/v1/admin/report?id=18 +GET http://127.0.0.1:8080/api/v1/admin/report?id=7 Content-Type: application/json -Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjYyM2YzNmM4MTZlZTNkZWQ2YzU0NTkyZTM4ZGFlZjcyZjE1YTBmMTMiLCJ0eXAiOiJKV1QifQ.eyJyb2xlcyI6W10sInN5bmMiOnRydWUsImlzcyI6Imh0dHBzOi8vc2VjdXJldG9rZW4uZ29vZ2xlLmNvbS9jb25mZXNpLXNlcnZlci1kZXYiLCJhdWQiOiJjb25mZXNpLXNlcnZlci1kZXYiLCJhdXRoX3RpbWUiOjE2OTEzMTc1NzksInVzZXJfaWQiOiJyQm5LcERKS3FpZ05kNTNTY1JBQktIbXR3VGoxIiwic3ViIjoickJuS3BESktxaWdOZDUzU2NSQUJLSG10d1RqMSIsImlhdCI6MTY5MTMxNzU3OSwiZXhwIjoxNjkxMzIxMTc5LCJlbWFpbCI6Im1hdHRoZXcucmwudHJlbnRAZ21haWwuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsibWF0dGhldy5ybC50cmVudEBnbWFpbC5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9fQ.iyPft2mx1tleTCEeOm9v8BercvOt9r6cGAcAiSiCBXrE38ez0PrZpsauRyIGjDNAKfi-nTGR9rkKfdsz6P_1ByQUKL2AdpXxGYVqqvCU2rE4Nk0Hv6TQtJUEdbkbkTGfgM2jRCjOmP4s_c6d5wP6RAL9XGkYHh1lgssbQFNntDNlUbJRnQnkMFVA2MOig1-ZaFWRuJe6LyL_BGXlhnPStSt3PzQbklC46hoGTH8jmcP8jt-DXUmBKiqIDe1zF4hkRE58zdp3saEiKEc2_lEHvjh2oF6B3Kk-0NeFRVeHpO3kTAc0fIzIlDIeO7FagiP6PzKM0OZpEc5V2TmNxfonLA +Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImNmM2I1YWRhM2NhMzkxNTQ4ZDM1OTJiMzU5MjkyM2UzNjAxMmI5MTQiLCJ0eXAiOiJKV1QifQ.eyJyb2xlcyI6WyJhZG1pbiIsIm1vZF80MSIsIm1vZF8xMzIiLCJzX21vZCJdLCJzeW5jIjp0cnVlLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vY29uZmVzaS1zZXJ2ZXItZGV2IiwiYXVkIjoiY29uZmVzaS1zZXJ2ZXItZGV2IiwiYXV0aF90aW1lIjoxNjkxNzQwOTkzLCJ1c2VyX2lkIjoic1AxRDhNWkFRcFpzcGQxTzd0dWZMUEJGcTl2MSIsInN1YiI6InNQMUQ4TVpBUXBac3BkMU83dHVmTFBCRnE5djEiLCJpYXQiOjE2OTE3NDA5OTMsImV4cCI6MTY5MTc0NDU5MywiZW1haWwiOiJodWtjaHJpc3RvcGhlcjEyQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJmaXJlYmFzZSI6eyJpZGVudGl0aWVzIjp7ImVtYWlsIjpbImh1a2NocmlzdG9waGVyMTJAZ21haWwuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoicGFzc3dvcmQifX0.i1GUZ1fx8aNoS0-cmhqkDLRCvDO_wjXpMtU6n-WishXT-UcOjnbydfR69aveLsVGVOQxvFAYJ_q_d2X4agygTxNEpss541FAEtWurtyfOi4_wMCNA5Q45b4hYYLraB5g1kttAk96EEnvT2CQjIJsWh4G6I0Tp2S2qK4gUwQ7_zXOFfFJSpSqfRKZ2hVVzrfLAlNpkJNqgvtO8p5yuSWYK4k0CbVxss5mGlBJQjoQ12ngV8Fzp39JXsUj9RvWX3Y73WR0HS-NoAGvtZ3z3g0Yv-qT7s4UCDP0-_tDjRe0GqOhZmrX1Iz2D6FcGA90H12xil2AJ2RuTCBy046e-v1_Ng ### get all reports GET http://127.0.0.1:8080/api/v1/admin/reports Content-Type: application/json -Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjYyM2YzNmM4MTZlZTNkZWQ2YzU0NTkyZTM4ZGFlZjcyZjE1YTBmMTMiLCJ0eXAiOiJKV1QifQ.eyJyb2xlcyI6W10sInN5bmMiOnRydWUsImlzcyI6Imh0dHBzOi8vc2VjdXJldG9rZW4uZ29vZ2xlLmNvbS9jb25mZXNpLXNlcnZlci1kZXYiLCJhdWQiOiJjb25mZXNpLXNlcnZlci1kZXYiLCJhdXRoX3RpbWUiOjE2OTEzMTc1NzksInVzZXJfaWQiOiJyQm5LcERKS3FpZ05kNTNTY1JBQktIbXR3VGoxIiwic3ViIjoickJuS3BESktxaWdOZDUzU2NSQUJLSG10d1RqMSIsImlhdCI6MTY5MTMxNzU3OSwiZXhwIjoxNjkxMzIxMTc5LCJlbWFpbCI6Im1hdHRoZXcucmwudHJlbnRAZ21haWwuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsibWF0dGhldy5ybC50cmVudEBnbWFpbC5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9fQ.iyPft2mx1tleTCEeOm9v8BercvOt9r6cGAcAiSiCBXrE38ez0PrZpsauRyIGjDNAKfi-nTGR9rkKfdsz6P_1ByQUKL2AdpXxGYVqqvCU2rE4Nk0Hv6TQtJUEdbkbkTGfgM2jRCjOmP4s_c6d5wP6RAL9XGkYHh1lgssbQFNntDNlUbJRnQnkMFVA2MOig1-ZaFWRuJe6LyL_BGXlhnPStSt3PzQbklC46hoGTH8jmcP8jt-DXUmBKiqIDe1zF4hkRE58zdp3saEiKEc2_lEHvjh2oF6B3Kk-0NeFRVeHpO3kTAc0fIzIlDIeO7FagiP6PzKM0OZpEc5V2TmNxfonLA +Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImNmM2I1YWRhM2NhMzkxNTQ4ZDM1OTJiMzU5MjkyM2UzNjAxMmI5MTQiLCJ0eXAiOiJKV1QifQ.eyJyb2xlcyI6WyJhZG1pbiIsIm1vZF80MSIsIm1vZF8xMzIiLCJzX21vZCJdLCJzeW5jIjp0cnVlLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vY29uZmVzaS1zZXJ2ZXItZGV2IiwiYXVkIjoiY29uZmVzaS1zZXJ2ZXItZGV2IiwiYXV0aF90aW1lIjoxNjkxNzQxMTUxLCJ1c2VyX2lkIjoic1AxRDhNWkFRcFpzcGQxTzd0dWZMUEJGcTl2MSIsInN1YiI6InNQMUQ4TVpBUXBac3BkMU83dHVmTFBCRnE5djEiLCJpYXQiOjE2OTE3NDExNTEsImV4cCI6MTY5MTc0NDc1MSwiZW1haWwiOiJodWtjaHJpc3RvcGhlcjEyQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJmaXJlYmFzZSI6eyJpZGVudGl0aWVzIjp7ImVtYWlsIjpbImh1a2NocmlzdG9waGVyMTJAZ21haWwuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoicGFzc3dvcmQifX0.SwiyuWcCbS_t_A7hw4dllpmnonm9kisIZxY0hqiJwhrHDYQWZ1-Y23oeqyQEbWVkEjE-O8dSV0Mzr4AvaQsRhJZERvVGVdYQJ-lnIijAX4IuhqfUfZO2vQwMl9kCTDVQsA1Y3UDa-ZoAxBB9CKmK22gc104hpOnOVFw3NkvsqbHdEezfDPVMyagG145IwibAz0ANM7fMKxM1CiL1jTBr2H7x8J2Wafm4Rh_TpM8YzYyohzFLXN5yVl9HSxaq7e68hYWxZnvxICkBA5ttpa7HlZsNxDwzUz6G73E_xU5N13kXYul_4iDBTpQCFIkGPqqvyWgjv46Jn4V8zx8aqS5Seg { "type": "all", @@ -85,12 +85,12 @@ Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjYyM2YzNmM4MTZlZTNkZWQ2YzU0NT PATCH http://127.0.0.1:8080/api/v1/admin/hide Content-Type: application/json -Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjYyM2YzNmM4MTZlZTNkZWQ2YzU0NTkyZTM4ZGFlZjcyZjE1YTBmMTMiLCJ0eXAiOiJKV1QifQ.eyJyb2xlcyI6W10sInN5bmMiOnRydWUsImlzcyI6Imh0dHBzOi8vc2VjdXJldG9rZW4uZ29vZ2xlLmNvbS9jb25mZXNpLXNlcnZlci1kZXYiLCJhdWQiOiJjb25mZXNpLXNlcnZlci1kZXYiLCJhdXRoX3RpbWUiOjE2OTEzMTc1NzksInVzZXJfaWQiOiJyQm5LcERKS3FpZ05kNTNTY1JBQktIbXR3VGoxIiwic3ViIjoickJuS3BESktxaWdOZDUzU2NSQUJLSG10d1RqMSIsImlhdCI6MTY5MTMxNzU3OSwiZXhwIjoxNjkxMzIxMTc5LCJlbWFpbCI6Im1hdHRoZXcucmwudHJlbnRAZ21haWwuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsibWF0dGhldy5ybC50cmVudEBnbWFpbC5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9fQ.iyPft2mx1tleTCEeOm9v8BercvOt9r6cGAcAiSiCBXrE38ez0PrZpsauRyIGjDNAKfi-nTGR9rkKfdsz6P_1ByQUKL2AdpXxGYVqqvCU2rE4Nk0Hv6TQtJUEdbkbkTGfgM2jRCjOmP4s_c6d5wP6RAL9XGkYHh1lgssbQFNntDNlUbJRnQnkMFVA2MOig1-ZaFWRuJe6LyL_BGXlhnPStSt3PzQbklC46hoGTH8jmcP8jt-DXUmBKiqIDe1zF4hkRE58zdp3saEiKEc2_lEHvjh2oF6B3Kk-0NeFRVeHpO3kTAc0fIzIlDIeO7FagiP6PzKM0OZpEc5V2TmNxfonLA +Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImNmM2I1YWRhM2NhMzkxNTQ4ZDM1OTJiMzU5MjkyM2UzNjAxMmI5MTQiLCJ0eXAiOiJKV1QifQ.eyJyb2xlcyI6WyJhZG1pbiIsIm1vZF80MSIsIm1vZF8xMzIiLCJzX21vZCJdLCJzeW5jIjp0cnVlLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vY29uZmVzaS1zZXJ2ZXItZGV2IiwiYXVkIjoiY29uZmVzaS1zZXJ2ZXItZGV2IiwiYXV0aF90aW1lIjoxNjkxNTE2MjYxLCJ1c2VyX2lkIjoic1AxRDhNWkFRcFpzcGQxTzd0dWZMUEJGcTl2MSIsInN1YiI6InNQMUQ4TVpBUXBac3BkMU83dHVmTFBCRnE5djEiLCJpYXQiOjE2OTE1MTYyNjEsImV4cCI6MTY5MTUxOTg2MSwiZW1haWwiOiJodWtjaHJpc3RvcGhlcjEyQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJmaXJlYmFzZSI6eyJpZGVudGl0aWVzIjp7ImVtYWlsIjpbImh1a2NocmlzdG9waGVyMTJAZ21haWwuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoicGFzc3dvcmQifX0.dKkby827ZjfOzurPelougPzYx6Gtc2SbiGinqTjFnQ08Uz5uwfKZPUc7Nvbtve0GfiEK2W55R-8uSYKPdiz0V_-y_pY1YDSPFI90UZHPq3OjeOVBUJO7yzJA3BRZSCHdPqN3UhJZcAcypg-Ta90j2VXarNy4LRFGCZ6x40cS4-likRTElxMbHsUvYp-w2485y4rDkwn6WIL9KHFI2n5H3ghYStjyrliNIv6E_M7rsjiIT6tBt_16oGeFUG-vbB2A1una9FR50PxkvokYl9u9hhsLjTJvcovF9EQERz5zQXwr-4tubpstZdgkefkKx06eXopcfKhuvvgSIWLn3LJZsQ { "content_type": "post", - "content_id": 1, - "hide": false, + "content_id": 17, + "hide": true, "reviewed_by_mod": true, "reason": "spammmmm" } @@ -99,12 +99,12 @@ Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjYyM2YzNmM4MTZlZTNkZWQ2YzU0NT PATCH http://127.0.0.1:8080/api/v1/admin/reviewed-by-mod Content-Type: application/json -Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjYyM2YzNmM4MTZlZTNkZWQ2YzU0NTkyZTM4ZGFlZjcyZjE1YTBmMTMiLCJ0eXAiOiJKV1QifQ.eyJyb2xlcyI6W10sInN5bmMiOnRydWUsImlzcyI6Imh0dHBzOi8vc2VjdXJldG9rZW4uZ29vZ2xlLmNvbS9jb25mZXNpLXNlcnZlci1kZXYiLCJhdWQiOiJjb25mZXNpLXNlcnZlci1kZXYiLCJhdXRoX3RpbWUiOjE2OTEzMTc1NzksInVzZXJfaWQiOiJyQm5LcERKS3FpZ05kNTNTY1JBQktIbXR3VGoxIiwic3ViIjoickJuS3BESktxaWdOZDUzU2NSQUJLSG10d1RqMSIsImlhdCI6MTY5MTMxNzU3OSwiZXhwIjoxNjkxMzIxMTc5LCJlbWFpbCI6Im1hdHRoZXcucmwudHJlbnRAZ21haWwuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsibWF0dGhldy5ybC50cmVudEBnbWFpbC5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9fQ.iyPft2mx1tleTCEeOm9v8BercvOt9r6cGAcAiSiCBXrE38ez0PrZpsauRyIGjDNAKfi-nTGR9rkKfdsz6P_1ByQUKL2AdpXxGYVqqvCU2rE4Nk0Hv6TQtJUEdbkbkTGfgM2jRCjOmP4s_c6d5wP6RAL9XGkYHh1lgssbQFNntDNlUbJRnQnkMFVA2MOig1-ZaFWRuJe6LyL_BGXlhnPStSt3PzQbklC46hoGTH8jmcP8jt-DXUmBKiqIDe1zF4hkRE58zdp3saEiKEc2_lEHvjh2oF6B3Kk-0NeFRVeHpO3kTAc0fIzIlDIeO7FagiP6PzKM0OZpEc5V2TmNxfonLA +Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImNmM2I1YWRhM2NhMzkxNTQ4ZDM1OTJiMzU5MjkyM2UzNjAxMmI5MTQiLCJ0eXAiOiJKV1QifQ.eyJyb2xlcyI6WyJhZG1pbiIsIm1vZF8xMiIsIm1vZF8xMzIiLCJzX21vZCJdLCJzeW5jIjp0cnVlLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vY29uZmVzaS1zZXJ2ZXItZGV2IiwiYXVkIjoiY29uZmVzaS1zZXJ2ZXItZGV2IiwiYXV0aF90aW1lIjoxNjkxNDgxNjkxLCJ1c2VyX2lkIjoic1AxRDhNWkFRcFpzcGQxTzd0dWZMUEJGcTl2MSIsInN1YiI6InNQMUQ4TVpBUXBac3BkMU83dHVmTFBCRnE5djEiLCJpYXQiOjE2OTE0ODE2OTEsImV4cCI6MTY5MTQ4NTI5MSwiZW1haWwiOiJodWtjaHJpc3RvcGhlcjEyQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJmaXJlYmFzZSI6eyJpZGVudGl0aWVzIjp7ImVtYWlsIjpbImh1a2NocmlzdG9waGVyMTJAZ21haWwuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoicGFzc3dvcmQifX0.ghTo-inp_LHtpnEbA1C9mxwwkX_jVTXI9PqyKoDSRm_F86em2hu8mD6m7agdMemNGOUFOR2PYpxGbNGI4aJseAZEflgHXQPVOCP2YnMokvcz8lkcEEVl_OL1uRCa4xEnKw8rCspX-UT8TVdEpZYO3W8SfrrXgVKDbzVpa1bsW6yfbXt5YDs8LDK_msRc_-U3swbIVQxMvEvRoYeZLfNkKtNFDDbKgSA878Z-gMDA03DH2xYYNHR9ZcKCWZFFqBbiNvkJiNum2HXmheYWkG_rozNqOTJuvyJcZUZbwKcUf0Kg2oLjRS4vf28_KACWO2WdRrppGAvIFdbcYVYei0pwZA { "content_type": "post", - "content_id": 1, - "reviewed_by_mod": false + "content_id": 17, + "reviewed_by_mod": true } ### traverse comments ranked by report count @@ -124,11 +124,11 @@ Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjYyM2YzNmM4MTZlZTNkZWQ2YzU0NT GET http://127.0.0.1:8080/api/v1/admin/posts-by-report Content-Type: application/json -Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjYyM2YzNmM4MTZlZTNkZWQ2YzU0NTkyZTM4ZGFlZjcyZjE1YTBmMTMiLCJ0eXAiOiJKV1QifQ.eyJyb2xlcyI6W10sInN5bmMiOnRydWUsImlzcyI6Imh0dHBzOi8vc2VjdXJldG9rZW4uZ29vZ2xlLmNvbS9jb25mZXNpLXNlcnZlci1kZXYiLCJhdWQiOiJjb25mZXNpLXNlcnZlci1kZXYiLCJhdXRoX3RpbWUiOjE2OTEzMTc1NzksInVzZXJfaWQiOiJyQm5LcERKS3FpZ05kNTNTY1JBQktIbXR3VGoxIiwic3ViIjoickJuS3BESktxaWdOZDUzU2NSQUJLSG10d1RqMSIsImlhdCI6MTY5MTMxNzU3OSwiZXhwIjoxNjkxMzIxMTc5LCJlbWFpbCI6Im1hdHRoZXcucmwudHJlbnRAZ21haWwuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsibWF0dGhldy5ybC50cmVudEBnbWFpbC5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9fQ.iyPft2mx1tleTCEeOm9v8BercvOt9r6cGAcAiSiCBXrE38ez0PrZpsauRyIGjDNAKfi-nTGR9rkKfdsz6P_1ByQUKL2AdpXxGYVqqvCU2rE4Nk0Hv6TQtJUEdbkbkTGfgM2jRCjOmP4s_c6d5wP6RAL9XGkYHh1lgssbQFNntDNlUbJRnQnkMFVA2MOig1-ZaFWRuJe6LyL_BGXlhnPStSt3PzQbklC46hoGTH8jmcP8jt-DXUmBKiqIDe1zF4hkRE58zdp3saEiKEc2_lEHvjh2oF6B3Kk-0NeFRVeHpO3kTAc0fIzIlDIeO7FagiP6PzKM0OZpEc5V2TmNxfonLA +Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImNmM2I1YWRhM2NhMzkxNTQ4ZDM1OTJiMzU5MjkyM2UzNjAxMmI5MTQiLCJ0eXAiOiJKV1QifQ.eyJyb2xlcyI6WyJhZG1pbiIsIm1vZF80MSIsIm1vZF8zIiwic19tb2QiXSwic3luYyI6dHJ1ZSwiaXNzIjoiaHR0cHM6Ly9zZWN1cmV0b2tlbi5nb29nbGUuY29tL2NvbmZlc2ktc2VydmVyLWRldiIsImF1ZCI6ImNvbmZlc2ktc2VydmVyLWRldiIsImF1dGhfdGltZSI6MTY5MTc0MTc4MiwidXNlcl9pZCI6InNQMUQ4TVpBUXBac3BkMU83dHVmTFBCRnE5djEiLCJzdWIiOiJzUDFEOE1aQVFwWnNwZDFPN3R1ZkxQQkZxOXYxIiwiaWF0IjoxNjkxNzQxNzgyLCJleHAiOjE2OTE3NDUzODIsImVtYWlsIjoiaHVrY2hyaXN0b3BoZXIxMkBnbWFpbC5jb20iLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiZmlyZWJhc2UiOnsiaWRlbnRpdGllcyI6eyJlbWFpbCI6WyJodWtjaHJpc3RvcGhlcjEyQGdtYWlsLmNvbSJdfSwic2lnbl9pbl9wcm92aWRlciI6InBhc3N3b3JkIn19.JkN59GJ6gqouVUd0G5YIx-71uuNdS_ZqhKBDsAWdjCiTNsgx98O55vtj_oaC8FeIabKu5QBaidBVTBGAyB_XDk8g1eP_vCw_0vy8_FZmwOAZEF-U8RfZW3c21fafH0K-WI8vDKJLKF4RKn_R3fObs_UTXnJae-zBXsbqgARI1dIIs_J6GRuJXTyySW5egsiIclLRJNmJ4DCUo7WVRtzp4hhUqok3TYd00u4wBLTKra3ASfeMT7P72-e-uqL69VOMBdQB45HPUoh4rx5vSX_UxRDTxBvr4nhLYyyTuMDVL5EMDFxKTHhba5bga8UVXJNzqQPzkGUNQI9B5ZbLX3Nr_A { "purge_cache": true, - "reviewed_by_mod": true, + "reviewed_by_mod": false, "session_key": "6ba7b810-9dad-11d1-80b4-00c04fd430c8" } @@ -137,10 +137,10 @@ Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjYyM2YzNmM4MTZlZTNkZWQ2YzU0NT GET http://127.0.0.1:8080/api/v1/admin/reports-for-post Content-Type: application/json -Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjYyM2YzNmM4MTZlZTNkZWQ2YzU0NTkyZTM4ZGFlZjcyZjE1YTBmMTMiLCJ0eXAiOiJKV1QifQ.eyJyb2xlcyI6W10sInN5bmMiOnRydWUsImlzcyI6Imh0dHBzOi8vc2VjdXJldG9rZW4uZ29vZ2xlLmNvbS9jb25mZXNpLXNlcnZlci1kZXYiLCJhdWQiOiJjb25mZXNpLXNlcnZlci1kZXYiLCJhdXRoX3RpbWUiOjE2OTEzMTc1NzksInVzZXJfaWQiOiJyQm5LcERKS3FpZ05kNTNTY1JBQktIbXR3VGoxIiwic3ViIjoickJuS3BESktxaWdOZDUzU2NSQUJLSG10d1RqMSIsImlhdCI6MTY5MTMxNzU3OSwiZXhwIjoxNjkxMzIxMTc5LCJlbWFpbCI6Im1hdHRoZXcucmwudHJlbnRAZ21haWwuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsibWF0dGhldy5ybC50cmVudEBnbWFpbC5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9fQ.iyPft2mx1tleTCEeOm9v8BercvOt9r6cGAcAiSiCBXrE38ez0PrZpsauRyIGjDNAKfi-nTGR9rkKfdsz6P_1ByQUKL2AdpXxGYVqqvCU2rE4Nk0Hv6TQtJUEdbkbkTGfgM2jRCjOmP4s_c6d5wP6RAL9XGkYHh1lgssbQFNntDNlUbJRnQnkMFVA2MOig1-ZaFWRuJe6LyL_BGXlhnPStSt3PzQbklC46hoGTH8jmcP8jt-DXUmBKiqIDe1zF4hkRE58zdp3saEiKEc2_lEHvjh2oF6B3Kk-0NeFRVeHpO3kTAc0fIzIlDIeO7FagiP6PzKM0OZpEc5V2TmNxfonLA +Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImNmM2I1YWRhM2NhMzkxNTQ4ZDM1OTJiMzU5MjkyM2UzNjAxMmI5MTQiLCJ0eXAiOiJKV1QifQ.eyJyb2xlcyI6WyJhZG1pbiIsIm1vZF80MSIsIm1vZF8zMiIsInNfbW9kIl0sInN5bmMiOnRydWUsImlzcyI6Imh0dHBzOi8vc2VjdXJldG9rZW4uZ29vZ2xlLmNvbS9jb25mZXNpLXNlcnZlci1kZXYiLCJhdWQiOiJjb25mZXNpLXNlcnZlci1kZXYiLCJhdXRoX3RpbWUiOjE2OTE3NDE4NjUsInVzZXJfaWQiOiJzUDFEOE1aQVFwWnNwZDFPN3R1ZkxQQkZxOXYxIiwic3ViIjoic1AxRDhNWkFRcFpzcGQxTzd0dWZMUEJGcTl2MSIsImlhdCI6MTY5MTc0MTg2NSwiZXhwIjoxNjkxNzQ1NDY1LCJlbWFpbCI6Imh1a2NocmlzdG9waGVyMTJAZ21haWwuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsiaHVrY2hyaXN0b3BoZXIxMkBnbWFpbC5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9fQ.pJefIPZESjF4cAF37ENrAfhbvGx0eo5gNRMTGuDkGmdlmCs6Mz8MhgDeePY5j5Gh2UpmPKrcVHQ47j7zNH9zKLIQF9EHFTaeePzqusspgVA81aTF2xg60dwTOl0-Q9z-qpSM3ETnoNyhau-MNaTJMDgozRG9mBr7LORkW-kSaNfcEulYuRaO_vqAf7HmjlAun49VRr_C922Ds3w22dNGwzDf7KuqeFusP-nf7wnVOfyXwr1y1aUbHUN3jA4yVZhn5fmc5wizhCBjzfXotcxaLZit2MN17TIYFsDjPGTz60qcag3RBBTk-C279P6rwPP-r8BFf1jBr11Z_4QAWgbH3w { - "post_id": 1, + "post_id": 78, "next": null } @@ -149,9 +149,22 @@ Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjYyM2YzNmM4MTZlZTNkZWQ2YzU0NT GET http://127.0.0.1:8080/api/v1/admin/reports-for-comment Content-Type: application/json -Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjYyM2YzNmM4MTZlZTNkZWQ2YzU0NTkyZTM4ZGFlZjcyZjE1YTBmMTMiLCJ0eXAiOiJKV1QifQ.eyJyb2xlcyI6W10sInN5bmMiOnRydWUsImlzcyI6Imh0dHBzOi8vc2VjdXJldG9rZW4uZ29vZ2xlLmNvbS9jb25mZXNpLXNlcnZlci1kZXYiLCJhdWQiOiJjb25mZXNpLXNlcnZlci1kZXYiLCJhdXRoX3RpbWUiOjE2OTEzMTc1NzksInVzZXJfaWQiOiJyQm5LcERKS3FpZ05kNTNTY1JBQktIbXR3VGoxIiwic3ViIjoickJuS3BESktxaWdOZDUzU2NSQUJLSG10d1RqMSIsImlhdCI6MTY5MTMxNzU3OSwiZXhwIjoxNjkxMzIxMTc5LCJlbWFpbCI6Im1hdHRoZXcucmwudHJlbnRAZ21haWwuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsibWF0dGhldy5ybC50cmVudEBnbWFpbC5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9fQ.iyPft2mx1tleTCEeOm9v8BercvOt9r6cGAcAiSiCBXrE38ez0PrZpsauRyIGjDNAKfi-nTGR9rkKfdsz6P_1ByQUKL2AdpXxGYVqqvCU2rE4Nk0Hv6TQtJUEdbkbkTGfgM2jRCjOmP4s_c6d5wP6RAL9XGkYHh1lgssbQFNntDNlUbJRnQnkMFVA2MOig1-ZaFWRuJe6LyL_BGXlhnPStSt3PzQbklC46hoGTH8jmcP8jt-DXUmBKiqIDe1zF4hkRE58zdp3saEiKEc2_lEHvjh2oF6B3Kk-0NeFRVeHpO3kTAc0fIzIlDIeO7FagiP6PzKM0OZpEc5V2TmNxfonLA +Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImNmM2I1YWRhM2NhMzkxNTQ4ZDM1OTJiMzU5MjkyM2UzNjAxMmI5MTQiLCJ0eXAiOiJKV1QifQ.eyJyb2xlcyI6WyJhZG1pbiIsIm1vZF80MSIsIm1vZF8xMzIiLCJzX21vZCJdLCJzeW5jIjp0cnVlLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vY29uZmVzaS1zZXJ2ZXItZGV2IiwiYXVkIjoiY29uZmVzaS1zZXJ2ZXItZGV2IiwiYXV0aF90aW1lIjoxNjkxNzQxNDQxLCJ1c2VyX2lkIjoic1AxRDhNWkFRcFpzcGQxTzd0dWZMUEJGcTl2MSIsInN1YiI6InNQMUQ4TVpBUXBac3BkMU83dHVmTFBCRnE5djEiLCJpYXQiOjE2OTE3NDE0NDEsImV4cCI6MTY5MTc0NTA0MSwiZW1haWwiOiJodWtjaHJpc3RvcGhlcjEyQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJmaXJlYmFzZSI6eyJpZGVudGl0aWVzIjp7ImVtYWlsIjpbImh1a2NocmlzdG9waGVyMTJAZ21haWwuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoicGFzc3dvcmQifX0.CuI-iJqd_-u28KaLlkCgadI7MFjpUgYtE_ZJjx7dzg_hCE7lxzTRFDVKsCfBKI8oQtZfrHO07YbkAhdcI-JtIh8ckqjQhb7sIgUgkcFAXtI_S3KmbLxbI4se0bCMdA2BVC8UyZUS2CIGE-zYjG_j_lt42fKZ83Mc-MDYfTE_b0zwWWpwSJ8tU5SrwW_LJPmTrkACN98mhJplAN-nEnDEgcyLkL3PrGaSQUlWiN-nXUltK740ut3RyPV6aWJoDZr8RkYkpRYr5wdTXqL20DqXYyVzB-6eVx0QhPUGOJcr1LTtMyu736v03DRy5UjWWOyNbOW-opg7SiY2y5sycDYlCQ { - "comment_id": 1, + "comment_id": 78, "next": null } + + +### Update A Specific User's Role (Add or Remove or Set) + +PATCH http://127.0.0.1:8080/api/v1/admin/set-user-role +Content-Type: application/json +Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImNmM2I1YWRhM2NhMzkxNTQ4ZDM1OTJiMzU5MjkyM2UzNjAxMmI5MTQiLCJ0eXAiOiJKV1QifQ.eyJyb2xlcyI6WyJhZG1pbiIsIm1vZF80MSIsIm1vZF8xMzIiLCJzX21vZCJdLCJzeW5jIjp0cnVlLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vY29uZmVzaS1zZXJ2ZXItZGV2IiwiYXVkIjoiY29uZmVzaS1zZXJ2ZXItZGV2IiwiYXV0aF90aW1lIjoxNjkxNzQxNzAxLCJ1c2VyX2lkIjoic1AxRDhNWkFRcFpzcGQxTzd0dWZMUEJGcTl2MSIsInN1YiI6InNQMUQ4TVpBUXBac3BkMU83dHVmTFBCRnE5djEiLCJpYXQiOjE2OTE3NDE3MDEsImV4cCI6MTY5MTc0NTMwMSwiZW1haWwiOiJodWtjaHJpc3RvcGhlcjEyQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJmaXJlYmFzZSI6eyJpZGVudGl0aWVzIjp7ImVtYWlsIjpbImh1a2NocmlzdG9waGVyMTJAZ21haWwuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoicGFzc3dvcmQifX0.rVY8TNqojJFbjQJvP_z0_ycxOozUa2LFsO3fSWdurCARF9rxlIEfIsQVEJXLc9BpvDHdRa8D6JA7jp2MHLI5hf8DSuhv3T_VlAOZw6bpeORuLNxlBMpjXMMMA92Iw1Pcz-Eve4IagTbFOHNWQAuXjC4ZMui9PdzPnPVt0qvZf2hIZvt_m1yUtg7x-ZxeYGB2mDE0v4z_paseLES0n-kPWXqXymxpWJP2fOFGEcIYeRSflgBhVRxVXAVYCYpu3aTJ3gwcBJzk2IMoRnM6yhQaOO35-SMA2konwIFAeEpxMjo0WfdGmgD0Lyx0tRxLL4cKc_mXkqvCBeSZ_-aJvPiVNg + +{ + "user_id": "sP1D8MZAQpZspd1O7tufLPBFq9v1", + "roles": ["admin", "mod_41", "mod_32","s_mod"], + "action": "set" +} diff --git a/features/admin/review_content_by_mod.go b/features/admin/review_content_by_mod.go index 06d445c8..7be537e6 100644 --- a/features/admin/review_content_by_mod.go +++ b/features/admin/review_content_by_mod.go @@ -27,11 +27,21 @@ func (h *handler) handleReviewContentByMod(c *gin.Context) { return } - err = h.db. + userRoles, err := getUserRoles(c) + if err != nil { + response.New(http.StatusInternalServerError).Err(err.Error()).Send(c) + return + } + + query := h.db. Table(table). - Where("id = ?", req.ContentID). - Update("reviewed_by_mod", req.ReviewedByMod). - Error + Where("id = ?", req.ContentID) + + if len(userRoles.SchoolMods) > 0 { + query.Where("school_id IN ?", userRoles.SchoolMods) + } + + err = query.Update("reviewed_by_mod", req.ReviewedByMod).Error if err != nil { response.New(http.StatusInternalServerError).Err(serverError.Error()).Send(c) return diff --git a/features/admin/router.go b/features/admin/router.go index be7747bc..c7436029 100644 --- a/features/admin/router.go +++ b/features/admin/router.go @@ -37,23 +37,56 @@ type fetchResults struct { func Router(mux *gin.RouterGroup) { h := handler{db: db.New(), fb: fire.New(), redis: cache.New()} mux.Use(func(c *gin.Context) { - //! ADMINS ONLY FOR THESE ROUTES. VERY IMPORTANT. ANY EDITS TO THIS SHOULD RAISE RED FLAGS. - middleware.UsersOnly(c, h.fb.AuthClient, middleware.RegisteredFbUsers, []string{"admin"}) - //! ADMINS ONLY FOR THESE ROUTES. VERY IMPORTANT. ANY EDITS TO THIS SHOULD RAISE RED FLAGS. + //! ADMINS & MODS ONLY FOR THESE ROUTES. VERY IMPORTANT. ANY EDITS TO THIS SHOULD RAISE RED FLAGS. + middleware.UsersOnly(c, h.fb.AuthClient, middleware.RegisteredFbUsers, []string{"admin", "mod", "s_mod"}, middleware.NeedsOne) + //! ADMINS & MODS ONLY FOR THESE ROUTES. VERY IMPORTANT. ANY EDITS TO THIS SHOULD RAISE RED FLAGS. }) + + // ? User Standing mux.PATCH("/user-standing", h.handleSetUserStanding) - mux.GET("/user-standing", h.handleGetUserStanding) - mux.POST("/daily-hottest-cron", h.handleManuallyTriggerDailyHottestCron) - mux.POST("/expire-fcm-tokens", h.handleManuallyTriggerClearExpiredFcmTokens) - mux.GET("/crons", h.handleGetDailyHottestCrons) - mux.GET("/feedback", h.handleListFeedback) - mux.GET("/feedback/:feedbackID", h.handleFeedbackID) + mux.GET("/user-standing", h.handleGetUserStanding) // Any Moderator can get user standing + + // ? Content + + mux.PATCH("/reviewed-by-mod", h.handleReviewContentByMod) + mux.PATCH("/hide", h.handleHideContent) // Only posts are uni specific, comments are not + + // ? Reports mux.GET("/report", h.handleGetReportById) mux.GET("/reports", h.handleGetReports) - mux.PATCH("/hide", h.handleHideContent) - mux.PATCH("/reviewed-by-mod", h.handleReviewContentByMod) mux.GET("/comments-by-report", h.handleGetRankedCommentsByReport) mux.GET("/posts-by-report", h.handleGetRankedPostsByReport) mux.GET("/reports-for-comment", h.handleFetchReportForCommentById) mux.GET("/reports-for-post", h.handleFetchReportForPostById) + + mux.Use(func(c *gin.Context) { + //! ADMINS ONLY FOR THESE ROUTES. VERY IMPORTANT. ANY EDITS TO THIS SHOULD RAISE RED FLAGS. + middleware.UsersOnly(c, h.fb.AuthClient, middleware.RegisteredFbUsers, []string{"admin"}, middleware.NeedsAll) + //! ADMINS ONLY FOR THESE ROUTES. VERY IMPORTANT. ANY EDITS TO THIS SHOULD RAISE RED FLAGS. + }) + + // ? Cron Jobs + mux.POST("/daily-hottest-cron", h.handleManuallyTriggerDailyHottestCron) + mux.POST("/expire-fcm-tokens", h.handleManuallyTriggerClearExpiredFcmTokens) + mux.GET("/crons", h.handleGetDailyHottestCrons) + + // ? Feedback + mux.GET("/feedback", h.handleListFeedback) + mux.GET("/feedback/:feedbackID", h.handleFeedbackID) + + // ? User + mux.PATCH("/set-user-role", h.handleSetUserRole) + +} + +func getUserRoles(c *gin.Context) (*middleware.UserRoleTypes, error) { + userRoleTypesContext, ok := c.Get("userRoleTypes") + if !ok { + return nil, errors.New("userRoleTypes not found in context") + } + roleTypes, err := userRoleTypesContext.(middleware.UserRoleTypes) + if !err { + return nil, serverError + } + return &roleTypes, nil } diff --git a/features/admin/set_user_role.go b/features/admin/set_user_role.go new file mode 100644 index 00000000..74a663fe --- /dev/null +++ b/features/admin/set_user_role.go @@ -0,0 +1,141 @@ +package admin + +import ( + "confesi/db" + "confesi/lib/response" + "confesi/lib/utils" + "confesi/lib/validation" + "net/http" + + "github.com/gin-gonic/gin" +) + +func (h *handler) handleSetUserRole(c *gin.Context) { + // Validate request + var req validation.UpdateUserRole + err := utils.New(c).Validate(&req) + if err != nil { + return + } + + // Obtain User ID + uid := req.UserID + + // Check if user exists + user, err := h.fb.AuthClient.GetUser(c, uid) + if err != nil { + response.New(http.StatusNotFound).Err("user not found").Send(c) + return + } + + // Obtain Action User ID + token, err := utils.UserTokenFromContext(c) + if err != nil { + response.New(http.StatusInternalServerError).Err("server error").Send(c) + return + } + + // Obtain Roles + roles := user.CustomClaims["roles"].([]interface{}) + sync := user.CustomClaims["sync"].(bool) + + // Create Role Logs + roleLogs := db.RoleAssignmentLog{ + ActionType: req.Action, + ActionUserID: token.UID, + AffectedUserID: uid, + OldRoles: interface_to_string(roles), + } + + if req.Action == "remove" { + // Remove roles + temp := make([]interface{}, 0) + + // Iterate through user current roles + for _, item := range roles { + found := false + // Check if item is in req.Roles (removal roles) + for _, rItem := range req.Roles { + if item == rItem { + found = true + break + } + } + // If not found, add to temp + if !found { + temp = append(temp, item) + } + } + + roles = temp + } else if req.Action == "add" { + // Add role (Append) + for _, role := range req.Roles { + roles = append(roles, role) + } + + } else { + // Set Roles (Overwrite) + temp := make([]interface{}, len(req.Roles)) + for i := range req.Roles { + temp[i] = req.Roles[i] + } + roles = temp + } + + // Remove duplicates + roles = unique_list(roles) + + // Update Role Logs + roleLogs.NewRoles = interface_to_string(roles) + + err = h.db.Create(&roleLogs).Error + if err != nil { + response.New(http.StatusInternalServerError).Err("server error").Send(c) + return + } + + // Set Custom Claims (Update User Roles) + err = h.fb.AuthClient.SetCustomUserClaims(c, uid, map[string]interface{}{ + "sync": sync, + "roles": roles, + }) + if err != nil { + response.New(http.StatusNotFound).Err("error updating user").Send(c) + return + } + + // If everything went well return success + response.New(http.StatusOK).Send(c) +} + +// Create unique lists (no dupes) +func unique_list(interface_list []interface{}) []interface{} { + stringSlice := make([]string, len(interface_list)) + for i, v := range interface_list { + stringSlice[i] = v.(string) + } + keys := make(map[string]bool) + list := make([]interface{}, 0) + for _, entry := range stringSlice { + if _, value := keys[entry]; !value { + keys[entry] = true + list = append(list, entry) + } + } + return list +} + +func interface_to_string(interface_list []interface{}) string { + output := "{" + for i, item := range interface_list { + if i == len(interface_list)-1 { + output += item.(string) + } else { + output += item.(string) + "," + } + + } + output += "}" + return output +} diff --git a/features/admin/set_user_standing.go b/features/admin/set_user_standing.go index 3e765c19..3cd78974 100644 --- a/features/admin/set_user_standing.go +++ b/features/admin/set_user_standing.go @@ -21,10 +21,33 @@ func (h *handler) handleSetUserStanding(c *gin.Context) { if err != nil { return } + userRoles, err := getUserRoles(c) + if err != nil { + response.New(http.StatusInternalServerError).Err(err.Error()).Send(c) + return + } + if len(userRoles.SchoolMods) > 0 { + user := db.User{} + query := h.db.Model(&db.User{}).Where("id = ?", req.UserID) + + res := query.First(&user) + if res.Error != nil { + response.New(http.StatusForbidden).Err("user doesn't exist").Send(c) + return + } + + res = query.Where("school_id IN ?", userRoles.SchoolMods).First(&user) + + if res.Error != nil { + response.New(http.StatusForbidden).Err("missing school permissions").Send(c) + return + } + } if req.Standing == "limited" { // update a column `limited` in the `users` table to be true res := h.db.Model(&db.User{}).Where("id = ?", req.UserID).Update("is_limited", true) + if res.Error != nil { response.New(http.StatusInternalServerError).Err("server error").Send(c) return @@ -35,6 +58,7 @@ func (h *handler) handleSetUserStanding(c *gin.Context) { } } else if req.Standing == "enabled" { res := h.db.Model(&db.User{}).Where("id = ?", req.UserID).Update("is_limited", false) + if res.Error != nil { response.New(http.StatusInternalServerError).Err("server error").Send(c) return diff --git a/features/auth/router.go b/features/auth/router.go index 39e2fcf9..21b3c3e5 100644 --- a/features/auth/router.go +++ b/features/auth/router.go @@ -39,7 +39,7 @@ func Router(mux *gin.RouterGroup) { anyFirebaseUser := mux.Group("") anyFirebaseUser.Use(func(c *gin.Context) { - middleware.UsersOnly(c, h.fb.AuthClient, middleware.AllFbUsers, []string{}) + middleware.UsersOnly(c, h.fb.AuthClient, middleware.AllFbUsers, []string{}, middleware.NeedsAll) }) // protect against email spam by UID anyFirebaseUser.Use(func(c *gin.Context) { @@ -55,6 +55,6 @@ func Router(mux *gin.RouterGroup) { // registered firebase users only registeredFirebaseUserRoutes := mux.Group("") registeredFirebaseUserRoutes.Use(func(c *gin.Context) { - middleware.UsersOnly(c, h.fb.AuthClient, middleware.RegisteredFbUsers, []string{}) + middleware.UsersOnly(c, h.fb.AuthClient, middleware.RegisteredFbUsers, []string{}, middleware.NeedsAll) }) } diff --git a/features/comments/create.go b/features/comments/create.go index 77eef425..d824c06d 100644 --- a/features/comments/create.go +++ b/features/comments/create.go @@ -103,9 +103,10 @@ func (h *handler) handleCreate(c *gin.Context) { // base comment comment := db.Comment{ - UserID: token.UID, - PostID: req.PostID, - Content: req.Content, + UserID: token.UID, + PostID: req.PostID, + Content: req.Content, + SchoolID: post.SchoolID, } parentComment := db.Comment{} diff --git a/features/comments/requests.http b/features/comments/requests.http index 37cdc3f4..05eb5405 100644 --- a/features/comments/requests.http +++ b/features/comments/requests.http @@ -3,10 +3,10 @@ POST http://127.0.0.1:8080/api/v1/comments/create Content-Type: application/json -Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjYyM2YzNmM4MTZlZTNkZWQ2YzU0NTkyZTM4ZGFlZjcyZjE1YTBmMTMiLCJ0eXAiOiJKV1QifQ.eyJyb2xlcyI6W10sInN5bmMiOnRydWUsImlzcyI6Imh0dHBzOi8vc2VjdXJldG9rZW4uZ29vZ2xlLmNvbS9jb25mZXNpLXNlcnZlci1kZXYiLCJhdWQiOiJjb25mZXNpLXNlcnZlci1kZXYiLCJhdXRoX3RpbWUiOjE2OTEzMTc1NzksInVzZXJfaWQiOiJyQm5LcERKS3FpZ05kNTNTY1JBQktIbXR3VGoxIiwic3ViIjoickJuS3BESktxaWdOZDUzU2NSQUJLSG10d1RqMSIsImlhdCI6MTY5MTMxNzU3OSwiZXhwIjoxNjkxMzIxMTc5LCJlbWFpbCI6Im1hdHRoZXcucmwudHJlbnRAZ21haWwuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsibWF0dGhldy5ybC50cmVudEBnbWFpbC5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9fQ.iyPft2mx1tleTCEeOm9v8BercvOt9r6cGAcAiSiCBXrE38ez0PrZpsauRyIGjDNAKfi-nTGR9rkKfdsz6P_1ByQUKL2AdpXxGYVqqvCU2rE4Nk0Hv6TQtJUEdbkbkTGfgM2jRCjOmP4s_c6d5wP6RAL9XGkYHh1lgssbQFNntDNlUbJRnQnkMFVA2MOig1-ZaFWRuJe6LyL_BGXlhnPStSt3PzQbklC46hoGTH8jmcP8jt-DXUmBKiqIDe1zF4hkRE58zdp3saEiKEc2_lEHvjh2oF6B3Kk-0NeFRVeHpO3kTAc0fIzIlDIeO7FagiP6PzKM0OZpEc5V2TmNxfonLA +Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImNmM2I1YWRhM2NhMzkxNTQ4ZDM1OTJiMzU5MjkyM2UzNjAxMmI5MTQiLCJ0eXAiOiJKV1QifQ.eyJyb2xlcyI6WyJhZG1pbiIsIm1vZF80MSIsIm1vZF8xMzIiLCJzX21vZCJdLCJzeW5jIjp0cnVlLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vY29uZmVzaS1zZXJ2ZXItZGV2IiwiYXVkIjoiY29uZmVzaS1zZXJ2ZXItZGV2IiwiYXV0aF90aW1lIjoxNjkxNzQwNDY5LCJ1c2VyX2lkIjoic1AxRDhNWkFRcFpzcGQxTzd0dWZMUEJGcTl2MSIsInN1YiI6InNQMUQ4TVpBUXBac3BkMU83dHVmTFBCRnE5djEiLCJpYXQiOjE2OTE3NDA0NjksImV4cCI6MTY5MTc0NDA2OSwiZW1haWwiOiJodWtjaHJpc3RvcGhlcjEyQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJmaXJlYmFzZSI6eyJpZGVudGl0aWVzIjp7ImVtYWlsIjpbImh1a2NocmlzdG9waGVyMTJAZ21haWwuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoicGFzc3dvcmQifX0.LSRCEiTW9vFtvJcqD3xMm5eJi8-reoO1k1KtqfTWGOHO3FrApsRApUHuWsIMymITHAhASD1-FPlid7TAKjRNHQc1uS7BA-vGlDiStAORQdI3Aah93a4jP6bb1ol1Zhi3gxpjwZQ_Aev5XkY1iBkZVzjPCrsUV3km9ZSUVnQQxg2IFdDno5A1lF9tdJNGWwxqjVApRAIWwA0ZN2z_hDd6mdxD04fmVWyPt1IrNOISQhQYcpup0SFK1EQEa8HDsLoIjJ26ajwSmTp9zGzKRNhxyYD6AdfTlNpj3aQpSJ36tE0kd59vCO2AB_vot0CKRLkj_DynuxArOfpXqcRnc6rV2g { - "post_id": 29, + "post_id": 36, "parent_comment_id": null, "content": "this is a comment" } diff --git a/features/comments/router.go b/features/comments/router.go index b52b7027..a98509ee 100644 --- a/features/comments/router.go +++ b/features/comments/router.go @@ -37,7 +37,7 @@ func Router(mux *gin.RouterGroup) { // any firebase user anyFirebaseUserRoutes := mux.Group("") anyFirebaseUserRoutes.Use(func(c *gin.Context) { - middleware.UsersOnly(c, h.fb.AuthClient, middleware.AllFbUsers, []string{}) + middleware.UsersOnly(c, h.fb.AuthClient, middleware.AllFbUsers, []string{}, middleware.NeedsAll) }) anyFirebaseUserRoutes.GET("/roots", h.handleGetComments) anyFirebaseUserRoutes.GET("/replies", h.handleGetReplies) @@ -47,7 +47,7 @@ func Router(mux *gin.RouterGroup) { // registered firebase users only registeredFirebaseUserOnly := mux.Group("") registeredFirebaseUserOnly.Use(func(c *gin.Context) { - middleware.UsersOnly(c, h.fb.AuthClient, middleware.RegisteredFbUsers, []string{}) + middleware.UsersOnly(c, h.fb.AuthClient, middleware.RegisteredFbUsers, []string{}, middleware.NeedsAll) }) registeredFirebaseUserOnly.POST("/create", h.handleCreate) registeredFirebaseUserOnly.PATCH("/hide", h.handleHideComment) diff --git a/features/drafts/router.go b/features/drafts/router.go index fc866c47..c8d5af58 100644 --- a/features/drafts/router.go +++ b/features/drafts/router.go @@ -34,7 +34,7 @@ func Router(mux *gin.RouterGroup) { // only registered firebase users registeredFirebaseUserRoutes := mux.Group("") registeredFirebaseUserRoutes.Use(func(c *gin.Context) { - middleware.UsersOnly(c, h.fb.AuthClient, middleware.RegisteredFbUsers, []string{}) + middleware.UsersOnly(c, h.fb.AuthClient, middleware.RegisteredFbUsers, []string{}, middleware.NeedsAll) }) registeredFirebaseUserRoutes.POST("/create", h.handleCreate) registeredFirebaseUserRoutes.GET("/your-drafts", h.handleGetYourDrafts) diff --git a/features/feedback/router.go b/features/feedback/router.go index 28458e0d..8916af88 100644 --- a/features/feedback/router.go +++ b/features/feedback/router.go @@ -23,7 +23,7 @@ type handler struct { func Router(mux *gin.RouterGroup) { h := handler{db: db.New(), fb: fire.New()} mux.Use(func(c *gin.Context) { - middleware.UsersOnly(c, h.fb.AuthClient, middleware.RegisteredFbUsers, []string{}) + middleware.UsersOnly(c, h.fb.AuthClient, middleware.RegisteredFbUsers, []string{}, middleware.NeedsAll) }) mux.POST("/create", h.handleFeedback) diff --git a/features/hide_log/router.go b/features/hide_log/router.go index d96623d7..8bf1baba 100644 --- a/features/hide_log/router.go +++ b/features/hide_log/router.go @@ -26,7 +26,7 @@ func Router(mux *gin.RouterGroup) { // any firebase user anyFirebaseUserRoutes := mux.Group("") anyFirebaseUserRoutes.Use(func(c *gin.Context) { - middleware.UsersOnly(c, h.fb.AuthClient, middleware.AllFbUsers, []string{}) + middleware.UsersOnly(c, h.fb.AuthClient, middleware.AllFbUsers, []string{}, middleware.NeedsAll) }) anyFirebaseUserRoutes.GET("/your-hide-log", h.handleYourHideLog) anyFirebaseUserRoutes.GET("/hide-log", h.handleGetHideLogById) diff --git a/features/notifications/router.go b/features/notifications/router.go index f3f7ed56..536d6e54 100644 --- a/features/notifications/router.go +++ b/features/notifications/router.go @@ -28,7 +28,7 @@ func Router(mux *gin.RouterGroup) { // any firebase users mux.Use(func(c *gin.Context) { - middleware.UsersOnly(c, h.fb.AuthClient, middleware.AllFbUsers, []string{}) + middleware.UsersOnly(c, h.fb.AuthClient, middleware.AllFbUsers, []string{}, middleware.NeedsAll) }) mux.POST("/token-uid", h.handleSetTokenWithUid) diff --git a/features/posts/requests.http b/features/posts/requests.http index df543629..95023d99 100644 --- a/features/posts/requests.http +++ b/features/posts/requests.http @@ -2,7 +2,7 @@ POST http://127.0.0.1:8080/api/v1/posts/create Content-Type: application/json -Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjYyM2YzNmM4MTZlZTNkZWQ2YzU0NTkyZTM4ZGFlZjcyZjE1YTBmMTMiLCJ0eXAiOiJKV1QifQ.eyJyb2xlcyI6W10sInN5bmMiOnRydWUsImlzcyI6Imh0dHBzOi8vc2VjdXJldG9rZW4uZ29vZ2xlLmNvbS9jb25mZXNpLXNlcnZlci1kZXYiLCJhdWQiOiJjb25mZXNpLXNlcnZlci1kZXYiLCJhdXRoX3RpbWUiOjE2OTEzMTc1NzksInVzZXJfaWQiOiJyQm5LcERKS3FpZ05kNTNTY1JBQktIbXR3VGoxIiwic3ViIjoickJuS3BESktxaWdOZDUzU2NSQUJLSG10d1RqMSIsImlhdCI6MTY5MTMxNzU3OSwiZXhwIjoxNjkxMzIxMTc5LCJlbWFpbCI6Im1hdHRoZXcucmwudHJlbnRAZ21haWwuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsibWF0dGhldy5ybC50cmVudEBnbWFpbC5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9fQ.iyPft2mx1tleTCEeOm9v8BercvOt9r6cGAcAiSiCBXrE38ez0PrZpsauRyIGjDNAKfi-nTGR9rkKfdsz6P_1ByQUKL2AdpXxGYVqqvCU2rE4Nk0Hv6TQtJUEdbkbkTGfgM2jRCjOmP4s_c6d5wP6RAL9XGkYHh1lgssbQFNntDNlUbJRnQnkMFVA2MOig1-ZaFWRuJe6LyL_BGXlhnPStSt3PzQbklC46hoGTH8jmcP8jt-DXUmBKiqIDe1zF4hkRE58zdp3saEiKEc2_lEHvjh2oF6B3Kk-0NeFRVeHpO3kTAc0fIzIlDIeO7FagiP6PzKM0OZpEc5V2TmNxfonLA +Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImNmM2I1YWRhM2NhMzkxNTQ4ZDM1OTJiMzU5MjkyM2UzNjAxMmI5MTQiLCJ0eXAiOiJKV1QifQ.eyJyb2xlcyI6WyJhZG1pbiIsIm1vZF80MSIsIm1vZF8xMzIiLCJzX21vZCJdLCJzeW5jIjp0cnVlLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vY29uZmVzaS1zZXJ2ZXItZGV2IiwiYXVkIjoiY29uZmVzaS1zZXJ2ZXItZGV2IiwiYXV0aF90aW1lIjoxNjkxNzQwNDY5LCJ1c2VyX2lkIjoic1AxRDhNWkFRcFpzcGQxTzd0dWZMUEJGcTl2MSIsInN1YiI6InNQMUQ4TVpBUXBac3BkMU83dHVmTFBCRnE5djEiLCJpYXQiOjE2OTE3NDA0NjksImV4cCI6MTY5MTc0NDA2OSwiZW1haWwiOiJodWtjaHJpc3RvcGhlcjEyQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJmaXJlYmFzZSI6eyJpZGVudGl0aWVzIjp7ImVtYWlsIjpbImh1a2NocmlzdG9waGVyMTJAZ21haWwuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoicGFzc3dvcmQifX0.LSRCEiTW9vFtvJcqD3xMm5eJi8-reoO1k1KtqfTWGOHO3FrApsRApUHuWsIMymITHAhASD1-FPlid7TAKjRNHQc1uS7BA-vGlDiStAORQdI3Aah93a4jP6bb1ol1Zhi3gxpjwZQ_Aev5XkY1iBkZVzjPCrsUV3km9ZSUVnQQxg2IFdDno5A1lF9tdJNGWwxqjVApRAIWwA0ZN2z_hDd6mdxD04fmVWyPt1IrNOISQhQYcpup0SFK1EQEa8HDsLoIjJ26ajwSmTp9zGzKRNhxyYD6AdfTlNpj3aQpSJ36tE0kd59vCO2AB_vot0CKRLkj_DynuxArOfpXqcRnc6rV2g { "title": "NEW NEWN EWblah this is a title", diff --git a/features/posts/router.go b/features/posts/router.go index 6d21a709..b4770f6a 100644 --- a/features/posts/router.go +++ b/features/posts/router.go @@ -40,7 +40,7 @@ func Router(mux *gin.RouterGroup) { // any firebase user anyFirebaseUserRoutes := mux.Group("") anyFirebaseUserRoutes.Use(func(c *gin.Context) { - middleware.UsersOnly(c, h.fb.AuthClient, middleware.AllFbUsers, []string{}) + middleware.UsersOnly(c, h.fb.AuthClient, middleware.AllFbUsers, []string{}, middleware.NeedsAll) }) anyFirebaseUserRoutes.GET("/posts", h.handleGetPosts) anyFirebaseUserRoutes.DELETE("/purge", h.handlePurgePostsCache) @@ -50,7 +50,7 @@ func Router(mux *gin.RouterGroup) { // only registered firebase users registeredFirebaseUserRoutes := mux.Group("") registeredFirebaseUserRoutes.Use(func(c *gin.Context) { - middleware.UsersOnly(c, h.fb.AuthClient, middleware.RegisteredFbUsers, []string{}) + middleware.UsersOnly(c, h.fb.AuthClient, middleware.RegisteredFbUsers, []string{}, middleware.NeedsAll) }) registeredFirebaseUserRoutes.PATCH("/hide", h.handleHidePost) registeredFirebaseUserRoutes.POST("/create", h.handleCreate) diff --git a/features/reports/create.go b/features/reports/create.go index 7bd3f9eb..4cd38f34 100644 --- a/features/reports/create.go +++ b/features/reports/create.go @@ -11,6 +11,7 @@ import ( "github.com/gin-gonic/gin" "github.com/jackc/pgx/v5/pgconn" "gorm.io/gorm" + "gorm.io/gorm/clause" ) func (h *handler) handleCreateReport(c *gin.Context) { @@ -55,11 +56,17 @@ func (h *handler) handleCreateReport(c *gin.Context) { return } + type foundSchoolId struct { + SchoolID uint + } + var SchoolID foundSchoolId // inc the report count for the post/comment by 1 err = tx. Model(&modelMatcher). Where("id = ?", req.ContentID). Update("report_count", gorm.Expr("report_count + 1")). + Clauses(clause.Returning{}). + Scan(&SchoolID). Error if err != nil { tx.Rollback() @@ -84,6 +91,7 @@ func (h *handler) handleCreateReport(c *gin.Context) { report.ReportedBy = token.UID report.Description = req.Description report.TypeID = uint(reportType.ID) + report.SchoolID = SchoolID.SchoolID err = tx.Create(&report).Error if err != nil { diff --git a/features/reports/requests.http b/features/reports/requests.http index 6a244f47..b0598eb5 100644 --- a/features/reports/requests.http +++ b/features/reports/requests.http @@ -2,13 +2,13 @@ POST http://127.0.0.1:8080/api/v1/reports/report Content-Type: application/json -Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6IjYyM2YzNmM4MTZlZTNkZWQ2YzU0NTkyZTM4ZGFlZjcyZjE1YTBmMTMiLCJ0eXAiOiJKV1QifQ.eyJyb2xlcyI6W10sInN5bmMiOnRydWUsImlzcyI6Imh0dHBzOi8vc2VjdXJldG9rZW4uZ29vZ2xlLmNvbS9jb25mZXNpLXNlcnZlci1kZXYiLCJhdWQiOiJjb25mZXNpLXNlcnZlci1kZXYiLCJhdXRoX3RpbWUiOjE2OTEzMTc1NzksInVzZXJfaWQiOiJyQm5LcERKS3FpZ05kNTNTY1JBQktIbXR3VGoxIiwic3ViIjoickJuS3BESktxaWdOZDUzU2NSQUJLSG10d1RqMSIsImlhdCI6MTY5MTMxNzU3OSwiZXhwIjoxNjkxMzIxMTc5LCJlbWFpbCI6Im1hdHRoZXcucmwudHJlbnRAZ21haWwuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImZpcmViYXNlIjp7ImlkZW50aXRpZXMiOnsiZW1haWwiOlsibWF0dGhldy5ybC50cmVudEBnbWFpbC5jb20iXX0sInNpZ25faW5fcHJvdmlkZXIiOiJwYXNzd29yZCJ9fQ.iyPft2mx1tleTCEeOm9v8BercvOt9r6cGAcAiSiCBXrE38ez0PrZpsauRyIGjDNAKfi-nTGR9rkKfdsz6P_1ByQUKL2AdpXxGYVqqvCU2rE4Nk0Hv6TQtJUEdbkbkTGfgM2jRCjOmP4s_c6d5wP6RAL9XGkYHh1lgssbQFNntDNlUbJRnQnkMFVA2MOig1-ZaFWRuJe6LyL_BGXlhnPStSt3PzQbklC46hoGTH8jmcP8jt-DXUmBKiqIDe1zF4hkRE58zdp3saEiKEc2_lEHvjh2oF6B3Kk-0NeFRVeHpO3kTAc0fIzIlDIeO7FagiP6PzKM0OZpEc5V2TmNxfonLA +Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6ImNmM2I1YWRhM2NhMzkxNTQ4ZDM1OTJiMzU5MjkyM2UzNjAxMmI5MTQiLCJ0eXAiOiJKV1QifQ.eyJyb2xlcyI6WyJhZG1pbiIsIm1vZF80MSIsIm1vZF8xMzIiLCJzX21vZCJdLCJzeW5jIjp0cnVlLCJpc3MiOiJodHRwczovL3NlY3VyZXRva2VuLmdvb2dsZS5jb20vY29uZmVzaS1zZXJ2ZXItZGV2IiwiYXVkIjoiY29uZmVzaS1zZXJ2ZXItZGV2IiwiYXV0aF90aW1lIjoxNjkxNzQwNDY5LCJ1c2VyX2lkIjoic1AxRDhNWkFRcFpzcGQxTzd0dWZMUEJGcTl2MSIsInN1YiI6InNQMUQ4TVpBUXBac3BkMU83dHVmTFBCRnE5djEiLCJpYXQiOjE2OTE3NDA0NjksImV4cCI6MTY5MTc0NDA2OSwiZW1haWwiOiJodWtjaHJpc3RvcGhlcjEyQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJmaXJlYmFzZSI6eyJpZGVudGl0aWVzIjp7ImVtYWlsIjpbImh1a2NocmlzdG9waGVyMTJAZ21haWwuY29tIl19LCJzaWduX2luX3Byb3ZpZGVyIjoicGFzc3dvcmQifX0.LSRCEiTW9vFtvJcqD3xMm5eJi8-reoO1k1KtqfTWGOHO3FrApsRApUHuWsIMymITHAhASD1-FPlid7TAKjRNHQc1uS7BA-vGlDiStAORQdI3Aah93a4jP6bb1ol1Zhi3gxpjwZQ_Aev5XkY1iBkZVzjPCrsUV3km9ZSUVnQQxg2IFdDno5A1lF9tdJNGWwxqjVApRAIWwA0ZN2z_hDd6mdxD04fmVWyPt1IrNOISQhQYcpup0SFK1EQEa8HDsLoIjJ26ajwSmTp9zGzKRNhxyYD6AdfTlNpj3aQpSJ36tE0kd59vCO2AB_vot0CKRLkj_DynuxArOfpXqcRnc6rV2g { - "content_id": 1, - "content_type": "post", - "type": "nsfw", - "description": "This is an nsfw post" + "content_id": 78, + "content_type": "comment", + "type": "porn", + "description": "This1 is an nsfw post" } ### Fetch your reports diff --git a/features/reports/router.go b/features/reports/router.go index 97555989..bfe06cb5 100644 --- a/features/reports/router.go +++ b/features/reports/router.go @@ -28,7 +28,7 @@ func Router(mux *gin.RouterGroup) { // any firebase user anyFirebaseUserRoutes := mux.Group("") anyFirebaseUserRoutes.Use(func(c *gin.Context) { - middleware.UsersOnly(c, h.fb.AuthClient, middleware.AllFbUsers, []string{}) + middleware.UsersOnly(c, h.fb.AuthClient, middleware.AllFbUsers, []string{}, middleware.NeedsAll) }) anyFirebaseUserRoutes.GET("/report", h.handleGetReportById) anyFirebaseUserRoutes.POST("/report", h.handleCreateReport) diff --git a/features/saves/router.go b/features/saves/router.go index d82030a3..89d0e61d 100644 --- a/features/saves/router.go +++ b/features/saves/router.go @@ -25,7 +25,7 @@ func Router(mux *gin.RouterGroup) { // only allow registered users to save content mux.Use(func(c *gin.Context) { - middleware.UsersOnly(c, h.fb.AuthClient, middleware.RegisteredFbUsers, []string{}) + middleware.UsersOnly(c, h.fb.AuthClient, middleware.RegisteredFbUsers, []string{}, middleware.NeedsAll) }) mux.POST("/save", h.handleSave) diff --git a/features/schools/router.go b/features/schools/router.go index b2459e0d..cacaa1b7 100644 --- a/features/schools/router.go +++ b/features/schools/router.go @@ -64,7 +64,7 @@ func Router(r *gin.RouterGroup) { // any firebase user anyFirebaseUser := r.Group("") anyFirebaseUser.Use(func(c *gin.Context) { - middleware.UsersOnly(c, h.fb.AuthClient, middleware.AllFbUsers, []string{}) + middleware.UsersOnly(c, h.fb.AuthClient, middleware.AllFbUsers, []string{}, middleware.NeedsAll) }) anyFirebaseUser.GET("/rank", h.handleGetRankedSchools) anyFirebaseUser.DELETE("/purge", h.handlePurgeRankedSchoolsCache) @@ -75,7 +75,7 @@ func Router(r *gin.RouterGroup) { // only registered firebase users registeredFirebaseUsersOnly := r.Group("") registeredFirebaseUsersOnly.Use(func(c *gin.Context) { - middleware.UsersOnly(c, h.fb.AuthClient, middleware.RegisteredFbUsers, []string{}) + middleware.UsersOnly(c, h.fb.AuthClient, middleware.RegisteredFbUsers, []string{}, middleware.NeedsAll) }) registeredFirebaseUsersOnly.POST("/watch", h.handleWatchSchool) registeredFirebaseUsersOnly.DELETE("/unwatch", h.handleUnwatchSchool) diff --git a/features/user/router.go b/features/user/router.go index 01d4f45c..6d00edf3 100644 --- a/features/user/router.go +++ b/features/user/router.go @@ -35,7 +35,7 @@ func Router(mux *gin.RouterGroup) { // registered firebase users only registeredFirebaseUsersOnly := mux.Group("") registeredFirebaseUsersOnly.Use(func(c *gin.Context) { - middleware.UsersOnly(c, h.fb.AuthClient, middleware.RegisteredFbUsers, []string{}) + middleware.UsersOnly(c, h.fb.AuthClient, middleware.RegisteredFbUsers, []string{}, middleware.NeedsAll) }) registeredFirebaseUsersOnly.GET("/user", h.handleGetUser) registeredFirebaseUsersOnly.GET("/user-standing", h.handleGetUserStanding) diff --git a/features/votes/router.go b/features/votes/router.go index df08365c..1401fcd2 100644 --- a/features/votes/router.go +++ b/features/votes/router.go @@ -18,7 +18,6 @@ var ( type handler struct { db *gorm.DB fb *fire.FirebaseApp - } func Router(mux *gin.RouterGroup) { @@ -26,7 +25,7 @@ func Router(mux *gin.RouterGroup) { // only allow registered users to vote mux.Use(func(c *gin.Context) { - middleware.UsersOnly(c, h.fb.AuthClient, middleware.RegisteredFbUsers, []string{}) + middleware.UsersOnly(c, h.fb.AuthClient, middleware.RegisteredFbUsers, []string{}, middleware.NeedsAll) }) mux.PUT("/vote", h.handleVote) diff --git a/go.mod b/go.mod index a801973c..d8bff1fe 100644 --- a/go.mod +++ b/go.mod @@ -96,6 +96,7 @@ require ( go.opencensus.io v0.24.0 // indirect golang.org/x/arch v0.3.0 // indirect golang.org/x/crypto v0.11.0 // indirect + golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b golang.org/x/net v0.12.0 // indirect golang.org/x/oauth2 v0.8.0 // indirect golang.org/x/sys v0.10.0 // indirect diff --git a/go.sum b/go.sum index 9e7e482c..8de5d888 100644 --- a/go.sum +++ b/go.sum @@ -306,6 +306,8 @@ golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45 golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b h1:r+vk0EmXNmekl0S0BascoeeoHk/L7wmaW2QF90K+kYI= +golang.org/x/exp v0.0.0-20230801115018-d63ba01acd4b/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= diff --git a/lib/validation/models.go b/lib/validation/models.go index 3ced15d4..60be235f 100644 --- a/lib/validation/models.go +++ b/lib/validation/models.go @@ -84,6 +84,14 @@ type UpdateYearOfStudy struct { // [required] year of study to update to YearOfStudy string `json:"year_of_study" validate:"required"` } +type UpdateUserRole struct { + // [required] user id to update role for + UserID string `json:"user_id" validate:"required"` + // [required] timestamp of last viewed report (microseconds since epoch) + Roles []string `json:"roles" validate:"required"` + // [required] action to take on the role, remove to remove the role, add to add the role + Action string `json:"action" validate:"required,oneof=add remove set"` +} type UpdateFaculty struct { // [required] faculty to update to diff --git a/middleware/firebase_auth.go b/middleware/firebase_auth.go index ee633a00..fafdff02 100644 --- a/middleware/firebase_auth.go +++ b/middleware/firebase_auth.go @@ -7,12 +7,21 @@ import ( "confesi/lib/validation" "errors" "net/http" + "regexp" + "strconv" + "strings" "firebase.google.com/go/auth" "github.com/gin-gonic/gin" "github.com/jackc/pgx/v5/pgconn" ) +type UserRoleTypes struct { + Admin bool `json:"admin"` // can do anything + GlobalMod bool `json:"global_mod"` // can do mod actions for any school + SchoolMods []int `json:"school_mods"` // can do mod actions for specific schools +} + type AllowedUser string const ( @@ -20,13 +29,20 @@ const ( RegisteredFbUsers AllowedUser = "registered_fb_users" // only registered users who have a postgres profile (fully created account) ) +type RoleRequirements string + +const ( + NeedsOne RoleRequirements = "strict" // every role listed must be present in the user's roles + NeedsAll RoleRequirements = "relaxed" // at least one of the roles listed must be present in the user's roles +) + // Only allows valid Firebase users to pass through. // // Sets the authenticated Firebase user id to the context as `user` // iff the user is anon or registered. // // Allows for additional checks to see if a user possess some specified roles. -func UsersOnly(c *gin.Context, auth *auth.Client, allowedUser AllowedUser, roles []string) { +func UsersOnly(c *gin.Context, auth *auth.Client, allowedUser AllowedUser, roles []string, roleRequirements RoleRequirements) { idToken := c.GetHeader("Authorization") if len(idToken) < 8 || idToken[:7] != "Bearer " { response.New(http.StatusUnauthorized).Err("malformed Authorization header").Send(c) @@ -87,22 +103,39 @@ func UsersOnly(c *gin.Context, auth *auth.Client, allowedUser AllowedUser, roles return } } + // create user role types + userRoleTypes, err := createUserRoleTypes(rolesInterfaceSlice) + if err != nil { + response.New(http.StatusInternalServerError).Err("server error").Send(c) + return + } // check if all the required roles exist in the parsed roles - for _, requiredRole := range roles { + { found := false - for _, role := range parsedRoles { - if requiredRole == role { - found = true - break + for _, requiredRole := range roles { + if roleRequirements == NeedsAll { + found = false + } + for _, role := range parsedRoles { + if requiredRole == role { + found = true + break + } + } + if !found && roleRequirements == NeedsAll { + response.New(http.StatusUnauthorized).Err("invalid role").Send(c) + return } } - if !found { + if !found && roleRequirements == NeedsOne { response.New(http.StatusUnauthorized).Err("invalid role").Send(c) return } } + c.Set("userRoleTypes", *userRoleTypes) + c.Set("user", token) c.Next() return @@ -201,3 +234,34 @@ func RetrySyncPostgresAccountCreation(c *gin.Context, token *auth.Token) error { return nil } + +func createUserRoleTypes(roles []interface{}) (*UserRoleTypes, error) { + userRoleTypes := UserRoleTypes{} + pattern := `^mod\_[0-9]+$` + regex, err := regexp.Compile(pattern) + if err != nil { + return nil, err + } + for _, role := range roles { + roleString := role.(string) + switch roleString { + case "admin": + userRoleTypes.Admin = true + case "mod": + userRoleTypes.GlobalMod = true + } + if strings.Contains(roleString, "mod_") { + res := regex.FindString(roleString) + + stringSchoolID := strings.Split(res, "_")[1] + schoolID, err := strconv.Atoi(stringSchoolID) + if err != nil { + return nil, err + } + userRoleTypes.SchoolMods = append(userRoleTypes.SchoolMods, schoolID) + } + + } + + return &userRoleTypes, nil +} diff --git a/migrations/000050_role_assignment_logs.down.sql b/migrations/000050_role_assignment_logs.down.sql new file mode 100644 index 00000000..011816cb --- /dev/null +++ b/migrations/000050_role_assignment_logs.down.sql @@ -0,0 +1,4 @@ +BEGIN; + DROP TABLE IF EXISTS role_assignment_logs; + DROP type IF EXISTS role_action_type; +END; diff --git a/migrations/000050_role_assignment_logs.up.sql b/migrations/000050_role_assignment_logs.up.sql new file mode 100644 index 00000000..243378ec --- /dev/null +++ b/migrations/000050_role_assignment_logs.up.sql @@ -0,0 +1,15 @@ +BEGIN; + +CREATE TYPE role_action_type AS ENUM ('set','add', 'remove'); + + CREATE TABLE role_assignment_logs ( + id SERIAL PRIMARY KEY UNIQUE NOT NULL, + created_at TIMESTAMPTZ NOT NULL, + action_user_id VARCHAR(255) NOT NULL, + affected_user_id VARCHAR(255) NOT NULL, + old_roles VARCHAR(255)[] NOT NULL, + new_roles VARCHAR(255)[] NOT NULL, + action_type role_action_type NOT NULL + + ); +END; \ No newline at end of file diff --git a/migrations/000051_report_school_ids.down.sql b/migrations/000051_report_school_ids.down.sql new file mode 100644 index 00000000..7618c3b9 --- /dev/null +++ b/migrations/000051_report_school_ids.down.sql @@ -0,0 +1,9 @@ +BEGIN; + +ALTER TABLE reports DROP CONSTRAINT sf_fk_sch; +ALTER TABLE reports DROP COLUMN school_id; + +ALTER TABLE comments DROP CONSTRAINT sf_fk_sch; +ALTER TABLE comments DROP COLUMN school_id; + +END; \ No newline at end of file diff --git a/migrations/000051_report_school_ids.up.sql b/migrations/000051_report_school_ids.up.sql new file mode 100644 index 00000000..c5dba1f6 --- /dev/null +++ b/migrations/000051_report_school_ids.up.sql @@ -0,0 +1,9 @@ +BEGIN; + +ALTER TABLE reports ADD COLUMN school_id INTEGER; +ALTER TABLE reports ADD CONSTRAINT sf_fk_sch FOREIGN KEY (school_id) REFERENCES schools (id); + +ALTER TABLE comments ADD COLUMN school_id INTEGER; +ALTER TABLE comments ADD CONSTRAINT sf_fk_sch FOREIGN KEY (school_id) REFERENCES schools (id); + +END; \ No newline at end of file