Starting to implement permissions into frontend

This commit is contained in:
cdricms
2025-01-30 15:50:58 +01:00
parent 8d2214e5dd
commit 0e707e8721
16 changed files with 715 additions and 35 deletions

View File

@@ -1,17 +1,17 @@
package blogs
<<<<<<< HEAD
import (
"context"
"log"
"net/http"
"fr.latosa-escrima/api/core"
"fr.latosa-escrima/core"
"fr.latosa-escrima/core/models"
)
func HandleDelete(w http.ResponseWriter, r *http.Request) {
uuid := r.PathValue("blog_uuid")
var blog core.Blog
var blog models.Blog
res, err := core.DB.NewDelete().
Model(&blog).
Where("blog_id = ?", uuid).
@@ -31,5 +31,3 @@ func HandleDelete(w http.ResponseWriter, r *http.Request) {
Message: "Blog deleted.",
}.Respond(w, http.StatusOK)
}
=======
>>>>>>> dev/cedric

View File

@@ -19,6 +19,9 @@ var EventsRoutes = map[string]core.Handler{
Handler: events.HandleDelete,
Middlewares: []core.Middleware{Methods("DELETE"), AuthJWT}},
"/events/{event_uuid}/update": {
Handler: events.HandleUpdate,
Middlewares: []core.Middleware{Methods("PATCH"), AuthJWT}},
Handler: events.HandleUpdate,
Middlewares: []core.Middleware{
Methods("PATCH"),
HasPermissions("events", "update"),
AuthJWT}},
}

View File

@@ -9,8 +9,10 @@ import (
"fr.latosa-escrima/api/users"
core "fr.latosa-escrima/core"
"fr.latosa-escrima/core/models"
"fr.latosa-escrima/utils"
"github.com/golang-jwt/jwt/v5"
"github.com/google/uuid"
)
func CORS(next http.Handler) http.Handler {
@@ -78,12 +80,12 @@ func AuthJWT(next http.Handler) http.Handler {
}
// @param methods string -> HttpMethods separated by commas.
func Methods(methods string) core.Middleware {
_methods := strings.Split(strings.ToUpper(methods), ",")
func Methods(methods ...string) core.Middleware {
// _methods := strings.Split(strings.ToUpper(methods), ",")
middleware := func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if len(methods) > 0 {
if !utils.Contains(_methods, r.Method) {
if !utils.Contains(methods, r.Method) {
core.JSONError{
Status: core.Error,
Message: "Method is not allowed.",
@@ -98,3 +100,109 @@ func Methods(methods string) core.Middleware {
return middleware
}
// WARNING: For this to work, the Middleware AuthJWT has to be applied after in
// the Middleswares ([]core.Middleware)
//
// This Middleware behaves like a AND on the actions. The user must have all the
// listed actions on this particular resource.
func HasPermissions(resource string, actions ...string) core.Middleware {
var middleware core.Middleware = func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := context.Background()
token, ok := r.Context().Value("token").(*jwt.Token)
if !ok {
core.JSONError{
Status: core.Error,
Message: "Couldn't retrieve your JWT.",
}.Respond(w, http.StatusInternalServerError)
return
}
claims, ok := token.Claims.(jwt.MapClaims)
if !ok {
core.JSONError{
Status: core.Error,
Message: "Invalid token claims.",
}.Respond(w, http.StatusInternalServerError)
return
}
id, err := uuid.Parse(claims["user_id"].(string))
if err != nil {
core.JSONError{
Status: core.Error,
Message: err.Error(),
}.Respond(w, http.StatusInternalServerError)
return
}
var user models.User
count, err := core.DB.NewSelect().Model(&user).
Where("user_id = ?", id).
Relation("Roles.Permissions").
Limit(1).
ScanAndCount(ctx)
if count == 0 {
core.JSONError{
Status: core.Error,
Message: "The user doesn't exist",
}.Respond(w, http.StatusNotFound)
return
}
if err != nil {
core.JSONError{
Status: core.Error,
Message: err.Error(),
}.Respond(w, http.StatusInternalServerError)
return
}
// permissions := utils.MergeArrays(
// utils.Map(user.Roles, func(r models.Role) []models.Permission {
// return r.Permissions
// })...)
//
// for _, action := range actions {
// permission := utils.Find(permissions, func(p models.Permission, i int) bool {
// return resource == p.Resource && action == p.Action
// })
// if permission == nil {
// core.JSONError{
// Status: core.Error,
// Message: fmt.Sprintf("The user doesn't have the proper permission %s:%s", resource, action),
// }.Respond(w, http.StatusUnauthorized)
// return
// }
// }
permissionsSet := make(map[string]struct{}) // Set to store unique permissions
// Populate the set with user's permissions
for _, role := range user.Roles {
for _, perm := range role.Permissions {
key := perm.Resource + ":" + perm.Action
permissionsSet[key] = struct{}{} // Store as a set for fast lookups
}
}
// Check if the user has all required permissions
for _, action := range actions {
key := resource + ":" + action
if _, exists := permissionsSet[key]; !exists {
core.JSONError{
Status: core.Error,
Message: fmt.Sprintf("The user doesn't have the proper permission %s:%s", resource, action),
}.Respond(w, http.StatusUnauthorized)
return
}
}
next.ServeHTTP(w, r)
})
}
return middleware
}

View File

@@ -0,0 +1,55 @@
package permissions
import (
"context"
"fmt"
"net/http"
"fr.latosa-escrima/core"
"fr.latosa-escrima/core/models"
"github.com/jackc/pgtype"
)
type ResourceActions struct {
Resource string `bun:"resource" json:"resource"`
Actions pgtype.TextArray `bun:"actions" json:"actions"`
}
func HandleResourceActions(w http.ResponseWriter, r *http.Request) {
var groupedPermissions []ResourceActions
err := core.DB.NewSelect().
Model((*models.Permission)(nil)).
Column("resource").
ColumnExpr("array_agg(action) AS actions").
Group("resource").
Scan(context.Background(), &groupedPermissions)
fmt.Println(groupedPermissions)
if err != nil {
core.JSONError{
Status: core.Error,
Message: err.Error(),
}.Respond(w, http.StatusInternalServerError)
return
}
result := make([]map[string]interface{}, 0)
for _, gp := range groupedPermissions {
var actions []string
_ = gp.Actions.AssignTo(&actions)
result = append(result, map[string]interface{}{
"resource": gp.Resource,
"actions": actions,
})
}
core.JSONSuccess{
Status: core.Success,
Message: "Permissions found.",
Data: result,
}.Respond(w, http.StatusOK)
}

View File

@@ -10,6 +10,10 @@ var PermissionsRoutes = map[string]core.Handler{
Handler: permissions.HandlePermissions,
Middlewares: []core.Middleware{Methods("GET"), AuthJWT},
},
"/permissions/grouped": {
Handler: permissions.HandleResourceActions,
Middlewares: []core.Middleware{Methods("GET"), AuthJWT},
},
"/permissions/{permission_id}": {
Handler: permissions.HandlePermission,
Middlewares: []core.Middleware{Methods("GET"), AuthJWT},

View File

@@ -9,31 +9,41 @@ var UserRoutes = map[string]core.Handler{
"/users/login": {
Handler: users.HandleLogin,
Middlewares: []core.Middleware{Methods("POST")}},
// Could add users:own:get permission there, but don't think it's
// necessary
"/users/me": {
Handler: users.HandleMe,
Middlewares: []core.Middleware{Methods("GET"), AuthJWT}},
"/users": {
Handler: users.HandleUsers,
Middlewares: []core.Middleware{Methods("GET"), AuthJWT}},
Handler: users.HandleUsers,
Middlewares: []core.Middleware{Methods("GET"),
HasPermissions("users", "get"), AuthJWT}},
"/users/new": {
Handler: users.HandleNew,
Middlewares: []core.Middleware{Methods("POST"), AuthJWT}},
Handler: users.HandleNew,
Middlewares: []core.Middleware{Methods("POST"),
HasPermissions("users", "insert"), AuthJWT}},
"/users/{user_uuid}": {
Handler: users.HandleUser,
Middlewares: []core.Middleware{Methods("GET"), AuthJWT}},
Handler: users.HandleUser,
Middlewares: []core.Middleware{Methods("GET"),
HasPermissions("users", "get"), AuthJWT}},
"/users/{user_uuid}/delete": {
Handler: users.HandleDelete,
Middlewares: []core.Middleware{Methods("DELETE"), AuthJWT}},
Handler: users.HandleDelete,
Middlewares: []core.Middleware{Methods("DELETE"),
HasPermissions("users", "delete"), AuthJWT}},
"/users/{user_uuid}/update": {
Handler: users.HandleUpdate,
Middlewares: []core.Middleware{Methods("PATCH"), AuthJWT}},
Handler: users.HandleUpdate,
Middlewares: []core.Middleware{Methods("PATCH"),
HasPermissions("users", "update"), AuthJWT}},
"/users/{user_uuid}/roles": {
Handler: users.HandleRoles,
Middlewares: []core.Middleware{Methods("GET"), AuthJWT}},
Handler: users.HandleRoles,
Middlewares: []core.Middleware{Methods("GET"),
HasPermissions("users", "get"), AuthJWT}},
"/users/{user_uuid}/roles/{role_id}/add": {
Handler: users.HandleAddRole,
Middlewares: []core.Middleware{Methods("PATCH"), AuthJWT}},
Handler: users.HandleAddRole,
Middlewares: []core.Middleware{Methods("PATCH"),
HasPermissions("users", "update"), AuthJWT}},
"/users/{user_uuid}/roles/{role_id}/remove": {
Handler: users.HandleRemoveRole,
Middlewares: []core.Middleware{Methods("PATCH"), AuthJWT}},
Handler: users.HandleRemoveRole,
Middlewares: []core.Middleware{Methods("PATCH"),
HasPermissions("users", "update"), AuthJWT}},
}