NOISSUE - Refactor alarms, reports and rule engines middlewares (#369)

* refactor middleware

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* update go mod file

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix tests

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* fix rules tests

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* revert common code

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

* update supermq version

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>

---------

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>
This commit is contained in:
Steve Munene
2025-12-26 01:53:57 +03:00
committed by dusan
parent e4cef0fdc2
commit 99e2c7aec4
14 changed files with 427 additions and 408 deletions
+36 -48
View File
@@ -10,9 +10,17 @@ import (
"github.com/absmach/supermq/auth"
"github.com/absmach/supermq/pkg/authn"
smqauthz "github.com/absmach/supermq/pkg/authz"
"github.com/absmach/supermq/pkg/errors"
"github.com/absmach/supermq/pkg/permissions"
"github.com/absmach/supermq/pkg/policies"
)
var (
errDomainUpdateAlarms = errors.New("not authorized to update alarms in domain")
errDomainDeleteAlarms = errors.New("not authorized to delete alarms in domain")
errDomainViewAlarms = errors.New("not authorized to view alarms in domain")
)
type authorizationMiddleware struct {
svc alarms.Service
authz smqauthz.Authorization
@@ -32,20 +40,10 @@ func (am *authorizationMiddleware) CreateAlarm(ctx context.Context, alarm alarms
}
func (am *authorizationMiddleware) UpdateAlarm(ctx context.Context, session authn.Session, alarm alarms.Alarm) (dba alarms.Alarm, err error) {
// if assignee is present check if assignee is member of domain
// If assignee is present, check if assignee is member of domain
req := smqauthz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: session.DomainUserID,
Permission: policies.AdminPermission,
ObjectType: policies.DomainType,
Object: session.DomainID,
}
if err := am.authz.Authorize(ctx, req); err != nil {
return alarms.Alarm{}, err
if err := am.authorize(ctx, alarms.OpUpdateAlarm, session); err != nil {
return alarms.Alarm{}, errors.Wrap(errDomainUpdateAlarms, err)
}
if alarm.AssigneeID != "" {
@@ -67,17 +65,8 @@ func (am *authorizationMiddleware) UpdateAlarm(ctx context.Context, session auth
}
func (am *authorizationMiddleware) DeleteAlarm(ctx context.Context, session authn.Session, id string) error {
req := smqauthz.PolicyReq{
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: session.DomainUserID,
Permission: policies.AdminPermission,
ObjectType: policies.DomainType,
Object: session.DomainID,
}
if err := am.authz.Authorize(ctx, req); err != nil {
return err
if err := am.authorize(ctx, alarms.OpDeleteAlarm, session); err != nil {
return errors.Wrap(errDomainDeleteAlarms, err)
}
return am.svc.DeleteAlarm(ctx, session, id)
@@ -88,37 +77,36 @@ func (am *authorizationMiddleware) ListAlarms(ctx context.Context, session authn
pm.DomainID = session.DomainID
}
req := smqauthz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: session.DomainUserID,
Permission: policies.MembershipPermission,
ObjectType: policies.DomainType,
Object: session.DomainID,
}
if err := am.authz.Authorize(ctx, req); err != nil {
return alarms.AlarmsPage{}, err
if err := am.authorize(ctx, alarms.OpListAlarms, session); err != nil {
return alarms.AlarmsPage{}, errors.Wrap(errDomainViewAlarms, err)
}
return am.svc.ListAlarms(ctx, session, pm)
}
func (am *authorizationMiddleware) ViewAlarm(ctx context.Context, session authn.Session, id string) (alarms.Alarm, error) {
req := smqauthz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: session.DomainUserID,
Permission: policies.MembershipPermission,
ObjectType: policies.DomainType,
Object: session.DomainID,
}
if err := am.authz.Authorize(ctx, req); err != nil {
return alarms.Alarm{}, err
if err := am.authorize(ctx, alarms.OpViewAlarm, session); err != nil {
return alarms.Alarm{}, errors.Wrap(errDomainViewAlarms, err)
}
return am.svc.ViewAlarm(ctx, session, id)
}
func (am *authorizationMiddleware) authorize(ctx context.Context, op permissions.Operation, session authn.Session) error {
perm, err := alarms.GetPermission(op)
if err != nil {
return err
}
pr := smqauthz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: session.DomainUserID,
Object: session.DomainID,
ObjectType: policies.DomainType,
Permission: perm,
}
return am.authz.Authorize(ctx, pr)
}
+30
View File
@@ -0,0 +1,30 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package alarms
import (
"github.com/absmach/supermq/pkg/errors"
"github.com/absmach/supermq/pkg/permissions"
"github.com/absmach/supermq/pkg/policies"
)
const (
OpAddAlarm = iota
OpViewAlarm
OpListAlarms
OpUpdateAlarm
OpDeleteAlarm
)
func GetPermission(op permissions.Operation) (string, error) {
if op < OpAddAlarm || op > OpDeleteAlarm {
return "", errors.New("invalid operation")
}
if op == OpUpdateAlarm || op == OpDeleteAlarm {
return policies.AdminPermission, nil
}
return policies.MembershipPermission, nil
}
+5 -1
View File
@@ -322,7 +322,11 @@ func newService(ctx context.Context, db pgclient.Database, runInfo chan pkglog.R
return nil, fmt.Errorf("failed to init re event store middleware: %w", err)
}
csvc, err = middleware.AuthorizationMiddleware(csvc, authz, callout)
csvc, err = middleware.AuthorizationMiddleware(csvc, authz)
if err != nil {
return nil, err
}
csvc, err = middleware.NewCallout(csvc, callout)
if err != nil {
return nil, err
}
+10 -10
View File
@@ -1,12 +1,12 @@
module github.com/absmach/magistrala
go 1.25.3
go 1.25.5
require (
github.com/0x6flab/namegenerator v1.4.0
github.com/absmach/callhome v0.18.2
github.com/absmach/certs v0.18.3
github.com/absmach/supermq v0.18.3
github.com/absmach/supermq v0.18.4-0.20251223143751-59f8d4e4d7b1
github.com/authzed/authzed-go v1.7.0
github.com/authzed/grpcutil v0.0.0-20250221190651-1985b19b35b8
github.com/caarlos0/env/v11 v11.3.1
@@ -74,7 +74,7 @@ require (
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
github.com/envoyproxy/protoc-gen-validate v1.3.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
@@ -134,18 +134,18 @@ require (
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.64.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0 // indirect
go.opentelemetry.io/otel/metric v1.39.0 // indirect
go.opentelemetry.io/otel/sdk v1.39.0 // indirect
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
golang.org/x/crypto v0.45.0 // indirect
golang.org/x/crypto v0.46.0 // indirect
golang.org/x/net v0.47.0 // indirect
golang.org/x/sys v0.39.0 // indirect
golang.org/x/text v0.31.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20251124214823-79d6a2a48846 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846 // indirect
golang.org/x/text v0.32.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
google.golang.org/protobuf v1.36.11
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
+24 -24
View File
@@ -30,8 +30,8 @@ github.com/absmach/mgate v0.5.0 h1:RV2Aalra3xIm+XTs13TM7iE7v4WTL2SKhKcPbKr22Ac=
github.com/absmach/mgate v0.5.0/go.mod h1:0KVq7mxM0wayosmyXPPxp1EL0c2d9kRp5V8NZCKdetA=
github.com/absmach/senml v1.0.8 h1:+opem/r4g6c6eA/JLyCIuksyEhj7eBdysY3pEmy1mqo=
github.com/absmach/senml v1.0.8/go.mod h1:DRhzHLgvQoIUHroBgpFrSWso+bJZO9E96RlHAHy+VRI=
github.com/absmach/supermq v0.18.3 h1:IUIZ/uCZ+ZH1O4/fOZwCZOrrll6kImlLuA6Gbye7HtU=
github.com/absmach/supermq v0.18.3/go.mod h1:QAKnqBIYfTMkFfMgWfqGTP0pTIZQxTXCJkTf7Vf7VOo=
github.com/absmach/supermq v0.18.4-0.20251223143751-59f8d4e4d7b1 h1:z7WjbHbbgUO0U3ZzFPzmVKFS446+n3YyM4tfc1qENno=
github.com/absmach/supermq v0.18.4-0.20251223143751-59f8d4e4d7b1/go.mod h1:euXoe+V0bSYi9bz4ljRl60N6R80Lc2ehkq0Lj/XfePg=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@@ -113,8 +113,8 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfUKS7KJ7spH3d86P8=
github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU=
github.com/envoyproxy/protoc-gen-validate v1.3.0 h1:TvGH1wof4H33rezVKWSpqKz5NXWg5VPuZ0uONDT6eb4=
github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
@@ -382,12 +382,12 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pion/dtls/v3 v3.0.7 h1:bItXtTYYhZwkPFk4t1n3Kkf5TDrfj6+4wG+CZR8uI9Q=
github.com/pion/dtls/v3 v3.0.7/go.mod h1:uDlH5VPrgOQIw59irKYkMudSFprY9IEFCqz/eTz16f8=
github.com/pion/dtls/v3 v3.0.9 h1:4AijfFRm8mAjd1gfdlB1wzJF3fjjR/VPIpJgkEtvYmM=
github.com/pion/dtls/v3 v3.0.9/go.mod h1:abApPjgadS/ra1wvUzHLc3o2HvoxppAh+NZkyApL4Os=
github.com/pion/logging v0.2.4 h1:tTew+7cmQ+Mc1pTBLKH2puKsOvhm32dROumOZ655zB8=
github.com/pion/logging v0.2.4/go.mod h1:DffhXTKYdNZU+KtJ5pyQDjvOAh/GsNSyv1lbkFbe3so=
github.com/pion/transport/v3 v3.0.8 h1:oI3myyYnTKUSTthu/NZZ8eu2I5sHbxbUNNFW62olaYc=
github.com/pion/transport/v3 v3.0.8/go.mod h1:+c2eewC5WJQHiAA46fkMMzoYZSuGzA/7E2FPrOYHctQ=
github.com/pion/transport/v3 v3.1.1 h1:Tr684+fnnKlhPceU+ICdrw6KKkTms+5qHMgw6bIkYOM=
github.com/pion/transport/v3 v3.1.1/go.mod h1:+c2eewC5WJQHiAA46fkMMzoYZSuGzA/7E2FPrOYHctQ=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -521,16 +521,16 @@ github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.64.0 h1:RN3ifU8y4prNWeEnQp2kRRHz8UwonAEYZl8tUzHEXAk=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.64.0/go.mod h1:habDz3tEWiFANTo6oUE99EmaFUrCNYAAg3wiVmusm70=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 h1:ssfIgGNANqpVFCndZvcuyKbl0g+UAVcbBcqGkG28H0Y=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0/go.mod h1:GQ/474YrbE4Jx8gZ4q5I4hrhUzM6UPzyrqJYV2AqPoQ=
go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 h1:aTL7F04bJHUlztTsNGJ2l+6he8c+y/b//eR0jjjemT4=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0/go.mod h1:kldtb7jDTeol0l3ewcmd8SDvx3EmIE7lyvqbasU3QC4=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 h1:f0cb2XPmrqn4XMy9PNliTgRKJgS5WcL/u0/WRYGz4t0=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0/go.mod h1:vnakAaFckOMiMtOIhFI2MNH4FYrZzXCYxmb1LlhoGz8=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0 h1:Ckwye2FpXkYgiHX7fyVrN1uA/UYd9ounqqTuSNAv0k4=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.39.0/go.mod h1:teIFJh5pW2y+AN7riv6IBPX2DuesS3HgP39mwOspKwU=
go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
@@ -575,8 +575,8 @@ golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ=
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20251017212417-90e834f514db h1:by6IehL4BH5k3e3SJmcoNbOobMey2SLpAF79iPOEBvw=
golang.org/x/exp v0.0.0-20251017212417-90e834f514db/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70=
@@ -613,8 +613,8 @@ golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo=
golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -676,8 +676,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
@@ -711,10 +711,10 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto/googleapis/api v0.0.0-20251124214823-79d6a2a48846 h1:ZdyUkS9po3H7G0tuh955QVyyotWvOD4W0aEapeGeUYk=
google.golang.org/genproto/googleapis/api v0.0.0-20251124214823-79d6a2a48846/go.mod h1:Fk4kyraUvqD7i5H6S43sj2W98fbZa75lpZz/eUyhfO0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846 h1:Wgl1rcDNThT+Zn47YyCXOXyX/COgMTIdhJ717F0l4xk=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251124214823-79d6a2a48846/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls=
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
+4 -4
View File
@@ -412,7 +412,7 @@ func TestListRulesEndpoint(t *testing.T) {
token: validToken,
query: "offset=invalid",
status: http.StatusBadRequest,
err: apiutil.ErrValidation,
err: apiutil.ErrInvalidQueryParams,
},
{
desc: "list rules with limit",
@@ -433,7 +433,7 @@ func TestListRulesEndpoint(t *testing.T) {
token: validToken,
query: "limit=invalid",
status: http.StatusBadRequest,
err: apiutil.ErrValidation,
err: apiutil.ErrInvalidQueryParams,
},
{
desc: "list rules with invalid direction",
@@ -449,7 +449,7 @@ func TestListRulesEndpoint(t *testing.T) {
token: validToken,
query: "order=invalid",
status: http.StatusBadRequest,
err: apiutil.ErrInvalidOrder,
err: apiutil.ErrValidation,
},
{
desc: "list rule with limit that is too big",
@@ -497,7 +497,7 @@ func TestListRulesEndpoint(t *testing.T) {
token: validToken,
query: "status=invalid",
status: http.StatusBadRequest,
err: apiutil.ErrValidation,
err: svcerr.ErrInvalidStatus,
},
{
desc: "list rules with duplicate status",
+31 -186
View File
@@ -5,14 +5,13 @@ package middleware
import (
"context"
"time"
"github.com/absmach/magistrala/re"
"github.com/absmach/supermq/pkg/authn"
smqauthz "github.com/absmach/supermq/pkg/authz"
"github.com/absmach/supermq/pkg/callout"
"github.com/absmach/supermq/pkg/errors"
"github.com/absmach/supermq/pkg/messaging"
"github.com/absmach/supermq/pkg/permissions"
"github.com/absmach/supermq/pkg/policies"
)
@@ -23,237 +22,88 @@ var (
errDomainDeleteRules = errors.New("not authorized to delete rules in domain")
)
const entityType = "rule"
type authorizationMiddleware struct {
svc re.Service
authz smqauthz.Authorization
callout callout.Callout
svc re.Service
authz smqauthz.Authorization
}
// AuthorizationMiddleware adds authorization to the re service.
func AuthorizationMiddleware(svc re.Service, authz smqauthz.Authorization, callout callout.Callout) (re.Service, error) {
func AuthorizationMiddleware(svc re.Service, authz smqauthz.Authorization) (re.Service, error) {
return &authorizationMiddleware{
svc: svc,
authz: authz,
callout: callout,
svc: svc,
authz: authz,
}, nil
}
func (am *authorizationMiddleware) AddRule(ctx context.Context, session authn.Session, r re.Rule) (re.Rule, error) {
if err := am.authorize(ctx, smqauthz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: session.DomainUserID,
Object: session.DomainID,
ObjectType: policies.DomainType,
Permission: policies.MembershipPermission,
}); err != nil {
if err := am.authorize(ctx, re.OpAddRule, session); err != nil {
return re.Rule{}, errors.Wrap(errDomainCreateRules, err)
}
params := map[string]any{
"entities": r,
"count": 1,
}
if err := am.callOut(ctx, session, re.OpAddRule, params); err != nil {
return re.Rule{}, err
}
return am.svc.AddRule(ctx, session, r)
}
func (am *authorizationMiddleware) ViewRule(ctx context.Context, session authn.Session, id string) (re.Rule, error) {
if err := am.authorize(ctx, smqauthz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: session.DomainUserID,
Object: session.DomainID,
ObjectType: policies.DomainType,
Permission: policies.MembershipPermission,
}); err != nil {
if err := am.authorize(ctx, re.OpViewRule, session); err != nil {
return re.Rule{}, errors.Wrap(errDomainViewRules, err)
}
params := map[string]any{
"entity_id": id,
}
if err := am.callOut(ctx, session, re.OpViewRule, params); err != nil {
return re.Rule{}, err
}
return am.svc.ViewRule(ctx, session, id)
}
func (am *authorizationMiddleware) UpdateRule(ctx context.Context, session authn.Session, r re.Rule) (re.Rule, error) {
if err := am.authorize(ctx, smqauthz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: session.DomainUserID,
Object: session.DomainID,
ObjectType: policies.DomainType,
Permission: policies.MembershipPermission,
}); err != nil {
if err := am.authorize(ctx, re.OpUpdateRule, session); err != nil {
return re.Rule{}, errors.Wrap(errDomainUpdateRules, err)
}
params := map[string]any{
"entity_id": r.ID,
}
if err := am.callOut(ctx, session, re.OpUpdateRule, params); err != nil {
return re.Rule{}, err
}
return am.svc.UpdateRule(ctx, session, r)
}
func (am *authorizationMiddleware) UpdateRuleTags(ctx context.Context, session authn.Session, r re.Rule) (re.Rule, error) {
if err := am.authorize(ctx, smqauthz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: session.DomainUserID,
Object: session.DomainID,
ObjectType: policies.DomainType,
Permission: policies.MembershipPermission,
}); err != nil {
if err := am.authorize(ctx, re.OpUpdateRuleTags, session); err != nil {
return re.Rule{}, errors.Wrap(errDomainUpdateRules, err)
}
params := map[string]any{
"entity_id": r.ID,
}
if err := am.callOut(ctx, session, re.OpUpdateRuleTags, params); err != nil {
return re.Rule{}, err
}
return am.svc.UpdateRuleTags(ctx, session, r)
}
func (am *authorizationMiddleware) UpdateRuleSchedule(ctx context.Context, session authn.Session, r re.Rule) (re.Rule, error) {
if err := am.authorize(ctx, smqauthz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: session.DomainUserID,
Object: session.DomainID,
ObjectType: policies.DomainType,
Permission: policies.MembershipPermission,
}); err != nil {
if err := am.authorize(ctx, re.OpUpdateRuleSchedule, session); err != nil {
return re.Rule{}, errors.Wrap(errDomainUpdateRules, err)
}
params := map[string]any{
"entity_id": r.ID,
}
if err := am.callOut(ctx, session, re.OpUpdateRuleSchedule, params); err != nil {
return re.Rule{}, err
}
return am.svc.UpdateRuleSchedule(ctx, session, r)
}
func (am *authorizationMiddleware) ListRules(ctx context.Context, session authn.Session, pm re.PageMeta) (re.Page, error) {
if err := am.authorize(ctx, smqauthz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: session.DomainUserID,
Object: session.DomainID,
ObjectType: policies.DomainType,
Permission: policies.MembershipPermission,
}); err != nil {
if err := am.authorize(ctx, re.OpListRules, session); err != nil {
return re.Page{}, errors.Wrap(errDomainViewRules, err)
}
params := map[string]any{
"pagemeta": pm,
}
if err := am.callOut(ctx, session, re.OpListRules, params); err != nil {
return re.Page{}, err
}
return am.svc.ListRules(ctx, session, pm)
}
func (am *authorizationMiddleware) RemoveRule(ctx context.Context, session authn.Session, id string) error {
if err := am.authorize(ctx, smqauthz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: session.DomainUserID,
Object: session.DomainID,
ObjectType: policies.DomainType,
Permission: policies.MembershipPermission,
}); err != nil {
if err := am.authorize(ctx, re.OpRemoveRule, session); err != nil {
return errors.Wrap(errDomainDeleteRules, err)
}
params := map[string]any{
"entity_id": id,
}
if err := am.callOut(ctx, session, re.OpRemoveRule, params); err != nil {
return err
}
return am.svc.RemoveRule(ctx, session, id)
}
func (am *authorizationMiddleware) EnableRule(ctx context.Context, session authn.Session, id string) (re.Rule, error) {
if err := am.authorize(ctx, smqauthz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: session.DomainUserID,
Object: session.DomainID,
ObjectType: policies.DomainType,
Permission: policies.MembershipPermission,
}); err != nil {
if err := am.authorize(ctx, re.OpEnableRule, session); err != nil {
return re.Rule{}, errors.Wrap(errDomainUpdateRules, err)
}
params := map[string]any{
"entity_id": id,
}
if err := am.callOut(ctx, session, re.OpEnableRule, params); err != nil {
return re.Rule{}, err
}
return am.svc.EnableRule(ctx, session, id)
}
func (am *authorizationMiddleware) DisableRule(ctx context.Context, session authn.Session, id string) (re.Rule, error) {
if err := am.authorize(ctx, smqauthz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: session.DomainUserID,
Object: session.DomainID,
ObjectType: policies.DomainType,
Permission: policies.MembershipPermission,
}); err != nil {
if err := am.authorize(ctx, re.OpDisableRule, session); err != nil {
return re.Rule{}, errors.Wrap(errDomainUpdateRules, err)
}
params := map[string]any{
"entity_id": id,
}
if err := am.callOut(ctx, session, re.OpDisableRule, params); err != nil {
return re.Rule{}, err
}
return am.svc.DisableRule(ctx, session, id)
}
@@ -269,28 +119,23 @@ func (am *authorizationMiddleware) Cancel() error {
return am.svc.Cancel()
}
func (am *authorizationMiddleware) authorize(ctx context.Context, pr smqauthz.PolicyReq) error {
if err := am.authz.Authorize(ctx, pr); err != nil {
return err
}
return nil
}
func (am *authorizationMiddleware) callOut(ctx context.Context, session authn.Session, op string, params map[string]any) error {
req := callout.Request{
BaseRequest: callout.BaseRequest{
EntityType: entityType,
CallerID: session.UserID,
CallerType: policies.UserType,
DomainID: session.DomainID,
Time: time.Now().UTC(),
Operation: op,
},
}
if err := am.callout.Callout(ctx, req); err != nil {
func (am *authorizationMiddleware) authorize(ctx context.Context, op permissions.Operation, session authn.Session) error {
perm, err := re.GetPermission(op)
if err != nil {
return err
}
return nil
pr := smqauthz.PolicyReq{
TokenType: session.Type,
UserID: session.UserID,
Domain: session.DomainID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: session.DomainUserID,
Object: session.DomainID,
ObjectType: policies.DomainType,
Permission: perm,
}
return am.authz.Authorize(ctx, pr)
}
+178
View File
@@ -0,0 +1,178 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package middleware
import (
"context"
"time"
"github.com/absmach/magistrala/re"
"github.com/absmach/supermq/pkg/authn"
"github.com/absmach/supermq/pkg/callout"
"github.com/absmach/supermq/pkg/messaging"
"github.com/absmach/supermq/pkg/policies"
)
var _ re.Service = (*calloutMiddleware)(nil)
type calloutMiddleware struct {
svc re.Service
callout callout.Callout
}
const entityType = "rule"
func NewCallout(svc re.Service, callout callout.Callout) (re.Service, error) {
return &calloutMiddleware{
svc: svc,
callout: callout,
}, nil
}
func (cm *calloutMiddleware) AddRule(ctx context.Context, session authn.Session, r re.Rule) (re.Rule, error) {
params := map[string]any{
"entities": r,
"count": 1,
}
if err := cm.callOut(ctx, session, re.OpAddRuleStr, params); err != nil {
return re.Rule{}, err
}
return cm.svc.AddRule(ctx, session, r)
}
func (cm *calloutMiddleware) ViewRule(ctx context.Context, session authn.Session, id string) (re.Rule, error) {
params := map[string]any{
"entity_id": id,
}
if err := cm.callOut(ctx, session, re.OpViewRuleStr, params); err != nil {
return re.Rule{}, err
}
return cm.svc.ViewRule(ctx, session, id)
}
func (cm *calloutMiddleware) UpdateRule(ctx context.Context, session authn.Session, r re.Rule) (re.Rule, error) {
params := map[string]any{
"entity_id": r.ID,
}
if err := cm.callOut(ctx, session, re.OpUpdateRuleStr, params); err != nil {
return re.Rule{}, err
}
return cm.svc.UpdateRule(ctx, session, r)
}
func (cm *calloutMiddleware) UpdateRuleTags(ctx context.Context, session authn.Session, r re.Rule) (re.Rule, error) {
params := map[string]any{
"entity_id": r.ID,
}
if err := cm.callOut(ctx, session, re.OpUpdateRuleTagsStr, params); err != nil {
return re.Rule{}, err
}
return cm.svc.UpdateRuleTags(ctx, session, r)
}
func (cm *calloutMiddleware) UpdateRuleSchedule(ctx context.Context, session authn.Session, r re.Rule) (re.Rule, error) {
params := map[string]any{
"entity_id": r.ID,
}
if err := cm.callOut(ctx, session, re.OpUpdateRuleScheduleStr, params); err != nil {
return re.Rule{}, err
}
return cm.svc.UpdateRuleSchedule(ctx, session, r)
}
func (cm *calloutMiddleware) ListRules(ctx context.Context, session authn.Session, pm re.PageMeta) (re.Page, error) {
params := map[string]any{
"pagemeta": pm,
}
if err := cm.callOut(ctx, session, re.OpListRulesStr, params); err != nil {
return re.Page{}, err
}
return cm.svc.ListRules(ctx, session, pm)
}
func (cm *calloutMiddleware) RemoveRule(ctx context.Context, session authn.Session, id string) error {
params := map[string]any{
"entity_id": id,
}
if err := cm.callOut(ctx, session, re.OpRemoveRuleStr, params); err != nil {
return err
}
return cm.svc.RemoveRule(ctx, session, id)
}
func (cm *calloutMiddleware) EnableRule(ctx context.Context, session authn.Session, id string) (re.Rule, error) {
params := map[string]any{
"entity_id": id,
}
if err := cm.callOut(ctx, session, re.OpEnableRuleStr, params); err != nil {
return re.Rule{}, err
}
return cm.svc.EnableRule(ctx, session, id)
}
func (cm *calloutMiddleware) DisableRule(ctx context.Context, session authn.Session, id string) (re.Rule, error) {
params := map[string]any{
"entity_id": id,
}
if err := cm.callOut(ctx, session, re.OpDisableRuleStr, params); err != nil {
return re.Rule{}, err
}
return cm.svc.DisableRule(ctx, session, id)
}
func (cm *calloutMiddleware) StartScheduler(ctx context.Context) error {
return cm.svc.StartScheduler(ctx)
}
func (cm *calloutMiddleware) Handle(msg *messaging.Message) error {
return cm.svc.Handle(msg)
}
func (cm *calloutMiddleware) Cancel() error {
return cm.svc.Cancel()
}
func (cm *calloutMiddleware) callOut(ctx context.Context, session authn.Session, op string, pld map[string]any) error {
var entityID string
if id, ok := pld["entity_id"].(string); ok {
entityID = id
}
req := callout.Request{
BaseRequest: callout.BaseRequest{
Operation: op,
EntityType: entityType,
EntityID: entityID,
CallerID: session.UserID,
CallerType: policies.UserType,
DomainID: session.DomainID,
Time: time.Now().UTC(),
},
Payload: pld,
}
if err := cm.callout.Callout(ctx, req); err != nil {
return err
}
return nil
}
+41
View File
@@ -0,0 +1,41 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package re
import (
"github.com/absmach/supermq/pkg/errors"
"github.com/absmach/supermq/pkg/permissions"
"github.com/absmach/supermq/pkg/policies"
)
const (
OpAddRule permissions.Operation = iota
OpViewRule
OpUpdateRule
OpUpdateRuleTags
OpUpdateRuleSchedule
OpListRules
OpRemoveRule
OpEnableRule
OpDisableRule
)
const (
OpAddRuleStr = "OpAddRule"
OpViewRuleStr = "OpViewRule"
OpUpdateRuleStr = "OpUpdateRule"
OpUpdateRuleTagsStr = "OpUpdateRuleTags"
OpUpdateRuleScheduleStr = "OpUpdateRuleSchedule"
OpListRulesStr = "OpListRules"
OpRemoveRuleStr = "OpRemoveRule"
OpEnableRuleStr = "OpEnableRule"
OpDisableRuleStr = "OpDisableRule"
)
func GetPermission(op permissions.Operation) (string, error) {
if op < OpAddRule || op > OpDisableRule {
return "", errors.New("invalid operation")
}
return policies.MembershipPermission, nil
}
-12
View File
@@ -20,18 +20,6 @@ const (
GoType
)
const (
OpAddRule = "OpAddRule"
OpViewRule = "OpViewRule"
OpUpdateRule = "OpUpdateRule"
OpUpdateRuleTags = "OpUpdateRuleTags"
OpUpdateRuleSchedule = "OpUpdateRuleSchedule"
OpListRules = "OpListRules"
OpRemoveRule = "OpRemoveRule"
OpEnableRule = "OpEnableRule"
OpDisableRule = "OpDisableRule"
)
type (
// ScriptType indicates Runtime type for the future versions
// that will support JS or Go runtimes alongside Lua.
+3 -3
View File
@@ -235,7 +235,7 @@ func TestReadAll(t *testing.T) {
token: "",
authResponse: false,
authnErr: svcerr.ErrAuthentication,
status: http.StatusUnauthorized,
status: http.StatusBadRequest,
err: svcerr.ErrAuthentication,
},
{
@@ -623,7 +623,7 @@ func TestReadAll(t *testing.T) {
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=10", ts.URL, domainID, chanID),
token: invalidToken,
authResponse: false,
status: http.StatusUnauthorized,
status: http.StatusForbidden,
err: svcerr.ErrAuthorization,
},
{
@@ -645,7 +645,7 @@ func TestReadAll(t *testing.T) {
url: fmt.Sprintf("%s/%s/channels/%s/messages?offset=0&limit=10", ts.URL, domainID, chanID),
token: "",
authResponse: false,
status: http.StatusUnauthorized,
status: http.StatusBadRequest,
err: svcerr.ErrAuthorization,
},
{
+5 -9
View File
@@ -210,15 +210,14 @@ func encodeResponse(_ context.Context, w http.ResponseWriter, response any) erro
}
func encodeError(_ context.Context, err error, w http.ResponseWriter) {
var wrapper error
if errors.Contains(err, apiutil.ErrValidation) {
wrapper, err = errors.Unwrap(err)
}
w.Header().Set("Content-Type", contentType)
switch {
case errors.Contains(err, nil):
w.WriteHeader(http.StatusOK)
case errors.Contains(err, apiutil.ErrInvalidQueryParams),
errors.Contains(err, svcerr.ErrMalformedEntity),
errors.Contains(err, apiutil.ErrValidation),
errors.Contains(err, apiutil.ErrMissingID),
errors.Contains(err, apiutil.ErrLimitSize),
errors.Contains(err, apiutil.ErrOffsetSize),
@@ -230,20 +229,17 @@ func encodeError(_ context.Context, err error, w http.ResponseWriter) {
errors.Contains(err, apiutil.ErrMissingDomainID):
w.WriteHeader(http.StatusBadRequest)
case errors.Contains(err, svcerr.ErrAuthentication),
errors.Contains(err, svcerr.ErrAuthorization),
errors.Contains(err, apiutil.ErrBearerToken):
w.WriteHeader(http.StatusUnauthorized)
case errors.Contains(err, svcerr.ErrAuthorization):
w.WriteHeader(http.StatusForbidden)
case errors.Contains(err, readers.ErrReadMessages):
w.WriteHeader(http.StatusInternalServerError)
default:
w.WriteHeader(http.StatusInternalServerError)
}
if wrapper != nil {
err = errors.Wrap(wrapper, err)
}
if errorVal, ok := err.(errors.Error); ok {
w.Header().Set("Content-Type", contentType)
if err := json.NewEncoder(w).Encode(errorVal); err != nil {
w.WriteHeader(http.StatusInternalServerError)
}
+28 -111
View File
@@ -10,6 +10,7 @@ import (
"github.com/absmach/supermq/pkg/authn"
smqauthz "github.com/absmach/supermq/pkg/authz"
"github.com/absmach/supermq/pkg/errors"
"github.com/absmach/supermq/pkg/permissions"
"github.com/absmach/supermq/pkg/policies"
)
@@ -39,15 +40,7 @@ func AuthorizationMiddleware(svc reports.Service, authz smqauthz.Authorization)
}
func (am *authorizationMiddleware) AddReportConfig(ctx context.Context, session authn.Session, cfg reports.ReportConfig) (reports.ReportConfig, error) {
if err := am.authorize(ctx, smqauthz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: session.DomainUserID,
Object: session.DomainID,
ObjectType: policies.DomainType,
Permission: policies.MembershipPermission,
}); err != nil {
if err := am.authorize(ctx, reports.OpAddReportConfig, session); err != nil {
return reports.ReportConfig{}, errors.Wrap(errDomainCreateConfigs, err)
}
@@ -55,15 +48,7 @@ func (am *authorizationMiddleware) AddReportConfig(ctx context.Context, session
}
func (am *authorizationMiddleware) ViewReportConfig(ctx context.Context, session authn.Session, id string) (reports.ReportConfig, error) {
if err := am.authorize(ctx, smqauthz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: session.DomainUserID,
Object: session.DomainID,
ObjectType: policies.DomainType,
Permission: policies.MembershipPermission,
}); err != nil {
if err := am.authorize(ctx, reports.OpViewReportConfig, session); err != nil {
return reports.ReportConfig{}, errors.Wrap(errDomainViewConfigs, err)
}
@@ -71,15 +56,7 @@ func (am *authorizationMiddleware) ViewReportConfig(ctx context.Context, session
}
func (am *authorizationMiddleware) UpdateReportConfig(ctx context.Context, session authn.Session, cfg reports.ReportConfig) (reports.ReportConfig, error) {
if err := am.authorize(ctx, smqauthz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: session.DomainUserID,
Object: session.DomainID,
ObjectType: policies.DomainType,
Permission: policies.MembershipPermission,
}); err != nil {
if err := am.authorize(ctx, reports.OpUpdateReportConfig, session); err != nil {
return reports.ReportConfig{}, errors.Wrap(errDomainUpdateConfigs, err)
}
@@ -87,15 +64,7 @@ func (am *authorizationMiddleware) UpdateReportConfig(ctx context.Context, sessi
}
func (am *authorizationMiddleware) UpdateReportSchedule(ctx context.Context, session authn.Session, cfg reports.ReportConfig) (reports.ReportConfig, error) {
if err := am.authorize(ctx, smqauthz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: session.DomainUserID,
Object: session.DomainID,
ObjectType: policies.DomainType,
Permission: policies.MembershipPermission,
}); err != nil {
if err := am.authorize(ctx, reports.OpUpdateReportSchedule, session); err != nil {
return reports.ReportConfig{}, errors.Wrap(errDomainDeleteConfigs, err)
}
@@ -103,15 +72,7 @@ func (am *authorizationMiddleware) UpdateReportSchedule(ctx context.Context, ses
}
func (am *authorizationMiddleware) RemoveReportConfig(ctx context.Context, session authn.Session, id string) error {
if err := am.authorize(ctx, smqauthz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: session.DomainUserID,
Object: session.DomainID,
ObjectType: policies.DomainType,
Permission: policies.MembershipPermission,
}); err != nil {
if err := am.authorize(ctx, reports.OpRemoveReportConfig, session); err != nil {
return errors.Wrap(errDomainDeleteConfigs, err)
}
@@ -119,15 +80,7 @@ func (am *authorizationMiddleware) RemoveReportConfig(ctx context.Context, sessi
}
func (am *authorizationMiddleware) ListReportsConfig(ctx context.Context, session authn.Session, pm reports.PageMeta) (reports.ReportConfigPage, error) {
if err := am.authorize(ctx, smqauthz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: session.DomainUserID,
Object: session.DomainID,
ObjectType: policies.DomainType,
Permission: policies.MembershipPermission,
}); err != nil {
if err := am.authorize(ctx, reports.OpListReportsConfig, session); err != nil {
return reports.ReportConfigPage{}, errors.Wrap(errDomainViewConfigs, err)
}
@@ -135,15 +88,7 @@ func (am *authorizationMiddleware) ListReportsConfig(ctx context.Context, sessio
}
func (am *authorizationMiddleware) EnableReportConfig(ctx context.Context, session authn.Session, id string) (reports.ReportConfig, error) {
if err := am.authorize(ctx, smqauthz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: session.DomainUserID,
Object: session.DomainID,
ObjectType: policies.DomainType,
Permission: policies.MembershipPermission,
}); err != nil {
if err := am.authorize(ctx, reports.OpEnableReportConfig, session); err != nil {
return reports.ReportConfig{}, errors.Wrap(errDomainUpdateConfigs, err)
}
@@ -151,15 +96,7 @@ func (am *authorizationMiddleware) EnableReportConfig(ctx context.Context, sessi
}
func (am *authorizationMiddleware) DisableReportConfig(ctx context.Context, session authn.Session, id string) (reports.ReportConfig, error) {
if err := am.authorize(ctx, smqauthz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: session.DomainUserID,
Object: session.DomainID,
ObjectType: policies.DomainType,
Permission: policies.MembershipPermission,
}); err != nil {
if err := am.authorize(ctx, reports.OpDisableReportConfig, session); err != nil {
return reports.ReportConfig{}, errors.Wrap(errDomainUpdateConfigs, err)
}
@@ -167,15 +104,7 @@ func (am *authorizationMiddleware) DisableReportConfig(ctx context.Context, sess
}
func (am *authorizationMiddleware) GenerateReport(ctx context.Context, session authn.Session, config reports.ReportConfig, action reports.ReportAction) (reports.ReportPage, error) {
if err := am.authorize(ctx, smqauthz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: session.DomainUserID,
Object: session.DomainID,
ObjectType: policies.DomainType,
Permission: policies.MembershipPermission,
}); err != nil {
if err := am.authorize(ctx, reports.OpGenerateReport, session); err != nil {
return reports.ReportPage{}, errors.Wrap(errDomainGenerateReports, err)
}
@@ -183,15 +112,7 @@ func (am *authorizationMiddleware) GenerateReport(ctx context.Context, session a
}
func (am *authorizationMiddleware) UpdateReportTemplate(ctx context.Context, session authn.Session, cfg reports.ReportConfig) error {
if err := am.authorize(ctx, smqauthz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: session.DomainUserID,
Object: session.DomainID,
ObjectType: policies.DomainType,
Permission: policies.MembershipPermission,
}); err != nil {
if err := am.authorize(ctx, reports.OpUpdateReportTemplate, session); err != nil {
return errors.Wrap(errDomainUpdateTemplates, err)
}
@@ -199,15 +120,7 @@ func (am *authorizationMiddleware) UpdateReportTemplate(ctx context.Context, ses
}
func (am *authorizationMiddleware) ViewReportTemplate(ctx context.Context, session authn.Session, id string) (reports.ReportTemplate, error) {
if err := am.authorize(ctx, smqauthz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: session.DomainUserID,
Object: session.DomainID,
ObjectType: policies.DomainType,
Permission: policies.MembershipPermission,
}); err != nil {
if err := am.authorize(ctx, reports.OpViewReportTemplate, session); err != nil {
return "", errors.Wrap(errDomainViewTemplates, err)
}
@@ -215,15 +128,7 @@ func (am *authorizationMiddleware) ViewReportTemplate(ctx context.Context, sessi
}
func (am *authorizationMiddleware) DeleteReportTemplate(ctx context.Context, session authn.Session, id string) error {
if err := am.authorize(ctx, smqauthz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: session.DomainUserID,
Object: session.DomainID,
ObjectType: policies.DomainType,
Permission: policies.MembershipPermission,
}); err != nil {
if err := am.authorize(ctx, reports.OpDeleteReportTemplate, session); err != nil {
return errors.Wrap(errDomainRemoveTemplates, err)
}
@@ -234,9 +139,21 @@ func (am *authorizationMiddleware) StartScheduler(ctx context.Context) error {
return am.svc.StartScheduler(ctx)
}
func (am *authorizationMiddleware) authorize(ctx context.Context, pr smqauthz.PolicyReq) error {
if err := am.authz.Authorize(ctx, pr); err != nil {
func (am *authorizationMiddleware) authorize(ctx context.Context, op permissions.Operation, session authn.Session) error {
perm, err := reports.GetPermission(op)
if err != nil {
return err
}
return nil
pr := smqauthz.PolicyReq{
Domain: session.DomainID,
SubjectType: policies.UserType,
SubjectKind: policies.UsersKind,
Subject: session.DomainUserID,
Object: session.DomainID,
ObjectType: policies.DomainType,
Permission: perm,
}
return am.authz.Authorize(ctx, pr)
}
+32
View File
@@ -0,0 +1,32 @@
// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package reports
import (
"github.com/absmach/supermq/pkg/errors"
"github.com/absmach/supermq/pkg/permissions"
"github.com/absmach/supermq/pkg/policies"
)
const (
OpAddReportConfig = iota
OpViewReportConfig
OpUpdateReportConfig
OpUpdateReportSchedule
OpRemoveReportConfig
OpListReportsConfig
OpEnableReportConfig
OpDisableReportConfig
OpGenerateReport
OpUpdateReportTemplate
OpViewReportTemplate
OpDeleteReportTemplate
)
func GetPermission(op permissions.Operation) (string, error) {
if op < OpAddReportConfig || op > OpDeleteReportTemplate {
return "", errors.New("invalid operation")
}
return policies.MembershipPermission, nil
}