263 lines
9.2 KiB
C#
263 lines
9.2 KiB
C#
using Nop.Core;
|
|
using Nop.Core.Caching;
|
|
using Nop.Core.Domain.Catalog;
|
|
using Nop.Core.Domain.Customers;
|
|
using Nop.Core.Domain.Security;
|
|
using Nop.Data;
|
|
using Nop.Services.Customers;
|
|
|
|
namespace Nop.Services.Security;
|
|
|
|
/// <summary>
|
|
/// ACL service
|
|
/// </summary>
|
|
public partial class AclService : IAclService
|
|
{
|
|
#region Fields
|
|
|
|
protected readonly CatalogSettings _catalogSettings;
|
|
protected readonly ICustomerService _customerService;
|
|
protected readonly IRepository<AclRecord> _aclRecordRepository;
|
|
protected readonly IStaticCacheManager _staticCacheManager;
|
|
protected readonly IWorkContext _workContext;
|
|
|
|
#endregion
|
|
|
|
#region Ctor
|
|
|
|
public AclService(CatalogSettings catalogSettings,
|
|
ICustomerService customerService,
|
|
IRepository<AclRecord> aclRecordRepository,
|
|
IStaticCacheManager staticCacheManager,
|
|
IWorkContext workContext)
|
|
{
|
|
_catalogSettings = catalogSettings;
|
|
_customerService = customerService;
|
|
_aclRecordRepository = aclRecordRepository;
|
|
_staticCacheManager = staticCacheManager;
|
|
_workContext = workContext;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Utilities
|
|
|
|
/// <summary>
|
|
/// Inserts an ACL record
|
|
/// </summary>
|
|
/// <param name="aclRecord">ACL record</param>
|
|
/// <returns>A task that represents the asynchronous operation</returns>
|
|
protected virtual async Task InsertAclRecordAsync(AclRecord aclRecord)
|
|
{
|
|
await _aclRecordRepository.InsertAsync(aclRecord);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get a value indicating whether any ACL records exist for entity type are related to customer roles
|
|
/// </summary>
|
|
/// <typeparam name="TEntity">Type of entity that supports the ACL</typeparam>
|
|
/// <returns>
|
|
/// A task that represents the asynchronous operation
|
|
/// The task result contains true if exist; otherwise false
|
|
/// </returns>
|
|
protected virtual async Task<bool> IsEntityAclMappingExistAsync<TEntity>() where TEntity : BaseEntity, IAclSupported
|
|
{
|
|
var entityName = typeof(TEntity).Name;
|
|
var key = _staticCacheManager.PrepareKeyForDefaultCache(NopSecurityDefaults.EntityAclRecordExistsCacheKey, entityName);
|
|
|
|
var query = from acl in _aclRecordRepository.Table
|
|
where acl.EntityName == entityName
|
|
select acl;
|
|
|
|
return await _staticCacheManager.GetAsync(key, query.Any);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Methods
|
|
|
|
/// <summary>
|
|
/// Apply ACL to the passed query
|
|
/// </summary>
|
|
/// <typeparam name="TEntity">Type of entity that supports the ACL</typeparam>
|
|
/// <param name="query">Query to filter</param>
|
|
/// <param name="customer">Customer</param>
|
|
/// <returns>
|
|
/// A task that represents the asynchronous operation
|
|
/// The task result contains the filtered query
|
|
/// </returns>
|
|
public virtual async Task<IQueryable<TEntity>> ApplyAcl<TEntity>(IQueryable<TEntity> query, Customer customer)
|
|
where TEntity : BaseEntity, IAclSupported
|
|
{
|
|
ArgumentNullException.ThrowIfNull(query);
|
|
|
|
ArgumentNullException.ThrowIfNull(customer);
|
|
|
|
var customerRoleIds = await _customerService.GetCustomerRoleIdsAsync(customer);
|
|
return await ApplyAcl(query, customerRoleIds);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Apply ACL to the passed query
|
|
/// </summary>
|
|
/// <typeparam name="TEntity">Type of entity that supports the ACL</typeparam>
|
|
/// <param name="query">Query to filter</param>
|
|
/// <param name="customerRoleIds">Identifiers of customer's roles</param>
|
|
/// <returns>
|
|
/// A task that represents the asynchronous operation
|
|
/// The task result contains the filtered query
|
|
/// </returns>
|
|
public virtual async Task<IQueryable<TEntity>> ApplyAcl<TEntity>(IQueryable<TEntity> query, int[] customerRoleIds)
|
|
where TEntity : BaseEntity, IAclSupported
|
|
{
|
|
ArgumentNullException.ThrowIfNull(query);
|
|
|
|
ArgumentNullException.ThrowIfNull(customerRoleIds);
|
|
|
|
if (!customerRoleIds.Any() || _catalogSettings.IgnoreAcl || !await IsEntityAclMappingExistAsync<TEntity>())
|
|
return query;
|
|
|
|
return from entity in query
|
|
where !entity.SubjectToAcl || _aclRecordRepository.Table.Any(acl =>
|
|
acl.EntityName == typeof(TEntity).Name && acl.EntityId == entity.Id && customerRoleIds.Contains(acl.CustomerRoleId))
|
|
select entity;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Deletes an ACL record
|
|
/// </summary>
|
|
/// <param name="aclRecord">ACL record</param>
|
|
/// <returns>A task that represents the asynchronous operation</returns>
|
|
public virtual async Task DeleteAclRecordAsync(AclRecord aclRecord)
|
|
{
|
|
await _aclRecordRepository.DeleteAsync(aclRecord);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets ACL records
|
|
/// </summary>
|
|
/// <typeparam name="TEntity">Type of entity that supports the ACL</typeparam>
|
|
/// <param name="entity">Entity</param>
|
|
/// <returns>
|
|
/// A task that represents the asynchronous operation
|
|
/// The task result contains the aCL records
|
|
/// </returns>
|
|
public virtual async Task<IList<AclRecord>> GetAclRecordsAsync<TEntity>(TEntity entity) where TEntity : BaseEntity, IAclSupported
|
|
{
|
|
ArgumentNullException.ThrowIfNull(entity);
|
|
|
|
var entityId = entity.Id;
|
|
var entityName = entity.GetType().Name;
|
|
|
|
var query = from ur in _aclRecordRepository.Table
|
|
where ur.EntityId == entityId &&
|
|
ur.EntityName == entityName
|
|
select ur;
|
|
var aclRecords = await query.ToListAsync();
|
|
|
|
return aclRecords;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Inserts an ACL record
|
|
/// </summary>
|
|
/// <typeparam name="TEntity">Type of entity that supports the ACL</typeparam>
|
|
/// <param name="entity">Entity</param>
|
|
/// <param name="customerRoleId">Customer role id</param>
|
|
/// <returns>A task that represents the asynchronous operation</returns>
|
|
public virtual async Task InsertAclRecordAsync<TEntity>(TEntity entity, int customerRoleId) where TEntity : BaseEntity, IAclSupported
|
|
{
|
|
ArgumentNullException.ThrowIfNull(entity);
|
|
|
|
if (customerRoleId == 0)
|
|
throw new ArgumentOutOfRangeException(nameof(customerRoleId));
|
|
|
|
var entityId = entity.Id;
|
|
var entityName = entity.GetType().Name;
|
|
|
|
var aclRecord = new AclRecord
|
|
{
|
|
EntityId = entityId,
|
|
EntityName = entityName,
|
|
CustomerRoleId = customerRoleId
|
|
};
|
|
|
|
await InsertAclRecordAsync(aclRecord);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Find customer role identifiers with granted access
|
|
/// </summary>
|
|
/// <typeparam name="TEntity">Type of entity that supports the ACL</typeparam>
|
|
/// <param name="entity">Entity</param>
|
|
/// <returns>
|
|
/// A task that represents the asynchronous operation
|
|
/// The task result contains the customer role identifiers
|
|
/// </returns>
|
|
public virtual async Task<int[]> GetCustomerRoleIdsWithAccessAsync<TEntity>(TEntity entity) where TEntity : BaseEntity, IAclSupported
|
|
{
|
|
ArgumentNullException.ThrowIfNull(entity);
|
|
|
|
var entityId = entity.Id;
|
|
var entityName = entity.GetType().Name;
|
|
|
|
var key = _staticCacheManager.PrepareKeyForDefaultCache(NopSecurityDefaults.AclRecordCacheKey, entityId, entityName);
|
|
|
|
var query = from ur in _aclRecordRepository.Table
|
|
where ur.EntityId == entityId &&
|
|
ur.EntityName == entityName
|
|
select ur.CustomerRoleId;
|
|
|
|
return await _staticCacheManager.GetAsync(key, () => query.ToArray());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Authorize ACL permission
|
|
/// </summary>
|
|
/// <typeparam name="TEntity">Type of entity that supports the ACL</typeparam>
|
|
/// <param name="entity">Entity</param>
|
|
/// <returns>
|
|
/// A task that represents the asynchronous operation
|
|
/// The task result contains true - authorized; otherwise, false
|
|
/// </returns>
|
|
public virtual async Task<bool> AuthorizeAsync<TEntity>(TEntity entity) where TEntity : BaseEntity, IAclSupported
|
|
{
|
|
return await AuthorizeAsync(entity, await _workContext.GetCurrentCustomerAsync());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Authorize ACL permission
|
|
/// </summary>
|
|
/// <typeparam name="TEntity">Type of entity that supports the ACL</typeparam>
|
|
/// <param name="entity">Entity</param>
|
|
/// <param name="customer">Customer</param>
|
|
/// <returns>
|
|
/// A task that represents the asynchronous operation
|
|
/// The task result contains true - authorized; otherwise, false
|
|
/// </returns>
|
|
public virtual async Task<bool> AuthorizeAsync<TEntity>(TEntity entity, Customer customer) where TEntity : BaseEntity, IAclSupported
|
|
{
|
|
if (entity == null)
|
|
return false;
|
|
|
|
if (customer == null)
|
|
return false;
|
|
|
|
if (_catalogSettings.IgnoreAcl)
|
|
return true;
|
|
|
|
if (!entity.SubjectToAcl)
|
|
return true;
|
|
|
|
foreach (var role1 in await _customerService.GetCustomerRolesAsync(customer))
|
|
foreach (var role2Id in await GetCustomerRoleIdsWithAccessAsync(entity))
|
|
if (role1.Id == role2Id)
|
|
//yes, we have such permission
|
|
return true;
|
|
|
|
//no permission found
|
|
return false;
|
|
}
|
|
|
|
#endregion
|
|
} |