diff --git a/Interfaces/IXFileProvider.cs b/Interfaces/IXFileProvider.cs index cc3f315..8d279ac 100644 --- a/Interfaces/IXFileProvider.cs +++ b/Interfaces/IXFileProvider.cs @@ -22,6 +22,7 @@ namespace xFileService.Interfaces { // #region Props ... + IXFileTagProvider TagProvider { get; } IHubContext Hub { get; } IXFileRepository FileRepository { get; } IXStorageProvider StorageProvider { get; } @@ -53,6 +54,72 @@ namespace xFileService.Interfaces Task> ToDtoQueryResult(XQueryResult queryResult); #endregion + // + #region Tags ... + /// + /// Attach Tag to Specified File ... + /// + /// + /// + /// + /// + Task TagAttach( + string tag, + Guid id, + XUserClaimsInfoDto userInfo = null + ); + + /// + /// Detach Tag fro Specified File ... + /// + /// + /// + /// + /// + Task TagDetach( + string tag, + Guid id, + XUserClaimsInfoDto userInfo = null + ); + + /// + /// Attach Tags for Specified File ... + /// + /// + /// + /// + /// + Task TagsAttach( + IEnumerable tags, + Guid id, + XUserClaimsInfoDto userInfo = null + ); + + /// + /// Detach Tags for Specified File ... + /// + /// + /// + /// + /// + Task TagsDetach( + IEnumerable tags, + Guid id, + XUserClaimsInfoDto userInfo = null + ); + + /// + /// Detach all Attached Tags for Specified File ... + /// + /// + /// + /// + Task TagsDetach( + Guid id, + XUserClaimsInfoDto userInfo = null + ); + #endregion + // #region Tools ... /// @@ -120,6 +187,56 @@ namespace xFileService.Interfaces Task GetFileDescriptor(string fileName); #endregion + // + #region Reference ... + /// + /// Get Reference Identifier for Specified Provider and Specified File ... + /// + /// + /// + /// + string GetIdentifier( + string providedFor, + Guid id + ); + + /// + /// Check Specified File has Refernce to Provider ... + /// + /// + /// + Task IsProvidedFor( + string providedFor, + Guid id + ); + + /// + /// Add Specified Reference to Specified File ... + /// + /// + /// + /// + /// + Task AddReference( + string providedFor, + Guid id, + XUserClaimsInfoDto userInfo = null + ); + + /// + /// Remove Specified Reference from Specified File ... + /// + /// + /// + /// + /// + Task RemoveReference( + string providedFor, + Guid id, + XUserClaimsInfoDto userInfo = null + ); + #endregion + // #region Data Model ... /// diff --git a/Models/Dtos/XFileDto.cs b/Models/Dtos/XFileDto.cs index bdc8355..81fdbc0 100644 --- a/Models/Dtos/XFileDto.cs +++ b/Models/Dtos/XFileDto.cs @@ -20,7 +20,7 @@ namespace xFileService.Models.Dtos public string OwnerId { get; set; } public XPersonDto Owner { get; set; } - + public IList References = new List(); } } \ No newline at end of file diff --git a/Providers/XFileProvider.cs b/Providers/XFileProvider.cs index 681d3fe..61149cd 100644 --- a/Providers/XFileProvider.cs +++ b/Providers/XFileProvider.cs @@ -31,6 +31,7 @@ namespace xFileService.Providers { // #region Props ... + public IXFileTagProvider TagProvider { get; } public IHubContext Hub { get; } public IXFileRepository FileRepository { get; } public IXStorageProvider StorageProvider { get; } @@ -41,6 +42,7 @@ namespace xFileService.Providers // #region Constructor ... public XFileProvider( + IXFileTagProvider tagProvider, IXFileRepository fileRepository, IXStorageProvider storageProvider, IXIdentityProvider identityProvider, @@ -50,6 +52,7 @@ namespace xFileService.Providers { // Hub = hub; + TagProvider = tagProvider; FileRepository = fileRepository; StorageProvider = storageProvider; IdentityProvider = identityProvider; @@ -155,6 +158,262 @@ namespace xFileService.Providers // return result; } + + /// + /// Check Specified User Info Has Permissions for Specified File ... + /// + /// + /// + /// + private async Task HasPermission( + Guid id, + XUserClaimsInfoDto userInfo = null + ) + { + // + var result = false; + + // + // Validate ... + result = !id.IsNull() && + !id.IsDefaultGuid() && + !userInfo.IsNullOrDefault(); + if (!result) + { + XException.InvalidArgs.Throw(); + } + + // + // Check Item Exists ... + result = await IsExists(id); + if (!result) + { + XException.NotFound.Throw(); + } + + // + // Retrieve Dto ... + var dto = await Get(id); + result = !dto.IsNullOrDefault(); + if (!result) + { + XException.ActionFailed.Throw(); + } + + // + result = + dto.OwnerId == userInfo.UserId || + userInfo.Roles.Any(r => r.ToNormalString() == "admin"); + + // + return result; + } + #endregion + + // + #region Tags ... + /// + /// Attach Tag to Specified File ... + /// + /// + /// + /// + public async Task TagAttach( + string tag, + Guid id, + XUserClaimsInfoDto userInfo = null + ) + { + // + // Validate ... + var isValid = !tag.IsNullOrEmpty() && + !id.IsNull() && + !id.IsDefaultGuid() && + !userInfo.IsNullOrDefault(); + if (isValid) + { + // + // Check Permissions ... + isValid = await HasPermission( + id: id, + userInfo: userInfo + ); + if (!isValid) + { + XException.NotAllowed.Throw(); + } + + // + try + { + await TagProvider.Attach(tag, id); + } + catch { } + } + } + + /// + /// Detach Tag fro Specified File ... + /// + /// + /// + /// + public async Task TagDetach( + string tag, + Guid id, + XUserClaimsInfoDto userInfo = null + ) + { + // + // Validate ... + var isValid = !tag.IsNullOrEmpty() && + !id.IsNull() && + !id.IsDefaultGuid(); + if (isValid) + { + // + // Check Permissions ... + isValid = await HasPermission( + id: id, + userInfo: userInfo + ); + if (!isValid) + { + XException.NotAllowed.Throw(); + } + + // + try + { + await TagProvider.Detach(tag, id); + } + catch { } + } + } + + /// + /// Attach Tags for Specified File ... + /// + /// + /// + /// + public async Task TagsAttach( + IEnumerable tags, + Guid id, + XUserClaimsInfoDto userInfo = null + ) + { + // + // Validate ... + var isValid = !tags.IsNull() && + tags.HasChild() && + !id.IsNull() && + !id.IsDefaultGuid(); + if (isValid) + { + // + // Check Permissions ... + isValid = await HasPermission( + id: id, + userInfo: userInfo + ); + if (!isValid) + { + XException.NotAllowed.Throw(); + } + + // + try + { + // + foreach (var tag in tags) + { + await TagAttach(tag, id); + } + } + catch { } + } + } + + /// + /// Detach Tags for Specified File ... + /// + /// + /// + /// + public async Task TagsDetach( + IEnumerable tags, + Guid id, + XUserClaimsInfoDto userInfo = null + ) + { + // + // Validate ... + var isValid = !tags.IsNull() && + tags.HasChild() && + !id.IsNull() && + !id.IsDefaultGuid(); + if (isValid) + { + // + // Check Permissions ... + isValid = await HasPermission( + id: id, + userInfo: userInfo + ); + if (!isValid) + { + XException.NotAllowed.Throw(); + } + + // + try + { + // + foreach (var tag in tags) + { + await TagDetach(tag, id); + } + } + catch { } + } + } + + /// + /// Detach all Attached Tags for Specified File ... + /// + /// + /// + public async Task TagsDetach( + Guid id, + XUserClaimsInfoDto userInfo = null + ) + { + // + // Validate ... + var isValid = !id.IsNull() && !id.IsDefaultGuid(); + if (isValid) + { + // + // Check Permissions ... + isValid = await HasPermission( + id: id, + userInfo: userInfo + ); + if (!isValid) + { + XException.NotAllowed.Throw(); + } + + // + try + { + // + var tags = await TagProvider.RemoveTagsFor(id); + } + catch { } + } + } #endregion // @@ -478,6 +737,17 @@ namespace xFileService.Providers result.Add(model.Id); } + // + // Remove Tags ... + if (!result.IsNull() && result.HasChild()) + { + // + foreach (var id in result) + { + await TagsDetach(id); + } + } + // return result; } @@ -642,6 +912,206 @@ namespace xFileService.Providers } #endregion + // + #region Reference ... + /// + /// Get Reference Identifier for Specified Provider and Specified File ... + /// + /// + /// + /// + public string GetIdentifier( + string providedFor, + Guid id + ) + { + return $"{providedFor}_{id}"; + } + + /// + /// Check Specified File has Refernce to Provider ... + /// + /// + /// + public async Task IsProvidedFor( + string providedFor, + Guid id + ) + { + // + // Validate ... + var isValid = + !providedFor.IsNullOrEmpty() && + !id.IsNull() && + !id.IsDefaultGuid(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Check Exists ... + isValid = await IsExists(id); + if (!isValid) + { + XException.NotFound.Throw(); + } + + // + var dto = await Get(id); + isValid = !dto.IsNullOrDefault(); + if (!isValid) + { + XException.ActionFailed.Throw(); + } + + // + var identifier = GetIdentifier(providedFor, id); + var result = + dto.References.IsNull() && + dto.References.HasChild() && + dto.References.Any(r => r.ToNormalString() == identifier.ToNormalString()); + + // + return result; + } + + /// + /// Add Specified Reference to Specified File ... + /// + /// + /// + /// + /// + public async Task AddReference( + string providedFor, + Guid id, + XUserClaimsInfoDto userInfo = null + ) + { + // + // Validate ... + var isValid = + !providedFor.IsNullOrEmpty() && + !id.IsNull() && + !id.IsDefaultGuid() && + !userInfo.IsNullOrDefault(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Check Has Reference or not ... + isValid = await IsProvidedFor(providedFor, id); + if (!isValid) + { + // + // Check Permissions ... + isValid = await HasPermission( + id: id, + userInfo: userInfo + ); + if (!isValid) + { + XException.NotAllowed.Throw(); + } + + // + var dto = await Get(id); + isValid = !dto.IsNullOrDefault(); + if (!isValid) + { + XException.ActionFailed.Throw(); + } + + // + var identifier = GetIdentifier(providedFor, id); + dto.References.Add(identifier); + + // + dto = await Update( + id: dto.Id, + item: dto, + userInfo: userInfo + ); + isValid = !dto.IsNullOrDefault(); + if (!isValid) + { + XException.ActionFailed.Throw(); + } + } + } + + /// + /// Remove Specified Reference from Specified File ... + /// + /// + /// + /// + /// + public async Task RemoveReference( + string providedFor, + Guid id, + XUserClaimsInfoDto userInfo = null + ) + { + // + // Validate ... + var isValid = + !providedFor.IsNullOrEmpty() && + !id.IsNull() && + !id.IsDefaultGuid() && + !userInfo.IsNullOrDefault(); + if (!isValid) + { + XException.InvalidArgs.Throw(); + } + + // + // Check Has Reference or not ... + isValid = await IsProvidedFor(providedFor, id); + if (isValid) + { + // + // Check Permissions ... + isValid = await HasPermission( + id: id, + userInfo: userInfo + ); + if (!isValid) + { + XException.NotAllowed.Throw(); + } + + // + // Retrieve Dto ... + var dto = await Get(id); + isValid = !dto.IsNullOrDefault(); + if (!isValid) + { + XException.ActionFailed.Throw(); + } + + // + var identifier = GetIdentifier(providedFor, id); + dto.References = dto.References + .Where(r => r.ToNormalString() != identifier.ToNormalString()) + .ToList(); + dto = await Update( + id: dto.Id, + item: dto, + userInfo: userInfo + ); + isValid = !dto.IsNullOrDefault(); + if (!isValid) + { + XException.ActionFailed.Throw(); + } + } + } + #endregion + // #region Data Model ... /// @@ -831,6 +1301,18 @@ namespace xFileService.Providers } // + // Check Permissions ... + isValid = await HasPermission( + id: id, + userInfo: userInfo + ); + if (!isValid) + { + XException.NotAllowed.Throw(); + } + + // + // Retrieve Entity ... var entity = await FileRepository.GetAsync(id); if (entity.IsNullOrDefault()) { @@ -845,9 +1327,11 @@ namespace xFileService.Providers { nameof(XFile.Id), nameof(XFile.Deleted), - nameof(XFileDto.Owner) + nameof(XFileDto.Owner), + nameof(XFile.References), } ); + entity.References = item.References.ToListString(); if (entity.IsNullOrDefault()) { XException.InvalidData.Throw(); @@ -861,16 +1345,7 @@ namespace xFileService.Providers } // - // Validate Action ... - isValid = - entity.OwnerId == userInfo.UserId || - userInfo.Roles.Any(r => r.ToNormalString() == "admin".ToNormalString()); - if (!isValid) - { - XException.NotAllowed.Throw(); - } - - // + // Converts to Dto ... var result = await ToDto(entity); // @@ -915,10 +1390,11 @@ namespace xFileService.Providers } // - // Validate Action ... - isValid = - result.OwnerId == userInfo.UserId || - userInfo.Roles.Any(r => r.ToNormalString() == "admin".ToNormalString()); + // Check Permissions ... + isValid = await HasPermission( + id: id, + userInfo: userInfo + ); if (!isValid) { XException.NotAllowed.Throw(); @@ -938,6 +1414,10 @@ namespace xFileService.Providers XException.ActionFailed.Throw(); } + // + // Removing Tags ... + await TagProvider.RemoveTagsFor(result.Id); + // return result; }