mirror of
https://github.com/bitwarden/server.git
synced 2025-04-22 21:45:15 -05:00
Move into and read ciphers from org subvaults
This commit is contained in:
parent
4779794599
commit
ed8d5d69a4
@ -88,6 +88,21 @@ namespace Bit.Api.Controllers
|
|||||||
// await _cipherService.SaveAsync(cipher);
|
// await _cipherService.SaveAsync(cipher);
|
||||||
//}
|
//}
|
||||||
|
|
||||||
|
[HttpPut("{id}/move")]
|
||||||
|
[HttpPost("{id}/move")]
|
||||||
|
public async Task PostMoveSubvault(string id, [FromBody]CipherMoveRequestModel model)
|
||||||
|
{
|
||||||
|
var userId = _userService.GetProperUserId(User).Value;
|
||||||
|
var cipher = await _cipherRepository.GetByIdAsync(new Guid(id));
|
||||||
|
if(cipher == null)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
await _cipherService.MoveSubvaultAsync(model.Cipher.ToCipher(cipher),
|
||||||
|
model.SubvaultIds.Select(s => new Guid(s)), userId);
|
||||||
|
}
|
||||||
|
|
||||||
[HttpDelete("{id}")]
|
[HttpDelete("{id}")]
|
||||||
[HttpPost("{id}/delete")]
|
[HttpPost("{id}/delete")]
|
||||||
public async Task Delete(string id)
|
public async Task Delete(string id)
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Abstractions" Version="1.1.2" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.Abstractions" Version="1.1.2" />
|
||||||
<PackageReference Include="Dapper" Version="1.50.2" />
|
<PackageReference Include="Dapper" Version="1.50.2" />
|
||||||
<PackageReference Include="DataTableProxy" Version="1.2.0" />
|
<PackageReference Include="DataTableProxy" Version="1.2.0" />
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="10.0.1" />
|
||||||
<PackageReference Include="Sendgrid" Version="9.0.12" />
|
<PackageReference Include="Sendgrid" Version="9.0.12" />
|
||||||
<PackageReference Include="PushSharp" Version="4.0.10" />
|
<PackageReference Include="PushSharp" Version="4.0.10" />
|
||||||
<PackageReference Include="WindowsAzure.Storage" Version="8.1.1" />
|
<PackageReference Include="WindowsAzure.Storage" Version="8.1.1" />
|
||||||
|
@ -4,6 +4,7 @@ using Bit.Core.Utilities;
|
|||||||
using Bit.Core.Models.Table;
|
using Bit.Core.Models.Table;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace Bit.Core.Models.Api
|
namespace Bit.Core.Models.Api
|
||||||
{
|
{
|
||||||
@ -15,6 +16,8 @@ namespace Bit.Core.Models.Api
|
|||||||
[StringLength(36)]
|
[StringLength(36)]
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
[StringLength(36)]
|
[StringLength(36)]
|
||||||
|
public string OrganizationId { get; set; }
|
||||||
|
[StringLength(36)]
|
||||||
public string FolderId { get; set; }
|
public string FolderId { get; set; }
|
||||||
[Required]
|
[Required]
|
||||||
[EncryptedString]
|
[EncryptedString]
|
||||||
@ -35,24 +38,35 @@ namespace Bit.Core.Models.Api
|
|||||||
|
|
||||||
public virtual Cipher ToCipher(Guid userId)
|
public virtual Cipher ToCipher(Guid userId)
|
||||||
{
|
{
|
||||||
var cipher = new Cipher
|
return ToCipher(new Cipher
|
||||||
{
|
{
|
||||||
Id = new Guid(Id),
|
Id = new Guid(Id),
|
||||||
UserId = userId,
|
UserId = string.IsNullOrWhiteSpace(OrganizationId) ? (Guid?)userId : null,
|
||||||
//FolderId = string.IsNullOrWhiteSpace(FolderId) ? null : (Guid?)new Guid(FolderId),
|
|
||||||
Type = Type
|
Type = Type
|
||||||
};
|
});
|
||||||
|
}
|
||||||
|
|
||||||
switch(Type)
|
public Cipher ToCipher(Cipher existingCipher)
|
||||||
|
{
|
||||||
|
existingCipher.OrganizationId = string.IsNullOrWhiteSpace(OrganizationId) ? null : (Guid?)new Guid(OrganizationId);
|
||||||
|
|
||||||
|
switch(existingCipher.Type)
|
||||||
{
|
{
|
||||||
case CipherType.Login:
|
case CipherType.Login:
|
||||||
cipher.Data = JsonConvert.SerializeObject(new LoginDataModel(this), new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
|
existingCipher.Data = JsonConvert.SerializeObject(new LoginDataModel(this), new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new ArgumentException("Unsupported " + nameof(Type) + ".");
|
throw new ArgumentException("Unsupported " + nameof(Type) + ".");
|
||||||
}
|
}
|
||||||
|
|
||||||
return cipher;
|
return existingCipher;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class CipherMoveRequestModel
|
||||||
|
{
|
||||||
|
public IEnumerable<string> SubvaultIds { get; set; }
|
||||||
|
[Required]
|
||||||
|
public CipherRequestModel Cipher { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
using Core.Models.Data;
|
using Core.Models.Data;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Bit.Core.Models.Table;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
namespace Bit.Core.Models.Api
|
namespace Bit.Core.Models.Api
|
||||||
{
|
{
|
||||||
@ -16,6 +19,7 @@ namespace Bit.Core.Models.Api
|
|||||||
Id = cipher.Id.ToString();
|
Id = cipher.Id.ToString();
|
||||||
Type = cipher.Type;
|
Type = cipher.Type;
|
||||||
RevisionDate = cipher.RevisionDate;
|
RevisionDate = cipher.RevisionDate;
|
||||||
|
OrganizationId = cipher.OrganizationId?.ToString();
|
||||||
FolderId = cipher.FolderId?.ToString();
|
FolderId = cipher.FolderId?.ToString();
|
||||||
Favorite = cipher.Favorite;
|
Favorite = cipher.Favorite;
|
||||||
|
|
||||||
@ -30,10 +34,22 @@ namespace Bit.Core.Models.Api
|
|||||||
}
|
}
|
||||||
|
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
|
public string OrganizationId { get; set; }
|
||||||
public string FolderId { get; set; }
|
public string FolderId { get; set; }
|
||||||
public Enums.CipherType Type { get; set; }
|
public Enums.CipherType Type { get; set; }
|
||||||
public bool Favorite { get; set; }
|
public bool Favorite { get; set; }
|
||||||
public dynamic Data { get; set; }
|
public dynamic Data { get; set; }
|
||||||
public DateTime RevisionDate { get; set; }
|
public DateTime RevisionDate { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class CipherDetailsResponseModel : CipherResponseModel
|
||||||
|
{
|
||||||
|
public CipherDetailsResponseModel(CipherDetails cipher, IEnumerable<SubvaultCipher> subvaultCipher)
|
||||||
|
: base(cipher, "cipherDetails")
|
||||||
|
{
|
||||||
|
SubvaultIds = subvaultCipher.Select(s => s.SubvaultId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<Guid> SubvaultIds { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ namespace Bit.Core.Models.Api
|
|||||||
var data = new LoginDataModel(cipher);
|
var data = new LoginDataModel(cipher);
|
||||||
|
|
||||||
Id = cipher.Id.ToString();
|
Id = cipher.Id.ToString();
|
||||||
|
OrganizationId = cipher.OrganizationId?.ToString();
|
||||||
FolderId = cipher.FolderId?.ToString();
|
FolderId = cipher.FolderId?.ToString();
|
||||||
Favorite = cipher.Favorite;
|
Favorite = cipher.Favorite;
|
||||||
Name = data.Name;
|
Name = data.Name;
|
||||||
@ -32,6 +33,7 @@ namespace Bit.Core.Models.Api
|
|||||||
}
|
}
|
||||||
|
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
|
public string OrganizationId { get; set; }
|
||||||
public string FolderId { get; set; }
|
public string FolderId { get; set; }
|
||||||
public bool Favorite { get; set; }
|
public bool Favorite { get; set; }
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
11
src/Core/Models/Data/SubvaultUserPermissions.cs
Normal file
11
src/Core/Models/Data/SubvaultUserPermissions.cs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Bit.Core.Models.Data
|
||||||
|
{
|
||||||
|
public class SubvaultUserPermissions
|
||||||
|
{
|
||||||
|
public Guid SubvaultId { get; set; }
|
||||||
|
public bool ReadOnly { get; set; }
|
||||||
|
public bool Admin { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -6,7 +6,7 @@ namespace Bit.Core.Models.Table
|
|||||||
public class Cipher : IDataObject<Guid>
|
public class Cipher : IDataObject<Guid>
|
||||||
{
|
{
|
||||||
public Guid Id { get; set; }
|
public Guid Id { get; set; }
|
||||||
public Guid UserId { get; set; }
|
public Guid? UserId { get; set; }
|
||||||
public Guid? OrganizationId { get; set; }
|
public Guid? OrganizationId { get; set; }
|
||||||
public Enums.CipherType Type { get; set; }
|
public Enums.CipherType Type { get; set; }
|
||||||
public string Data { get; set; }
|
public string Data { get; set; }
|
||||||
|
10
src/Core/Models/Table/SubvaultCipher.cs
Normal file
10
src/Core/Models/Table/SubvaultCipher.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Bit.Core.Models.Table
|
||||||
|
{
|
||||||
|
public class SubvaultCipher
|
||||||
|
{
|
||||||
|
public Guid SubvaultId { get; set; }
|
||||||
|
public Guid CipherId { get; set; }
|
||||||
|
}
|
||||||
|
}
|
@ -16,6 +16,7 @@ namespace Bit.Core.Repositories
|
|||||||
Task CreateAsync(CipherDetails cipher);
|
Task CreateAsync(CipherDetails cipher);
|
||||||
Task ReplaceAsync(CipherDetails cipher);
|
Task ReplaceAsync(CipherDetails cipher);
|
||||||
Task UpsertAsync(CipherDetails cipher);
|
Task UpsertAsync(CipherDetails cipher);
|
||||||
|
Task ReplaceAsync(Cipher obj, IEnumerable<Guid> subvaultIds);
|
||||||
Task UpdateUserEmailPasswordAndCiphersAsync(User user, IEnumerable<Cipher> ciphers);
|
Task UpdateUserEmailPasswordAndCiphersAsync(User user, IEnumerable<Cipher> ciphers);
|
||||||
Task CreateAsync(IEnumerable<Cipher> ciphers);
|
Task CreateAsync(IEnumerable<Cipher> ciphers);
|
||||||
}
|
}
|
||||||
|
@ -2,11 +2,15 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.Core.Models.Table;
|
using Bit.Core.Models.Table;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Bit.Core.Models.Data;
|
||||||
|
|
||||||
namespace Bit.Core.Repositories
|
namespace Bit.Core.Repositories
|
||||||
{
|
{
|
||||||
public interface ISubvaultUserRepository : IRepository<SubvaultUser, Guid>
|
public interface ISubvaultUserRepository : IRepository<SubvaultUser, Guid>
|
||||||
{
|
{
|
||||||
Task<ICollection<SubvaultUser>> GetManyByOrganizationUserIdAsync(Guid orgUserId);
|
Task<ICollection<SubvaultUser>> GetManyByOrganizationUserIdAsync(Guid orgUserId);
|
||||||
|
Task<ICollection<SubvaultUserDetails>> GetManyDetailsByUserIdAsync(Guid userId);
|
||||||
|
Task<ICollection<SubvaultUserPermissions>> GetPermissionsByUserIdAsync(Guid userId, IEnumerable<Guid> subvaultIds,
|
||||||
|
Guid organizationId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,8 @@ using Bit.Core.Models.Table;
|
|||||||
using System.Data;
|
using System.Data;
|
||||||
using Dapper;
|
using Dapper;
|
||||||
using Core.Models.Data;
|
using Core.Models.Data;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace Bit.Core.Repositories.SqlServer
|
namespace Bit.Core.Repositories.SqlServer
|
||||||
{
|
{
|
||||||
@ -120,6 +122,20 @@ namespace Bit.Core.Repositories.SqlServer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task ReplaceAsync(Cipher obj, IEnumerable<Guid> subvaultIds)
|
||||||
|
{
|
||||||
|
var objWithSubvaults = JsonConvert.DeserializeObject<CipherWithSubvaults>(JsonConvert.SerializeObject(obj));
|
||||||
|
objWithSubvaults.SubvaultIds = subvaultIds.ToGuidIdArrayTVP();
|
||||||
|
|
||||||
|
using(var connection = new SqlConnection(ConnectionString))
|
||||||
|
{
|
||||||
|
var results = await connection.ExecuteAsync(
|
||||||
|
$"[{Schema}].[Cipher_UpdateWithSubvaults]",
|
||||||
|
objWithSubvaults,
|
||||||
|
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)
|
||||||
@ -252,5 +268,10 @@ namespace Bit.Core.Repositories.SqlServer
|
|||||||
|
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class CipherWithSubvaults : Cipher
|
||||||
|
{
|
||||||
|
public DataTable SubvaultIds { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@ using System.Data;
|
|||||||
using System.Data.SqlClient;
|
using System.Data.SqlClient;
|
||||||
using Dapper;
|
using Dapper;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Bit.Core.Models.Data;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
namespace Bit.Core.Repositories.SqlServer
|
namespace Bit.Core.Repositories.SqlServer
|
||||||
{
|
{
|
||||||
@ -31,5 +33,32 @@ namespace Bit.Core.Repositories.SqlServer
|
|||||||
return results.ToList();
|
return results.ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<ICollection<SubvaultUserDetails>> GetManyDetailsByUserIdAsync(Guid userId)
|
||||||
|
{
|
||||||
|
using(var connection = new SqlConnection(ConnectionString))
|
||||||
|
{
|
||||||
|
var results = await connection.QueryAsync<SubvaultUserDetails>(
|
||||||
|
$"[{Schema}].[SubvaultUserDetails_ReadByUserId]",
|
||||||
|
new { UserId = userId },
|
||||||
|
commandType: CommandType.StoredProcedure);
|
||||||
|
|
||||||
|
return results.ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<ICollection<SubvaultUserPermissions>> GetPermissionsByUserIdAsync(Guid userId,
|
||||||
|
IEnumerable<Guid> subvaultIds, Guid organizationId)
|
||||||
|
{
|
||||||
|
using(var connection = new SqlConnection(ConnectionString))
|
||||||
|
{
|
||||||
|
var results = await connection.QueryAsync<SubvaultUserPermissions>(
|
||||||
|
$"[{Schema}].[SubvaultUser_ReadPermissionsBySubvaultUserId]",
|
||||||
|
new { UserId = userId, SubvaultIds = subvaultIds.ToGuidIdArrayTVP(), OrganizationId = organizationId },
|
||||||
|
commandType: CommandType.StoredProcedure);
|
||||||
|
|
||||||
|
return results.ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.Core.Models.Table;
|
using Bit.Core.Models.Table;
|
||||||
using Core.Models.Data;
|
using Core.Models.Data;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace Bit.Core.Services
|
namespace Bit.Core.Services
|
||||||
{
|
{
|
||||||
@ -11,6 +12,7 @@ namespace Bit.Core.Services
|
|||||||
Task DeleteAsync(Cipher cipher);
|
Task DeleteAsync(Cipher cipher);
|
||||||
Task SaveFolderAsync(Folder folder);
|
Task SaveFolderAsync(Folder folder);
|
||||||
Task DeleteFolderAsync(Folder folder);
|
Task DeleteFolderAsync(Folder folder);
|
||||||
|
Task MoveSubvaultAsync(Cipher cipher, IEnumerable<Guid> subvaultIds, Guid userId);
|
||||||
Task ImportCiphersAsync(List<Folder> folders, List<CipherDetails> ciphers,
|
Task ImportCiphersAsync(List<Folder> folders, List<CipherDetails> ciphers,
|
||||||
IEnumerable<KeyValuePair<int, int>> folderRelationships);
|
IEnumerable<KeyValuePair<int, int>> folderRelationships);
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ using System.Threading.Tasks;
|
|||||||
using Bit.Core.Models.Table;
|
using Bit.Core.Models.Table;
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
using Core.Models.Data;
|
using Core.Models.Data;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
|
|
||||||
namespace Bit.Core.Services
|
namespace Bit.Core.Services
|
||||||
{
|
{
|
||||||
@ -13,17 +14,26 @@ namespace Bit.Core.Services
|
|||||||
private readonly ICipherRepository _cipherRepository;
|
private readonly ICipherRepository _cipherRepository;
|
||||||
private readonly IFolderRepository _folderRepository;
|
private readonly IFolderRepository _folderRepository;
|
||||||
private readonly IUserRepository _userRepository;
|
private readonly IUserRepository _userRepository;
|
||||||
|
private readonly IOrganizationRepository _organizationRepository;
|
||||||
|
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||||
|
private readonly ISubvaultUserRepository _subvaultUserRepository;
|
||||||
private readonly IPushService _pushService;
|
private readonly IPushService _pushService;
|
||||||
|
|
||||||
public CipherService(
|
public CipherService(
|
||||||
ICipherRepository cipherRepository,
|
ICipherRepository cipherRepository,
|
||||||
IFolderRepository folderRepository,
|
IFolderRepository folderRepository,
|
||||||
IUserRepository userRepository,
|
IUserRepository userRepository,
|
||||||
|
IOrganizationRepository organizationRepository,
|
||||||
|
IOrganizationUserRepository organizationUserRepository,
|
||||||
|
ISubvaultUserRepository subvaultUserRepository,
|
||||||
IPushService pushService)
|
IPushService pushService)
|
||||||
{
|
{
|
||||||
_cipherRepository = cipherRepository;
|
_cipherRepository = cipherRepository;
|
||||||
_folderRepository = folderRepository;
|
_folderRepository = folderRepository;
|
||||||
_userRepository = userRepository;
|
_userRepository = userRepository;
|
||||||
|
_organizationRepository = organizationRepository;
|
||||||
|
_organizationUserRepository = organizationUserRepository;
|
||||||
|
_subvaultUserRepository = subvaultUserRepository;
|
||||||
_pushService = pushService;
|
_pushService = pushService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,6 +91,35 @@ namespace Bit.Core.Services
|
|||||||
//await _pushService.PushSyncCipherDeleteAsync(cipher);
|
//await _pushService.PushSyncCipherDeleteAsync(cipher);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task MoveSubvaultAsync(Cipher cipher, IEnumerable<Guid> subvaultIds, Guid userId)
|
||||||
|
{
|
||||||
|
if(cipher.Id == default(Guid))
|
||||||
|
{
|
||||||
|
throw new BadRequestException(nameof(cipher.Id));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!cipher.OrganizationId.HasValue)
|
||||||
|
{
|
||||||
|
throw new BadRequestException(nameof(cipher.OrganizationId));
|
||||||
|
}
|
||||||
|
|
||||||
|
var existingCipher = await _cipherRepository.GetByIdAsync(cipher.Id);
|
||||||
|
if(existingCipher == null || (existingCipher.UserId.HasValue && existingCipher.UserId != userId))
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
var subvaultUserDetails = await _subvaultUserRepository.GetPermissionsByUserIdAsync(userId, subvaultIds,
|
||||||
|
cipher.OrganizationId.Value);
|
||||||
|
|
||||||
|
cipher.UserId = null;
|
||||||
|
cipher.RevisionDate = DateTime.UtcNow;
|
||||||
|
await _cipherRepository.ReplaceAsync(cipher, subvaultUserDetails.Where(s => s.Admin).Select(s => s.SubvaultId));
|
||||||
|
|
||||||
|
// push
|
||||||
|
await _pushService.PushSyncCipherUpdateAsync(cipher);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task ImportCiphersAsync(
|
public async Task ImportCiphersAsync(
|
||||||
List<Folder> folders,
|
List<Folder> folders,
|
||||||
List<CipherDetails> ciphers,
|
List<CipherDetails> ciphers,
|
||||||
|
@ -71,7 +71,7 @@ namespace Bit.Core.Services
|
|||||||
{
|
{
|
||||||
Type = type,
|
Type = type,
|
||||||
Id = cipher.Id,
|
Id = cipher.Id,
|
||||||
UserId = cipher.UserId,
|
UserId = cipher.UserId.Value,
|
||||||
RevisionDate = cipher.RevisionDate,
|
RevisionDate = cipher.RevisionDate,
|
||||||
Aps = new PushNotification.AppleData { ContentAvailable = 1 }
|
Aps = new PushNotification.AppleData { ContentAvailable = 1 }
|
||||||
};
|
};
|
||||||
@ -82,7 +82,7 @@ namespace Bit.Core.Services
|
|||||||
excludedTokens.Add(_currentContext.DeviceIdentifier);
|
excludedTokens.Add(_currentContext.DeviceIdentifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
await PushToAllUserDevicesAsync(cipher.UserId, JObject.FromObject(message), excludedTokens);
|
await PushToAllUserDevicesAsync(cipher.UserId.Value, JObject.FromObject(message), excludedTokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task PushSyncCiphersAsync(Guid userId)
|
public async Task PushSyncCiphersAsync(Guid userId)
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
using System;
|
using Dapper;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
@ -40,6 +43,28 @@ namespace Bit.Core.Utilities
|
|||||||
return new Guid(guidArray);
|
return new Guid(guidArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static DataTable ToGuidIdArrayTVP(this IEnumerable<Guid> ids)
|
||||||
|
{
|
||||||
|
return ids.ToArrayTVP("GuidId");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DataTable ToArrayTVP<T>(this IEnumerable<T> values, string columnName)
|
||||||
|
{
|
||||||
|
var table = new DataTable();
|
||||||
|
table.Columns.Add(columnName, typeof(T));
|
||||||
|
table.SetTypeName($"[dbo].[{columnName}Array]");
|
||||||
|
|
||||||
|
if(values != null)
|
||||||
|
{
|
||||||
|
foreach(var value in values)
|
||||||
|
{
|
||||||
|
table.Rows.Add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
public static X509Certificate2 GetCertificate(string thumbprint)
|
public static X509Certificate2 GetCertificate(string thumbprint)
|
||||||
{
|
{
|
||||||
// Clean possible garbage characters from thumbprint copy/paste
|
// Clean possible garbage characters from thumbprint copy/paste
|
||||||
|
@ -63,6 +63,7 @@
|
|||||||
<Folder Include="dbo\Tables\" />
|
<Folder Include="dbo\Tables\" />
|
||||||
<Folder Include="dbo\Views\" />
|
<Folder Include="dbo\Views\" />
|
||||||
<Folder Include="dbo\Stored Procedures\" />
|
<Folder Include="dbo\Stored Procedures\" />
|
||||||
|
<Folder Include="dbo\UserDefinedTypes" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Build Include="dbo\Tables\SubvaultCipher.sql" />
|
<Build Include="dbo\Tables\SubvaultCipher.sql" />
|
||||||
@ -168,5 +169,8 @@
|
|||||||
<Build Include="dbo\Stored Procedures\CipherDetails_Update.sql" />
|
<Build Include="dbo\Stored Procedures\CipherDetails_Update.sql" />
|
||||||
<Build Include="dbo\Stored Procedures\CipherDetails_Create.sql" />
|
<Build Include="dbo\Stored Procedures\CipherDetails_Create.sql" />
|
||||||
<Build Include="dbo\Stored Procedures\FolderCipher_DeleteByUserId.sql" />
|
<Build Include="dbo\Stored Procedures\FolderCipher_DeleteByUserId.sql" />
|
||||||
|
<Build Include="dbo\Stored Procedures\SubvaultUser_ReadPermissionsBySubvaultUserId.sql" />
|
||||||
|
<Build Include="dbo\UserDefinedTypes\GuidIdArray.sql" />
|
||||||
|
<Build Include="dbo\Stored Procedures\Cipher_UpdateWithSubvaults.sql" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
@ -4,10 +4,17 @@ AS
|
|||||||
BEGIN
|
BEGIN
|
||||||
SET NOCOUNT ON
|
SET NOCOUNT ON
|
||||||
|
|
||||||
SELECT
|
SELECT DISTINCT
|
||||||
*
|
C.*
|
||||||
FROM
|
FROM
|
||||||
[dbo].[CipherDetailsView]
|
[dbo].[CipherDetailsView] 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
|
WHERE
|
||||||
[UserId] = @UserId
|
(C.[UserId] IS NOT NULL AND C.[UserId] = @UserId)
|
||||||
|
OR OU.[UserId] = @UserId
|
||||||
END
|
END
|
43
src/Sql/dbo/Stored Procedures/Cipher_UpdateWithSubvaults.sql
Normal file
43
src/Sql/dbo/Stored Procedures/Cipher_UpdateWithSubvaults.sql
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
CREATE PROCEDURE [dbo].[Cipher_UpdateWithSubvaults]
|
||||||
|
@Id UNIQUEIDENTIFIER,
|
||||||
|
@UserId UNIQUEIDENTIFIER,
|
||||||
|
@OrganizationId UNIQUEIDENTIFIER,
|
||||||
|
@Type TINYINT,
|
||||||
|
@Data NVARCHAR(MAX),
|
||||||
|
@CreationDate DATETIME2(7),
|
||||||
|
@RevisionDate DATETIME2(7),
|
||||||
|
@SubvaultIds AS [dbo].[GuidIdArray] READONLY
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON
|
||||||
|
|
||||||
|
UPDATE
|
||||||
|
[dbo].[Cipher]
|
||||||
|
SET
|
||||||
|
[UserId] = @UserId,
|
||||||
|
[OrganizationId] = @OrganizationId,
|
||||||
|
[Type] = @Type,
|
||||||
|
[Data] = @Data,
|
||||||
|
[CreationDate] = @CreationDate,
|
||||||
|
[RevisionDate] = @RevisionDate
|
||||||
|
WHERE
|
||||||
|
[Id] = @Id
|
||||||
|
|
||||||
|
MERGE
|
||||||
|
[dbo].[SubvaultCipher] AS [Target]
|
||||||
|
USING
|
||||||
|
@SubvaultIds AS [Source]
|
||||||
|
ON
|
||||||
|
[Target].[SubvaultId] = [Source].[Id]
|
||||||
|
AND [Target].[CipherId] = @Id
|
||||||
|
WHEN NOT MATCHED BY TARGET THEN
|
||||||
|
INSERT VALUES
|
||||||
|
(
|
||||||
|
[Source].[Id],
|
||||||
|
@Id
|
||||||
|
)
|
||||||
|
WHEN NOT MATCHED BY SOURCE
|
||||||
|
AND [Target].[CipherId] = @Id THEN
|
||||||
|
DELETE
|
||||||
|
;
|
||||||
|
END
|
@ -0,0 +1,22 @@
|
|||||||
|
CREATE PROCEDURE [dbo].[SubvaultUser_ReadPermissionsBySubvaultUserId]
|
||||||
|
@UserId UNIQUEIDENTIFIER,
|
||||||
|
@SubvaultIds AS [dbo].[GuidIdArray] READONLY,
|
||||||
|
@OrganizationId UNIQUEIDENTIFIER
|
||||||
|
AS
|
||||||
|
BEGIN
|
||||||
|
SET NOCOUNT ON
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
SU.[SubvaultId],
|
||||||
|
CASE WHEN OU.[Type] = 2 THEN SU.[Admin] ELSE 1 END AS [Admin], -- 2 = Regular User
|
||||||
|
CASE WHEN OU.[Type] = 2 THEN SU.[ReadOnly] ELSE 0 END AS [ReadOnly] -- 2 = Regular User
|
||||||
|
FROM
|
||||||
|
[dbo].[SubvaultUser] SU
|
||||||
|
INNER JOIN
|
||||||
|
[dbo].[OrganizationUser] OU ON OU.Id = SU.OrganizationUserId
|
||||||
|
WHERE
|
||||||
|
OU.[UserId] = @UserId
|
||||||
|
AND OU.[OrganizationId] = @OrganizationId
|
||||||
|
AND OU.[Status] = 2 -- 2 = Confirmed
|
||||||
|
AND SU.[SubvaultId] IN (SELECT [Id] FROM @SubvaultIds)
|
||||||
|
END
|
@ -1,6 +1,6 @@
|
|||||||
CREATE TABLE [dbo].[History] (
|
CREATE TABLE [dbo].[History] (
|
||||||
[Id] BIGINT IDENTITY (1, 1) NOT NULL,
|
[Id] BIGINT IDENTITY (1, 1) NOT NULL,
|
||||||
[UserId] UNIQUEIDENTIFIER NOT NULL,
|
[UserId] UNIQUEIDENTIFIER NULL,
|
||||||
[CipherId] UNIQUEIDENTIFIER NOT NULL,
|
[CipherId] UNIQUEIDENTIFIER NOT NULL,
|
||||||
[Event] TINYINT NOT NULL,
|
[Event] TINYINT NOT NULL,
|
||||||
[Date] DATETIME2 (7) NOT NULL,
|
[Date] DATETIME2 (7) NOT NULL,
|
||||||
|
1
src/Sql/dbo/UserDefinedTypes/GuidIdArray.sql
Normal file
1
src/Sql/dbo/UserDefinedTypes/GuidIdArray.sql
Normal file
@ -0,0 +1 @@
|
|||||||
|
CREATE TYPE [dbo].[GuidIdArray] AS TABLE ([Id] UNIQUEIDENTIFIER NOT NULL);
|
Loading…
x
Reference in New Issue
Block a user