feat(api): agent CRUD

This commit is contained in:
ttk
2024-09-05 10:01:17 +08:00
parent cbaff314b7
commit b6ef1bf3e5
13 changed files with 116 additions and 51 deletions

View File

@@ -109,3 +109,18 @@ type UserInfoRespResult struct {
UID int `json:"uid"`
Username string `json:"username"`
}
type AuthWithKeyResp struct {
User AuthWithKeyResult `json:"user"`
}
type AuthWithKeyResult struct {
Avatar string `json:"avatar"`
Email string `json:"email"`
Name string `json:"name"`
Rid int `json:"rid"`
Role Role `json:"role"`
UID int `json:"uid"`
Username string `json:"username"`
ParentRoles []string `json:"parentRoles"`
}

View File

@@ -11,6 +11,7 @@ import (
"fmt"
"hash"
"io"
"reflect"
"strings"
"github.com/veops/oneterm/conf"
@@ -141,24 +142,40 @@ func NewSignature(secret, salt, sep, derivation string, digest func() hash.Hash,
}
func AuthWithKey(path string, originData map[string]any) (sess *Session, err error) {
originData["path"] = path
data := &UserInfoRespResult{}
body := map[string]any{
"path": path,
"key": originData["_key"],
"secret": originData["_secret"],
"need_parentRoles": true,
"app_id": "oneterm",
}
payload := make(map[string]any)
for k, v := range originData {
rv := reflect.ValueOf(v)
if rv.Kind() == reflect.Map || rv.Kind() == reflect.Slice || rv.Kind() == reflect.Array {
continue
}
payload[k] = v
}
body["payload"] = payload
url := fmt.Sprintf("%s%s", conf.Cfg.Auth.Acl.Url, "/acl/auth_with_key")
data := &AuthWithKeyResp{}
resp, err := remote.RC.R().
SetBody(originData).
SetBody(body).
SetResult(data).
Post(fmt.Sprintf("%s%s", conf.Cfg.Auth.Acl.Url, "/acl/apps/token"))
Post(url)
if err = remote.HandleErr(err, resp, nil); err == nil {
sess = &Session{
Uid: data.UID,
Uid: data.User.UID,
Acl: Acl{
Uid: data.UID,
UserName: data.Username,
Rid: data.Rid,
NickName: data.Name,
ParentRoles: data.Role.Permissions,
Uid: data.User.UID,
UserName: data.User.Username,
Rid: data.User.Rid,
NickName: data.User.Name,
ParentRoles: data.User.ParentRoles,
},
}
}
return
}

View File

@@ -19,6 +19,22 @@ import (
)
var (
assetPreHooks = []preHook[*model.Asset]{
func(ctx *gin.Context, data *model.Asset) {
if data.AccessAuth == nil {
data.AccessAuth = &model.AccessAuth{
Start: nil,
End: nil,
CmdIds: make(model.Slice[int], 0),
Ranges: make(model.Slice[model.Range], 0),
Allow: true,
}
}
if data.Authorization == nil {
data.Authorization = make(model.Map[int, model.Slice[int]])
}
},
}
assetPostHooks = []postHook[*model.Asset]{assetPostHookCount, assetPostHookAuth}
)
@@ -30,7 +46,8 @@ var (
// @Router /asset [post]
func (c *Controller) CreateAsset(ctx *gin.Context) {
asset := &model.Asset{}
doCreate(ctx, true, asset, conf.RESOURCE_ASSET)
doCreate(ctx, true, asset, conf.RESOURCE_ASSET, assetPreHooks...)
schedule.CheckUpdate(asset.Id)
}

View File

@@ -261,16 +261,23 @@ func doUpdate[T model.Model](ctx *gin.Context, needAcl bool, md T, preHooks ...p
if err = mysql.DB.Transaction(func(tx *gorm.DB) (err error) {
omits := []string{"resource_id", "created_at", "deleted_at"}
selects := []string{"*"}
switch t := any(md).(type) {
case *model.Asset:
if err = HandleAuthorization(currentUser, tx, model.ACTION_UPDATE, any(old).(*model.Asset), t); err != nil {
handleRemoteErr(ctx, err)
return
}
omits = append(omits, "ci_id")
if cast.ToBool(ctx.Value("isAuthWithKey")) {
selects = []string{"ip", "protocols", "authorization"}
}
case *model.Account:
if cast.ToBool(ctx.Value("isAuthWithKey")) {
selects = []string{"password", "phrase", "pk", "account_type"}
}
}
if err = mysql.DB.Omit(omits...).Save(md).Error; err != nil {
if err = mysql.DB.Select(selects).Omit(omits...).Save(md).Error; err != nil {
return
}
err = mysql.DB.Create(&model.History{
@@ -306,7 +313,6 @@ func doGet[T any](ctx *gin.Context, needAcl bool, dbFind *gorm.DB, resourceType
currentUser, _ := acl.GetSessionFromCtx(ctx)
if needAcl && !acl.IsAdmin(currentUser) {
//rs := make([]*acl.Resource, 0)
var rs []*acl.Resource
rs, err = acl.GetRoleResources(ctx, currentUser.Acl.Rid, resourceType)
if err != nil {
@@ -330,8 +336,8 @@ func doGet[T any](ctx *gin.Context, needAcl bool, dbFind *gorm.DB, resourceType
if _, ok := ctx.GetQuery("page_index"); ok {
dbFind = dbFind.Offset((pi - 1) * ps)
}
if _, ok := ctx.GetQuery("page_size"); ok {
dbFind = dbFind.Limit(ps)
if _, ok := ctx.GetQuery("page_size"); ok && ps != -1 {
dbFind = dbFind.Limit(lo.Ternary(ps == 0, 20, ps))
}
return dbFind.
Order("id DESC").

View File

@@ -25,6 +25,7 @@ const (
ErrAccessTime = 4011
ErrIdleTimeout = 4012
ErrWrongPvk = 4013
ErrUnauthorized = 4401
ErrInternal = 5000
ErrRemoteServer = 5001
ErrConnectServer = 5002
@@ -48,6 +49,7 @@ var (
ErrLogin: myi18n.MsgLoginError,
ErrAccessTime: myi18n.MsgAccessTime,
ErrIdleTimeout: myi18n.MsgIdleTimeout,
ErrUnauthorized: myi18n.MsgUnauthorized,
ErrInternal: myi18n.MsgInternalError,
ErrRemoteServer: myi18n.MsgRemoteServer,
ErrConnectServer: myi18n.MsgConnectServer,

View File

@@ -17,6 +17,10 @@ import (
"github.com/veops/oneterm/logger"
)
var (
errUnauthorized = &controller.ApiError{Code: controller.ErrUnauthorized}
)
func ginLogger() gin.HandlerFunc {
return func(ctx *gin.Context) {
start := time.Now()
@@ -44,18 +48,25 @@ func auth() gin.HandlerFunc {
m := make(map[string]any)
ctx.ShouldBindBodyWithJSON(&m)
if _, ok := m["key"]; ok {
if ctx.Request.Method == "GET" {
if _, ok := ctx.GetQuery("_key"); ok {
m["_key"] = ctx.Query("_key")
m["_secret"] = ctx.Query("_secret")
}
}
if _, ok := m["_key"]; ok {
sess, err = acl.AuthWithKey(ctx.Request.URL.Path, m)
if err != nil {
logger.L().Error("cannot authwithkey", zap.Error(err))
ctx.AbortWithStatus(http.StatusUnauthorized)
ctx.AbortWithError(http.StatusUnauthorized, errUnauthorized)
return
}
ctx.Set("isAuthWithKey", true)
} else {
cookie, err = ctx.Cookie("session")
if err != nil || cookie == "" {
logger.L().Error("cannot get cookie.session", zap.Error(err))
ctx.AbortWithStatus(http.StatusUnauthorized)
ctx.AbortWithError(http.StatusUnauthorized, errUnauthorized)
return
}
sess, err = acl.ParseCookie(cookie)

View File

@@ -20,13 +20,14 @@ var (
func init() {
ctx := context.Background()
addr := fmt.Sprintf("%s:%d", conf.Cfg.Redis.Host, conf.Cfg.Redis.Port)
RC = redis.NewClient(&redis.Options{
Addr: fmt.Sprintf("%s:%d", conf.Cfg.Redis.Host, conf.Cfg.Redis.Port),
Addr: addr,
Password: conf.Cfg.Redis.Password,
})
if _, err := RC.Ping(ctx).Result(); err != nil {
logger.L().Fatal("ping redis failed", zap.Error(err))
logger.L().Fatal("ping redis failed", zap.String("addr", addr), zap.Error(err))
}
}

View File

@@ -103,6 +103,11 @@ var (
One: "Bad Request: idle timeout more than {{.second}} seconds",
Other: "Bad Request: idle timeout more than {{.second}} seconds",
}
MsgUnauthorized = &i18n.Message{
ID: "MsgUnauthorized",
One: "Unauthorized",
Other: "Unauthorized",
}
//
MsgInternalError = &i18n.Message{
ID: "MsgInternalError",

View File

@@ -12,7 +12,6 @@ const (
type Asset struct {
Id int `json:"id" gorm:"column:id;primarykey"`
Ciid int `json:"ci_id" gorm:"column:ci_id"`
Name string `json:"name" gorm:"column:name"`
Comment string `json:"comment" gorm:"column:comment"`
ParentId int `json:"parent_id" gorm:"column:parent_id"`

View File

@@ -17,7 +17,6 @@ type Node struct {
ParentId int `json:"parent_id" gorm:"column:parent_id"`
Authorization Map[int, Slice[int]] `json:"authorization" gorm:"column:authorization"`
*AccessAuth `json:"access_auth" gorm:"column:access_auth"`
*Sync `json:"sync" gorm:"column:sync"`
Protocols Slice[string] `json:"protocols" gorm:"column:protocols"`
GatewayId int `json:"gateway_id" gorm:"column:gateway_id"`
@@ -32,14 +31,6 @@ type Node struct {
HasChild bool `json:"has_child" gorm:"-"`
}
type Sync struct {
TypeId int `json:"type_id,omitempty" gorm:"column:type_id"`
Mapping Map[string, string] `json:"mapping" gorm:"column:mapping"`
Filters string `json:"filters" gorm:"column:filters"`
Enable bool `json:"enable" gorm:"column:enable"`
Frequency float64 `json:"frequency" gorm:"column:frequency"`
}
func (m *Node) TableName() string {
return TABLE_NAME_NODE
}

View File

@@ -100,11 +100,6 @@ CREATE TABLE
`cmd_ids` JSON NOT NULL,
`ranges` JSON NOT NULL,
`allow` TINYINT(1) NOT NULL DEFAULT 0,
`type_id` INT NOT NULL DEFAULT 0,
`mapping` JSON NOT NULL,
`filters` TEXT NOT NULL,
`enable` TINYINT(1) NOT NULL DEFAULT 0,
`frequency` DOUBLE NOT NULL DEFAULT 0,
`creator_id` INT NOT NULL DEFAULT 0,
`created_at` TIMESTAMP NOT NULL,
`updater_id` INT NOT NULL DEFAULT 0,

View File

@@ -7,21 +7,30 @@ http:
ssh:
host: 0.0.0.0
port: 2222
privateKey: --BEGIN PRIVATE KEY-----END PRIVATE KEY-----
privateKey: |
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACBg490b4zqumtizCyM4RWtzJnPEsPIInBFugk8+UCb8XgAAAKCc1yKrnNci
qwAAAAtzc2gtZWQyNTUxOQAAACBg490b4zqumtizCyM4RWtzJnPEsPIInBFugk8+UCb8Xg
AAAECvd1Yj+bQxyxJtU3PirLK68CD3MWqBv0/shlFKS6wmbWDj3RvjOq6a2LMLIzhFa3Mm
c8Sw8gicEW6CTz5QJvxeAAAAGnJvb3RAbG9jYWxob3N0LmxvY2FsZG9tYWluAQID
-----END OPENSSH PRIVATE KEY-----
guacd:
host: oneterm-guacd
port: 4822
mysql:
host: oneterm-mysql
host: mysql
port: 3306
user: root
password: root
password: "123456"
redis:
addr: oneterm-redis:6379
password: root
host: redis
port: 6379
password: ""
log:
level: debug
@@ -33,7 +42,7 @@ auth:
acl:
appId: acl app id
secretKey: acl app secret key
url: http://host/api/v1
url: http://acl-api/api/v1
resourceNames:
- key: account
value: account
@@ -46,4 +55,4 @@ auth:
- key: authorization
value: authorization
secretKey: acl secret key
secretKey: xW2FAUfgffjmerTEBXADmURDOQ43ojLN

View File

@@ -2,7 +2,7 @@ version: "3.0"
services:
oneterm-api:
# image: registry.cn-hangzhou.aliyuncs.com/veops/oneterm-api:24.3
image: oneterm:dev
build:
context: .
dockerfile: ../backend/dockerfile
@@ -51,13 +51,10 @@ services:
new:
aliases:
- mysql
ports:
- '23306:3306'
redis:
image: registry.cn-hangzhou.aliyuncs.com/veops/redis:latest
container_name: oneterm-redis
#command: redis-server --requirepass tyrj5QVP9rHs
restart: always
environment:
TZ: Asia/Shanghai
@@ -73,7 +70,7 @@ services:
- oneterm-api
environment:
TZ: Asia/Shanghai
ONETERM_API_HOST: oneterm-api:8080
ONETERM_API_HOST: oneterm-api:8888
ACL_API_HOST: acl-api:5000
NGINX_PORT: 80
volumes:
@@ -88,13 +85,13 @@ services:
networks:
- new
ports:
- "8000:80"
- "8666:80"
acl-api:
image: registry.cn-hangzhou.aliyuncs.com/veops/acl-api:1.1
container_name: oneterm-acl-api
environment:
#TZ: Asia/Shanghai
TZ: Asia/Shanghai
WAIT_HOSTS: mysql:3306, redis:6379
volumes:
- ./.env:/data/apps/acl/.env