权限整改:完成服务器资源项目权限隔离
将服务器新增、修改、删除接口纳入项目资源权限校验,并同步更新服务器接口测试依赖。
This commit is contained in:
parent
f5ab9c1610
commit
747e897ba9
@ -230,9 +230,9 @@ func (s *Server) registerRoutes() {
|
||||
domainAPI.POST("/domains/:id/certificate/validate", s.longOperation(certificateTaskTimeout), s.validateCertificate)
|
||||
domainAPI.POST("/domains/:id/deploy", s.longOperation(certificateTaskTimeout), s.deployCertificateCurrent)
|
||||
domainAPI.POST("/domains/:id/certificate/versions/:version/restore", s.longOperation(certificateTaskTimeout), s.restoreCertificateVersion)
|
||||
adminAPI.POST("/servers", s.createServer)
|
||||
adminAPI.PUT("/servers/:id", s.updateServer)
|
||||
adminAPI.DELETE("/servers/:id", s.deleteServer)
|
||||
api.POST("/servers", s.createServer)
|
||||
api.PUT("/servers/:id", s.updateServer)
|
||||
api.DELETE("/servers/:id", s.deleteServer)
|
||||
adminAPI.GET("/ssh-keys", s.listSSHKeys)
|
||||
adminAPI.POST("/ssh-keys", s.createSSHKey)
|
||||
adminAPI.DELETE("/ssh-keys/:id", s.deleteSSHKey)
|
||||
@ -407,6 +407,7 @@ type serverRequest struct {
|
||||
WebServer string `json:"web_server" binding:"required"`
|
||||
ValidationCommand []string `json:"validation_command"`
|
||||
ReloadCommand []string `json:"reload_command"`
|
||||
ProjectID string `json:"project_id"`
|
||||
}
|
||||
|
||||
func (s *Server) health(c *gin.Context) {
|
||||
@ -553,6 +554,28 @@ func (s *Server) authorizeDomainMutation(c *gin.Context, projectID string) bool
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *Server) authorizeServerMutation(c *gin.Context, projectID string) bool {
|
||||
authorizer, ok := s.projectDomains.(repository.ProjectAuthorizer)
|
||||
if !ok {
|
||||
if fallback, fallbackOK := s.projectMembers.(repository.ProjectAuthorizer); fallbackOK {
|
||||
authorizer = fallback
|
||||
ok = true
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
s.writeError(c, http.StatusServiceUnavailable, "项目权限服务尚未就绪", nil)
|
||||
return false
|
||||
}
|
||||
if err := authorizer.AuthorizeProject(c.Request.Context(), c.GetString(authenticatedActorKey), projectID, domain.PermissionServerManage); err != nil {
|
||||
if errors.Is(err, repository.ErrProjectAccessDenied) {
|
||||
s.writeError(c, http.StatusForbidden, "无权管理此项目的服务器", nil)
|
||||
} else {
|
||||
s.writeError(c, http.StatusInternalServerError, "检查项目服务器权限失败", err)
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
func (s *Server) listDomains(c *gin.Context) {
|
||||
if !s.requireProjectResourceReader(c) {
|
||||
return
|
||||
@ -745,6 +768,13 @@ func (s *Server) createServer(c *gin.Context) {
|
||||
}
|
||||
item := serverFromRequest(request)
|
||||
item.ResourceScope = s.newResourceScope(c)
|
||||
if strings.TrimSpace(request.ProjectID) != "" {
|
||||
item.ProjectID = strings.TrimSpace(request.ProjectID)
|
||||
}
|
||||
item.ResourceScope = item.ResourceScope.Normalize()
|
||||
if !s.authorizeServerMutation(c, item.ProjectID) {
|
||||
return
|
||||
}
|
||||
item = item.Normalize()
|
||||
now := time.Now().UTC()
|
||||
item.CreatedAt = now
|
||||
@ -779,6 +809,9 @@ func (s *Server) updateServer(c *gin.Context) {
|
||||
s.writeError(c, http.StatusNotImplemented, "服务器管理接口尚未配置", errors.New("服务器仓储未配置"))
|
||||
return
|
||||
}
|
||||
if !s.requireProjectResourceReader(c) {
|
||||
return
|
||||
}
|
||||
var request serverRequest
|
||||
if err := bindJSON(c, &request, jsonRequestBodyLimit); err != nil {
|
||||
s.writeError(c, http.StatusBadRequest, "请求参数无效", err)
|
||||
@ -787,8 +820,12 @@ func (s *Server) updateServer(c *gin.Context) {
|
||||
item := serverFromRequest(request)
|
||||
item.ID = c.Param("id")
|
||||
item = item.Normalize()
|
||||
existing, err := s.servers.GetServerByID(c.Request.Context(), item.ID)
|
||||
existing, err := s.projectResources.GetVisibleServer(c.Request.Context(), c.GetString(authenticatedActorKey), item.ID)
|
||||
if err != nil {
|
||||
if errors.Is(err, repository.ErrProjectAccessDenied) {
|
||||
s.writeProjectResourceError(c, err)
|
||||
return
|
||||
}
|
||||
if repository.IsNotFound(err) {
|
||||
s.writeError(c, http.StatusNotFound, "服务器不存在", err)
|
||||
return
|
||||
@ -796,6 +833,9 @@ func (s *Server) updateServer(c *gin.Context) {
|
||||
s.writeError(c, http.StatusInternalServerError, "读取服务器失败", err)
|
||||
return
|
||||
}
|
||||
if !s.authorizeServerMutation(c, existing.ProjectID) {
|
||||
return
|
||||
}
|
||||
item.CreatedAt = existing.CreatedAt
|
||||
item.ResourceScope = s.updatedResourceScope(c, existing.ResourceScope)
|
||||
item.UpdatedAt = time.Now().UTC()
|
||||
@ -968,8 +1008,15 @@ func (s *Server) deleteServer(c *gin.Context) {
|
||||
s.writeError(c, http.StatusNotImplemented, "服务器管理接口尚未配置", errors.New("服务器仓储未配置"))
|
||||
return
|
||||
}
|
||||
item, err := s.servers.GetServerByID(c.Request.Context(), c.Param("id"))
|
||||
if !s.requireProjectResourceReader(c) {
|
||||
return
|
||||
}
|
||||
item, err := s.projectResources.GetVisibleServer(c.Request.Context(), c.GetString(authenticatedActorKey), c.Param("id"))
|
||||
if err != nil {
|
||||
if errors.Is(err, repository.ErrProjectAccessDenied) {
|
||||
s.writeProjectResourceError(c, err)
|
||||
return
|
||||
}
|
||||
if repository.IsNotFound(err) {
|
||||
s.writeError(c, http.StatusNotFound, "服务器不存在", err)
|
||||
return
|
||||
@ -977,6 +1024,9 @@ func (s *Server) deleteServer(c *gin.Context) {
|
||||
s.writeError(c, http.StatusInternalServerError, "读取服务器失败", err)
|
||||
return
|
||||
}
|
||||
if !s.authorizeServerMutation(c, item.ProjectID) {
|
||||
return
|
||||
}
|
||||
if err := s.servers.DeleteServer(c.Request.Context(), c.Param("id")); err != nil {
|
||||
if repository.IsNotFound(err) {
|
||||
s.writeError(c, http.StatusNotFound, "服务器不存在", err)
|
||||
|
||||
@ -38,7 +38,9 @@ func TestSSHKeyAPIHidesPrivateKeyAndBlocksDeletingUsedReference(t *testing.T) {
|
||||
serverRepo := repository.NewSQLServerRepository(db.DB)
|
||||
server := New(Dependencies{
|
||||
Config: cfg, Repository: repository.NewSQLDomainRepository(db.DB), Servers: serverRepo,
|
||||
SSHKeys: sshkey.NewService(store),
|
||||
SSHKeys: sshkey.NewService(store),
|
||||
ProjectResources: projectResourceReaderStub{servers: serverRepo},
|
||||
ProjectDomains: projectDomainRepositoryStub{DomainRepository: repository.NewSQLDomainRepository(db.DB)},
|
||||
})
|
||||
|
||||
privateKey := testSSHPrivateKey(t)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user