diff --git a/src/Api/Controllers/CiphersController.cs b/src/Api/Controllers/CiphersController.cs index 626084e0d1..470c8ee510 100644 --- a/src/Api/Controllers/CiphersController.cs +++ b/src/Api/Controllers/CiphersController.cs @@ -52,18 +52,19 @@ namespace Bit.Api.Controllers } [HttpGet("{id}/full-details")] - public async Task GetDetails(string id) + [HttpGet("{id}/details")] + public async Task GetDetails(string id) { var userId = _userService.GetProperUserId(User).Value; var cipherId = new Guid(id); - var cipher = await _cipherRepository.GetFullDetailsByIdAsync(cipherId, userId); + var cipher = await _cipherRepository.GetByIdAsync(cipherId, userId); if(cipher == null) { throw new NotFoundException(); } var collectionCiphers = await _collectionCipherRepository.GetManyByUserIdCipherIdAsync(userId, cipherId); - return new CipherFullDetailsResponseModel(cipher, collectionCiphers); + return new CipherDetailsResponseModel(cipher, collectionCiphers); } [HttpGet("")] diff --git a/src/Api/Controllers/LoginsController.cs b/src/Api/Controllers/LoginsController.cs index e44fac4e07..a7823e3106 100644 --- a/src/Api/Controllers/LoginsController.cs +++ b/src/Api/Controllers/LoginsController.cs @@ -41,16 +41,16 @@ namespace Bit.Api.Controllers } [HttpGet("{id}")] - public async Task Get(string id) + public async Task Get(string id) { var userId = _userService.GetProperUserId(User).Value; - var login = await _cipherRepository.GetFullDetailsByIdAsync(new Guid(id), userId); + var login = await _cipherRepository.GetByIdAsync(new Guid(id), userId); if(login == null || login.Type != Core.Enums.CipherType.Login) { throw new NotFoundException(); } - var response = new LoginDetailsResponseModel(login); + var response = new LoginResponseModel(login); return response; } diff --git a/src/Core/Models/Api/Response/CipherResponseModel.cs b/src/Core/Models/Api/Response/CipherResponseModel.cs index 0d0e77eb1b..107ea9be6d 100644 --- a/src/Core/Models/Api/Response/CipherResponseModel.cs +++ b/src/Core/Models/Api/Response/CipherResponseModel.cs @@ -60,6 +60,7 @@ namespace Bit.Core.Models.Api { FolderId = cipher.FolderId?.ToString(); Favorite = cipher.Favorite; + Edit = cipher.Edit; } [Obsolete] @@ -69,6 +70,7 @@ namespace Bit.Core.Models.Api public string FolderId { get; set; } public bool Favorite { get; set; } + public bool Edit { get; set; } } public class CipherDetailsResponseModel : CipherResponseModel @@ -115,15 +117,4 @@ namespace Bit.Core.Models.Api public IEnumerable CollectionIds { get; set; } } - - public class CipherFullDetailsResponseModel : CipherDetailsResponseModel - { - public CipherFullDetailsResponseModel(CipherFullDetails cipher, IEnumerable collectionCiphers) - : base(cipher, collectionCiphers, "cipherFullDetails") - { - Edit = cipher.Edit; - } - - public bool Edit { get; set; } - } } diff --git a/src/Core/Models/Api/Response/LoginResponseModel.cs b/src/Core/Models/Api/Response/LoginResponseModel.cs index 1bea3c7003..55fc4e9887 100644 --- a/src/Core/Models/Api/Response/LoginResponseModel.cs +++ b/src/Core/Models/Api/Response/LoginResponseModel.cs @@ -36,12 +36,14 @@ namespace Bit.Core.Models.Api { FolderId = cipher.FolderId?.ToString(); Favorite = cipher.Favorite; + Edit = cipher.Edit; } public string Id { get; set; } public string OrganizationId { get; set; } public string FolderId { get; set; } public bool Favorite { get; set; } + public bool Edit { get; set; } public string Name { get; set; } public string Uri { get; set; } public string Username { get; set; } @@ -52,15 +54,4 @@ namespace Bit.Core.Models.Api [Obsolete] public FolderResponseModel Folder { get; set; } } - - public class LoginDetailsResponseModel : LoginResponseModel - { - public LoginDetailsResponseModel(CipherFullDetails cipher) - : base(cipher, "loginDetails") - { - Edit = cipher.Edit; - } - - public bool Edit { get; set; } - } } diff --git a/src/Core/Models/Data/CipherDetails.cs b/src/Core/Models/Data/CipherDetails.cs index a938a54dee..cb1bb58527 100644 --- a/src/Core/Models/Data/CipherDetails.cs +++ b/src/Core/Models/Data/CipherDetails.cs @@ -7,5 +7,6 @@ namespace Core.Models.Data { public Guid? FolderId { get; set; } public bool Favorite { get; set; } + public bool Edit { get; set; } } } diff --git a/src/Core/Models/Data/CipherFullDetails.cs b/src/Core/Models/Data/CipherFullDetails.cs deleted file mode 100644 index c4d86e76b4..0000000000 --- a/src/Core/Models/Data/CipherFullDetails.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Core.Models.Data -{ - public class CipherFullDetails : CipherDetails - { - public bool Edit { get; set; } - } -} diff --git a/src/Core/Repositories/ICipherRepository.cs b/src/Core/Repositories/ICipherRepository.cs index bcf8c2c7a7..361fdab562 100644 --- a/src/Core/Repositories/ICipherRepository.cs +++ b/src/Core/Repositories/ICipherRepository.cs @@ -9,7 +9,7 @@ namespace Bit.Core.Repositories public interface ICipherRepository : IRepository { Task GetByIdAsync(Guid id, Guid userId); - Task GetFullDetailsByIdAsync(Guid id, Guid userId); + Task GetCanEditByIdAsync(Guid userId, Guid cipherId); Task> GetManyByUserIdAsync(Guid userId); Task> GetManyByUserIdHasCollectionsAsync(Guid userId); Task> GetManyByOrganizationIdAsync(Guid organizationId); diff --git a/src/Core/Repositories/ICollectionUserRepository.cs b/src/Core/Repositories/ICollectionUserRepository.cs index fe80c85b23..d741614732 100644 --- a/src/Core/Repositories/ICollectionUserRepository.cs +++ b/src/Core/Repositories/ICollectionUserRepository.cs @@ -11,6 +11,5 @@ namespace Bit.Core.Repositories Task> GetManyByOrganizationUserIdAsync(Guid orgUserId); Task> GetManyDetailsByUserIdAsync(Guid userId); Task> GetManyDetailsByCollectionIdAsync(Guid organizationId, Guid collectionId); - Task GetCanEditByUserIdCipherIdAsync(Guid userId, Guid cipherId); } } diff --git a/src/Core/Repositories/SqlServer/CipherRepository.cs b/src/Core/Repositories/SqlServer/CipherRepository.cs index 0734c662de..a980003a82 100644 --- a/src/Core/Repositories/SqlServer/CipherRepository.cs +++ b/src/Core/Repositories/SqlServer/CipherRepository.cs @@ -35,16 +35,16 @@ namespace Bit.Core.Repositories.SqlServer } } - public async Task GetFullDetailsByIdAsync(Guid id, Guid userId) + public async Task GetCanEditByIdAsync(Guid userId, Guid cipherId) { using(var connection = new SqlConnection(ConnectionString)) { - var results = await connection.QueryAsync( - $"[{Schema}].[CipherFullDetails_ReadByIdUserId]", - new { Id = id, UserId = userId }, + var result = await connection.QueryFirstOrDefaultAsync( + $"[{Schema}].[Cipher_ReadCanEditByIdUserId]", + new { UserId = userId, CipherId = cipherId }, commandType: CommandType.StoredProcedure); - return results.FirstOrDefault(); + return result; } } @@ -57,8 +57,11 @@ namespace Bit.Core.Repositories.SqlServer new { UserId = userId }, commandType: CommandType.StoredProcedure); - // Return distinct Id results - return results.GroupBy(c => c.Id).Select(g => g.First()).ToList(); + // Return distinct Id results. If at least one of the grouped results allows edit, that we return it. + return results + .GroupBy(c => c.Id) + .Select(g => g.OrderByDescending(og => og.Edit).First()) + .ToList(); } } @@ -71,8 +74,11 @@ namespace Bit.Core.Repositories.SqlServer new { UserId = userId }, commandType: CommandType.StoredProcedure); - // Return distinct Id results - return results.GroupBy(c => c.Id).Select(g => g.First()).ToList(); + // Return distinct Id results. If at least one of the grouped results allows edit, that we return it. + return results + .GroupBy(c => c.Id) + .Select(g => g.OrderByDescending(og => og.Edit).First()) + .ToList(); } } @@ -102,8 +108,11 @@ namespace Bit.Core.Repositories.SqlServer }, commandType: CommandType.StoredProcedure); - // Return distinct Id results - return results.GroupBy(c => c.Id).Select(g => g.First()).ToList(); + // Return distinct Id results. If at least one of the grouped results allows edit, that we return it. + return results + .GroupBy(c => c.Id) + .Select(g => g.OrderByDescending(og => og.Edit).First()) + .ToList(); } } diff --git a/src/Core/Repositories/SqlServer/CollectionUserRepository.cs b/src/Core/Repositories/SqlServer/CollectionUserRepository.cs index 5a51936c39..811a9d0cce 100644 --- a/src/Core/Repositories/SqlServer/CollectionUserRepository.cs +++ b/src/Core/Repositories/SqlServer/CollectionUserRepository.cs @@ -59,18 +59,5 @@ namespace Bit.Core.Repositories.SqlServer return results.ToList(); } } - - public async Task GetCanEditByUserIdCipherIdAsync(Guid userId, Guid cipherId) - { - using(var connection = new SqlConnection(ConnectionString)) - { - var result = await connection.QueryFirstOrDefaultAsync( - $"[{Schema}].[CollectionUser_ReadCanEditByCipherIdUserId]", - new { UserId = userId, CipherId = cipherId }, - commandType: CommandType.StoredProcedure); - - return result; - } - } } } diff --git a/src/Core/Services/Implementations/CipherService.cs b/src/Core/Services/Implementations/CipherService.cs index e62bf0e0cf..3d699f1f86 100644 --- a/src/Core/Services/Implementations/CipherService.cs +++ b/src/Core/Services/Implementations/CipherService.cs @@ -16,7 +16,6 @@ namespace Bit.Core.Services private readonly IUserRepository _userRepository; private readonly IOrganizationRepository _organizationRepository; private readonly IOrganizationUserRepository _organizationUserRepository; - private readonly ICollectionUserRepository _collectionUserRepository; private readonly ICollectionCipherRepository _collectionCipherRepository; private readonly IPushService _pushService; @@ -26,7 +25,6 @@ namespace Bit.Core.Services IUserRepository userRepository, IOrganizationRepository organizationRepository, IOrganizationUserRepository organizationUserRepository, - ICollectionUserRepository collectionUserRepository, ICollectionCipherRepository collectionCipherRepository, IPushService pushService) { @@ -35,7 +33,6 @@ namespace Bit.Core.Services _userRepository = userRepository; _organizationRepository = organizationRepository; _organizationUserRepository = organizationUserRepository; - _collectionUserRepository = collectionUserRepository; _collectionCipherRepository = collectionCipherRepository; _pushService = pushService; } @@ -237,7 +234,7 @@ namespace Bit.Core.Services return true; } - return await _collectionUserRepository.GetCanEditByUserIdCipherIdAsync(userId, cipher.Id); + return await _cipherRepository.GetCanEditByIdAsync(userId, cipher.Id); } } } diff --git a/src/Sql/Sql.sqlproj b/src/Sql/Sql.sqlproj index 90e88383a4..f8bba0f8aa 100644 --- a/src/Sql/Sql.sqlproj +++ b/src/Sql/Sql.sqlproj @@ -93,7 +93,6 @@ - @@ -136,7 +135,6 @@ - @@ -159,7 +157,6 @@ - @@ -180,5 +177,6 @@ + \ No newline at end of file diff --git a/src/Sql/dbo/Functions/UserCanEditCipher.sql b/src/Sql/dbo/Functions/UserCanEditCipher.sql deleted file mode 100644 index 3c1a70d344..0000000000 --- a/src/Sql/dbo/Functions/UserCanEditCipher.sql +++ /dev/null @@ -1,33 +0,0 @@ -CREATE FUNCTION [dbo].[UserCanEditCipher](@UserId UNIQUEIDENTIFIER, @CipherId UNIQUEIDENTIFIER) -RETURNS BIT AS -BEGIN - DECLARE @CanEdit BIT - - ;WITH [CTE] AS( - SELECT - CASE WHEN OU.[AccessAll] = 1 OR CU.[ReadOnly] = 0 THEN 1 ELSE 0 END [CanEdit] - FROM - [dbo].[Cipher] C - INNER JOIN - [dbo].[Organization] O ON C.[UserId] IS NULL AND O.[Id] = C.[OrganizationId] - INNER JOIN - [dbo].[OrganizationUser] OU ON OU.[OrganizationId] = O.[Id] AND OU.[UserId] = @UserId - LEFT JOIN - [dbo].[CollectionCipher] CC ON C.[UserId] IS NULL AND OU.[AccessAll] = 0 AND CC.[CipherId] = C.[Id] - LEFT JOIN - [dbo].[CollectionUser] CU ON CU.[CollectionId] = CC.[CollectionId] AND CU.[OrganizationUserId] = OU.[Id] - WHERE - C.[Id] = @CipherId - AND OU.[Status] = 2 -- 2 = Confirmed - AND O.[Enabled] = 1 - AND (OU.[AccessAll] = 1 OR CU.[CollectionId] IS NOT NULL) - ) - SELECT - @CanEdit = CASE WHEN COUNT(1) > 0 THEN 1 ELSE 0 END - FROM - [CTE] - WHERE - [CanEdit] = 1 - - RETURN @CanEdit -END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/CipherDetails_ReadByIdUserId.sql b/src/Sql/dbo/Stored Procedures/CipherDetails_ReadByIdUserId.sql index 6928c65741..6e45b2fcaa 100644 --- a/src/Sql/dbo/Stored Procedures/CipherDetails_ReadByIdUserId.sql +++ b/src/Sql/dbo/Stored Procedures/CipherDetails_ReadByIdUserId.sql @@ -6,7 +6,11 @@ BEGIN SET NOCOUNT ON SELECT TOP 1 - C.* + C.*, + CASE + WHEN C.[UserId] IS NOT NULL OR OU.[AccessAll] = 1 OR CU.[ReadOnly] = 0 THEN 1 + ELSE 0 + END [Edit] FROM [dbo].[CipherDetails](@UserId) C LEFT JOIN @@ -28,4 +32,6 @@ BEGIN AND (OU.[AccessAll] = 1 OR CU.[CollectionId] IS NOT NULL) ) ) + ORDER BY + [Edit] DESC END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/CipherDetails_ReadByTypeUserId.sql b/src/Sql/dbo/Stored Procedures/CipherDetails_ReadByTypeUserId.sql index 9edef5e578..c58bf263c8 100644 --- a/src/Sql/dbo/Stored Procedures/CipherDetails_ReadByTypeUserId.sql +++ b/src/Sql/dbo/Stored Procedures/CipherDetails_ReadByTypeUserId.sql @@ -6,7 +6,11 @@ BEGIN SET NOCOUNT ON SELECT - C.* + C.*, + CASE + WHEN C.[UserId] IS NOT NULL OR OU.[AccessAll] = 1 OR CU.[ReadOnly] = 0 THEN 1 + ELSE 0 + END [Edit] FROM [dbo].[CipherDetails](@UserId) C LEFT JOIN diff --git a/src/Sql/dbo/Stored Procedures/CipherDetails_ReadByUserId.sql b/src/Sql/dbo/Stored Procedures/CipherDetails_ReadByUserId.sql index c4d7d787fa..558a73f934 100644 --- a/src/Sql/dbo/Stored Procedures/CipherDetails_ReadByUserId.sql +++ b/src/Sql/dbo/Stored Procedures/CipherDetails_ReadByUserId.sql @@ -5,7 +5,11 @@ BEGIN SET NOCOUNT ON SELECT - C.* + C.*, + CASE + WHEN C.[UserId] IS NOT NULL OR OU.[AccessAll] = 1 OR CU.[ReadOnly] = 0 THEN 1 + ELSE 0 + END [Edit] FROM [dbo].[CipherDetails](@UserId) C LEFT JOIN diff --git a/src/Sql/dbo/Stored Procedures/CipherDetails_ReadByUserIdHasCollection.sql b/src/Sql/dbo/Stored Procedures/CipherDetails_ReadByUserIdHasCollection.sql index 574717b6b2..893785d0a0 100644 --- a/src/Sql/dbo/Stored Procedures/CipherDetails_ReadByUserIdHasCollection.sql +++ b/src/Sql/dbo/Stored Procedures/CipherDetails_ReadByUserIdHasCollection.sql @@ -5,7 +5,11 @@ BEGIN SET NOCOUNT ON SELECT - C.* + C.*, + CASE + WHEN OU.[AccessAll] = 1 OR CU.[ReadOnly] = 0 THEN 1 + ELSE 0 + END [Edit] FROM [dbo].[CipherDetails](@UserId) C INNER JOIN diff --git a/src/Sql/dbo/Stored Procedures/CipherFullDetails_ReadByIdUserId.sql b/src/Sql/dbo/Stored Procedures/CipherFullDetails_ReadByIdUserId.sql deleted file mode 100644 index dcc017c2dd..0000000000 --- a/src/Sql/dbo/Stored Procedures/CipherFullDetails_ReadByIdUserId.sql +++ /dev/null @@ -1,35 +0,0 @@ -CREATE PROCEDURE [dbo].[CipherFullDetails_ReadByIdUserId] - @Id UNIQUEIDENTIFIER, - @UserId UNIQUEIDENTIFIER -AS -BEGIN - SET NOCOUNT ON - - SELECT TOP 1 - C.*, - CASE - WHEN C.[OrganizationId] IS NULL THEN 1 - ELSE [dbo].[UserCanEditCipher](@UserId, @Id) - END [Edit] - FROM - [dbo].[CipherDetails](@UserId) C - LEFT JOIN - [dbo].[Organization] O ON C.[UserId] IS NULL AND O.[Id] = C.[OrganizationId] - LEFT JOIN - [dbo].[OrganizationUser] OU ON OU.[OrganizationId] = O.[Id] AND OU.[UserId] = @UserId - LEFT JOIN - [dbo].[CollectionCipher] CC ON C.[UserId] IS NULL AND OU.[AccessAll] = 0 AND CC.[CipherId] = C.[Id] - LEFT JOIN - [dbo].[CollectionUser] CU ON CU.[CollectionId] = CC.[CollectionId] AND CU.[OrganizationUserId] = OU.[Id] - WHERE - C.Id = @Id - AND ( - C.[UserId] = @UserId - OR ( - C.[UserId] IS NULL - AND OU.[Status] = 2 -- 2 = Confirmed - AND O.[Enabled] = 1 - AND (OU.[AccessAll] = 1 OR CU.[CollectionId] IS NOT NULL) - ) - ) -END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/Cipher_ReadCanEditByIdUserId.sql b/src/Sql/dbo/Stored Procedures/Cipher_ReadCanEditByIdUserId.sql new file mode 100644 index 0000000000..b56a3e478a --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/Cipher_ReadCanEditByIdUserId.sql @@ -0,0 +1,39 @@ +CREATE PROCEDURE [dbo].[Cipher_CanEditByIdUserId] + @Id UNIQUEIDENTIFIER, + @UserId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + ( + SELECT TOP 1 + CASE + WHEN C.[UserId] IS NOT NULL OR OU.[AccessAll] = 1 OR CU.[ReadOnly] = 0 THEN 1 + ELSE 0 + END [Edit] + FROM + [dbo].[CipherDetails](@UserId) C + LEFT JOIN + [dbo].[Organization] O ON C.[UserId] IS NULL AND O.[Id] = C.[OrganizationId] + LEFT JOIN + [dbo].[OrganizationUser] OU ON OU.[OrganizationId] = O.[Id] AND OU.[UserId] = @UserId + LEFT JOIN + [dbo].[CollectionCipher] CC ON C.[UserId] IS NULL AND OU.[AccessAll] = 0 AND CC.[CipherId] = C.[Id] + LEFT JOIN + [dbo].[CollectionUser] CU ON CU.[CollectionId] = CC.[CollectionId] AND CU.[OrganizationUserId] = OU.[Id] + WHERE + C.Id = @Id + AND ( + C.[UserId] = @UserId + OR ( + C.[UserId] IS NULL + AND OU.[Status] = 2 -- 2 = Confirmed + AND O.[Enabled] = 1 + AND (OU.[AccessAll] = 1 OR CU.[CollectionId] IS NOT NULL) + ) + ) + ORDER BY + [Edit] DESC + ) +END \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/CollectionUser_ReadCanEditByCipherIdUserId.sql b/src/Sql/dbo/Stored Procedures/CollectionUser_ReadCanEditByCipherIdUserId.sql deleted file mode 100644 index 86c825fd2a..0000000000 --- a/src/Sql/dbo/Stored Procedures/CollectionUser_ReadCanEditByCipherIdUserId.sql +++ /dev/null @@ -1,10 +0,0 @@ -CREATE PROCEDURE [dbo].[CollectionUser_ReadCanEditByCipherIdUserId] - @UserId UNIQUEIDENTIFIER, - @CipherId AS UNIQUEIDENTIFIER -AS -BEGIN - SET NOCOUNT ON - - SELECT - [dbo].[UserCanEditCipher](@UserId, @CipherId) -END \ No newline at end of file