mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 23:25:45 +03:00
setup more models
This commit is contained in:
parent
a1ba2bf60b
commit
c89805d123
14 changed files with 412 additions and 7 deletions
14
src/Core/Models/Api/LoginApi.cs
Normal file
14
src/Core/Models/Api/LoginApi.cs
Normal 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; }
|
||||
}
|
||||
}
|
10
src/Core/Models/Api/LoginUriApi.cs
Normal file
10
src/Core/Models/Api/LoginUriApi.cs
Normal 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; }
|
||||
}
|
||||
}
|
10
src/Core/Models/Data/Data.cs
Normal file
10
src/Core/Models/Data/Data.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Bit.Core.Models.Data
|
||||
{
|
||||
public abstract class Data
|
||||
{
|
||||
}
|
||||
}
|
27
src/Core/Models/Data/LoginData.cs
Normal file
27
src/Core/Models/Data/LoginData.cs
Normal 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; }
|
||||
}
|
||||
}
|
19
src/Core/Models/Data/LoginUriData.cs
Normal file
19
src/Core/Models/Data/LoginUriData.cs
Normal 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; }
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@ using Bit.Core.Models.Response;
|
|||
|
||||
namespace Bit.Core.Models.Data
|
||||
{
|
||||
public class OrganizationData
|
||||
public class OrganizationData : Data
|
||||
{
|
||||
public OrganizationData(ProfileOrganizationResponse response)
|
||||
{
|
||||
|
|
|
@ -9,5 +9,6 @@ namespace Bit.Core.Models.Domain
|
|||
public string Id { get; set; }
|
||||
public string OrganizationId { get; set; }
|
||||
public CipherString Name { get; set; }
|
||||
public Login Login { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using Bit.Core.Enums;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Bit.Core.Models.Domain
|
||||
{
|
||||
|
@ -96,14 +97,14 @@ namespace Bit.Core.Models.Domain
|
|||
public string Data { 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)
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
return _decryptedValue;
|
||||
return Task.FromResult(_decryptedValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,86 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Bit.Core.Models.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
15
src/Core/Models/Domain/Login.cs
Normal file
15
src/Core/Models/Domain/Login.cs
Normal 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; }
|
||||
}
|
||||
}
|
43
src/Core/Models/Domain/LoginUri.cs
Normal file
43
src/Core/Models/Domain/LoginUri.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
34
src/Core/Models/View/LoginUriView.cs
Normal file
34
src/Core/Models/View/LoginUriView.cs
Normal 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
|
||||
}
|
||||
}
|
27
src/Core/Models/View/LoginView.cs
Normal file
27
src/Core/Models/View/LoginView.cs
Normal 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;
|
||||
}
|
||||
}
|
129
src/Core/Services/CipherService.cs
Normal file
129
src/Core/Services/CipherService.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue