mirror of
https://github.com/bitwarden/android.git
synced 2024-12-26 02:48:29 +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
|
namespace Bit.Core.Models.Data
|
||||||
{
|
{
|
||||||
public class OrganizationData
|
public class OrganizationData : Data
|
||||||
{
|
{
|
||||||
public OrganizationData(ProfileOrganizationResponse response)
|
public OrganizationData(ProfileOrganizationResponse response)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
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