refactor(auth): delegate access control to be-system
Remove Agent Desk users, roles, login sessions, tokens, and local permission persistence. Expose the backend as an embeddable ai-agent module with host-provided subject lookup and operation authorization callbacks, and complete the frontend/backend repository split.
This commit is contained in:
@@ -5,7 +5,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"agent-desk/internal/pkg/config"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/config"
|
||||
)
|
||||
|
||||
func printBanner() {
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"agent-desk/internal/pkg/config"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/config"
|
||||
)
|
||||
|
||||
func TestRenderBanner(t *testing.T) {
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"agent-desk/internal/pkg/config"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/config"
|
||||
|
||||
"github.com/mlogclub/simple/sqls"
|
||||
"gorm.io/driver/mysql"
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"agent-desk/internal/pkg/config"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/config"
|
||||
)
|
||||
|
||||
func TestNewDialector(t *testing.T) {
|
||||
|
||||
@@ -1,17 +1,15 @@
|
||||
package bootstrap
|
||||
|
||||
import (
|
||||
"agent-desk/internal/ai/rag/vectordb"
|
||||
"agent-desk/internal/oidcclient"
|
||||
"agent-desk/internal/pkg/config"
|
||||
"agent-desk/internal/pkg/i18nx"
|
||||
"agent-desk/internal/pkg/logx"
|
||||
"agent-desk/internal/services/cronx"
|
||||
"agent-desk/internal/wxwork"
|
||||
"context"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/ai/rag/vectordb"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/config"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/i18nx"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/logx"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/services/cronx"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/wxwork"
|
||||
"log/slog"
|
||||
|
||||
_ "agent-desk/internal/services/event_handlers"
|
||||
_ "code.tczkiot.com/wlw/ai-agent/internal/services/event_handlers"
|
||||
)
|
||||
|
||||
func Init(configPath string) error {
|
||||
@@ -46,9 +44,5 @@ func Init(configPath string) error {
|
||||
cronx.Init()
|
||||
|
||||
wxwork.Init()
|
||||
if err := oidcclient.Init(context.Background()); err != nil {
|
||||
slog.Error("init oidc failed", "error", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package bootstrap
|
||||
|
||||
import (
|
||||
"agent-desk/internal/migration"
|
||||
"agent-desk/internal/models"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/migration"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/models"
|
||||
|
||||
"github.com/mlogclub/simple/sqls"
|
||||
)
|
||||
|
||||
@@ -1,34 +1,17 @@
|
||||
package bootstrap
|
||||
|
||||
import (
|
||||
"agent-desk/internal/handlers/api"
|
||||
"agent-desk/internal/handlers/dashboard"
|
||||
"agent-desk/internal/handlers/third"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/handlers/api"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/handlers/dashboard"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/handlers/third"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func registerApiAuthRoutes(group *gin.RouterGroup) {
|
||||
group.POST("/login", api.Login)
|
||||
group.POST("/logout", api.Logout)
|
||||
group.GET("/profile", api.Profile)
|
||||
group.GET("/wxwork_callback", api.WxWorkCallback)
|
||||
group.POST("/wxwork_exchange", api.WxWorkExchange)
|
||||
group.GET("/wxwork_login", api.WxWorkLogin)
|
||||
group.GET("/wxwork_qr_login", api.WxWorkQRLogin)
|
||||
group.GET("/oidc_callback", api.OIDCCallback)
|
||||
group.POST("/oidc_exchange", api.OIDCExchange)
|
||||
group.GET("/oidc_login", api.OIDCLogin)
|
||||
}
|
||||
|
||||
func registerApiChannelRoutes(group *gin.RouterGroup) {
|
||||
group.Any("/config", api.ChannelAnyConfig)
|
||||
}
|
||||
|
||||
func registerApiCustomerRoutes(group *gin.RouterGroup) {
|
||||
group.POST("/session_exchange", api.CustomerPostSession_exchange)
|
||||
}
|
||||
|
||||
func registerApiConversationRoutes(group *gin.RouterGroup) {
|
||||
group.GET("/:id", api.ConversationGetBy)
|
||||
group.POST("/close", api.ConversationPostClose)
|
||||
@@ -47,19 +30,6 @@ func registerDashboardDashboardRoutes(group *gin.RouterGroup) {
|
||||
group.GET("/overview", dashboard.DashboardGetOverview)
|
||||
}
|
||||
|
||||
func registerDashboardUserRoutes(group *gin.RouterGroup) {
|
||||
group.GET("/:id", dashboard.UserGetBy)
|
||||
group.POST("/assign_role", dashboard.UserPostAssign_role)
|
||||
group.POST("/change_password", dashboard.UserPostChange_password)
|
||||
group.POST("/create", dashboard.UserPostCreate)
|
||||
group.POST("/delete", dashboard.UserPostDelete)
|
||||
group.Any("/list", dashboard.UserAnyList)
|
||||
group.Any("/list_all", dashboard.UserAnyList_all)
|
||||
group.POST("/reset_password", dashboard.UserPostReset_password)
|
||||
group.POST("/update", dashboard.UserPostUpdate)
|
||||
group.POST("/update_status", dashboard.UserPostUpdate_status)
|
||||
}
|
||||
|
||||
func registerDashboardCompanyRoutes(group *gin.RouterGroup) {
|
||||
group.GET("/:id", dashboard.CompanyGetBy)
|
||||
group.POST("/create", dashboard.CompanyPostCreate)
|
||||
@@ -86,30 +56,6 @@ func registerDashboardCustomerContactRoutes(group *gin.RouterGroup) {
|
||||
group.POST("/update", dashboard.CustomerContactPostUpdate)
|
||||
}
|
||||
|
||||
func registerDashboardRoleRoutes(group *gin.RouterGroup) {
|
||||
group.GET("/:id", dashboard.RoleGetBy)
|
||||
group.POST("/assign_permission", dashboard.RolePostAssign_permission)
|
||||
group.POST("/create", dashboard.RolePostCreate)
|
||||
group.POST("/delete", dashboard.RolePostDelete)
|
||||
group.Any("/list", dashboard.RoleAnyList)
|
||||
group.GET("/list_all", dashboard.RoleGetList_all)
|
||||
group.POST("/update", dashboard.RolePostUpdate)
|
||||
group.POST("/update_sort", dashboard.RolePostUpdate_sort)
|
||||
group.POST("/update_status", dashboard.RolePostUpdate_status)
|
||||
}
|
||||
|
||||
func registerDashboardPermissionRoutes(group *gin.RouterGroup) {
|
||||
group.GET("/:id", dashboard.PermissionGetBy)
|
||||
group.Any("/list", dashboard.PermissionAnyList)
|
||||
group.POST("/sync", dashboard.PermissionPostSync)
|
||||
}
|
||||
|
||||
func registerDashboardSessionRoutes(group *gin.RouterGroup) {
|
||||
group.Any("/list", dashboard.SessionAnyList)
|
||||
group.POST("/revoke", dashboard.SessionPostRevoke)
|
||||
group.POST("/revoke/by/user", dashboard.SessionPostRevokeByUser)
|
||||
}
|
||||
|
||||
func registerDashboardTagRoutes(group *gin.RouterGroup) {
|
||||
group.GET("/:id", dashboard.TagGetBy)
|
||||
group.POST("/create", dashboard.TagPostCreate)
|
||||
@@ -178,7 +124,6 @@ func registerDashboardChannelRoutes(group *gin.RouterGroup) {
|
||||
group.POST("/create", dashboard.ChannelPostCreate)
|
||||
group.POST("/delete", dashboard.ChannelPostDelete)
|
||||
group.Any("/list", dashboard.ChannelAnyList)
|
||||
group.POST("/reset_user_token_secret", dashboard.ChannelPostReset_user_token_secret)
|
||||
group.POST("/rollback_ai_agent_rollout", dashboard.ChannelPostRollback_ai_agent_rollout)
|
||||
group.POST("/update", dashboard.ChannelPostUpdate)
|
||||
group.POST("/update_status", dashboard.ChannelPostUpdate_status)
|
||||
@@ -194,6 +139,7 @@ func registerDashboardAgentRoutes(group *gin.RouterGroup) {
|
||||
group.POST("/delete", dashboard.AgentPostDelete)
|
||||
group.Any("/list", dashboard.AgentAnyList)
|
||||
group.GET("/list_all", dashboard.AgentGetList_all)
|
||||
group.GET("/user-options", dashboard.AgentGetUser_options)
|
||||
group.POST("/update", dashboard.AgentPostUpdate)
|
||||
}
|
||||
|
||||
|
||||
@@ -6,21 +6,21 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"agent-desk/internal/ai/mcps"
|
||||
_ "agent-desk/internal/ai/runtime"
|
||||
"agent-desk/internal/handlers/api"
|
||||
"agent-desk/internal/middleware"
|
||||
"agent-desk/internal/pkg/config"
|
||||
"agent-desk/internal/pkg/ginx"
|
||||
"agent-desk/internal/pkg/httpx"
|
||||
"agent-desk/internal/pkg/i18nx"
|
||||
"agent-desk/internal/pkg/tracex"
|
||||
"agent-desk/internal/services"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/ai/mcps"
|
||||
_ "code.tczkiot.com/wlw/ai-agent/internal/ai/runtime"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/handlers/api"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/middleware"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/config"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/ginx"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/httpx"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/i18nx"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/tracex"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/services"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/mlogclub/simple/web"
|
||||
|
||||
_ "agent-desk/internal/services/wx_callback_handlers"
|
||||
_ "code.tczkiot.com/wlw/ai-agent/internal/services/wx_callback_handlers"
|
||||
)
|
||||
|
||||
func NewServer() (*gin.Engine, error) {
|
||||
@@ -60,8 +60,8 @@ func NewServer() (*gin.Engine, error) {
|
||||
|
||||
func corsMiddleware() gin.HandlerFunc {
|
||||
allowedOrigins := config.Current().Server.CORS.AllowedOrigins
|
||||
allowHeaders := "Origin, Content-Type, Accept, Authorization, X-Requested-With, X-Guest-Id, X-Channel-Id, X-External-Id, X-External-Name, X-Customer-Session-Token, X-Customer-Session-Expires-At"
|
||||
exposeHeaders := "Content-Length, Content-Type, Authorization, X-Guest-Id, X-Channel-Id, X-External-Id, X-External-Name, X-Customer-Session-Token, X-Customer-Session-Expires-At"
|
||||
allowHeaders := "Origin, Content-Type, Accept, Authorization, X-Requested-With, X-Channel-Id"
|
||||
exposeHeaders := "Content-Length, Content-Type, Authorization"
|
||||
allowMethods := "GET, POST, PUT, PATCH, DELETE, OPTIONS"
|
||||
allowedOriginSet := make(map[string]struct{}, len(allowedOrigins))
|
||||
for _, origin := range allowedOrigins {
|
||||
@@ -152,9 +152,7 @@ func addRouter(app *gin.Engine) {
|
||||
apiGroup := app.Group("/api")
|
||||
apiGroup.GET("/health", api.Health)
|
||||
apiGroup.GET("/config", api.PublicConfig)
|
||||
registerApiAuthRoutes(apiGroup.Group("/auth"))
|
||||
registerApiChannelRoutes(apiGroup.Group("/channel"))
|
||||
registerApiCustomerRoutes(apiGroup.Group("/customer"))
|
||||
registerApiConversationRoutes(apiGroup.Group("/conversation", middleware.ExternalUserMiddleware))
|
||||
registerApiMessageRoutes(apiGroup.Group("/message", middleware.ExternalUserMiddleware))
|
||||
|
||||
@@ -165,13 +163,9 @@ func addRouter(app *gin.Engine) {
|
||||
|
||||
dashboardGroup := app.Group("/api/dashboard", middleware.AuthMiddleware)
|
||||
registerDashboardDashboardRoutes(dashboardGroup.Group("/dashboard"))
|
||||
registerDashboardUserRoutes(dashboardGroup.Group("/user"))
|
||||
registerDashboardCompanyRoutes(dashboardGroup.Group("/company"))
|
||||
registerDashboardCustomerRoutes(dashboardGroup.Group("/customer"))
|
||||
registerDashboardCustomerContactRoutes(dashboardGroup.Group("/customer-contact"))
|
||||
registerDashboardRoleRoutes(dashboardGroup.Group("/role"))
|
||||
registerDashboardPermissionRoutes(dashboardGroup.Group("/permission"))
|
||||
registerDashboardSessionRoutes(dashboardGroup.Group("/session"))
|
||||
registerDashboardTagRoutes(dashboardGroup.Group("/tag"))
|
||||
registerDashboardConversationRoutes(dashboardGroup.Group("/conversation"))
|
||||
registerDashboardTicketRoutes(dashboardGroup.Group("/ticket"))
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"agent-desk/internal/pkg/config"
|
||||
"code.tczkiot.com/wlw/ai-agent/internal/pkg/config"
|
||||
)
|
||||
|
||||
func TestNewServerRegistersGinRoutes(t *testing.T) {
|
||||
@@ -31,16 +31,8 @@ func TestNewServerRegistersGinRoutes(t *testing.T) {
|
||||
}
|
||||
|
||||
expected := []string{
|
||||
http.MethodPost + " /api/auth/login",
|
||||
http.MethodGet + " /api/config",
|
||||
http.MethodGet + " /api/health",
|
||||
http.MethodGet + " /api/auth/oidc_login",
|
||||
http.MethodGet + " /api/auth/oidc_callback",
|
||||
http.MethodPost + " /api/auth/oidc_exchange",
|
||||
http.MethodGet + " /api/auth/profile",
|
||||
http.MethodGet + " /api/dashboard/user/list",
|
||||
http.MethodGet + " /api/dashboard/user/:id",
|
||||
http.MethodPost + " /api/dashboard/user/create",
|
||||
http.MethodPost + " /api/dashboard/conversation/send_message",
|
||||
http.MethodGet + " /api/dashboard/ai-workflow/default-definition",
|
||||
http.MethodGet + " /api/dashboard/ai-workflow/template/list",
|
||||
@@ -61,6 +53,20 @@ func TestNewServerRegistersGinRoutes(t *testing.T) {
|
||||
t.Fatalf("expected route %s to be registered", route)
|
||||
}
|
||||
}
|
||||
|
||||
removed := []string{
|
||||
http.MethodPost + " /api/auth/login",
|
||||
http.MethodGet + " /api/auth/profile",
|
||||
http.MethodGet + " /api/dashboard/user/list",
|
||||
http.MethodGet + " /api/dashboard/role/list",
|
||||
http.MethodGet + " /api/dashboard/permission/list",
|
||||
http.MethodGet + " /api/dashboard/session/list",
|
||||
}
|
||||
for _, route := range removed {
|
||||
if routes[route] {
|
||||
t.Fatalf("removed local auth route %s is still registered", route)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewServerHealthEndpointIsPublic(t *testing.T) {
|
||||
@@ -114,10 +120,6 @@ func TestNewServerExposesPublicConfig(t *testing.T) {
|
||||
WxWork: config.WxWorkConfig{
|
||||
Enabled: true,
|
||||
},
|
||||
OIDC: config.OIDCConfig{
|
||||
Enabled: false,
|
||||
ClientSecret: "must-not-leak",
|
||||
},
|
||||
})
|
||||
|
||||
app, err := NewServer()
|
||||
@@ -135,9 +137,7 @@ func TestNewServerExposesPublicConfig(t *testing.T) {
|
||||
var body struct {
|
||||
Success bool `json:"success"`
|
||||
Data struct {
|
||||
Language string `json:"language"`
|
||||
WxWorkEnabled bool `json:"wxworkEnabled"`
|
||||
OIDCEnabled bool `json:"oidcEnabled"`
|
||||
Language string `json:"language"`
|
||||
} `json:"data"`
|
||||
}
|
||||
if err := json.Unmarshal(rec.Body.Bytes(), &body); err != nil {
|
||||
@@ -149,14 +149,8 @@ func TestNewServerExposesPublicConfig(t *testing.T) {
|
||||
if body.Data.Language != "zh-CN" {
|
||||
t.Fatalf("language=%q want zh-CN", body.Data.Language)
|
||||
}
|
||||
if !body.Data.WxWorkEnabled {
|
||||
t.Fatalf("wxworkEnabled=false want true")
|
||||
}
|
||||
if body.Data.OIDCEnabled {
|
||||
t.Fatalf("oidcEnabled=true want false")
|
||||
}
|
||||
if strings.Contains(rec.Body.String(), "must-not-leak") {
|
||||
t.Fatalf("response leaked sensitive OIDC config: %s", rec.Body.String())
|
||||
if strings.Contains(rec.Body.String(), "wxworkEnabled") || strings.Contains(rec.Body.String(), "oidcEnabled") {
|
||||
t.Fatalf("response still exposes removed login options: %s", rec.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user