Dissallow users to save languages they do not have access to
This commit is contained in:
9
src/Umbraco.Core/Constants-ModelStateErrorKeys.cs
Normal file
9
src/Umbraco.Core/Constants-ModelStateErrorKeys.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace Umbraco.Cms.Core;
|
||||
|
||||
public static partial class Constants
|
||||
{
|
||||
public class ModelStateErrorKeys
|
||||
{
|
||||
public const string PermissionError = "PermissionError";
|
||||
}
|
||||
}
|
||||
@@ -111,8 +111,7 @@ namespace Umbraco.Cms.Infrastructure.Persistence.Factories
|
||||
private static IReadOnlyUserGroup ToReadOnlyGroup(UserGroupDto group)
|
||||
{
|
||||
return new ReadOnlyUserGroup(group.Id, group.Name, group.Icon,
|
||||
group.StartContentId, group.StartMediaId, group.Alias,
|
||||
Enumerable.Empty<int>(), // TODO: Need to find the real languages when the dto model is updated
|
||||
group.StartContentId, group.StartMediaId, group.Alias, group.UserGroup2LanguageDtos.Select(x => x.LanguageId),
|
||||
group.UserGroup2AppDtos.Select(x => x.AppAlias).WhereNotNull().ToArray(),
|
||||
group.DefaultPermissions == null ? Enumerable.Empty<string>() : group.DefaultPermissions.ToCharArray().Select(x => x.ToString()));
|
||||
}
|
||||
|
||||
@@ -393,6 +393,17 @@ SELECT 4 AS [Key], COUNT(id) AS [Value] FROM umbracoUser WHERE userDisabled = 0
|
||||
|
||||
var startNodes = Database.Fetch<UserStartNodeDto>(sql);
|
||||
|
||||
// get groups2languages
|
||||
|
||||
sql = SqlContext.Sql()
|
||||
.Select<UserGroup2LanguageDto>()
|
||||
.From<UserGroup2LanguageDto>()
|
||||
.WhereIn<UserGroup2LanguageDto>(x => x.UserGroupId, groupIds);
|
||||
|
||||
var groups2languages = Database.Fetch<UserGroup2LanguageDto>(sql)
|
||||
.GroupBy(x => x.UserGroupId)
|
||||
.ToDictionary(x => x.Key, x => x);
|
||||
|
||||
// map groups
|
||||
|
||||
foreach (var user2group in users2groups)
|
||||
@@ -419,6 +430,16 @@ SELECT 4 AS [Key], COUNT(id) AS [Value] FROM umbracoUser WHERE userDisabled = 0
|
||||
if (groups2apps.TryGetValue(group.Id, out var list))
|
||||
group.UserGroup2AppDtos = list.ToList(); // groups2apps is distinct
|
||||
}
|
||||
|
||||
// map languages
|
||||
|
||||
foreach (var group in groups.Values)
|
||||
{
|
||||
if (groups2languages.TryGetValue(group.Id, out var list))
|
||||
{
|
||||
group.UserGroup2LanguageDtos = list.ToList(); // groups2apps is distinct
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -796,6 +796,13 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
// we will continue to save if model state is invalid, however we cannot save if critical data is missing.
|
||||
if (!ModelState.IsValid)
|
||||
{
|
||||
// Don't try and save if we do not have access
|
||||
if (ModelState.Keys.Contains(Constants.ModelStateErrorKeys.PermissionError))
|
||||
{
|
||||
var forDisplay = mapToDisplay(contentItem.PersistedContent);
|
||||
return ValidationProblem(forDisplay, ModelState);
|
||||
}
|
||||
|
||||
// check for critical data validation issues, we can't continue saving if this data is invalid
|
||||
if (!passesCriticalValidationRules)
|
||||
{
|
||||
|
||||
@@ -10,6 +10,8 @@ using Umbraco.Cms.Core;
|
||||
using Umbraco.Cms.Core.Actions;
|
||||
using Umbraco.Cms.Core.Models;
|
||||
using Umbraco.Cms.Core.Models.ContentEditing;
|
||||
using Umbraco.Cms.Core.Models.Membership;
|
||||
using Umbraco.Cms.Core.Security;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Web.BackOffice.Authorization;
|
||||
using Umbraco.Cms.Web.Common.Authorization;
|
||||
@@ -33,6 +35,8 @@ namespace Umbraco.Cms.Web.BackOffice.Filters
|
||||
private readonly IContentService _contentService;
|
||||
private readonly IPropertyValidationService _propertyValidationService;
|
||||
private readonly IAuthorizationService _authorizationService;
|
||||
private readonly IBackOfficeSecurityAccessor _backOfficeSecurityAccessor;
|
||||
private readonly ILocalizationService _localizationService;
|
||||
private readonly ILoggerFactory _loggerFactory;
|
||||
|
||||
|
||||
@@ -40,12 +44,16 @@ namespace Umbraco.Cms.Web.BackOffice.Filters
|
||||
ILoggerFactory loggerFactory,
|
||||
IContentService contentService,
|
||||
IPropertyValidationService propertyValidationService,
|
||||
IAuthorizationService authorizationService)
|
||||
IAuthorizationService authorizationService,
|
||||
IBackOfficeSecurityAccessor backOfficeSecurityAccessor,
|
||||
ILocalizationService localizationService)
|
||||
{
|
||||
_loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory));
|
||||
_contentService = contentService ?? throw new ArgumentNullException(nameof(contentService));
|
||||
_propertyValidationService = propertyValidationService ?? throw new ArgumentNullException(nameof(propertyValidationService));
|
||||
_authorizationService = authorizationService;
|
||||
_backOfficeSecurityAccessor = backOfficeSecurityAccessor;
|
||||
_localizationService = localizationService;
|
||||
}
|
||||
|
||||
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
|
||||
@@ -76,7 +84,7 @@ namespace Umbraco.Cms.Web.BackOffice.Filters
|
||||
|
||||
if (!ValidateAtLeastOneVariantIsBeingSaved(model, context)) return;
|
||||
if (!contentItemValidator.ValidateExistingContent(model, context)) return;
|
||||
if (!await ValidateUserAccessAsync(model, context)) return;
|
||||
if (!await ValidateUserAccessAsync(model, context))
|
||||
|
||||
if (model is not null)
|
||||
{
|
||||
@@ -129,6 +137,31 @@ namespace Umbraco.Cms.Web.BackOffice.Filters
|
||||
var permissionToCheck = new List<char>();
|
||||
IContent? contentToCheck = null;
|
||||
int contentIdToCheck;
|
||||
|
||||
// First check if user has Access to that language
|
||||
IUser? currentUser = _backOfficeSecurityAccessor.BackOfficeSecurity?.CurrentUser;
|
||||
bool hasAccess = false;
|
||||
if (currentUser is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (IReadOnlyUserGroup group in currentUser.Groups)
|
||||
{
|
||||
IEnumerable<ILanguage> languages = _localizationService.GetAllLanguages().Where(x => group.AllowedLanguages.Contains(x.Id));
|
||||
if (group.AllowedLanguages.Count() is 0 ||
|
||||
languages.Select(x => x.IsoCode).Intersect(contentItem?.Variants.Where(x => x.Save || x.Publish).Select(x => x.Culture) ?? Enumerable.Empty<string>()).Count() is not 0)
|
||||
{
|
||||
hasAccess = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasAccess && contentItem?.Variants.First().Culture is not null)
|
||||
{
|
||||
actionContext.ModelState.AddModelError(Constants.ModelStateErrorKeys.PermissionError, "User does not have access to save language");
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (contentItem?.Action)
|
||||
{
|
||||
case ContentSaveAction.Save:
|
||||
|
||||
Reference in New Issue
Block a user