1
0
mirror of https://github.com/bitwarden/server.git synced 2025-02-23 03:01:23 +01:00

permission checks for cipher crud operations

This commit is contained in:
Kyle Spearrin 2017-03-24 09:27:15 -04:00
parent 0dae19bd4f
commit 10c72fafda
10 changed files with 78 additions and 11 deletions

View File

@ -127,7 +127,7 @@ namespace Bit.Api.Controllers
throw new NotFoundException();
}
await _cipherService.DeleteAsync(cipher);
await _cipherService.DeleteAsync(cipher, userId);
}
}
}

View File

@ -59,7 +59,7 @@ namespace Bit.Api.Controllers
{
var userId = _userService.GetProperUserId(User).Value;
var login = model.ToCipherDetails(userId);
await _cipherService.SaveAsync(login);
await _cipherService.SaveAsync(login, userId);
var response = new LoginResponseModel(login);
return response;
@ -76,7 +76,7 @@ namespace Bit.Api.Controllers
throw new NotFoundException();
}
await _cipherService.SaveAsync(model.ToCipherDetails(login));
await _cipherService.SaveAsync(model.ToCipherDetails(login), userId);
var response = new LoginResponseModel(login);
return response;
@ -86,13 +86,14 @@ namespace Bit.Api.Controllers
[HttpPost("{id}/delete")]
public async Task Delete(string id)
{
var login = await _cipherRepository.GetByIdAsync(new Guid(id), _userService.GetProperUserId(User).Value);
var userId = _userService.GetProperUserId(User).Value;
var login = await _cipherRepository.GetByIdAsync(new Guid(id), userId);
if(login == null || login.Type != Core.Enums.CipherType.Login)
{
throw new NotFoundException();
}
await _cipherService.DeleteAsync(login);
await _cipherService.DeleteAsync(login, userId);
}
}
}

View File

@ -12,5 +12,6 @@ namespace Bit.Core.Repositories
Task<ICollection<SubvaultUserDetails>> GetManyDetailsByUserIdAsync(Guid userId);
Task<ICollection<SubvaultUserPermissions>> GetPermissionsByUserIdAsync(Guid userId, IEnumerable<Guid> subvaultIds,
Guid organizationId);
Task<bool> GetIsAdminByUserIdCipherIdAsync(Guid userId, Guid cipherId);
}
}

View File

@ -60,5 +60,18 @@ namespace Bit.Core.Repositories.SqlServer
return results.ToList();
}
}
public async Task<bool> GetIsAdminByUserIdCipherIdAsync(Guid userId, Guid cipherId)
{
using(var connection = new SqlConnection(ConnectionString))
{
var result = await connection.QueryFirstOrDefaultAsync<bool>(
$"[{Schema}].[SubvaultUser_ReadIsAdminByCipherIdUserId]",
new { UserId = userId, CipherId = cipherId },
commandType: CommandType.StoredProcedure);
return result;
}
}
}
}

View File

@ -8,8 +8,8 @@ namespace Bit.Core.Services
{
public interface ICipherService
{
Task SaveAsync(CipherDetails cipher);
Task DeleteAsync(Cipher cipher);
Task SaveAsync(CipherDetails cipher, Guid savingUserId);
Task DeleteAsync(CipherDetails cipher, Guid deletingUserId);
Task SaveFolderAsync(Folder folder);
Task DeleteFolderAsync(Folder folder);
Task MoveSubvaultAsync(Cipher cipher, IEnumerable<Guid> subvaultIds, Guid userId);

View File

@ -37,8 +37,14 @@ namespace Bit.Core.Services
_pushService = pushService;
}
public async Task SaveAsync(CipherDetails cipher)
public async Task SaveAsync(CipherDetails cipher, Guid savingUserId)
{
if(!(await UserHasAdminRights(cipher, savingUserId)))
{
throw new BadRequestException("Not an admin.");
}
cipher.UserId = savingUserId;
if(cipher.Id == default(Guid))
{
await _cipherRepository.CreateAsync(cipher);
@ -56,8 +62,13 @@ namespace Bit.Core.Services
}
}
public async Task DeleteAsync(Cipher cipher)
public async Task DeleteAsync(CipherDetails cipher, Guid deletingUserId)
{
if(!(await UserHasAdminRights(cipher, deletingUserId)))
{
throw new BadRequestException("Not an admin.");
}
await _cipherRepository.DeleteAsync(cipher);
// push
@ -151,5 +162,15 @@ namespace Bit.Core.Services
await _pushService.PushSyncCiphersAsync(userId.Value);
}
}
private async Task<bool> UserHasAdminRights(CipherDetails cipher, Guid userId)
{
if(!cipher.OrganizationId.HasValue && cipher.UserId.HasValue && cipher.UserId.Value == userId)
{
return true;
}
return await _subvaultUserRepository.GetIsAdminByUserIdCipherIdAsync(userId, cipher.Id);
}
}
}

View File

@ -173,5 +173,6 @@
<Build Include="dbo\Stored Procedures\OrganizationUserUserDetails_ReadByOrganizationId.sql" />
<Build Include="dbo\Stored Procedures\Subvault_ReadByOrganizationIdAdminUserId.sql" />
<Build Include="dbo\User Defined Types\GuidIdArray.sql" />
<Build Include="dbo\Stored Procedures\SubvaultUser_ReadIsAdminByCipherIdUserId.sql" />
</ItemGroup>
</Project>

View File

@ -25,7 +25,7 @@ BEGIN
VALUES
(
@Id,
@UserId,
CASE WHEN @OrganizationId IS NULL THEN @UserId ELSE NULL END,
@OrganizationId,
@Type,
@Data,

View File

@ -15,7 +15,7 @@ BEGIN
UPDATE
[dbo].[Cipher]
SET
[UserId] = @UserId,
[UserId] = CASE WHEN @OrganizationId IS NULL THEN @UserId ELSE NULL END,
[OrganizationId] = @OrganizationId,
[Type] = @Type,
[Data] = @Data,

View File

@ -0,0 +1,30 @@
CREATE PROCEDURE [dbo].[SubvaultUser_ReadIsAdminByCipherIdUserId]
@UserId UNIQUEIDENTIFIER,
@CipherId AS UNIQUEIDENTIFIER
AS
BEGIN
SET NOCOUNT ON
;WITH [CTE] AS(
SELECT
CASE WHEN OU.[Type] = 2 THEN SU.[Admin] ELSE 1 END AS [Admin] -- 2 = Regular User
FROM
[dbo].[SubvaultUser] SU
INNER JOIN
[dbo].[SubvaultCipher] SC ON SC.SubvaultId = SU.SubvaultId
INNER JOIN
[dbo].[Cipher] C ON SC.[CipherId] = C.[Id]
INNER JOIN
[dbo].[OrganizationUser] OU ON OU.Id = SU.OrganizationUserId AND OU.OrganizationId = C.OrganizationId
WHERE
C.[Id] = @CipherId
AND OU.[UserId] = @UserId
AND OU.[Status] = 2 -- 2 = Confirmed
)
SELECT
CASE WHEN COUNT(1) > 0 THEN 1 ELSE 0 END
FROM
[CTE]
WHERE
[Admin] = 1
END