feat: 方案100%匹配 — RBAC动态权限+Grafana监控+目录库独立微服务+全国目录中心+标识同步
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/tcs-iptv/tcs/internal/httpx"
|
||||
"github.com/tcs-iptv/tcs/internal/model"
|
||||
"github.com/tcs-iptv/tcs/internal/service"
|
||||
)
|
||||
|
||||
// AdminHandler 用户管理/组织管理 API 处理器(系统管理模块补足)。
|
||||
type AdminHandler struct {
|
||||
admin *service.AdminService
|
||||
}
|
||||
|
||||
// NewAdminHandler 创建用户/组织管理 API 处理器。
|
||||
func NewAdminHandler(admin *service.AdminService) *AdminHandler {
|
||||
return &AdminHandler{admin: admin}
|
||||
}
|
||||
|
||||
// Register 注册管理路由(应挂载在监管主体权限组下)。
|
||||
func (h *AdminHandler) Register(rg *gin.RouterGroup) {
|
||||
// 组织管理
|
||||
rg.POST("/admin/orgs", h.createOrg)
|
||||
rg.PUT("/admin/orgs/:id", h.updateOrg)
|
||||
rg.GET("/admin/orgs", h.listOrgs)
|
||||
rg.GET("/admin/orgs/:id", h.getOrg)
|
||||
rg.POST("/admin/orgs/:id/disable", h.disableOrg)
|
||||
// 用户管理
|
||||
rg.POST("/admin/users", h.createUser)
|
||||
rg.PUT("/admin/users/:id", h.updateUser)
|
||||
rg.GET("/admin/users", h.listUsers)
|
||||
rg.GET("/admin/users/:id", h.getUser)
|
||||
rg.POST("/admin/users/:id/disable", h.disableUser)
|
||||
rg.POST("/admin/users/:id/reset-key", h.resetAPIKey)
|
||||
}
|
||||
|
||||
// ---- 组织管理 handlers ----
|
||||
|
||||
func (h *AdminHandler) createOrg(c *gin.Context) {
|
||||
var org model.Organization
|
||||
if err := c.ShouldBindJSON(&org); err != nil {
|
||||
httpx.Error(c, http.StatusBadRequest, "INVALID_REQUEST", err.Error())
|
||||
return
|
||||
}
|
||||
result, err := h.admin.CreateOrg(org)
|
||||
if err != nil {
|
||||
httpx.Error(c, http.StatusBadRequest, "CREATE_ORG_FAILED", err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Created(c, result)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) updateOrg(c *gin.Context) {
|
||||
orgID := c.Param("id")
|
||||
var updates model.Organization
|
||||
if err := c.ShouldBindJSON(&updates); err != nil {
|
||||
httpx.Error(c, http.StatusBadRequest, "INVALID_REQUEST", err.Error())
|
||||
return
|
||||
}
|
||||
result, err := h.admin.UpdateOrg(orgID, updates)
|
||||
if err != nil {
|
||||
httpx.Error(c, http.StatusBadRequest, "UPDATE_ORG_FAILED", err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OK(c, result)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) getOrg(c *gin.Context) {
|
||||
orgID := c.Param("id")
|
||||
org, err := h.admin.GetOrg(orgID)
|
||||
if err != nil {
|
||||
httpx.Error(c, http.StatusNotFound, "NOT_FOUND", err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OK(c, org)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) listOrgs(c *gin.Context) {
|
||||
orgType := c.Query("type")
|
||||
orgs := h.admin.ListOrgs(orgType)
|
||||
httpx.OK(c, orgs)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) disableOrg(c *gin.Context) {
|
||||
orgID := c.Param("id")
|
||||
if err := h.admin.DisableOrg(orgID); err != nil {
|
||||
httpx.Error(c, http.StatusBadRequest, "DISABLE_ORG_FAILED", err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OK(c, gin.H{"status": "disabled"})
|
||||
}
|
||||
|
||||
// ---- 用户管理 handlers ----
|
||||
|
||||
func (h *AdminHandler) createUser(c *gin.Context) {
|
||||
var user model.User
|
||||
if err := c.ShouldBindJSON(&user); err != nil {
|
||||
httpx.Error(c, http.StatusBadRequest, "INVALID_REQUEST", err.Error())
|
||||
return
|
||||
}
|
||||
result, err := h.admin.CreateUser(user)
|
||||
if err != nil {
|
||||
httpx.Error(c, http.StatusBadRequest, "CREATE_USER_FAILED", err.Error())
|
||||
return
|
||||
}
|
||||
httpx.Created(c, result)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) updateUser(c *gin.Context) {
|
||||
userID := c.Param("id")
|
||||
var updates model.User
|
||||
if err := c.ShouldBindJSON(&updates); err != nil {
|
||||
httpx.Error(c, http.StatusBadRequest, "INVALID_REQUEST", err.Error())
|
||||
return
|
||||
}
|
||||
result, err := h.admin.UpdateUser(userID, updates)
|
||||
if err != nil {
|
||||
httpx.Error(c, http.StatusBadRequest, "UPDATE_USER_FAILED", err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OK(c, result)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) getUser(c *gin.Context) {
|
||||
userID := c.Param("id")
|
||||
user, err := h.admin.GetUser(userID)
|
||||
if err != nil {
|
||||
httpx.Error(c, http.StatusNotFound, "NOT_FOUND", err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OK(c, user)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) listUsers(c *gin.Context) {
|
||||
orgID := c.Query("org_id")
|
||||
role := c.Query("role")
|
||||
users := h.admin.ListUsers(orgID, role)
|
||||
httpx.OK(c, users)
|
||||
}
|
||||
|
||||
func (h *AdminHandler) disableUser(c *gin.Context) {
|
||||
userID := c.Param("id")
|
||||
if err := h.admin.DisableUser(userID); err != nil {
|
||||
httpx.Error(c, http.StatusBadRequest, "DISABLE_USER_FAILED", err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OK(c, gin.H{"status": "disabled"})
|
||||
}
|
||||
|
||||
func (h *AdminHandler) resetAPIKey(c *gin.Context) {
|
||||
userID := c.Param("id")
|
||||
user, err := h.admin.ResetAPIKey(userID)
|
||||
if err != nil {
|
||||
httpx.Error(c, http.StatusBadRequest, "RESET_KEY_FAILED", err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OK(c, user)
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/tcs-iptv/tcs/internal/catalog"
|
||||
"github.com/tcs-iptv/tcs/internal/httpx"
|
||||
)
|
||||
|
||||
// CatalogHandler 目录库独立微服务 API 处理器。
|
||||
// 提供四维度标识查询接口,可作为独立服务部署。
|
||||
type CatalogHandler struct {
|
||||
cat *catalog.Catalog
|
||||
}
|
||||
|
||||
// NewCatalogHandler 创建目录库 API 处理器。
|
||||
func NewCatalogHandler(cat *catalog.Catalog) *CatalogHandler {
|
||||
return &CatalogHandler{cat: cat}
|
||||
}
|
||||
|
||||
// Register 注册目录库查询路由。
|
||||
func (h *CatalogHandler) Register(rg *gin.RouterGroup) {
|
||||
rg.GET("/catalog/query-by-ma", h.queryByMA)
|
||||
rg.GET("/catalog/query-by-hash", h.queryByHash)
|
||||
rg.GET("/catalog/query-by-provincial-code", h.queryByProvincialCode)
|
||||
rg.GET("/catalog/query-by-library-id", h.queryByLibraryID)
|
||||
rg.GET("/catalog/query-all", h.queryAll)
|
||||
}
|
||||
|
||||
func (h *CatalogHandler) queryByMA(c *gin.Context) {
|
||||
maCode := c.Query("ma_code")
|
||||
if maCode == "" {
|
||||
httpx.Error(c, http.StatusBadRequest, "INVALID_REQUEST", "缺少 ma_code 参数")
|
||||
return
|
||||
}
|
||||
result, err := h.cat.QueryByMA(maCode)
|
||||
if err != nil {
|
||||
httpx.Error(c, http.StatusNotFound, "NOT_FOUND", err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OK(c, result)
|
||||
}
|
||||
|
||||
func (h *CatalogHandler) queryByHash(c *gin.Context) {
|
||||
fileHash := c.Query("file_hash")
|
||||
if fileHash == "" {
|
||||
httpx.Error(c, http.StatusBadRequest, "INVALID_REQUEST", "缺少 file_hash 参数")
|
||||
return
|
||||
}
|
||||
result, err := h.cat.QueryByHash(fileHash)
|
||||
if err != nil {
|
||||
httpx.Error(c, http.StatusNotFound, "NOT_FOUND", err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OK(c, result)
|
||||
}
|
||||
|
||||
func (h *CatalogHandler) queryByProvincialCode(c *gin.Context) {
|
||||
code := c.Query("provincial_code")
|
||||
if code == "" {
|
||||
httpx.Error(c, http.StatusBadRequest, "INVALID_REQUEST", "缺少 provincial_code 参数")
|
||||
return
|
||||
}
|
||||
result, err := h.cat.QueryByProvincialCode(code)
|
||||
if err != nil {
|
||||
httpx.Error(c, http.StatusNotFound, "NOT_FOUND", err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OK(c, result)
|
||||
}
|
||||
|
||||
func (h *CatalogHandler) queryByLibraryID(c *gin.Context) {
|
||||
libraryID := c.Query("library_id")
|
||||
if libraryID == "" {
|
||||
httpx.Error(c, http.StatusBadRequest, "INVALID_REQUEST", "缺少 library_id 参数")
|
||||
return
|
||||
}
|
||||
result, err := h.cat.QueryByLibraryFileID(libraryID)
|
||||
if err != nil {
|
||||
httpx.Error(c, http.StatusNotFound, "NOT_FOUND", err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OK(c, result)
|
||||
}
|
||||
|
||||
func (h *CatalogHandler) queryAll(c *gin.Context) {
|
||||
maCode := c.Query("ma_code")
|
||||
if maCode == "" {
|
||||
httpx.Error(c, http.StatusBadRequest, "INVALID_REQUEST", "缺少 ma_code 参数")
|
||||
return
|
||||
}
|
||||
result, err := h.cat.QueryAll(maCode)
|
||||
if err != nil {
|
||||
httpx.Error(c, http.StatusNotFound, "NOT_FOUND", err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OK(c, result)
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/tcs-iptv/tcs/internal/catalog"
|
||||
"github.com/tcs-iptv/tcs/internal/chain"
|
||||
"github.com/tcs-iptv/tcs/internal/model"
|
||||
)
|
||||
|
||||
func TestCatalogHandler_QueryByMA(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
c := chain.NewMemoryChain()
|
||||
|
||||
// 发码
|
||||
_, err := c.IssueMA(chain.RoleRegulator, chain.IssueRequest{
|
||||
MACode: "MA.156.8531.6101/WD/20260000001", ContentTwinID: "ctid-cat-svc-001",
|
||||
FileHash: "fh-cat-svc-001", MerkleRoot: "mr-cat-svc-001",
|
||||
Content: model.Content{Title: "目录库服务测试", MAType: "WD", Issuer: "测试局"},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
cat := catalog.New(c)
|
||||
h := NewCatalogHandler(cat)
|
||||
|
||||
r := gin.New()
|
||||
rg := r.Group("/api/v1")
|
||||
h.Register(rg)
|
||||
|
||||
// 按 MA 码查询
|
||||
w := httptest.NewRecorder()
|
||||
req := httptest.NewRequest("GET", "/api/v1/catalog/query-by-ma?ma_code=MA.156.8531.6101/WD/20260000001", nil)
|
||||
r.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var resp struct {
|
||||
Code string `json:"code"`
|
||||
Data model.ContentQueryResult `json:"data"`
|
||||
}
|
||||
err = json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "SUCCESS", resp.Code)
|
||||
assert.True(t, resp.Data.Found)
|
||||
assert.Equal(t, "目录库服务测试", resp.Data.Content.Title)
|
||||
}
|
||||
|
||||
func TestCatalogHandler_QueryByHash(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
c := chain.NewMemoryChain()
|
||||
|
||||
_, err := c.IssueMA(chain.RoleRegulator, chain.IssueRequest{
|
||||
MACode: "MA.156.8531.6101/WD/20260000002", ContentTwinID: "ctid-cat-svc-002",
|
||||
FileHash: "fh-cat-svc-002", MerkleRoot: "mr-cat-svc-002",
|
||||
Content: model.Content{Title: "Hash查询服务测试", MAType: "WD", Issuer: "测试局"},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
cat := catalog.New(c)
|
||||
h := NewCatalogHandler(cat)
|
||||
|
||||
r := gin.New()
|
||||
rg := r.Group("/api/v1")
|
||||
h.Register(rg)
|
||||
|
||||
// 按 Hash 查询
|
||||
w := httptest.NewRecorder()
|
||||
req := httptest.NewRequest("GET", "/api/v1/catalog/query-by-hash?file_hash=fh-cat-svc-002", nil)
|
||||
r.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
|
||||
var resp struct {
|
||||
Code string `json:"code"`
|
||||
Data model.ContentQueryResult `json:"data"`
|
||||
}
|
||||
err = json.Unmarshal(w.Body.Bytes(), &resp)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, resp.Data.Found)
|
||||
assert.Equal(t, "MA.156.8531.6101/WD/20260000002", resp.Data.Content.MACode)
|
||||
|
||||
// 缺少参数
|
||||
w2 := httptest.NewRecorder()
|
||||
req2 := httptest.NewRequest("GET", "/api/v1/catalog/query-by-hash", nil)
|
||||
r.ServeHTTP(w2, req2)
|
||||
assert.Equal(t, http.StatusBadRequest, w2.Code)
|
||||
}
|
||||
|
||||
func TestCatalogHandler_NotFound(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
c := chain.NewMemoryChain()
|
||||
cat := catalog.New(c)
|
||||
h := NewCatalogHandler(cat)
|
||||
|
||||
r := gin.New()
|
||||
rg := r.Group("/api/v1")
|
||||
h.Register(rg)
|
||||
|
||||
// 不存在的 MA 码
|
||||
w := httptest.NewRecorder()
|
||||
req := httptest.NewRequest("GET", "/api/v1/catalog/query-by-ma?ma_code=nonexistent", nil)
|
||||
r.ServeHTTP(w, req)
|
||||
assert.Equal(t, http.StatusNotFound, w.Code)
|
||||
}
|
||||
@@ -60,6 +60,13 @@ func (h *Handler) Register(rg *gin.RouterGroup) {
|
||||
rg.GET("/regulatory/national-stats", h.nationalStats) // 全国监管统计(三期F.2)
|
||||
rg.GET("/regulatory/daily-report", h.dailyReport) // 监管数据上报日报(三期A.2)
|
||||
rg.GET("/admin/segments", h.listSegments) // 号段管理(三期B.1)
|
||||
// ---- 多维度标识查询(方案第一阶段标识查询接口补足)----
|
||||
rg.GET("/content/query-by-hash", h.queryByHash) // 按 Hash 反查标识信息
|
||||
rg.GET("/content/query-by-provincial-code", h.queryByProvincialCode) // 按省级内容编码反查
|
||||
rg.GET("/content/query-by-library-id", h.queryByLibraryID) // 按片库文件ID反查
|
||||
// ---- MA 合并/拆分(方案 MA 管理模块补足)----
|
||||
rg.POST("/content/merge", h.mergeMA) // MA 码合并(仅监管)
|
||||
rg.POST("/content/split", h.splitMA) // MA 码拆分(仅监管)
|
||||
// ---- 四期:大小屏融合(跨域解析/扫码验真/跨屏权益)----
|
||||
rg.GET("/content/resolve", h.resolve) // MA 跨域解析网关(C.1/C.2)
|
||||
rg.POST("/content/scan-verify", h.scanVerify) // 用户扫码验真(B.2)
|
||||
@@ -744,3 +751,82 @@ func (h *Handler) verifyRights(c *gin.Context) {
|
||||
}
|
||||
httpx.OK(c, h.svc.VerifyCrossScreenRights(req.MACode, req.UserHash, model.ScreenType(req.Screen)))
|
||||
}
|
||||
|
||||
// ---- 多维度标识查询 handlers ----
|
||||
|
||||
// queryByHash 按内容哈希反查标识信息。
|
||||
func (h *Handler) queryByHash(c *gin.Context) {
|
||||
fileHash := c.Query("file_hash")
|
||||
if fileHash == "" {
|
||||
httpx.Error(c, http.StatusBadRequest, "INVALID_REQUEST", "缺少 file_hash 参数")
|
||||
return
|
||||
}
|
||||
res, err := h.svc.QueryByHash(fileHash)
|
||||
if err != nil {
|
||||
httpx.Error(c, http.StatusNotFound, "NOT_FOUND", err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OK(c, res)
|
||||
}
|
||||
|
||||
// queryByProvincialCode 按省级内容编码反查标识信息。
|
||||
func (h *Handler) queryByProvincialCode(c *gin.Context) {
|
||||
code := c.Query("provincial_code")
|
||||
if code == "" {
|
||||
httpx.Error(c, http.StatusBadRequest, "INVALID_REQUEST", "缺少 provincial_code 参数")
|
||||
return
|
||||
}
|
||||
res, err := h.svc.QueryByProvincialCode(code)
|
||||
if err != nil {
|
||||
httpx.Error(c, http.StatusNotFound, "NOT_FOUND", err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OK(c, res)
|
||||
}
|
||||
|
||||
// queryByLibraryID 按片库文件 ID 反查标识信息。
|
||||
func (h *Handler) queryByLibraryID(c *gin.Context) {
|
||||
libraryID := c.Query("library_file_id")
|
||||
if libraryID == "" {
|
||||
httpx.Error(c, http.StatusBadRequest, "INVALID_REQUEST", "缺少 library_file_id 参数")
|
||||
return
|
||||
}
|
||||
res, err := h.svc.QueryByLibraryFileID(libraryID)
|
||||
if err != nil {
|
||||
httpx.Error(c, http.StatusNotFound, "NOT_FOUND", err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OK(c, res)
|
||||
}
|
||||
|
||||
// ---- MA 合并/拆分 handlers ----
|
||||
|
||||
// mergeMA MA 码合并(仅监管主体)。
|
||||
func (h *Handler) mergeMA(c *gin.Context) {
|
||||
var req model.MergeRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Error(c, http.StatusBadRequest, "INVALID_REQUEST", err.Error())
|
||||
return
|
||||
}
|
||||
res, err := h.svc.MergeMACodes(roleOf(c), req)
|
||||
if err != nil {
|
||||
httpx.Error(c, http.StatusBadRequest, "MERGE_FAILED", err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OK(c, res)
|
||||
}
|
||||
|
||||
// splitMA MA 码拆分(仅监管主体)。
|
||||
func (h *Handler) splitMA(c *gin.Context) {
|
||||
var req model.SplitRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Error(c, http.StatusBadRequest, "INVALID_REQUEST", err.Error())
|
||||
return
|
||||
}
|
||||
res, err := h.svc.SplitMACode(roleOf(c), req)
|
||||
if err != nil {
|
||||
httpx.Error(c, http.StatusBadRequest, "SPLIT_FAILED", err.Error())
|
||||
return
|
||||
}
|
||||
httpx.OK(c, res)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/tcs-iptv/tcs/internal/httpx"
|
||||
)
|
||||
|
||||
// RBACHandler RBAC 权限管理 API 处理器。
|
||||
type RBACHandler struct {
|
||||
rbac *httpx.RBACManager
|
||||
}
|
||||
|
||||
// NewRBACHandler 创建 RBAC 管理 API 处理器。
|
||||
func NewRBACHandler(rbac *httpx.RBACManager) *RBACHandler {
|
||||
return &RBACHandler{rbac: rbac}
|
||||
}
|
||||
|
||||
// Register 注册 RBAC 管理路由(应挂载在监管主体权限组下)。
|
||||
func (h *RBACHandler) Register(rg *gin.RouterGroup) {
|
||||
rg.GET("/admin/rbac/roles", h.listRoles)
|
||||
rg.GET("/admin/rbac/permissions/:role", h.listPermissions)
|
||||
rg.PUT("/admin/rbac/roles/:role", h.setRolePermissions)
|
||||
rg.POST("/admin/rbac/grant", h.grantPermission)
|
||||
rg.POST("/admin/rbac/revoke", h.revokePermission)
|
||||
}
|
||||
|
||||
func (h *RBACHandler) listRoles(c *gin.Context) {
|
||||
roles := h.rbac.ListRoles()
|
||||
httpx.OK(c, roles)
|
||||
}
|
||||
|
||||
func (h *RBACHandler) listPermissions(c *gin.Context) {
|
||||
role := c.Param("role")
|
||||
perms := h.rbac.ListPermissions(role)
|
||||
httpx.OK(c, perms)
|
||||
}
|
||||
|
||||
type setPermissionsReq struct {
|
||||
Permissions []string `json:"permissions"`
|
||||
}
|
||||
|
||||
func (h *RBACHandler) setRolePermissions(c *gin.Context) {
|
||||
role := c.Param("role")
|
||||
var req setPermissionsReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Error(c, http.StatusBadRequest, "INVALID_REQUEST", err.Error())
|
||||
return
|
||||
}
|
||||
perms := make([]httpx.Permission, len(req.Permissions))
|
||||
for i, p := range req.Permissions {
|
||||
perms[i] = httpx.Permission(p)
|
||||
}
|
||||
h.rbac.SetRolePermissions(role, perms)
|
||||
httpx.OK(c, gin.H{"role": role, "permissions": req.Permissions})
|
||||
}
|
||||
|
||||
type grantRevokeReq struct {
|
||||
Role string `json:"role"`
|
||||
Permission string `json:"permission"`
|
||||
}
|
||||
|
||||
func (h *RBACHandler) grantPermission(c *gin.Context) {
|
||||
var req grantRevokeReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Error(c, http.StatusBadRequest, "INVALID_REQUEST", err.Error())
|
||||
return
|
||||
}
|
||||
h.rbac.GrantPermission(req.Role, httpx.Permission(req.Permission))
|
||||
httpx.OK(c, gin.H{"role": req.Role, "permission": req.Permission, "granted": true})
|
||||
}
|
||||
|
||||
func (h *RBACHandler) revokePermission(c *gin.Context) {
|
||||
var req grantRevokeReq
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
httpx.Error(c, http.StatusBadRequest, "INVALID_REQUEST", err.Error())
|
||||
return
|
||||
}
|
||||
h.rbac.RevokePermission(req.Role, httpx.Permission(req.Permission))
|
||||
httpx.OK(c, gin.H{"role": req.Role, "permission": req.Permission, "revoked": true})
|
||||
}
|
||||
Reference in New Issue
Block a user