mirror of
https://github.com/SigNoz/signoz.git
synced 2026-02-03 08:33:26 +00:00
* feat(authz): initial commit for migrating rbac to openfga * feat(authz): make the role updates idempotant * feat(authz): split role module into role and grant * feat(authz): some naming changes * feat(authz): integrate the grant module * feat(authz): add support for migrating existing user role * feat(authz): add support for migrating existing user role * feat(authz): figure out the * selector * feat(authz): merge main * feat(authz): merge main * feat(authz): address couple of todos * feat(authz): address couple of todos * feat(authz): fix tests and revert public dashboard change * feat(authz): fix tests and revert public dashboard change * feat(authz): add open api spec * feat(authz): add open api spec * feat(authz): add api key changes and missing migration * feat(authz): split role into getter and setter * feat(authz): add integration tests for authz register * feat(authz): add more tests for user invite and delete * feat(authz): update user tests * feat(authz): rename grant to granter * feat(authz): address review comments * feat(authz): address review comments * feat(authz): address review comments * feat(authz): add the migration for existing roles * feat(authz): go mod tidy * feat(authz): fix integration tests * feat(authz): handle community changes * feat(authz): handle community changes * feat(authz): role selectors for open claims * feat(authz): role selectors for open claims * feat(authz): prevent duplicate entries for changelog * feat(authz): scafolding for rbac migration * feat(authz): scafolding for rbac migration * feat(authz): scafolding for rbac migration * feat(authz): scafolding for rbac migration * feat(authz): scafolding for rbac migration
180 lines
4.7 KiB
Go
180 lines
4.7 KiB
Go
package middleware
|
|
|
|
import (
|
|
"log/slog"
|
|
"net/http"
|
|
|
|
"github.com/SigNoz/signoz/pkg/authz"
|
|
"github.com/SigNoz/signoz/pkg/http/render"
|
|
"github.com/SigNoz/signoz/pkg/modules/organization"
|
|
"github.com/SigNoz/signoz/pkg/modules/role"
|
|
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
|
"github.com/SigNoz/signoz/pkg/valuer"
|
|
"github.com/gorilla/mux"
|
|
)
|
|
|
|
const (
|
|
authzDeniedMessage string = "::AUTHZ-DENIED::"
|
|
)
|
|
|
|
type AuthZ struct {
|
|
logger *slog.Logger
|
|
orgGetter organization.Getter
|
|
authzService authz.AuthZ
|
|
roleGetter role.Getter
|
|
}
|
|
|
|
func NewAuthZ(logger *slog.Logger, orgGetter organization.Getter, authzService authz.AuthZ, roleGetter role.Getter) *AuthZ {
|
|
if logger == nil {
|
|
panic("cannot build authz middleware, logger is empty")
|
|
}
|
|
|
|
return &AuthZ{logger: logger, orgGetter: orgGetter, authzService: authzService, roleGetter: roleGetter}
|
|
}
|
|
|
|
func (middleware *AuthZ) ViewAccess(next http.HandlerFunc) http.HandlerFunc {
|
|
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
|
claims, err := authtypes.ClaimsFromContext(req.Context())
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
if err := claims.IsViewer(); err != nil {
|
|
middleware.logger.WarnContext(req.Context(), authzDeniedMessage, "claims", claims)
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
next(rw, req)
|
|
})
|
|
}
|
|
|
|
func (middleware *AuthZ) EditAccess(next http.HandlerFunc) http.HandlerFunc {
|
|
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
|
claims, err := authtypes.ClaimsFromContext(req.Context())
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
if err := claims.IsEditor(); err != nil {
|
|
middleware.logger.WarnContext(req.Context(), authzDeniedMessage, "claims", claims)
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
next(rw, req)
|
|
})
|
|
}
|
|
|
|
func (middleware *AuthZ) AdminAccess(next http.HandlerFunc) http.HandlerFunc {
|
|
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
|
claims, err := authtypes.ClaimsFromContext(req.Context())
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
if err := claims.IsAdmin(); err != nil {
|
|
middleware.logger.WarnContext(req.Context(), authzDeniedMessage, "claims", claims)
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
next(rw, req)
|
|
})
|
|
}
|
|
|
|
func (middleware *AuthZ) SelfAccess(next http.HandlerFunc) http.HandlerFunc {
|
|
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
|
claims, err := authtypes.ClaimsFromContext(req.Context())
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
id := mux.Vars(req)["id"]
|
|
if err := claims.IsSelfAccess(id); err != nil {
|
|
middleware.logger.WarnContext(req.Context(), authzDeniedMessage, "claims", claims)
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
next(rw, req)
|
|
})
|
|
}
|
|
|
|
func (middleware *AuthZ) OpenAccess(next http.HandlerFunc) http.HandlerFunc {
|
|
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
|
next(rw, req)
|
|
})
|
|
}
|
|
|
|
func (middleware *AuthZ) Check(next http.HandlerFunc, relation authtypes.Relation, typeable authtypes.Typeable, cb authtypes.SelectorCallbackWithClaimsFn, roles []string) http.HandlerFunc {
|
|
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
|
ctx := req.Context()
|
|
claims, err := authtypes.ClaimsFromContext(ctx)
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
orgId, err := valuer.NewUUID(claims.OrgID)
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
selectors, err := cb(req, claims)
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
roles, err := middleware.roleGetter.ListByOrgIDAndNames(req.Context(), orgId, roles)
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
roleSelectors := []authtypes.Selector{}
|
|
for _, role := range roles {
|
|
selectors = append(selectors, authtypes.MustNewSelector(authtypes.TypeRole, role.ID.String()))
|
|
}
|
|
|
|
err = middleware.authzService.CheckWithTupleCreation(ctx, claims, orgId, relation, typeable, selectors, roleSelectors)
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
next(rw, req)
|
|
})
|
|
}
|
|
|
|
func (middleware *AuthZ) CheckWithoutClaims(next http.HandlerFunc, relation authtypes.Relation, typeable authtypes.Typeable, cb authtypes.SelectorCallbackWithoutClaimsFn, roles []string) http.HandlerFunc {
|
|
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
|
ctx := req.Context()
|
|
orgs, err := middleware.orgGetter.ListByOwnedKeyRange(ctx)
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
selectors, orgID, err := cb(req, orgs)
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
err = middleware.authzService.CheckWithTupleCreationWithoutClaims(ctx, orgID, relation, typeable, selectors, selectors)
|
|
if err != nil {
|
|
render.Error(rw, err)
|
|
return
|
|
}
|
|
|
|
next(rw, req)
|
|
})
|
|
}
|