NOISSUE - Add PAT support for rules and reports (#3466)

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>
This commit is contained in:
Steve Munene
2026-04-16 11:01:03 +03:00
committed by GitHub
parent ac8dadefc6
commit f3a7230cc0
9 changed files with 102 additions and 18 deletions
+2 -2
View File
@@ -141,8 +141,8 @@ func (am *authorizationMiddleware) authorize(ctx context.Context, op permissions
pat = &smqauthz.PATReq{
UserID: session.UserID,
PatID: session.PatID,
EntityID: session.DomainID,
EntityType: operations.EntityType,
EntityID: auth.AnyIDs,
EntityType: auth.RulesType.String(),
Operation: opName,
Domain: session.DomainID,
}
+3 -3
View File
@@ -33,15 +33,15 @@ func OperationDetails() map[permissions.Operation]permissions.OperationDetails {
PermissionRequired: true,
},
OpAssignAlarm: {
Name: "assign",
Name: "alarm_assign",
PermissionRequired: true,
},
OpAcknowledgeAlarm: {
Name: "acknowledge",
Name: "alarm_acknowledge",
PermissionRequired: true,
},
OpResolveAlarm: {
Name: "resolve",
Name: "alarm_resolve",
PermissionRequired: true,
},
OpUpdateAlarm: {
+30 -2
View File
@@ -44,7 +44,17 @@ const (
OpMessageSubscribe = "message_subscribe"
)
var errInvalidEntityOp = errors.NewRequestError("operation not valid for entity type")
var (
errInvalidEntityOp = errors.NewRequestError("operation not valid for entity type")
errAlarmOpRequiresWildcardEntityID = errors.NewRequestError("alarm operations on rules entity type require wildcard entity ID")
)
// alarmOnlyOperations are RulesType operations authorized at the domain level; only wildcard entity ID is valid.
var alarmOnlyOperations = map[string]struct{}{
"alarm_assign": {},
"alarm_acknowledge": {},
"alarm_resolve": {},
}
type Operation = permissions.Operation
@@ -70,6 +80,8 @@ const (
MessagesType
DomainsType
UsersType
RulesType
ReportsType
)
const (
@@ -80,6 +92,8 @@ const (
MessagesStr = "messages"
DomainsStr = "domains"
UsersStr = "users"
RulesScopeStr = "rules"
ReportsScopeStr = "reports"
)
func (et EntityType) String() string {
@@ -98,6 +112,10 @@ func (et EntityType) String() string {
return DomainsStr
case UsersType:
return UsersStr
case RulesType:
return RulesScopeStr
case ReportsType:
return ReportsScopeStr
default:
return fmt.Sprintf("unknown domain entity type %d", et)
}
@@ -119,6 +137,10 @@ func ParseEntityType(et string) (EntityType, error) {
return DomainsType, nil
case UsersStr:
return UsersType, nil
case RulesScopeStr:
return RulesType, nil
case ReportsScopeStr:
return ReportsType, nil
default:
return 0, fmt.Errorf("unknown domain entity type %s", et)
}
@@ -147,7 +169,7 @@ func (et *EntityType) UnmarshalText(data []byte) (err error) {
func IsValidOperationForEntity(entityType EntityType, operation string) bool {
switch entityType {
case ClientsType, ChannelsType, GroupsType, DomainsType:
case ClientsType, ChannelsType, GroupsType, DomainsType, RulesType, ReportsType:
return true
case DashboardType:
return operation == OpDashboardShare || operation == OpDashboardUnshare
@@ -283,6 +305,12 @@ func (s *Scope) Validate() error {
return errors.Wrap(apiutil.ErrInvalidQueryParams, errInvalidEntityOp)
}
if s.EntityType == RulesType {
if _, ok := alarmOnlyOperations[s.Operation]; ok && s.EntityID != AnyIDs {
return errors.Wrap(apiutil.ErrInvalidQueryParams, errAlarmOpRequiresWildcardEntityID)
}
}
return nil
}
+46
View File
@@ -41,6 +41,16 @@ func TestEntityTypeString(t *testing.T) {
et: auth.MessagesType,
expected: "messages",
},
{
desc: "Rules entity type",
et: auth.RulesType,
expected: "rules",
},
{
desc: "Reports entity type",
et: auth.ReportsType,
expected: "reports",
},
{
desc: "Unknown entity type",
et: auth.EntityType(100),
@@ -87,6 +97,18 @@ func TestParseEntityType(t *testing.T) {
expected: auth.DashboardType,
err: false,
},
{
desc: "Parse rules",
et: "rules",
expected: auth.RulesType,
err: false,
},
{
desc: "Parse reports",
et: "reports",
expected: auth.ReportsType,
err: false,
},
{
desc: "Parse unknown entity type",
et: "unknown",
@@ -133,6 +155,18 @@ func TestEntityTypeMarshalJSON(t *testing.T) {
expected: []byte(`"clients"`),
err: nil,
},
{
desc: "Marshal rules",
et: auth.RulesType,
expected: []byte(`"rules"`),
err: nil,
},
{
desc: "Marshal reports",
et: auth.ReportsType,
expected: []byte(`"reports"`),
err: nil,
},
}
for _, tc := range cases {
@@ -163,6 +197,18 @@ func TestEntityTypeUnmarshalJSON(t *testing.T) {
expected: auth.ChannelsType,
err: false,
},
{
desc: "Unmarshal rules",
data: []byte(`"rules"`),
expected: auth.RulesType,
err: false,
},
{
desc: "Unmarshal reports",
data: []byte(`"reports"`),
expected: auth.ReportsType,
err: false,
},
{
desc: "Unmarshal unknown",
data: []byte(`"unknown"`),
+5 -5
View File
@@ -137,13 +137,13 @@ alarm:
- view: alarm_read_permission
- update: alarm_update_permission
- delete: alarm_delete_permission
- assign: alarm_assign_permission
- acknowledge: alarm_acknowledge_permission
- resolve: alarm_resolve_permission
- alarm_assign: alarm_assign_permission
- alarm_acknowledge: alarm_acknowledge_permission
- alarm_resolve: alarm_resolve_permission
rule:
operations:
- add: rule_create_permission
- create: rule_create_permission
- list: rule_read_permission
- view: read_permission
- update: update_permission
@@ -174,7 +174,7 @@ rule:
report:
operations:
- add: report_create_permission
- create: report_create_permission
- list: report_read_permission
- generate: report_read_permission
- view: read_permission
+7 -2
View File
@@ -6,6 +6,7 @@ package middleware
import (
"context"
"github.com/absmach/magistrala/auth"
"github.com/absmach/magistrala/pkg/authn"
smqauthz "github.com/absmach/magistrala/pkg/authz"
"github.com/absmach/magistrala/pkg/errors"
@@ -156,12 +157,16 @@ func (am *authorizationMiddleware) authorize(ctx context.Context, op permissions
var pat *smqauthz.PATReq
if session.PatID != "" {
entityID := obj
if objType == policies.DomainType {
entityID = auth.AnyIDs
}
opName := am.entitiesOps.OperationName(operations.EntityType, op)
pat = &smqauthz.PATReq{
UserID: session.UserID,
PatID: session.PatID,
EntityID: session.DomainID,
EntityType: operations.EntityType,
EntityID: entityID,
EntityType: auth.RulesType.String(),
Operation: opName,
Domain: session.DomainID,
}
+1 -1
View File
@@ -23,7 +23,7 @@ const (
func OperationDetails() map[permissions.Operation]permissions.OperationDetails {
return map[permissions.Operation]permissions.OperationDetails{
OpAddRule: {
Name: "add",
Name: "create",
PermissionRequired: true,
},
OpViewRule: {
+7 -2
View File
@@ -6,6 +6,7 @@ package middleware
import (
"context"
"github.com/absmach/magistrala/auth"
"github.com/absmach/magistrala/pkg/authn"
smqauthz "github.com/absmach/magistrala/pkg/authz"
"github.com/absmach/magistrala/pkg/errors"
@@ -175,12 +176,16 @@ func (am *authorizationMiddleware) authorize(ctx context.Context, op permissions
var pat *smqauthz.PATReq
if session.PatID != "" {
entityID := obj
if objType == policies.DomainType {
entityID = auth.AnyIDs
}
opName := am.entitiesOps.OperationName(operations.EntityType, op)
pat = &smqauthz.PATReq{
UserID: session.UserID,
PatID: session.PatID,
EntityID: session.DomainID,
EntityType: operations.EntityType,
EntityID: entityID,
EntityType: auth.ReportsType.String(),
Operation: opName,
Domain: session.DomainID,
}
+1 -1
View File
@@ -26,7 +26,7 @@ const (
func OperationDetails() map[permissions.Operation]permissions.OperationDetails {
return map[permissions.Operation]permissions.OperationDetails{
OpAddReportConfig: {
Name: "add",
Name: "create",
PermissionRequired: true,
},
OpViewReportConfig: {