mirror of
https://github.com/bitwarden/server.git
synced 2025-05-29 15:24:51 -05:00
user can edit responses and cipher partial updates
This commit is contained in:
parent
84c5873cfd
commit
5029af33c5
@ -104,6 +104,15 @@ namespace Bit.Api.Controllers
|
|||||||
// await _cipherService.SaveAsync(cipher);
|
// await _cipherService.SaveAsync(cipher);
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
[HttpPut("{id}/partial")]
|
||||||
|
[HttpPost("{id}/partial")]
|
||||||
|
public async Task PutPartial(string id, [FromBody]CipherPartialRequestModel model)
|
||||||
|
{
|
||||||
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
|
var folderId = string.IsNullOrWhiteSpace(model.FolderId) ? null : (Guid?)new Guid(model.FolderId);
|
||||||
|
await _cipherService.UpdatePartialAsync(new Guid(id), userId, folderId, model.Favorite);
|
||||||
|
}
|
||||||
|
|
||||||
[HttpPut("{id}/move")]
|
[HttpPut("{id}/move")]
|
||||||
[HttpPost("{id}/move")]
|
[HttpPost("{id}/move")]
|
||||||
public async Task PostMove(string id, [FromBody]CipherMoveRequestModel model)
|
public async Task PostMove(string id, [FromBody]CipherMoveRequestModel model)
|
||||||
|
@ -31,16 +31,16 @@ namespace Bit.Api.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("{id}")]
|
[HttpGet("{id}")]
|
||||||
public async Task<LoginResponseModel> Get(string id)
|
public async Task<LoginDetailsResponseModel> Get(string id)
|
||||||
{
|
{
|
||||||
var userId = _userService.GetProperUserId(User).Value;
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
var login = await _cipherRepository.GetByIdAsync(new Guid(id), userId);
|
var login = await _cipherRepository.GetFullDetailsByIdAsync(new Guid(id), userId);
|
||||||
if(login == null || login.Type != Core.Enums.CipherType.Login)
|
if(login == null || login.Type != Core.Enums.CipherType.Login)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
var response = new LoginResponseModel(login);
|
var response = new LoginDetailsResponseModel(login);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,7 +70,7 @@ namespace Bit.Api.Controllers
|
|||||||
public async Task<LoginResponseModel> Put(string id, [FromBody]LoginRequestModel model)
|
public async Task<LoginResponseModel> Put(string id, [FromBody]LoginRequestModel model)
|
||||||
{
|
{
|
||||||
var userId = _userService.GetProperUserId(User).Value;
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
var login = await _cipherRepository.GetByIdAsync(new Guid(id), _userService.GetProperUserId(User).Value);
|
var login = await _cipherRepository.GetByIdAsync(new Guid(id), userId);
|
||||||
if(login == null || login.Type != Core.Enums.CipherType.Login)
|
if(login == null || login.Type != Core.Enums.CipherType.Login)
|
||||||
{
|
{
|
||||||
throw new NotFoundException();
|
throw new NotFoundException();
|
||||||
|
11
src/Core/Models/Api/Request/CipherPartialRequestModel.cs
Normal file
11
src/Core/Models/Api/Request/CipherPartialRequestModel.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Bit.Core.Models.Api
|
||||||
|
{
|
||||||
|
public class CipherPartialRequestModel
|
||||||
|
{
|
||||||
|
[StringLength(36)]
|
||||||
|
public string FolderId { get; set; }
|
||||||
|
public bool Favorite { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -43,4 +43,15 @@ namespace Bit.Core.Models.Api
|
|||||||
public string Notes { get; set; }
|
public string Notes { get; set; }
|
||||||
public DateTime RevisionDate { get; set; }
|
public DateTime RevisionDate { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class LoginDetailsResponseModel : LoginResponseModel
|
||||||
|
{
|
||||||
|
public LoginDetailsResponseModel(CipherFullDetails cipher)
|
||||||
|
: base(cipher, "loginDetails")
|
||||||
|
{
|
||||||
|
Edit = cipher.Edit;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Edit { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
7
src/Core/Models/Data/CipherFullDetails.cs
Normal file
7
src/Core/Models/Data/CipherFullDetails.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace Core.Models.Data
|
||||||
|
{
|
||||||
|
public class CipherFullDetails : CipherDetails
|
||||||
|
{
|
||||||
|
public bool Edit { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,7 @@ namespace Bit.Core.Repositories
|
|||||||
public interface ICipherRepository : IRepository<Cipher, Guid>
|
public interface ICipherRepository : IRepository<Cipher, Guid>
|
||||||
{
|
{
|
||||||
Task<CipherDetails> GetByIdAsync(Guid id, Guid userId);
|
Task<CipherDetails> GetByIdAsync(Guid id, Guid userId);
|
||||||
|
Task<CipherFullDetails> GetFullDetailsByIdAsync(Guid id, Guid userId);
|
||||||
Task<ICollection<CipherDetails>> GetManyByUserIdAsync(Guid userId);
|
Task<ICollection<CipherDetails>> GetManyByUserIdAsync(Guid userId);
|
||||||
Task<ICollection<CipherDetails>> GetManyByUserIdHasSubvaultsAsync(Guid userId);
|
Task<ICollection<CipherDetails>> GetManyByUserIdHasSubvaultsAsync(Guid userId);
|
||||||
Task<ICollection<CipherDetails>> GetManyByTypeAndUserIdAsync(Enums.CipherType type, Guid userId);
|
Task<ICollection<CipherDetails>> GetManyByTypeAndUserIdAsync(Enums.CipherType type, Guid userId);
|
||||||
@ -18,6 +19,7 @@ namespace Bit.Core.Repositories
|
|||||||
Task ReplaceAsync(CipherDetails cipher);
|
Task ReplaceAsync(CipherDetails cipher);
|
||||||
Task UpsertAsync(CipherDetails cipher);
|
Task UpsertAsync(CipherDetails cipher);
|
||||||
Task ReplaceAsync(Cipher obj, IEnumerable<Guid> subvaultIds);
|
Task ReplaceAsync(Cipher obj, IEnumerable<Guid> subvaultIds);
|
||||||
|
Task UpdatePartialAsync(Guid id, Guid userId, Guid? folderId, bool favorite);
|
||||||
Task UpdateUserEmailPasswordAndCiphersAsync(User user, IEnumerable<Cipher> ciphers);
|
Task UpdateUserEmailPasswordAndCiphersAsync(User user, IEnumerable<Cipher> ciphers);
|
||||||
Task CreateAsync(IEnumerable<Cipher> ciphers);
|
Task CreateAsync(IEnumerable<Cipher> ciphers);
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,6 @@ namespace Bit.Core.Repositories
|
|||||||
Task<ICollection<SubvaultUserDetails>> GetManyDetailsByUserIdAsync(Guid userId);
|
Task<ICollection<SubvaultUserDetails>> GetManyDetailsByUserIdAsync(Guid userId);
|
||||||
Task<ICollection<SubvaultUserPermissions>> GetPermissionsByUserIdAsync(Guid userId, IEnumerable<Guid> subvaultIds,
|
Task<ICollection<SubvaultUserPermissions>> GetPermissionsByUserIdAsync(Guid userId, IEnumerable<Guid> subvaultIds,
|
||||||
Guid organizationId);
|
Guid organizationId);
|
||||||
Task<bool> GetIsAdminByUserIdCipherIdAsync(Guid userId, Guid cipherId);
|
Task<bool> GetCanEditByUserIdCipherIdAsync(Guid userId, Guid cipherId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,19 @@ namespace Bit.Core.Repositories.SqlServer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<CipherFullDetails> GetFullDetailsByIdAsync(Guid id, Guid userId)
|
||||||
|
{
|
||||||
|
using(var connection = new SqlConnection(ConnectionString))
|
||||||
|
{
|
||||||
|
var results = await connection.QueryAsync<CipherFullDetails>(
|
||||||
|
$"[{Schema}].[CipherFullDetails_ReadByIdUserId]",
|
||||||
|
new { Id = id, UserId = userId },
|
||||||
|
commandType: CommandType.StoredProcedure);
|
||||||
|
|
||||||
|
return results.FirstOrDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<ICollection<CipherDetails>> GetManyByUserIdAsync(Guid userId)
|
public async Task<ICollection<CipherDetails>> GetManyByUserIdAsync(Guid userId)
|
||||||
{
|
{
|
||||||
using(var connection = new SqlConnection(ConnectionString))
|
using(var connection = new SqlConnection(ConnectionString))
|
||||||
@ -149,6 +162,17 @@ namespace Bit.Core.Repositories.SqlServer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task UpdatePartialAsync(Guid id, Guid userId, Guid? folderId, bool favorite)
|
||||||
|
{
|
||||||
|
using(var connection = new SqlConnection(ConnectionString))
|
||||||
|
{
|
||||||
|
var results = await connection.ExecuteAsync(
|
||||||
|
$"[{Schema}].[Cipher_UpdatePartial]",
|
||||||
|
new { Id = id, UserId = userId, FolderId = folderId, Favorite = favorite },
|
||||||
|
commandType: CommandType.StoredProcedure);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Task UpdateUserEmailPasswordAndCiphersAsync(User user, IEnumerable<Cipher> ciphers)
|
public Task UpdateUserEmailPasswordAndCiphersAsync(User user, IEnumerable<Cipher> ciphers)
|
||||||
{
|
{
|
||||||
if(ciphers.Count() == 0)
|
if(ciphers.Count() == 0)
|
||||||
|
@ -61,12 +61,12 @@ namespace Bit.Core.Repositories.SqlServer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> GetIsAdminByUserIdCipherIdAsync(Guid userId, Guid cipherId)
|
public async Task<bool> GetCanEditByUserIdCipherIdAsync(Guid userId, Guid cipherId)
|
||||||
{
|
{
|
||||||
using(var connection = new SqlConnection(ConnectionString))
|
using(var connection = new SqlConnection(ConnectionString))
|
||||||
{
|
{
|
||||||
var result = await connection.QueryFirstOrDefaultAsync<bool>(
|
var result = await connection.QueryFirstOrDefaultAsync<bool>(
|
||||||
$"[{Schema}].[SubvaultUser_ReadIsAdminByCipherIdUserId]",
|
$"[{Schema}].[SubvaultUser_ReadCanEditByCipherIdUserId]",
|
||||||
new { UserId = userId, CipherId = cipherId },
|
new { UserId = userId, CipherId = cipherId },
|
||||||
commandType: CommandType.StoredProcedure);
|
commandType: CommandType.StoredProcedure);
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ namespace Bit.Core.Services
|
|||||||
public interface ICipherService
|
public interface ICipherService
|
||||||
{
|
{
|
||||||
Task SaveAsync(CipherDetails cipher, Guid savingUserId);
|
Task SaveAsync(CipherDetails cipher, Guid savingUserId);
|
||||||
|
Task UpdatePartialAsync(Guid cipherId, Guid savingUserId, Guid? folderId, bool favorite);
|
||||||
Task DeleteAsync(CipherDetails cipher, Guid deletingUserId);
|
Task DeleteAsync(CipherDetails cipher, Guid deletingUserId);
|
||||||
Task SaveFolderAsync(Folder folder);
|
Task SaveFolderAsync(Folder folder);
|
||||||
Task DeleteFolderAsync(Folder folder);
|
Task DeleteFolderAsync(Folder folder);
|
||||||
|
@ -39,7 +39,7 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
public async Task SaveAsync(CipherDetails cipher, Guid savingUserId)
|
public async Task SaveAsync(CipherDetails cipher, Guid savingUserId)
|
||||||
{
|
{
|
||||||
if(!(await UserHasAdminRights(cipher, savingUserId)))
|
if(!(await UserCanEditAsync(cipher, savingUserId)))
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Not an admin.");
|
throw new BadRequestException("Not an admin.");
|
||||||
}
|
}
|
||||||
@ -62,9 +62,19 @@ namespace Bit.Core.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task UpdatePartialAsync(Guid cipherId, Guid savingUserId, Guid? folderId, bool favorite)
|
||||||
|
{
|
||||||
|
if(!(await UserCanPartialEditAsync(cipherId, savingUserId)))
|
||||||
|
{
|
||||||
|
throw new BadRequestException("Cannot edit.");
|
||||||
|
}
|
||||||
|
|
||||||
|
await _cipherRepository.UpdatePartialAsync(cipherId, savingUserId, folderId, favorite);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task DeleteAsync(CipherDetails cipher, Guid deletingUserId)
|
public async Task DeleteAsync(CipherDetails cipher, Guid deletingUserId)
|
||||||
{
|
{
|
||||||
if(!(await UserHasAdminRights(cipher, deletingUserId)))
|
if(!(await UserCanEditAsync(cipher, deletingUserId)))
|
||||||
{
|
{
|
||||||
throw new BadRequestException("Not an admin.");
|
throw new BadRequestException("Not an admin.");
|
||||||
}
|
}
|
||||||
@ -163,14 +173,22 @@ namespace Bit.Core.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> UserHasAdminRights(CipherDetails cipher, Guid userId)
|
private async Task<bool> UserCanEditAsync(CipherDetails cipher, Guid userId)
|
||||||
{
|
{
|
||||||
if(!cipher.OrganizationId.HasValue && cipher.UserId.HasValue && cipher.UserId.Value == userId)
|
if(!cipher.OrganizationId.HasValue && cipher.UserId.HasValue && cipher.UserId.Value == userId)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return await _subvaultUserRepository.GetIsAdminByUserIdCipherIdAsync(userId, cipher.Id);
|
return await _subvaultUserRepository.GetCanEditByUserIdCipherIdAsync(userId, cipher.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task<bool> UserCanPartialEditAsync(Guid cipherId, Guid userId)
|
||||||
|
{
|
||||||
|
// TODO: implement
|
||||||
|
|
||||||
|
return Task.FromResult(true);
|
||||||
|
//return await _subvaultUserRepository.GetCanEditByUserIdCipherIdAsync(userId, cipherId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -173,6 +173,9 @@
|
|||||||
<Build Include="dbo\Stored Procedures\OrganizationUserUserDetails_ReadByOrganizationId.sql" />
|
<Build Include="dbo\Stored Procedures\OrganizationUserUserDetails_ReadByOrganizationId.sql" />
|
||||||
<Build Include="dbo\Stored Procedures\Subvault_ReadByOrganizationIdAdminUserId.sql" />
|
<Build Include="dbo\Stored Procedures\Subvault_ReadByOrganizationIdAdminUserId.sql" />
|
||||||
<Build Include="dbo\User Defined Types\GuidIdArray.sql" />
|
<Build Include="dbo\User Defined Types\GuidIdArray.sql" />
|
||||||
<Build Include="dbo\Stored Procedures\SubvaultUser_ReadIsAdminByCipherIdUserId.sql" />
|
<Build Include="dbo\Stored Procedures\SubvaultUser_ReadCanEditByCipherIdUserId.sql" />
|
||||||
|
<Build Include="dbo\Stored Procedures\CipherFullDetails_ReadByIdUserId.sql" />
|
||||||
|
<Build Include="dbo\Functions\UserCanEditCipher.sql" />
|
||||||
|
<Build Include="dbo\Stored Procedures\Cipher_UpdatePartial.sql" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
@ -1,13 +1,15 @@
|
|||||||
CREATE PROCEDURE [dbo].[SubvaultUser_ReadIsAdminByCipherIdUserId]
|
CREATE FUNCTION [dbo].[UserCanEditCipher](@UserId UNIQUEIDENTIFIER, @CipherId UNIQUEIDENTIFIER)
|
||||||
@UserId UNIQUEIDENTIFIER,
|
RETURNS BIT AS
|
||||||
@CipherId AS UNIQUEIDENTIFIER
|
|
||||||
AS
|
|
||||||
BEGIN
|
BEGIN
|
||||||
SET NOCOUNT ON
|
DECLARE @CanEdit BIT
|
||||||
|
|
||||||
;WITH [CTE] AS(
|
;WITH [CTE] AS(
|
||||||
SELECT
|
SELECT
|
||||||
CASE WHEN OU.[Type] = 2 THEN SU.[Admin] ELSE 1 END AS [Admin] -- 2 = Regular User
|
CASE
|
||||||
|
WHEN OU.[Type] = 2 AND SU.[Admin] = 1 THEN 1 -- 2 = Regular User
|
||||||
|
WHEN SU.[ReadOnly] = 0 THEN 1
|
||||||
|
ELSE 0
|
||||||
|
END [CanEdit]
|
||||||
FROM
|
FROM
|
||||||
[dbo].[SubvaultUser] SU
|
[dbo].[SubvaultUser] SU
|
||||||
INNER JOIN
|
INNER JOIN
|
||||||
@ -22,9 +24,11 @@ BEGIN
|
|||||||
AND OU.[Status] = 2 -- 2 = Confirmed
|
AND OU.[Status] = 2 -- 2 = Confirmed
|
||||||
)
|
)
|
||||||
SELECT
|
SELECT
|
||||||
CASE WHEN COUNT(1) > 0 THEN 1 ELSE 0 END
|
@CanEdit = CASE WHEN COUNT(1) > 0 THEN 1 ELSE 0 END
|
||||||
FROM
|
FROM
|
||||||
[CTE]
|
[CTE]
|
||||||
WHERE
|
WHERE
|
||||||
[Admin] = 1
|
[CanEdit] = 1
|
||||||
END
|
|
||||||
|
RETURN @CanEdit
|
||||||
|
END
|
@ -24,21 +24,5 @@ BEGIN
|
|||||||
WHERE
|
WHERE
|
||||||
[Id] = @Id
|
[Id] = @Id
|
||||||
|
|
||||||
IF @FolderId IS NULL
|
EXEC [dbo].[Cipher_UpdatePartial] @Id, @UserId, @FolderId, @Favorite
|
||||||
BEGIN
|
|
||||||
EXEC [dbo].[FolderCipher_DeleteByUserId] @UserId, @Id
|
|
||||||
END
|
|
||||||
ELSE IF (SELECT COUNT(1) FROM [dbo].[FolderCipher] WHERE [FolderId] = @FolderId AND [CipherId] = @Id) = 0
|
|
||||||
BEGIN
|
|
||||||
EXEC [dbo].[FolderCipher_Create] @FolderId, @Id
|
|
||||||
END
|
|
||||||
|
|
||||||
IF @Favorite = 0
|
|
||||||
BEGIN
|
|
||||||
EXEC [dbo].[Favorite_Delete] @UserId, @Id
|
|
||||||
END
|
|
||||||
ELSE IF (SELECT COUNT(1) FROM [dbo].[Favorite] WHERE [UserId] = @UserId AND [CipherId] = @Id) = 0
|
|
||||||
BEGIN
|
|
||||||
EXEC [dbo].[Favorite_Create] @UserId, @Id
|
|
||||||
END
|
|
||||||
END
|
END
|
@ -0,0 +1,28 @@
|
|||||||
|
CREATE PROCEDURE [dbo].[CipherFullDetails_ReadByIdUserId]
|
||||||
|
@Id UNIQUEIDENTIFIER,
|
||||||
|
@UserId UNIQUEIDENTIFIER
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON
|
||||||
|
|
||||||
|
SELECT DISTINCT
|
||||||
|
C.*,
|
||||||
|
CASE
|
||||||
|
WHEN C.[OrganizationId] IS NULL THEN 1
|
||||||
|
ELSE [dbo].[UserCanEditCipher](@UserId, @Id)
|
||||||
|
END [Edit]
|
||||||
|
FROM
|
||||||
|
[dbo].[CipherDetails](@UserId) C
|
||||||
|
LEFT JOIN
|
||||||
|
[dbo].[SubvaultCipher] SC ON SC.[CipherId] = C.[Id]
|
||||||
|
LEFT JOIN
|
||||||
|
[dbo].[SubvaultUser] SU ON SU.[SubvaultId] = SC.[SubvaultId]
|
||||||
|
LEFT JOIN
|
||||||
|
[dbo].[OrganizationUser] OU ON OU.[Id] = SU.[OrganizationUserId]
|
||||||
|
WHERE
|
||||||
|
C.Id = @Id
|
||||||
|
AND (
|
||||||
|
(C.[UserId] IS NOT NULL AND C.[UserId] = @UserId)
|
||||||
|
OR (OU.[UserId] = @UserId AND OU.[Status] = 2) -- 2 = Confirmed
|
||||||
|
)
|
||||||
|
END
|
27
src/Sql/dbo/Stored Procedures/Cipher_UpdatePartial.sql
Normal file
27
src/Sql/dbo/Stored Procedures/Cipher_UpdatePartial.sql
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
CREATE PROCEDURE [dbo].[Cipher_UpdatePartial]
|
||||||
|
@Id UNIQUEIDENTIFIER,
|
||||||
|
@UserId UNIQUEIDENTIFIER,
|
||||||
|
@FolderId UNIQUEIDENTIFIER,
|
||||||
|
@Favorite TINYINT
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON
|
||||||
|
|
||||||
|
IF @FolderId IS NULL
|
||||||
|
BEGIN
|
||||||
|
EXEC [dbo].[FolderCipher_DeleteByUserId] @UserId, @Id
|
||||||
|
END
|
||||||
|
ELSE IF (SELECT COUNT(1) FROM [dbo].[FolderCipher] WHERE [FolderId] = @FolderId AND [CipherId] = @Id) = 0
|
||||||
|
BEGIN
|
||||||
|
EXEC [dbo].[FolderCipher_Create] @FolderId, @Id
|
||||||
|
END
|
||||||
|
|
||||||
|
IF @Favorite = 0
|
||||||
|
BEGIN
|
||||||
|
EXEC [dbo].[Favorite_Delete] @UserId, @Id
|
||||||
|
END
|
||||||
|
ELSE IF (SELECT COUNT(1) FROM [dbo].[Favorite] WHERE [UserId] = @UserId AND [CipherId] = @Id) = 0
|
||||||
|
BEGIN
|
||||||
|
EXEC [dbo].[Favorite_Create] @UserId, @Id
|
||||||
|
END
|
||||||
|
END
|
@ -0,0 +1,10 @@
|
|||||||
|
CREATE PROCEDURE [dbo].[SubvaultUser_ReadCanEditByCipherIdUserId]
|
||||||
|
@UserId UNIQUEIDENTIFIER,
|
||||||
|
@CipherId AS UNIQUEIDENTIFIER
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
[dbo].[UserCanEditCipher](@UserId, @CipherId)
|
||||||
|
END
|
Loading…
x
Reference in New Issue
Block a user