* fixed: 修复addFunction下前端api.js无法创建的bug。

* feature: 增加严格角色模式

* Update system.vue

* fixed: 多点登录拦截模式下,jwt换票期间不需要拉黑token。

* fixed: 修复使用ast时候产生无意义的换行的问题

* fixed: 修复跨级操作角色权限的越权问题

* feature: 优化严格模式角色鉴权操作。

* fixed: 增加菜单和api设置越权问题的限制

* feature: 增加插件打包前的自动化同步所需菜单和api的功能

* feature: 自动化代码可以默认生成导入导出

* feature: 自动化导入导出对模板进行回滚

* feature: 剔除无用的packfile代码包

* feature: 发布V2.7.3版本公测。

---------

Co-authored-by: task <ms.yangdan@gmail.com>
This commit is contained in:
PiexlMax(奇淼
2024-08-27 13:15:56 +08:00
committed by GitHub
parent 87ced16d63
commit 866fa5643e
52 changed files with 1506 additions and 629 deletions

View File

@@ -67,6 +67,12 @@ func (s *autoCodeHistory) RollBack(ctx context.Context, info request.SysAutoHist
if err != nil {
return err
}
if history.ExportTemplateID != 0 {
err = global.GVA_DB.Delete(&model.SysExportTemplate{}, "id = ?", history.ExportTemplateID).Error
if err != nil {
return err
}
}
if info.DeleteApi {
ids := info.ApiIds(history)
err = ApiServiceApp.DeleteApisByIds(ids)

View File

@@ -1,13 +1,21 @@
package system
import (
"bytes"
"context"
"fmt"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
"github.com/flipped-aurora/gin-vue-admin/server/model/system/request"
"github.com/flipped-aurora/gin-vue-admin/server/utils"
"github.com/flipped-aurora/gin-vue-admin/server/utils/ast"
"github.com/mholt/archiver/v4"
cp "github.com/otiai10/copy"
"github.com/pkg/errors"
"go.uber.org/zap"
"go/parser"
"go/printer"
"go/token"
"io"
"mime/multipart"
"os"
@@ -172,3 +180,70 @@ func (s *autoCodePlugin) PubPlug(plugName string) (zipPath string, err error) {
return filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, fileName), nil
}
func (s *autoCodePlugin) InitMenu(menuInfo request.InitMenu) (err error) {
menuPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", menuInfo.PlugName, "initialize", "menu.go")
src, err := os.ReadFile(menuPath)
if err != nil {
fmt.Println(err)
}
fileSet := token.NewFileSet()
astFile, err := parser.ParseFile(fileSet, "", src, 0)
arrayAst := ast.FindArray(astFile, "model", "SysBaseMenu")
var menus []system.SysBaseMenu
parentMenu := []system.SysBaseMenu{
{
ParentId: 0,
Path: menuInfo.PlugName + "Menu",
Name: menuInfo.PlugName + "Menu",
Hidden: false,
Component: "view/routerHolder.vue",
Sort: 0,
Meta: system.Meta{
Title: menuInfo.ParentMenu,
Icon: "school",
},
},
}
err = global.GVA_DB.Find(&menus, "id in (?)", menuInfo.Menus).Error
if err != nil {
return err
}
menus = append(parentMenu, menus...)
menuExpr := ast.CreateMenuStructAst(menus)
arrayAst.Elts = *menuExpr
var out []byte
bf := bytes.NewBuffer(out)
printer.Fprint(bf, fileSet, astFile)
os.WriteFile(menuPath, bf.Bytes(), 0666)
return nil
}
func (s *autoCodePlugin) InitAPI(apiInfo request.InitApi) (err error) {
apiPath := filepath.Join(global.GVA_CONFIG.AutoCode.Root, global.GVA_CONFIG.AutoCode.Server, "plugin", apiInfo.PlugName, "initialize", "api.go")
src, err := os.ReadFile(apiPath)
if err != nil {
fmt.Println(err)
}
fileSet := token.NewFileSet()
astFile, err := parser.ParseFile(fileSet, "", src, 0)
arrayAst := ast.FindArray(astFile, "model", "SysApi")
var apis []system.SysApi
err = global.GVA_DB.Find(&apis, "id in (?)", apiInfo.APIs).Error
if err != nil {
return err
}
apisExpr := ast.CreateApiStructAst(apis)
arrayAst.Elts = *apisExpr
var out []byte
bf := bytes.NewBuffer(out)
printer.Fprint(bf, fileSet, astFile)
os.WriteFile(apiPath, bf.Bytes(), 0666)
return nil
}

View File

@@ -126,6 +126,14 @@ func (s *autoCodeTemplate) Create(ctx context.Context, info request.AutoCode) er
{SysBaseMenuID: entity.ID, Name: "edit", Desc: "编辑"},
{SysBaseMenuID: entity.ID, Name: "info", Desc: "详情"},
}
if info.HasExcel {
excelBtn := []model.SysBaseMenuBtn{
{SysBaseMenuID: entity.ID, Name: "exportTemplate", Desc: "导出模板"},
{SysBaseMenuID: entity.ID, Name: "exportExcel", Desc: "导出Excel"},
{SysBaseMenuID: entity.ID, Name: "importExcel", Desc: "导入Excel"},
}
entity.MenuBtn = append(entity.MenuBtn, excelBtn...)
}
}
err = global.GVA_DB.WithContext(ctx).Create(&entity).Error
id = entity.ID
@@ -136,6 +144,31 @@ func (s *autoCodeTemplate) Create(ctx context.Context, info request.AutoCode) er
history.MenuID = id
}
if info.HasExcel {
dbName := info.BusinessDB
name := info.Package + "_" + info.StructName
tableName := info.TableName
fieldsMap := make(map[string]string, len(info.Fields))
for _, field := range info.Fields {
if field.Excel {
fieldsMap[field.ColumnName] = field.FieldDesc
}
}
templateInfo, _ := json.Marshal(fieldsMap)
sysExportTemplate := model.SysExportTemplate{
DBName: dbName,
Name: name,
TableName: tableName,
TemplateID: name,
TemplateInfo: string(templateInfo),
}
err = SysExportTemplateServiceApp.CreateSysExportTemplate(&sysExportTemplate)
if err != nil {
return err
}
history.ExportTemplateID = sysExportTemplate.ID
}
// 创建历史记录
history.Templates = templates
history.Injections = make(map[string]string, len(injections))

View File

@@ -233,9 +233,26 @@ func (apiService *ApiService) GetAPIInfoList(api system.SysApi, info request.Pag
//@description: 获取所有的api
//@return: apis []model.SysApi, err error
func (apiService *ApiService) GetAllApis() (apis []system.SysApi, err error) {
func (apiService *ApiService) GetAllApis(authorityID uint) (apis []system.SysApi, err error) {
parentAuthorityID, err := AuthorityServiceApp.GetParentAuthorityID(authorityID)
if err != nil {
return nil, err
}
err = global.GVA_DB.Order("id desc").Find(&apis).Error
return
if parentAuthorityID == 0 || !global.GVA_CONFIG.System.UseStrictAuth {
return
}
paths := CasbinServiceApp.GetPolicyPathByAuthorityId(authorityID)
// 挑选 apis里面的path和method也在paths里面的api
var authApis []system.SysApi
for i := range apis {
for j := range paths {
if paths[j].Path == apis[i].Path && paths[j].Method == apis[i].Method {
authApis = append(authApis, apis[i])
}
}
}
return authApis, err
}
//@author: [piexlmax](https://github.com/piexlmax)

View File

@@ -59,7 +59,7 @@ func (authorityService *AuthorityService) CreateAuthority(auth system.SysAuthori
//@param: copyInfo response.SysAuthorityCopyResponse
//@return: authority system.SysAuthority, err error
func (authorityService *AuthorityService) CopyAuthority(copyInfo response.SysAuthorityCopyResponse) (authority system.SysAuthority, err error) {
func (authorityService *AuthorityService) CopyAuthority(adminAuthorityID uint, copyInfo response.SysAuthorityCopyResponse) (authority system.SysAuthority, err error) {
var authorityBox system.SysAuthority
if !errors.Is(global.GVA_DB.Where("authority_id = ?", copyInfo.Authority.AuthorityId).First(&authorityBox).Error, gorm.ErrRecordNotFound) {
return authority, ErrRoleExistence
@@ -98,7 +98,7 @@ func (authorityService *AuthorityService) CopyAuthority(copyInfo response.SysAut
}
}
paths := CasbinServiceApp.GetPolicyPathByAuthorityId(copyInfo.OldAuthorityId)
err = CasbinServiceApp.UpdateCasbin(copyInfo.Authority.AuthorityId, paths)
err = CasbinServiceApp.UpdateCasbin(adminAuthorityID, copyInfo.Authority.AuthorityId, paths)
if err != nil {
_ = authorityService.DeleteAuthority(&copyInfo.Authority)
}
@@ -183,19 +183,75 @@ func (authorityService *AuthorityService) DeleteAuthority(auth *system.SysAuthor
//@param: info request.PageInfo
//@return: list interface{}, total int64, err error
func (authorityService *AuthorityService) GetAuthorityInfoList(info request.PageInfo) (list interface{}, total int64, err error) {
limit := info.PageSize
offset := info.PageSize * (info.Page - 1)
func (authorityService *AuthorityService) GetAuthorityInfoList(authorityID uint) (list []system.SysAuthority, err error) {
var authority system.SysAuthority
err = global.GVA_DB.Where("authority_id = ?", authorityID).First(&authority).Error
if err != nil {
return nil, err
}
var authorities []system.SysAuthority
db := global.GVA_DB.Model(&system.SysAuthority{})
if err = db.Where("parent_id = ?", "0").Count(&total).Error; total == 0 || err != nil {
return
if global.GVA_CONFIG.System.UseStrictAuth {
// 当开启了严格树形结构后
if *authority.ParentId == 0 {
// 只有顶级角色可以修改自己的权限和以下权限
err = db.Preload("DataAuthorityId").Where("authority_id = ?", authorityID).Find(&authorities).Error
} else {
// 非顶级角色只能修改以下权限
err = db.Debug().Preload("DataAuthorityId").Where("parent_id = ?", authorityID).Find(&authorities).Error
}
} else {
err = db.Preload("DataAuthorityId").Where("parent_id = ?", "0").Find(&authorities).Error
}
var authority []system.SysAuthority
err = db.Limit(limit).Offset(offset).Preload("DataAuthorityId").Where("parent_id = ?", "0").Find(&authority).Error
for k := range authority {
err = authorityService.findChildrenAuthority(&authority[k])
for k := range authorities {
err = authorityService.findChildrenAuthority(&authorities[k])
}
return authority, total, err
return authorities, err
}
//@author: [piexlmax](https://github.com/piexlmax)
//@function: GetAuthorityInfoList
//@description: 分页获取数据
//@param: info request.PageInfo
//@return: list interface{}, total int64, err error
func (authorityService *AuthorityService) GetStructAuthorityList(authorityID uint) (list []uint, err error) {
var auth system.SysAuthority
_ = global.GVA_DB.First(&auth, "authority_id = ?", authorityID).Error
var authorities []system.SysAuthority
err = global.GVA_DB.Preload("DataAuthorityId").Where("parent_id = ?", authorityID).Find(&authorities).Error
if len(authorities) > 0 {
for k := range authorities {
list = append(list, authorities[k].AuthorityId)
_, err = authorityService.GetStructAuthorityList(authorities[k].AuthorityId)
}
}
if *auth.ParentId == 0 {
list = append(list, authorityID)
}
return list, err
}
func (authorityService *AuthorityService) CheckAuthorityIDAuth(authorityID, targetID uint) (err error) {
if !global.GVA_CONFIG.System.UseStrictAuth {
return nil
}
authIDS, err := authorityService.GetStructAuthorityList(authorityID)
if err != nil {
return err
}
hasAuth := false
for _, v := range authIDS {
if v == targetID {
hasAuth = true
break
}
}
if !hasAuth {
return errors.New("您提交的角色ID不合法")
}
return nil
}
//@author: [piexlmax](https://github.com/piexlmax)
@@ -215,7 +271,20 @@ func (authorityService *AuthorityService) GetAuthorityInfo(auth system.SysAuthor
//@param: auth model.SysAuthority
//@return: error
func (authorityService *AuthorityService) SetDataAuthority(auth system.SysAuthority) error {
func (authorityService *AuthorityService) SetDataAuthority(adminAuthorityID uint, auth system.SysAuthority) error {
var checkIDs []uint
checkIDs = append(checkIDs, auth.AuthorityId)
for i := range auth.DataAuthorityId {
checkIDs = append(checkIDs, auth.DataAuthorityId[i].AuthorityId)
}
for i := range checkIDs {
err := authorityService.CheckAuthorityIDAuth(adminAuthorityID, checkIDs[i])
if err != nil {
return err
}
}
var s system.SysAuthority
global.GVA_DB.Preload("DataAuthorityId").First(&s, "authority_id = ?", auth.AuthorityId)
err := global.GVA_DB.Model(&s).Association("DataAuthorityId").Replace(&auth.DataAuthorityId)
@@ -250,3 +319,9 @@ func (authorityService *AuthorityService) findChildrenAuthority(authority *syste
}
return err
}
func (authorityService *AuthorityService) GetParentAuthorityID(authorityID uint) (parentID uint, err error) {
var authority system.SysAuthority
err = global.GVA_DB.Where("authority_id = ?", authorityID).First(&authority).Error
return *authority.ParentId, err
}

View File

@@ -26,7 +26,33 @@ type CasbinService struct{}
var CasbinServiceApp = new(CasbinService)
func (casbinService *CasbinService) UpdateCasbin(AuthorityID uint, casbinInfos []request.CasbinInfo) error {
func (casbinService *CasbinService) UpdateCasbin(adminAuthorityID, AuthorityID uint, casbinInfos []request.CasbinInfo) error {
err := AuthorityServiceApp.CheckAuthorityIDAuth(adminAuthorityID, AuthorityID)
if err != nil {
return err
}
if global.GVA_CONFIG.System.UseStrictAuth {
apis, e := ApiServiceApp.GetAllApis(adminAuthorityID)
if e != nil {
return e
}
for i := range casbinInfos {
hasApi := false
for j := range apis {
if apis[j].Path == casbinInfos[i].Path && apis[j].Method == casbinInfos[i].Method {
hasApi = true
break
}
}
if !hasApi {
return errors.New("存在api不在权限列表中")
}
}
}
authorityId := strconv.Itoa(int(AuthorityID))
casbinService.ClearCasbin(0, authorityId)
rules := [][]string{}

View File

@@ -2,11 +2,11 @@ package system
import (
"errors"
"github.com/flipped-aurora/gin-vue-admin/server/global"
"github.com/flipped-aurora/gin-vue-admin/server/model/common/request"
"github.com/flipped-aurora/gin-vue-admin/server/model/system"
"gorm.io/gorm"
"strconv"
)
//@author: [piexlmax](https://github.com/piexlmax)
@@ -103,14 +103,14 @@ func (menuService *MenuService) getChildrenList(menu *system.SysMenu, treeMap ma
//@description: 获取路由分页
//@return: list interface{}, total int64,err error
func (menuService *MenuService) GetInfoList() (list interface{}, total int64, err error) {
func (menuService *MenuService) GetInfoList(authorityID uint) (list interface{}, err error) {
var menuList []system.SysBaseMenu
treeMap, err := menuService.getBaseMenuTreeMap()
treeMap, err := menuService.getBaseMenuTreeMap(authorityID)
menuList = treeMap[0]
for i := 0; i < len(menuList); i++ {
err = menuService.getBaseChildrenList(&menuList[i], treeMap)
}
return menuList, total, err
return menuList, err
}
//@author: [piexlmax](https://github.com/piexlmax)
@@ -145,10 +145,31 @@ func (menuService *MenuService) AddBaseMenu(menu system.SysBaseMenu) error {
//@description: 获取路由总树map
//@return: treeMap map[string][]system.SysBaseMenu, err error
func (menuService *MenuService) getBaseMenuTreeMap() (treeMap map[uint][]system.SysBaseMenu, err error) {
func (menuService *MenuService) getBaseMenuTreeMap(authorityID uint) (treeMap map[uint][]system.SysBaseMenu, err error) {
parentAuthorityID, err := AuthorityServiceApp.GetParentAuthorityID(authorityID)
if err != nil {
return nil, err
}
var allMenus []system.SysBaseMenu
treeMap = make(map[uint][]system.SysBaseMenu)
err = global.GVA_DB.Order("sort").Preload("MenuBtn").Preload("Parameters").Find(&allMenus).Error
db := global.GVA_DB.Order("sort").Preload("MenuBtn").Preload("Parameters")
// 当开启了严格的树角色并且父角色不为0时需要进行菜单筛选
if global.GVA_CONFIG.System.UseStrictAuth && parentAuthorityID != 0 {
var authorityMenus []system.SysAuthorityMenu
err = global.GVA_DB.Where("sys_authority_authority_id = ?", authorityID).Find(&authorityMenus).Error
if err != nil {
return nil, err
}
var menuIds []string
for i := range authorityMenus {
menuIds = append(menuIds, authorityMenus[i].MenuId)
}
db = db.Where("id in (?)", menuIds)
}
err = db.Find(&allMenus).Error
for _, v := range allMenus {
treeMap[v.ParentId] = append(treeMap[v.ParentId], v)
}
@@ -160,8 +181,8 @@ func (menuService *MenuService) getBaseMenuTreeMap() (treeMap map[uint][]system.
//@description: 获取基础路由树
//@return: menus []system.SysBaseMenu, err error
func (menuService *MenuService) GetBaseMenuTree() (menus []system.SysBaseMenu, err error) {
treeMap, err := menuService.getBaseMenuTreeMap()
func (menuService *MenuService) GetBaseMenuTree(authorityID uint) (menus []system.SysBaseMenu, err error) {
treeMap, err := menuService.getBaseMenuTreeMap(authorityID)
menus = treeMap[0]
for i := 0; i < len(menus); i++ {
err = menuService.getBaseChildrenList(&menus[i], treeMap)
@@ -175,10 +196,45 @@ func (menuService *MenuService) GetBaseMenuTree() (menus []system.SysBaseMenu, e
//@param: menus []model.SysBaseMenu, authorityId string
//@return: err error
func (menuService *MenuService) AddMenuAuthority(menus []system.SysBaseMenu, authorityId uint) (err error) {
func (menuService *MenuService) AddMenuAuthority(menus []system.SysBaseMenu, adminAuthorityID, authorityId uint) (err error) {
var auth system.SysAuthority
auth.AuthorityId = authorityId
auth.SysBaseMenus = menus
err = AuthorityServiceApp.CheckAuthorityIDAuth(adminAuthorityID, authorityId)
if err != nil {
return err
}
var authority system.SysAuthority
_ = global.GVA_DB.First(&authority, "authority_id = ?", adminAuthorityID).Error
var menuIds []string
// 当开启了严格的树角色并且父角色不为0时需要进行菜单筛选
if global.GVA_CONFIG.System.UseStrictAuth && *authority.ParentId != 0 {
var authorityMenus []system.SysAuthorityMenu
err = global.GVA_DB.Where("sys_authority_authority_id = ?", adminAuthorityID).Find(&authorityMenus).Error
if err != nil {
return err
}
for i := range authorityMenus {
menuIds = append(menuIds, authorityMenus[i].MenuId)
}
for i := range menus {
hasMenu := false
for j := range menuIds {
idStr := strconv.Itoa(int(menus[i].ID))
if idStr == menuIds[j] {
hasMenu = true
}
}
if !hasMenu {
return errors.New("添加失败,请勿跨级操作")
}
}
}
err = AuthorityServiceApp.SetMenuAuthority(&auth)
return err
}

View File

@@ -152,7 +152,7 @@ func (userService *UserService) SetUserAuthority(id uint, authorityId uint) (err
//@param: id uint, authorityIds []string
//@return: err error
func (userService *UserService) SetUserAuthorities(id uint, authorityIds []uint) (err error) {
func (userService *UserService) SetUserAuthorities(adminAuthorityID, id uint, authorityIds []uint) (err error) {
return global.GVA_DB.Transaction(func(tx *gorm.DB) error {
var user system.SysUser
TxErr := tx.Where("id = ?", id).First(&user).Error
@@ -166,6 +166,10 @@ func (userService *UserService) SetUserAuthorities(id uint, authorityIds []uint)
}
var useAuthority []system.SysUserAuthority
for _, v := range authorityIds {
e := AuthorityServiceApp.CheckAuthorityIDAuth(adminAuthorityID, v)
if e != nil {
return e
}
useAuthority = append(useAuthority, system.SysUserAuthority{
SysUserId: id, SysAuthorityAuthorityId: v,
})