Skip to content
This repository has been archived by the owner on Dec 4, 2024. It is now read-only.

Commit

Permalink
Merge pull request #25 from swibly/chore/permission-usability
Browse files Browse the repository at this point in the history
chore: Permission Usability
  • Loading branch information
devkcud authored Jun 6, 2024
2 parents 9837c7b + 1ce8fad commit e38e1b6
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 27 deletions.
12 changes: 12 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ var (
BcryptCost int `yaml:"bcrypt_cost"`
JWTSecret string `yaml:"jwt_secret"`
}

Permissions struct {
Admin string `yaml:"admin"`
ManageUser string `yaml:"manage_user"`
ManagePermissions string `yaml:"manager_permissions"`
ManageProjects string `yaml:"manage_projects"`
ManageStore string `yaml:"manage_store"`
}
)

func Parse() {
Expand All @@ -47,6 +55,10 @@ func Parse() {
log.Fatalf("error: %v", err)
}

if err := yaml.Unmarshal(read("permissions.yaml"), &Permissions); err != nil {
log.Fatalf("error: %v", err)
}

log.Print("Loaded config files")
}

Expand Down
5 changes: 5 additions & 0 deletions config/permissions.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
admin: admin
manage_user: manage_user
manage_permissions: manage_permissions
manage_projects: manage_projects
manage_store: manage_store
48 changes: 28 additions & 20 deletions internal/controller/http/v1/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,22 @@ import (
"log"
"net/http"

"github.com/devkcud/arkhon-foundation/arkhon-api/config"
"github.com/devkcud/arkhon-foundation/arkhon-api/internal/model/dto"
"github.com/devkcud/arkhon-foundation/arkhon-api/internal/service"
"github.com/devkcud/arkhon-foundation/arkhon-api/pkg/middleware"
"github.com/devkcud/arkhon-foundation/arkhon-api/pkg/utils"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)

func newUserRoutes(handler *gin.RouterGroup) {
h := handler.Group("/user")
{
h.GET("/:username/profile", middleware.OptionalAuthMiddleware, GetProfileHandler)
h.GET("/:username/profile", middleware.OptionalAuthMiddleware, middleware.GetPermissionsMiddleware, GetProfileHandler)

h.GET("/:username/followers", middleware.OptionalAuthMiddleware, GetFollowersHandler)
h.GET("/:username/following", middleware.OptionalAuthMiddleware, GetFollowingHandler)
h.GET("/:username/followers", middleware.OptionalAuthMiddleware, middleware.GetPermissionsMiddleware, GetFollowersHandler)
h.GET("/:username/following", middleware.OptionalAuthMiddleware, middleware.GetPermissionsMiddleware, GetFollowingHandler)

h.GET("/:username/permissions", GetUserPermissions)

Expand All @@ -37,9 +39,11 @@ func GetProfileHandler(ctx *gin.Context) {
username := ctx.Param("username")
user, err := service.User.GetByUsername(username)
if err == nil {
if user.Show.Profile == -1 && (issuer == nil || issuer.ID != user.ID) {
ctx.JSON(http.StatusForbidden, gin.H{"error": "User disabled viewing their profile"})
return
if !utils.HasPermissionsByContext(ctx, config.Permissions.ManageUser) {
if user.Show.Profile == -1 && (issuer == nil || issuer.ID != user.ID) {
ctx.JSON(http.StatusForbidden, gin.H{"error": "User disabled viewing their profile"})
return
}
}

ctx.JSON(http.StatusOK, user)
Expand Down Expand Up @@ -76,14 +80,16 @@ func GetFollowersHandler(ctx *gin.Context) {
return
}

if user.Show.Profile == -1 && (issuer == nil || issuer.ID != user.ID) {
ctx.JSON(http.StatusForbidden, gin.H{"error": "User disabled viewing their profile"})
return
}
if !utils.HasPermissionsByContext(ctx, config.Permissions.ManageUser) {
if user.Show.Profile == -1 && (issuer == nil || issuer.ID != user.ID) {
ctx.JSON(http.StatusForbidden, gin.H{"error": "User disabled viewing their profile"})
return
}

if user.Show.Followers == -1 && (issuer == nil || issuer.ID != user.ID) {
ctx.JSON(http.StatusForbidden, gin.H{"error": "User disabled viewing whom are following them"})
return
if user.Show.Followers == -1 && (issuer == nil || issuer.ID != user.ID) {
ctx.JSON(http.StatusForbidden, gin.H{"error": "User disabled viewing whom are following them"})
return
}
}

followers, err := service.Follow.GetFollowers(user.ID)
Expand Down Expand Up @@ -115,14 +121,16 @@ func GetFollowingHandler(ctx *gin.Context) {
return
}

if user.Show.Profile == -1 && (issuer == nil || issuer.ID != user.ID) {
ctx.JSON(http.StatusForbidden, gin.H{"error": "User disabled viewing their profile"})
return
}
if !utils.HasPermissionsByContext(ctx, config.Permissions.ManageUser) {
if user.Show.Profile == -1 && (issuer == nil || issuer.ID != user.ID) {
ctx.JSON(http.StatusForbidden, gin.H{"error": "User disabled viewing their profile"})
return
}

if user.Show.Following == -1 && (issuer == nil || issuer.ID != user.ID) {
ctx.JSON(http.StatusForbidden, gin.H{"error": "User disabled viewing whom they are following"})
return
if user.Show.Following == -1 && (issuer == nil || issuer.ID != user.ID) {
ctx.JSON(http.StatusForbidden, gin.H{"error": "User disabled viewing whom they are following"})
return
}
}

following, err := service.Follow.GetFollowing(user.ID)
Expand Down
5 changes: 3 additions & 2 deletions internal/service/repository/permission.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ func (pr permissionRepository) GetPermissions(userID uint) ([]*model.Permission,
var permissions []*model.Permission

err := pr.db.Table("users").
Select("users.id, permissions.id, permissions.name").
Joins("JOIN permissions ON permissions.id = users.id").
Select("permissions.id, permissions.name").
Joins("JOIN user_permissions ON user_permissions.user_id = users.id").
Joins("JOIN permissions ON permissions.id = user_permissions.permission_id").
Where("users.id = ?", userID).
Scan(&permissions).Error

Expand Down
10 changes: 5 additions & 5 deletions pkg/db/postgres.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ func Load() {
}

Postgres.Create([]model.Permission{
{Name: "admin"},
{Name: "manage_user"},
{Name: "manage_permissions"},
{Name: "manage_projects"},
{Name: "manage_store"},
{Name: config.Permissions.Admin},
{Name: config.Permissions.ManageUser},
{Name: config.Permissions.ManagePermissions},
{Name: config.Permissions.ManageProjects},
{Name: config.Permissions.ManageStore},
})

log.Print("Loaded migrations")
Expand Down
36 changes: 36 additions & 0 deletions pkg/middleware/permission.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package middleware

import (
"github.com/devkcud/arkhon-foundation/arkhon-api/internal/model/dto"
"github.com/devkcud/arkhon-foundation/arkhon-api/internal/service"
"github.com/gin-gonic/gin"
)

// GetPermissionsMiddleware must be after OptionalAuthMiddleware or AuthMiddleware
func GetPermissionsMiddleware(ctx *gin.Context) {
var issuer *dto.ProfileSearch = nil
p, exists := ctx.Get("auth_user")

if !exists {
ctx.Set("permissions", []string{}) // Set to an empty string so it can be queried anyway
ctx.Next()
return
}

issuer = p.(*dto.ProfileSearch)

permissions, err := service.Permission.GetPermissions(issuer.ID)
if err != nil {
ctx.Set("permissions", []string{})
ctx.Next()
return
}

var list []string

for _, permission := range permissions {
list = append(list, permission.Name)
}

ctx.Set("permissions", list)
}
32 changes: 32 additions & 0 deletions pkg/utils/permission.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package utils

import (
"slices"

"github.com/devkcud/arkhon-foundation/arkhon-api/config"
"github.com/gin-gonic/gin"
)

// There is no need to pass anything to check for admin roles
func HasPermissions(list []string, lookup ...string) bool {
if slices.Contains(list, config.Permissions.Admin) {
return true
}

if len(lookup) == 0 {
return false
}

for _, perm := range lookup {
if !slices.Contains(list, perm) {
return false
}
}

return true
}

// WARN: must have the GetPermissionsMiddleware in the route before using
func HasPermissionsByContext(ctx *gin.Context, lookup ...string) bool {
return HasPermissions(ctx.Keys["permissions"].([]string), lookup...)
}

0 comments on commit e38e1b6

Please sign in to comment.