mirror of
https://github.com/veops/oneterm.git
synced 2025-10-05 07:16:57 +08:00
refactor(backend): authorization v2
This commit is contained in:
329
backend/internal/api/controller/authorization_v2.go
Normal file
329
backend/internal/api/controller/authorization_v2.go
Normal file
@@ -0,0 +1,329 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/spf13/cast"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/veops/oneterm/internal/acl"
|
||||
"github.com/veops/oneterm/internal/model"
|
||||
"github.com/veops/oneterm/internal/service"
|
||||
myErrors "github.com/veops/oneterm/pkg/errors"
|
||||
)
|
||||
|
||||
// CreateAuthorizationV2 godoc
|
||||
//
|
||||
// @Tags authorization_v2
|
||||
// @Param authorization body model.AuthorizationV2 true "authorization rule"
|
||||
// @Success 200 {object} HttpResponse
|
||||
// @Router /authorization_v2 [post]
|
||||
func (c *Controller) CreateAuthorizationV2(ctx *gin.Context) {
|
||||
auth := &model.AuthorizationV2{}
|
||||
err := ctx.ShouldBindBodyWithJSON(auth)
|
||||
if err != nil {
|
||||
ctx.AbortWithError(http.StatusBadRequest, &myErrors.ApiError{Code: myErrors.ErrInvalidArgument, Data: map[string]any{"err": err}})
|
||||
return
|
||||
}
|
||||
|
||||
authV2Service := service.NewAuthorizationV2Service()
|
||||
|
||||
// Check permissions
|
||||
currentUser, _ := acl.GetSessionFromCtx(ctx)
|
||||
if !acl.IsAdmin(currentUser) {
|
||||
ctx.AbortWithError(http.StatusForbidden, &myErrors.ApiError{Code: myErrors.ErrNoPerm, Data: map[string]any{"perm": acl.GRANT}})
|
||||
return
|
||||
}
|
||||
|
||||
// Create the rule
|
||||
err = authV2Service.CreateRule(ctx, auth)
|
||||
if err != nil {
|
||||
if ctx.IsAborted() {
|
||||
return
|
||||
}
|
||||
ctx.AbortWithError(http.StatusInternalServerError, &myErrors.ApiError{Code: myErrors.ErrInternal, Data: map[string]any{"err": err}})
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, HttpResponse{
|
||||
Data: map[string]any{
|
||||
"id": auth.GetId(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateAuthorizationV2 godoc
|
||||
//
|
||||
// @Tags authorization_v2
|
||||
// @Param id path int true "authorization id"
|
||||
// @Param authorization body model.AuthorizationV2 true "authorization rule"
|
||||
// @Success 200 {object} HttpResponse
|
||||
// @Router /authorization_v2/:id [put]
|
||||
func (c *Controller) UpdateAuthorizationV2(ctx *gin.Context) {
|
||||
authId := cast.ToInt(ctx.Param("id"))
|
||||
auth := &model.AuthorizationV2{}
|
||||
err := ctx.ShouldBindBodyWithJSON(auth)
|
||||
if err != nil {
|
||||
ctx.AbortWithError(http.StatusBadRequest, &myErrors.ApiError{Code: myErrors.ErrInvalidArgument, Data: map[string]any{"err": err}})
|
||||
return
|
||||
}
|
||||
|
||||
auth.Id = authId
|
||||
authV2Service := service.NewAuthorizationV2Service()
|
||||
|
||||
// Check permissions
|
||||
currentUser, _ := acl.GetSessionFromCtx(ctx)
|
||||
if !acl.IsAdmin(currentUser) {
|
||||
ctx.AbortWithError(http.StatusForbidden, &myErrors.ApiError{Code: myErrors.ErrNoPerm, Data: map[string]any{"perm": acl.GRANT}})
|
||||
return
|
||||
}
|
||||
|
||||
// Update the rule
|
||||
err = authV2Service.UpdateRule(ctx, auth)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
ctx.AbortWithError(http.StatusBadRequest, &myErrors.ApiError{Code: myErrors.ErrInvalidArgument, Data: map[string]any{"err": err}})
|
||||
} else {
|
||||
ctx.AbortWithError(http.StatusInternalServerError, &myErrors.ApiError{Code: myErrors.ErrInternal, Data: map[string]any{"err": err}})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, HttpResponse{
|
||||
Data: map[string]any{
|
||||
"id": auth.GetId(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteAuthorizationV2 godoc
|
||||
//
|
||||
// @Tags authorization_v2
|
||||
// @Param id path int true "authorization id"
|
||||
// @Success 200 {object} HttpResponse
|
||||
// @Router /authorization_v2/:id [delete]
|
||||
func (c *Controller) DeleteAuthorizationV2(ctx *gin.Context) {
|
||||
authId := cast.ToInt(ctx.Param("id"))
|
||||
|
||||
authV2Service := service.NewAuthorizationV2Service()
|
||||
|
||||
// Check permissions
|
||||
currentUser, _ := acl.GetSessionFromCtx(ctx)
|
||||
if !acl.IsAdmin(currentUser) {
|
||||
ctx.AbortWithError(http.StatusForbidden, &myErrors.ApiError{Code: myErrors.ErrNoPerm, Data: map[string]any{"perm": acl.GRANT}})
|
||||
return
|
||||
}
|
||||
|
||||
// Delete the rule
|
||||
if err := authV2Service.DeleteRule(ctx, authId); err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
ctx.AbortWithError(http.StatusBadRequest, &myErrors.ApiError{Code: myErrors.ErrInvalidArgument, Data: map[string]any{"err": err}})
|
||||
} else {
|
||||
ctx.AbortWithError(http.StatusInternalServerError, &myErrors.ApiError{Code: myErrors.ErrInternal, Data: map[string]any{"err": err}})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, HttpResponse{
|
||||
Data: map[string]any{
|
||||
"id": authId,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// GetAuthorizationV2 godoc
|
||||
//
|
||||
// @Tags authorization_v2
|
||||
// @Param id path int true "authorization id"
|
||||
// @Success 200 {object} HttpResponse{data=model.AuthorizationV2}
|
||||
// @Router /authorization_v2/:id [get]
|
||||
func (c *Controller) GetAuthorizationV2(ctx *gin.Context) {
|
||||
authId := cast.ToInt(ctx.Param("id"))
|
||||
|
||||
authV2Service := service.NewAuthorizationV2Service()
|
||||
|
||||
// Get the rule
|
||||
auth, err := authV2Service.GetRuleById(ctx, authId)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
ctx.AbortWithError(http.StatusNotFound, &myErrors.ApiError{Code: myErrors.ErrInvalidArgument, Data: map[string]any{"err": err}})
|
||||
} else {
|
||||
ctx.AbortWithError(http.StatusInternalServerError, &myErrors.ApiError{Code: myErrors.ErrInternal, Data: map[string]any{"err": err}})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if auth == nil {
|
||||
ctx.AbortWithError(http.StatusNotFound, &myErrors.ApiError{Code: myErrors.ErrInvalidArgument, Data: map[string]any{"err": "rule not found"}})
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, HttpResponse{
|
||||
Data: auth,
|
||||
})
|
||||
}
|
||||
|
||||
// CloneAuthorizationV2 godoc
|
||||
//
|
||||
// @Tags authorization_v2
|
||||
// @Param id path int true "source authorization id"
|
||||
// @Param body body object true "clone request"
|
||||
// @Success 200 {object} HttpResponse{data=model.AuthorizationV2}
|
||||
// @Router /authorization_v2/:id/clone [post]
|
||||
func (c *Controller) CloneAuthorizationV2(ctx *gin.Context) {
|
||||
sourceId := cast.ToInt(ctx.Param("id"))
|
||||
|
||||
// Parse request body
|
||||
var req struct {
|
||||
Name string `json:"name" binding:"required"`
|
||||
}
|
||||
err := ctx.ShouldBindBodyWithJSON(&req)
|
||||
if err != nil {
|
||||
ctx.AbortWithError(http.StatusBadRequest, &myErrors.ApiError{Code: myErrors.ErrInvalidArgument, Data: map[string]any{"err": err}})
|
||||
return
|
||||
}
|
||||
|
||||
authV2Service := service.NewAuthorizationV2Service()
|
||||
|
||||
// Check permissions
|
||||
currentUser, _ := acl.GetSessionFromCtx(ctx)
|
||||
if !acl.IsAdmin(currentUser) {
|
||||
ctx.AbortWithError(http.StatusForbidden, &myErrors.ApiError{Code: myErrors.ErrNoPerm, Data: map[string]any{"perm": acl.GRANT}})
|
||||
return
|
||||
}
|
||||
|
||||
// Clone the rule
|
||||
clonedRule, err := authV2Service.CloneRule(ctx, sourceId, req.Name)
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
ctx.AbortWithError(http.StatusNotFound, &myErrors.ApiError{Code: myErrors.ErrInvalidArgument, Data: map[string]any{"err": "source rule not found"}})
|
||||
} else {
|
||||
ctx.AbortWithError(http.StatusInternalServerError, &myErrors.ApiError{Code: myErrors.ErrInternal, Data: map[string]any{"err": err}})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, HttpResponse{
|
||||
Data: clonedRule,
|
||||
})
|
||||
}
|
||||
|
||||
// GetAuthorizationsV2 godoc
|
||||
//
|
||||
// @Tags authorization_v2
|
||||
// @Param page_index query int false "page index"
|
||||
// @Param page_size query int false "page size"
|
||||
// @Param enabled query bool false "filter by enabled status"
|
||||
// @Param search query string false "search by name or description"
|
||||
// @Success 200 {object} HttpResponse{data=ListData{list=[]model.AuthorizationV2}}
|
||||
// @Router /authorization_v2 [get]
|
||||
func (c *Controller) GetAuthorizationsV2(ctx *gin.Context) {
|
||||
pageIndex := cast.ToInt(ctx.DefaultQuery("page_index", "1"))
|
||||
pageSize := cast.ToInt(ctx.DefaultQuery("page_size", "20"))
|
||||
enabled := ctx.Query("enabled")
|
||||
search := ctx.Query("search")
|
||||
|
||||
// Check permissions
|
||||
currentUser, _ := acl.GetSessionFromCtx(ctx)
|
||||
if !acl.IsAdmin(currentUser) {
|
||||
ctx.AbortWithError(http.StatusForbidden, &myErrors.ApiError{Code: myErrors.ErrNoPerm, Data: map[string]any{"perm": acl.READ}})
|
||||
return
|
||||
}
|
||||
|
||||
authV2Service := service.NewAuthorizationV2Service()
|
||||
|
||||
// Build query with filters
|
||||
db, err := authV2Service.BuildQuery(ctx)
|
||||
if err != nil {
|
||||
ctx.AbortWithError(http.StatusInternalServerError, &myErrors.ApiError{Code: myErrors.ErrInternal, Data: map[string]any{"err": err}})
|
||||
return
|
||||
}
|
||||
|
||||
// Apply filters
|
||||
if enabled != "" {
|
||||
db = db.Where("enabled = ?", enabled == "true")
|
||||
}
|
||||
if search != "" {
|
||||
db = db.Where("name LIKE ? OR description LIKE ?", "%"+search+"%", "%"+search+"%")
|
||||
}
|
||||
|
||||
// Get total count
|
||||
var count int64
|
||||
if err := db.Count(&count).Error; err != nil {
|
||||
ctx.AbortWithError(http.StatusInternalServerError, &myErrors.ApiError{Code: myErrors.ErrInternal, Data: map[string]any{"err": err}})
|
||||
return
|
||||
}
|
||||
|
||||
// Apply pagination
|
||||
offset := (pageIndex - 1) * pageSize
|
||||
var auths []*model.AuthorizationV2
|
||||
if err := db.Offset(offset).Limit(pageSize).Order("created_at DESC").Find(&auths).Error; err != nil {
|
||||
ctx.AbortWithError(http.StatusInternalServerError, &myErrors.ApiError{Code: myErrors.ErrInternal, Data: map[string]any{"err": err}})
|
||||
return
|
||||
}
|
||||
|
||||
// Convert to slice of any type
|
||||
authsAny := make([]any, 0, len(auths))
|
||||
for _, a := range auths {
|
||||
authsAny = append(authsAny, a)
|
||||
}
|
||||
|
||||
result := &ListData{
|
||||
Count: count,
|
||||
List: authsAny,
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, HttpResponse{
|
||||
Data: result,
|
||||
})
|
||||
}
|
||||
|
||||
// CheckPermissionV2 godoc
|
||||
//
|
||||
// @Tags authorization_v2
|
||||
// @Param request body CheckPermissionRequest true "permission check request"
|
||||
// @Success 200 {object} HttpResponse{data=model.AuthResult}
|
||||
// @Router /authorization_v2/check [post]
|
||||
func (c *Controller) CheckPermissionV2(ctx *gin.Context) {
|
||||
var req CheckPermissionRequest
|
||||
if err := ctx.ShouldBindBodyWithJSON(&req); err != nil {
|
||||
ctx.AbortWithError(http.StatusBadRequest, &myErrors.ApiError{Code: myErrors.ErrInvalidArgument})
|
||||
return
|
||||
}
|
||||
|
||||
result, err := service.DefaultAuthService.CheckPermission(ctx, req.NodeId, req.AssetId, req.AccountId, req.Action)
|
||||
if err != nil {
|
||||
ctx.AbortWithError(http.StatusInternalServerError, &myErrors.ApiError{Code: myErrors.ErrInternal})
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, HttpResponse{Data: result})
|
||||
}
|
||||
|
||||
// CheckPermissionRequest represents a permission check request
|
||||
type CheckPermissionRequest struct {
|
||||
NodeId int `json:"node_id" binding:"gte=0"`
|
||||
AssetId int `json:"asset_id" binding:"gte=0"`
|
||||
AccountId int `json:"account_id" binding:"gte=0"`
|
||||
Action model.AuthAction `json:"action" binding:"required"`
|
||||
}
|
||||
|
||||
// AuthorizationV2 hooks
|
||||
var (
|
||||
authV2PreHooks = []preHook[*model.AuthorizationV2]{
|
||||
func(ctx *gin.Context, data *model.AuthorizationV2) {
|
||||
authV2Service := service.NewAuthorizationV2Service()
|
||||
if err := authV2Service.ValidateRule(ctx, data); err != nil {
|
||||
ctx.AbortWithError(http.StatusBadRequest, &myErrors.ApiError{Code: myErrors.ErrInvalidArgument, Data: map[string]any{"err": err.Error()}})
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
authV2PostHooks = []postHook[*model.AuthorizationV2]{
|
||||
func(ctx *gin.Context, data []*model.AuthorizationV2) {
|
||||
// Add any post-processing logic here
|
||||
},
|
||||
}
|
||||
)
|
Reference in New Issue
Block a user