From 7a507192a47dbe57351f180ca9f0abd36fbcfff7 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 12 Nov 2024 21:45:18 +0300 Subject: [PATCH 01/30] Dockerfile was added | admin middleware | get post route --- enshi_back/.dockerignore | 8 + enshi_back/dockerfile | 15 ++ enshi_back/global/globalColorsForConsole.go | 18 +- enshi_back/global/globalVars.go | 10 +- enshi_back/global/keys.go | 5 + enshi_back/main.go | 3 +- enshi_back/middleware/adminMiddleware.go | 32 +++- enshi_back/middleware/authMiddleware.go | 5 +- enshi_back/middleware/getters/claims.go | 3 +- enshi_back/middleware/getters/userId.go | 4 +- enshi_back/middleware/keys.go | 5 - enshi_back/routes/{ => authRoutes}/login.go | 2 +- .../routes/{ => authRoutes}/registerUser.go | 2 +- .../routes/{ => postsRoutes}/createPost.go | 2 +- .../routes/{ => postsRoutes}/deletePost.go | 8 +- enshi_back/routes/postsRoutes/getPost.go | 34 ++++ enshi_back/utils/routesSetup.go | 20 +- package-lock.json | 171 ------------------ package.json | 6 - 19 files changed, 138 insertions(+), 215 deletions(-) create mode 100644 enshi_back/.dockerignore create mode 100644 enshi_back/dockerfile create mode 100644 enshi_back/global/keys.go delete mode 100644 enshi_back/middleware/keys.go rename enshi_back/routes/{ => authRoutes}/login.go (98%) rename enshi_back/routes/{ => authRoutes}/registerUser.go (99%) rename enshi_back/routes/{ => postsRoutes}/createPost.go (97%) rename enshi_back/routes/{ => postsRoutes}/deletePost.go (94%) create mode 100644 enshi_back/routes/postsRoutes/getPost.go delete mode 100644 package-lock.json delete mode 100644 package.json diff --git a/enshi_back/.dockerignore b/enshi_back/.dockerignore new file mode 100644 index 0000000..7d02313 --- /dev/null +++ b/enshi_back/.dockerignore @@ -0,0 +1,8 @@ +main +*.log +*.swp +*.tmp +*.out +node_modules +.idea +.vscode \ No newline at end of file diff --git a/enshi_back/dockerfile b/enshi_back/dockerfile new file mode 100644 index 0000000..03b4638 --- /dev/null +++ b/enshi_back/dockerfile @@ -0,0 +1,15 @@ +FROM golang:1.23.3-alpine3.20 + +WORKDIR /enshi_app + +COPY go.mod go.sum ./ + +RUN go mod download + +COPY . . + +RUN go build -o enshi_bin . + +EXPOSE 9876 + +CMD [ "./enshi_bin" ] \ No newline at end of file diff --git a/enshi_back/global/globalColorsForConsole.go b/enshi_back/global/globalColorsForConsole.go index 1de4c21..e7ea966 100644 --- a/enshi_back/global/globalColorsForConsole.go +++ b/enshi_back/global/globalColorsForConsole.go @@ -1,11 +1,11 @@ package global -var ResetColor = "\033[0m" -var RedColor = "\033[31m" -var GreenColor = "\033[32m" -var YellowColor = "\033[33m" -var BlueColor = "\033[34m" -var MagentaColor = "\033[35m" -var CyanColor = "\033[36m" -var GrayColor = "\033[37m" -var WhiteColor = "\033[97m" +const ResetColor = "\033[0m" +const RedColor = "\033[31m" +const GreenColor = "\033[32m" +const YellowColor = "\033[33m" +const BlueColor = "\033[34m" +const MagentaColor = "\033[35m" +const CyanColor = "\033[36m" +const GrayColor = "\033[37m" +const WhiteColor = "\033[97m" diff --git a/enshi_back/global/globalVars.go b/enshi_back/global/globalVars.go index b064789..a581119 100644 --- a/enshi_back/global/globalVars.go +++ b/enshi_back/global/globalVars.go @@ -1,6 +1,8 @@ package global -var PathForCookies = "/" -var DomainForCookies = "localhost" -var SecureForCookies = false -var HttpOnlyForCookies = false +const PathForCookies = "/" +const DomainForCookies = "localhost" +const SecureForCookies = false +const HttpOnlyForCookies = false + +const GinWorkPath = "localhost:9876" diff --git a/enshi_back/global/keys.go b/enshi_back/global/keys.go new file mode 100644 index 0000000..902798e --- /dev/null +++ b/enshi_back/global/keys.go @@ -0,0 +1,5 @@ +package global + +const ContextUserId = "id" +const ContextIsAdmin = "isAdmin" +const ContextTokenData = "tokenData" diff --git a/enshi_back/main.go b/enshi_back/main.go index 72320f0..85fe413 100644 --- a/enshi_back/main.go +++ b/enshi_back/main.go @@ -5,6 +5,7 @@ import ( db_repo "enshi/db/go_queries" "enshi/db_connection" "enshi/env" + "enshi/global" utils "enshi/utils" "fmt" @@ -47,7 +48,7 @@ func main() { return } - router.Run("localhost:9876") + router.Run(global.GinWorkPath) fmt.Printf("Hey!, %v", "you") } diff --git a/enshi_back/middleware/adminMiddleware.go b/enshi_back/middleware/adminMiddleware.go index d336e0b..49a1165 100644 --- a/enshi_back/middleware/adminMiddleware.go +++ b/enshi_back/middleware/adminMiddleware.go @@ -1,10 +1,40 @@ package middleware -import "github.com/gin-gonic/gin" +import ( + "context" + rest_api_stuff "enshi/REST_API_stuff" + db_repo "enshi/db/go_queries" + "enshi/db_connection" + "enshi/middleware/getters" + "fmt" + + "github.com/gin-gonic/gin" +) func AdminMiddleware() gin.HandlerFunc { return func(c *gin.Context) { + userId, err := getters.GetUserIdFromContext(c) + + if err != nil || userId == 0 { + rest_api_stuff.BadRequestAnswer(c, err) + c.Abort() + } + + user, err := + db_repo.New(db_connection.Dbx). + GetUserById(context.Background(), userId) + + if err != nil || user.UserID == 0 { + rest_api_stuff.BadRequestAnswer(c, err) + c.Abort() + } + + if !user.IsAdmin { + rest_api_stuff.UnauthorizedAnswer(c, fmt.Errorf("not allowed")) + c.Abort() + } + c.Next() } } diff --git a/enshi_back/middleware/authMiddleware.go b/enshi_back/middleware/authMiddleware.go index d99a6df..201f08f 100644 --- a/enshi_back/middleware/authMiddleware.go +++ b/enshi_back/middleware/authMiddleware.go @@ -2,6 +2,7 @@ package middleware import ( "enshi/auth" + "enshi/global" "net/http" "github.com/gin-gonic/gin" @@ -28,8 +29,8 @@ func AuthMiddleware() gin.HandlerFunc { // } // Claims -> data stored in token - c.Set(ContextUserId, cookieClimes["id"]) - c.Set(ContextTokenData, cookieClimes) + c.Set(global.ContextUserId, cookieClimes["id"]) + c.Set(global.ContextTokenData, cookieClimes) c.Next() } diff --git a/enshi_back/middleware/getters/claims.go b/enshi_back/middleware/getters/claims.go index c074cd4..371650d 100644 --- a/enshi_back/middleware/getters/claims.go +++ b/enshi_back/middleware/getters/claims.go @@ -3,7 +3,6 @@ package getters import ( "enshi/auth" "enshi/global" - "enshi/middleware" "fmt" "strconv" @@ -14,7 +13,7 @@ import ( func GetClaimsFromContext(c *gin.Context) (auth.UserInfoJWT, error) { var UserInfo auth.UserInfoJWT - claims, exists := c.Get(middleware.ContextTokenData) + claims, exists := c.Get(global.ContextTokenData) if !exists { return auth.UserInfoJWT{}, fmt.Errorf("error getting user id") diff --git a/enshi_back/middleware/getters/userId.go b/enshi_back/middleware/getters/userId.go index f4a1319..7afaead 100644 --- a/enshi_back/middleware/getters/userId.go +++ b/enshi_back/middleware/getters/userId.go @@ -1,7 +1,7 @@ package getters import ( - "enshi/middleware" + "enshi/global" "fmt" "strconv" @@ -9,7 +9,7 @@ import ( ) func GetUserIdFromContext(c *gin.Context) (int64, error) { - userId, exists := c.Get(middleware.ContextUserId) + userId, exists := c.Get(global.ContextUserId) if !exists { return -1, fmt.Errorf("error getting user id") diff --git a/enshi_back/middleware/keys.go b/enshi_back/middleware/keys.go deleted file mode 100644 index 3035b5e..0000000 --- a/enshi_back/middleware/keys.go +++ /dev/null @@ -1,5 +0,0 @@ -package middleware - -var ContextUserId = "id" -var ContextIsAdmin = "isAdmin" -var ContextTokenData = "tokenData" diff --git a/enshi_back/routes/login.go b/enshi_back/routes/authRoutes/login.go similarity index 98% rename from enshi_back/routes/login.go rename to enshi_back/routes/authRoutes/login.go index 6d4e3a7..212cb43 100644 --- a/enshi_back/routes/login.go +++ b/enshi_back/routes/authRoutes/login.go @@ -1,4 +1,4 @@ -package routes +package authRoutes import ( "context" diff --git a/enshi_back/routes/registerUser.go b/enshi_back/routes/authRoutes/registerUser.go similarity index 99% rename from enshi_back/routes/registerUser.go rename to enshi_back/routes/authRoutes/registerUser.go index ffdc7cd..79b994f 100644 --- a/enshi_back/routes/registerUser.go +++ b/enshi_back/routes/authRoutes/registerUser.go @@ -1,4 +1,4 @@ -package routes +package authRoutes import ( "context" diff --git a/enshi_back/routes/createPost.go b/enshi_back/routes/postsRoutes/createPost.go similarity index 97% rename from enshi_back/routes/createPost.go rename to enshi_back/routes/postsRoutes/createPost.go index 65dfc49..2d3b221 100644 --- a/enshi_back/routes/createPost.go +++ b/enshi_back/routes/postsRoutes/createPost.go @@ -1,4 +1,4 @@ -package routes +package postsRoutes import ( "context" diff --git a/enshi_back/routes/deletePost.go b/enshi_back/routes/postsRoutes/deletePost.go similarity index 94% rename from enshi_back/routes/deletePost.go rename to enshi_back/routes/postsRoutes/deletePost.go index 29867e5..0ac4395 100644 --- a/enshi_back/routes/deletePost.go +++ b/enshi_back/routes/postsRoutes/deletePost.go @@ -1,4 +1,4 @@ -package routes +package postsRoutes import ( "context" @@ -13,7 +13,7 @@ import ( func DeletePost(c *gin.Context) { var deletePostId struct { - postId int64 + PostId int64 } if err := c.BindJSON(&deletePostId); err != nil { @@ -28,7 +28,7 @@ func DeletePost(c *gin.Context) { } query := db_repo.New(db_connection.Dbx) - post, err := query.GetPostsByPostId(context.Background(), deletePostId.postId) + post, err := query.GetPostsByPostId(context.Background(), deletePostId.PostId) if err != nil { rest_api_stuff.InternalErrorAnswer(c, err) return @@ -41,7 +41,7 @@ func DeletePost(c *gin.Context) { // TODO: Add block of code, so admin could delete anything - err = query.DeletePostByPostId(context.Background(), deletePostId.postId) + err = query.DeletePostByPostId(context.Background(), deletePostId.PostId) if err != nil { rest_api_stuff.InternalErrorAnswer(c, err) return diff --git a/enshi_back/routes/postsRoutes/getPost.go b/enshi_back/routes/postsRoutes/getPost.go new file mode 100644 index 0000000..ab661a6 --- /dev/null +++ b/enshi_back/routes/postsRoutes/getPost.go @@ -0,0 +1,34 @@ +package postsRoutes + +import ( + "context" + rest_api_stuff "enshi/REST_API_stuff" + db_repo "enshi/db/go_queries" + "enshi/db_connection" + "net/http" + + "github.com/gin-gonic/gin" +) + +func GetPost(c *gin.Context) { + var postParams struct { + PostId int64 + } + + if err := c.BindJSON(&postParams); err != nil { + rest_api_stuff.BadRequestAnswer(c, err) + return + } + + postData, err := + db_repo.New(db_connection.Dbx). + GetPostsByPostId(context.Background(), postParams.PostId) + + if err != nil { + rest_api_stuff.InternalErrorAnswer(c, err) + return + } + + c.IndentedJSON(http.StatusOK, postData) + +} diff --git a/enshi_back/utils/routesSetup.go b/enshi_back/utils/routesSetup.go index 9bc1057..556dedd 100644 --- a/enshi_back/utils/routesSetup.go +++ b/enshi_back/utils/routesSetup.go @@ -3,6 +3,8 @@ package utils import ( "enshi/middleware" "enshi/routes" + "enshi/routes/authRoutes" + "enshi/routes/postsRoutes" "net/http" "strings" @@ -14,6 +16,10 @@ func testCookie(c *gin.Context) { c.IndentedJSON(http.StatusOK, gin.H{"token": "SLESAR' U STASA " + strings.Split(cock, "_")[0]}) } +func testAdmin(c *gin.Context) { + c.IndentedJSON(http.StatusOK, gin.H{"message": "you are an admin, congrats!"}) +} + func SetupRotes(g *gin.Engine) error { g.Use(middleware.CORSMiddleware()) @@ -22,19 +28,23 @@ func SetupRotes(g *gin.Engine) error { freeGroup.GET("getCookie", testCookie) - freeGroup.POST("login", routes.Login) - freeGroup.POST("registerUser", routes.RegisterUser) + freeGroup.POST("login", authRoutes.Login) + freeGroup.POST("registerUser", authRoutes.RegisterUser) // Auth group routes authGroup := g.Group("/") authGroup.Use(middleware.AuthMiddleware()) - authGroup.POST("createPost", routes.CreatePost) - authGroup.POST("deletePost", routes.DeletePost) + + authGroup.GET("getPost", postsRoutes.GetPost) + + authGroup.POST("createPost", postsRoutes.CreatePost) + authGroup.POST("deletePost", postsRoutes.DeletePost) + authGroup.POST("changeUserProfile", routes.ChangeUserProfile) adminGroup := authGroup.Group("/admin/") adminGroup.Use(middleware.AdminMiddleware()) - authGroup.POST("changeUserProfile", routes.ChangeUserProfile) + adminGroup.GET("testAdmin", testAdmin) return nil } diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 793629c..0000000 --- a/package-lock.json +++ /dev/null @@ -1,171 +0,0 @@ -{ - "name": "Enshi", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "dependencies": { - "@tanstack/react-query": "^5.59.0", - "axios": "^1.7.7" - } - }, - "node_modules/@tanstack/query-core": { - "version": "5.59.0", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.59.0.tgz", - "integrity": "sha512-WGD8uIhX6/deH/tkZqPNcRyAhDUqs729bWKoByYHSogcshXfFbppOdTER5+qY7mFvu8KEFJwT0nxr8RfPTVh0Q==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, - "node_modules/@tanstack/react-query": { - "version": "5.59.0", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.59.0.tgz", - "integrity": "sha512-YDXp3OORbYR+8HNQx+lf4F73NoiCmCcSvZvgxE29OifmQFk0sBlO26NWLHpcNERo92tVk3w+JQ53/vkcRUY1hA==", - "license": "MIT", - "dependencies": { - "@tanstack/query-core": "5.59.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "react": "^18 || ^19" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/axios": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", - "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", - "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT", - "peer": true - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "license": "MIT", - "peer": true, - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" - }, - "node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "loose-envify": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index 82a5d97..0000000 --- a/package.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "dependencies": { - "@tanstack/react-query": "^5.59.0", - "axios": "^1.7.7" - } -} From d39c38d065162d1fe8c6f87c124d33a8276e715d Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 13 Nov 2024 22:09:18 +0300 Subject: [PATCH 02/30] Some epic changes --- enshi_back/db/go_queries/posts_queries.sql.go | 6 +-- enshi_back/db/queries/posts_queries.sql | 4 +- enshi_back/middleware/adminMiddleware.go | 12 ++--- enshi_back/middleware/checkRole/isAdmin.go | 23 +++++++++ enshi_back/middleware/checkRole/isOwner.go | 23 +++++++++ enshi_back/middleware/getters/claims.go | 9 +--- enshi_back/routes/postsRoutes/deletePost.go | 2 +- enshi_back/routes/postsRoutes/getPost.go | 2 +- enshi_back/routes/postsRoutes/updatePost.go | 51 +++++++++++++++++++ .../userProfileRoutes/updateUserProfile.go | 38 ++++++++++++++ enshi_back/utils/routesSetup.go | 10 ++-- 11 files changed, 154 insertions(+), 26 deletions(-) create mode 100644 enshi_back/middleware/checkRole/isAdmin.go create mode 100644 enshi_back/middleware/checkRole/isOwner.go create mode 100644 enshi_back/routes/postsRoutes/updatePost.go create mode 100644 enshi_back/routes/userProfileRoutes/updateUserProfile.go diff --git a/enshi_back/db/go_queries/posts_queries.sql.go b/enshi_back/db/go_queries/posts_queries.sql.go index ba88faa..2090708 100644 --- a/enshi_back/db/go_queries/posts_queries.sql.go +++ b/enshi_back/db/go_queries/posts_queries.sql.go @@ -148,14 +148,13 @@ func (q *Queries) GetPostsByUserId(ctx context.Context, userID int64) ([]Post, e const updatePostByPostId = `-- name: UpdatePostByPostId :one UPDATE public.posts -SET blog_id=$1, user_id=$2, title=$3, "content"=$4, updated_at=CURRENT_TIMESTAMP -WHERE post_id = $5 +SET blog_id=$1, title=$2, "content"=$3, updated_at=CURRENT_TIMESTAMP +WHERE post_id = $4 RETURNING post_id, blog_id, user_id, title, content, created_at, updated_at ` type UpdatePostByPostIdParams struct { BlogID pgtype.Int8 `json:"blog_id"` - UserID int64 `json:"user_id"` Title pgtype.Text `json:"title"` Content pgtype.Text `json:"content"` PostID int64 `json:"post_id"` @@ -164,7 +163,6 @@ type UpdatePostByPostIdParams struct { func (q *Queries) UpdatePostByPostId(ctx context.Context, arg UpdatePostByPostIdParams) (Post, error) { row := q.db.QueryRow(ctx, updatePostByPostId, arg.BlogID, - arg.UserID, arg.Title, arg.Content, arg.PostID, diff --git a/enshi_back/db/queries/posts_queries.sql b/enshi_back/db/queries/posts_queries.sql index c8e5545..718e005 100644 --- a/enshi_back/db/queries/posts_queries.sql +++ b/enshi_back/db/queries/posts_queries.sql @@ -21,8 +21,8 @@ RETURNING *; -- name: UpdatePostByPostId :one UPDATE public.posts -SET blog_id=$1, user_id=$2, title=$3, "content"=$4, updated_at=CURRENT_TIMESTAMP -WHERE post_id = $5 +SET blog_id=$1, title=$2, "content"=$3, updated_at=CURRENT_TIMESTAMP +WHERE post_id = $4 RETURNING *; -- name: DeletePostByPostId :exec diff --git a/enshi_back/middleware/adminMiddleware.go b/enshi_back/middleware/adminMiddleware.go index 49a1165..3d35800 100644 --- a/enshi_back/middleware/adminMiddleware.go +++ b/enshi_back/middleware/adminMiddleware.go @@ -1,10 +1,8 @@ package middleware import ( - "context" rest_api_stuff "enshi/REST_API_stuff" - db_repo "enshi/db/go_queries" - "enshi/db_connection" + "enshi/middleware/checkRole" "enshi/middleware/getters" "fmt" @@ -21,16 +19,14 @@ func AdminMiddleware() gin.HandlerFunc { c.Abort() } - user, err := - db_repo.New(db_connection.Dbx). - GetUserById(context.Background(), userId) + isAdmin, err := checkRole.IsAdmin(userId) - if err != nil || user.UserID == 0 { + if err != nil { rest_api_stuff.BadRequestAnswer(c, err) c.Abort() } - if !user.IsAdmin { + if !isAdmin { rest_api_stuff.UnauthorizedAnswer(c, fmt.Errorf("not allowed")) c.Abort() } diff --git a/enshi_back/middleware/checkRole/isAdmin.go b/enshi_back/middleware/checkRole/isAdmin.go new file mode 100644 index 0000000..93a356e --- /dev/null +++ b/enshi_back/middleware/checkRole/isAdmin.go @@ -0,0 +1,23 @@ +package checkRole + +import ( + "context" + db_repo "enshi/db/go_queries" + "enshi/db_connection" +) + +func IsAdmin(userId int64) (bool, error) { + user, err := + db_repo.New(db_connection.Dbx). + GetUserById(context.Background(), userId) + + if err != nil || user.UserID == 0 { + return false, err + } + + if !user.IsAdmin { + return false, nil + } + + return true, nil +} diff --git a/enshi_back/middleware/checkRole/isOwner.go b/enshi_back/middleware/checkRole/isOwner.go new file mode 100644 index 0000000..5f046e9 --- /dev/null +++ b/enshi_back/middleware/checkRole/isOwner.go @@ -0,0 +1,23 @@ +package checkRole + +import ( + "context" + db_repo "enshi/db/go_queries" + "enshi/db_connection" +) + +func IsOwnerOfThePost(userId int64, postId int64) (bool, error) { + post, err := + db_repo.New(db_connection.Dbx). + GetPostsByPostId(context.Background(), postId) + + if err != nil { + return false, err + } + + if post.UserID != userId { + return false, nil + } + + return true, nil +} diff --git a/enshi_back/middleware/getters/claims.go b/enshi_back/middleware/getters/claims.go index 371650d..19d170d 100644 --- a/enshi_back/middleware/getters/claims.go +++ b/enshi_back/middleware/getters/claims.go @@ -30,13 +30,8 @@ func GetClaimsFromContext(c *gin.Context) (auth.UserInfoJWT, error) { UserInfo.Id = parsedUserId UserInfo.Username = claims.(jwt.MapClaims)["username"].(string) - isAdmin, err := strconv.ParseBool(claims.(jwt.MapClaims)["isAdmin"].(string)) - if err != nil { - UserInfo.IsAdmin = false - fmt.Println(global.RedColor + "isAdmin prop corrupted" + global.ResetColor) - } else { - UserInfo.IsAdmin = isAdmin - } + isAdmin := claims.(jwt.MapClaims)["isAdmin"].(bool) + UserInfo.IsAdmin = isAdmin return UserInfo, nil diff --git a/enshi_back/routes/postsRoutes/deletePost.go b/enshi_back/routes/postsRoutes/deletePost.go index 0ac4395..52c6e1b 100644 --- a/enshi_back/routes/postsRoutes/deletePost.go +++ b/enshi_back/routes/postsRoutes/deletePost.go @@ -13,7 +13,7 @@ import ( func DeletePost(c *gin.Context) { var deletePostId struct { - PostId int64 + PostId int64 `json:"post_id"` } if err := c.BindJSON(&deletePostId); err != nil { diff --git a/enshi_back/routes/postsRoutes/getPost.go b/enshi_back/routes/postsRoutes/getPost.go index ab661a6..2f9e8b2 100644 --- a/enshi_back/routes/postsRoutes/getPost.go +++ b/enshi_back/routes/postsRoutes/getPost.go @@ -12,7 +12,7 @@ import ( func GetPost(c *gin.Context) { var postParams struct { - PostId int64 + PostId int64 `json:"post_id"` } if err := c.BindJSON(&postParams); err != nil { diff --git a/enshi_back/routes/postsRoutes/updatePost.go b/enshi_back/routes/postsRoutes/updatePost.go new file mode 100644 index 0000000..d1cfd45 --- /dev/null +++ b/enshi_back/routes/postsRoutes/updatePost.go @@ -0,0 +1,51 @@ +package postsRoutes + +import ( + "context" + rest_api_stuff "enshi/REST_API_stuff" + db_repo "enshi/db/go_queries" + "enshi/db_connection" + "enshi/middleware/checkRole" + "enshi/middleware/getters" + "fmt" + + "github.com/gin-gonic/gin" +) + +func UpdatePost(c *gin.Context) { + var UpdatedPostParams db_repo.UpdatePostByPostIdParams + + if err := c.BindJSON(&UpdatedPostParams); err != nil { + rest_api_stuff.BadRequestAnswer(c, err) + return + } + + userId, err := getters.GetUserIdFromContext(c) + + if err != nil { + rest_api_stuff.InternalErrorAnswer(c, err) + return + } + + if isOwner, _ := checkRole.IsOwnerOfThePost( + userId, + UpdatedPostParams.PostID, + ); !isOwner { + rest_api_stuff.UnauthorizedAnswer(c, fmt.Errorf("you are now allowed to change this")) + return + } + + _, err = db_repo.New( + db_connection.Dbx, + ).UpdatePostByPostId( + context.Background(), + UpdatedPostParams, + ) + + if err != nil { + rest_api_stuff.InternalErrorAnswer(c, err) + return + } + + rest_api_stuff.OkAnswer(c, "post has been updated") +} diff --git a/enshi_back/routes/userProfileRoutes/updateUserProfile.go b/enshi_back/routes/userProfileRoutes/updateUserProfile.go new file mode 100644 index 0000000..fe792e9 --- /dev/null +++ b/enshi_back/routes/userProfileRoutes/updateUserProfile.go @@ -0,0 +1,38 @@ +package userProfileRoutes + +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 UpdateUserProfile(c *gin.Context) { + var newProfile db_repo.UpdateProfileByUserIdParams + + if err := c.BindJSON(&newProfile); err != nil { + rest_api_stuff.BadRequestAnswer(c, err) + return + } + + userId, err := getters.GetUserIdFromContext(c) + if err != nil { + rest_api_stuff.InternalErrorAnswer(c, err) + return + } + + newProfile.UserID = userId + + if _, err := db_repo.New(db_connection.Dbx).UpdateProfileByUserId( + context.Background(), + newProfile, + ); err != nil { + rest_api_stuff.InternalErrorAnswer(c, err) + return + } + + rest_api_stuff.OkAnswer(c, "profile was updated") +} diff --git a/enshi_back/utils/routesSetup.go b/enshi_back/utils/routesSetup.go index 556dedd..832273e 100644 --- a/enshi_back/utils/routesSetup.go +++ b/enshi_back/utils/routesSetup.go @@ -5,6 +5,7 @@ import ( "enshi/routes" "enshi/routes/authRoutes" "enshi/routes/postsRoutes" + "enshi/routes/userProfileRoutes" "net/http" "strings" @@ -30,17 +31,20 @@ func SetupRotes(g *gin.Engine) error { freeGroup.POST("login", authRoutes.Login) freeGroup.POST("registerUser", authRoutes.RegisterUser) + freeGroup.GET("getPost", postsRoutes.GetPost) // Auth group routes authGroup := g.Group("/") authGroup.Use(middleware.AuthMiddleware()) - authGroup.GET("getPost", postsRoutes.GetPost) - + authGroup.POST("updatePost", postsRoutes.UpdatePost) authGroup.POST("createPost", postsRoutes.CreatePost) - authGroup.POST("deletePost", postsRoutes.DeletePost) authGroup.POST("changeUserProfile", routes.ChangeUserProfile) + authGroup.POST("updateProfile", userProfileRoutes.UpdateUserProfile) + authGroup.DELETE("deletePost", postsRoutes.DeletePost) + + // Admin group routes adminGroup := authGroup.Group("/admin/") adminGroup.Use(middleware.AdminMiddleware()) From 01da5ec2ee6268aa06996e5f60dfbc6929c85cc9 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 15 Nov 2024 01:04:48 +0300 Subject: [PATCH 03/30] Massive rework has been started --- enshi_back/ABAC/globalRules/authorizedRule.go | 24 +++++++++ enshi_back/ABAC/postsPolicies/postPolicy.go | 50 +++++++++++++++++++ .../postsPolicies/postRules/createRule.go | 10 ++++ .../postsPolicies/postRules/deleteRule.go | 22 ++++++++ .../ABAC/postsPolicies/postRules/readRule.go | 10 ++++ .../postsPolicies/postRules/updateRule.go | 17 +++++++ enshi_back/middleware/adminMiddleware.go | 10 +--- enshi_back/middleware/checkRole/isAdmin.go | 11 +++- .../{isOwner.go => isOwnerOfThePost.go} | 17 ++++++- enshi_back/middleware/getters/getIntParam.go | 17 +++++++ enshi_back/middleware/postsMiddleware.go | 41 +++++++++++++++ enshi_back/routes/changeUserProfile.go | 17 ------- enshi_back/routes/postsRoutes/deletePost.go | 11 ++-- enshi_back/routes/postsRoutes/getPost.go | 9 ++-- enshi_back/routes/postsRoutes/updatePost.go | 18 +++---- enshi_back/utils/routesSetup.go | 37 ++++++++++---- 16 files changed, 263 insertions(+), 58 deletions(-) create mode 100644 enshi_back/ABAC/globalRules/authorizedRule.go create mode 100644 enshi_back/ABAC/postsPolicies/postPolicy.go create mode 100644 enshi_back/ABAC/postsPolicies/postRules/createRule.go create mode 100644 enshi_back/ABAC/postsPolicies/postRules/deleteRule.go create mode 100644 enshi_back/ABAC/postsPolicies/postRules/readRule.go create mode 100644 enshi_back/ABAC/postsPolicies/postRules/updateRule.go rename enshi_back/middleware/checkRole/{isOwner.go => isOwnerOfThePost.go} (51%) create mode 100644 enshi_back/middleware/getters/getIntParam.go create mode 100644 enshi_back/middleware/postsMiddleware.go delete mode 100644 enshi_back/routes/changeUserProfile.go diff --git a/enshi_back/ABAC/globalRules/authorizedRule.go b/enshi_back/ABAC/globalRules/authorizedRule.go new file mode 100644 index 0000000..0aa714a --- /dev/null +++ b/enshi_back/ABAC/globalRules/authorizedRule.go @@ -0,0 +1,24 @@ +package globalrules + +import ( + "enshi/auth" + "enshi/global" + "net/http" + + "github.com/gin-gonic/gin" +) + +func AuthorizedRule(c *gin.Context) (bool, error) { + tokenFromCookies := c.Request.CookiesNamed("auth_cookie")[0].Value + cookieClimes, err := auth.ValidateToken(tokenFromCookies) + if err != nil { + c.IndentedJSON(http.StatusUnauthorized, gin.H{"error auth": err.Error()}) + c.Abort() + return false, err + } else { + c.Set(global.ContextUserId, cookieClimes["id"]) + c.Set(global.ContextTokenData, cookieClimes) + } + + return true, nil +} diff --git a/enshi_back/ABAC/postsPolicies/postPolicy.go b/enshi_back/ABAC/postsPolicies/postPolicy.go new file mode 100644 index 0000000..7d16b81 --- /dev/null +++ b/enshi_back/ABAC/postsPolicies/postPolicy.go @@ -0,0 +1,50 @@ +package postspolicies + +import ( + postRules "enshi/ABAC/postsPolicies/postRules" + + "github.com/gin-gonic/gin" +) + +const ( + DELETE_POST = "delete_post" + UPDATE_POST = "update_post" + CREATE_POST = "create_post" + GET_POST = "get_post" +) + +func checkRule( + c *gin.Context, + ruleChecker func(*gin.Context) (bool, error), +) (bool, error) { + IsAllowed, err := ruleChecker(c) + if err != nil { + return false, err + } + + return IsAllowed, nil +} + +func PostsPolicies(c *gin.Context) (bool, error) { + target, exists := c.Get("target") + if !exists { + return false, nil + } + + switch target { + case DELETE_POST: + return checkRule(c, postRules.DeleteRule) + + case UPDATE_POST: + return checkRule(c, postRules.PostUpdateRule) + + case GET_POST: + return checkRule(c, postRules.PostReadRule) + + case CREATE_POST: + return checkRule(c, postRules.PostCreateRule) + + } + + return false, nil +} diff --git a/enshi_back/ABAC/postsPolicies/postRules/createRule.go b/enshi_back/ABAC/postsPolicies/postRules/createRule.go new file mode 100644 index 0000000..81026fd --- /dev/null +++ b/enshi_back/ABAC/postsPolicies/postRules/createRule.go @@ -0,0 +1,10 @@ +package postRules + +import ( + "github.com/gin-gonic/gin" +) + +// Only owner of the post can change it +func PostCreateRule(c *gin.Context) (bool, error) { + return true, nil +} diff --git a/enshi_back/ABAC/postsPolicies/postRules/deleteRule.go b/enshi_back/ABAC/postsPolicies/postRules/deleteRule.go new file mode 100644 index 0000000..4e2cfec --- /dev/null +++ b/enshi_back/ABAC/postsPolicies/postRules/deleteRule.go @@ -0,0 +1,22 @@ +package postRules + +import ( + "enshi/middleware/checkRole" + + "github.com/gin-gonic/gin" +) + +// Only owner or admin can delete post +func DeleteRule(c *gin.Context) (bool, error) { + isOwner, err := checkRole.IsOwnerOfThePost(c) + if err != nil { + return false, err + } + + isAdmin, err := checkRole.IsAdmin(c) + if err != nil { + return false, err + } + + return isAdmin || isOwner, err +} diff --git a/enshi_back/ABAC/postsPolicies/postRules/readRule.go b/enshi_back/ABAC/postsPolicies/postRules/readRule.go new file mode 100644 index 0000000..df83bc5 --- /dev/null +++ b/enshi_back/ABAC/postsPolicies/postRules/readRule.go @@ -0,0 +1,10 @@ +package postRules + +import ( + "github.com/gin-gonic/gin" +) + +// Only owner of the post can change it +func PostReadRule(c *gin.Context) (bool, error) { + return true, nil +} diff --git a/enshi_back/ABAC/postsPolicies/postRules/updateRule.go b/enshi_back/ABAC/postsPolicies/postRules/updateRule.go new file mode 100644 index 0000000..7a1200c --- /dev/null +++ b/enshi_back/ABAC/postsPolicies/postRules/updateRule.go @@ -0,0 +1,17 @@ +package postRules + +import ( + "enshi/middleware/checkRole" + + "github.com/gin-gonic/gin" +) + +// Only owner of the post can change it +func PostUpdateRule(c *gin.Context) (bool, error) { + isOwner, err := checkRole.IsOwnerOfThePost(c) + if err != nil { + return false, err + } + + return isOwner, nil +} diff --git a/enshi_back/middleware/adminMiddleware.go b/enshi_back/middleware/adminMiddleware.go index 3d35800..0ddf300 100644 --- a/enshi_back/middleware/adminMiddleware.go +++ b/enshi_back/middleware/adminMiddleware.go @@ -3,7 +3,6 @@ package middleware import ( rest_api_stuff "enshi/REST_API_stuff" "enshi/middleware/checkRole" - "enshi/middleware/getters" "fmt" "github.com/gin-gonic/gin" @@ -12,14 +11,7 @@ import ( func AdminMiddleware() gin.HandlerFunc { return func(c *gin.Context) { - userId, err := getters.GetUserIdFromContext(c) - - if err != nil || userId == 0 { - rest_api_stuff.BadRequestAnswer(c, err) - c.Abort() - } - - isAdmin, err := checkRole.IsAdmin(userId) + isAdmin, err := checkRole.IsAdmin(c) if err != nil { rest_api_stuff.BadRequestAnswer(c, err) diff --git a/enshi_back/middleware/checkRole/isAdmin.go b/enshi_back/middleware/checkRole/isAdmin.go index 93a356e..4e16a59 100644 --- a/enshi_back/middleware/checkRole/isAdmin.go +++ b/enshi_back/middleware/checkRole/isAdmin.go @@ -4,9 +4,18 @@ import ( "context" db_repo "enshi/db/go_queries" "enshi/db_connection" + "enshi/middleware/getters" + + "github.com/gin-gonic/gin" ) -func IsAdmin(userId int64) (bool, error) { +func IsAdmin(c *gin.Context) (bool, error) { + userId, err := getters.GetUserIdFromContext(c) + + if err != nil { + return false, err + } + user, err := db_repo.New(db_connection.Dbx). GetUserById(context.Background(), userId) diff --git a/enshi_back/middleware/checkRole/isOwner.go b/enshi_back/middleware/checkRole/isOwnerOfThePost.go similarity index 51% rename from enshi_back/middleware/checkRole/isOwner.go rename to enshi_back/middleware/checkRole/isOwnerOfThePost.go index 5f046e9..58cc5b5 100644 --- a/enshi_back/middleware/checkRole/isOwner.go +++ b/enshi_back/middleware/checkRole/isOwnerOfThePost.go @@ -4,9 +4,24 @@ import ( "context" db_repo "enshi/db/go_queries" "enshi/db_connection" + "enshi/middleware/getters" + + "github.com/gin-gonic/gin" ) -func IsOwnerOfThePost(userId int64, postId int64) (bool, error) { +func IsOwnerOfThePost(c *gin.Context) (bool, error) { + postId, err := getters.GetInt64Param(c, "post-id") + + if err != nil { + return false, err + } + + userId, err := getters.GetUserIdFromContext(c) + + if err != nil { + return false, err + } + post, err := db_repo.New(db_connection.Dbx). GetPostsByPostId(context.Background(), postId) diff --git a/enshi_back/middleware/getters/getIntParam.go b/enshi_back/middleware/getters/getIntParam.go new file mode 100644 index 0000000..f5ddd57 --- /dev/null +++ b/enshi_back/middleware/getters/getIntParam.go @@ -0,0 +1,17 @@ +package getters + +import ( + "strconv" + + "github.com/gin-gonic/gin" +) + +func GetInt64Param(c *gin.Context, paramName string) (int64, error) { + int64ParamValue, err := strconv.ParseInt(c.Param(paramName), 10, 64) + + if err != nil { + return -1, err + } + + return int64ParamValue, nil +} diff --git a/enshi_back/middleware/postsMiddleware.go b/enshi_back/middleware/postsMiddleware.go new file mode 100644 index 0000000..fc2ee21 --- /dev/null +++ b/enshi_back/middleware/postsMiddleware.go @@ -0,0 +1,41 @@ +package middleware + +import ( + postspolicies "enshi/ABAC/postsPolicies" + rest_api_stuff "enshi/REST_API_stuff" + "fmt" + + "github.com/gin-gonic/gin" +) + +func PostsMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + + switch c.Request.Method { + case "DELETE": + c.Set("target", postspolicies.DELETE_POST) + case "PUT": + c.Set("target", postspolicies.UPDATE_POST) + case "POST": + c.Set("target", postspolicies.CREATE_POST) + case "GET": + c.Set("target", postspolicies.GET_POST) + } + + isAllowed, err := postspolicies.PostsPolicies(c) + + if err != nil { + rest_api_stuff.InternalErrorAnswer(c, err) + c.Abort() + return + } + + if !isAllowed { + rest_api_stuff.UnauthorizedAnswer(c, fmt.Errorf("you have no permission")) + c.Abort() + return + } + + c.Next() + } +} diff --git a/enshi_back/routes/changeUserProfile.go b/enshi_back/routes/changeUserProfile.go deleted file mode 100644 index f4fd3e5..0000000 --- a/enshi_back/routes/changeUserProfile.go +++ /dev/null @@ -1,17 +0,0 @@ -package routes - -import ( - rest_api_stuff "enshi/REST_API_stuff" - db_repo "enshi/db/go_queries" - - "github.com/gin-gonic/gin" -) - -func ChangeUserProfile(c *gin.Context) { - var userProfileParams db_repo.UpdateProfileByUserIdParams - - if err := c.BindJSON(&userProfileParams); err != nil { - rest_api_stuff.BadRequestAnswer(c, err) - } - -} diff --git a/enshi_back/routes/postsRoutes/deletePost.go b/enshi_back/routes/postsRoutes/deletePost.go index 52c6e1b..b0e3388 100644 --- a/enshi_back/routes/postsRoutes/deletePost.go +++ b/enshi_back/routes/postsRoutes/deletePost.go @@ -7,16 +7,15 @@ import ( "enshi/db_connection" "enshi/middleware/getters" "fmt" + "strconv" "github.com/gin-gonic/gin" ) func DeletePost(c *gin.Context) { - var deletePostId struct { - PostId int64 `json:"post_id"` - } + postId, err := strconv.ParseInt(c.Param("post-id"), 10, 64) - if err := c.BindJSON(&deletePostId); err != nil { + if err != nil { rest_api_stuff.BadRequestAnswer(c, err) return } @@ -28,7 +27,7 @@ func DeletePost(c *gin.Context) { } query := db_repo.New(db_connection.Dbx) - post, err := query.GetPostsByPostId(context.Background(), deletePostId.PostId) + post, err := query.GetPostsByPostId(context.Background(), postId) if err != nil { rest_api_stuff.InternalErrorAnswer(c, err) return @@ -41,7 +40,7 @@ func DeletePost(c *gin.Context) { // TODO: Add block of code, so admin could delete anything - err = query.DeletePostByPostId(context.Background(), deletePostId.PostId) + err = query.DeletePostByPostId(context.Background(), postId) if err != nil { rest_api_stuff.InternalErrorAnswer(c, err) return diff --git a/enshi_back/routes/postsRoutes/getPost.go b/enshi_back/routes/postsRoutes/getPost.go index 2f9e8b2..f05cc2c 100644 --- a/enshi_back/routes/postsRoutes/getPost.go +++ b/enshi_back/routes/postsRoutes/getPost.go @@ -5,24 +5,23 @@ import ( 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 GetPost(c *gin.Context) { - var postParams struct { - PostId int64 `json:"post_id"` - } + postId, err := getters.GetInt64Param(c, "post-id") - if err := c.BindJSON(&postParams); err != nil { + if err != nil { rest_api_stuff.BadRequestAnswer(c, err) return } postData, err := db_repo.New(db_connection.Dbx). - GetPostsByPostId(context.Background(), postParams.PostId) + GetPostsByPostId(context.Background(), postId) if err != nil { rest_api_stuff.InternalErrorAnswer(c, err) diff --git a/enshi_back/routes/postsRoutes/updatePost.go b/enshi_back/routes/postsRoutes/updatePost.go index d1cfd45..8568897 100644 --- a/enshi_back/routes/postsRoutes/updatePost.go +++ b/enshi_back/routes/postsRoutes/updatePost.go @@ -5,9 +5,7 @@ import ( rest_api_stuff "enshi/REST_API_stuff" db_repo "enshi/db/go_queries" "enshi/db_connection" - "enshi/middleware/checkRole" "enshi/middleware/getters" - "fmt" "github.com/gin-gonic/gin" ) @@ -20,20 +18,20 @@ func UpdatePost(c *gin.Context) { return } - userId, err := getters.GetUserIdFromContext(c) + _, err := getters.GetUserIdFromContext(c) if err != nil { rest_api_stuff.InternalErrorAnswer(c, err) return } - if isOwner, _ := checkRole.IsOwnerOfThePost( - userId, - UpdatedPostParams.PostID, - ); !isOwner { - rest_api_stuff.UnauthorizedAnswer(c, fmt.Errorf("you are now allowed to change this")) - return - } + // if isOwner, _ := checkRole.IsOwnerOfThePost( + // userId, + // UpdatedPostParams.PostID, + // ); !isOwner { + // rest_api_stuff.UnauthorizedAnswer(c, fmt.Errorf("you are now allowed to change this")) + // return + // } _, err = db_repo.New( db_connection.Dbx, diff --git a/enshi_back/utils/routesSetup.go b/enshi_back/utils/routesSetup.go index 832273e..3cc9f15 100644 --- a/enshi_back/utils/routesSetup.go +++ b/enshi_back/utils/routesSetup.go @@ -2,7 +2,6 @@ package utils import ( "enshi/middleware" - "enshi/routes" "enshi/routes/authRoutes" "enshi/routes/postsRoutes" "enshi/routes/userProfileRoutes" @@ -29,20 +28,40 @@ func SetupRotes(g *gin.Engine) error { freeGroup.GET("getCookie", testCookie) - freeGroup.POST("login", authRoutes.Login) - freeGroup.POST("registerUser", authRoutes.RegisterUser) - freeGroup.GET("getPost", postsRoutes.GetPost) + freeGroup.POST( + "login", + authRoutes.Login, + ) + freeGroup.POST( + "users", + authRoutes.RegisterUser, + ) + freeGroup.GET( + "posts/:post-id", + postsRoutes.GetPost, + ) // Auth group routes authGroup := g.Group("/") authGroup.Use(middleware.AuthMiddleware()) - authGroup.POST("updatePost", postsRoutes.UpdatePost) - authGroup.POST("createPost", postsRoutes.CreatePost) - authGroup.POST("changeUserProfile", routes.ChangeUserProfile) - authGroup.POST("updateProfile", userProfileRoutes.UpdateUserProfile) + authGroup.PUT( + "posts/:post-id", + postsRoutes.UpdatePost, + ) + authGroup.POST( + "posts", + postsRoutes.CreatePost, + ) + authGroup.DELETE( + "posts/:post-id", + postsRoutes.DeletePost, + ) - authGroup.DELETE("deletePost", postsRoutes.DeletePost) + authGroup.PUT( + "user-profiles", + userProfileRoutes.UpdateUserProfile, + ) // Admin group routes adminGroup := authGroup.Group("/admin/") From fac1b30bf44e429f214cf76a3f461b5ff2b3c2db Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 16 Nov 2024 10:25:40 +0300 Subject: [PATCH 04/30] Tested ABAC --- enshi_back/ABAC/globalRules/authorizedRule.go | 8 +++++++- .../ABAC/postsPolicies/postRules/deleteRule.go | 9 +++++++++ .../ABAC/postsPolicies/postRules/updateRule.go | 9 +++++++++ enshi_back/utils/routesSetup.go | 17 ++++++++++------- 4 files changed, 35 insertions(+), 8 deletions(-) diff --git a/enshi_back/ABAC/globalRules/authorizedRule.go b/enshi_back/ABAC/globalRules/authorizedRule.go index 0aa714a..9d48ab0 100644 --- a/enshi_back/ABAC/globalRules/authorizedRule.go +++ b/enshi_back/ABAC/globalRules/authorizedRule.go @@ -3,13 +3,19 @@ package globalrules import ( "enshi/auth" "enshi/global" + "fmt" "net/http" "github.com/gin-gonic/gin" ) func AuthorizedRule(c *gin.Context) (bool, error) { - tokenFromCookies := c.Request.CookiesNamed("auth_cookie")[0].Value + cookies := c.Request.CookiesNamed("auth_cookie") + if len(cookies) == 0 { + return false, fmt.Errorf("no cookies provided") + } + + tokenFromCookies := cookies[0].Value cookieClimes, err := auth.ValidateToken(tokenFromCookies) if err != nil { c.IndentedJSON(http.StatusUnauthorized, gin.H{"error auth": err.Error()}) diff --git a/enshi_back/ABAC/postsPolicies/postRules/deleteRule.go b/enshi_back/ABAC/postsPolicies/postRules/deleteRule.go index 4e2cfec..0bfa92b 100644 --- a/enshi_back/ABAC/postsPolicies/postRules/deleteRule.go +++ b/enshi_back/ABAC/postsPolicies/postRules/deleteRule.go @@ -1,6 +1,7 @@ package postRules import ( + globalrules "enshi/ABAC/globalRules" "enshi/middleware/checkRole" "github.com/gin-gonic/gin" @@ -8,6 +9,14 @@ import ( // Only owner or admin can delete post func DeleteRule(c *gin.Context) (bool, error) { + // Sender should be authorized + isAuthorized, err := globalrules.AuthorizedRule(c) + if err != nil { + return false, err + } else if !isAuthorized { + return false, nil + } + isOwner, err := checkRole.IsOwnerOfThePost(c) if err != nil { return false, err diff --git a/enshi_back/ABAC/postsPolicies/postRules/updateRule.go b/enshi_back/ABAC/postsPolicies/postRules/updateRule.go index 7a1200c..959fd55 100644 --- a/enshi_back/ABAC/postsPolicies/postRules/updateRule.go +++ b/enshi_back/ABAC/postsPolicies/postRules/updateRule.go @@ -1,6 +1,7 @@ package postRules import ( + globalrules "enshi/ABAC/globalRules" "enshi/middleware/checkRole" "github.com/gin-gonic/gin" @@ -8,6 +9,14 @@ import ( // Only owner of the post can change it func PostUpdateRule(c *gin.Context) (bool, error) { + // Sender should be authorized + isAuthorized, err := globalrules.AuthorizedRule(c) + if err != nil { + return false, err + } else if !isAuthorized { + return false, nil + } + isOwner, err := checkRole.IsOwnerOfThePost(c) if err != nil { return false, err diff --git a/enshi_back/utils/routesSetup.go b/enshi_back/utils/routesSetup.go index 3cc9f15..9ef6d4f 100644 --- a/enshi_back/utils/routesSetup.go +++ b/enshi_back/utils/routesSetup.go @@ -36,24 +36,27 @@ func SetupRotes(g *gin.Engine) error { "users", authRoutes.RegisterUser, ) - freeGroup.GET( - "posts/:post-id", - postsRoutes.GetPost, - ) // Auth group routes authGroup := g.Group("/") authGroup.Use(middleware.AuthMiddleware()) - authGroup.PUT( + postsGroup := g.Group("/") + postsGroup.Use(middleware.PostsMiddleware()) + + postsGroup.GET( + "posts/:post-id", + postsRoutes.GetPost, + ) + postsGroup.PUT( "posts/:post-id", postsRoutes.UpdatePost, ) - authGroup.POST( + postsGroup.POST( "posts", postsRoutes.CreatePost, ) - authGroup.DELETE( + postsGroup.DELETE( "posts/:post-id", postsRoutes.DeletePost, ) From 4c129e776c2b9094040d72143b8befc1db81220e Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 16 Nov 2024 12:51:17 +0300 Subject: [PATCH 05/30] Some improvements for ABAC --- enshi_back/ABAC/blogsPolicies/blogPolicies.go | 1 + .../blogsPolicies/blogRules/createRule.go | 22 ++++++ .../{authorizedRule.go => AuthorizedRule.go} | 6 +- enshi_back/ABAC/globalRules/IsAdminRule.go | 33 +++++++++ .../ABAC/globalRules/IsOwnerOfTheBlogRule.go | 39 ++++++++++ .../ABAC/globalRules/IsOwnerOfThePostRule.go | 39 ++++++++++ enshi_back/ABAC/postsPolicies/postPolicy.go | 24 ++----- .../postsPolicies/postRules/createRule.go | 17 ++++- .../postsPolicies/postRules/deleteRule.go | 32 ++++----- .../ABAC/postsPolicies/postRules/readRule.go | 2 +- .../postsPolicies/postRules/updateRule.go | 24 +++---- enshi_back/ABAC/rules/CheckRule.go | 72 +++++++++++++++++++ enshi_back/db/go_queries/blogs_queries.sql.go | 20 ++++++ enshi_back/db/queries/blogs_queries.sql | 5 ++ enshi_back/main.go | 4 +- enshi_back/middleware/postsMiddleware.go | 12 +++- enshi_back/routes/postsRoutes/updatePost.go | 10 +-- enshi_back/{utils => routes}/routesSetup.go | 9 ++- 18 files changed, 299 insertions(+), 72 deletions(-) create mode 100644 enshi_back/ABAC/blogsPolicies/blogPolicies.go create mode 100644 enshi_back/ABAC/blogsPolicies/blogRules/createRule.go rename enshi_back/ABAC/globalRules/{authorizedRule.go => AuthorizedRule.go} (78%) create mode 100644 enshi_back/ABAC/globalRules/IsAdminRule.go create mode 100644 enshi_back/ABAC/globalRules/IsOwnerOfTheBlogRule.go create mode 100644 enshi_back/ABAC/globalRules/IsOwnerOfThePostRule.go create mode 100644 enshi_back/ABAC/rules/CheckRule.go rename enshi_back/{utils => routes}/routesSetup.go (98%) diff --git a/enshi_back/ABAC/blogsPolicies/blogPolicies.go b/enshi_back/ABAC/blogsPolicies/blogPolicies.go new file mode 100644 index 0000000..d1e3971 --- /dev/null +++ b/enshi_back/ABAC/blogsPolicies/blogPolicies.go @@ -0,0 +1 @@ +package blogspolicies diff --git a/enshi_back/ABAC/blogsPolicies/blogRules/createRule.go b/enshi_back/ABAC/blogsPolicies/blogRules/createRule.go new file mode 100644 index 0000000..8c25f3c --- /dev/null +++ b/enshi_back/ABAC/blogsPolicies/blogRules/createRule.go @@ -0,0 +1,22 @@ +package blogrules + +import ( + globalrules "enshi/ABAC/globalRules" + "enshi/ABAC/rules" + + "github.com/gin-gonic/gin" +) + +func BlogCreateRule(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/globalRules/authorizedRule.go b/enshi_back/ABAC/globalRules/AuthorizedRule.go similarity index 78% rename from enshi_back/ABAC/globalRules/authorizedRule.go rename to enshi_back/ABAC/globalRules/AuthorizedRule.go index 9d48ab0..63fe327 100644 --- a/enshi_back/ABAC/globalRules/authorizedRule.go +++ b/enshi_back/ABAC/globalRules/AuthorizedRule.go @@ -9,10 +9,10 @@ import ( "github.com/gin-gonic/gin" ) -func AuthorizedRule(c *gin.Context) (bool, error) { +func AuthorizedRule(c *gin.Context) (bool, []error) { cookies := c.Request.CookiesNamed("auth_cookie") if len(cookies) == 0 { - return false, fmt.Errorf("no cookies provided") + return false, []error{fmt.Errorf("no cookies provided")} } tokenFromCookies := cookies[0].Value @@ -20,7 +20,7 @@ func AuthorizedRule(c *gin.Context) (bool, error) { if err != nil { c.IndentedJSON(http.StatusUnauthorized, gin.H{"error auth": err.Error()}) c.Abort() - return false, err + return false, []error{err} } else { c.Set(global.ContextUserId, cookieClimes["id"]) c.Set(global.ContextTokenData, cookieClimes) diff --git a/enshi_back/ABAC/globalRules/IsAdminRule.go b/enshi_back/ABAC/globalRules/IsAdminRule.go new file mode 100644 index 0000000..d1596e5 --- /dev/null +++ b/enshi_back/ABAC/globalRules/IsAdminRule.go @@ -0,0 +1,33 @@ +package globalrules + +import ( + "context" + db_repo "enshi/db/go_queries" + "enshi/db_connection" + "enshi/middleware/getters" + "fmt" + + "github.com/gin-gonic/gin" +) + +func IsAdminRule(c *gin.Context) (bool, []error) { + contextUserId, err := getters.GetUserIdFromContext(c) + + if err != nil { + return false, []error{err} + } + + user, err := + db_repo.New(db_connection.Dbx). + GetUserById(context.Background(), contextUserId) + + if err != nil || user.UserID == 0 { + return false, []error{err} + } + + if !user.IsAdmin { + return false, []error{fmt.Errorf("not admin")} + } + + return true, nil +} diff --git a/enshi_back/ABAC/globalRules/IsOwnerOfTheBlogRule.go b/enshi_back/ABAC/globalRules/IsOwnerOfTheBlogRule.go new file mode 100644 index 0000000..e67ca75 --- /dev/null +++ b/enshi_back/ABAC/globalRules/IsOwnerOfTheBlogRule.go @@ -0,0 +1,39 @@ +package globalrules + +import ( + "context" + db_repo "enshi/db/go_queries" + "enshi/db_connection" + "enshi/middleware/getters" + "fmt" + + "github.com/gin-gonic/gin" +) + +func IsOwnerOfTheBlogRule(c *gin.Context) (bool, []error) { + blogId, err := getters.GetInt64Param(c, "blog-id") + + if err != nil { + return false, []error{err} + } + + contextUserId, err := getters.GetUserIdFromContext(c) + + if err != nil { + return false, []error{err} + } + + blog, err := + db_repo.New(db_connection.Dbx). + GetBlogByBlogId(context.Background(), blogId) + + if err != nil { + return false, []error{err} + } + + if blog.UserID != contextUserId { + return false, []error{fmt.Errorf("now owner of the blog")} + } + + return true, nil +} diff --git a/enshi_back/ABAC/globalRules/IsOwnerOfThePostRule.go b/enshi_back/ABAC/globalRules/IsOwnerOfThePostRule.go new file mode 100644 index 0000000..af25324 --- /dev/null +++ b/enshi_back/ABAC/globalRules/IsOwnerOfThePostRule.go @@ -0,0 +1,39 @@ +package globalrules + +import ( + "context" + db_repo "enshi/db/go_queries" + "enshi/db_connection" + "enshi/middleware/getters" + "fmt" + + "github.com/gin-gonic/gin" +) + +func IsOwnerOfThePostRule(c *gin.Context) (bool, []error) { + postId, err := getters.GetInt64Param(c, "post-id") + + if err != nil { + return false, []error{err} + } + + contextUserId, err := getters.GetUserIdFromContext(c) + + if err != nil { + return false, []error{err} + } + + post, err := + db_repo.New(db_connection.Dbx). + GetPostsByPostId(context.Background(), postId) + + if err != nil { + return false, []error{err} + } + + if post.UserID != contextUserId { + return false, []error{fmt.Errorf("now owner of the post")} + } + + return true, nil +} diff --git a/enshi_back/ABAC/postsPolicies/postPolicy.go b/enshi_back/ABAC/postsPolicies/postPolicy.go index 7d16b81..d4bb86d 100644 --- a/enshi_back/ABAC/postsPolicies/postPolicy.go +++ b/enshi_back/ABAC/postsPolicies/postPolicy.go @@ -2,6 +2,7 @@ package postspolicies import ( postRules "enshi/ABAC/postsPolicies/postRules" + "enshi/ABAC/rules" "github.com/gin-gonic/gin" ) @@ -13,36 +14,25 @@ const ( GET_POST = "get_post" ) -func checkRule( - c *gin.Context, - ruleChecker func(*gin.Context) (bool, error), -) (bool, error) { - IsAllowed, err := ruleChecker(c) - if err != nil { - return false, err - } - - return IsAllowed, nil -} - -func PostsPolicies(c *gin.Context) (bool, error) { +func PostsPolicies(c *gin.Context) (bool, []error) { target, exists := c.Get("target") if !exists { return false, nil } + // Permit if one permit switch target { case DELETE_POST: - return checkRule(c, postRules.DeleteRule) + return rules.CheckRule(c, postRules.DeleteRule) case UPDATE_POST: - return checkRule(c, postRules.PostUpdateRule) + return rules.CheckRule(c, postRules.PostUpdateRule) case GET_POST: - return checkRule(c, postRules.PostReadRule) + return rules.CheckRule(c, postRules.PostReadRule) case CREATE_POST: - return checkRule(c, postRules.PostCreateRule) + return rules.CheckRule(c, postRules.PostCreateRule) } diff --git a/enshi_back/ABAC/postsPolicies/postRules/createRule.go b/enshi_back/ABAC/postsPolicies/postRules/createRule.go index 81026fd..1745c19 100644 --- a/enshi_back/ABAC/postsPolicies/postRules/createRule.go +++ b/enshi_back/ABAC/postsPolicies/postRules/createRule.go @@ -1,10 +1,23 @@ package postRules import ( + globalrules "enshi/ABAC/globalRules" + "enshi/ABAC/rules" + "github.com/gin-gonic/gin" ) // Only owner of the post can change it -func PostCreateRule(c *gin.Context) (bool, error) { - return true, nil +func PostCreateRule(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/postsPolicies/postRules/deleteRule.go b/enshi_back/ABAC/postsPolicies/postRules/deleteRule.go index 0bfa92b..fb3aecc 100644 --- a/enshi_back/ABAC/postsPolicies/postRules/deleteRule.go +++ b/enshi_back/ABAC/postsPolicies/postRules/deleteRule.go @@ -2,30 +2,26 @@ package postRules import ( globalrules "enshi/ABAC/globalRules" - "enshi/middleware/checkRole" + "enshi/ABAC/rules" "github.com/gin-gonic/gin" ) +const RULES_NUMBER_TO_COMPLETE = 2 + // Only owner or admin can delete post -func DeleteRule(c *gin.Context) (bool, error) { - // Sender should be authorized - isAuthorized, err := globalrules.AuthorizedRule(c) - if err != nil { - return false, err - } else if !isAuthorized { - return false, nil +func DeleteRule(c *gin.Context) (bool, []error) { + rulesToCheck := []rules.RuleFunction{ + globalrules.AuthorizedRule, + globalrules.IsOwnerOfThePostRule, + globalrules.IsAdminRule, } - isOwner, err := checkRole.IsOwnerOfThePost(c) - if err != nil { - return false, err - } + isAllowed, errors := rules.CheckRules( + c, + rulesToCheck, + RULES_NUMBER_TO_COMPLETE, + ) - isAdmin, err := checkRole.IsAdmin(c) - if err != nil { - return false, err - } - - return isAdmin || isOwner, err + return isAllowed, errors } diff --git a/enshi_back/ABAC/postsPolicies/postRules/readRule.go b/enshi_back/ABAC/postsPolicies/postRules/readRule.go index df83bc5..922b45e 100644 --- a/enshi_back/ABAC/postsPolicies/postRules/readRule.go +++ b/enshi_back/ABAC/postsPolicies/postRules/readRule.go @@ -5,6 +5,6 @@ import ( ) // Only owner of the post can change it -func PostReadRule(c *gin.Context) (bool, error) { +func PostReadRule(c *gin.Context) (bool, []error) { return true, nil } diff --git a/enshi_back/ABAC/postsPolicies/postRules/updateRule.go b/enshi_back/ABAC/postsPolicies/postRules/updateRule.go index 959fd55..eabb5b9 100644 --- a/enshi_back/ABAC/postsPolicies/postRules/updateRule.go +++ b/enshi_back/ABAC/postsPolicies/postRules/updateRule.go @@ -2,25 +2,23 @@ package postRules import ( globalrules "enshi/ABAC/globalRules" - "enshi/middleware/checkRole" + "enshi/ABAC/rules" "github.com/gin-gonic/gin" ) // Only owner of the post can change it -func PostUpdateRule(c *gin.Context) (bool, error) { - // Sender should be authorized - isAuthorized, err := globalrules.AuthorizedRule(c) - if err != nil { - return false, err - } else if !isAuthorized { - return false, nil +func PostUpdateRule(c *gin.Context) (bool, []error) { + rulesToCheck := []rules.RuleFunction{ + globalrules.AuthorizedRule, + globalrules.IsOwnerOfThePostRule, } - isOwner, err := checkRole.IsOwnerOfThePost(c) - if err != nil { - return false, err - } + isAllowed, errors := rules.CheckRules( + c, + rulesToCheck, + rules.ALL_RULES_MUST_BE_COMPLETED, + ) - return isOwner, nil + return isAllowed, errors } diff --git a/enshi_back/ABAC/rules/CheckRule.go b/enshi_back/ABAC/rules/CheckRule.go new file mode 100644 index 0000000..6147efc --- /dev/null +++ b/enshi_back/ABAC/rules/CheckRule.go @@ -0,0 +1,72 @@ +package rules + +import ( + "fmt" + "strconv" + + "github.com/gin-gonic/gin" +) + +type RuleFunction func(*gin.Context) (bool, []error) + +const ( + ALL_RULES_MUST_BE_COMPLETED = iota +) + +func CheckRule( + c *gin.Context, + ruleChecker RuleFunction, +) (bool, []error) { + IsAllowed, err := ruleChecker(c) + if err != nil { + return false, err + } + + return IsAllowed, nil +} + +func CheckRules( + c *gin.Context, + rules []RuleFunction, + completedRulesCount int, +) (bool, []error) { + var allowancesIndexes []int + var errors []error + + if len(rules) < completedRulesCount { + return false, []error{fmt.Errorf("there is less rules, that should be completed")} + } + + for i, rule := range rules { + if isAllowed, err := CheckRule(c, rule); err != nil { + errors = append( + errors, + err..., + ) + } else if !isAllowed { + errors = append( + errors, + fmt.Errorf("rule "+ + strconv.Itoa(i)+ + " was rejected"), + ) + } else { + allowancesIndexes = append(allowancesIndexes, i) + } + } + + switch completedRulesCount { + case ALL_RULES_MUST_BE_COMPLETED: + if len(allowancesIndexes) == len(rules) { + return true, nil + } else { + return false, errors + } + default: + if len(allowancesIndexes) >= completedRulesCount { + return true, nil + } else { + return false, errors + } + } +} diff --git a/enshi_back/db/go_queries/blogs_queries.sql.go b/enshi_back/db/go_queries/blogs_queries.sql.go index ecf6c80..fc040cf 100644 --- a/enshi_back/db/go_queries/blogs_queries.sql.go +++ b/enshi_back/db/go_queries/blogs_queries.sql.go @@ -56,6 +56,26 @@ func (q *Queries) DeleteBlogByBlogId(ctx context.Context, blogID int64) error { return err } +const getBlogByBlogId = `-- name: GetBlogByBlogId :one +SELECT blog_id, user_id, title, description, category_id, created_at +FROM public.blogs +WHERE blog_id = $1 +` + +func (q *Queries) GetBlogByBlogId(ctx context.Context, blogID int64) (Blog, error) { + row := q.db.QueryRow(ctx, getBlogByBlogId, blogID) + var i Blog + err := row.Scan( + &i.BlogID, + &i.UserID, + &i.Title, + &i.Description, + &i.CategoryID, + &i.CreatedAt, + ) + return i, err +} + const getBlogsByUserId = `-- name: GetBlogsByUserId :many SELECT blog_id, user_id, title, description, category_id, created_at FROM public.blogs diff --git a/enshi_back/db/queries/blogs_queries.sql b/enshi_back/db/queries/blogs_queries.sql index e47614b..cb5c575 100644 --- a/enshi_back/db/queries/blogs_queries.sql +++ b/enshi_back/db/queries/blogs_queries.sql @@ -15,6 +15,11 @@ SELECT * FROM public.blogs WHERE user_id = $1; +-- name: GetBlogByBlogId :one +SELECT * +FROM public.blogs +WHERE blog_id = $1; + -- name: DeleteBlogByBlogId :exec DELETE FROM public.blogs WHERE blog_id=$1; \ No newline at end of file diff --git a/enshi_back/main.go b/enshi_back/main.go index 85fe413..3b8374b 100644 --- a/enshi_back/main.go +++ b/enshi_back/main.go @@ -6,7 +6,7 @@ import ( "enshi/db_connection" "enshi/env" "enshi/global" - utils "enshi/utils" + "enshi/routes" "fmt" "github.com/gin-gonic/gin" @@ -27,7 +27,7 @@ func main() { defer db_connection.Dbx_connection.Close(context.Background()) router := gin.Default() - if err := utils.SetupRotes(router); err != nil { + if err := routes.SetupRotes(router); err != nil { fmt.Println(err.Error()) return } diff --git a/enshi_back/middleware/postsMiddleware.go b/enshi_back/middleware/postsMiddleware.go index fc2ee21..9f3c911 100644 --- a/enshi_back/middleware/postsMiddleware.go +++ b/enshi_back/middleware/postsMiddleware.go @@ -4,6 +4,7 @@ import ( postspolicies "enshi/ABAC/postsPolicies" rest_api_stuff "enshi/REST_API_stuff" "fmt" + "net/http" "github.com/gin-gonic/gin" ) @@ -22,10 +23,15 @@ func PostsMiddleware() gin.HandlerFunc { c.Set("target", postspolicies.GET_POST) } - isAllowed, err := postspolicies.PostsPolicies(c) + isAllowed, errors := postspolicies.PostsPolicies(c) - if err != nil { - rest_api_stuff.InternalErrorAnswer(c, err) + var errorsMap = map[int]string{} + for i, error := range errors { + errorsMap[i] = error.Error() + } + + if errors != nil { + c.IndentedJSON(http.StatusUnauthorized, errorsMap) c.Abort() return } diff --git a/enshi_back/routes/postsRoutes/updatePost.go b/enshi_back/routes/postsRoutes/updatePost.go index 8568897..bb26330 100644 --- a/enshi_back/routes/postsRoutes/updatePost.go +++ b/enshi_back/routes/postsRoutes/updatePost.go @@ -18,20 +18,14 @@ func UpdatePost(c *gin.Context) { return } - _, err := getters.GetUserIdFromContext(c) + postId, err := getters.GetInt64Param(c, "post-id") if err != nil { rest_api_stuff.InternalErrorAnswer(c, err) return } - // if isOwner, _ := checkRole.IsOwnerOfThePost( - // userId, - // UpdatedPostParams.PostID, - // ); !isOwner { - // rest_api_stuff.UnauthorizedAnswer(c, fmt.Errorf("you are now allowed to change this")) - // return - // } + UpdatedPostParams.PostID = postId _, err = db_repo.New( db_connection.Dbx, diff --git a/enshi_back/utils/routesSetup.go b/enshi_back/routes/routesSetup.go similarity index 98% rename from enshi_back/utils/routesSetup.go rename to enshi_back/routes/routesSetup.go index 9ef6d4f..f2c9f4c 100644 --- a/enshi_back/utils/routesSetup.go +++ b/enshi_back/routes/routesSetup.go @@ -1,4 +1,4 @@ -package utils +package routes import ( "enshi/middleware" @@ -37,10 +37,6 @@ func SetupRotes(g *gin.Engine) error { authRoutes.RegisterUser, ) - // Auth group routes - authGroup := g.Group("/") - authGroup.Use(middleware.AuthMiddleware()) - postsGroup := g.Group("/") postsGroup.Use(middleware.PostsMiddleware()) @@ -61,6 +57,9 @@ func SetupRotes(g *gin.Engine) error { postsRoutes.DeletePost, ) + // Auth group routes + authGroup := g.Group("/") + authGroup.Use(middleware.AuthMiddleware()) authGroup.PUT( "user-profiles", userProfileRoutes.UpdateUserProfile, From b454f7a50f5943119a31ebcf279623a22f60ee90 Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 16 Nov 2024 13:21:55 +0300 Subject: [PATCH 06/30] Some improvements for ABAC --- enshi_back/ABAC/blogsPolicies/blogPolicies.go | 39 +++++++++++++++ .../blogsPolicies/blogRules/deleteRule.go | 24 ++++++++++ .../ABAC/blogsPolicies/blogRules/readRule.go | 19 ++++++++ .../blogsPolicies/blogRules/updateRule.go | 23 +++++++++ enshi_back/middleware/blogsMiddleware.go | 47 +++++++++++++++++++ 5 files changed, 152 insertions(+) create mode 100644 enshi_back/ABAC/blogsPolicies/blogRules/deleteRule.go create mode 100644 enshi_back/ABAC/blogsPolicies/blogRules/readRule.go create mode 100644 enshi_back/ABAC/blogsPolicies/blogRules/updateRule.go create mode 100644 enshi_back/middleware/blogsMiddleware.go diff --git a/enshi_back/ABAC/blogsPolicies/blogPolicies.go b/enshi_back/ABAC/blogsPolicies/blogPolicies.go index d1e3971..e9dcfff 100644 --- a/enshi_back/ABAC/blogsPolicies/blogPolicies.go +++ b/enshi_back/ABAC/blogsPolicies/blogPolicies.go @@ -1 +1,40 @@ package blogspolicies + +import ( + blogrules "enshi/ABAC/blogsPolicies/blogRules" + "enshi/ABAC/rules" + + "github.com/gin-gonic/gin" +) + +const ( + DELETE_BLOG = "delete_blog" + UPDATE_BLOG = "update_blog" + CREATE_BLOG = "create_blog" + GET_BLOG = "get_blog" +) + +func BlogPolicies(c *gin.Context) (bool, []error) { + target, exists := c.Get("target") + if !exists { + return false, nil + } + + // Permit if one permit + switch target { + case DELETE_BLOG: + return rules.CheckRule(c, blogrules.BlogDeleteRule) + + case UPDATE_BLOG: + return rules.CheckRule(c, blogrules.BlogUpdateRule) + + case GET_BLOG: + return rules.CheckRule(c, blogrules.BlogReadRule) + + case CREATE_BLOG: + return rules.CheckRule(c, blogrules.BlogCreateRule) + + } + + return false, nil +} diff --git a/enshi_back/ABAC/blogsPolicies/blogRules/deleteRule.go b/enshi_back/ABAC/blogsPolicies/blogRules/deleteRule.go new file mode 100644 index 0000000..49b7f01 --- /dev/null +++ b/enshi_back/ABAC/blogsPolicies/blogRules/deleteRule.go @@ -0,0 +1,24 @@ +package blogrules + +import ( + globalrules "enshi/ABAC/globalRules" + "enshi/ABAC/rules" + + "github.com/gin-gonic/gin" +) + +func BlogDeleteRule(c *gin.Context) (bool, []error) { + rulesToCheck := []rules.RuleFunction{ + globalrules.AuthorizedRule, + globalrules.IsOwnerOfTheBlogRule, + globalrules.IsAdminRule, + } + + isAllowed, errors := rules.CheckRules( + c, + rulesToCheck, + 2, + ) + + return isAllowed, errors +} diff --git a/enshi_back/ABAC/blogsPolicies/blogRules/readRule.go b/enshi_back/ABAC/blogsPolicies/blogRules/readRule.go new file mode 100644 index 0000000..6658687 --- /dev/null +++ b/enshi_back/ABAC/blogsPolicies/blogRules/readRule.go @@ -0,0 +1,19 @@ +package blogrules + +import ( + "enshi/ABAC/rules" + + "github.com/gin-gonic/gin" +) + +func BlogReadRule(c *gin.Context) (bool, []error) { + rulesToCheck := []rules.RuleFunction{} + + isAllowed, errors := rules.CheckRules( + c, + rulesToCheck, + rules.ALL_RULES_MUST_BE_COMPLETED, + ) + + return isAllowed, errors +} diff --git a/enshi_back/ABAC/blogsPolicies/blogRules/updateRule.go b/enshi_back/ABAC/blogsPolicies/blogRules/updateRule.go new file mode 100644 index 0000000..8371955 --- /dev/null +++ b/enshi_back/ABAC/blogsPolicies/blogRules/updateRule.go @@ -0,0 +1,23 @@ +package blogrules + +import ( + globalrules "enshi/ABAC/globalRules" + "enshi/ABAC/rules" + + "github.com/gin-gonic/gin" +) + +func BlogUpdateRule(c *gin.Context) (bool, []error) { + rulesToCheck := []rules.RuleFunction{ + globalrules.AuthorizedRule, + globalrules.IsOwnerOfTheBlogRule, + } + + isAllowed, errors := rules.CheckRules( + c, + rulesToCheck, + rules.ALL_RULES_MUST_BE_COMPLETED, + ) + + return isAllowed, errors +} diff --git a/enshi_back/middleware/blogsMiddleware.go b/enshi_back/middleware/blogsMiddleware.go new file mode 100644 index 0000000..bed7ea1 --- /dev/null +++ b/enshi_back/middleware/blogsMiddleware.go @@ -0,0 +1,47 @@ +package middleware + +import ( + blogspolicies "enshi/ABAC/blogsPolicies" + rest_api_stuff "enshi/REST_API_stuff" + "fmt" + "net/http" + + "github.com/gin-gonic/gin" +) + +func BlogsMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + + switch c.Request.Method { + case "DELETE": + c.Set("target", blogspolicies.DELETE_BLOG) + case "PUT": + c.Set("target", blogspolicies.UPDATE_BLOG) + case "POST": + c.Set("target", blogspolicies.CREATE_BLOG) + case "GET": + c.Set("target", blogspolicies.GET_BLOG) + } + + isAllowed, errors := blogspolicies.BlogPolicies(c) + + var errorsMap = map[int]string{} + for i, error := range errors { + errorsMap[i] = error.Error() + } + + if errors != nil { + c.IndentedJSON(http.StatusUnauthorized, errorsMap) + c.Abort() + return + } + + if !isAllowed { + rest_api_stuff.UnauthorizedAnswer(c, fmt.Errorf("you have no permission")) + c.Abort() + return + } + + c.Next() + } +} From cc0caf2ab036bbb8ea253ac086b44e770013adb2 Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 16 Nov 2024 18:04:33 +0300 Subject: [PATCH 07/30] Another bunch of improvements --- .../AuthorizedRule.go | 0 .../IsAdminRule.go | 0 .../IsOwnerOfTheBlogRule.go | 0 .../IsOwnerOfThePostRule.go | 0 .../postPolicy.go | 2 +- .../postRules/createRule.go | 2 +- .../postRules/deleteRule.go | 2 +- .../postRules/readRule.go | 0 .../postRules/updateRule.go | 3 +- .../ABAC/ProfilePolicies/ProfilePolicies.go | 31 ++++++++++++ .../ProfilesRules/UpdateRule.go | 22 +++++++++ .../blogsPolicies/blogRules/createRule.go | 2 +- .../blogsPolicies/blogRules/deleteRule.go | 2 +- .../blogsPolicies/blogRules/updateRule.go | 2 +- enshi_back/db/go_queries/blogs_queries.sql.go | 4 +- enshi_back/db/go_queries/models.go | 2 +- enshi_back/db/sqlc.yml | 3 ++ enshi_back/middleware/ProfileMiddleware.go | 40 ++++++++++++++++ enshi_back/middleware/postsMiddleware.go | 2 +- enshi_back/routes/blogRoutes/createBlog.go | 48 +++++++++++++++++++ enshi_back/routes/routesSetup.go | 21 ++++++-- .../userProfileRoutes/updateUserProfile.go | 6 ++- enshi_back/utils/GetContextPayload.go | 21 ++++++++ enshi_back/utils/uuidGenerator.go | 18 +++++++ 24 files changed, 215 insertions(+), 18 deletions(-) rename enshi_back/ABAC/{globalRules => GlobalRules}/AuthorizedRule.go (100%) rename enshi_back/ABAC/{globalRules => GlobalRules}/IsAdminRule.go (100%) rename enshi_back/ABAC/{globalRules => GlobalRules}/IsOwnerOfTheBlogRule.go (100%) rename enshi_back/ABAC/{globalRules => GlobalRules}/IsOwnerOfThePostRule.go (100%) rename enshi_back/ABAC/{postsPolicies => PostsPolicies}/postPolicy.go (93%) rename enshi_back/ABAC/{postsPolicies => PostsPolicies}/postRules/createRule.go (90%) rename enshi_back/ABAC/{postsPolicies => PostsPolicies}/postRules/deleteRule.go (92%) rename enshi_back/ABAC/{postsPolicies => PostsPolicies}/postRules/readRule.go (100%) rename enshi_back/ABAC/{postsPolicies => PostsPolicies}/postRules/updateRule.go (83%) create mode 100644 enshi_back/ABAC/ProfilePolicies/ProfilePolicies.go create mode 100644 enshi_back/ABAC/ProfilePolicies/ProfilesRules/UpdateRule.go create mode 100644 enshi_back/middleware/ProfileMiddleware.go create mode 100644 enshi_back/routes/blogRoutes/createBlog.go create mode 100644 enshi_back/utils/GetContextPayload.go create mode 100644 enshi_back/utils/uuidGenerator.go diff --git a/enshi_back/ABAC/globalRules/AuthorizedRule.go b/enshi_back/ABAC/GlobalRules/AuthorizedRule.go similarity index 100% rename from enshi_back/ABAC/globalRules/AuthorizedRule.go rename to enshi_back/ABAC/GlobalRules/AuthorizedRule.go diff --git a/enshi_back/ABAC/globalRules/IsAdminRule.go b/enshi_back/ABAC/GlobalRules/IsAdminRule.go similarity index 100% rename from enshi_back/ABAC/globalRules/IsAdminRule.go rename to enshi_back/ABAC/GlobalRules/IsAdminRule.go diff --git a/enshi_back/ABAC/globalRules/IsOwnerOfTheBlogRule.go b/enshi_back/ABAC/GlobalRules/IsOwnerOfTheBlogRule.go similarity index 100% rename from enshi_back/ABAC/globalRules/IsOwnerOfTheBlogRule.go rename to enshi_back/ABAC/GlobalRules/IsOwnerOfTheBlogRule.go diff --git a/enshi_back/ABAC/globalRules/IsOwnerOfThePostRule.go b/enshi_back/ABAC/GlobalRules/IsOwnerOfThePostRule.go similarity index 100% rename from enshi_back/ABAC/globalRules/IsOwnerOfThePostRule.go rename to enshi_back/ABAC/GlobalRules/IsOwnerOfThePostRule.go diff --git a/enshi_back/ABAC/postsPolicies/postPolicy.go b/enshi_back/ABAC/PostsPolicies/postPolicy.go similarity index 93% rename from enshi_back/ABAC/postsPolicies/postPolicy.go rename to enshi_back/ABAC/PostsPolicies/postPolicy.go index d4bb86d..0d5415f 100644 --- a/enshi_back/ABAC/postsPolicies/postPolicy.go +++ b/enshi_back/ABAC/PostsPolicies/postPolicy.go @@ -1,7 +1,7 @@ package postspolicies import ( - postRules "enshi/ABAC/postsPolicies/postRules" + "enshi/ABAC/PostsPolicies/postRules" "enshi/ABAC/rules" "github.com/gin-gonic/gin" diff --git a/enshi_back/ABAC/postsPolicies/postRules/createRule.go b/enshi_back/ABAC/PostsPolicies/postRules/createRule.go similarity index 90% rename from enshi_back/ABAC/postsPolicies/postRules/createRule.go rename to enshi_back/ABAC/PostsPolicies/postRules/createRule.go index 1745c19..b998d5c 100644 --- a/enshi_back/ABAC/postsPolicies/postRules/createRule.go +++ b/enshi_back/ABAC/PostsPolicies/postRules/createRule.go @@ -1,7 +1,7 @@ package postRules import ( - globalrules "enshi/ABAC/globalRules" + globalrules "enshi/ABAC/GlobalRules" "enshi/ABAC/rules" "github.com/gin-gonic/gin" diff --git a/enshi_back/ABAC/postsPolicies/postRules/deleteRule.go b/enshi_back/ABAC/PostsPolicies/postRules/deleteRule.go similarity index 92% rename from enshi_back/ABAC/postsPolicies/postRules/deleteRule.go rename to enshi_back/ABAC/PostsPolicies/postRules/deleteRule.go index fb3aecc..1e71cb6 100644 --- a/enshi_back/ABAC/postsPolicies/postRules/deleteRule.go +++ b/enshi_back/ABAC/PostsPolicies/postRules/deleteRule.go @@ -1,7 +1,7 @@ package postRules import ( - globalrules "enshi/ABAC/globalRules" + globalrules "enshi/ABAC/GlobalRules" "enshi/ABAC/rules" "github.com/gin-gonic/gin" diff --git a/enshi_back/ABAC/postsPolicies/postRules/readRule.go b/enshi_back/ABAC/PostsPolicies/postRules/readRule.go similarity index 100% rename from enshi_back/ABAC/postsPolicies/postRules/readRule.go rename to enshi_back/ABAC/PostsPolicies/postRules/readRule.go diff --git a/enshi_back/ABAC/postsPolicies/postRules/updateRule.go b/enshi_back/ABAC/PostsPolicies/postRules/updateRule.go similarity index 83% rename from enshi_back/ABAC/postsPolicies/postRules/updateRule.go rename to enshi_back/ABAC/PostsPolicies/postRules/updateRule.go index eabb5b9..44cd7b6 100644 --- a/enshi_back/ABAC/postsPolicies/postRules/updateRule.go +++ b/enshi_back/ABAC/PostsPolicies/postRules/updateRule.go @@ -1,7 +1,7 @@ package postRules import ( - globalrules "enshi/ABAC/globalRules" + globalrules "enshi/ABAC/GlobalRules" "enshi/ABAC/rules" "github.com/gin-gonic/gin" @@ -11,7 +11,6 @@ import ( func PostUpdateRule(c *gin.Context) (bool, []error) { rulesToCheck := []rules.RuleFunction{ globalrules.AuthorizedRule, - globalrules.IsOwnerOfThePostRule, } isAllowed, errors := rules.CheckRules( diff --git a/enshi_back/ABAC/ProfilePolicies/ProfilePolicies.go b/enshi_back/ABAC/ProfilePolicies/ProfilePolicies.go new file mode 100644 index 0000000..cee74e1 --- /dev/null +++ b/enshi_back/ABAC/ProfilePolicies/ProfilePolicies.go @@ -0,0 +1,31 @@ +package profilepolicies + +import ( + profilesrules "enshi/ABAC/ProfilePolicies/ProfilesRules" + "enshi/ABAC/rules" + + "github.com/gin-gonic/gin" +) + +const ( + RESET_PROFILE = "reset_profile" + UPDATE_PROFILE = "update_profile" + CREATE_PROFILE = "create_profile" + GET_PROFILE = "get_profile" +) + +func ProfilePolicies(c *gin.Context) (bool, []error) { + target, exists := c.Get("target") + if !exists { + return false, nil + } + + // Permit if one permit + switch target { + case UPDATE_PROFILE: + return rules.CheckRule(c, profilesrules.UpdateProfileRule) + + } + + return false, nil +} diff --git a/enshi_back/ABAC/ProfilePolicies/ProfilesRules/UpdateRule.go b/enshi_back/ABAC/ProfilePolicies/ProfilesRules/UpdateRule.go new file mode 100644 index 0000000..f90f857 --- /dev/null +++ b/enshi_back/ABAC/ProfilePolicies/ProfilesRules/UpdateRule.go @@ -0,0 +1,22 @@ +package profilesrules + +import ( + globalrules "enshi/ABAC/GlobalRules" + "enshi/ABAC/rules" + + "github.com/gin-gonic/gin" +) + +func UpdateProfileRule(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/blogsPolicies/blogRules/createRule.go b/enshi_back/ABAC/blogsPolicies/blogRules/createRule.go index 8c25f3c..c973353 100644 --- a/enshi_back/ABAC/blogsPolicies/blogRules/createRule.go +++ b/enshi_back/ABAC/blogsPolicies/blogRules/createRule.go @@ -1,7 +1,7 @@ package blogrules import ( - globalrules "enshi/ABAC/globalRules" + globalrules "enshi/ABAC/GlobalRules" "enshi/ABAC/rules" "github.com/gin-gonic/gin" diff --git a/enshi_back/ABAC/blogsPolicies/blogRules/deleteRule.go b/enshi_back/ABAC/blogsPolicies/blogRules/deleteRule.go index 49b7f01..5c4f024 100644 --- a/enshi_back/ABAC/blogsPolicies/blogRules/deleteRule.go +++ b/enshi_back/ABAC/blogsPolicies/blogRules/deleteRule.go @@ -1,7 +1,7 @@ package blogrules import ( - globalrules "enshi/ABAC/globalRules" + globalrules "enshi/ABAC/GlobalRules" "enshi/ABAC/rules" "github.com/gin-gonic/gin" diff --git a/enshi_back/ABAC/blogsPolicies/blogRules/updateRule.go b/enshi_back/ABAC/blogsPolicies/blogRules/updateRule.go index 8371955..cb6ab72 100644 --- a/enshi_back/ABAC/blogsPolicies/blogRules/updateRule.go +++ b/enshi_back/ABAC/blogsPolicies/blogRules/updateRule.go @@ -1,7 +1,7 @@ package blogrules import ( - globalrules "enshi/ABAC/globalRules" + globalrules "enshi/ABAC/GlobalRules" "enshi/ABAC/rules" "github.com/gin-gonic/gin" diff --git a/enshi_back/db/go_queries/blogs_queries.sql.go b/enshi_back/db/go_queries/blogs_queries.sql.go index fc040cf..3029ce9 100644 --- a/enshi_back/db/go_queries/blogs_queries.sql.go +++ b/enshi_back/db/go_queries/blogs_queries.sql.go @@ -21,7 +21,7 @@ RETURNING blog_id, user_id, title, description, category_id, created_at type CreateBlogByUserIdParams struct { BlogID int64 `json:"blog_id"` UserID int64 `json:"user_id"` - Title pgtype.Text `json:"title"` + Title pgtype.Text `json:"title" validate:"required"` Description pgtype.Text `json:"description"` CategoryID pgtype.Int4 `json:"category_id"` } @@ -117,7 +117,7 @@ RETURNING blog_id, user_id, title, description, category_id, created_at ` type UpdateBlogInfoByBlogIdParams struct { - Title pgtype.Text `json:"title"` + Title pgtype.Text `json:"title" validate:"required"` Description pgtype.Text `json:"description"` CategoryID pgtype.Int4 `json:"category_id"` BlogID int64 `json:"blog_id"` diff --git a/enshi_back/db/go_queries/models.go b/enshi_back/db/go_queries/models.go index 7d227a9..6438133 100644 --- a/enshi_back/db/go_queries/models.go +++ b/enshi_back/db/go_queries/models.go @@ -11,7 +11,7 @@ import ( type Blog struct { BlogID int64 `json:"blog_id"` UserID int64 `json:"user_id"` - Title pgtype.Text `json:"title"` + Title pgtype.Text `json:"title" validate:"required"` Description pgtype.Text `json:"description"` CategoryID pgtype.Int4 `json:"category_id"` CreatedAt pgtype.Timestamp `json:"created_at"` diff --git a/enshi_back/db/sqlc.yml b/enshi_back/db/sqlc.yml index cdfcea3..1eb5d5f 100644 --- a/enshi_back/db/sqlc.yml +++ b/enshi_back/db/sqlc.yml @@ -18,6 +18,9 @@ sql: - column: users.email go_struct_tag: validate:"required,email" + - column: blogs.title + go_struct_tag: validate:"required" + - db_type: "uuid" go_type: import: 'github.com/google/uuid' diff --git a/enshi_back/middleware/ProfileMiddleware.go b/enshi_back/middleware/ProfileMiddleware.go new file mode 100644 index 0000000..494b09c --- /dev/null +++ b/enshi_back/middleware/ProfileMiddleware.go @@ -0,0 +1,40 @@ +package middleware + +import ( + profilepolicies "enshi/ABAC/ProfilePolicies" + rest_api_stuff "enshi/REST_API_stuff" + "fmt" + "net/http" + + "github.com/gin-gonic/gin" +) + +func ProfileMiddleware() gin.HandlerFunc { + return func(c *gin.Context) { + switch c.Request.Method { + case "PUT": + c.Set("target", profilepolicies.UPDATE_PROFILE) + } + + isAllowed, errors := profilepolicies.ProfilePolicies(c) + + var errorsMap = map[int]string{} + for i, error := range errors { + errorsMap[i] = error.Error() + } + + if errors != nil { + c.IndentedJSON(http.StatusUnauthorized, errorsMap) + c.Abort() + return + } + + if !isAllowed { + rest_api_stuff.UnauthorizedAnswer(c, fmt.Errorf("you have no permission")) + c.Abort() + return + } + + c.Next() + } +} diff --git a/enshi_back/middleware/postsMiddleware.go b/enshi_back/middleware/postsMiddleware.go index 9f3c911..2dc90fa 100644 --- a/enshi_back/middleware/postsMiddleware.go +++ b/enshi_back/middleware/postsMiddleware.go @@ -1,7 +1,7 @@ package middleware import ( - postspolicies "enshi/ABAC/postsPolicies" + postspolicies "enshi/ABAC/PostsPolicies" rest_api_stuff "enshi/REST_API_stuff" "fmt" "net/http" diff --git a/enshi_back/routes/blogRoutes/createBlog.go b/enshi_back/routes/blogRoutes/createBlog.go new file mode 100644 index 0000000..ecb1e2f --- /dev/null +++ b/enshi_back/routes/blogRoutes/createBlog.go @@ -0,0 +1,48 @@ +package blogRoutes + +import ( + "context" + rest_api_stuff "enshi/REST_API_stuff" + db_repo "enshi/db/go_queries" + "enshi/db_connection" + "enshi/middleware/getters" + "enshi/utils" + + "github.com/gin-gonic/gin" +) + +func CreateBlog(c *gin.Context) { + blogParams, err := utils.GetContextPayload[db_repo.CreateBlogByUserIdParams](c) + if err != nil { + rest_api_stuff.BadRequestAnswer(c, err) + return + } + + userId, err := getters.GetUserIdFromContext(c) + if err != nil { + rest_api_stuff.BadRequestAnswer(c, err) + return + } + + blogId, err := utils.GetUUIDv7AsInt64() + if err != nil { + rest_api_stuff.InternalErrorAnswer(c, err) + return + } + + blogParams.UserID = userId + blogParams.BlogID = blogId + + _, err = db_repo. + New(db_connection.Dbx). + CreateBlogByUserId( + context.Background(), + blogParams, + ) + if err != nil { + rest_api_stuff.InternalErrorAnswer(c, err) + return + } + + rest_api_stuff.OkAnswer(c, "blog has been created") +} diff --git a/enshi_back/routes/routesSetup.go b/enshi_back/routes/routesSetup.go index f2c9f4c..6662770 100644 --- a/enshi_back/routes/routesSetup.go +++ b/enshi_back/routes/routesSetup.go @@ -3,6 +3,7 @@ package routes import ( "enshi/middleware" "enshi/routes/authRoutes" + "enshi/routes/blogRoutes" "enshi/routes/postsRoutes" "enshi/routes/userProfileRoutes" "net/http" @@ -57,13 +58,25 @@ func SetupRotes(g *gin.Engine) error { postsRoutes.DeletePost, ) + blogGroup := g.Group("/") + blogGroup.Use(middleware.BlogsMiddleware()) + + blogGroup.POST( + "blogs", + blogRoutes.CreateBlog, + ) + + profilesGroup := g.Group("/") + profilesGroup.Use(middleware.ProfileMiddleware()) + + profilesGroup.PUT( + "profiles", + userProfileRoutes.UpdateUserProfile, + ) + // Auth group routes authGroup := g.Group("/") authGroup.Use(middleware.AuthMiddleware()) - authGroup.PUT( - "user-profiles", - userProfileRoutes.UpdateUserProfile, - ) // Admin group routes adminGroup := authGroup.Group("/admin/") diff --git a/enshi_back/routes/userProfileRoutes/updateUserProfile.go b/enshi_back/routes/userProfileRoutes/updateUserProfile.go index fe792e9..22f04e7 100644 --- a/enshi_back/routes/userProfileRoutes/updateUserProfile.go +++ b/enshi_back/routes/userProfileRoutes/updateUserProfile.go @@ -6,14 +6,16 @@ import ( db_repo "enshi/db/go_queries" "enshi/db_connection" "enshi/middleware/getters" + "enshi/utils" "github.com/gin-gonic/gin" ) func UpdateUserProfile(c *gin.Context) { - var newProfile db_repo.UpdateProfileByUserIdParams + newProfile, err := + utils.GetContextPayload[db_repo.UpdateProfileByUserIdParams](c) - if err := c.BindJSON(&newProfile); err != nil { + if err != nil { rest_api_stuff.BadRequestAnswer(c, err) return } diff --git a/enshi_back/utils/GetContextPayload.go b/enshi_back/utils/GetContextPayload.go new file mode 100644 index 0000000..934207c --- /dev/null +++ b/enshi_back/utils/GetContextPayload.go @@ -0,0 +1,21 @@ +package utils + +import ( + "github.com/gin-gonic/gin" + "github.com/go-playground/validator/v10" +) + +func GetContextPayload[T any](c *gin.Context) (T, error) { + var params T + + if err := c.BindJSON(¶ms); err != nil { + return params, err + } + + validate := validator.New(validator.WithRequiredStructEnabled()) + if err := validate.Struct(params); err != nil { + return params, err + } + + return params, nil +} diff --git a/enshi_back/utils/uuidGenerator.go b/enshi_back/utils/uuidGenerator.go new file mode 100644 index 0000000..d3f17b9 --- /dev/null +++ b/enshi_back/utils/uuidGenerator.go @@ -0,0 +1,18 @@ +package utils + +import ( + "encoding/binary" + + "github.com/google/uuid" +) + +func GetUUIDv7AsInt64() (int64, error) { + uuid, err := uuid.NewV7() + if err != nil { + return -1, err + } + + return -int64( + binary.BigEndian.Uint64(uuid[8:]), + ), nil +} From b1543c9e22290f246e6e0b6c475ff77ac0a2e28d Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 17 Nov 2024 12:58:13 +0300 Subject: [PATCH 08/30] New routes --- enshi_back/ABAC/AdminPolicies/AdminPolicy.go | 17 ++++++ enshi_back/ABAC/PostsPolicies/postPolicy.go | 12 ++-- .../postRules/updatePostBlogRule.go | 25 ++++++++ .../PostsPolicies/postRules/updateRule.go | 1 + enshi_back/ABAC/rules/ShouldAbortRequest.go | 28 +++++++++ enshi_back/db/go_queries/posts_queries.sql.go | 29 +++++++--- enshi_back/db/queries/posts_queries.sql | 12 +++- enshi_back/middleware/ProfileMiddleware.go | 18 +----- enshi_back/middleware/adminMiddleware.go | 17 ++---- enshi_back/middleware/blogsMiddleware.go | 18 +----- .../getters}/GetContextPayload.go | 2 +- enshi_back/middleware/getters/getIntParam.go | 4 +- enshi_back/middleware/postsMiddleware.go | 29 ++++------ enshi_back/routes/blogRoutes/createBlog.go | 2 +- enshi_back/routes/blogRoutes/deleteBlog.go | 36 ++++++++++++ enshi_back/routes/blogRoutes/getBlog.go | 29 ++++++++++ enshi_back/routes/blogRoutes/updateBlog.go | 46 +++++++++++++++ .../routes/postsRoutes/updatePostBlog.go | 58 +++++++++++++++++++ enshi_back/routes/routesSetup.go | 25 ++++++-- .../userProfileRoutes/updateUserProfile.go | 3 +- 20 files changed, 324 insertions(+), 87 deletions(-) create mode 100644 enshi_back/ABAC/AdminPolicies/AdminPolicy.go create mode 100644 enshi_back/ABAC/PostsPolicies/postRules/updatePostBlogRule.go create mode 100644 enshi_back/ABAC/rules/ShouldAbortRequest.go rename enshi_back/{utils => middleware/getters}/GetContextPayload.go (96%) create mode 100644 enshi_back/routes/blogRoutes/deleteBlog.go create mode 100644 enshi_back/routes/blogRoutes/getBlog.go create mode 100644 enshi_back/routes/blogRoutes/updateBlog.go create mode 100644 enshi_back/routes/postsRoutes/updatePostBlog.go diff --git a/enshi_back/ABAC/AdminPolicies/AdminPolicy.go b/enshi_back/ABAC/AdminPolicies/AdminPolicy.go new file mode 100644 index 0000000..00a91e7 --- /dev/null +++ b/enshi_back/ABAC/AdminPolicies/AdminPolicy.go @@ -0,0 +1,17 @@ +package adminpolicies + +import ( + globalrules "enshi/ABAC/GlobalRules" + "enshi/ABAC/rules" + + "github.com/gin-gonic/gin" +) + +func AdminPolicies(c *gin.Context) (bool, []error) { + rulesToCheck := []rules.RuleFunction{ + globalrules.AuthorizedRule, + globalrules.IsAdminRule, + } + + return rules.CheckRules(c, rulesToCheck, rules.ALL_RULES_MUST_BE_COMPLETED) +} diff --git a/enshi_back/ABAC/PostsPolicies/postPolicy.go b/enshi_back/ABAC/PostsPolicies/postPolicy.go index 0d5415f..5bce3dc 100644 --- a/enshi_back/ABAC/PostsPolicies/postPolicy.go +++ b/enshi_back/ABAC/PostsPolicies/postPolicy.go @@ -8,10 +8,11 @@ import ( ) const ( - DELETE_POST = "delete_post" - UPDATE_POST = "update_post" - CREATE_POST = "create_post" - GET_POST = "get_post" + DELETE_POST = "delete_post" + UPDATE_POST = "update_post" + UPDATE_POST_BLOG = "update_post_blog" + CREATE_POST = "create_post" + GET_POST = "get_post" ) func PostsPolicies(c *gin.Context) (bool, []error) { @@ -28,6 +29,9 @@ func PostsPolicies(c *gin.Context) (bool, []error) { case UPDATE_POST: return rules.CheckRule(c, postRules.PostUpdateRule) + case UPDATE_POST_BLOG: + return rules.CheckRule(c, postRules.UpdatePostBlogRule) + case GET_POST: return rules.CheckRule(c, postRules.PostReadRule) diff --git a/enshi_back/ABAC/PostsPolicies/postRules/updatePostBlogRule.go b/enshi_back/ABAC/PostsPolicies/postRules/updatePostBlogRule.go new file mode 100644 index 0000000..887c784 --- /dev/null +++ b/enshi_back/ABAC/PostsPolicies/postRules/updatePostBlogRule.go @@ -0,0 +1,25 @@ +package postRules + +import ( + globalrules "enshi/ABAC/GlobalRules" + "enshi/ABAC/rules" + + "github.com/gin-gonic/gin" +) + +// Only user that own target post and blog can do that +func UpdatePostBlogRule(c *gin.Context) (bool, []error) { + rulesToCheck := []rules.RuleFunction{ + globalrules.AuthorizedRule, + globalrules.IsOwnerOfThePostRule, + globalrules.IsOwnerOfTheBlogRule, + } + + isAllowed, errors := rules.CheckRules( + c, + rulesToCheck, + rules.ALL_RULES_MUST_BE_COMPLETED, + ) + + return isAllowed, errors +} diff --git a/enshi_back/ABAC/PostsPolicies/postRules/updateRule.go b/enshi_back/ABAC/PostsPolicies/postRules/updateRule.go index 44cd7b6..9e04db0 100644 --- a/enshi_back/ABAC/PostsPolicies/postRules/updateRule.go +++ b/enshi_back/ABAC/PostsPolicies/postRules/updateRule.go @@ -11,6 +11,7 @@ import ( func PostUpdateRule(c *gin.Context) (bool, []error) { rulesToCheck := []rules.RuleFunction{ globalrules.AuthorizedRule, + globalrules.IsOwnerOfThePostRule, } isAllowed, errors := rules.CheckRules( diff --git a/enshi_back/ABAC/rules/ShouldAbortRequest.go b/enshi_back/ABAC/rules/ShouldAbortRequest.go new file mode 100644 index 0000000..ee5b804 --- /dev/null +++ b/enshi_back/ABAC/rules/ShouldAbortRequest.go @@ -0,0 +1,28 @@ +package rules + +import ( + rest_api_stuff "enshi/REST_API_stuff" + "fmt" + "net/http" + + "github.com/gin-gonic/gin" +) + +func ShouldAbortRequest(c *gin.Context, isAllowed bool, errors []error) bool { + var errorsMap = map[int]string{} + for i, error := range errors { + errorsMap[i] = error.Error() + } + + if errors != nil { + c.IndentedJSON(http.StatusUnauthorized, errorsMap) + return true + } + + if !isAllowed { + rest_api_stuff.UnauthorizedAnswer(c, fmt.Errorf("you have no permission")) + return true + } + + return false +} diff --git a/enshi_back/db/go_queries/posts_queries.sql.go b/enshi_back/db/go_queries/posts_queries.sql.go index 2090708..5dc478f 100644 --- a/enshi_back/db/go_queries/posts_queries.sql.go +++ b/enshi_back/db/go_queries/posts_queries.sql.go @@ -146,27 +146,38 @@ func (q *Queries) GetPostsByUserId(ctx context.Context, userID int64) ([]Post, e return items, nil } +const updatePostBlogId = `-- name: UpdatePostBlogId :exec +UPDATE public.posts +SET blog_id=$2, updated_at=CURRENT_TIMESTAMP +WHERE post_id = $1 +RETURNING post_id, blog_id, user_id, title, content, created_at, updated_at +` + +type UpdatePostBlogIdParams struct { + PostID int64 `json:"post_id"` + BlogID pgtype.Int8 `json:"blog_id"` +} + +func (q *Queries) UpdatePostBlogId(ctx context.Context, arg UpdatePostBlogIdParams) error { + _, err := q.db.Exec(ctx, updatePostBlogId, arg.PostID, arg.BlogID) + return err +} + const updatePostByPostId = `-- name: UpdatePostByPostId :one UPDATE public.posts -SET blog_id=$1, title=$2, "content"=$3, updated_at=CURRENT_TIMESTAMP -WHERE post_id = $4 +SET title=$1, "content"=$2, updated_at=CURRENT_TIMESTAMP +WHERE post_id = $3 RETURNING post_id, blog_id, user_id, title, content, created_at, updated_at ` type UpdatePostByPostIdParams struct { - BlogID pgtype.Int8 `json:"blog_id"` Title pgtype.Text `json:"title"` Content pgtype.Text `json:"content"` PostID int64 `json:"post_id"` } func (q *Queries) UpdatePostByPostId(ctx context.Context, arg UpdatePostByPostIdParams) (Post, error) { - row := q.db.QueryRow(ctx, updatePostByPostId, - arg.BlogID, - arg.Title, - arg.Content, - arg.PostID, - ) + row := q.db.QueryRow(ctx, updatePostByPostId, arg.Title, arg.Content, arg.PostID) var i Post err := row.Scan( &i.PostID, diff --git a/enshi_back/db/queries/posts_queries.sql b/enshi_back/db/queries/posts_queries.sql index 718e005..39f61f3 100644 --- a/enshi_back/db/queries/posts_queries.sql +++ b/enshi_back/db/queries/posts_queries.sql @@ -21,10 +21,16 @@ RETURNING *; -- name: UpdatePostByPostId :one UPDATE public.posts -SET blog_id=$1, title=$2, "content"=$3, updated_at=CURRENT_TIMESTAMP -WHERE post_id = $4 +SET title=$1, "content"=$2, updated_at=CURRENT_TIMESTAMP +WHERE post_id = $3 RETURNING *; -- name: DeletePostByPostId :exec DELETE FROM public.posts -WHERE post_id=$1; \ No newline at end of file +WHERE post_id=$1; + +-- name: UpdatePostBlogId :exec +UPDATE public.posts +SET blog_id=$2, updated_at=CURRENT_TIMESTAMP +WHERE post_id = $1 +RETURNING *; diff --git a/enshi_back/middleware/ProfileMiddleware.go b/enshi_back/middleware/ProfileMiddleware.go index 494b09c..36877d5 100644 --- a/enshi_back/middleware/ProfileMiddleware.go +++ b/enshi_back/middleware/ProfileMiddleware.go @@ -2,9 +2,7 @@ package middleware import ( profilepolicies "enshi/ABAC/ProfilePolicies" - rest_api_stuff "enshi/REST_API_stuff" - "fmt" - "net/http" + "enshi/ABAC/rules" "github.com/gin-gonic/gin" ) @@ -18,19 +16,7 @@ func ProfileMiddleware() gin.HandlerFunc { isAllowed, errors := profilepolicies.ProfilePolicies(c) - var errorsMap = map[int]string{} - for i, error := range errors { - errorsMap[i] = error.Error() - } - - if errors != nil { - c.IndentedJSON(http.StatusUnauthorized, errorsMap) - c.Abort() - return - } - - if !isAllowed { - rest_api_stuff.UnauthorizedAnswer(c, fmt.Errorf("you have no permission")) + if rules.ShouldAbortRequest(c, isAllowed, errors) { c.Abort() return } diff --git a/enshi_back/middleware/adminMiddleware.go b/enshi_back/middleware/adminMiddleware.go index 0ddf300..51841d6 100644 --- a/enshi_back/middleware/adminMiddleware.go +++ b/enshi_back/middleware/adminMiddleware.go @@ -1,26 +1,19 @@ package middleware import ( - rest_api_stuff "enshi/REST_API_stuff" - "enshi/middleware/checkRole" - "fmt" + adminpolicies "enshi/ABAC/AdminPolicies" + "enshi/ABAC/rules" "github.com/gin-gonic/gin" ) func AdminMiddleware() gin.HandlerFunc { return func(c *gin.Context) { + isAllowed, errors := adminpolicies.AdminPolicies(c) - isAdmin, err := checkRole.IsAdmin(c) - - if err != nil { - rest_api_stuff.BadRequestAnswer(c, err) - c.Abort() - } - - if !isAdmin { - rest_api_stuff.UnauthorizedAnswer(c, fmt.Errorf("not allowed")) + if rules.ShouldAbortRequest(c, isAllowed, errors) { c.Abort() + return } c.Next() diff --git a/enshi_back/middleware/blogsMiddleware.go b/enshi_back/middleware/blogsMiddleware.go index bed7ea1..eeb58dc 100644 --- a/enshi_back/middleware/blogsMiddleware.go +++ b/enshi_back/middleware/blogsMiddleware.go @@ -2,9 +2,7 @@ package middleware import ( blogspolicies "enshi/ABAC/blogsPolicies" - rest_api_stuff "enshi/REST_API_stuff" - "fmt" - "net/http" + "enshi/ABAC/rules" "github.com/gin-gonic/gin" ) @@ -25,19 +23,7 @@ func BlogsMiddleware() gin.HandlerFunc { isAllowed, errors := blogspolicies.BlogPolicies(c) - var errorsMap = map[int]string{} - for i, error := range errors { - errorsMap[i] = error.Error() - } - - if errors != nil { - c.IndentedJSON(http.StatusUnauthorized, errorsMap) - c.Abort() - return - } - - if !isAllowed { - rest_api_stuff.UnauthorizedAnswer(c, fmt.Errorf("you have no permission")) + if rules.ShouldAbortRequest(c, isAllowed, errors) { c.Abort() return } diff --git a/enshi_back/utils/GetContextPayload.go b/enshi_back/middleware/getters/GetContextPayload.go similarity index 96% rename from enshi_back/utils/GetContextPayload.go rename to enshi_back/middleware/getters/GetContextPayload.go index 934207c..84c4dd9 100644 --- a/enshi_back/utils/GetContextPayload.go +++ b/enshi_back/middleware/getters/GetContextPayload.go @@ -1,4 +1,4 @@ -package utils +package getters import ( "github.com/gin-gonic/gin" diff --git a/enshi_back/middleware/getters/getIntParam.go b/enshi_back/middleware/getters/getIntParam.go index f5ddd57..1501981 100644 --- a/enshi_back/middleware/getters/getIntParam.go +++ b/enshi_back/middleware/getters/getIntParam.go @@ -2,12 +2,14 @@ package getters import ( "strconv" + "strings" "github.com/gin-gonic/gin" ) +// Returns -1, error if there is no such param or value invalid func GetInt64Param(c *gin.Context, paramName string) (int64, error) { - int64ParamValue, err := strconv.ParseInt(c.Param(paramName), 10, 64) + int64ParamValue, err := strconv.ParseInt(strings.Trim(c.Param(paramName), "/"), 10, 64) if err != nil { return -1, err diff --git a/enshi_back/middleware/postsMiddleware.go b/enshi_back/middleware/postsMiddleware.go index 2dc90fa..7ee3338 100644 --- a/enshi_back/middleware/postsMiddleware.go +++ b/enshi_back/middleware/postsMiddleware.go @@ -2,9 +2,8 @@ package middleware import ( postspolicies "enshi/ABAC/PostsPolicies" - rest_api_stuff "enshi/REST_API_stuff" - "fmt" - "net/http" + "enshi/ABAC/rules" + "enshi/middleware/getters" "github.com/gin-gonic/gin" ) @@ -16,7 +15,15 @@ func PostsMiddleware() gin.HandlerFunc { case "DELETE": c.Set("target", postspolicies.DELETE_POST) case "PUT": - c.Set("target", postspolicies.UPDATE_POST) + blogId, _ := getters.GetInt64Param(c, "blog-id") + postId, _ := getters.GetInt64Param(c, "post-id") + + if postId > 0 && blogId > 0 { + c.Set("target", postspolicies.UPDATE_POST_BLOG) + } else if postId > 0 { + c.Set("target", postspolicies.UPDATE_POST) + } + case "POST": c.Set("target", postspolicies.CREATE_POST) case "GET": @@ -25,19 +32,7 @@ func PostsMiddleware() gin.HandlerFunc { isAllowed, errors := postspolicies.PostsPolicies(c) - var errorsMap = map[int]string{} - for i, error := range errors { - errorsMap[i] = error.Error() - } - - if errors != nil { - c.IndentedJSON(http.StatusUnauthorized, errorsMap) - c.Abort() - return - } - - if !isAllowed { - rest_api_stuff.UnauthorizedAnswer(c, fmt.Errorf("you have no permission")) + if rules.ShouldAbortRequest(c, isAllowed, errors) { c.Abort() return } diff --git a/enshi_back/routes/blogRoutes/createBlog.go b/enshi_back/routes/blogRoutes/createBlog.go index ecb1e2f..a1f3ede 100644 --- a/enshi_back/routes/blogRoutes/createBlog.go +++ b/enshi_back/routes/blogRoutes/createBlog.go @@ -12,7 +12,7 @@ import ( ) func CreateBlog(c *gin.Context) { - blogParams, err := utils.GetContextPayload[db_repo.CreateBlogByUserIdParams](c) + blogParams, err := getters.GetContextPayload[db_repo.CreateBlogByUserIdParams](c) if err != nil { rest_api_stuff.BadRequestAnswer(c, err) return diff --git a/enshi_back/routes/blogRoutes/deleteBlog.go b/enshi_back/routes/blogRoutes/deleteBlog.go new file mode 100644 index 0000000..25bc5af --- /dev/null +++ b/enshi_back/routes/blogRoutes/deleteBlog.go @@ -0,0 +1,36 @@ +package blogRoutes + +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 DeleteBlog(c *gin.Context) { + blogId, err := getters.GetInt64Param(c, "blog-id") + if err != nil { + rest_api_stuff.BadRequestAnswer(c, err) + return + } + + transaction, err := db_connection.Dbx.Begin(context.Background()) + if err != nil { + rest_api_stuff.InternalErrorAnswer(c, err) + return + } + defer transaction.Rollback(context.Background()) + + err = db_repo.New(transaction). + DeleteBlogByBlogId(context.Background(), blogId) + if err != nil { + rest_api_stuff.InternalErrorAnswer(c, err) + return + } + + transaction.Commit(context.Background()) + rest_api_stuff.OkAnswer(c, "blog has been deleted") +} diff --git a/enshi_back/routes/blogRoutes/getBlog.go b/enshi_back/routes/blogRoutes/getBlog.go new file mode 100644 index 0000000..81c20b6 --- /dev/null +++ b/enshi_back/routes/blogRoutes/getBlog.go @@ -0,0 +1,29 @@ +package blogRoutes + +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 GetBlog(c *gin.Context) { + blogId, err := getters.GetInt64Param(c, "blog-id") + if err != nil { + rest_api_stuff.BadRequestAnswer(c, err) + return + } + + blogData, err := db_repo.New(db_connection.Dbx). + GetBlogByBlogId(context.Background(), blogId) + if err != nil { + rest_api_stuff.InternalErrorAnswer(c, err) + return + } + + c.IndentedJSON(http.StatusOK, blogData) +} diff --git a/enshi_back/routes/blogRoutes/updateBlog.go b/enshi_back/routes/blogRoutes/updateBlog.go new file mode 100644 index 0000000..7924db3 --- /dev/null +++ b/enshi_back/routes/blogRoutes/updateBlog.go @@ -0,0 +1,46 @@ +package blogRoutes + +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 UpdateBlog(c *gin.Context) { + newBlogParams, err := + getters.GetContextPayload[db_repo.UpdateBlogInfoByBlogIdParams](c) + if err != nil { + rest_api_stuff.BadRequestAnswer(c, err) + return + } + + blogId, err := getters.GetInt64Param(c, "blog-id") + if err != nil { + rest_api_stuff.BadRequestAnswer(c, err) + return + } + + newBlogParams.BlogID = blogId + + transaction, err := db_connection.Dbx.Begin(context.Background()) + if err != nil { + rest_api_stuff.InternalErrorAnswer(c, err) + return + } + defer transaction.Rollback(context.Background()) + + _, err = db_repo.New(transaction). + UpdateBlogInfoByBlogId(context.Background(), newBlogParams) + if err != nil { + rest_api_stuff.InternalErrorAnswer(c, err) + return + } + transaction.Commit(context.Background()) + + rest_api_stuff.OkAnswer(c, "blog has been updated") + +} diff --git a/enshi_back/routes/postsRoutes/updatePostBlog.go b/enshi_back/routes/postsRoutes/updatePostBlog.go new file mode 100644 index 0000000..0fcb3c8 --- /dev/null +++ b/enshi_back/routes/postsRoutes/updatePostBlog.go @@ -0,0 +1,58 @@ +package postsRoutes + +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" + "github.com/jackc/pgx/v5/pgtype" +) + +func UpdatePostBlog(c *gin.Context) { + var UpdatedPostParams db_repo.UpdatePostBlogIdParams + + postId, err := getters.GetInt64Param(c, "post-id") + + if err != nil { + rest_api_stuff.InternalErrorAnswer(c, err) + return + } + + blogId, err := getters.GetInt64Param(c, "blog-id") + + if err != nil { + rest_api_stuff.InternalErrorAnswer(c, err) + return + } + + UpdatedPostParams.PostID = postId + UpdatedPostParams.BlogID = pgtype.Int8{ + Valid: true, + Int64: blogId, + } + + transaction, err := db_connection.Dbx.Begin(context.Background()) + if err != nil { + rest_api_stuff.InternalErrorAnswer(c, err) + return + } + defer transaction.Rollback(context.Background()) + + err = db_repo.New( + transaction, + ).UpdatePostBlogId( + context.Background(), + UpdatedPostParams, + ) + + if err != nil { + rest_api_stuff.InternalErrorAnswer(c, err) + return + } + + transaction.Commit(context.Background()) + rest_api_stuff.OkAnswer(c, "post has been updated") +} diff --git a/enshi_back/routes/routesSetup.go b/enshi_back/routes/routesSetup.go index 6662770..5215b95 100644 --- a/enshi_back/routes/routesSetup.go +++ b/enshi_back/routes/routesSetup.go @@ -49,6 +49,10 @@ func SetupRotes(g *gin.Engine) error { "posts/:post-id", postsRoutes.UpdatePost, ) + postsGroup.PUT( + "posts/:post-id/blogs/:blog-id", + postsRoutes.UpdatePostBlog, + ) postsGroup.POST( "posts", postsRoutes.CreatePost, @@ -66,6 +70,21 @@ func SetupRotes(g *gin.Engine) error { blogRoutes.CreateBlog, ) + blogGroup.PUT( + "blogs/:blog-id", + blogRoutes.UpdateBlog, + ) + + blogGroup.DELETE( + "blogs/:blog-id", + blogRoutes.DeleteBlog, + ) + + blogGroup.GET( + "blogs/:blog-id", + blogRoutes.GetBlog, + ) + profilesGroup := g.Group("/") profilesGroup.Use(middleware.ProfileMiddleware()) @@ -74,12 +93,8 @@ func SetupRotes(g *gin.Engine) error { userProfileRoutes.UpdateUserProfile, ) - // Auth group routes - authGroup := g.Group("/") - authGroup.Use(middleware.AuthMiddleware()) - // Admin group routes - adminGroup := authGroup.Group("/admin/") + adminGroup := g.Group("/admin/") adminGroup.Use(middleware.AdminMiddleware()) adminGroup.GET("testAdmin", testAdmin) diff --git a/enshi_back/routes/userProfileRoutes/updateUserProfile.go b/enshi_back/routes/userProfileRoutes/updateUserProfile.go index 22f04e7..2abbd9b 100644 --- a/enshi_back/routes/userProfileRoutes/updateUserProfile.go +++ b/enshi_back/routes/userProfileRoutes/updateUserProfile.go @@ -6,14 +6,13 @@ import ( db_repo "enshi/db/go_queries" "enshi/db_connection" "enshi/middleware/getters" - "enshi/utils" "github.com/gin-gonic/gin" ) func UpdateUserProfile(c *gin.Context) { newProfile, err := - utils.GetContextPayload[db_repo.UpdateProfileByUserIdParams](c) + getters.GetContextPayload[db_repo.UpdateProfileByUserIdParams](c) if err != nil { rest_api_stuff.BadRequestAnswer(c, err) From 7de4cfa0a080ab0f05008a7de44a6d72545042f7 Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 17 Nov 2024 16:35:38 +0300 Subject: [PATCH 09/30] Done with back for now --- enshi_back/ABAC/PostsPolicies/postPolicy.go | 4 +++ .../postRules/deletePostFromBlogRule.go | 24 +++++++++++++ enshi_back/db/queries/posts_queries.sql | 2 +- enshi_back/middleware/postsMiddleware.go | 5 ++- enshi_back/routes/postsRoutes/deletePost.go | 20 ----------- .../routes/postsRoutes/deletePostBlog.go | 35 +++++++++++++++++++ enshi_back/routes/routesSetup.go | 4 +++ 7 files changed, 70 insertions(+), 24 deletions(-) create mode 100644 enshi_back/ABAC/PostsPolicies/postRules/deletePostFromBlogRule.go create mode 100644 enshi_back/routes/postsRoutes/deletePostBlog.go diff --git a/enshi_back/ABAC/PostsPolicies/postPolicy.go b/enshi_back/ABAC/PostsPolicies/postPolicy.go index 5bce3dc..6e7e542 100644 --- a/enshi_back/ABAC/PostsPolicies/postPolicy.go +++ b/enshi_back/ABAC/PostsPolicies/postPolicy.go @@ -9,6 +9,7 @@ import ( const ( DELETE_POST = "delete_post" + DELETE_POST_BLOG = "delete_post_blog" UPDATE_POST = "update_post" UPDATE_POST_BLOG = "update_post_blog" CREATE_POST = "create_post" @@ -26,6 +27,9 @@ func PostsPolicies(c *gin.Context) (bool, []error) { case DELETE_POST: return rules.CheckRule(c, postRules.DeleteRule) + case DELETE_POST_BLOG: + return rules.CheckRule(c, postRules.DeletePostFromBlogRule) + case UPDATE_POST: return rules.CheckRule(c, postRules.PostUpdateRule) diff --git a/enshi_back/ABAC/PostsPolicies/postRules/deletePostFromBlogRule.go b/enshi_back/ABAC/PostsPolicies/postRules/deletePostFromBlogRule.go new file mode 100644 index 0000000..d99c10c --- /dev/null +++ b/enshi_back/ABAC/PostsPolicies/postRules/deletePostFromBlogRule.go @@ -0,0 +1,24 @@ +package postRules + +import ( + globalrules "enshi/ABAC/GlobalRules" + "enshi/ABAC/rules" + + "github.com/gin-gonic/gin" +) + +func DeletePostFromBlogRule(c *gin.Context) (bool, []error) { + rulesToCheck := []rules.RuleFunction{ + globalrules.AuthorizedRule, + globalrules.IsOwnerOfThePostRule, + globalrules.IsOwnerOfTheBlogRule, + } + + isAllowed, errors := rules.CheckRules( + c, + rulesToCheck, + RULES_NUMBER_TO_COMPLETE, + ) + + return isAllowed, errors +} diff --git a/enshi_back/db/queries/posts_queries.sql b/enshi_back/db/queries/posts_queries.sql index 39f61f3..bc260c1 100644 --- a/enshi_back/db/queries/posts_queries.sql +++ b/enshi_back/db/queries/posts_queries.sql @@ -33,4 +33,4 @@ WHERE post_id=$1; UPDATE public.posts SET blog_id=$2, updated_at=CURRENT_TIMESTAMP WHERE post_id = $1 -RETURNING *; +RETURNING *; \ No newline at end of file diff --git a/enshi_back/middleware/postsMiddleware.go b/enshi_back/middleware/postsMiddleware.go index 7ee3338..824046d 100644 --- a/enshi_back/middleware/postsMiddleware.go +++ b/enshi_back/middleware/postsMiddleware.go @@ -10,14 +10,13 @@ import ( func PostsMiddleware() gin.HandlerFunc { return func(c *gin.Context) { + blogId, _ := getters.GetInt64Param(c, "blog-id") + postId, _ := getters.GetInt64Param(c, "post-id") switch c.Request.Method { case "DELETE": c.Set("target", postspolicies.DELETE_POST) case "PUT": - blogId, _ := getters.GetInt64Param(c, "blog-id") - postId, _ := getters.GetInt64Param(c, "post-id") - if postId > 0 && blogId > 0 { c.Set("target", postspolicies.UPDATE_POST_BLOG) } else if postId > 0 { diff --git a/enshi_back/routes/postsRoutes/deletePost.go b/enshi_back/routes/postsRoutes/deletePost.go index b0e3388..83a54f0 100644 --- a/enshi_back/routes/postsRoutes/deletePost.go +++ b/enshi_back/routes/postsRoutes/deletePost.go @@ -5,8 +5,6 @@ import ( rest_api_stuff "enshi/REST_API_stuff" db_repo "enshi/db/go_queries" "enshi/db_connection" - "enshi/middleware/getters" - "fmt" "strconv" "github.com/gin-gonic/gin" @@ -20,25 +18,7 @@ func DeletePost(c *gin.Context) { return } - userClaims, err := getters.GetClaimsFromContext(c) - if err != nil { - rest_api_stuff.BadRequestAnswer(c, err) - return - } - query := db_repo.New(db_connection.Dbx) - post, err := query.GetPostsByPostId(context.Background(), postId) - if err != nil { - rest_api_stuff.InternalErrorAnswer(c, err) - return - } - - if post.UserID != userClaims.Id { - rest_api_stuff.UnauthorizedAnswer(c, fmt.Errorf("you are not the author")) - return - } - - // TODO: Add block of code, so admin could delete anything err = query.DeletePostByPostId(context.Background(), postId) if err != nil { diff --git a/enshi_back/routes/postsRoutes/deletePostBlog.go b/enshi_back/routes/postsRoutes/deletePostBlog.go new file mode 100644 index 0000000..c8265f1 --- /dev/null +++ b/enshi_back/routes/postsRoutes/deletePostBlog.go @@ -0,0 +1,35 @@ +package postsRoutes + +import ( + "context" + rest_api_stuff "enshi/REST_API_stuff" + db_repo "enshi/db/go_queries" + "enshi/db_connection" + "strconv" + + "github.com/gin-gonic/gin" + "github.com/jackc/pgx/v5/pgtype" +) + +func DeletePostBlog(c *gin.Context) { + var queryParams db_repo.UpdatePostBlogIdParams + postId, err := strconv.ParseInt(c.Param("post-id"), 10, 64) + + if err != nil { + rest_api_stuff.BadRequestAnswer(c, err) + return + } + + queryParams.BlogID = pgtype.Int8{} + queryParams.PostID = postId + + query := db_repo.New(db_connection.Dbx) + + err = query.UpdatePostBlogId(context.Background(), queryParams) + if err != nil { + rest_api_stuff.InternalErrorAnswer(c, err) + return + } + + rest_api_stuff.OkAnswer(c, "post has been deleted") +} diff --git a/enshi_back/routes/routesSetup.go b/enshi_back/routes/routesSetup.go index 5215b95..8978527 100644 --- a/enshi_back/routes/routesSetup.go +++ b/enshi_back/routes/routesSetup.go @@ -61,6 +61,10 @@ func SetupRotes(g *gin.Engine) error { "posts/:post-id", postsRoutes.DeletePost, ) + postsGroup.DELETE( + "posts/:post-id/blogs", + postsRoutes.DeletePostBlog, + ) blogGroup := g.Group("/") blogGroup.Use(middleware.BlogsMiddleware()) From c828d6b132cddc1a596a1bb05db0da6def8b9ea6 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 18 Nov 2024 19:35:17 +0300 Subject: [PATCH 10/30] Done --- enshi/package-lock.json | 100 ++++++++++++++++++ enshi/package.json | 1 + .../{ArcticleViewer.tsx => ArticleViewer.tsx} | 2 +- enshi/src/Components/NavBar/NavBar.tsx | 87 ++++++++------- enshi/src/index.css | 8 +- enshi/src/routes/routes.tsx | 4 +- enshi/tailwind.config.js | 47 ++++---- package-lock.json | 54 ++++++++++ package.json | 5 + 9 files changed, 241 insertions(+), 67 deletions(-) rename enshi/src/Components/ArticleViewer/{ArcticleViewer.tsx => ArticleViewer.tsx} (85%) create mode 100644 package-lock.json create mode 100644 package.json diff --git a/enshi/package-lock.json b/enshi/package-lock.json index d490701..641fb3f 100644 --- a/enshi/package-lock.json +++ b/enshi/package-lock.json @@ -14,6 +14,7 @@ "@radix-ui/react-tooltip": "^1.1.2", "@radix-ui/themes": "^3.1.3", "@tanstack/react-query": "^5.55.0", + "axios": "^1.7.7", "html-react-parser": "^5.1.16", "i18n": "^0.15.1", "i18next": "^23.14.0", @@ -3336,6 +3337,12 @@ "node": ">=10" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, "node_modules/autoprefixer": { "version": "10.4.20", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", @@ -3374,6 +3381,17 @@ "postcss": "^8.1.0" } }, + "node_modules/axios": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -3602,6 +3620,18 @@ "dev": true, "license": "MIT" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -3738,6 +3768,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/detect-node-es": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", @@ -4357,6 +4396,26 @@ "dev": true, "license": "ISC" }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/foreground-child": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", @@ -4374,6 +4433,20 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -5185,6 +5258,27 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -5712,6 +5806,12 @@ "react-is": "^16.13.1" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", diff --git a/enshi/package.json b/enshi/package.json index d3a5b58..9047b25 100644 --- a/enshi/package.json +++ b/enshi/package.json @@ -16,6 +16,7 @@ "@radix-ui/react-tooltip": "^1.1.2", "@radix-ui/themes": "^3.1.3", "@tanstack/react-query": "^5.55.0", + "axios": "^1.7.7", "html-react-parser": "^5.1.16", "i18n": "^0.15.1", "i18next": "^23.14.0", diff --git a/enshi/src/Components/ArticleViewer/ArcticleViewer.tsx b/enshi/src/Components/ArticleViewer/ArticleViewer.tsx similarity index 85% rename from enshi/src/Components/ArticleViewer/ArcticleViewer.tsx rename to enshi/src/Components/ArticleViewer/ArticleViewer.tsx index 753f69d..ac30298 100644 --- a/enshi/src/Components/ArticleViewer/ArcticleViewer.tsx +++ b/enshi/src/Components/ArticleViewer/ArticleViewer.tsx @@ -1,7 +1,7 @@ import { Container } from "@radix-ui/themes"; import React from "react"; -export default function ArcticleViewer() { +export default function ArticleViewer() { return ( <>
diff --git a/enshi/src/Components/NavBar/NavBar.tsx b/enshi/src/Components/NavBar/NavBar.tsx index a3d630c..64cfbe9 100644 --- a/enshi/src/Components/NavBar/NavBar.tsx +++ b/enshi/src/Components/NavBar/NavBar.tsx @@ -1,42 +1,25 @@ -import { Button, Card, ChevronDownIcon, Text } from "@radix-ui/themes"; +import { Button, Heading, IconButton, TextField, useThemeContext } from "@radix-ui/themes"; import * as NavigationMenu from "@radix-ui/react-navigation-menu"; import { useLocation, useNavigate } from "react-router-dom"; - +import { PersonIcon } from "@radix-ui/react-icons" + export default function NavBar() { return ( -