thread-master/internal/middleware/permissionrbac_middleware.go

60 lines
1.8 KiB
Go
Raw Permalink Normal View History

2026-06-26 08:37:04 +00:00
package middleware
import (
"net/http"
"haixun-backend/internal/library/authctx"
app "haixun-backend/internal/library/errors"
"haixun-backend/internal/library/errors/code"
"haixun-backend/internal/library/permmatch"
memberdomain "haixun-backend/internal/model/member/domain/usecase"
permissiondomain "haixun-backend/internal/model/permission/domain/usecase"
"haixun-backend/internal/response"
)
// PermissionRBACMiddleware enforces catalog permissions for authenticated members.
// Mount after AuthJWT or MemberAuth so actor is present in context.
type PermissionRBACMiddleware struct {
members memberdomain.UseCase
permissions permissiondomain.UseCase
}
func NewPermissionRBACMiddleware(
members memberdomain.UseCase,
permissions permissiondomain.UseCase,
) *PermissionRBACMiddleware {
return &PermissionRBACMiddleware{members: members, permissions: permissions}
}
func (m *PermissionRBACMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
actor, ok := authctx.ActorFromContext(r.Context())
if !ok {
response.Write(r.Context(), w, nil, app.For(code.Auth).AuthUnauthorized("missing actor"))
return
}
if m.members == nil || m.permissions == nil {
response.Write(r.Context(), w, nil, app.For(code.Permission).SysNotImplemented("permission rbac is not configured"))
return
}
member, err := m.members.GetByUID(r.Context(), actor.TenantID, actor.UID)
if err != nil {
response.Write(r.Context(), w, nil, err)
return
}
me, err := m.permissions.Me(r.Context(), member, false)
if err != nil {
response.Write(r.Context(), w, nil, err)
return
}
if !permmatch.RequestAllowed(me.Permissions, r.Method, r.URL.Path) {
response.Write(r.Context(), w, nil, app.For(code.Permission).AuthForbidden("permission denied"))
return
}
next(w, r)
}
}