diff --git a/src/Squidex.Domain.Apps.Entities/Backup/IRestoreGrain.cs b/src/Squidex.Domain.Apps.Entities/Backup/IRestoreGrain.cs index 6ee281401..1fcc2ffb9 100644 --- a/src/Squidex.Domain.Apps.Entities/Backup/IRestoreGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Backup/IRestoreGrain.cs @@ -8,13 +8,14 @@ using System; using System.Threading.Tasks; using Orleans; +using Squidex.Infrastructure; using Squidex.Infrastructure.Orleans; namespace Squidex.Domain.Apps.Entities.Backup { public interface IRestoreGrain : IGrainWithStringKey { - Task RestoreAsync(Uri url, string newAppName = null); + Task RestoreAsync(Uri url, RefToken actor, string newAppName = null); Task> GetJobAsync(); } diff --git a/src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs b/src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs index 3740d812b..6f3ac3cb3 100644 --- a/src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs +++ b/src/Squidex.Domain.Apps.Entities/Backup/RestoreGrain.cs @@ -37,7 +37,6 @@ namespace Squidex.Domain.Apps.Entities.Backup private readonly IEventDataFormatter eventDataFormatter; private readonly ISemanticLog log; private readonly IStreamNameResolver streamNameResolver; - private RefToken actor; private RestoreStateJob CurrentJob { @@ -80,8 +79,6 @@ namespace Squidex.Domain.Apps.Entities.Backup protected override Task OnActivateAsync(string key) { - actor = new RefToken(RefTokenType.Subject, key); - RecoverAfterRestartAsync().Forget(); return TaskHelper.Done; @@ -100,9 +97,10 @@ namespace Squidex.Domain.Apps.Entities.Backup } } - public Task RestoreAsync(Uri url, string newAppName) + public Task RestoreAsync(Uri url, RefToken actor, string newAppName) { Guard.NotNull(url, nameof(url)); + Guard.NotNull(actor, nameof(actor)); if (!string.IsNullOrWhiteSpace(newAppName)) { @@ -118,6 +116,7 @@ namespace Squidex.Domain.Apps.Entities.Backup { Id = Guid.NewGuid(), NewAppName = newAppName, + Actor = actor, Started = clock.GetCurrentInstant(), Status = JobStatus.Started, Url = url @@ -238,16 +237,25 @@ namespace Squidex.Domain.Apps.Entities.Backup private async Task AssignContributorAsync() { - await commandBus.PublishAsync(new AssignContributor - { - Actor = actor, - AppId = CurrentJob.AppId, - ContributorId = actor.Identifier, - IsRestore = true, - Role = Role.Owner - }); + var actor = CurrentJob.Actor; - Log("Assigned current user."); + if (string.Equals(actor?.Type, RefTokenType.Subject)) + { + await commandBus.PublishAsync(new AssignContributor + { + Actor = actor, + AppId = CurrentJob.AppId, + ContributorId = actor.Identifier, + IsRestore = true, + Role = Role.Owner + }); + + Log("Assigned current user."); + } + else + { + Log("Current user not assigned because restore was triggered by client."); + } } private async Task CleanupAsync() @@ -286,7 +294,7 @@ namespace Squidex.Domain.Apps.Entities.Backup { if (@event.Payload is SquidexEvent squidexEvent) { - squidexEvent.Actor = actor; + squidexEvent.Actor = CurrentJob.Actor; } if (@event.Payload is AppCreated appCreated) @@ -306,7 +314,7 @@ namespace Squidex.Domain.Apps.Entities.Backup foreach (var handler in handlers) { - if (!await handler.RestoreEventAsync(@event, CurrentJob.AppId, reader, actor)) + if (!await handler.RestoreEventAsync(@event, CurrentJob.AppId, reader, CurrentJob.Actor)) { return; } diff --git a/src/Squidex.Domain.Apps.Entities/Backup/State/RestoreStateJob.cs b/src/Squidex.Domain.Apps.Entities/Backup/State/RestoreStateJob.cs index edefb80dd..71abbdab7 100644 --- a/src/Squidex.Domain.Apps.Entities/Backup/State/RestoreStateJob.cs +++ b/src/Squidex.Domain.Apps.Entities/Backup/State/RestoreStateJob.cs @@ -9,6 +9,7 @@ using System; using System.Collections.Generic; using System.Runtime.Serialization; using NodaTime; +using Squidex.Infrastructure; namespace Squidex.Domain.Apps.Entities.Backup.State { @@ -24,6 +25,9 @@ namespace Squidex.Domain.Apps.Entities.Backup.State [DataMember] public Guid AppId { get; set; } + [DataMember] + public RefToken Actor { get; set; } + [DataMember] public Uri Url { get; set; } diff --git a/src/Squidex.Infrastructure/Security/Extensions.cs b/src/Squidex.Infrastructure/Security/Extensions.cs index 2f28f2fb2..bb25956c6 100644 --- a/src/Squidex.Infrastructure/Security/Extensions.cs +++ b/src/Squidex.Infrastructure/Security/Extensions.cs @@ -13,6 +13,25 @@ namespace Squidex.Infrastructure.Security { public static class Extensions { + public static RefToken Token(this ClaimsPrincipal principal) + { + var subjectId = principal.OpenIdSubject(); + + if (!string.IsNullOrWhiteSpace(subjectId)) + { + return new RefToken(subjectId, RefTokenType.Subject); + } + + var clientId = principal.OpenIdClientId(); + + if (!string.IsNullOrWhiteSpace(clientId)) + { + return new RefToken(clientId, RefTokenType.Client); + } + + return null; + } + public static string OpenIdSubject(this ClaimsPrincipal principal) { return principal.Claims.FirstOrDefault(x => x.Type == OpenIdClaims.Subject)?.Value; diff --git a/src/Squidex/Areas/Api/Controllers/Backups/RestoreController.cs b/src/Squidex/Areas/Api/Controllers/Backups/RestoreController.cs index b9eafaac4..d564e8d11 100644 --- a/src/Squidex/Areas/Api/Controllers/Backups/RestoreController.cs +++ b/src/Squidex/Areas/Api/Controllers/Backups/RestoreController.cs @@ -12,6 +12,7 @@ using Squidex.Areas.Api.Controllers.Backups.Models; using Squidex.Domain.Apps.Entities.Backup; using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Orleans; +using Squidex.Infrastructure.Security; using Squidex.Pipeline; using Squidex.Shared; @@ -71,7 +72,7 @@ namespace Squidex.Areas.Api.Controllers.Backups { var restoreGrain = grainFactory.GetGrain(SingleGrain.Id); - await restoreGrain.RestoreAsync(request.Url, request.Name); + await restoreGrain.RestoreAsync(request.Url, User.Token(), request.Name); return NoContent(); } diff --git a/src/Squidex/Pipeline/CommandMiddlewares/EnrichWithActorCommandMiddleware.cs b/src/Squidex/Pipeline/CommandMiddlewares/EnrichWithActorCommandMiddleware.cs index 33690ade1..4baf4b814 100644 --- a/src/Squidex/Pipeline/CommandMiddlewares/EnrichWithActorCommandMiddleware.cs +++ b/src/Squidex/Pipeline/CommandMiddlewares/EnrichWithActorCommandMiddleware.cs @@ -10,7 +10,6 @@ using System.Security; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Squidex.Domain.Apps.Entities; -using Squidex.Infrastructure; using Squidex.Infrastructure.Commands; using Squidex.Infrastructure.Security; @@ -34,11 +33,11 @@ namespace Squidex.Pipeline.CommandMiddlewares if (context.Command is SquidexCommand squidexCommand) { + var user = httpContextAccessor.HttpContext.User; + if (squidexCommand.Actor == null) { - var actorToken = - FindActorFromSubject() ?? - FindActorFromClient(); + var actorToken = user.Token(); squidexCommand.Actor = actorToken ?? throw new SecurityException("No actor with subject or client id available."); } @@ -51,19 +50,5 @@ namespace Squidex.Pipeline.CommandMiddlewares return next(); } - - private RefToken FindActorFromSubject() - { - var subjectId = httpContextAccessor.HttpContext.User.OpenIdSubject(); - - return subjectId == null ? null : new RefToken(RefTokenType.Subject, subjectId); - } - - private RefToken FindActorFromClient() - { - var clientId = httpContextAccessor.HttpContext.User.OpenIdClientId(); - - return clientId == null ? null : new RefToken(RefTokenType.Client, clientId); - } } }