setup more models

This commit is contained in:
Kyle Spearrin 2019-04-12 10:06:47 -04:00
parent a1ba2bf60b
commit c89805d123
14 changed files with 412 additions and 7 deletions

View file

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
namespace Bit.Core.Models.Api
{
public class LoginApi
{
public List<LoginUriApi> Uris { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public DateTime? PasswordRevisionDate { get; set; }
public string Totp { get; set; }
}
}

View file

@ -0,0 +1,10 @@
using Bit.Core.Enums;
namespace Bit.Core.Models.Api
{
public class LoginUriApi
{
public string Uri { get; set; }
public UriMatchType? Match { get; set; }
}
}

View file

@ -0,0 +1,10 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Bit.Core.Models.Data
{
public abstract class Data
{
}
}

View file

@ -0,0 +1,27 @@
using Bit.Core.Models.Api;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Bit.Core.Models.Data
{
public class LoginData : Data
{
public LoginData() { }
public LoginData(LoginApi data)
{
Username = data.Username;
Password = data.Password;
PasswordRevisionDate = data.PasswordRevisionDate;
Totp = data.Totp;
Uris = data.Uris?.Select(u => new LoginUriData(u)).ToList();
}
public List<LoginUriData> Uris { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public DateTime? PasswordRevisionDate { get; set; }
public string Totp { get; set; }
}
}

View file

@ -0,0 +1,19 @@
using Bit.Core.Enums;
using Bit.Core.Models.Api;
namespace Bit.Core.Models.Data
{
public class LoginUriData : Data
{
public LoginUriData() { }
public LoginUriData(LoginUriApi data)
{
Uri = data.Uri;
Match = data.Match;
}
public string Uri { get; set; }
public UriMatchType? Match { get; set; }
}
}

View file

@ -3,7 +3,7 @@ using Bit.Core.Models.Response;
namespace Bit.Core.Models.Data namespace Bit.Core.Models.Data
{ {
public class OrganizationData public class OrganizationData : Data
{ {
public OrganizationData(ProfileOrganizationResponse response) public OrganizationData(ProfileOrganizationResponse response)
{ {

View file

@ -9,5 +9,6 @@ namespace Bit.Core.Models.Domain
public string Id { get; set; } public string Id { get; set; }
public string OrganizationId { get; set; } public string OrganizationId { get; set; }
public CipherString Name { get; set; } public CipherString Name { get; set; }
public Login Login { get; set; }
} }
} }

View file

@ -1,5 +1,6 @@
using Bit.Core.Enums; using Bit.Core.Enums;
using System; using System;
using System.Threading.Tasks;
namespace Bit.Core.Models.Domain namespace Bit.Core.Models.Domain
{ {
@ -96,14 +97,14 @@ namespace Bit.Core.Models.Domain
public string Data { get; private set; } public string Data { get; private set; }
public string Mac { get; private set; } public string Mac { get; private set; }
public string Decrypt(string orgId = null) public Task<string> DecryptAsync(string orgId = null)
{ {
if(_decryptedValue == null) if(_decryptedValue == null)
{ {
// TODO // TODO
} }
return _decryptedValue; return Task.FromResult(_decryptedValue);
} }
} }
} }

View file

@ -1,11 +1,86 @@
using System; using System.Collections.Generic;
using System.Collections.Generic; using System.Threading.Tasks;
using System.Text;
namespace Bit.Core.Models.Domain namespace Bit.Core.Models.Domain
{ {
public abstract class Domain public abstract class Domain
{ {
// TODO protected void BuildDomainModel<D, O>(D domain, O dataObj, HashSet<string> map, bool alreadyEncrypted,
HashSet<string> notEncList = null)
where D : Domain
where O : Data.Data
{
var domainType = domain.GetType();
var dataObjType = dataObj.GetType();
foreach(var prop in map)
{
var dataObjPropInfo = dataObjType.GetProperty(prop);
var dataObjProp = dataObjPropInfo.GetValue(dataObj);
var domainPropInfo = domainType.GetProperty(prop);
if(alreadyEncrypted || (notEncList?.Contains(prop) ?? false))
{
domainPropInfo.SetValue(domain, dataObjProp, null);
}
else
{
domainPropInfo.SetValue(domain,
dataObjProp != null ? new CipherString(dataObjProp as string) : null, null);
}
}
}
protected void BuildDataModel<D, O>(D domain, O dataObj, HashSet<string> map,
HashSet<string> notCipherStringList = null)
where D : Domain
where O : Data.Data
{
var domainType = domain.GetType();
var dataObjType = dataObj.GetType();
foreach(var prop in map)
{
var domainPropInfo = domainType.GetProperty(prop);
var domainProp = domainPropInfo.GetValue(domain);
var dataObjPropInfo = dataObjType.GetProperty(prop);
if(notCipherStringList?.Contains(prop) ?? false)
{
dataObjPropInfo.SetValue(dataObj, domainProp, null);
}
else
{
dataObjPropInfo.SetValue(dataObj, (domainProp as CipherString)?.EncryptedString, null);
}
}
}
protected async Task<V> DecryptObjAsync<V, D>(V viewModel, D domain, HashSet<string> map, string orgId)
where V : View.View
{
var viewModelType = viewModel.GetType();
var domainType = domain.GetType();
Task<string> decCs(string propName)
{
var domainPropInfo = domainType.GetProperty(propName);
var domainProp = domainPropInfo.GetValue(domain) as CipherString;
if(domainProp != null)
{
return domainProp.DecryptAsync(orgId);
}
return Task.FromResult((string)null);
};
void setDec(string propName, string val)
{
var viewModelPropInfo = viewModelType.GetProperty(propName);
viewModelPropInfo.SetValue(viewModel, val, null);
};
var tasks = new List<Task>();
foreach(var prop in map)
{
tasks.Add(decCs(prop).ContinueWith(async val => setDec(prop, await val)));
}
await Task.WhenAll(tasks);
return viewModel;
}
} }
} }

View file

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Bit.Core.Models.Domain
{
public class Login : Domain
{
public List<LoginUri> Uris { get; set; }
public CipherString Username { get; set; }
public CipherString Password { get; set; }
public DateTime? PasswordRevisionDate { get; set; }
public CipherString Totp { get; set; }
}
}

View file

@ -0,0 +1,43 @@
using Bit.Core.Enums;
using Bit.Core.Models.Data;
using Bit.Core.Models.View;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Bit.Core.Models.Domain
{
public class LoginUri : Domain
{
public LoginUri() { }
public LoginUri(LoginUriData obj, bool alreadyEncrypted = false)
{
Match = obj.Match;
BuildDomainModel(this, obj, new HashSet<string>
{
"Uri"
}, alreadyEncrypted);
}
public CipherString Uri { get; set; }
public UriMatchType? Match { get; set; }
public Task<LoginUriView> DecryptAsync(string orgId)
{
return DecryptObjAsync(new LoginUriView(this), this, new HashSet<string>
{
"Uri"
}, orgId);
}
public LoginUriData ToLoginUriData()
{
var u = new LoginUriData();
BuildDataModel(this, u, new HashSet<string>
{
"Uri"
}, new HashSet<string> { "Match" });
return u;
}
}
}

View file

@ -0,0 +1,34 @@
using Bit.Core.Enums;
using Bit.Core.Models.Domain;
namespace Bit.Core.Models.View
{
public class LoginUriView : View
{
private string _uri;
private string _domain;
private string _hostname;
private bool? _canLaunch;
public LoginUriView() { }
public LoginUriView(LoginUri u)
{
Match = u.Match;
}
public UriMatchType? Match { get; set; }
public string Uri
{
get => _uri;
set
{
_uri = value;
_domain = null;
_canLaunch = null;
}
}
// TODO
}
}

View file

@ -0,0 +1,27 @@
using Bit.Core.Models.Domain;
using System;
using System.Collections.Generic;
namespace Bit.Core.Models.View
{
public class LoginView : View
{
public LoginView() { }
public LoginView(Login l)
{
PasswordRevisionDate = l.PasswordRevisionDate;
}
public string Username { get; set; }
public string Password { get; set; }
public DateTime? PasswordRevisionDate { get; set; }
public string Totp { get; set; }
public List<LoginUriView> Uris { get; set; }
public string Uri => HashUris ? Uris[0].Uri : null;
public string MaskedPassword => Password != null ? "••••••••" : null;
public string SubTitle => Username;
// TODO: uri launch props
public bool HashUris => (Uris?.Count ?? 0) > 0;
}
}

View file

@ -0,0 +1,129 @@
using Bit.Core.Abstractions;
using Bit.Core.Models.Domain;
using Bit.Core.Models.View;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Bit.Core.Services
{
public class CipherService
{
private const string Keys_CiphersFormat = "ciphers_{0}";
private const string Keys_LocalData = "ciphersLocalData";
private const string Keys_NeverDomains = "neverDomains";
private List<CipherView> _decryptedCipherCache;
private readonly ICryptoService _cryptoService;
private readonly IUserService _userService;
private readonly ISettingsService _settingsService;
private readonly IApiService _apiService;
private readonly IStorageService _storageService;
private readonly II18nService _i18NService;
private Dictionary<string, HashSet<string>> _domainMatchBlacklist = new Dictionary<string, HashSet<string>>
{
["google.com"] = new HashSet<string> { "script.google.com" }
};
public CipherService(
ICryptoService cryptoService,
IUserService userService,
ISettingsService settingsService,
IApiService apiService,
IStorageService storageService,
II18nService i18nService)
{
_cryptoService = cryptoService;
_userService = userService;
_settingsService = settingsService;
_apiService = apiService;
_storageService = storageService;
_i18NService = i18nService;
}
private List<CipherView> DecryptedCipherCache
{
get => _decryptedCipherCache;
set
{
if(value == null)
{
_decryptedCipherCache.Clear();
}
_decryptedCipherCache = value;
// TODO: update search index
}
}
public void ClearCache()
{
DecryptedCipherCache = null;
}
public async Task<Cipher> Encrypt(CipherView model, SymmetricCryptoKey key = null,
Cipher originalCipher = null)
{
// Adjust password history
if(model.Id != null)
{
// TODO
}
var cipher = new Cipher();
cipher.Id = model.Id;
// TODO others
if(key == null && cipher.OrganizationId != null)
{
key = await _cryptoService.GetOrgKeyAsync(cipher.OrganizationId);
if(key == null)
{
throw new Exception("Cannot encrypt cipher for organization. No key.");
}
}
var tasks = new List<Task>();
tasks.Add(EncryptObjPropertyAsync(model, cipher, new HashSet<string>
{
nameof(model.Name)
}, key));
await Task.WhenAll(tasks);
return cipher;
}
private Task EncryptObjPropertyAsync<V, D>(V model, D obj, HashSet<string> map, SymmetricCryptoKey key)
where V : View
where D : Domain
{
var modelType = model.GetType();
var objType = obj.GetType();
Task<CipherString> makeCs(string propName)
{
var modelPropInfo = modelType.GetProperty(propName);
var modelProp = modelPropInfo.GetValue(model) as string;
if(!string.IsNullOrWhiteSpace(modelProp))
{
return _cryptoService.EncryptAsync(modelProp, key);
}
return Task.FromResult((CipherString)null);
};
void setCs(string propName, CipherString val)
{
var objPropInfo = objType.GetProperty(propName);
objPropInfo.SetValue(obj, val, null);
};
var tasks = new List<Task>();
foreach(var prop in map)
{
tasks.Add(makeCs(prop).ContinueWith(async val => setCs(prop, await val)));
}
return Task.WhenAll(tasks);
}
}
}