From 01da5ec2ee6268aa06996e5f60dfbc6929c85cc9 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 15 Nov 2024 01:04:48 +0300 Subject: [PATCH 1/5] 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 2/5] 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 3/5] 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 4/5] 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 5/5] 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 +}