fix platform authorization and workflow integrity
This commit is contained in:
@@ -1,12 +1,19 @@
|
||||
package platform
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"git.apinb.com/bsm-sdk/core/errcode"
|
||||
"git.apinb.com/bsm-sdk/core/infra"
|
||||
"git.apinb.com/bsm-sdk/core/middleware"
|
||||
"git.apinb.com/heqiapp/platforms/backend/api/internal/impl"
|
||||
"git.apinb.com/heqiapp/platforms/backend/api/internal/logic/common"
|
||||
platformbase "git.apinb.com/heqiapp/platforms/backend/api/internal/logic/platform"
|
||||
"git.apinb.com/heqiapp/platforms/backend/api/internal/models"
|
||||
"github.com/gin-gonic/gin"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const platformMenusContextKey = "platform_authorized_menus"
|
||||
@@ -35,6 +42,10 @@ func platformMenuAllowsRequest(menus []platformbase.Menu, requestPath, method st
|
||||
(menuIdentity == "gasorder_contract" || resource == "user_address") {
|
||||
return true
|
||||
}
|
||||
if method == "GET" && menu.Identity == "gasorder_basic" &&
|
||||
(resource == "delivery_basic" || resource == "staff_account") {
|
||||
return true
|
||||
}
|
||||
if method == "GET" && relative == "wallet_basic" &&
|
||||
(menu.Identity == "gas_basic" || menu.Identity == "delivery_basic" ||
|
||||
menu.Identity == "staff" || menu.Identity == "user_account" ||
|
||||
@@ -87,12 +98,21 @@ func RequirePlatformMenuAccess() gin.HandlerFunc {
|
||||
ctx.Abort()
|
||||
return
|
||||
}
|
||||
var account models.PlatformAccount
|
||||
if err := impl.DBService.Select("id", "platform_role_code").
|
||||
Where("identity = ? AND platform_role_code = ? AND status = ?", claims.Identity, claims.Role, common.StatusEnable).
|
||||
First(&account).Error; err != nil {
|
||||
infra.Response.Error(ctx, errcode.ErrPermissionDenied)
|
||||
ctx.Abort()
|
||||
return
|
||||
}
|
||||
if claims.Role == "root" {
|
||||
ctx.Next()
|
||||
return
|
||||
}
|
||||
menus, err := platformbase.LoadPlatformMenus(claims.Role)
|
||||
if err != nil || !platformMenuAllowsRequest(menus, ctx.Request.URL.Path, ctx.Request.Method) {
|
||||
if err != nil || !platformMenuAllowsRequest(menus, ctx.Request.URL.Path, ctx.Request.Method) ||
|
||||
!platformScopedRequestAllowed(ctx, menus) {
|
||||
infra.Response.Error(ctx, errcode.ErrPermissionDenied)
|
||||
ctx.Abort()
|
||||
return
|
||||
@@ -101,3 +121,104 @@ func RequirePlatformMenuAccess() gin.HandlerFunc {
|
||||
ctx.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func platformScopedRequestAllowed(ctx *gin.Context, menus []platformbase.Menu) bool {
|
||||
relative := strings.Trim(strings.SplitN(ctx.Request.URL.Path, "/platform/v1/", 2)[1], "/")
|
||||
parts := strings.Split(relative, "/")
|
||||
resource := parts[0]
|
||||
if resource == "wallet_basic" && len(parts) == 1 && ctx.Request.Method == "GET" {
|
||||
if hasMenuIdentity(menus, "wallet_apply_cash") {
|
||||
return true
|
||||
}
|
||||
required := map[string]string{
|
||||
"gas": "gas_basic", "delivery": "delivery_basic", "staff": "staff", "user": "user_account",
|
||||
}[ctx.Query("owner_type")]
|
||||
return required != "" && hasMenuIdentity(menus, required)
|
||||
}
|
||||
if resource == "staff_credential" {
|
||||
return staffCredentialRequestAllowed(ctx, menus, parts)
|
||||
}
|
||||
if resource != "staff_account" {
|
||||
return true
|
||||
}
|
||||
if len(parts) == 1 {
|
||||
if ctx.Request.Method == "POST" {
|
||||
return hasMenuIdentity(menus, "staff_add")
|
||||
}
|
||||
if ctx.Request.Method == "GET" {
|
||||
required := staffMenuIdentity(ctx.Query("role_code"))
|
||||
return required != "" && (hasMenuIdentity(menus, required) ||
|
||||
(required == "staff_delivery" && hasMenuIdentity(menus, "gasorder_basic")))
|
||||
}
|
||||
return false
|
||||
}
|
||||
var staff models.StaffAccount
|
||||
if err := common.ActiveRecords(impl.DBService).Select("role_code").
|
||||
Where("identity = ?", parts[1]).First(&staff).Error; err != nil {
|
||||
return false
|
||||
}
|
||||
return hasMenuIdentity(menus, staffMenuIdentity(staff.RoleCode))
|
||||
}
|
||||
|
||||
func staffCredentialRequestAllowed(ctx *gin.Context, menus []platformbase.Menu, parts []string) bool {
|
||||
var staffIdentity string
|
||||
if len(parts) == 1 && ctx.Request.Method == "GET" {
|
||||
staffIdentity = ctx.Query("staff_account_identity")
|
||||
} else if (ctx.Request.Method == "POST" || ctx.Request.Method == "PUT") && ctx.Request.Body != nil {
|
||||
body, err := io.ReadAll(ctx.Request.Body)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
ctx.Request.Body = io.NopCloser(bytes.NewReader(body))
|
||||
var payload struct {
|
||||
StaffAccountIdentity string `json:"staff_account_identity"`
|
||||
}
|
||||
if json.Unmarshal(body, &payload) != nil {
|
||||
return false
|
||||
}
|
||||
staffIdentity = payload.StaffAccountIdentity
|
||||
} else if len(parts) > 1 {
|
||||
var credential models.StaffCredential
|
||||
if err := common.ActiveRecords(impl.DBService).Select("staff_account_id").
|
||||
Where("identity = ?", parts[1]).First(&credential).Error; err != nil {
|
||||
return false
|
||||
}
|
||||
var staff models.StaffAccount
|
||||
if err := common.ActiveRecords(impl.DBService).Select("role_code").
|
||||
Where("id = ?", credential.StaffAccountID).First(&staff).Error; err != nil {
|
||||
return false
|
||||
}
|
||||
return hasMenuIdentity(menus, staffMenuIdentity(staff.RoleCode))
|
||||
}
|
||||
if staffIdentity == "" {
|
||||
return false
|
||||
}
|
||||
var staff models.StaffAccount
|
||||
if err := common.ActiveRecords(impl.DBService).Select("role_code").
|
||||
Where("identity = ?", staffIdentity).First(&staff).Error; err != nil {
|
||||
return false
|
||||
}
|
||||
return hasMenuIdentity(menus, staffMenuIdentity(staff.RoleCode))
|
||||
}
|
||||
|
||||
func staffMenuIdentity(roleCode string) string {
|
||||
switch roleCode {
|
||||
case "installer":
|
||||
return "staff_installer"
|
||||
case "delivery":
|
||||
return "staff_delivery"
|
||||
case "operations":
|
||||
return "staff_operations"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func hasMenuIdentity(menus []platformbase.Menu, identity string) bool {
|
||||
for _, menu := range menus {
|
||||
if menu.Identity == identity {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ func platformAccountView(account models.PlatformAccount) map[string]any {
|
||||
|
||||
func GetPlatformAccount(ctx *gin.Context) {
|
||||
var account models.PlatformAccount
|
||||
if err := impl.DBService.Where("identity = ?", ctx.Param("identity")).First(&account).Error; err != nil {
|
||||
if err := common.ActiveRecords(impl.DBService).Where("identity = ?", ctx.Param("identity")).First(&account).Error; err != nil {
|
||||
common.RespondRecordError(ctx, err)
|
||||
return
|
||||
}
|
||||
@@ -127,6 +127,9 @@ func UpdatePlatformAccountStatus(ctx *gin.Context) {
|
||||
if !common.RequirePlatformRoot(ctx) {
|
||||
return
|
||||
}
|
||||
if !modifiablePlatformAccount(ctx) {
|
||||
return
|
||||
}
|
||||
common.UpdateRecordStatus(ctx, &models.PlatformAccount{})
|
||||
}
|
||||
|
||||
@@ -135,9 +138,26 @@ func ArchivePlatformAccount(ctx *gin.Context) {
|
||||
if !common.RequirePlatformRoot(ctx) {
|
||||
return
|
||||
}
|
||||
if !modifiablePlatformAccount(ctx) {
|
||||
return
|
||||
}
|
||||
common.ArchiveRecord(ctx, &models.PlatformAccount{})
|
||||
}
|
||||
|
||||
func modifiablePlatformAccount(ctx *gin.Context) bool {
|
||||
var account models.PlatformAccount
|
||||
if err := common.ActiveRecords(impl.DBService).Select("platform_role_code").
|
||||
Where("identity = ?", ctx.Param("identity")).First(&account).Error; err != nil {
|
||||
common.RespondRecordError(ctx, err)
|
||||
return false
|
||||
}
|
||||
if account.PlatformRoleCode == "root" {
|
||||
infra.Response.Error(ctx, errcode.ErrInvalidArgument)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func platformPasswordHash(password string) (string, error) {
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
return string(hash), err
|
||||
|
||||
@@ -55,7 +55,7 @@ func UpdatePlatformRole(ctx *gin.Context) {
|
||||
return
|
||||
}
|
||||
var role models.PlatformRole
|
||||
if err := impl.DBService.Where("identity = ?", ctx.Param("identity")).First(&role).Error; err != nil {
|
||||
if err := common.ActiveRecords(impl.DBService).Where("identity = ?", ctx.Param("identity")).First(&role).Error; err != nil {
|
||||
common.RespondRecordError(ctx, err)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ func ReplacePlatformRoleMenus(ctx *gin.Context) {
|
||||
}
|
||||
if err := impl.DBService.Transaction(func(transaction *gorm.DB) error {
|
||||
var role models.PlatformRole
|
||||
if err := transaction.Where("identity = ?", ctx.Param("identity")).First(&role).Error; err != nil {
|
||||
if err := common.ActiveRecords(transaction).Where("identity = ?", ctx.Param("identity")).First(&role).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if role.IsSystem {
|
||||
@@ -77,7 +77,7 @@ func ListPlatformRoleMenuIdentities(ctx *gin.Context) {
|
||||
return
|
||||
}
|
||||
var role models.PlatformRole
|
||||
if err := impl.DBService.Where("identity = ?", ctx.Param("identity")).First(&role).Error; err != nil {
|
||||
if err := common.ActiveRecords(impl.DBService).Where("identity = ?", ctx.Param("identity")).First(&role).Error; err != nil {
|
||||
common.RespondRecordError(ctx, err)
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user