Files
Steve Munene 178a62c08f MG-370 - Add fine grained access control to reports (#403)
* add access control to rules engine

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

* fix build

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

* remove unused variable

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

* fix report database

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

* fix variable naming

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

* fix entity type

Signed-off-by: Arvindh <arvindh91@gmail.com>

* update authorize method

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

* fix generate report

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

* revert env changes

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

* fix linter

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

* fix failing linter

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

* update generate permission

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

* revert go mod file

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

* revert go mod file

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

---------

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>
Signed-off-by: Arvindh <arvindh91@gmail.com>
Co-authored-by: Arvindh <arvindh91@gmail.com>
2026-03-05 13:59:22 +01:00

243 lines
5.5 KiB
Go

// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package api
import (
"fmt"
"github.com/absmach/magistrala/pkg/schedule"
"github.com/absmach/magistrala/reports"
api "github.com/absmach/supermq/api/http"
apiutil "github.com/absmach/supermq/api/http/util"
"github.com/absmach/supermq/pkg/errors"
svcerr "github.com/absmach/supermq/pkg/errors/service"
)
const (
maxLimitSize = 1000
MaxNameSize = 1024
MaxTitleSize = 37
errInvalidMetric = "invalid metric[%d]: %w"
)
var (
errInvalidReportAction = errors.New("invalid report action")
errMetricsNotProvided = errors.New("metrics not provided")
errMissingReportConfig = errors.New("missing report config")
errMissingReportEmailConfig = errors.New("missing report email config")
errInvalidRecurringPeriod = errors.New("invalid recurring period")
errMissingReportTemplate = errors.New("missing report template")
errTitleSize = errors.New("invalid title size")
)
type addReportConfigReq struct {
reports.ReportConfig `json:",inline"`
}
func (req addReportConfigReq) validate() error {
if req.Name == "" {
return apiutil.ErrMissingName
}
if err := req.Schedule.Validate(); err != nil {
return errors.Wrap(err, apiutil.ErrValidation)
}
if req.ReportTemplate.String() != "" {
if err := req.ReportTemplate.Validate(); err != nil {
return errors.Wrap(err, apiutil.ErrValidation)
}
}
return validateReportConfig(req.ReportConfig, false, false)
}
type viewReportConfigReq struct {
ID string `json:"id"`
withRoles bool
}
func (req viewReportConfigReq) validate() error {
if req.ID == "" {
return apiutil.ErrMissingID
}
return nil
}
type listReportsConfigReq struct {
reports.PageMeta `json:",inline"`
}
func (req listReportsConfigReq) validate() error {
if req.Limit > maxLimitSize {
return svcerr.ErrMalformedEntity
}
switch req.Order {
case "", api.NameKey, api.CreatedAtOrder, api.UpdatedAtOrder:
default:
return apiutil.ErrInvalidOrder
}
if req.Dir != api.AscDir && req.Dir != api.DescDir {
return apiutil.ErrInvalidDirection
}
return nil
}
type updateReportConfigReq struct {
reports.ReportConfig `json:",inline"`
}
func (req updateReportConfigReq) validate() error {
if req.ID == "" {
return apiutil.ErrMissingID
}
return validateReportConfig(req.ReportConfig, false, false)
}
type updateReportScheduleReq struct {
id string
Schedule schedule.Schedule `json:"schedule,omitempty"`
}
func (req updateReportScheduleReq) validate() error {
if req.id == "" {
return apiutil.ErrMissingID
}
if err := req.Schedule.Validate(); err != nil {
return errors.Wrap(err, apiutil.ErrValidation)
}
return nil
}
type deleteReportConfigReq struct {
ID string `json:"id"`
}
func (req deleteReportConfigReq) validate() error {
if req.ID == "" {
return apiutil.ErrMissingID
}
return nil
}
type generateReportReq struct {
reports.ReportConfig
action reports.ReportAction
}
func (req generateReportReq) validate() error {
if len(req.Config.Title) > MaxTitleSize {
return errors.Wrap(apiutil.ErrValidation, errTitleSize)
}
if req.ReportTemplate.String() != "" {
if err := req.ReportTemplate.Validate(); err != nil {
return errors.Wrap(err, apiutil.ErrValidation)
}
}
switch req.action {
case reports.ViewReport, reports.DownloadReport:
return validateReportConfig(req.ReportConfig, true, true)
case reports.EmailReport:
return validateReportConfig(req.ReportConfig, false, true)
default:
return errors.Wrap(apiutil.ErrValidation, errInvalidReportAction)
}
}
type updateReportStatusReq struct {
id string
}
func (req updateReportStatusReq) validate() error {
if req.id == "" {
return apiutil.ErrMissingID
}
return nil
}
func validateReportConfig(req reports.ReportConfig, skipEmailValidation bool, skipSchedularValidation bool) error {
if len(req.Metrics) == 0 {
return errors.Wrap(apiutil.ErrValidation, errMetricsNotProvided)
}
for i, metric := range req.Metrics {
if err := metric.Validate(); err != nil {
return errors.Wrap(apiutil.ErrValidation, fmt.Errorf(errInvalidMetric, i+1, err))
}
}
if req.Config == nil {
return errors.Wrap(errMissingReportConfig, apiutil.ErrValidation)
}
if err := req.Config.Validate(); err != nil {
return errors.Wrap(err, apiutil.ErrValidation)
}
if skipEmailValidation {
return nil
}
if req.Email == nil {
return errors.Wrap(errMissingReportEmailConfig, apiutil.ErrValidation)
}
if err := req.Email.Validate(); err != nil {
return errors.Wrap(apiutil.ErrValidation, err)
}
if skipSchedularValidation {
return nil
}
return validateScheduler(req.Schedule)
}
func validateScheduler(sch schedule.Schedule) error {
if sch.Recurring != schedule.None && sch.RecurringPeriod < 1 {
return errInvalidRecurringPeriod
}
return nil
}
type updateReportTemplateReq struct {
reports.ReportConfig `json:",inline"`
}
func (req updateReportTemplateReq) validate() error {
if req.ID == "" {
return apiutil.ErrMissingID
}
if req.ReportTemplate == "" {
return errors.Wrap(errMissingReportTemplate, apiutil.ErrValidation)
}
if err := req.ReportTemplate.Validate(); err != nil {
return errors.Wrap(err, apiutil.ErrValidation)
}
return nil
}
type getReportTemplateReq struct {
ID string `json:"id"`
}
func (req getReportTemplateReq) validate() error {
if req.ID == "" {
return apiutil.ErrMissingID
}
return nil
}
type deleteReportTemplateReq struct {
ID string `json:"id"`
}
func (req deleteReportTemplateReq) validate() error {
if req.ID == "" {
return apiutil.ErrMissingID
}
return nil
}