245 lines
6.4 KiB
Go
245 lines
6.4 KiB
Go
package applogic
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"strings"
|
||
|
||
"aijianzhan/platform/internal/agentcap"
|
||
"aijianzhan/platform/internal/agentstore"
|
||
"aijianzhan/platform/internal/authx"
|
||
"aijianzhan/platform/internal/svc"
|
||
"aijianzhan/platform/internal/tenantperm"
|
||
"aijianzhan/platform/internal/types"
|
||
)
|
||
|
||
type AgentAdminLogic struct {
|
||
ctx context.Context
|
||
svcCtx *svc.ServiceContext
|
||
}
|
||
|
||
func NewAgentAdminLogic(ctx context.Context, svcCtx *svc.ServiceContext) *AgentAdminLogic {
|
||
return &AgentAdminLogic{ctx: ctx, svcCtx: svcCtx}
|
||
}
|
||
|
||
func (l *AgentAdminLogic) store() (agentstore.Store, error) {
|
||
if l.svcCtx.Agents == nil {
|
||
return nil, fmt.Errorf("agent store unavailable")
|
||
}
|
||
return l.svcCtx.Agents, nil
|
||
}
|
||
|
||
func (l *AgentAdminLogic) List() ([]agentstore.Account, error) {
|
||
st, err := l.store()
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
items, err := st.List(l.ctx, authx.TenantID(l.ctx))
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
for i := range items {
|
||
l.attachRole(&items[i])
|
||
}
|
||
return items, nil
|
||
}
|
||
|
||
func (l *AgentAdminLogic) Get(agentID int64) (*agentstore.Account, error) {
|
||
st, err := l.store()
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
acc, err := st.Get(l.ctx, authx.TenantID(l.ctx), agentID)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
l.attachRole(acc)
|
||
return acc, nil
|
||
}
|
||
|
||
func (l *AgentAdminLogic) attachRole(acc *agentstore.Account) {
|
||
if acc == nil || acc.RoleID <= 0 || l.svcCtx.Roles == nil {
|
||
return
|
||
}
|
||
role, err := l.svcCtx.Roles.Get(l.ctx, authx.TenantID(l.ctx), acc.RoleID)
|
||
if err != nil {
|
||
return
|
||
}
|
||
acc.RoleCode = role.Code
|
||
acc.RoleName = role.Name
|
||
}
|
||
|
||
func (l *AgentAdminLogic) resolvePerms(roleID int64, fallback []string) (int64, []string, error) {
|
||
if roleID <= 0 {
|
||
return 0, authx.NormalizePerms(fallback), nil
|
||
}
|
||
if l.svcCtx.Roles == nil {
|
||
return 0, nil, fmt.Errorf("role store unavailable")
|
||
}
|
||
role, err := l.svcCtx.Roles.Get(l.ctx, authx.TenantID(l.ctx), roleID)
|
||
if err != nil {
|
||
return 0, nil, err
|
||
}
|
||
return role.RoleID, authx.NormalizePerms(append([]string{}, role.Permissions...)), nil
|
||
}
|
||
|
||
func (l *AgentAdminLogic) Create(req *types.AgentCreateReq) (*types.AgentCreateResp, error) {
|
||
st, err := l.store()
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
roleID, perms, err := l.resolvePerms(req.RoleID, req.Permissions)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if roleID <= 0 && len(perms) == 0 {
|
||
return nil, fmt.Errorf("role_id or permissions required")
|
||
}
|
||
if err := tenantperm.MustAllow(l.ctx, l.svcCtx.TenantPerm, authx.TenantID(l.ctx), perms); err != nil {
|
||
return nil, err
|
||
}
|
||
acc, secret, err := st.Create(l.ctx, authx.TenantID(l.ctx), authx.UserID(l.ctx), agentstore.CreateInput{
|
||
Name: req.Name,
|
||
Perms: perms,
|
||
AppSlugs: req.AppSlugs,
|
||
Status: agentstore.StatusActive,
|
||
RoleID: roleID,
|
||
})
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
l.attachRole(acc)
|
||
return &types.AgentCreateResp{Account: *acc, ClientSecret: secret}, nil
|
||
}
|
||
|
||
func (l *AuthLogic) SelfRegisterAgent(req *types.AgentSelfRegisterReq) (*types.AgentSelfRegisterResp, error) {
|
||
if l.svcCtx.Agents == nil {
|
||
return nil, fmt.Errorf("agent store unavailable")
|
||
}
|
||
want := l.svcCtx.Config.Agent.RegisterSecret
|
||
if want == "" {
|
||
want = l.svcCtx.Config.Auth.IssueSecret
|
||
}
|
||
if want == "" {
|
||
want = l.svcCtx.JWT.AccessSecret
|
||
}
|
||
if req.RegisterSecret == "" || req.RegisterSecret != want {
|
||
return nil, fmt.Errorf("invalid register secret")
|
||
}
|
||
tenantID := req.TenantID
|
||
if tenantID <= 0 {
|
||
tenantID = 1
|
||
}
|
||
acc, secret, reused, err := l.svcCtx.Agents.Register(l.ctx, tenantID, req.Name, req.HostKey)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
msg := "已登记为 pending,请管理员在控制台分配角色并启用后再换票"
|
||
if reused {
|
||
msg = "已存在 pending 登记,已轮换 client_secret;仍须管理员分配角色并启用"
|
||
}
|
||
return &types.AgentSelfRegisterResp{
|
||
Account: *acc,
|
||
ClientSecret: secret,
|
||
Reused: reused,
|
||
Message: msg,
|
||
}, nil
|
||
}
|
||
|
||
func (l *AgentAdminLogic) Update(agentID int64, req *types.AgentUpdateReq) (*agentstore.Account, error) {
|
||
st, err := l.store()
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
in := agentstore.UpdateInput{}
|
||
if req.Name != nil {
|
||
in.Name = req.Name
|
||
}
|
||
if req.Status != nil {
|
||
in.Status = req.Status
|
||
}
|
||
if req.AppSlugs != nil {
|
||
in.AppSlugs = req.AppSlugs
|
||
}
|
||
if req.RoleID != nil {
|
||
roleID, perms, err := l.resolvePerms(*req.RoleID, nil)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if err := tenantperm.MustAllow(l.ctx, l.svcCtx.TenantPerm, authx.TenantID(l.ctx), perms); err != nil {
|
||
return nil, err
|
||
}
|
||
in.RoleID = &roleID
|
||
in.Perms = &perms
|
||
} else if req.Permissions != nil {
|
||
n := authx.NormalizePerms(*req.Permissions)
|
||
if err := tenantperm.MustAllow(l.ctx, l.svcCtx.TenantPerm, authx.TenantID(l.ctx), n); err != nil {
|
||
return nil, err
|
||
}
|
||
in.Perms = &n
|
||
}
|
||
acc, err := st.Update(l.ctx, authx.TenantID(l.ctx), agentID, in)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
l.attachRole(acc)
|
||
return acc, nil
|
||
}
|
||
|
||
func (l *AgentAdminLogic) Rotate(agentID int64) (*types.AgentSecretResp, error) {
|
||
st, err := l.store()
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
secret, err := st.RotateSecret(l.ctx, authx.TenantID(l.ctx), agentID)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
acc, err := st.Get(l.ctx, authx.TenantID(l.ctx), agentID)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return &types.AgentSecretResp{ClientID: acc.ClientID, ClientSecret: secret}, nil
|
||
}
|
||
|
||
func (l *AgentAdminLogic) Delete(agentID int64) error {
|
||
st, err := l.store()
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return st.Delete(l.ctx, authx.TenantID(l.ctx), agentID)
|
||
}
|
||
|
||
func (l *AuthLogic) IssueClientCredentials(clientID, clientSecret string) (*types.TokenResp, error) {
|
||
if l.svcCtx.Agents == nil {
|
||
return nil, fmt.Errorf("agent store unavailable")
|
||
}
|
||
acc, err := l.svcCtx.Agents.Authenticate(l.ctx, strings.TrimSpace(clientID), clientSecret)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
token, exp, err := authx.IssueAgentToken(l.svcCtx.JWT, acc.TenantID, acc.AgentID, acc.Perms)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
_ = l.svcCtx.Agents.TouchToken(l.ctx, acc.AgentID)
|
||
secret := l.svcCtx.Config.Agent.CapsuleSecret
|
||
if secret == "" {
|
||
secret = l.svcCtx.JWT.AccessSecret
|
||
}
|
||
return &types.TokenResp{
|
||
AccessToken: token,
|
||
TokenType: "Bearer",
|
||
ExpiresAt: exp,
|
||
TenantID: acc.TenantID,
|
||
UserID: acc.AgentID,
|
||
Username: acc.ClientID,
|
||
DisplayName: acc.Name,
|
||
Role: authx.RoleAgent,
|
||
AgentKey: agentcap.PublicAgentKey(secret, acc.TenantID, acc.AgentID),
|
||
AgentID: acc.AgentID,
|
||
Permissions: append([]string{}, acc.Perms...),
|
||
AppSlugs: append([]string{}, acc.AppSlugs...),
|
||
}, nil
|
||
}
|