diff --git a/backend/src/Squidex.Shared/Permissions.cs b/backend/src/Squidex.Shared/Permissions.cs index bc762bdbb..5af03382f 100644 --- a/backend/src/Squidex.Shared/Permissions.cs +++ b/backend/src/Squidex.Shared/Permissions.cs @@ -135,6 +135,10 @@ namespace Squidex.Shared public const string AppRules = "squidex.apps.{app}.rules"; public const string AppRulesRead = "squidex.apps.{app}.rules.read"; public const string AppRulesEvents = "squidex.apps.{app}.rules.events"; + public const string AppRulesEventsRun = "squidex.apps.{app}.rules.events.run"; + public const string AppRulesEventsRead = "squidex.apps.{app}.rules.events.read"; + public const string AppRulesEventsUpdate = "squidex.apps.{app}.rules.events.update"; + public const string AppRulesEventsDelete = "squidex.apps.{app}.rules.events.delete"; public const string AppRulesCreate = "squidex.apps.{app}.rules.create"; public const string AppRulesUpdate = "squidex.apps.{app}.rules.update"; public const string AppRulesDisable = "squidex.apps.{app}.rules.disable"; diff --git a/backend/src/Squidex.Web/Resources.cs b/backend/src/Squidex.Web/Resources.cs index d87f99999..644782d7c 100644 --- a/backend/src/Squidex.Web/Resources.cs +++ b/backend/src/Squidex.Web/Resources.cs @@ -114,7 +114,16 @@ namespace Squidex.Web public bool CanDeleteRule => IsAllowed(Permissions.AppRulesDelete); [Lazy] - public bool CanReadRuleEvents => IsAllowed(Permissions.AppRulesEvents); + public bool CanReadRuleEvents => IsAllowed(Permissions.AppRulesEventsRead); + + [Lazy] + public bool CanUpdateRuleEvents => IsAllowed(Permissions.AppRulesEventsUpdate); + + [Lazy] + public bool CanRunRuleEvents => IsAllowed(Permissions.AppRulesEventsRun); + + [Lazy] + public bool CanDeleteRuleEvents => IsAllowed(Permissions.AppRulesEventsDelete); // Users [Lazy] diff --git a/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleDto.cs index f8bf40c47..6799d4906 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleDto.cs @@ -126,7 +126,7 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models AddPutLink("update", resources.Url(x => nameof(x.PutRule), values)); } - if (resources.CanReadRuleEvents) + if (resources.CanRunRuleEvents) { AddPutLink("trigger", resources.Url(x => nameof(x.TriggerRule), values)); @@ -141,7 +141,10 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models AddPutLink("run/snapshots", resources.Url(x => nameof(x.PutRuleRun), snaphshotValues)); } + } + if (resources.CanReadRuleEvents) + { AddGetLink("logs", resources.Url(x => nameof(x.GetEvents), values)); } diff --git a/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleEventDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleEventDto.cs index 304077089..5847ad345 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleEventDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleEventDto.cs @@ -78,9 +78,12 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models { var values = new { app = resources.App, id = Id }; - AddPutLink("update", resources.Url(x => nameof(x.PutEvent), values)); + if (resources.CanUpdateRuleEvents) + { + AddPutLink("update", resources.Url(x => nameof(x.PutEvent), values)); + } - if (NextAttempt != null) + if (resources.CanDeleteRuleEvents && NextAttempt != null) { AddDeleteLink("cancel", resources.Url(x => nameof(x.DeleteEvent), values)); } diff --git a/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleEventsDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleEventsDto.cs index 76acf4699..7c5a393ae 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleEventsDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RuleEventsDto.cs @@ -42,15 +42,18 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models AddSelfLink(resources.Url(x => nameof(x.GetEvents), values)); - if (ruleId != null) + if (resources.CanDeleteRuleEvents) { - var routeValeus = new { values.app, id = ruleId }; - - AddDeleteLink("cancel", resources.Url(x => nameof(x.DeleteRuleEvents), routeValeus)); - } - else - { - AddDeleteLink("cancel", resources.Url(x => nameof(x.DeleteEvents), values)); + if (ruleId != null) + { + var routeValues = new { values.app, id = ruleId }; + + AddDeleteLink("cancel", resources.Url(x => nameof(x.DeleteRuleEvents), routeValues)); + } + else + { + AddDeleteLink("cancel", resources.Url(x => nameof(x.DeleteEvents), values)); + } } return this; diff --git a/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RulesDto.cs b/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RulesDto.cs index 660dd0f9d..13045d861 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RulesDto.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Rules/Models/RulesDto.cs @@ -54,11 +54,11 @@ namespace Squidex.Areas.Api.Controllers.Rules.Models if (resources.CanReadRuleEvents) { AddGetLink("events", resources.Url(x => nameof(x.GetEvents), values)); + } - if (runningRuleId != null) - { - AddDeleteLink("run/cancel", resources.Url(x => nameof(x.DeleteRuleRun), values)); - } + if (resources.CanDeleteRuleEvents && runningRuleId != null) + { + AddDeleteLink("run/cancel", resources.Url(x => nameof(x.DeleteRuleRun), values)); } return this; diff --git a/backend/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs b/backend/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs index 11792225c..2e80450e8 100644 --- a/backend/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs +++ b/backend/src/Squidex/Areas/Api/Controllers/Rules/RulesController.cs @@ -140,7 +140,7 @@ namespace Squidex.Areas.Api.Controllers.Rules [HttpDelete] [Route("apps/{app}/rules/run")] [ProducesResponseType(StatusCodes.Status204NoContent)] - [ApiPermissionOrAnonymous(Permissions.AppRulesEvents)] + [ApiPermissionOrAnonymous(Permissions.AppRulesEventsUpdate)] [ApiCosts(1)] public async Task DeleteRuleRun(string app) { @@ -231,7 +231,7 @@ namespace Squidex.Areas.Api.Controllers.Rules /// [HttpPut] [Route("apps/{app}/rules/{id}/trigger/")] - [ApiPermissionOrAnonymous(Permissions.AppRulesEvents)] + [ApiPermissionOrAnonymous(Permissions.AppRulesEventsRun)] [ApiCosts(1)] public async Task TriggerRule(string app, DomainId id) { @@ -254,7 +254,7 @@ namespace Squidex.Areas.Api.Controllers.Rules [HttpPut] [Route("apps/{app}/rules/{id}/run")] [ProducesResponseType(StatusCodes.Status204NoContent)] - [ApiPermissionOrAnonymous(Permissions.AppRulesEvents)] + [ApiPermissionOrAnonymous(Permissions.AppRulesEventsRun)] [ApiCosts(1)] public async Task PutRuleRun(string app, DomainId id, [FromQuery] bool fromSnapshots = false) { @@ -274,7 +274,7 @@ namespace Squidex.Areas.Api.Controllers.Rules [HttpDelete] [Route("apps/{app}/rules/{id}/events/")] [ProducesResponseType(StatusCodes.Status204NoContent)] - [ApiPermissionOrAnonymous(Permissions.AppRulesEvents)] + [ApiPermissionOrAnonymous(Permissions.AppRulesEventsDelete)] [ApiCosts(1)] public async Task DeleteRuleEvents(string app, DomainId id) { @@ -295,7 +295,7 @@ namespace Squidex.Areas.Api.Controllers.Rules [HttpPost] [Route("apps/{app}/rules/simulate/")] [ProducesResponseType(typeof(SimulatedRuleEventsDto), StatusCodes.Status200OK)] - [ApiPermissionOrAnonymous(Permissions.AppRulesEvents)] + [ApiPermissionOrAnonymous(Permissions.AppRulesEventsRead)] [ApiCosts(5)] public async Task Simulate(string app, [FromBody] CreateRuleDto request) { @@ -320,7 +320,7 @@ namespace Squidex.Areas.Api.Controllers.Rules [HttpGet] [Route("apps/{app}/rules/{id}/simulate/")] [ProducesResponseType(typeof(SimulatedRuleEventsDto), StatusCodes.Status200OK)] - [ApiPermissionOrAnonymous(Permissions.AppRulesEvents)] + [ApiPermissionOrAnonymous(Permissions.AppRulesEventsRead)] [ApiCosts(5)] public async Task Simulate(string app, DomainId id) { @@ -372,7 +372,7 @@ namespace Squidex.Areas.Api.Controllers.Rules [HttpGet] [Route("apps/{app}/rules/events/")] [ProducesResponseType(typeof(RuleEventsDto), StatusCodes.Status200OK)] - [ApiPermissionOrAnonymous(Permissions.AppRulesRead)] + [ApiPermissionOrAnonymous(Permissions.AppRulesEventsRead)] [ApiCosts(0)] public async Task GetEvents(string app, [FromQuery] DomainId? ruleId = null, [FromQuery] int skip = 0, [FromQuery] int take = 20) { @@ -394,7 +394,7 @@ namespace Squidex.Areas.Api.Controllers.Rules /// [HttpPut] [Route("apps/{app}/rules/events/{id}/")] - [ApiPermissionOrAnonymous(Permissions.AppRulesEvents)] + [ApiPermissionOrAnonymous(Permissions.AppRulesEventsUpdate)] [ApiCosts(0)] public async Task PutEvent(string app, DomainId id) { @@ -420,7 +420,7 @@ namespace Squidex.Areas.Api.Controllers.Rules [HttpDelete] [Route("apps/{app}/rules/events/")] [ProducesResponseType(StatusCodes.Status204NoContent)] - [ApiPermissionOrAnonymous(Permissions.AppRulesEvents)] + [ApiPermissionOrAnonymous(Permissions.AppRulesEventsDelete)] [ApiCosts(1)] public async Task DeleteEvents(string app) { @@ -440,7 +440,7 @@ namespace Squidex.Areas.Api.Controllers.Rules /// [HttpDelete] [Route("apps/{app}/rules/events/{id}/")] - [ApiPermissionOrAnonymous(Permissions.AppRulesEvents)] + [ApiPermissionOrAnonymous(Permissions.AppRulesEventsDelete)] [ApiCosts(0)] public async Task DeleteEvent(string app, DomainId id) { diff --git a/frontend/src/app/features/rules/pages/events/rule-event.component.html b/frontend/src/app/features/rules/pages/events/rule-event.component.html index 308db60fb..05c48cb3b 100644 --- a/frontend/src/app/features/rules/pages/events/rule-event.component.html +++ b/frontend/src/app/features/rules/pages/events/rule-event.component.html @@ -34,7 +34,7 @@ {{ 'rules.ruleEvents.nextAttemptLabel' | sqxTranslate }}: {{event.nextAttempt | sqxFromNow}}
-
diff --git a/frontend/src/app/features/rules/pages/events/rule-event.component.scss b/frontend/src/app/features/rules/pages/events/rule-event.component.scss index 73aa9a079..8c0226230 100644 --- a/frontend/src/app/features/rules/pages/events/rule-event.component.scss +++ b/frontend/src/app/features/rules/pages/events/rule-event.component.scss @@ -30,6 +30,7 @@ td { &-stats { font-size: $font-smallest; font-weight: normal; + height: 4rem; } &-header { diff --git a/frontend/src/app/features/rules/pages/rules/rule.component.html b/frontend/src/app/features/rules/pages/rules/rule.component.html index 7da500096..2771ba252 100644 --- a/frontend/src/app/features/rules/pages/rules/rule.component.html +++ b/frontend/src/app/features/rules/pages/rules/rule.component.html @@ -10,7 +10,7 @@ [isRequired]="false"> -
+
@@ -109,8 +109,8 @@
{{ 'common.lastExecuted' | sqxTranslate }}: {{rule.lastExecuted | sqxFromNow:'-'}}
-
- +
+ {{ 'common.logs' | sqxTranslate }} diff --git a/frontend/src/app/shared/services/rules.service.ts b/frontend/src/app/shared/services/rules.service.ts index 7ebde192a..52ecebfdb 100644 --- a/frontend/src/app/shared/services/rules.service.ts +++ b/frontend/src/app/shared/services/rules.service.ts @@ -127,6 +127,7 @@ export class RuleDto { public readonly canDelete: boolean; public readonly canDisable: boolean; public readonly canEnable: boolean; + public readonly canReadLogs: boolean; public readonly canRun: boolean; public readonly canRunFromSnapshots: boolean; public readonly canTrigger: boolean; @@ -155,9 +156,10 @@ export class RuleDto { this.canDelete = hasAnyLink(links, 'delete'); this.canDisable = hasAnyLink(links, 'disable'); this.canEnable = hasAnyLink(links, 'enable'); + this.canReadLogs = hasAnyLink(links, 'logs'); this.canRun = hasAnyLink(links, 'run'); this.canRunFromSnapshots = hasAnyLink(links, 'run/snapshots'); - this.canTrigger = hasAnyLink(links, 'logs'); + this.canTrigger = hasAnyLink(links, 'trigger'); this.canUpdate = hasAnyLink(links, 'update'); } } @@ -186,7 +188,7 @@ export class RuleEventDto extends Model { this._links = links; - this.canDelete = hasAnyLink(links, 'delete'); + this.canDelete = hasAnyLink(links, 'cancel'); this.canUpdate = hasAnyLink(links, 'update'); } }