preview service
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
import { post, ApiResponse, ApiRequestConfig } from '../../services/request';
|
||||
|
||||
export default {
|
||||
|
||||
createPreviewToken: (key: string, config?: ApiRequestConfig): Promise<ApiResponse<any>> => post('backoffice/ui/previews/token', { key }, { ...config }),
|
||||
|
||||
};
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import api from './api';
|
||||
|
||||
export default defineComponent({
|
||||
|
||||
@@ -19,7 +20,11 @@
|
||||
|
||||
created()
|
||||
{
|
||||
this.src = 'http://localhost:2310' + this.$route.query.path + '?zero_preview=true';
|
||||
api.createPreviewToken('mykey').then(res =>
|
||||
{
|
||||
let model = res.data;
|
||||
this.src = 'http://localhost:2310' + this.$route.query.path + `?${model.queryParameter}=${model.token}`;
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace zero.Backoffice.Endpoints.UI;
|
||||
|
||||
public class PreviewRequestTokenModel
|
||||
{
|
||||
public string Key { get; set; }
|
||||
|
||||
public class Response
|
||||
{
|
||||
public string Token { get; set; }
|
||||
|
||||
public string QueryParameter { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using zero.Backoffice.Services;
|
||||
using zero.Preview;
|
||||
|
||||
namespace zero.Backoffice.Endpoints.UI;
|
||||
|
||||
@@ -19,9 +20,12 @@ public class UIController : ZeroBackofficeController
|
||||
readonly ICultureService CultureService;
|
||||
readonly IUserService UserService;
|
||||
readonly IRequestUrlResolver RequestUrlResolver;
|
||||
readonly IPreviewService PreviewService;
|
||||
readonly IZeroContext ZeroContext;
|
||||
readonly HttpClient HttpClient;
|
||||
|
||||
public UIController(IUserService userService, IIconService iconService, IResourceService resourceService, ISectionService sectionService, IZeroOptions options, IMediaManagement media, ICultureService cultureService, IRequestUrlResolver requestUrlResolver)
|
||||
public UIController(IUserService userService, IIconService iconService, IResourceService resourceService, ISectionService sectionService, IZeroOptions options, IMediaManagement media,
|
||||
ICultureService cultureService, IRequestUrlResolver requestUrlResolver, IPreviewService previewService, IZeroContext zeroContext)
|
||||
{
|
||||
UserService = userService;
|
||||
IconService = iconService;
|
||||
@@ -31,6 +35,8 @@ public class UIController : ZeroBackofficeController
|
||||
Options = options;
|
||||
Media = media;
|
||||
RequestUrlResolver = requestUrlResolver;
|
||||
PreviewService = previewService;
|
||||
ZeroContext = zeroContext;
|
||||
HttpClient = new HttpClient();
|
||||
}
|
||||
|
||||
@@ -97,6 +103,18 @@ public class UIController : ZeroBackofficeController
|
||||
}
|
||||
|
||||
|
||||
[HttpPost("previews/token")]
|
||||
public async Task<ActionResult<PreviewRequestTokenModel.Response>> GetPreviewToken(PreviewRequestTokenModel model)
|
||||
{
|
||||
string token = await PreviewService.CreateAccessToken(model.Key, ZeroContext.BackofficeUser);
|
||||
return new PreviewRequestTokenModel.Response()
|
||||
{
|
||||
Token = token,
|
||||
QueryParameter = Options.For<PreviewOptions>().QueryParameter
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
[HttpGet("thumbnail/{id}-{size}.tmp")]
|
||||
public async Task<IActionResult> GetThumbnail(string id, string size)
|
||||
{
|
||||
|
||||
@@ -56,12 +56,14 @@ public class ZeroContext : IZeroContext
|
||||
|
||||
protected IPrimitiveTypeCollection ValueCollection { get; private set; }
|
||||
|
||||
protected IPreviewService PreviewService { get; private set; }
|
||||
|
||||
|
||||
bool _resolved = false;
|
||||
|
||||
|
||||
public ZeroContext(IZeroOptions options, IHttpContextAccessor httpContextAccessor, IApplicationResolver appResolver, ICultureResolver cultureResolver,
|
||||
ILogger<ZeroContext> logger, IZeroStore store, IHandlerHolder handler, IServiceProvider services)
|
||||
ILogger<ZeroContext> logger, IZeroStore store, IHandlerHolder handler, IServiceProvider services, IPreviewService previewService)
|
||||
{
|
||||
Options = options;
|
||||
AppResolver = appResolver;
|
||||
@@ -72,6 +74,7 @@ public class ZeroContext : IZeroContext
|
||||
ValueCollection = new PrimitiveTypeCollection();
|
||||
HttpContextAccessor = httpContextAccessor;
|
||||
Services = services;
|
||||
PreviewService = previewService;
|
||||
}
|
||||
|
||||
|
||||
@@ -96,9 +99,6 @@ public class ZeroContext : IZeroContext
|
||||
|
||||
_resolved = true;
|
||||
|
||||
// check whether the current request is a preview request
|
||||
IsPreview = context.Request.Query.ContainsKey("zero_preview");
|
||||
|
||||
// check if the current request is a backoffice request
|
||||
IsBackofficeRequest = context.IsBackofficeRequest(Options.ZeroPath);
|
||||
|
||||
@@ -112,6 +112,9 @@ public class ZeroContext : IZeroContext
|
||||
IsLoggedIntoBackoffice = true;
|
||||
}
|
||||
|
||||
// check whether the current request is a preview request
|
||||
IsPreview = await PreviewService.IsAccessGranted(context.Request, BackofficeUser);
|
||||
|
||||
// resolve current application
|
||||
Application = await AppResolver.Resolve(context, BackofficeUser);
|
||||
AppId = Application.Id;
|
||||
|
||||
@@ -127,6 +127,22 @@ public class ZeroTokenProvider : IZeroTokenProvider
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual async Task<bool> Exists(string token)
|
||||
{
|
||||
if (token.IsNullOrWhiteSpace())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
IZeroDocumentSession session = Store.Session();
|
||||
|
||||
// try to find a valid token
|
||||
SecurityToken securityToken = await session.LoadAsync<SecurityToken>(TokenToId(token));
|
||||
return securityToken != null;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Converts the token to a database id
|
||||
/// </summary>
|
||||
@@ -271,4 +287,9 @@ public interface IZeroTokenProvider
|
||||
/// This method won't store the token in the database and can't be used for verification. Use <see cref="Create(string, TimeSpan, int)"/> instead.
|
||||
/// </remarks>
|
||||
string Random(int length);
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a token exists in the database.
|
||||
/// </summary>
|
||||
Task<bool> Exists(string token);
|
||||
}
|
||||
|
||||
@@ -4,8 +4,14 @@ public class PreviewOptions
|
||||
{
|
||||
public PreviewOptions()
|
||||
{
|
||||
PreviewPath = "/preview";
|
||||
Path = "/preview";
|
||||
QueryParameter = "zero_preview";
|
||||
TokenExpirationInMinutes = 10;
|
||||
}
|
||||
|
||||
public string PreviewPath { get; set; }
|
||||
public string Path { get; set; }
|
||||
|
||||
public string QueryParameter { get; set; }
|
||||
|
||||
public double TokenExpirationInMinutes { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace zero.Preview;
|
||||
|
||||
public class PreviewService : IPreviewService
|
||||
{
|
||||
protected IZeroTokenProvider TokenProvider { get; private set; }
|
||||
|
||||
protected IZeroOptions Options { get; private set; }
|
||||
|
||||
|
||||
public PreviewService(IZeroTokenProvider tokenProvider, IZeroOptions options)
|
||||
{
|
||||
TokenProvider = tokenProvider;
|
||||
Options = options;
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<string> CreateAccessToken(string key, ClaimsPrincipal backofficeUser)
|
||||
{
|
||||
string fullKey = GetKey(key, backofficeUser);
|
||||
return await TokenProvider.Create(fullKey, TimeSpan.FromMinutes(Options.For<PreviewOptions>().TokenExpirationInMinutes), 32);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<bool> IsAccessGranted(HttpRequest request, ClaimsPrincipal backofficeUser)
|
||||
{
|
||||
string token = request.Query[Options.For<PreviewOptions>().QueryParameter];
|
||||
|
||||
if (!token.HasValue())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return await IsAccessGranted(token, backofficeUser);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<bool> IsAccessGranted(string token, ClaimsPrincipal backofficeUser)
|
||||
{
|
||||
return await TokenProvider.Exists(token);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Generate the key for a new preview access
|
||||
/// </summary>
|
||||
protected string GetKey(string keyPrefix, ClaimsPrincipal backofficeUser)
|
||||
{
|
||||
//string userId = UserManager.GetUserId(backofficeUser);
|
||||
|
||||
//if (userId.IsNullOrEmpty())
|
||||
//{
|
||||
// throw new InvalidOperationException("Preview access is only granted for authenticated users");
|
||||
//}
|
||||
|
||||
if (keyPrefix.IsNullOrEmpty())
|
||||
{
|
||||
throw new ArgumentNullException(nameof(keyPrefix), "Please define a key for generating preview access");
|
||||
}
|
||||
|
||||
return "zero:preview:" + /*userId + ":" + */ keyPrefix;
|
||||
}
|
||||
}
|
||||
|
||||
public interface IPreviewService
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a token for a new preview access.
|
||||
/// </summary>
|
||||
Task<string> CreateAccessToken(string key, ClaimsPrincipal backofficeUser);
|
||||
|
||||
// <summary>
|
||||
/// Determine whether the access to the preview is granted based on an HTTP request
|
||||
/// </summary>
|
||||
Task<bool> IsAccessGranted(HttpRequest request, ClaimsPrincipal backofficeUser);
|
||||
|
||||
/// <summary>
|
||||
/// Determine whether the access to the preview is granted based on the passed token
|
||||
/// </summary>
|
||||
Task<bool> IsAccessGranted(string token, ClaimsPrincipal backofficeUser);
|
||||
}
|
||||
@@ -12,7 +12,7 @@ public class PreviewRouteProvider : ZeroRouteProvider<PreviewRouteModel>
|
||||
public override async Task<Route> Create(RoutingContext context, PreviewRouteModel model)
|
||||
{
|
||||
Route route = await base.Create(context, model);
|
||||
route.Url = context.Context.Options.For<PreviewOptions>().PreviewPath.EnsureStartsWith('/').TrimEnd('/');
|
||||
route.Url = context.Context.Options.For<PreviewOptions>().Path.EnsureStartsWith('/').TrimEnd('/');
|
||||
return route;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
|
||||
namespace zero.Preview;
|
||||
|
||||
@@ -9,7 +7,7 @@ internal class ZeroPreviewModule : ZeroModule
|
||||
{
|
||||
public override void ConfigureServices(IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
//services.AddScoped<IRouteProvider, PreviewRouteProvider>();
|
||||
services.AddScoped<IPreviewService, PreviewService>();
|
||||
services.AddOptions<PreviewOptions>().Bind(configuration.GetSection("Zero:Preview"));
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ global using zero.Media;
|
||||
global using zero.Models;
|
||||
global using zero.Pages;
|
||||
global using zero.Persistence;
|
||||
global using zero.Preview;
|
||||
global using zero.Rendering;
|
||||
global using zero.Routing;
|
||||
global using zero.Stores;
|
||||
|
||||
@@ -52,6 +52,7 @@ public class ZeroBuilder
|
||||
Modules.Add<ZeroMediaModule>();
|
||||
Modules.Add<ZeroPageModule>();
|
||||
Modules.Add<ZeroPersistenceModule>();
|
||||
Modules.Add<ZeroPreviewModule>();
|
||||
Modules.Add<ZeroRenderingModule>();
|
||||
Modules.Add<ZeroRoutingModule>();
|
||||
Modules.Add<ZeroSpaceModule>();
|
||||
|
||||
Reference in New Issue
Block a user