bitwarden-android/src/App/Services/LoginService.cs

347 lines
13 KiB
C#
Raw Normal View History

2016-05-02 09:52:09 +03:00
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Bit.App.Abstractions;
using Bit.App.Models;
2016-05-03 09:08:50 +03:00
using Bit.App.Models.Api;
2016-05-02 09:52:09 +03:00
using Bit.App.Models.Data;
using Xamarin.Forms;
using System.Net.Http;
2016-05-02 09:52:09 +03:00
namespace Bit.App.Services
{
2017-01-03 08:17:15 +03:00
public class LoginService : ILoginService
2016-05-02 09:52:09 +03:00
{
2017-01-03 08:17:15 +03:00
private readonly ILoginRepository _loginRepository;
private readonly IAttachmentRepository _attachmentRepository;
2016-05-03 00:50:16 +03:00
private readonly IAuthService _authService;
2017-01-03 08:17:15 +03:00
private readonly ILoginApiRepository _loginApiRepository;
2017-07-22 22:38:08 +03:00
private readonly ICipherApiRepository _cipherApiRepository;
private readonly ISettingsService _settingsService;
private readonly ICryptoService _cryptoService;
2016-05-02 09:52:09 +03:00
2017-01-03 08:17:15 +03:00
public LoginService(
ILoginRepository loginRepository,
IAttachmentRepository attachmentRepository,
IAuthService authService,
ILoginApiRepository loginApiRepository,
2017-07-22 22:38:08 +03:00
ICipherApiRepository cipherApiRepository,
ISettingsService settingsService,
ICryptoService cryptoService)
2016-05-02 09:52:09 +03:00
{
2017-01-03 08:17:15 +03:00
_loginRepository = loginRepository;
_attachmentRepository = attachmentRepository;
2016-05-03 00:50:16 +03:00
_authService = authService;
2017-01-03 08:17:15 +03:00
_loginApiRepository = loginApiRepository;
2017-07-22 22:38:08 +03:00
_cipherApiRepository = cipherApiRepository;
_settingsService = settingsService;
_cryptoService = cryptoService;
2016-05-03 00:50:16 +03:00
}
2017-01-03 08:17:15 +03:00
public async Task<Login> GetByIdAsync(string id)
2016-05-03 00:50:16 +03:00
{
2017-01-03 08:17:15 +03:00
var data = await _loginRepository.GetByIdAsync(id);
if(data == null || data.UserId != _authService.UserId)
{
return null;
}
var attachments = await _attachmentRepository.GetAllByLoginIdAsync(id);
var login = new Login(data, attachments);
2017-01-03 08:17:15 +03:00
return login;
}
2017-01-03 08:17:15 +03:00
public async Task<IEnumerable<Login>> GetAllAsync()
{
var attachmentData = await _attachmentRepository.GetAllByUserIdAsync(_authService.UserId);
var attachmentDict = attachmentData.GroupBy(a => a.LoginId).ToDictionary(g => g.Key, g => g.ToList());
2017-01-03 08:17:15 +03:00
var data = await _loginRepository.GetAllByUserIdAsync(_authService.UserId);
var logins = data.Select(f => new Login(f, attachmentDict.ContainsKey(f.Id) ? attachmentDict[f.Id] : null));
2017-01-03 08:17:15 +03:00
return logins;
2016-05-02 09:52:09 +03:00
}
2017-01-03 08:17:15 +03:00
public async Task<IEnumerable<Login>> GetAllAsync(bool favorites)
{
var attachmentData = await _attachmentRepository.GetAllByUserIdAsync(_authService.UserId);
var attachmentDict = attachmentData.GroupBy(a => a.LoginId).ToDictionary(g => g.Key, g => g.ToList());
2017-01-03 08:17:15 +03:00
var data = await _loginRepository.GetAllByUserIdAsync(_authService.UserId, favorites);
var logins = data.Select(f => new Login(f, attachmentDict.ContainsKey(f.Id) ? attachmentDict[f.Id] : null));
2017-01-03 08:17:15 +03:00
return logins;
}
public async Task<Tuple<IEnumerable<Login>, IEnumerable<Login>>> GetAllAsync(string uriString)
{
if(string.IsNullOrWhiteSpace(uriString))
{
return null;
}
Uri uri = null;
2017-09-07 06:08:24 +03:00
string domainName = null;
2017-04-28 19:14:53 +03:00
var androidApp = UriIsAndroidApp(uriString);
var iosApp = UriIsiOSApp(uriString);
var mobileApp = androidApp || iosApp;
if(!mobileApp &&
(!Uri.TryCreate(uriString, UriKind.Absolute, out uri) ||
2017-09-07 06:08:24 +03:00
!DomainName.TryParseBaseDomain(uri.Host, out domainName)))
{
return null;
}
var mobileAppWebUriString = androidApp ? WebUriFromAndroidAppUri(uriString) :
iosApp ? WebUriFromiOSAppUri(uriString) : null;
var eqDomains = (await _settingsService.GetEquivalentDomainsAsync()).Select(d => d.ToArray());
var matchingDomains = new List<string>();
var matchingFuzzyDomains = new List<string>();
foreach(var eqDomain in eqDomains)
{
if(mobileApp)
{
if(Array.IndexOf(eqDomain, uriString) >= 0)
{
matchingDomains.AddRange(eqDomain.Select(d => d).ToList());
}
else if(mobileAppWebUriString != null && Array.IndexOf(eqDomain, mobileAppWebUriString) >= 0)
{
matchingFuzzyDomains.AddRange(eqDomain.Select(d => d).ToList());
}
}
2017-09-07 06:08:24 +03:00
else if(Array.IndexOf(eqDomain, domainName) >= 0)
{
matchingDomains.AddRange(eqDomain.Select(d => d).ToList());
}
}
if(!matchingDomains.Any())
{
matchingDomains.Add(mobileApp ? uriString : domainName);
}
if(mobileApp && mobileAppWebUriString != null &&
!matchingFuzzyDomains.Any() && !matchingDomains.Contains(mobileAppWebUriString))
{
matchingFuzzyDomains.Add(mobileAppWebUriString);
}
var matchingDomainsArray = matchingDomains.ToArray();
var matchingFuzzyDomainsArray = matchingFuzzyDomains.ToArray();
var matchingLogins = new List<Login>();
var matchingFuzzyLogins = new List<Login>();
var logins = await _loginRepository.GetAllByUserIdAsync(_authService.UserId);
foreach(var login in logins)
{
if(string.IsNullOrWhiteSpace(login.Uri))
{
continue;
}
var loginUriString = new CipherString(login.Uri).Decrypt(login.OrganizationId);
if(string.IsNullOrWhiteSpace(loginUriString))
{
continue;
}
if(Array.IndexOf(matchingDomainsArray, loginUriString) >= 0)
{
matchingLogins.Add(new Login(login));
continue;
}
else if(mobileApp && Array.IndexOf(matchingFuzzyDomainsArray, loginUriString) >= 0)
{
matchingFuzzyLogins.Add(new Login(login));
continue;
}
else if(!mobileApp && Array.IndexOf(matchingDomainsArray, WebUriFromAndroidAppUri(loginUriString)) >= 0)
2017-02-14 06:10:34 +03:00
{
matchingFuzzyLogins.Add(new Login(login));
continue;
}
Uri loginUri;
2017-09-07 06:08:24 +03:00
string loginDomainName = null;
if(!Uri.TryCreate(loginUriString, UriKind.Absolute, out loginUri)
2017-09-07 06:08:24 +03:00
|| !DomainName.TryParseBaseDomain(loginUri.Host, out loginDomainName))
{
continue;
}
2017-09-07 06:08:24 +03:00
if(Array.IndexOf(matchingDomainsArray, loginDomainName) >= 0)
{
matchingLogins.Add(new Login(login));
}
else if(mobileApp && Array.IndexOf(matchingFuzzyDomainsArray, loginDomainName) >= 0)
{
matchingFuzzyLogins.Add(new Login(login));
}
}
return new Tuple<IEnumerable<Login>, IEnumerable<Login>>(matchingLogins, matchingFuzzyLogins);
}
2017-01-03 08:17:15 +03:00
public async Task<ApiResult<LoginResponse>> SaveAsync(Login login)
2016-05-02 09:52:09 +03:00
{
2017-01-03 08:17:15 +03:00
ApiResult<LoginResponse> response = null;
var request = new LoginRequest(login);
2016-05-03 09:08:50 +03:00
2017-01-03 08:17:15 +03:00
if(login.Id == null)
2016-05-02 09:52:09 +03:00
{
2017-01-03 08:17:15 +03:00
response = await _loginApiRepository.PostAsync(request);
2016-05-02 09:52:09 +03:00
}
else
{
2017-01-03 08:17:15 +03:00
response = await _loginApiRepository.PutAsync(login.Id, request);
}
if(response.Succeeded)
{
2017-01-03 08:17:15 +03:00
var data = new LoginData(response.Result, _authService.UserId);
if(login.Id == null)
{
2017-01-03 08:17:15 +03:00
await _loginRepository.InsertAsync(data);
login.Id = data.Id;
}
else
{
2017-01-03 08:17:15 +03:00
await _loginRepository.UpdateAsync(data);
}
2016-05-02 09:52:09 +03:00
}
else if(response.StatusCode == System.Net.HttpStatusCode.Forbidden
|| response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
MessagingCenter.Send(Application.Current, "Logout", (string)null);
}
2016-05-02 09:52:09 +03:00
return response;
2016-05-02 09:52:09 +03:00
}
2016-05-04 02:49:49 +03:00
public async Task<ApiResult> DeleteAsync(string id)
2016-05-04 02:49:49 +03:00
{
2017-01-03 08:17:15 +03:00
var response = await _loginApiRepository.DeleteAsync(id);
if(response.Succeeded)
2016-05-04 02:49:49 +03:00
{
2017-01-03 08:17:15 +03:00
await _loginRepository.DeleteAsync(id);
2016-05-04 02:49:49 +03:00
}
else if(response.StatusCode == System.Net.HttpStatusCode.Forbidden
|| response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
MessagingCenter.Send(Application.Current, "Logout", (string)null);
}
2016-05-04 02:49:49 +03:00
return response;
2016-05-04 02:49:49 +03:00
}
2017-02-14 06:10:34 +03:00
2017-07-13 19:08:48 +03:00
public async Task<byte[]> DownloadAndDecryptAttachmentAsync(string url, string orgId = null)
{
using(var client = new HttpClient())
{
try
{
var response = await client.GetAsync(new Uri(url)).ConfigureAwait(false);
if(!response.IsSuccessStatusCode)
{
return null;
}
var data = await response.Content.ReadAsByteArrayAsync();
if(data == null)
{
return null;
}
2017-07-22 22:38:08 +03:00
2017-07-13 19:08:48 +03:00
if(!string.IsNullOrWhiteSpace(orgId))
{
return _cryptoService.DecryptToBytes(data, _cryptoService.GetOrgKey(orgId));
}
else
{
return _cryptoService.DecryptToBytes(data, null);
}
}
catch
{
return null;
}
}
}
2017-07-22 22:38:08 +03:00
public async Task<ApiResult<CipherResponse>> EncryptAndSaveAttachmentAsync(Login login, byte[] data, string fileName)
{
var encFileName = fileName.Encrypt(login.OrganizationId);
var encBytes = _cryptoService.EncryptToBytes(data,
login.OrganizationId != null ? _cryptoService.GetOrgKey(login.OrganizationId) : null);
var response = await _cipherApiRepository.PostAttachmentAsync(login.Id, encBytes, encFileName.EncryptedString);
if(response.Succeeded)
{
var attachmentData = response.Result.Attachments.Select(a => new AttachmentData(a, login.Id));
foreach(var attachment in attachmentData)
{
await _attachmentRepository.UpsertAsync(attachment);
}
login.Attachments = response.Result.Attachments.Select(a => new Attachment(a));
}
else if(response.StatusCode == System.Net.HttpStatusCode.Forbidden
|| response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
MessagingCenter.Send(Application.Current, "Logout", (string)null);
}
return response;
}
public async Task<ApiResult> DeleteAttachmentAsync(Login login, string attachmentId)
{
var response = await _cipherApiRepository.DeleteAttachmentAsync(login.Id, attachmentId);
if(response.Succeeded)
{
await _attachmentRepository.DeleteAsync(attachmentId);
}
else if(response.StatusCode == System.Net.HttpStatusCode.Forbidden
|| response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
MessagingCenter.Send(Application.Current, "Logout", (string)null);
}
return response;
}
2017-02-14 06:10:34 +03:00
private string WebUriFromAndroidAppUri(string androidAppUriString)
{
if(!UriIsAndroidApp(androidAppUriString))
{
return null;
}
var androidUriParts = androidAppUriString.Replace(Constants.AndroidAppProtocol, string.Empty).Split('.');
if(androidUriParts.Length >= 2)
{
return string.Join(".", androidUriParts[1], androidUriParts[0]);
}
return null;
}
private string WebUriFromiOSAppUri(string iosAppUriString)
{
if(!UriIsiOSApp(iosAppUriString))
{
return null;
}
return iosAppUriString.Replace(Constants.iOSAppProtocol, string.Empty);
}
2017-02-14 06:10:34 +03:00
private bool UriIsAndroidApp(string uriString)
{
return uriString.StartsWith(Constants.AndroidAppProtocol);
}
private bool UriIsiOSApp(string uriString)
{
return uriString.StartsWith(Constants.iOSAppProtocol);
}
2016-05-02 09:52:09 +03:00
}
}