mirror of
https://github.com/veops/oneterm.git
synced 2025-10-24 07:43:20 +08:00
feat(api): permission
This commit is contained in:
@@ -45,7 +45,7 @@ func migrateNode() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
nodes := make([]*model.Node, 0)
|
nodes := make([]*model.Node, 0)
|
||||||
if err = mysql.DB.Model(&nodes).Where("resource_id IS NULL").Find(&nodes).Error; err != nil {
|
if err = mysql.DB.Model(&nodes).Where("resource_id=0").Find(&nodes).Error; err != nil {
|
||||||
logger.L().Fatal("get nodes failed", zap.Error(err))
|
logger.L().Fatal("get nodes failed", zap.Error(err))
|
||||||
}
|
}
|
||||||
eg := errgroup.Group{}
|
eg := errgroup.Group{}
|
||||||
|
|||||||
@@ -3,13 +3,11 @@ package acl
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
"github.com/spf13/cast"
|
"github.com/spf13/cast"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
|
|
||||||
redis "github.com/veops/oneterm/cache"
|
|
||||||
"github.com/veops/oneterm/conf"
|
"github.com/veops/oneterm/conf"
|
||||||
"github.com/veops/oneterm/remote"
|
"github.com/veops/oneterm/remote"
|
||||||
)
|
)
|
||||||
@@ -19,10 +17,10 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func GetRoleResources(ctx context.Context, rid int, resourceTypeId string) (res []*Resource, err error) {
|
func GetRoleResources(ctx context.Context, rid int, resourceTypeId string) (res []*Resource, err error) {
|
||||||
k := fmt.Sprintf(kFmtResources, resourceTypeId, rid)
|
// k := fmt.Sprintf(kFmtResources, resourceTypeId, rid)
|
||||||
if err = redis.Get(ctx, k, &res); err == nil {
|
// if err = redis.Get(ctx, k, &res); err == nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
token, err := remote.GetAclToken(ctx)
|
token, err := remote.GetAclToken(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -46,7 +44,7 @@ func GetRoleResources(ctx context.Context, rid int, resourceTypeId string) (res
|
|||||||
|
|
||||||
res = data.Resources
|
res = data.Resources
|
||||||
|
|
||||||
redis.SetEx(ctx, k, res, time.Minute)
|
// redis.SetEx(ctx, k, res, time.Minute)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,8 @@ package controller
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
@@ -14,7 +12,6 @@ import (
|
|||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"github.com/veops/oneterm/acl"
|
"github.com/veops/oneterm/acl"
|
||||||
redis "github.com/veops/oneterm/cache"
|
|
||||||
"github.com/veops/oneterm/conf"
|
"github.com/veops/oneterm/conf"
|
||||||
mysql "github.com/veops/oneterm/db"
|
mysql "github.com/veops/oneterm/db"
|
||||||
"github.com/veops/oneterm/model"
|
"github.com/veops/oneterm/model"
|
||||||
@@ -166,12 +163,12 @@ func (c *Controller) GetAccounts(ctx *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetAccountIdsByAuthorization(ctx *gin.Context) (ids []int, err error) {
|
func GetAccountIdsByAuthorization(ctx *gin.Context) (ids []int, err error) {
|
||||||
currentUser, _ := acl.GetSessionFromCtx(ctx)
|
// currentUser, _ := acl.GetSessionFromCtx(ctx)
|
||||||
|
|
||||||
k := fmt.Sprintf(kFmtAccountIds, currentUser.GetUid())
|
// k := fmt.Sprintf(kFmtAccountIds, currentUser.GetUid())
|
||||||
if err = redis.Get(ctx, k, &ids); err == nil {
|
// if err = redis.Get(ctx, k, &ids); err == nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
assetIds, err := GetAssetIdsByAuthorization(ctx)
|
assetIds, err := GetAssetIdsByAuthorization(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -187,7 +184,7 @@ func GetAccountIdsByAuthorization(ctx *gin.Context) (ids []int, err error) {
|
|||||||
_, _, accountIds := getIdsByAuthorizationIds(ctx)
|
_, _, accountIds := getIdsByAuthorizationIds(ctx)
|
||||||
ids = lo.Uniq(append(ids, accountIds...))
|
ids = lo.Uniq(append(ids, accountIds...))
|
||||||
|
|
||||||
redis.SetEx(ctx, k, ids, time.Minute)
|
// redis.SetEx(ctx, k, ids, time.Minute)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
@@ -13,7 +12,6 @@ import (
|
|||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
|
||||||
"github.com/veops/oneterm/acl"
|
"github.com/veops/oneterm/acl"
|
||||||
redis "github.com/veops/oneterm/cache"
|
|
||||||
"github.com/veops/oneterm/conf"
|
"github.com/veops/oneterm/conf"
|
||||||
mysql "github.com/veops/oneterm/db"
|
mysql "github.com/veops/oneterm/db"
|
||||||
"github.com/veops/oneterm/logger"
|
"github.com/veops/oneterm/logger"
|
||||||
@@ -154,12 +152,21 @@ func assetPostHookAuth(ctx *gin.Context, data []*model.Asset) {
|
|||||||
if acl.IsAdmin(currentUser) {
|
if acl.IsAdmin(currentUser) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
info := cast.ToBool(ctx.Query("info"))
|
||||||
|
noInfoIds := make([]int, 0)
|
||||||
|
if !info {
|
||||||
|
t := mysql.DB.Model(&model.Asset{})
|
||||||
|
assetResIds, _ := acl.GetRoleResourceIds(ctx, currentUser.GetRid(), conf.RESOURCE_ASSET)
|
||||||
|
t, _ = handleAssetIds(ctx, t, assetResIds)
|
||||||
|
t.Pluck("id", &noInfoIds)
|
||||||
|
}
|
||||||
|
|
||||||
authorizationIds, _ := ctx.Value(kAuthorizationIds).([]*model.AuthorizationIds)
|
authorizationIds, _ := ctx.Value(kAuthorizationIds).([]*model.AuthorizationIds)
|
||||||
nodeIds, _, accountIds := getIdsByAuthorizationIds(ctx)
|
nodeIds, _, accountIds := getIdsByAuthorizationIds(ctx)
|
||||||
nodeIds, _ = handleSelfChild(ctx, nodeIds...)
|
nodeIds, _ = handleSelfChild(ctx, nodeIds...)
|
||||||
|
|
||||||
for _, a := range data {
|
for _, a := range data {
|
||||||
if lo.Contains(nodeIds, a.ParentId) {
|
if lo.Contains(nodeIds, a.ParentId) || lo.Contains(noInfoIds, a.Id) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if lo.ContainsBy(authorizationIds, func(item *model.AuthorizationIds) bool {
|
if lo.ContainsBy(authorizationIds, func(item *model.AuthorizationIds) bool {
|
||||||
@@ -203,7 +210,7 @@ func handleParentId(ctx context.Context, parentId int) (pids []int, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetAssetIdsByAuthorization(ctx *gin.Context) (ids []int, err error) {
|
func GetAssetIdsByAuthorization(ctx *gin.Context) (ids []int, err error) {
|
||||||
currentUser, _ := acl.GetSessionFromCtx(ctx)
|
// currentUser, _ := acl.GetSessionFromCtx(ctx)
|
||||||
|
|
||||||
authIds, err := getAuthorizationIds(ctx)
|
authIds, err := getAuthorizationIds(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -211,10 +218,10 @@ func GetAssetIdsByAuthorization(ctx *gin.Context) (ids []int, err error) {
|
|||||||
}
|
}
|
||||||
ctx.Set(kAuthorizationIds, authIds)
|
ctx.Set(kAuthorizationIds, authIds)
|
||||||
|
|
||||||
k := fmt.Sprintf(kFmtAssetIds, currentUser.GetUid())
|
// k := fmt.Sprintf(kFmtAssetIds, currentUser.GetUid())
|
||||||
if err = redis.Get(ctx, k, &ids); err == nil {
|
// if err = redis.Get(ctx, k, &ids); err == nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
nodeIds, ids, accountIds := getIdsByAuthorizationIds(ctx)
|
nodeIds, ids, accountIds := getIdsByAuthorizationIds(ctx)
|
||||||
|
|
||||||
@@ -231,21 +238,22 @@ func GetAssetIdsByAuthorization(ctx *gin.Context) (ids []int, err error) {
|
|||||||
}
|
}
|
||||||
ids = lo.Uniq(append(ids, tmp...))
|
ids = lo.Uniq(append(ids, tmp...))
|
||||||
|
|
||||||
redis.SetEx(ctx, k, ids, time.Minute)
|
// redis.SetEx(ctx, k, ids, time.Minute)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func getIdsByAuthorizationIds(ctx context.Context) (nodeIds, assetIds, accountIds []int) {
|
func getIdsByAuthorizationIds(ctx *gin.Context) (nodeIds, assetIds, accountIds []int) {
|
||||||
authIds, _ := ctx.Value(kAuthorizationIds).([]*model.AuthorizationIds)
|
authIds, _ := ctx.Value(kAuthorizationIds).([]*model.AuthorizationIds)
|
||||||
|
info := cast.ToBool(ctx.Query("info"))
|
||||||
for _, a := range authIds {
|
for _, a := range authIds {
|
||||||
if a.NodeId != 0 && a.AssetId == 0 && a.AccountId == 0 {
|
if a.NodeId != 0 && a.AssetId == 0 && a.AccountId == 0 {
|
||||||
nodeIds = append(nodeIds, a.NodeId)
|
nodeIds = append(nodeIds, a.NodeId)
|
||||||
}
|
}
|
||||||
if a.AssetId != 0 && a.NodeId == 0 && a.AccountId == 0 {
|
if a.AssetId != 0 && a.NodeId == 0 && (info || a.AccountId == 0) {
|
||||||
assetIds = append(assetIds, a.AssetId)
|
assetIds = append(assetIds, a.AssetId)
|
||||||
}
|
}
|
||||||
if a.AccountId != 0 && a.AssetId == 0 && a.NodeId == 0 {
|
if a.AccountId != 0 && a.AssetId == 0 && (info || a.NodeId == 0) {
|
||||||
accountIds = append(accountIds, a.AccountId)
|
accountIds = append(accountIds, a.AccountId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -253,6 +261,11 @@ func getIdsByAuthorizationIds(ctx context.Context) (nodeIds, assetIds, accountId
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getAssetIdsByNodeAccount(ctx context.Context, nodeIds, accountIds []int) (assetIds []int, err error) {
|
func getAssetIdsByNodeAccount(ctx context.Context, nodeIds, accountIds []int) (assetIds []int, err error) {
|
||||||
err = mysql.DB.WithContext(ctx).Model(&model.Asset{}).Where("parent_id IN?", nodeIds).Or("JSON_KEYS(authorization) IN ?", accountIds).Pluck("id", &assetIds).Error
|
db := mysql.DB.WithContext(ctx).Model(&model.Asset{}).Where("parent_id IN ?", nodeIds)
|
||||||
|
if len(accountIds) > 0 {
|
||||||
|
db = db.Or(fmt.Sprintf("JSON_CONTAINS_PATH(authorization,'one',%s)",
|
||||||
|
strings.Join(lo.Map(accountIds, func(id int, _ int) string { return fmt.Sprintf("'$.\"%d\"'", id) }), ",")))
|
||||||
|
}
|
||||||
|
err = db.Pluck("id", &assetIds).Error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
@@ -15,7 +14,6 @@ import (
|
|||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
|
||||||
"github.com/veops/oneterm/acl"
|
"github.com/veops/oneterm/acl"
|
||||||
redis "github.com/veops/oneterm/cache"
|
|
||||||
"github.com/veops/oneterm/conf"
|
"github.com/veops/oneterm/conf"
|
||||||
mysql "github.com/veops/oneterm/db"
|
mysql "github.com/veops/oneterm/db"
|
||||||
"github.com/veops/oneterm/logger"
|
"github.com/veops/oneterm/logger"
|
||||||
@@ -123,20 +121,14 @@ func (c *Controller) GetAuthorizations(ctx *gin.Context) {
|
|||||||
auth := &model.Authorization{}
|
auth := &model.Authorization{}
|
||||||
db := mysql.DB.Model(auth)
|
db := mysql.DB.Model(auth)
|
||||||
for _, k := range []string{"node_id", "asset_id", "account_id"} {
|
for _, k := range []string{"node_id", "asset_id", "account_id"} {
|
||||||
q, ok := ctx.GetQuery(k)
|
q, _ := ctx.GetQuery(k)
|
||||||
if ok {
|
|
||||||
db = db.Where(fmt.Sprintf("%s=?", k), cast.ToInt(q))
|
db = db.Where(fmt.Sprintf("%s=?", k), cast.ToInt(q))
|
||||||
switch k {
|
|
||||||
case "node_id":
|
|
||||||
auth.NodeId = cast.ToInt(q)
|
|
||||||
case "asset_id":
|
|
||||||
auth.AssetId = cast.ToInt(q)
|
|
||||||
case "account_id":
|
|
||||||
auth.AccountId = cast.ToInt(q)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
db = db.Where("?=0", k)
|
|
||||||
}
|
}
|
||||||
|
t := db.Session(&gorm.Session{})
|
||||||
|
|
||||||
|
if err := t.First(&auth).Error; err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
ctx.AbortWithError(http.StatusInternalServerError, &ApiError{Code: ErrInternal, Data: map[string]any{"err": err}})
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !hasPermAuthorization(ctx, auth, acl.GRANT) {
|
if !hasPermAuthorization(ctx, auth, acl.GRANT) {
|
||||||
@@ -213,7 +205,11 @@ func hasPermAuthorization(ctx context.Context, auth *model.Authorization, action
|
|||||||
currentUser, _ := acl.GetSessionFromCtx(ctx)
|
currentUser, _ := acl.GetSessionFromCtx(ctx)
|
||||||
|
|
||||||
if ok = acl.IsAdmin(currentUser); ok {
|
if ok = acl.IsAdmin(currentUser); ok {
|
||||||
return true
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok = auth == nil || auth.Id == 0; ok {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeIds, assetIds, accountIds, err := getGrantNodeAssetAccoutIds(ctx, action)
|
nodeIds, assetIds, accountIds, err := getGrantNodeAssetAccoutIds(ctx, action)
|
||||||
@@ -233,41 +229,51 @@ func hasPermAuthorization(ctx context.Context, auth *model.Authorization, action
|
|||||||
}
|
}
|
||||||
|
|
||||||
func getAuthsByAsset(t *model.Asset) (data []*model.Authorization, err error) {
|
func getAuthsByAsset(t *model.Asset) (data []*model.Authorization, err error) {
|
||||||
db := mysql.DB.Model(data)
|
err = mysql.DB.Model(data).Where("asset_id=? AND account_id IN ? AND node_id=0", t.Id, lo.Without(lo.Keys(t.Authorization), 0)).Find(&data).Error
|
||||||
for accountId := range t.Authorization {
|
|
||||||
db = db.Or("asset_id=? AND account_id=? AND node_id=NULL", t.Id, accountId)
|
|
||||||
}
|
|
||||||
err = db.Find(&data).Error
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleAuthorization(ctx *gin.Context, tx *gorm.DB, action int, asset *model.Asset, auths ...*model.Authorization) (err error) {
|
func handleAuthorization(ctx *gin.Context, tx *gorm.DB, action int, asset *model.Asset, auths ...*model.Authorization) (err error) {
|
||||||
currentUser, _ := acl.GetSessionFromCtx(ctx)
|
currentUser, _ := acl.GetSessionFromCtx(ctx)
|
||||||
|
|
||||||
eg := &errgroup.Group{}
|
eg := &errgroup.Group{}
|
||||||
|
|
||||||
if asset != nil {
|
if asset != nil && asset.Id > 0 {
|
||||||
if action == model.ACTION_UPDATE {
|
|
||||||
var pres []*model.Authorization
|
var pres []*model.Authorization
|
||||||
pres, err = getAuthsByAsset(asset)
|
pres, err = getAuthsByAsset(asset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, pre := range pres {
|
switch action {
|
||||||
p := pre
|
case model.ACTION_CREATE:
|
||||||
if _, ok := asset.Authorization[p.AccountId]; ok {
|
|
||||||
auths = append(auths, p)
|
|
||||||
} else {
|
|
||||||
eg.Go(func() error {
|
|
||||||
return acl.DeleteResource(ctx, currentUser.GetUid(), p.ResourceId)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
auths = lo.Map(lo.Keys(asset.Authorization), func(id int, _ int) *model.Authorization {
|
auths = lo.Map(lo.Keys(asset.Authorization), func(id int, _ int) *model.Authorization {
|
||||||
return &model.Authorization{AssetId: asset.Id, AccountId: id, Rids: asset.Authorization[id]}
|
return &model.Authorization{AssetId: asset.Id, AccountId: id, Rids: asset.Authorization[id]}
|
||||||
})
|
})
|
||||||
|
case model.ACTION_DELETE:
|
||||||
|
auths = pres
|
||||||
|
case model.ACTION_UPDATE:
|
||||||
|
for _, pre := range pres {
|
||||||
|
p := pre
|
||||||
|
if v, ok := asset.Authorization[p.AccountId]; ok {
|
||||||
|
p.Rids = v
|
||||||
|
auths = append(auths, p)
|
||||||
|
} else {
|
||||||
|
eg.Go(func() (err error) {
|
||||||
|
if err = acl.DeleteResource(ctx, currentUser.GetUid(), p.ResourceId); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = mysql.DB.Model(p).Where("id=?", p.Id).Delete(p).Error; err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
preAccountsIds := lo.Map(pres, func(p *model.Authorization, _ int) int { return p.AccountId })
|
||||||
|
for k, v := range asset.Authorization {
|
||||||
|
if !lo.Contains(preAccountsIds, k) {
|
||||||
|
auths = append(auths, &model.Authorization{AssetId: asset.Id, AccountId: k, Rids: v})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,9 +301,19 @@ func handleAuthorization(ctx *gin.Context, tx *gorm.DB, action int, asset *model
|
|||||||
case model.ACTION_UPDATE:
|
case model.ACTION_UPDATE:
|
||||||
eg.Go(func() (err error) {
|
eg.Go(func() (err error) {
|
||||||
pre := &model.Authorization{}
|
pre := &model.Authorization{}
|
||||||
if err = mysql.DB.First(pre, auth.GetId()).Error; err != nil {
|
if err = mysql.DB.Where("id=?", auth.GetId()).First(pre).Error; err != nil {
|
||||||
|
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
resourceId := 0
|
||||||
|
if resourceId, err = acl.CreateGrantAcl(ctx, currentUser, conf.RESOURCE_AUTHORIZATION, auth.GetName()); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
auth.ResourceId = resourceId
|
||||||
|
if err = tx.Create(auth).Error; err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
revokeRids := lo.Without(pre.Rids, auth.Rids...)
|
revokeRids := lo.Without(pre.Rids, auth.Rids...)
|
||||||
if len(revokeRids) > 0 {
|
if len(revokeRids) > 0 {
|
||||||
if err = acl.BatchRevokeRoleResource(ctx, currentUser.GetUid(), revokeRids, auth.ResourceId, []string{acl.READ}); err != nil {
|
if err = acl.BatchRevokeRoleResource(ctx, currentUser.GetUid(), revokeRids, auth.ResourceId, []string{acl.READ}); err != nil {
|
||||||
@@ -323,17 +339,17 @@ func handleAuthorization(ctx *gin.Context, tx *gorm.DB, action int, asset *model
|
|||||||
func getAuthorizations(ctx *gin.Context) (res []*acl.Resource, err error) {
|
func getAuthorizations(ctx *gin.Context) (res []*acl.Resource, err error) {
|
||||||
currentUser, _ := acl.GetSessionFromCtx(ctx)
|
currentUser, _ := acl.GetSessionFromCtx(ctx)
|
||||||
|
|
||||||
k := fmt.Sprintf(kFmtAuthorizations, currentUser.GetUid())
|
// k := fmt.Sprintf(kFmtAuthorizations, currentUser.GetUid())
|
||||||
if err = redis.Get(ctx, k, &res); err == nil {
|
// if err = redis.Get(ctx, k, &res); err == nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
res, err = acl.GetRoleResources(ctx, currentUser.Acl.Rid, conf.RESOURCE_AUTHORIZATION)
|
res, err = acl.GetRoleResources(ctx, currentUser.Acl.Rid, conf.RESOURCE_AUTHORIZATION)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
redis.SetEx(ctx, k, res, time.Minute)
|
// redis.SetEx(ctx, k, res, time.Minute)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -367,12 +383,13 @@ func hasAuthorization(ctx *gin.Context, sess *gsession.Session) (ok bool) {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
k := fmt.Sprintf(kFmtHasAuthorization, currentUser.GetUid(), sess.AccountId, sess.AssetId)
|
// k := fmt.Sprintf(kFmtHasAuthorization, currentUser.GetUid(), sess.AccountId, sess.AssetId)
|
||||||
if err := redis.Get(ctx, k, &ok); err == nil {
|
// if err := redis.Get(ctx, k, &ok); err == nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
redis.SetEx(ctx, k, ok, time.Minute)
|
// redis.SetEx(ctx, k, ok, time.Minute)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
if ok = acl.IsAdmin(currentUser); ok {
|
if ok = acl.IsAdmin(currentUser); ok {
|
||||||
@@ -380,7 +397,7 @@ func hasAuthorization(ctx *gin.Context, sess *gsession.Session) (ok bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if sess.Session.Asset == nil {
|
if sess.Session.Asset == nil {
|
||||||
if err := mysql.DB.Model(sess.Session.Asset).First(&sess.Session.Asset, sess.AssetId).Error; err != nil {
|
if err := mysql.DB.Model(sess.Session.Asset).Where("id=?", sess.AssetId).First(&sess.Session.Asset).Error; err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -389,25 +406,25 @@ func hasAuthorization(ctx *gin.Context, sess *gsession.Session) (ok bool) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if lo.ContainsBy(authIds, func(item *model.AuthorizationIds) bool {
|
if ok = lo.ContainsBy(authIds, func(item *model.AuthorizationIds) bool {
|
||||||
return item.NodeId == 0 && item.AssetId == sess.AssetId && item.AccountId == sess.AccountId
|
return item.NodeId == 0 && item.AssetId == sess.AssetId && item.AccountId == sess.AccountId
|
||||||
}) {
|
}); ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ctx.Set(kAuthorizationIds, authIds)
|
ctx.Set(kAuthorizationIds, authIds)
|
||||||
|
|
||||||
parentNodeIds, assetIds, accountIds := getIdsByAuthorizationIds(ctx)
|
nodeIds, assetIds, accountIds := getIdsByAuthorizationIds(ctx)
|
||||||
tmp, err := handleSelfChild(ctx, parentNodeIds...)
|
tmp, err := handleSelfChild(ctx, nodeIds...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.L().Error("", zap.Error(err))
|
logger.L().Error("", zap.Error(err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
parentNodeIds = append(parentNodeIds, tmp...)
|
nodeIds = append(nodeIds, tmp...)
|
||||||
if ok = lo.Contains(parentNodeIds, sess.Session.Asset.ParentId) || lo.Contains(assetIds, sess.AssetId) || lo.Contains(accountIds, sess.AccountId); ok {
|
if ok = lo.Contains(nodeIds, sess.Session.Asset.ParentId) || lo.Contains(assetIds, sess.AssetId) || lo.Contains(accountIds, sess.AccountId); ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ids, err := getAssetIdsByNodeAccount(ctx, parentNodeIds, accountIds)
|
ids, err := getAssetIdsByNodeAccount(ctx, nodeIds, accountIds)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.L().Error("", zap.Error(err))
|
logger.L().Error("", zap.Error(err))
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -314,12 +314,10 @@ func DoConnect(ctx *gin.Context, ws *websocket.Conn) (sess *gsession.Session, er
|
|||||||
|
|
||||||
if !checkTime(asset.AccessAuth) {
|
if !checkTime(asset.AccessAuth) {
|
||||||
err = &ApiError{Code: ErrAccessTime}
|
err = &ApiError{Code: ErrAccessTime}
|
||||||
ctx.AbortWithError(http.StatusBadRequest, err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !hasAuthorization(ctx, sess) {
|
if !hasAuthorization(ctx, sess) {
|
||||||
err = &ApiError{Code: ErrUnauthorized}
|
err = &ApiError{Code: ErrUnauthorized}
|
||||||
ctx.AbortWithError(http.StatusForbidden, err)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -334,7 +332,7 @@ func DoConnect(ctx *gin.Context, ws *websocket.Conn) (sess *gsession.Session, er
|
|||||||
|
|
||||||
if err = <-sess.Chans.ErrChan; err != nil {
|
if err = <-sess.Chans.ErrChan; err != nil {
|
||||||
logger.L().Error("failed to connect", zap.Error(err))
|
logger.L().Error("failed to connect", zap.Error(err))
|
||||||
ctx.AbortWithError(http.StatusInternalServerError, &ApiError{Code: ErrConnectServer, Data: map[string]any{"err": err}})
|
err = &ApiError{Code: ErrConnectServer, Data: map[string]any{"err": err}}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -374,6 +372,12 @@ func connectSsh(ctx *gin.Context, sess *gsession.Session, asset *model.Asset, ac
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if asset.GatewayId != 0 {
|
||||||
|
if err = <-ggateway.GetGatewayBySessionId(sess.SessionId).Opened; err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sshSess, err := sshCli.NewSession()
|
sshSess, err := sshCli.NewSession()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.L().Error("ssh session create failed", zap.Error(err))
|
logger.L().Error("ssh session create failed", zap.Error(err))
|
||||||
|
|||||||
@@ -253,6 +253,9 @@ func doUpdate[T model.Model](ctx *gin.Context, needAcl bool, md T, resourceType
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if needAcl {
|
if needAcl {
|
||||||
|
md.SetResourceId(old.GetResourceId())
|
||||||
|
fmt.Printf("%+v\n", old)
|
||||||
|
fmt.Printf("%+v\n", md)
|
||||||
if !hasPerm(ctx, md, resourceType, acl.WRITE) {
|
if !hasPerm(ctx, md, resourceType, acl.WRITE) {
|
||||||
ctx.AbortWithError(http.StatusForbidden, &ApiError{Code: ErrNoPerm, Data: map[string]any{"perm": acl.WRITE}})
|
ctx.AbortWithError(http.StatusForbidden, &ApiError{Code: ErrNoPerm, Data: map[string]any{"perm": acl.WRITE}})
|
||||||
return
|
return
|
||||||
@@ -488,7 +491,7 @@ func hasPerm[T model.Model](ctx context.Context, md T, resourceTypeName, action
|
|||||||
case *model.Asset:
|
case *model.Asset:
|
||||||
pids, _ = handleSelfParent(ctx, t.ParentId)
|
pids, _ = handleSelfParent(ctx, t.ParentId)
|
||||||
case *model.Node:
|
case *model.Node:
|
||||||
pids, _ = handleSelfParent(ctx, t.ParentId)
|
pids, _ = handleSelfParent(ctx, t.Id)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(pids) > 0 {
|
if len(pids) > 0 {
|
||||||
@@ -574,6 +577,12 @@ func handleAcl[T any](ctx *gin.Context, dbFind *gorm.DB, resourceType string) (d
|
|||||||
db, err = handleNodeIds(ctx, dbFind, resIds)
|
db, err = handleNodeIds(ctx, dbFind, resIds)
|
||||||
case *model.Asset:
|
case *model.Asset:
|
||||||
db, err = handleAssetIds(ctx, dbFind, resIds)
|
db, err = handleAssetIds(ctx, dbFind, resIds)
|
||||||
|
case *model.Account:
|
||||||
|
db, err = handleAccountIds(ctx, dbFind, resIds)
|
||||||
|
case *model.Gateway:
|
||||||
|
db = dbFind
|
||||||
|
case *model.Command:
|
||||||
|
db = dbFind
|
||||||
default:
|
default:
|
||||||
db = dbFind.Where("resource_id IN ?", resIds)
|
db = dbFind.Where("resource_id IN ?", resIds)
|
||||||
}
|
}
|
||||||
@@ -594,7 +603,7 @@ func handleNodeIds(ctx *gin.Context, dbFind *gorm.DB, resIds []int) (db *gorm.DB
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
assetResIds, err := acl.GetRoleResources(ctx, currentUser.GetRid(), conf.RESOURCE_ASSET)
|
assetResIds, err := acl.GetRoleResourceIds(ctx, currentUser.GetRid(), conf.RESOURCE_ASSET)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -637,3 +646,24 @@ func handleAssetIds(ctx *gin.Context, dbFind *gorm.DB, resIds []int) (db *gorm.D
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleAccountIds(ctx *gin.Context, dbFind *gorm.DB, resIds []int) (db *gorm.DB, err error) {
|
||||||
|
currentUser, _ := acl.GetSessionFromCtx(ctx)
|
||||||
|
|
||||||
|
assetResIds, err := acl.GetRoleResourceIds(ctx, currentUser.GetRid(), conf.RESOURCE_ASSET)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t, _ := handleAssetIds(ctx, mysql.DB.Model(&model.Asset{}), assetResIds)
|
||||||
|
ss := make([]model.Slice[string], 0)
|
||||||
|
if err = t.Pluck("JSON_KEYS(authorization)", &ss).Error; err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ids := lo.Uniq(lo.Map(lo.Flatten(ss), func(s string, _ int) int { return cast.ToInt(s) }))
|
||||||
|
|
||||||
|
d := mysql.DB.Where("resource_id IN ?", resIds).Or("id IN ?", ids)
|
||||||
|
|
||||||
|
db = dbFind.Where(d)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package controller
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -114,9 +113,6 @@ func (c *Controller) GetNodes(ctx *gin.Context) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if ids, err = handleSelfChild(ctx, ids...); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if ids, err = handleSelfParent(ctx, ids...); err != nil {
|
if ids, err = handleSelfParent(ctx, ids...); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -149,15 +145,26 @@ func nodePreHookCheckCycle(ctx *gin.Context, data *model.Node) {
|
|||||||
|
|
||||||
func nodePostHookCountAsset(ctx *gin.Context, data []*model.Node) {
|
func nodePostHookCountAsset(ctx *gin.Context, data []*model.Node) {
|
||||||
currentUser, _ := acl.GetSessionFromCtx(ctx)
|
currentUser, _ := acl.GetSessionFromCtx(ctx)
|
||||||
isAdmin := acl.IsAdmin(currentUser)
|
|
||||||
assets := make([]*model.AssetIdPid, 0)
|
assets := make([]*model.AssetIdPid, 0)
|
||||||
db := mysql.DB.Model(&model.Asset{})
|
db := mysql.DB.Model(&model.Asset{})
|
||||||
if !isAdmin {
|
if !acl.IsAdmin(currentUser) {
|
||||||
|
info := cast.ToBool(ctx.Query("info"))
|
||||||
|
if info {
|
||||||
assetIds, err := GetAssetIdsByAuthorization(ctx)
|
assetIds, err := GetAssetIdsByAuthorization(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
db = db.Where("id IN ?", assetIds)
|
db = db.Where("id IN ?", assetIds)
|
||||||
|
} else {
|
||||||
|
assetResId, err := acl.GetRoleResourceIds(ctx, currentUser.GetRid(), conf.RESOURCE_ASSET)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
db, err = handleAssetIds(ctx, db, assetResId)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if err := db.Find(&assets).Error; err != nil {
|
if err := db.Find(&assets).Error; err != nil {
|
||||||
logger.L().Error("node posthookfailed asset count", zap.Error(err))
|
logger.L().Error("node posthookfailed asset count", zap.Error(err))
|
||||||
@@ -321,12 +328,12 @@ func handleSelfChild(ctx context.Context, ids ...int) (res []int, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetNodeIdsByAuthorization(ctx *gin.Context) (ids []int, err error) {
|
func GetNodeIdsByAuthorization(ctx *gin.Context) (ids []int, err error) {
|
||||||
currentUser, _ := acl.GetSessionFromCtx(ctx)
|
// currentUser, _ := acl.GetSessionFromCtx(ctx)
|
||||||
|
|
||||||
k := fmt.Sprintf(kFmtNodeIds, currentUser.GetUid())
|
// k := fmt.Sprintf(kFmtNodeIds, currentUser.GetUid())
|
||||||
if err = redis.Get(ctx, k, &ids); err == nil {
|
// if err = redis.Get(ctx, k, &ids); err == nil {
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
|
|
||||||
assetIds, err := GetAssetIdsByAuthorization(ctx)
|
assetIds, err := GetAssetIdsByAuthorization(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -336,7 +343,7 @@ func GetNodeIdsByAuthorization(ctx *gin.Context) (ids []int, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
redis.SetEx(ctx, k, ids, time.Minute)
|
// redis.SetEx(ctx, k, ids, time.Minute)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,10 @@ func GetGatewayManager() *GateWayManager {
|
|||||||
return manager
|
return manager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetGatewayBySessionId(sessionId string) *GatewayTunnel {
|
||||||
|
return manager.gateways[sessionId]
|
||||||
|
}
|
||||||
|
|
||||||
type GatewayTunnel struct {
|
type GatewayTunnel struct {
|
||||||
listener net.Listener
|
listener net.Listener
|
||||||
GatewayId int
|
GatewayId int
|
||||||
@@ -37,7 +41,7 @@ type GatewayTunnel struct {
|
|||||||
RemotePort int
|
RemotePort int
|
||||||
LocalConn net.Conn
|
LocalConn net.Conn
|
||||||
RemoteConn net.Conn
|
RemoteConn net.Conn
|
||||||
Opened chan struct{}
|
Opened chan error
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gt *GatewayTunnel) Open() (err error) {
|
func (gt *GatewayTunnel) Open() (err error) {
|
||||||
@@ -46,20 +50,21 @@ func (gt *GatewayTunnel) Open() (err error) {
|
|||||||
logger.L().Debug("timeout 5 second close listener", zap.String("sessionId", gt.SessionId))
|
logger.L().Debug("timeout 5 second close listener", zap.String("sessionId", gt.SessionId))
|
||||||
gt.listener.Close()
|
gt.listener.Close()
|
||||||
}()
|
}()
|
||||||
close(gt.Opened)
|
defer func() {
|
||||||
|
gt.Opened <- err
|
||||||
|
}()
|
||||||
|
gt.Opened <- nil
|
||||||
gt.LocalConn, err = gt.listener.Accept()
|
gt.LocalConn, err = gt.listener.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.L().Error("accept failed", zap.String("sessionId", gt.SessionId), zap.Error(err))
|
logger.L().Error("accept failed", zap.String("sessionId", gt.SessionId), zap.Error(err))
|
||||||
return err
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteAddr := fmt.Sprintf("%s:%d", gt.RemoteIp, gt.RemotePort)
|
remoteAddr := fmt.Sprintf("%s:%d", gt.RemoteIp, gt.RemotePort)
|
||||||
gt.RemoteConn, err = manager.sshClients[gt.GatewayId].Dial("tcp", remoteAddr)
|
gt.RemoteConn, err = manager.sshClients[gt.GatewayId].Dial("tcp", remoteAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.L().Error("dial remote failed", zap.String("sessionId", gt.SessionId), zap.Error(err))
|
logger.L().Error("dial remote failed", zap.String("sessionId", gt.SessionId), zap.Error(err))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
go io.Copy(gt.LocalConn, gt.RemoteConn)
|
go io.Copy(gt.LocalConn, gt.RemoteConn)
|
||||||
go io.Copy(gt.RemoteConn, gt.LocalConn)
|
go io.Copy(gt.RemoteConn, gt.LocalConn)
|
||||||
|
|
||||||
@@ -95,6 +100,7 @@ func (gm *GateWayManager) Open(sessionId, remoteIp string, remotePort int, gatew
|
|||||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
logger.L().Error("open gateway sshcli failed", zap.Int("gatewayId", gateway.Id), zap.Error(err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
go func() {
|
go func() {
|
||||||
@@ -120,7 +126,7 @@ func (gm *GateWayManager) Open(sessionId, remoteIp string, remotePort int, gatew
|
|||||||
LocalPort: localPort,
|
LocalPort: localPort,
|
||||||
RemoteIp: remoteIp,
|
RemoteIp: remoteIp,
|
||||||
RemotePort: remotePort,
|
RemotePort: remotePort,
|
||||||
Opened: make(chan struct{}),
|
Opened: make(chan error),
|
||||||
}
|
}
|
||||||
gm.gateways[sessionId] = g
|
gm.gateways[sessionId] = g
|
||||||
go g.Open()
|
go g.Open()
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ func (m *Node) SetUpdaterId(updaterId int) {
|
|||||||
m.UpdaterId = updaterId
|
m.UpdaterId = updaterId
|
||||||
}
|
}
|
||||||
func (m *Node) SetResourceId(resourceId int) {
|
func (m *Node) SetResourceId(resourceId int) {
|
||||||
|
m.ResourceId = resourceId
|
||||||
}
|
}
|
||||||
func (m *Node) GetResourceId() int {
|
func (m *Node) GetResourceId() int {
|
||||||
return m.ResourceId
|
return m.ResourceId
|
||||||
|
|||||||
@@ -133,7 +133,7 @@ func (m *view) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
if ln > 100 {
|
if ln > 100 {
|
||||||
m.cmds = m.cmds[ln-100 : ln]
|
m.cmds = m.cmds[ln-100 : ln]
|
||||||
}
|
}
|
||||||
m.cmdsIdx = len(m.cmds) - 1
|
m.cmdsIdx = len(m.cmds)
|
||||||
if cmd == "exit" {
|
if cmd == "exit" {
|
||||||
return m, tea.Sequence(hisCmd, tea.Quit)
|
return m, tea.Sequence(hisCmd, tea.Quit)
|
||||||
} else if strings.HasPrefix(cmd, "ssh") {
|
} else if strings.HasPrefix(cmd, "ssh") {
|
||||||
@@ -286,7 +286,7 @@ func (m *view) refresh() {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
m.cmds, err = redis.RC.LRange(m.Ctx, fmt.Sprintf(hisCmdsFmt, m.currentUser.GetUid()), -100, -1).Result()
|
m.cmds, err = redis.RC.LRange(m.Ctx, fmt.Sprintf(hisCmdsFmt, m.currentUser.GetUid()), -100, -1).Result()
|
||||||
m.cmdsIdx = len(m.cmds) - 1
|
m.cmdsIdx = len(m.cmds)
|
||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user