diff --git a/enshi_back/ABAC/PostVotesPolicies/PostVotePolicies.go b/enshi_back/ABAC/PostVotesPolicies/PostVotePolicies.go new file mode 100644 index 0000000..045a327 --- /dev/null +++ b/enshi_back/ABAC/PostVotesPolicies/PostVotePolicies.go @@ -0,0 +1,36 @@ +package postvotespolicies + +import ( + postvoterules "enshi/ABAC/PostVotesPolicies/PostVoteRules" + "enshi/ABAC/rules" + + "github.com/gin-gonic/gin" +) + +const ( + DELETE_VOTE = "delete_vote" + CREATE_VOTE = "create_vote" + READ_VOTE = "read_vote" +) + +func PostVotePolicies(c *gin.Context) (bool, []error) { + target, exists := c.Get("target") + if !exists { + return false, nil + } + + // Permit if one permit + switch target { + case DELETE_VOTE: + return rules.CheckRule(c, postvoterules.PostVoteDeleteRule) + + case CREATE_VOTE: + return rules.CheckRule(c, postvoterules.PostVoteCreateRule) + + case READ_VOTE: + return rules.CheckRule(c, postvoterules.PostVoteReadRule) + + } + + return false, nil +} diff --git a/enshi_back/ABAC/PostVotesPolicies/PostVoteRules/createRule.go b/enshi_back/ABAC/PostVotesPolicies/PostVoteRules/createRule.go new file mode 100644 index 0000000..cdde23a --- /dev/null +++ b/enshi_back/ABAC/PostVotesPolicies/PostVoteRules/createRule.go @@ -0,0 +1,22 @@ +package postvoterules + +import ( + globalrules "enshi/ABAC/GlobalRules" + "enshi/ABAC/rules" + + "github.com/gin-gonic/gin" +) + +func PostVoteCreateRule(c *gin.Context) (bool, []error) { + rulesToCheck := []rules.RuleFunction{ + globalrules.AuthorizedRule, + } + + isAllowed, errors := rules.CheckRules( + c, + rulesToCheck, + rules.ALL_RULES_MUST_BE_COMPLETED, + ) + + return isAllowed, errors +} diff --git a/enshi_back/ABAC/PostVotesPolicies/PostVoteRules/deleteRule.go b/enshi_back/ABAC/PostVotesPolicies/PostVoteRules/deleteRule.go new file mode 100644 index 0000000..b24ecc9 --- /dev/null +++ b/enshi_back/ABAC/PostVotesPolicies/PostVoteRules/deleteRule.go @@ -0,0 +1,22 @@ +package postvoterules + +import ( + globalrules "enshi/ABAC/GlobalRules" + "enshi/ABAC/rules" + + "github.com/gin-gonic/gin" +) + +func PostVoteDeleteRule(c *gin.Context) (bool, []error) { + rulesToCheck := []rules.RuleFunction{ + globalrules.AuthorizedRule, + } + + isAllowed, errors := rules.CheckRules( + c, + rulesToCheck, + rules.ALL_RULES_MUST_BE_COMPLETED, + ) + + return isAllowed, errors +} diff --git a/enshi_back/ABAC/PostVotesPolicies/PostVoteRules/readRule.go b/enshi_back/ABAC/PostVotesPolicies/PostVoteRules/readRule.go new file mode 100644 index 0000000..0f707cc --- /dev/null +++ b/enshi_back/ABAC/PostVotesPolicies/PostVoteRules/readRule.go @@ -0,0 +1,22 @@ +package postvoterules + +import ( + globalrules "enshi/ABAC/GlobalRules" + "enshi/ABAC/rules" + + "github.com/gin-gonic/gin" +) + +func PostVoteReadRule(c *gin.Context) (bool, []error) { + rulesToCheck := []rules.RuleFunction{ + globalrules.AuthorizedRule, + } + + isAllowed, errors := rules.CheckRules( + c, + rulesToCheck, + rules.ALL_RULES_MUST_BE_COMPLETED, + ) + + return isAllowed, errors +} diff --git a/enshi_back/db/go_queries/post_votes_queries.sql.go b/enshi_back/db/go_queries/post_votes_queries.sql.go index 171b179..688d23e 100644 --- a/enshi_back/db/go_queries/post_votes_queries.sql.go +++ b/enshi_back/db/go_queries/post_votes_queries.sql.go @@ -13,6 +13,9 @@ const createPostVote = `-- name: CreatePostVote :one INSERT INTO public.post_votes (post_id, user_id, vote) VALUES($1, $2, $3) +ON CONFLICT (user_id, post_id) +DO UPDATE SET + vote = $3 RETURNING post_id, user_id, vote ` diff --git a/enshi_back/db/queries/post_votes_queries.sql b/enshi_back/db/queries/post_votes_queries.sql index 74cbfd3..ee6b651 100644 --- a/enshi_back/db/queries/post_votes_queries.sql +++ b/enshi_back/db/queries/post_votes_queries.sql @@ -2,6 +2,9 @@ INSERT INTO public.post_votes (post_id, user_id, vote) VALUES($1, $2, $3) +ON CONFLICT (user_id, post_id) +DO UPDATE SET + vote = $3 RETURNING *; -- name: DeletePostVote :exec diff --git a/enshi_back/middleware/postVotesMiddleware.go b/enshi_back/middleware/postVotesMiddleware.go new file mode 100644 index 0000000..f6805b9 --- /dev/null +++ b/enshi_back/middleware/postVotesMiddleware.go @@ -0,0 +1,33 @@ +package middleware + +import ( + postvotespolicies "enshi/ABAC/PostVotesPolicies" + "enshi/ABAC/rules" + + "github.com/gin-gonic/gin" +) + +func PostVotesMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + + switch c.Request.Method { + case "DELETE": + c.Set("target", postvotespolicies.DELETE_VOTE) + + case "POST": + c.Set("target", postvotespolicies.CREATE_VOTE) + + case "GET": + c.Set("target", postvotespolicies.READ_VOTE) + } + + isAllowed, errors := postvotespolicies.PostVotePolicies(c) + + if rules.ShouldAbortRequest(c, isAllowed, errors) { + c.Abort() + return + } + + c.Next() + } +} diff --git a/enshi_back/routes/bookmarksRoutes/createBookmark.go b/enshi_back/routes/bookmarksRoutes/createBookmark.go index 14fdae9..d12734e 100644 --- a/enshi_back/routes/bookmarksRoutes/createBookmark.go +++ b/enshi_back/routes/bookmarksRoutes/createBookmark.go @@ -10,7 +10,7 @@ import ( "github.com/gin-gonic/gin" ) -func CreatePost(c *gin.Context) { +func CreateBookmark(c *gin.Context) { var bookmarkParams db_repo.CreateBookmarkParams if err := c.BindJSON(&bookmarkParams); err != nil { diff --git a/enshi_back/routes/bookmarksRoutes/deleteBookmark.go b/enshi_back/routes/bookmarksRoutes/deleteBookmark.go new file mode 100644 index 0000000..b0d9432 --- /dev/null +++ b/enshi_back/routes/bookmarksRoutes/deleteBookmark.go @@ -0,0 +1,35 @@ +package bookmarksroutes + +import ( + "context" + rest_api_stuff "enshi/REST_API_stuff" + db_repo "enshi/db/go_queries" + "enshi/db_connection" + "enshi/middleware/getters" + + "github.com/gin-gonic/gin" +) + +func DeleteBookmark(c *gin.Context) { + var bookmarkParams db_repo.DeleteBookmarkParams + + if err := c.BindJSON(&bookmarkParams); err != nil { + rest_api_stuff.BadRequestAnswer(c, err) + return + } + + userId, err := getters.GetUserIdFromContext(c) + if err != nil { + rest_api_stuff.BadRequestAnswer(c, err) + return + } + bookmarkParams.UserID = userId + + query := db_repo.New(db_connection.Dbx) + if err := query.DeleteBookmark(context.Background(), bookmarkParams); err != nil { + rest_api_stuff.InternalErrorAnswer(c, err) + return + } + + rest_api_stuff.OkAnswer(c, "Bookmark has been deleted!") +} diff --git a/enshi_back/routes/bookmarksRoutes/getBookmark.go b/enshi_back/routes/bookmarksRoutes/getBookmark.go new file mode 100644 index 0000000..c2630bc --- /dev/null +++ b/enshi_back/routes/bookmarksRoutes/getBookmark.go @@ -0,0 +1,48 @@ +package bookmarksroutes + +import ( + "context" + rest_api_stuff "enshi/REST_API_stuff" + db_repo "enshi/db/go_queries" + "enshi/db_connection" + "enshi/middleware/getters" + "net/http" + "time" + + "github.com/gin-gonic/gin" +) + +func GetBookmark(c *gin.Context) { + var bookmarkParams db_repo.GetBookmarkTimestampParams + + if err := c.BindJSON(&bookmarkParams); err != nil { + rest_api_stuff.BadRequestAnswer(c, err) + return + } + + userId, err := getters.GetUserIdFromContext(c) + if err != nil { + rest_api_stuff.BadRequestAnswer(c, err) + return + } + bookmarkParams.UserID = userId + + query := db_repo.New(db_connection.Dbx) + if timestamp, err := query.GetBookmarkTimestamp(context.Background(), bookmarkParams); err != nil { + rest_api_stuff.InternalErrorAnswer(c, err) + return + } else { + if timestamp.Valid { + c.IndentedJSON(http.StatusOK, gin.H{ + "isBookmarked": timestamp.Valid, + "bookmarkedAt": timestamp.Time, + }) + return + } else { + c.IndentedJSON(http.StatusOK, gin.H{ + "isBookmarked": timestamp.Valid, + "bookmarkedAt": time.Unix(1<<63-1, 0).UTC(), + }) + } + } +} diff --git a/enshi_back/routes/routesSetup.go b/enshi_back/routes/routesSetup.go index 0e6e3de..ebbbdb7 100644 --- a/enshi_back/routes/routesSetup.go +++ b/enshi_back/routes/routesSetup.go @@ -8,6 +8,7 @@ import ( bookmarksroutes "enshi/routes/bookmarksRoutes" "enshi/routes/postsRoutes" "enshi/routes/userProfileRoutes" + voteroutes "enshi/routes/voteRoutes" "net/http" "strings" @@ -123,6 +124,34 @@ func SetupRotes(g *gin.Engine) error { bookmarksroutes.CreateBookmark, ) + bookmarksGroup.DELETE( + "bookmarks/:post-id", + bookmarksroutes.DeleteBookmark, + ) + + bookmarksGroup.GET( + "bookmarks/:post-id", + bookmarksroutes.GetBookmark, + ) + + postVoteGroup := g.Group("/") + postVoteGroup.Use(middleware.PostVotesMiddleware()) + + postVoteGroup.POST( + "post-votes/:post-id", + voteroutes.CreateVote, + ) + + postVoteGroup.DELETE( + "post-votes/:post-id", + voteroutes.DeleteVote, + ) + + postVoteGroup.GET( + "post-votes/:post-id", + voteroutes.GetVote, + ) + // Admin group routes adminGroup := g.Group("/admin/") adminGroup.Use(middleware.AdminMiddleware()) diff --git a/enshi_back/routes/voteRoutes/createVote.go b/enshi_back/routes/voteRoutes/createVote.go new file mode 100644 index 0000000..58946dc --- /dev/null +++ b/enshi_back/routes/voteRoutes/createVote.go @@ -0,0 +1,35 @@ +package voteroutes + +import ( + "context" + rest_api_stuff "enshi/REST_API_stuff" + db_repo "enshi/db/go_queries" + "enshi/db_connection" + "enshi/middleware/getters" + + "github.com/gin-gonic/gin" +) + +func CreateVote(c *gin.Context) { + var postVoteParams db_repo.CreatePostVoteParams + + if err := c.BindJSON(&postVoteParams); err != nil { + rest_api_stuff.BadRequestAnswer(c, err) + return + } + + userId, err := getters.GetUserIdFromContext(c) + if err != nil { + rest_api_stuff.BadRequestAnswer(c, err) + return + } + postVoteParams.UserID = userId + + query := db_repo.New(db_connection.Dbx) + if _, err := query.CreatePostVote(context.Background(), postVoteParams); err != nil { + rest_api_stuff.InternalErrorAnswer(c, err) + return + } + + rest_api_stuff.OkAnswer(c, "Vote has been created!") +} diff --git a/enshi_back/routes/voteRoutes/deleteVote.go b/enshi_back/routes/voteRoutes/deleteVote.go new file mode 100644 index 0000000..baf1fdf --- /dev/null +++ b/enshi_back/routes/voteRoutes/deleteVote.go @@ -0,0 +1,35 @@ +package voteroutes + +import ( + "context" + rest_api_stuff "enshi/REST_API_stuff" + db_repo "enshi/db/go_queries" + "enshi/db_connection" + "enshi/middleware/getters" + + "github.com/gin-gonic/gin" +) + +func DeleteVote(c *gin.Context) { + var postVoteParams db_repo.DeletePostVoteParams + + if err := c.BindJSON(&postVoteParams); err != nil { + rest_api_stuff.BadRequestAnswer(c, err) + return + } + + userId, err := getters.GetUserIdFromContext(c) + if err != nil { + rest_api_stuff.BadRequestAnswer(c, err) + return + } + postVoteParams.UserID = userId + + query := db_repo.New(db_connection.Dbx) + if err := query.DeletePostVote(context.Background(), postVoteParams); err != nil { + rest_api_stuff.InternalErrorAnswer(c, err) + return + } + + rest_api_stuff.OkAnswer(c, "Vote has been deleted!") +} diff --git a/enshi_back/routes/voteRoutes/getVote.go b/enshi_back/routes/voteRoutes/getVote.go new file mode 100644 index 0000000..609e74d --- /dev/null +++ b/enshi_back/routes/voteRoutes/getVote.go @@ -0,0 +1,38 @@ +package voteroutes + +import ( + "context" + rest_api_stuff "enshi/REST_API_stuff" + db_repo "enshi/db/go_queries" + "enshi/db_connection" + "enshi/middleware/getters" + "net/http" + + "github.com/gin-gonic/gin" +) + +func GetVote(c *gin.Context) { + var postVoteParams db_repo.GetPostVoteParams + + if err := c.BindJSON(&postVoteParams); err != nil { + rest_api_stuff.BadRequestAnswer(c, err) + return + } + + userId, err := getters.GetUserIdFromContext(c) + if err != nil { + rest_api_stuff.BadRequestAnswer(c, err) + return + } + postVoteParams.UserID = userId + + query := db_repo.New(db_connection.Dbx) + if voteData, err := query.GetPostVote(context.Background(), postVoteParams); err != nil { + rest_api_stuff.InternalErrorAnswer(c, err) + return + } else { + c.IndentedJSON(http.StatusOK, gin.H{ + "vote": voteData, + }) + } +}