Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ private async Task ExecuteAsync(HttpContext context)
options.OperationName = request.OperationName;
options.Variables = request.Variables;
options.UserContext = _settings.BuildUserContext?.Invoke(context);
options.User = context.User;
options.ValidationRules = DocumentValidator.CoreRules
.Concat(context.RequestServices.GetServices<IValidationRule>())
.Append(new ComplexityValidationRule(new ComplexityOptions
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using GraphQL;
using GraphQL.Resolvers;
using GraphQL.Types;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Localization;
using OrchardCore.Apis.GraphQL;
Expand All @@ -11,14 +11,10 @@ namespace OrchardCore.ContentManagement.GraphQL.Queries;

public sealed class ContentItemQuery : ISchemaBuilder
{
private readonly IHttpContextAccessor _httpContextAccessor;

internal readonly IStringLocalizer S;

public ContentItemQuery(IHttpContextAccessor httpContextAccessor,
IStringLocalizer<ContentItemQuery> localizer)
public ContentItemQuery(IStringLocalizer<ContentItemQuery> localizer)
{
_httpContextAccessor = httpContextAccessor;
S = localizer;
}

Expand All @@ -42,6 +38,8 @@ public Task BuildAsync(ISchema schema)
Resolver = new FuncFieldResolver<ContentItem>(ResolveAsync),
};

field.RequirePermission(GraphQLPermissions.ExecuteGraphQL);

schema.Query.AddField(field);

return Task.CompletedTask;
Expand All @@ -50,8 +48,22 @@ public Task BuildAsync(ISchema schema)
private async ValueTask<ContentItem> ResolveAsync(IResolveFieldContext context)
{
var contentItemId = context.GetArgument<string>("contentItemId");
var contentManager = _httpContextAccessor.HttpContext.RequestServices.GetService<IContentManager>();
var contentManager = context.RequestServices.GetService<IContentManager>();
var authorizationService = context.RequestServices.GetService<IAuthorizationService>();

var contentItem = await contentManager.GetAsync(contentItemId);

if (contentItem == null)
{
return null;
}

if (!await authorizationService.AuthorizeAsync(context.User, Contents.CommonPermissions.ViewContent, contentItem))
{
// Return null if the user doesn't have permission to view the content item, so that it doesn't appear in the GraphQL response.
return null;
}

return await contentManager.GetAsync(contentItemId);
return contentItem;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using OrchardCore.Apis.GraphQL;
using OrchardCore.ContentManagement;
using OrchardCore.ContentManagement.Records;
using OrchardCore.Tests.Apis.Context;

namespace OrchardCore.Tests.Apis.GraphQL;

public class ContentItemQueryTests
{
[Fact]
public async Task ShouldReturnContentItemWhenViewContentPermissionIsGranted()
{
using var context = new SiteContext()
.WithRecipe("Blog")
.WithPermissionsContext(new PermissionsContext
{
UsePermissionsContext = true,
AuthorizedPermissions =
[
GraphQLPermissions.ExecuteGraphQL,
global::OrchardCore.Contents.CommonPermissions.ViewContent,
],
});

await context.InitializeAsync();

var contentItemId = await GetPublishedContentItemIdAsync(context, "Blog");

var result = await context.GraphQLClient.Content
.Query($@"item: contentItem(contentItemId: ""{contentItemId}"") {{
contentItemId
}}");

Assert.Equal(contentItemId, result["data"]["item"]["contentItemId"].ToString());
}

[Fact]
public async Task ShouldNotReturnContentItemWithoutViewContentPermission()
{
using var context = new SiteContext()
.WithRecipe("Blog")
.WithPermissionsContext(new PermissionsContext
{
UsePermissionsContext = true,
AuthorizedPermissions =
[
GraphQLPermissions.ExecuteGraphQL,
],
});

await context.InitializeAsync();

var contentItemId = await GetPublishedContentItemIdAsync(context, "Blog");

var result = await context.GraphQLClient.Content
.Query($@"item: contentItem(contentItemId: ""{contentItemId}"") {{
contentItemId
}}");

Assert.Null(result["data"]["item"]);
}

private static async Task<string> GetPublishedContentItemIdAsync(SiteContext context, string contentType)
{
string contentItemId = null;

await context.UsingTenantScopeAsync(async scope =>
{
var session = scope.ServiceProvider.GetRequiredService<global::YesSql.ISession>();
var contentItem = await session.Query<ContentItem, ContentItemIndex>(x => x.ContentType == contentType && x.Published).FirstOrDefaultAsync();

contentItemId = contentItem.ContentItemId;
});

return contentItemId;
}
}
Loading