MG-133 - Prevent Rule scheduling in past (#211)

* add validation during unmarshalling

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

* move validation to individual requests

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

* fix failing tests

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

* revert error

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

* revert error

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

---------

Signed-off-by: nyagamunene <stevenyaga2014@gmail.com>
This commit is contained in:
Steve Munene
2025-06-23 14:15:32 +03:00
committed by GitHub
parent cc18373844
commit 8f45405efb
5 changed files with 71 additions and 2 deletions
+1
View File
@@ -97,6 +97,7 @@ func (s *Schedule) UnmarshalJSON(data []byte) error {
if err != nil {
return err
}
s.StartDateTime = startDateTime
if aux.Time != "" {
+22 -1
View File
@@ -42,7 +42,7 @@ var (
invalidToken = "invalid"
now = time.Now().UTC().Truncate(time.Minute)
schedule = pkgSch.Schedule{
StartDateTime: now.Add(-1 * time.Hour),
StartDateTime: now.Add(1 * time.Hour),
Recurring: pkgSch.Daily,
RecurringPeriod: 1,
Time: now,
@@ -109,6 +109,16 @@ func TestAddRuleEndpoint(t *testing.T) {
ts, svc, authn := newRuleEngineServer()
defer ts.Close()
scheduleInPast := pkgSch.Schedule{
StartDateTime: now.Add(-1 * time.Hour),
Recurring: pkgSch.Daily,
RecurringPeriod: 1,
Time: now,
}
ruleInPast := rule
ruleInPast.Schedule = scheduleInPast
cases := []struct {
desc string
rule re.Rule
@@ -187,6 +197,17 @@ func TestAddRuleEndpoint(t *testing.T) {
status: http.StatusUnsupportedMediaType,
err: apiutil.ErrUnsupportedContentType,
},
{
desc: "add rule with startdatetime in past",
token: validToken,
domainID: domainID,
authnRes: smqauthn.Session{DomainUserID: auth.EncodeDomainUserID(domainID, userID), UserID: userID, DomainID: domainID},
rule: ruleInPast,
contentType: contentType,
svcErr: svcerr.ErrAuthorization,
status: http.StatusBadRequest,
err: apiutil.ErrValidation,
},
{
desc: "add rule with service error",
token: validToken,
+14
View File
@@ -4,10 +4,13 @@
package api
import (
"time"
"github.com/absmach/magistrala/pkg/schedule"
"github.com/absmach/magistrala/re"
api "github.com/absmach/supermq/api/http"
apiutil "github.com/absmach/supermq/api/http/util"
"github.com/absmach/supermq/pkg/errors"
)
const (
@@ -16,6 +19,8 @@ const (
MaxTitleSize = 37
)
var ErrStartDateTimeInPast = errors.New("start_datetime must be greater than or equal to current time")
type addRuleReq struct {
re.Rule
}
@@ -24,6 +29,10 @@ func (req addRuleReq) validate() error {
if len(req.Name) > api.MaxNameSize || req.Name == "" {
return apiutil.ErrNameSize
}
now := time.Now().UTC()
if req.Schedule.StartDateTime.Before(now) {
return errors.Wrap(ErrStartDateTimeInPast, apiutil.ErrValidation)
}
return nil
}
@@ -92,6 +101,11 @@ func (req updateRuleScheduleReq) validate() error {
return apiutil.ErrMissingID
}
now := time.Now().UTC()
if req.Schedule.StartDateTime.Before(now) {
return errors.Wrap(ErrStartDateTimeInPast, apiutil.ErrValidation)
}
return nil
}
+22 -1
View File
@@ -42,7 +42,7 @@ var (
invalidToken = "invalid"
now = time.Now().UTC().Truncate(time.Minute)
schedule = pkgSch.Schedule{
StartDateTime: now.Add(-1 * time.Hour),
StartDateTime: now.Add(1 * time.Hour),
Recurring: pkgSch.Daily,
RecurringPeriod: 1,
Time: now,
@@ -125,6 +125,16 @@ func TestAddReportConfigEndpoint(t *testing.T) {
ts, svc, authn := newReportsServer()
defer ts.Close()
scheduleInPast := pkgSch.Schedule{
StartDateTime: now.Add(-1 * time.Hour),
Recurring: pkgSch.Daily,
RecurringPeriod: 1,
Time: now,
}
reportInPast := reportConfig
reportInPast.Schedule = scheduleInPast
cases := []struct {
desc string
cfg reports.ReportConfig
@@ -186,6 +196,17 @@ func TestAddReportConfigEndpoint(t *testing.T) {
status: http.StatusUnsupportedMediaType,
err: apiutil.ErrUnsupportedContentType,
},
{
desc: "add report config with startdatetime in past",
token: validToken,
domainID: domainID,
authnRes: smqauthn.Session{DomainUserID: auth.EncodeDomainUserID(domainID, userID), UserID: userID, DomainID: domainID},
cfg: reportInPast,
contentType: contentType,
svcErr: svcerr.ErrAuthorization,
status: http.StatusBadRequest,
err: apiutil.ErrValidation,
},
{
desc: "add report config with service error",
token: validToken,
+12
View File
@@ -5,6 +5,7 @@ package api
import (
"fmt"
"time"
"github.com/absmach/magistrala/pkg/schedule"
"github.com/absmach/magistrala/reports"
@@ -21,6 +22,8 @@ const (
errInvalidMetric = "invalid metric[%d]: %w"
)
var ErrStartDateTimeInPast = errors.New("start_datetime must be greater than or equal to current time")
var (
errInvalidReportAction = errors.New("invalid report action")
errMetricsNotProvided = errors.New("metrics not provided")
@@ -38,6 +41,10 @@ func (req addReportConfigReq) validate() error {
if req.Name == "" {
return apiutil.ErrMissingName
}
now := time.Now().UTC()
if req.Schedule.StartDateTime.Before(now) {
return errors.Wrap(apiutil.ErrValidation, ErrStartDateTimeInPast)
}
return validateReportConfig(req.ReportConfig, false, false)
}
@@ -84,6 +91,11 @@ func (req updateReportScheduleReq) validate() error {
return apiutil.ErrMissingID
}
now := time.Now().UTC()
if req.Schedule.StartDateTime.Before(now) {
return errors.Wrap(apiutil.ErrValidation, ErrStartDateTimeInPast)
}
return nil
}