Skip to content

Commit

Permalink
Basic fixes for skybrud#154 by filtering domains and redirects API to…
Browse files Browse the repository at this point in the history
… only show redirects on nodes the user has access to.

Doesn't solve risky payload issues, TBD w/ @abjerner later
  • Loading branch information
jamiehowarth0 committed Oct 11, 2022
1 parent c0cf24f commit 623d388
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,16 @@
using Skybrud.Umbraco.Redirects.Models.Api;
using Skybrud.Umbraco.Redirects.Models.Options;
using Skybrud.Umbraco.Redirects.Services;
using Umbraco.Cms.Core;
using Umbraco.Cms.Core.Models;
using Umbraco.Cms.Core.Models.Membership;
using Umbraco.Cms.Core.Models.PublishedContent;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Web;
using Umbraco.Cms.Web.BackOffice.Controllers;
using Umbraco.Cms.Web.Common.Attributes;
using Umbraco.Cms.Web.Common.UmbracoContext;
using Umbraco.Extensions;

#pragma warning disable 1591

Expand All @@ -36,19 +40,22 @@ public class RedirectsController : UmbracoAuthorizedApiController {
private readonly RedirectsBackOfficeHelper _backOffice;
private readonly IContentService _contentService;
private readonly IMediaService _mediaService;
private readonly IUserService _userService;
private readonly IUmbracoContextAccessor _umbracoContextAccessor;

#region Constructors

public RedirectsController(ILogger<RedirectsController> logger, IRedirectsService redirectsService, RedirectsBackOfficeHelper backOffice,
IContentService contentService,
IMediaService mediaService,
IUserService userService,
IUmbracoContextAccessor umbracoContextAccessor) {
_logger = logger;
_redirects = redirectsService;
_backOffice = backOffice;
_contentService = contentService;
_mediaService = mediaService;
_userService = userService;
_umbracoContextAccessor = umbracoContextAccessor;
}

Expand All @@ -61,13 +68,13 @@ public RedirectsController(ILogger<RedirectsController> logger, IRedirectsServic
[HttpGet]
public ActionResult GetRootNodes() {

RedirectRootNode[] rootNodes = _redirects.GetRootNodes();

return new JsonResult(new {
total = rootNodes.Length,
items = rootNodes.Select(x => new RedirectRootNodeModel(x, _backOffice))
});
RedirectRootNode[] rootNodes = _redirects.GetRootNodes(GetUser()).ToArray();

return new JsonResult(new
{
total = rootNodes.Length,
items = rootNodes.Select(x => new RedirectRootNodeModel(x, _backOffice))
});
}

[HttpPost]
Expand Down Expand Up @@ -267,12 +274,17 @@ public ActionResult GetRedirects(int page = 1, int limit = 20, string type = nul
Page = page,
Limit = limit,
Type = EnumUtils.ParseEnum(type, RedirectTypeFilter.All),
Text = text,
RootNodeKey = rootNodeKey
Text = text
};

if (rootNodeKey != null) options.RootNodeKey = rootNodeKey;

var rootKeys = _contentService.GetByIds(_redirects.GetUserAccessibleNodes(GetUser()))
.Select(p => p.Key)
.ToArray();

// Make the search for redirects via the redirects service
RedirectsSearchResult result = _redirects.GetRedirects(options);
RedirectsSearchResult result = _redirects.GetRedirects(options, rootKeys);

// Map the result for the API
return new JsonResult(_backOffice.Map(result));
Expand Down Expand Up @@ -370,6 +382,11 @@ private ActionResult Error(RedirectsException ex) {

}

private IUser GetUser()
{
var currentUser = HttpContext.User.GetUmbracoIdentity();
return _userService.GetByUsername(currentUser.Name);
}
}

}
11 changes: 8 additions & 3 deletions src/Skybrud.Umbraco.Redirects/Services/IRedirectsService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
using Skybrud.Umbraco.Redirects.Models;
using Skybrud.Umbraco.Redirects.Models.Options;

using Umbraco.Cms.Core.Models.Membership;

namespace Skybrud.Umbraco.Redirects.Services {

/// <summary>
Expand All @@ -17,6 +19,8 @@ public interface IRedirectsService {
/// <returns>An array of <see cref="RedirectDomain"/>.</returns>
RedirectDomain[] GetDomains();

int[] GetUserAccessibleNodes(IUser umbracoUser);

/// <summary>
/// Adds a new redirect with the specified <paramref name="options"/>.
/// </summary>
Expand Down Expand Up @@ -99,13 +103,14 @@ public interface IRedirectsService {
/// <param name="uri">The inbound URL.</param>
/// <returns>The destination URL.</returns>
string GetDestinationUrl(IRedirectBase redirect, Uri uri);

/// <summary>
/// Returns a paginated list of redirects matching the specified <paramref name="options"/>.
/// </summary>
/// <param name="options">The options the returned redirects should match.</param>
/// <param name="rootNodes">The user's root nodes that should be visible for security</param>
/// <returns>An instance of <see cref="RedirectsSearchResult"/>.</returns>
RedirectsSearchResult GetRedirects(RedirectsSearchOptions options);
RedirectsSearchResult GetRedirects(RedirectsSearchOptions options, Guid[] rootNodes);

/// <summary>
/// Returns a collection with all redirects.
Expand All @@ -117,7 +122,7 @@ public interface IRedirectsService {
/// Returns an array of all rode nodes configured in Umbraco.
/// </summary>
/// <returns>An array of <see cref="RedirectRootNode"/> representing the root nodes.</returns>
RedirectRootNode[] GetRootNodes();
RedirectRootNode[] GetRootNodes(IUser user);

/// <summary>
/// Returns an array of redirects where the destination matches the specified <paramref name="nodeType"/> and <paramref name="nodeId"/>.
Expand Down
38 changes: 34 additions & 4 deletions src/Skybrud.Umbraco.Redirects/Services/RedirectsService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using NPoco;
using NUglify.Helpers;
using Skybrud.Essentials.Strings.Extensions;
using Skybrud.Essentials.Time;
using Skybrud.Umbraco.Redirects.Exceptions;
using Skybrud.Umbraco.Redirects.Extensions;
using Skybrud.Umbraco.Redirects.Models;
using Skybrud.Umbraco.Redirects.Models.Dtos;
using Skybrud.Umbraco.Redirects.Models.Options;
using Umbraco.Cms.Core.Models.Membership;
using Umbraco.Cms.Core.Models.PublishedContent;
using Umbraco.Cms.Core.Routing;
using Umbraco.Cms.Core.Services;
Expand Down Expand Up @@ -59,6 +61,26 @@ public RedirectDomain[] GetDomains() {
return _domains.GetAll(false).Select(RedirectDomain.GetFromDomain).ToArray();
}

public int[] GetUserAccessibleNodes(IUser umbracoUser)
{
var rootNodeIDs = umbracoUser.StartContentIds.ToList();
var groupRootNodes = umbracoUser.Groups
.Where(p => p.StartContentId.HasValue)
.Select(p => p.StartContentId.Value)
.ToArray();
rootNodeIDs.AddRange(groupRootNodes);

// JH: Admin should access all nodes
if (rootNodeIDs.Count == 1 && rootNodeIDs.Single() == -1)
{
rootNodeIDs.Clear();
rootNodeIDs = GetDomains().Select(c => c.RootNodeId).ToList();
}

return rootNodeIDs.ToArray();

}

/// <summary>
/// Deletes the specified <paramref name="redirect"/>.
/// </summary>
Expand Down Expand Up @@ -353,8 +375,9 @@ public IRedirect SaveRedirect(IRedirect redirect) {
/// Returns a paginated list of redirects matching the specified <paramref name="options"/>.
/// </summary>
/// <param name="options">The options the returned redirects should match.</param>
/// <param name="rootNodes">The user's root nodes that should be visible for security</param>
/// <returns>An instance of <see cref="RedirectsSearchResult"/>.</returns>
public RedirectsSearchResult GetRedirects(RedirectsSearchOptions options) {
public RedirectsSearchResult GetRedirects(RedirectsSearchOptions options, Guid[] rootNodes) {

if (options == null) throw new ArgumentNullException(nameof(options));

Expand All @@ -365,7 +388,11 @@ public RedirectsSearchResult GetRedirects(RedirectsSearchOptions options) {
var sql = scope.SqlContext.Sql().Select<RedirectDto>().From<RedirectDto>();

// Search by the rootNodeId
if (options.RootNodeKey != null) sql = sql.Where<RedirectDto>(x => x.RootKey == options.RootNodeKey.Value);
if (options.RootNodeKey != null) {
sql = sql.Where<RedirectDto>(x => x.RootKey == options.RootNodeKey);
} else if (rootNodes.Any()) {
sql = sql.Where<RedirectDto>(x => rootNodes.Contains(x.RootKey));
}

// Search by the type
if (options.Type != RedirectTypeFilter.All) {
Expand Down Expand Up @@ -394,6 +421,7 @@ public RedirectsSearchResult GetRedirects(RedirectsSearchOptions options) {
||
(x.Path.Contains(url) && x.QueryString.Contains(query))
));

}
}

Expand Down Expand Up @@ -452,15 +480,17 @@ public IEnumerable<IRedirect> GetAllRedirects() {
/// Returns an array of all rode nodes configured in Umbraco.
/// </summary>
/// <returns>An array of <see cref="RedirectRootNode"/> representing the root nodes.</returns>
public RedirectRootNode[] GetRootNodes() {
public RedirectRootNode[] GetRootNodes(IUser user)
{

var rootNodeIDs = GetUserAccessibleNodes(user);
// Multiple domains may be configured for a single node, so we need to group the domains before proceeding
var domainsByRootNodeId = GetDomains().GroupBy(x => x.RootNodeId);

return (
from domainGroup in domainsByRootNodeId
let content = _contentService.GetById(domainGroup.First().RootNodeId)
where content is { Trashed: false }
where !content.Trashed && rootNodeIDs.Contains(content.Id)
orderby content.Id
select RedirectRootNode.GetFromContent(content, domainGroup)
).ToArray();
Expand Down

0 comments on commit 623d388

Please sign in to comment.