Starting to implement permissions into frontend
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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}},
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
55
backend/api/permissions/resource_actions.go
Normal file
55
backend/api/permissions/resource_actions.go
Normal 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)
|
||||
}
|
||||
@@ -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},
|
||||
|
||||
@@ -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}},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user