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; /// /// ACL service /// public partial class AclService : IAclService { #region Fields protected readonly CatalogSettings _catalogSettings; protected readonly ICustomerService _customerService; protected readonly IRepository _aclRecordRepository; protected readonly IStaticCacheManager _staticCacheManager; protected readonly IWorkContext _workContext; #endregion #region Ctor public AclService(CatalogSettings catalogSettings, ICustomerService customerService, IRepository aclRecordRepository, IStaticCacheManager staticCacheManager, IWorkContext workContext) { _catalogSettings = catalogSettings; _customerService = customerService; _aclRecordRepository = aclRecordRepository; _staticCacheManager = staticCacheManager; _workContext = workContext; } #endregion #region Utilities /// /// Inserts an ACL record /// /// ACL record /// A task that represents the asynchronous operation protected virtual async Task InsertAclRecordAsync(AclRecord aclRecord) { await _aclRecordRepository.InsertAsync(aclRecord); } /// /// Get a value indicating whether any ACL records exist for entity type are related to customer roles /// /// Type of entity that supports the ACL /// /// A task that represents the asynchronous operation /// The task result contains true if exist; otherwise false /// protected virtual async Task IsEntityAclMappingExistAsync() 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 /// /// Apply ACL to the passed query /// /// Type of entity that supports the ACL /// Query to filter /// Customer /// /// A task that represents the asynchronous operation /// The task result contains the filtered query /// public virtual async Task> ApplyAcl(IQueryable query, Customer customer) where TEntity : BaseEntity, IAclSupported { ArgumentNullException.ThrowIfNull(query); ArgumentNullException.ThrowIfNull(customer); var customerRoleIds = await _customerService.GetCustomerRoleIdsAsync(customer); return await ApplyAcl(query, customerRoleIds); } /// /// Apply ACL to the passed query /// /// Type of entity that supports the ACL /// Query to filter /// Identifiers of customer's roles /// /// A task that represents the asynchronous operation /// The task result contains the filtered query /// public virtual async Task> ApplyAcl(IQueryable query, int[] customerRoleIds) where TEntity : BaseEntity, IAclSupported { ArgumentNullException.ThrowIfNull(query); ArgumentNullException.ThrowIfNull(customerRoleIds); if (!customerRoleIds.Any() || _catalogSettings.IgnoreAcl || !await IsEntityAclMappingExistAsync()) 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; } /// /// Deletes an ACL record /// /// ACL record /// A task that represents the asynchronous operation public virtual async Task DeleteAclRecordAsync(AclRecord aclRecord) { await _aclRecordRepository.DeleteAsync(aclRecord); } /// /// Gets ACL records /// /// Type of entity that supports the ACL /// Entity /// /// A task that represents the asynchronous operation /// The task result contains the aCL records /// public virtual async Task> GetAclRecordsAsync(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; } /// /// Inserts an ACL record /// /// Type of entity that supports the ACL /// Entity /// Customer role id /// A task that represents the asynchronous operation public virtual async Task InsertAclRecordAsync(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); } /// /// Find customer role identifiers with granted access /// /// Type of entity that supports the ACL /// Entity /// /// A task that represents the asynchronous operation /// The task result contains the customer role identifiers /// public virtual async Task GetCustomerRoleIdsWithAccessAsync(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()); } /// /// Authorize ACL permission /// /// Type of entity that supports the ACL /// Entity /// /// A task that represents the asynchronous operation /// The task result contains true - authorized; otherwise, false /// public virtual async Task AuthorizeAsync(TEntity entity) where TEntity : BaseEntity, IAclSupported { return await AuthorizeAsync(entity, await _workContext.GetCurrentCustomerAsync()); } /// /// Authorize ACL permission /// /// Type of entity that supports the ACL /// Entity /// Customer /// /// A task that represents the asynchronous operation /// The task result contains true - authorized; otherwise, false /// public virtual async Task AuthorizeAsync(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 }