mirror of https://github.com/Squidex/squidex.git
Browse Source
* Simplify api permissions and improve performance with simple caching. * Schema resolver tests. * IDentity server improvements. * Set base url. * Code cleanup and tests fixed.pull/520/head
committed by
GitHub
74 changed files with 1130 additions and 569 deletions
@ -0,0 +1,3 @@ |
|||||
|
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
||||
|
<Lazy /> |
||||
|
</Weavers> |
||||
@ -0,0 +1,26 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
||||
|
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
||||
|
<xs:element name="Weavers"> |
||||
|
<xs:complexType> |
||||
|
<xs:all> |
||||
|
<xs:element name="Lazy" minOccurs="0" maxOccurs="1" type="xs:anyType" /> |
||||
|
</xs:all> |
||||
|
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
||||
|
<xs:annotation> |
||||
|
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
||||
|
</xs:annotation> |
||||
|
</xs:attribute> |
||||
|
</xs:complexType> |
||||
|
</xs:element> |
||||
|
</xs:schema> |
||||
@ -0,0 +1,17 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using Squidex.Infrastructure; |
||||
|
|
||||
|
namespace Squidex.Web |
||||
|
{ |
||||
|
public interface ISchemaFeature |
||||
|
{ |
||||
|
NamedId<Guid> SchemaId { get; } |
||||
|
} |
||||
|
} |
||||
@ -1,64 +0,0 @@ |
|||||
// ==========================================================================
|
|
||||
// Squidex Headless CMS
|
|
||||
// ==========================================================================
|
|
||||
// Copyright (c) Squidex UG (haftungsbeschränkt)
|
|
||||
// All rights reserved. Licensed under the MIT license.
|
|
||||
// ==========================================================================
|
|
||||
|
|
||||
using Microsoft.AspNetCore.Http; |
|
||||
using Squidex.Infrastructure.Security; |
|
||||
using AllPermissions = Squidex.Shared.Permissions; |
|
||||
|
|
||||
namespace Squidex.Web |
|
||||
{ |
|
||||
public static class PermissionExtensions |
|
||||
{ |
|
||||
public static PermissionSet Permissions(this HttpContext httpContext) |
|
||||
{ |
|
||||
return httpContext.Context().Permissions; |
|
||||
} |
|
||||
|
|
||||
public static bool Includes(this HttpContext httpContext, Permission permission, PermissionSet? additional = null) |
|
||||
{ |
|
||||
return httpContext.Permissions().Includes(permission) || additional?.Includes(permission) == true; |
|
||||
} |
|
||||
|
|
||||
public static bool Includes(this ApiController controller, Permission permission, PermissionSet? additional = null) |
|
||||
{ |
|
||||
return controller.HttpContext.Includes(permission) || additional?.Includes(permission) == true; |
|
||||
} |
|
||||
|
|
||||
public static bool HasPermission(this HttpContext httpContext, Permission permission, PermissionSet? additional = null) |
|
||||
{ |
|
||||
return httpContext.Permissions().Allows(permission) || additional?.Allows(permission) == true; |
|
||||
} |
|
||||
|
|
||||
public static bool HasPermission(this ApiController controller, Permission permission, PermissionSet? additional = null) |
|
||||
{ |
|
||||
return controller.HttpContext.HasPermission(permission) || additional?.Allows(permission) == true; |
|
||||
} |
|
||||
|
|
||||
public static bool HasPermission(this ApiController controller, string id, string app = Permission.Any, string schema = Permission.Any, PermissionSet? additional = null) |
|
||||
{ |
|
||||
if (app == Permission.Any) |
|
||||
{ |
|
||||
if (controller.RouteData.Values.TryGetValue("app", out var value) && value is string s) |
|
||||
{ |
|
||||
app = s; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
if (schema == Permission.Any) |
|
||||
{ |
|
||||
if (controller.RouteData.Values.TryGetValue("name", out var value) && value is string s) |
|
||||
{ |
|
||||
schema = s; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
var permission = AllPermissions.ForApp(id, app, schema); |
|
||||
|
|
||||
return controller.HasPermission(permission, additional); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,19 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using Microsoft.AspNetCore.Builder; |
||||
|
|
||||
|
namespace Squidex.Web.Pipeline |
||||
|
{ |
||||
|
public static class AccessTokenQueryExtensions |
||||
|
{ |
||||
|
public static void UseAccessTokenQueryString(this IApplicationBuilder app) |
||||
|
{ |
||||
|
app.UseMiddleware<AccessTokenQueryMiddleware>(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,35 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.Http; |
||||
|
using Microsoft.Net.Http.Headers; |
||||
|
|
||||
|
namespace Squidex.Web.Pipeline |
||||
|
{ |
||||
|
public sealed class AccessTokenQueryMiddleware |
||||
|
{ |
||||
|
private readonly RequestDelegate next; |
||||
|
|
||||
|
public AccessTokenQueryMiddleware(RequestDelegate next) |
||||
|
{ |
||||
|
this.next = next; |
||||
|
} |
||||
|
|
||||
|
public Task InvokeAsync(HttpContext context) |
||||
|
{ |
||||
|
var request = context.Request; |
||||
|
|
||||
|
if (!string.IsNullOrWhiteSpace(request.Headers[HeaderNames.Authorization]) && request.Query.TryGetValue("access_token", out var token)) |
||||
|
{ |
||||
|
request.Headers[HeaderNames.Authorization] = token; |
||||
|
} |
||||
|
|
||||
|
return next(context); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,22 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using Squidex.Infrastructure; |
||||
|
|
||||
|
namespace Squidex.Web.Pipeline |
||||
|
{ |
||||
|
public sealed class SchemaFeature : ISchemaFeature |
||||
|
{ |
||||
|
public NamedId<Guid> SchemaId { get; } |
||||
|
|
||||
|
public SchemaFeature(NamedId<Guid> schemaId) |
||||
|
{ |
||||
|
SchemaId = schemaId; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,66 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Microsoft.AspNetCore.Mvc.Filters; |
||||
|
using Squidex.Domain.Apps.Entities; |
||||
|
using Squidex.Domain.Apps.Entities.Schemas; |
||||
|
using Squidex.Infrastructure; |
||||
|
|
||||
|
namespace Squidex.Web.Pipeline |
||||
|
{ |
||||
|
public sealed class SchemaResolver : IAsyncActionFilter |
||||
|
{ |
||||
|
private readonly IAppProvider appProvider; |
||||
|
|
||||
|
public SchemaResolver(IAppProvider appProvider) |
||||
|
{ |
||||
|
Guard.NotNull(appProvider); |
||||
|
|
||||
|
this.appProvider = appProvider; |
||||
|
} |
||||
|
|
||||
|
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) |
||||
|
{ |
||||
|
var appId = context.HttpContext.Features.Get<IAppFeature>()?.AppId.Id ?? default; |
||||
|
|
||||
|
if (appId != default) |
||||
|
{ |
||||
|
var schemaIdOrName = context.RouteData.Values["name"]?.ToString(); |
||||
|
|
||||
|
if (!string.IsNullOrWhiteSpace(schemaIdOrName)) |
||||
|
{ |
||||
|
var schema = await GetSchemaAsync(appId, schemaIdOrName); |
||||
|
|
||||
|
if (schema == null) |
||||
|
{ |
||||
|
context.Result = new NotFoundResult(); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
context.HttpContext.Features.Set<ISchemaFeature>(new SchemaFeature(schema.NamedId())); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
await next(); |
||||
|
} |
||||
|
|
||||
|
private Task<ISchemaEntity?> GetSchemaAsync(Guid appId, string schemaIdOrName) |
||||
|
{ |
||||
|
if (Guid.TryParse(schemaIdOrName, out var id)) |
||||
|
{ |
||||
|
return appProvider.GetSchemaAsync(appId, id); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
return appProvider.GetSchemaAsync(appId, schemaIdOrName); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,242 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using Lazy; |
||||
|
using Squidex.Infrastructure; |
||||
|
using Squidex.Infrastructure.Security; |
||||
|
using P = Squidex.Shared.Permissions; |
||||
|
|
||||
|
namespace Squidex.Web |
||||
|
{ |
||||
|
public sealed class Resources |
||||
|
{ |
||||
|
private readonly Dictionary<(string, string), bool> schemaPermissions = new Dictionary<(string, string), bool>(); |
||||
|
|
||||
|
// Contents
|
||||
|
public bool CanReadContent(string schema) => IsAllowedForSchema(P.AppContentsRead, schema); |
||||
|
|
||||
|
public bool CanCreateContent(string schema) => IsAllowedForSchema(P.AppContentsCreate, schema); |
||||
|
|
||||
|
public bool CanCreateContentVersion(string schema) => IsAllowedForSchema(P.AppContentsVersionCreate, schema); |
||||
|
|
||||
|
public bool CanDeleteContent(string schema) => IsAllowedForSchema(P.AppContentsDelete, schema); |
||||
|
|
||||
|
public bool CanDeleteContentVersion(string schema) => IsAllowedForSchema(P.AppContentsVersionDelete, schema); |
||||
|
|
||||
|
public bool CanUpdateContent(string schema) => IsAllowedForSchema(P.AppContentsUpdate, schema); |
||||
|
|
||||
|
public bool CanUpdateContentPartial(string schema) => IsAllowedForSchema(P.AppContentsUpdatePartial, schema); |
||||
|
|
||||
|
// Schemas
|
||||
|
public bool CanUpdateSchema(string schema) => IsAllowedForSchema(P.AppSchemasDelete, schema); |
||||
|
|
||||
|
public bool CanUpdateSchemaScripts(string schema) => IsAllowedForSchema(P.AppSchemasScripts, schema); |
||||
|
|
||||
|
public bool CanPublishSchema(string schema) => IsAllowedForSchema(P.AppSchemasPublish, schema); |
||||
|
|
||||
|
public bool CanDeleteSchema(string schema) => IsAllowedForSchema(P.AppSchemasDelete, schema); |
||||
|
|
||||
|
[Lazy] |
||||
|
public bool CanCreateSchema => IsAllowed(P.AppSchemasUpdate); |
||||
|
|
||||
|
// Contributors
|
||||
|
[Lazy] |
||||
|
public bool CanAssignContributor => IsAllowed(P.AppContributorsAssign); |
||||
|
|
||||
|
[Lazy] |
||||
|
public bool CanRevokeContributor => IsAllowed(P.AppContributorsRevoke); |
||||
|
|
||||
|
// Workflows
|
||||
|
[Lazy] |
||||
|
public bool CanCreateWorkflow => IsAllowed(P.AppWorkflowsCreate); |
||||
|
|
||||
|
[Lazy] |
||||
|
public bool CanUpdateWorkflow => IsAllowed(P.AppWorkflowsUpdate); |
||||
|
|
||||
|
[Lazy] |
||||
|
public bool CanDeleteWorkflow => IsAllowed(P.AppWorkflowsDelete); |
||||
|
|
||||
|
// Roles
|
||||
|
[Lazy] |
||||
|
public bool CanCreateRole => IsAllowed(P.AppRolesCreate); |
||||
|
|
||||
|
[Lazy] |
||||
|
public bool CanUpdateRole => IsAllowed(P.AppRolesUpdate); |
||||
|
|
||||
|
[Lazy] |
||||
|
public bool CanDeleteRole => IsAllowed(P.AppRolesDelete); |
||||
|
|
||||
|
// Languages
|
||||
|
[Lazy] |
||||
|
public bool CanCreateLanguage => IsAllowed(P.AppLanguagesCreate); |
||||
|
|
||||
|
[Lazy] |
||||
|
public bool CanUpdateLanguage => IsAllowed(P.AppLanguagesUpdate); |
||||
|
|
||||
|
[Lazy] |
||||
|
public bool CanDeleteLanguage => IsAllowed(P.AppLanguagesDelete); |
||||
|
|
||||
|
// Patterns
|
||||
|
[Lazy] |
||||
|
public bool CanCreatePattern => IsAllowed(P.AppClientsCreate); |
||||
|
|
||||
|
[Lazy] |
||||
|
public bool CanUpdatePattern => IsAllowed(P.AppPatternsUpdate); |
||||
|
|
||||
|
[Lazy] |
||||
|
public bool CanDeletePattern => IsAllowed(P.AppPatternsDelete); |
||||
|
|
||||
|
// Clients
|
||||
|
[Lazy] |
||||
|
public bool CanCreateClient => IsAllowed(P.AppClientsCreate); |
||||
|
|
||||
|
[Lazy] |
||||
|
public bool CanUpdateClient => IsAllowed(P.AppClientsUpdate); |
||||
|
|
||||
|
[Lazy] |
||||
|
public bool CanDeleteClient => IsAllowed(P.AppClientsDelete); |
||||
|
|
||||
|
// Rules
|
||||
|
[Lazy] |
||||
|
public bool CanDisableRule => IsAllowed(P.AppRulesDisable); |
||||
|
|
||||
|
[Lazy] |
||||
|
public bool CanCreateRule => IsAllowed(P.AppRulesCreate); |
||||
|
|
||||
|
[Lazy] |
||||
|
public bool CanUpdateRule => IsAllowed(P.AppRulesUpdate); |
||||
|
|
||||
|
[Lazy] |
||||
|
public bool CanDeleteRule => IsAllowed(P.AppRulesDelete); |
||||
|
|
||||
|
[Lazy] |
||||
|
public bool CanReadRuleEvents => IsAllowed(P.AppRulesEvents); |
||||
|
|
||||
|
// Users
|
||||
|
[Lazy] |
||||
|
public bool CanReadUsers => IsAllowed(P.AdminUsersRead); |
||||
|
|
||||
|
[Lazy] |
||||
|
public bool CanCreateUser => IsAllowed(P.AdminUsersCreate); |
||||
|
|
||||
|
[Lazy] |
||||
|
public bool CanLockUser => IsAllowed(P.AdminUsersLock); |
||||
|
|
||||
|
[Lazy] |
||||
|
public bool CanUnlockUser => IsAllowed(P.AdminUsersUnlock); |
||||
|
|
||||
|
[Lazy] |
||||
|
public bool CanUpdateUser => IsAllowed(P.AdminUsersUpdate); |
||||
|
|
||||
|
// Assets
|
||||
|
[Lazy] |
||||
|
public bool CanUploadAsset => IsAllowed(P.AppAssetsUpload); |
||||
|
|
||||
|
[Lazy] |
||||
|
public bool CanCreateAsset => IsAllowed(P.AppAssetsCreate); |
||||
|
|
||||
|
[Lazy] |
||||
|
public bool CanDeleteAsset => IsAllowed(P.AppAssetsDelete); |
||||
|
|
||||
|
[Lazy] |
||||
|
public bool CanUpdateAsset => IsAllowed(P.AppAssetsUpdate); |
||||
|
|
||||
|
[Lazy] |
||||
|
public bool CanReadAssets => IsAllowed(P.AppAssetsRead); |
||||
|
|
||||
|
// Events
|
||||
|
[Lazy] |
||||
|
public bool CanReadEvents => IsAllowed(P.AdminEventsRead); |
||||
|
|
||||
|
[Lazy] |
||||
|
public bool CanManageEvents => IsAllowed(P.AdminEventsManage); |
||||
|
|
||||
|
// Orleans
|
||||
|
[Lazy] |
||||
|
public bool CanReadOrleans => IsAllowed(P.AdminOrleans); |
||||
|
|
||||
|
// Backups
|
||||
|
[Lazy] |
||||
|
public bool CanRestoreBackup => IsAllowed(P.AdminEventsRead); |
||||
|
|
||||
|
[Lazy] |
||||
|
public bool CanCreateBackup => IsAllowed(P.AppBackupsCreate); |
||||
|
|
||||
|
[Lazy] |
||||
|
public bool CanDeleteBackup => IsAllowed(P.AppBackupsDelete); |
||||
|
|
||||
|
[Lazy] |
||||
|
public string? App => GetAppName(); |
||||
|
|
||||
|
public ApiController Controller { get; } |
||||
|
|
||||
|
public PermissionSet Permissions { get; } |
||||
|
|
||||
|
public Resources(ApiController controller) |
||||
|
{ |
||||
|
Controller = controller; |
||||
|
|
||||
|
Permissions = controller.HttpContext.Context().Permissions; |
||||
|
} |
||||
|
|
||||
|
public string Url<T>(Func<T?, string> action, object? values = null) where T : ApiController |
||||
|
{ |
||||
|
return Controller.Url(action, values); |
||||
|
} |
||||
|
|
||||
|
public bool IsUser(string userId) |
||||
|
{ |
||||
|
var subject = Controller.User.OpenIdSubject(); |
||||
|
|
||||
|
return string.Equals(subject, userId, StringComparison.OrdinalIgnoreCase); |
||||
|
} |
||||
|
|
||||
|
public bool Includes(Permission permission, PermissionSet? additional = null) |
||||
|
{ |
||||
|
return Permissions.Includes(permission) || additional?.Includes(permission) == true; |
||||
|
} |
||||
|
|
||||
|
public bool IsAllowedForSchema(string id, string schema) |
||||
|
{ |
||||
|
return schemaPermissions.GetOrAdd((id, schema), k => IsAllowed(k.Item1, "*", k.Item2)); |
||||
|
} |
||||
|
|
||||
|
public bool IsAllowed(string id, string app = Permission.Any, string schema = Permission.Any, PermissionSet? additional = null) |
||||
|
{ |
||||
|
if (app == Permission.Any) |
||||
|
{ |
||||
|
var falback = App; |
||||
|
|
||||
|
if (!string.IsNullOrWhiteSpace(falback)) |
||||
|
{ |
||||
|
app = falback; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (schema == Permission.Any) |
||||
|
{ |
||||
|
var falback = Controller.HttpContext.Features.Get<ISchemaFeature>()?.SchemaId.Name; |
||||
|
|
||||
|
if (!string.IsNullOrWhiteSpace(falback)) |
||||
|
{ |
||||
|
schema = falback; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
var permission = P.ForApp(id, app, schema); |
||||
|
|
||||
|
return Permissions.Allows(permission) || additional?.Allows(permission) == true; |
||||
|
} |
||||
|
|
||||
|
private string? GetAppName() |
||||
|
{ |
||||
|
return Controller.HttpContext.Context().App?.Name; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,36 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System.Threading.Tasks; |
||||
|
using IdentityServer4.Extensions; |
||||
|
using Microsoft.AspNetCore.Http; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using Squidex.Web; |
||||
|
|
||||
|
namespace Squidex.Areas.Api.Config |
||||
|
{ |
||||
|
public sealed class IdentityServerPathMiddleware |
||||
|
{ |
||||
|
private readonly UrlsOptions urlsOptions; |
||||
|
private readonly RequestDelegate next; |
||||
|
|
||||
|
public IdentityServerPathMiddleware(IOptions<UrlsOptions> urlsOptions, RequestDelegate next) |
||||
|
{ |
||||
|
this.urlsOptions = urlsOptions.Value; |
||||
|
|
||||
|
this.next = next; |
||||
|
} |
||||
|
|
||||
|
public Task InvokeAsync(HttpContext context) |
||||
|
{ |
||||
|
context.SetIdentityServerOrigin(urlsOptions.BaseUrl); |
||||
|
context.SetIdentityServerBasePath(Constants.IdentityServerPrefix); |
||||
|
|
||||
|
return next(context); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,151 @@ |
|||||
|
// ==========================================================================
|
||||
|
// Squidex Headless CMS
|
||||
|
// ==========================================================================
|
||||
|
// Copyright (c) Squidex UG (haftungsbeschraenkt)
|
||||
|
// All rights reserved. Licensed under the MIT license.
|
||||
|
// ==========================================================================
|
||||
|
|
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Security.Claims; |
||||
|
using System.Threading.Tasks; |
||||
|
using FakeItEasy; |
||||
|
using Microsoft.AspNetCore.Http; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Microsoft.AspNetCore.Mvc.Abstractions; |
||||
|
using Microsoft.AspNetCore.Mvc.Filters; |
||||
|
using Microsoft.AspNetCore.Routing; |
||||
|
using Squidex.Domain.Apps.Core.Schemas; |
||||
|
using Squidex.Domain.Apps.Entities; |
||||
|
using Squidex.Domain.Apps.Entities.Schemas; |
||||
|
using Squidex.Infrastructure; |
||||
|
using Xunit; |
||||
|
|
||||
|
#pragma warning disable IDE0017 // Simplify object initialization
|
||||
|
|
||||
|
namespace Squidex.Web.Pipeline |
||||
|
{ |
||||
|
public class SchemaResolverTests |
||||
|
{ |
||||
|
private readonly IAppProvider appProvider = A.Fake<IAppProvider>(); |
||||
|
private readonly HttpContext httpContext = new DefaultHttpContext(); |
||||
|
private readonly ActionContext actionContext; |
||||
|
private readonly ActionExecutingContext actionExecutingContext; |
||||
|
private readonly ActionExecutionDelegate next; |
||||
|
private readonly ClaimsIdentity user = new ClaimsIdentity(); |
||||
|
private readonly NamedId<Guid> schemaId = NamedId.Of(Guid.NewGuid(), "my-schema"); |
||||
|
private readonly NamedId<Guid> appId = NamedId.Of(Guid.NewGuid(), "my-app"); |
||||
|
private readonly SchemaResolver sut; |
||||
|
private bool isNextCalled; |
||||
|
|
||||
|
public SchemaResolverTests() |
||||
|
{ |
||||
|
actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor |
||||
|
{ |
||||
|
EndpointMetadata = new List<object>() |
||||
|
}); |
||||
|
|
||||
|
actionExecutingContext = new ActionExecutingContext(actionContext, new List<IFilterMetadata>(), new Dictionary<string, object>(), this); |
||||
|
actionExecutingContext.HttpContext = httpContext; |
||||
|
actionExecutingContext.HttpContext.User = new ClaimsPrincipal(user); |
||||
|
|
||||
|
next = () => |
||||
|
{ |
||||
|
isNextCalled = true; |
||||
|
|
||||
|
return Task.FromResult<ActionExecutedContext?>(null); |
||||
|
}; |
||||
|
|
||||
|
sut = new SchemaResolver(appProvider); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_return_not_found_if_schema_not_found() |
||||
|
{ |
||||
|
actionExecutingContext.HttpContext.Features.Set<IAppFeature>(new AppFeature(appId)); |
||||
|
actionContext.RouteData.Values["name"] = schemaId.Id.ToString(); |
||||
|
|
||||
|
A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, schemaId.Id, false)) |
||||
|
.Returns(Task.FromResult<ISchemaEntity?>(null)); |
||||
|
|
||||
|
await sut.OnActionExecutionAsync(actionExecutingContext, next); |
||||
|
|
||||
|
Assert.IsType<NotFoundResult>(actionExecutingContext.Result); |
||||
|
Assert.False(isNextCalled); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_resolve_schema_from_id() |
||||
|
{ |
||||
|
actionExecutingContext.HttpContext.Features.Set<IAppFeature>(new AppFeature(appId)); |
||||
|
actionContext.RouteData.Values["name"] = schemaId.Id.ToString(); |
||||
|
|
||||
|
var schema = CreateSchema(); |
||||
|
|
||||
|
A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, schemaId.Id, false)) |
||||
|
.Returns(schema); |
||||
|
|
||||
|
await sut.OnActionExecutionAsync(actionExecutingContext, next); |
||||
|
|
||||
|
Assert.Equal(schemaId, actionContext.HttpContext.Features.Get<ISchemaFeature>().SchemaId); |
||||
|
Assert.True(isNextCalled); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_resolve_schema_from_name() |
||||
|
{ |
||||
|
actionExecutingContext.HttpContext.Features.Set<IAppFeature>(new AppFeature(appId)); |
||||
|
actionContext.RouteData.Values["name"] = schemaId.Name; |
||||
|
|
||||
|
var schema = CreateSchema(); |
||||
|
|
||||
|
A.CallTo(() => appProvider.GetSchemaAsync(appId.Id, schemaId.Name)) |
||||
|
.Returns(schema); |
||||
|
|
||||
|
await sut.OnActionExecutionAsync(actionExecutingContext, next); |
||||
|
|
||||
|
Assert.Equal(schemaId, actionContext.HttpContext.Features.Get<ISchemaFeature>().SchemaId); |
||||
|
Assert.True(isNextCalled); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_do_nothing_if_app_feature_not_set() |
||||
|
{ |
||||
|
actionExecutingContext.RouteData.Values["name"] = schemaId.Name; |
||||
|
|
||||
|
await sut.OnActionExecutionAsync(actionExecutingContext, next); |
||||
|
|
||||
|
Assert.True(isNextCalled); |
||||
|
|
||||
|
A.CallTo(() => appProvider.GetAppAsync(A<string>._)) |
||||
|
.MustNotHaveHappened(); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_do_nothing_if_parameter_not_set() |
||||
|
{ |
||||
|
actionExecutingContext.HttpContext.Features.Set<IAppFeature>(new AppFeature(appId)); |
||||
|
actionExecutingContext.RouteData.Values.Remove("name"); |
||||
|
|
||||
|
await sut.OnActionExecutionAsync(actionExecutingContext, next); |
||||
|
|
||||
|
Assert.True(isNextCalled); |
||||
|
|
||||
|
A.CallTo(() => appProvider.GetAppAsync(A<string>._)) |
||||
|
.MustNotHaveHappened(); |
||||
|
} |
||||
|
|
||||
|
private ISchemaEntity CreateSchema() |
||||
|
{ |
||||
|
var schemaEntity = A.Fake<ISchemaEntity>(); |
||||
|
|
||||
|
A.CallTo(() => schemaEntity.SchemaDef) |
||||
|
.Returns(new Schema(schemaId.Name)); |
||||
|
|
||||
|
A.CallTo(() => schemaEntity.Id) |
||||
|
.Returns(schemaId.Id); |
||||
|
|
||||
|
return schemaEntity; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue