From 588f6c7c2cc1676c6fb00d671fa867134a02c7e6 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Sat, 18 Mar 2017 11:58:02 -0400 Subject: [PATCH] refactor for cipher details, folders, favorites --- src/Api/Controllers/CiphersController.cs | 48 ++++++------ src/Api/Controllers/LoginsController.cs | 78 ++----------------- src/Core/Enums/CipherType.cs | 2 +- src/Core/Models/Api/LoginDataModel.cs | 19 ++++- .../Models/Api/Request/CipherRequestModel.cs | 5 +- .../Models/Api/Request/LoginRequestModel.cs | 10 ++- .../Response/CipherHistoryResponseModel.cs | 2 +- .../Api/Response/CipherResponseModel.cs | 31 ++++++-- .../Models/Api/Response/LoginResponseModel.cs | 5 +- src/Core/Models/Data/CipherDetails.cs | 11 +-- src/Core/Repositories/ICipherRepository.cs | 2 +- .../SqlServer/CipherRepository.cs | 14 ++-- src/Core/Services/ICipherService.cs | 5 +- .../Services/Implementations/CipherService.cs | 39 +++++++++- .../Implementations/PushSharpPushService.cs | 6 +- 15 files changed, 139 insertions(+), 138 deletions(-) diff --git a/src/Api/Controllers/CiphersController.cs b/src/Api/Controllers/CiphersController.cs index 11cafa6c4..3206aab7d 100644 --- a/src/Api/Controllers/CiphersController.cs +++ b/src/Api/Controllers/CiphersController.cs @@ -38,7 +38,7 @@ namespace Bit.Api.Controllers throw new NotFoundException(); } - return new CipherResponseModel(cipher, userId); + return new CipherResponseModel(cipher); } [HttpGet("")] @@ -46,25 +46,25 @@ namespace Bit.Api.Controllers { var userId = _userService.GetProperUserId(User).Value; var ciphers = await _cipherRepository.GetManyByUserIdAsync(userId); - var responses = ciphers.Select(c => new CipherResponseModel(c, userId)); + var responses = ciphers.Select(c => new CipherResponseModel(c)); return new ListResponseModel(responses); } - [Obsolete] - [HttpGet("history")] - public async Task Get(DateTime since) - { - var userId = _userService.GetProperUserId(User).Value; - var history = await _cipherRepository.GetManySinceRevisionDateAndUserIdWithDeleteHistoryAsync( - since, userId); - return new CipherHistoryResponseModel(history.Item1, history.Item2, userId); - } + //[Obsolete] + //[HttpGet("history")] + //public async Task Get(DateTime since) + //{ + // var userId = _userService.GetProperUserId(User).Value; + // var history = await _cipherRepository.GetManySinceRevisionDateAndUserIdWithDeleteHistoryAsync( + // since, userId); + // return new CipherHistoryResponseModel(history.Item1, history.Item2, userId); + //} [HttpPost("import")] public async Task PostImport([FromBody]ImportRequestModel model) { var userId = _userService.GetProperUserId(User).Value; - var folderCiphers = model.Folders.Select(f => f.ToCipher(userId)).ToList(); + var folderCiphers = model.Folders.Select(f => f.ToFolder(userId)).ToList(); var otherCiphers = model.Logins.Select(s => s.ToCipher(userId)).ToList(); await _cipherService.ImportCiphersAsync( @@ -73,20 +73,20 @@ namespace Bit.Api.Controllers model.FolderRelationships); } - [HttpPut("{id}/favorite")] - [HttpPost("{id}/favorite")] - public async Task Favorite(string id) - { - var cipher = await _cipherRepository.GetByIdAsync(new Guid(id), _userService.GetProperUserId(User).Value); - if(cipher == null) - { - throw new NotFoundException(); - } + //[HttpPut("{id}/favorite")] + //[HttpPost("{id}/favorite")] + //public async Task Favorite(string id) + //{ + // var cipher = await _cipherRepository.GetByIdAsync(new Guid(id), _userService.GetProperUserId(User).Value); + // if(cipher == null) + // { + // throw new NotFoundException(); + // } - cipher.Favorite = !cipher.Favorite; + // cipher.Favorite = !cipher.Favorite; - await _cipherService.SaveAsync(cipher); - } + // await _cipherService.SaveAsync(cipher); + //} [HttpDelete("{id}")] [HttpPost("{id}/delete")] diff --git a/src/Api/Controllers/LoginsController.cs b/src/Api/Controllers/LoginsController.cs index dd3d7a8cc..fdc21a249 100644 --- a/src/Api/Controllers/LoginsController.cs +++ b/src/Api/Controllers/LoginsController.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; @@ -7,7 +6,6 @@ using Bit.Core.Repositories; using Microsoft.AspNetCore.Authorization; using Bit.Core.Models.Api; using Bit.Core.Exceptions; -using Bit.Core.Models.Table; using Bit.Core.Services; namespace Bit.Api.Controllers @@ -33,7 +31,7 @@ namespace Bit.Api.Controllers } [HttpGet("{id}")] - public async Task Get(string id, string[] expand = null) + public async Task Get(string id) { var userId = _userService.GetProperUserId(User).Value; var login = await _cipherRepository.GetByIdAsync(new Guid(id), userId); @@ -42,37 +40,34 @@ namespace Bit.Api.Controllers throw new NotFoundException(); } - var response = new LoginResponseModel(login, userId); - await ExpandAsync(login, response, expand, null, userId); + var response = new LoginResponseModel(login); return response; } [HttpGet("")] - public async Task> Get(string[] expand = null) + public async Task> Get() { var userId = _userService.GetProperUserId(User).Value; var logins = await _cipherRepository.GetManyByTypeAndUserIdAsync(Core.Enums.CipherType.Login, userId); - var responses = logins.Select(s => new LoginResponseModel(s, userId)).ToList(); - await ExpandManyAsync(logins, responses, expand, null, userId); + var responses = logins.Select(s => new LoginResponseModel(s)).ToList(); return new ListResponseModel(responses); } [HttpPost("")] - public async Task Post([FromBody]LoginRequestModel model, string[] expand = null) + public async Task Post([FromBody]LoginRequestModel model) { var userId = _userService.GetProperUserId(User).Value; var login = model.ToCipher(userId); await _cipherService.SaveAsync(login); - var response = new LoginResponseModel(login, userId); - await ExpandAsync(login, response, expand, null, userId); + var response = new LoginResponseModel(login); return response; } [HttpPut("{id}")] [HttpPost("{id}")] - public async Task Put(string id, [FromBody]LoginRequestModel model, string[] expand = null) + public async Task Put(string id, [FromBody]LoginRequestModel model) { var userId = _userService.GetProperUserId(User).Value; var login = await _cipherRepository.GetByIdAsync(new Guid(id), _userService.GetProperUserId(User).Value); @@ -83,8 +78,7 @@ namespace Bit.Api.Controllers await _cipherService.SaveAsync(model.ToCipher(login)); - var response = new LoginResponseModel(login, userId); - await ExpandAsync(login, response, expand, null, userId); + var response = new LoginResponseModel(login); return response; } @@ -100,61 +94,5 @@ namespace Bit.Api.Controllers await _cipherService.DeleteAsync(login); } - - private async Task ExpandAsync(Cipher login, LoginResponseModel response, string[] expand, Cipher folder, Guid userId) - { - if(expand == null || expand.Count() == 0) - { - return; - } - - if(expand.Any(e => e.ToLower() == "folder") && login.FolderId.HasValue) - { - if(folder == null) - { - folder = await _cipherRepository.GetByIdAsync(login.FolderId.Value); - } - - response.Folder = new FolderResponseModel(folder, userId); - } - } - - private async Task ExpandManyAsync(IEnumerable logins, ICollection responses, - string[] expand, IEnumerable folders, Guid userId) - { - if(expand == null || expand.Count() == 0) - { - return; - } - - if(expand.Any(e => e.ToLower() == "folder")) - { - if(folders == null) - { - folders = await _cipherRepository.GetManyByTypeAndUserIdAsync(Core.Enums.CipherType.Folder, - _userService.GetProperUserId(User).Value); - } - - if(folders != null && folders.Count() > 0) - { - foreach(var response in responses) - { - var login = logins.SingleOrDefault(s => s.Id.ToString() == response.Id); - if(login == null) - { - continue; - } - - var folder = folders.SingleOrDefault(f => f.Id == login.FolderId); - if(folder == null) - { - continue; - } - - response.Folder = new FolderResponseModel(folder, userId); - } - } - } - } } } diff --git a/src/Core/Enums/CipherType.cs b/src/Core/Enums/CipherType.cs index 9f56e8d01..65242f18c 100644 --- a/src/Core/Enums/CipherType.cs +++ b/src/Core/Enums/CipherType.cs @@ -2,7 +2,7 @@ { public enum CipherType : byte { - Folder = 0, + //Folder = 0, Login = 1 } } diff --git a/src/Core/Models/Api/LoginDataModel.cs b/src/Core/Models/Api/LoginDataModel.cs index 71e2a9073..1ec08b3f5 100644 --- a/src/Core/Models/Api/LoginDataModel.cs +++ b/src/Core/Models/Api/LoginDataModel.cs @@ -1,6 +1,7 @@ using System; using Bit.Core.Models.Table; using Newtonsoft.Json; +using Core.Models.Data; namespace Bit.Core.Models.Api { @@ -28,7 +29,23 @@ namespace Bit.Core.Models.Api public LoginDataModel(Cipher cipher) { - if(cipher.Type != Core.Enums.CipherType.Login) + if(cipher.Type != Enums.CipherType.Login) + { + throw new ArgumentException("Cipher is not correct type."); + } + + var data = JsonConvert.DeserializeObject(cipher.Data); + + Name = data.Name; + Uri = data.Uri; + Username = data.Username; + Password = data.Password; + Notes = data.Notes; + } + + public LoginDataModel(CipherDetails cipher) + { + if(cipher.Type != Enums.CipherType.Login) { throw new ArgumentException("Cipher is not correct type."); } diff --git a/src/Core/Models/Api/Request/CipherRequestModel.cs b/src/Core/Models/Api/Request/CipherRequestModel.cs index a12100543..17c492c79 100644 --- a/src/Core/Models/Api/Request/CipherRequestModel.cs +++ b/src/Core/Models/Api/Request/CipherRequestModel.cs @@ -39,15 +39,12 @@ namespace Bit.Core.Models.Api { Id = new Guid(Id), UserId = userId, - FolderId = string.IsNullOrWhiteSpace(FolderId) ? null : (Guid?)new Guid(FolderId), + //FolderId = string.IsNullOrWhiteSpace(FolderId) ? null : (Guid?)new Guid(FolderId), Type = Type }; switch(Type) { - case CipherType.Folder: - cipher.Data = JsonConvert.SerializeObject(new FolderDataModel(this), new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); - break; case CipherType.Login: cipher.Data = JsonConvert.SerializeObject(new LoginDataModel(this), new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); break; diff --git a/src/Core/Models/Api/Request/LoginRequestModel.cs b/src/Core/Models/Api/Request/LoginRequestModel.cs index 49efedc92..8b44d0500 100644 --- a/src/Core/Models/Api/Request/LoginRequestModel.cs +++ b/src/Core/Models/Api/Request/LoginRequestModel.cs @@ -3,6 +3,7 @@ using System.ComponentModel.DataAnnotations; using Bit.Core.Utilities; using Bit.Core.Models.Table; using Newtonsoft.Json; +using Core.Models.Data; namespace Bit.Core.Models.Api { @@ -28,21 +29,22 @@ namespace Bit.Core.Models.Api [StringLength(10000)] public string Notes { get; set; } - public Cipher ToCipher(Guid userId) + public CipherDetails ToCipher(Guid userId) { - return ToCipher(new Cipher + return ToCipher(new CipherDetails { UserId = userId }); } - public Cipher ToCipher(Cipher existingLogin) + public CipherDetails ToCipher(CipherDetails existingLogin) { existingLogin.FolderId = string.IsNullOrWhiteSpace(FolderId) ? null : (Guid?)new Guid(FolderId); existingLogin.Favorite = Favorite; + existingLogin.Data = JsonConvert.SerializeObject(new LoginDataModel(this), new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); - existingLogin.Type = Core.Enums.CipherType.Login; + existingLogin.Type = Enums.CipherType.Login; return existingLogin; } diff --git a/src/Core/Models/Api/Response/CipherHistoryResponseModel.cs b/src/Core/Models/Api/Response/CipherHistoryResponseModel.cs index ba7cf39df..3ece67cc8 100644 --- a/src/Core/Models/Api/Response/CipherHistoryResponseModel.cs +++ b/src/Core/Models/Api/Response/CipherHistoryResponseModel.cs @@ -20,7 +20,7 @@ namespace Bit.Core.Models.Api throw new ArgumentNullException(nameof(deletedIds)); } - Revised = revisedCiphers.Select(c => new CipherResponseModel(c, userId)); + Revised = revisedCiphers.Select(c => new CipherResponseModel(c)); Deleted = deletedIds.Select(id => id.ToString()); } diff --git a/src/Core/Models/Api/Response/CipherResponseModel.cs b/src/Core/Models/Api/Response/CipherResponseModel.cs index daa218eef..8ae52a672 100644 --- a/src/Core/Models/Api/Response/CipherResponseModel.cs +++ b/src/Core/Models/Api/Response/CipherResponseModel.cs @@ -1,11 +1,12 @@ using System; using Bit.Core.Models.Table; +using Core.Models.Data; namespace Bit.Core.Models.Api { public class CipherResponseModel : ResponseModel { - public CipherResponseModel(Cipher cipher, Guid userId, string obj = "cipher") + public CipherResponseModel(Cipher cipher, string obj = "cipher") : base(obj) { if(cipher == null) @@ -14,17 +15,33 @@ namespace Bit.Core.Models.Api } Id = cipher.Id.ToString(); - FolderId = cipher.FolderId?.ToString(); Type = cipher.Type; - Favorite = cipher.Favorite; RevisionDate = cipher.RevisionDate; switch(cipher.Type) { - case Core.Enums.CipherType.Folder: - Data = new FolderDataModel(cipher); + case Enums.CipherType.Login: + Data = new LoginDataModel(cipher); break; - case Core.Enums.CipherType.Login: + default: + throw new ArgumentException("Unsupported " + nameof(Type) + "."); + } + } + public CipherResponseModel(CipherDetails cipher, string obj = "cipher") + : base(obj) + { + if(cipher == null) + { + throw new ArgumentNullException(nameof(cipher)); + } + + Id = cipher.Id.ToString(); + Type = cipher.Type; + RevisionDate = cipher.RevisionDate; + + switch(cipher.Type) + { + case Enums.CipherType.Login: Data = new LoginDataModel(cipher); break; default: @@ -34,7 +51,7 @@ namespace Bit.Core.Models.Api public string Id { get; set; } public string FolderId { get; set; } - public Core.Enums.CipherType Type { get; set; } + public Enums.CipherType Type { get; set; } public bool Favorite { get; set; } public dynamic Data { get; set; } public DateTime RevisionDate { get; set; } diff --git a/src/Core/Models/Api/Response/LoginResponseModel.cs b/src/Core/Models/Api/Response/LoginResponseModel.cs index 2fd2dae7f..ffcf4410d 100644 --- a/src/Core/Models/Api/Response/LoginResponseModel.cs +++ b/src/Core/Models/Api/Response/LoginResponseModel.cs @@ -1,11 +1,12 @@ using System; +using Core.Models.Data; using Bit.Core.Models.Table; namespace Bit.Core.Models.Api { public class LoginResponseModel : ResponseModel { - public LoginResponseModel(Cipher cipher, Guid userId, string obj = "login") + public LoginResponseModel(CipherDetails cipher, string obj = "login") : base(obj) { if(cipher == null) @@ -13,7 +14,7 @@ namespace Bit.Core.Models.Api throw new ArgumentNullException(nameof(cipher)); } - if(cipher.Type != Core.Enums.CipherType.Login) + if(cipher.Type != Enums.CipherType.Login) { throw new ArgumentException(nameof(cipher.Type)); } diff --git a/src/Core/Models/Data/CipherDetails.cs b/src/Core/Models/Data/CipherDetails.cs index eee69a691..a938a54de 100644 --- a/src/Core/Models/Data/CipherDetails.cs +++ b/src/Core/Models/Data/CipherDetails.cs @@ -1,18 +1,11 @@ -using Bit.Core.Enums; +using Bit.Core.Models.Table; using System; namespace Core.Models.Data { - public class CipherDetails + public class CipherDetails : Cipher { - public Guid Id { get; set; } - public Guid? UserId { get; set; } - public Guid? OrganizationId { get; set; } public Guid? FolderId { get; set; } - public CipherType Type { get; set; } public bool Favorite { get; set; } - public string Data { get; set; } - public DateTime CreationDate { get; set; } - public DateTime RevisionDate { get; set; } } } diff --git a/src/Core/Repositories/ICipherRepository.cs b/src/Core/Repositories/ICipherRepository.cs index 2305909f6..ab10677ed 100644 --- a/src/Core/Repositories/ICipherRepository.cs +++ b/src/Core/Repositories/ICipherRepository.cs @@ -8,7 +8,7 @@ namespace Bit.Core.Repositories { public interface ICipherRepository : IRepository { - Task GetByIdAsync(Guid id, Guid userId); + Task GetByIdAsync(Guid id, Guid userId); Task> GetManyByUserIdAsync(Guid userId); Task> GetManyByTypeAndUserIdAsync(Enums.CipherType type, Guid userId); Task, ICollection>> GetManySinceRevisionDateAndUserIdWithDeleteHistoryAsync( diff --git a/src/Core/Repositories/SqlServer/CipherRepository.cs b/src/Core/Repositories/SqlServer/CipherRepository.cs index 0527b55a2..d1aa517ca 100644 --- a/src/Core/Repositories/SqlServer/CipherRepository.cs +++ b/src/Core/Repositories/SqlServer/CipherRepository.cs @@ -21,15 +21,17 @@ namespace Bit.Core.Repositories.SqlServer : base(connectionString) { } - public async Task GetByIdAsync(Guid id, Guid userId) + public async Task GetByIdAsync(Guid id, Guid userId) { - var cipher = await GetByIdAsync(id); - if(cipher == null || cipher.UserId != userId) + using(var connection = new SqlConnection(ConnectionString)) { - return null; - } + var results = await connection.QueryAsync( + $"[{Schema}].[CipherDetails_ReadById]", + new { Id = id }, + commandType: CommandType.StoredProcedure); - return cipher; + return results.FirstOrDefault(c => c.UserId == userId); + } } public async Task> GetManyByUserIdAsync(Guid userId) diff --git a/src/Core/Services/ICipherService.cs b/src/Core/Services/ICipherService.cs index 2cebb1bfa..753cd263e 100644 --- a/src/Core/Services/ICipherService.cs +++ b/src/Core/Services/ICipherService.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Threading.Tasks; using Bit.Core.Models.Table; +using Core.Models.Data; namespace Bit.Core.Services { @@ -8,7 +9,9 @@ namespace Bit.Core.Services { Task SaveAsync(Cipher cipher); Task DeleteAsync(Cipher cipher); - Task ImportCiphersAsync(List folders, List ciphers, + Task SaveFolderAsync(Folder folder); + Task DeleteFolderAsync(Folder folder); + Task ImportCiphersAsync(List folders, List ciphers, IEnumerable> folderRelationships); } } diff --git a/src/Core/Services/Implementations/CipherService.cs b/src/Core/Services/Implementations/CipherService.cs index d73327a8a..72425c22d 100644 --- a/src/Core/Services/Implementations/CipherService.cs +++ b/src/Core/Services/Implementations/CipherService.cs @@ -4,21 +4,25 @@ using System.Collections.Generic; using System.Threading.Tasks; using Bit.Core.Models.Table; using Bit.Core.Repositories; +using Core.Models.Data; namespace Bit.Core.Services { public class CipherService : ICipherService { private readonly ICipherRepository _cipherRepository; + private readonly IFolderRepository _folderRepository; private readonly IUserRepository _userRepository; private readonly IPushService _pushService; public CipherService( ICipherRepository cipherRepository, + IFolderRepository folderRepository, IUserRepository userRepository, IPushService pushService) { _cipherRepository = cipherRepository; + _folderRepository = folderRepository; _userRepository = userRepository; _pushService = pushService; } @@ -50,16 +54,43 @@ namespace Bit.Core.Services await _pushService.PushSyncCipherDeleteAsync(cipher); } + public async Task SaveFolderAsync(Folder folder) + { + if(folder.Id == default(Guid)) + { + await _folderRepository.CreateAsync(folder); + + // push + //await _pushService.PushSyncCipherCreateAsync(cipher); + } + else + { + folder.RevisionDate = DateTime.UtcNow; + await _folderRepository.UpsertAsync(folder); + + // push + //await _pushService.PushSyncCipherUpdateAsync(cipher); + } + } + + public async Task DeleteFolderAsync(Folder folder) + { + await _folderRepository.DeleteAsync(folder); + + // push + //await _pushService.PushSyncCipherDeleteAsync(cipher); + } + public async Task ImportCiphersAsync( - List folders, - List ciphers, + List folders, + List ciphers, IEnumerable> folderRelationships) { // create all the folders var folderTasks = new List(); foreach(var folder in folders) { - folderTasks.Add(_cipherRepository.CreateAsync(folder)); + folderTasks.Add(_folderRepository.CreateAsync(folder)); } await Task.WhenAll(folderTasks); @@ -74,7 +105,7 @@ namespace Bit.Core.Services continue; } - cipher.FolderId = folder.Id; + //cipher.FolderId = folder.Id; } // create all the ciphers diff --git a/src/Core/Services/Implementations/PushSharpPushService.cs b/src/Core/Services/Implementations/PushSharpPushService.cs index dc54691e5..de39fbf8c 100644 --- a/src/Core/Services/Implementations/PushSharpPushService.cs +++ b/src/Core/Services/Implementations/PushSharpPushService.cs @@ -54,9 +54,9 @@ namespace Bit.Core.Services { switch(cipher.Type) { - case CipherType.Folder: - await PushCipherAsync(cipher, PushType.SyncFolderDelete); - break; + //case CipherType.Folder: + // await PushCipherAsync(cipher, PushType.SyncFolderDelete); + // break; case CipherType.Login: await PushCipherAsync(cipher, PushType.SyncLoginDelete); break;