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;
|
2016-07-20 01:46:39 +03:00
|
|
|
|
using Xamarin.Forms;
|
2017-07-13 06:09:44 +03:00
|
|
|
|
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;
|
2017-07-12 23:23:24 +03:00
|
|
|
|
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-02-09 07:58:37 +03:00
|
|
|
|
private readonly ISettingsService _settingsService;
|
2017-07-13 06:09:44 +03:00
|
|
|
|
private readonly ICryptoService _cryptoService;
|
2016-05-02 09:52:09 +03:00
|
|
|
|
|
2017-01-03 08:17:15 +03:00
|
|
|
|
public LoginService(
|
|
|
|
|
ILoginRepository loginRepository,
|
2017-07-12 23:23:24 +03:00
|
|
|
|
IAttachmentRepository attachmentRepository,
|
2016-05-03 01:35:01 +03:00
|
|
|
|
IAuthService authService,
|
2017-02-09 07:58:37 +03:00
|
|
|
|
ILoginApiRepository loginApiRepository,
|
2017-07-13 06:09:44 +03:00
|
|
|
|
ISettingsService settingsService,
|
|
|
|
|
ICryptoService cryptoService)
|
2016-05-02 09:52:09 +03:00
|
|
|
|
{
|
2017-01-03 08:17:15 +03:00
|
|
|
|
_loginRepository = loginRepository;
|
2017-07-12 23:23:24 +03:00
|
|
|
|
_attachmentRepository = attachmentRepository;
|
2016-05-03 00:50:16 +03:00
|
|
|
|
_authService = authService;
|
2017-01-03 08:17:15 +03:00
|
|
|
|
_loginApiRepository = loginApiRepository;
|
2017-02-09 07:58:37 +03:00
|
|
|
|
_settingsService = settingsService;
|
2017-07-13 06:09:44 +03:00
|
|
|
|
_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);
|
2016-05-06 07:17:38 +03:00
|
|
|
|
if(data == null || data.UserId != _authService.UserId)
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-12 23:23:24 +03:00
|
|
|
|
var attachments = await _attachmentRepository.GetAllByLoginIdAsync(id);
|
|
|
|
|
var login = new Login(data, attachments);
|
2017-01-03 08:17:15 +03:00
|
|
|
|
return login;
|
2016-05-06 07:17:38 +03:00
|
|
|
|
}
|
|
|
|
|
|
2017-01-03 08:17:15 +03:00
|
|
|
|
public async Task<IEnumerable<Login>> GetAllAsync()
|
2016-05-06 07:17:38 +03:00
|
|
|
|
{
|
2017-01-03 08:17:15 +03:00
|
|
|
|
var data = await _loginRepository.GetAllByUserIdAsync(_authService.UserId);
|
|
|
|
|
var logins = data.Select(f => new Login(f));
|
|
|
|
|
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)
|
2016-06-15 06:23:05 +03:00
|
|
|
|
{
|
2017-01-03 08:17:15 +03:00
|
|
|
|
var data = await _loginRepository.GetAllByUserIdAsync(_authService.UserId, favorites);
|
|
|
|
|
var logins = data.Select(f => new Login(f));
|
|
|
|
|
return logins;
|
2016-06-15 06:23:05 +03:00
|
|
|
|
}
|
|
|
|
|
|
2017-02-14 03:12:02 +03:00
|
|
|
|
public async Task<Tuple<IEnumerable<Login>, IEnumerable<Login>>> GetAllAsync(string uriString)
|
2017-02-09 07:58:37 +03:00
|
|
|
|
{
|
|
|
|
|
if(string.IsNullOrWhiteSpace(uriString))
|
|
|
|
|
{
|
2017-02-14 03:12:02 +03:00
|
|
|
|
return null;
|
2017-02-09 07:58:37 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Uri uri = null;
|
|
|
|
|
DomainName domainName = null;
|
2017-04-28 19:14:53 +03:00
|
|
|
|
var androidApp = UriIsAndroidApp(uriString);
|
2017-02-09 07:58:37 +03:00
|
|
|
|
|
2017-04-28 19:14:53 +03:00
|
|
|
|
if(!androidApp &&
|
|
|
|
|
(!Uri.TryCreate(uriString, UriKind.Absolute, out uri) || !DomainName.TryParse(uri.Host, out domainName)))
|
2017-02-09 07:58:37 +03:00
|
|
|
|
{
|
2017-02-14 03:12:02 +03:00
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-14 06:10:34 +03:00
|
|
|
|
var androidAppWebUriString = WebUriFromAndroidAppUri(uriString);
|
2017-02-09 07:58:37 +03:00
|
|
|
|
var eqDomains = (await _settingsService.GetEquivalentDomainsAsync()).Select(d => d.ToArray());
|
2017-02-14 03:12:02 +03:00
|
|
|
|
var matchingDomains = new List<string>();
|
|
|
|
|
var matchingFuzzyDomains = new List<string>();
|
|
|
|
|
foreach(var eqDomain in eqDomains)
|
|
|
|
|
{
|
|
|
|
|
if(androidApp)
|
|
|
|
|
{
|
|
|
|
|
if(Array.IndexOf(eqDomain, uriString) >= 0)
|
|
|
|
|
{
|
|
|
|
|
matchingDomains.AddRange(eqDomain.Select(d => d).ToList());
|
|
|
|
|
}
|
2017-04-28 19:14:53 +03:00
|
|
|
|
else if(androidAppWebUriString != null && Array.IndexOf(eqDomain, androidAppWebUriString) >= 0)
|
2017-02-14 03:12:02 +03:00
|
|
|
|
{
|
|
|
|
|
matchingFuzzyDomains.AddRange(eqDomain.Select(d => d).ToList());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if(Array.IndexOf(eqDomain, domainName.BaseDomain) >= 0)
|
|
|
|
|
{
|
|
|
|
|
matchingDomains.AddRange(eqDomain.Select(d => d).ToList());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-09 07:58:37 +03:00
|
|
|
|
if(!matchingDomains.Any())
|
|
|
|
|
{
|
|
|
|
|
matchingDomains.Add(androidApp ? uriString : domainName.BaseDomain);
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-28 19:14:53 +03:00
|
|
|
|
if(androidApp && androidAppWebUriString != null &&
|
|
|
|
|
!matchingFuzzyDomains.Any() && !matchingDomains.Contains(androidAppWebUriString))
|
2017-02-14 03:12:02 +03:00
|
|
|
|
{
|
|
|
|
|
matchingFuzzyDomains.Add(androidAppWebUriString);
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-09 07:58:37 +03:00
|
|
|
|
var matchingDomainsArray = matchingDomains.ToArray();
|
2017-02-14 03:12:02 +03:00
|
|
|
|
var matchingFuzzyDomainsArray = matchingFuzzyDomains.ToArray();
|
2017-02-09 07:58:37 +03:00
|
|
|
|
var matchingLogins = new List<Login>();
|
2017-02-14 03:12:02 +03:00
|
|
|
|
var matchingFuzzyLogins = new List<Login>();
|
2017-02-09 07:58:37 +03:00
|
|
|
|
var logins = await _loginRepository.GetAllByUserIdAsync(_authService.UserId);
|
|
|
|
|
foreach(var login in logins)
|
|
|
|
|
{
|
|
|
|
|
if(string.IsNullOrWhiteSpace(login.Uri))
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-20 18:23:30 +03:00
|
|
|
|
var loginUriString = new CipherString(login.Uri).Decrypt(login.OrganizationId);
|
2017-02-09 07:58:37 +03:00
|
|
|
|
if(string.IsNullOrWhiteSpace(loginUriString))
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-14 03:12:02 +03:00
|
|
|
|
if(Array.IndexOf(matchingDomainsArray, loginUriString) >= 0)
|
2017-02-09 07:58:37 +03:00
|
|
|
|
{
|
|
|
|
|
matchingLogins.Add(new Login(login));
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2017-02-14 03:12:02 +03:00
|
|
|
|
else if(androidApp && Array.IndexOf(matchingFuzzyDomainsArray, loginUriString) >= 0)
|
|
|
|
|
{
|
|
|
|
|
matchingFuzzyLogins.Add(new Login(login));
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2017-02-14 06:10:34 +03:00
|
|
|
|
else if(!androidApp && Array.IndexOf(matchingDomainsArray, WebUriFromAndroidAppUri(loginUriString)) >= 0)
|
|
|
|
|
{
|
|
|
|
|
matchingFuzzyLogins.Add(new Login(login));
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2017-02-09 07:58:37 +03:00
|
|
|
|
|
|
|
|
|
Uri loginUri;
|
|
|
|
|
DomainName loginDomainName;
|
|
|
|
|
if(!Uri.TryCreate(loginUriString, UriKind.Absolute, out loginUri)
|
|
|
|
|
|| !DomainName.TryParse(loginUri.Host, out loginDomainName))
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(Array.IndexOf(matchingDomainsArray, loginDomainName.BaseDomain) >= 0)
|
|
|
|
|
{
|
|
|
|
|
matchingLogins.Add(new Login(login));
|
|
|
|
|
}
|
2017-02-14 03:12:02 +03:00
|
|
|
|
else if(androidApp && Array.IndexOf(matchingFuzzyDomainsArray, loginDomainName.BaseDomain) >= 0)
|
|
|
|
|
{
|
|
|
|
|
matchingFuzzyLogins.Add(new Login(login));
|
|
|
|
|
}
|
2017-02-09 07:58:37 +03:00
|
|
|
|
}
|
|
|
|
|
|
2017-02-14 03:12:02 +03:00
|
|
|
|
return new Tuple<IEnumerable<Login>, IEnumerable<Login>>(matchingLogins, matchingFuzzyLogins);
|
2017-02-09 07:58:37 +03:00
|
|
|
|
}
|
|
|
|
|
|
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);
|
2016-05-06 07:17:38 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(response.Succeeded)
|
|
|
|
|
{
|
2017-01-03 08:17:15 +03:00
|
|
|
|
var data = new LoginData(response.Result, _authService.UserId);
|
|
|
|
|
if(login.Id == null)
|
2016-05-06 07:17:38 +03:00
|
|
|
|
{
|
2017-01-03 08:17:15 +03:00
|
|
|
|
await _loginRepository.InsertAsync(data);
|
|
|
|
|
login.Id = data.Id;
|
2016-05-06 07:17:38 +03:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2017-01-03 08:17:15 +03:00
|
|
|
|
await _loginRepository.UpdateAsync(data);
|
2016-05-06 07:17:38 +03:00
|
|
|
|
}
|
2016-05-02 09:52:09 +03:00
|
|
|
|
}
|
2017-02-09 07:58:37 +03:00
|
|
|
|
else if(response.StatusCode == System.Net.HttpStatusCode.Forbidden
|
2016-07-20 01:46:39 +03:00
|
|
|
|
|| response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
|
|
|
|
|
{
|
2016-07-20 05:00:28 +03:00
|
|
|
|
MessagingCenter.Send(Application.Current, "Logout", (string)null);
|
2016-07-20 01:46:39 +03:00
|
|
|
|
}
|
2016-05-02 09:52:09 +03:00
|
|
|
|
|
2016-05-06 07:17:38 +03:00
|
|
|
|
return response;
|
2016-05-02 09:52:09 +03:00
|
|
|
|
}
|
2016-05-04 02:49:49 +03:00
|
|
|
|
|
2016-07-02 02:06:07 +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);
|
2016-05-06 07:17:38 +03:00
|
|
|
|
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
|
|
|
|
}
|
2016-07-20 01:46:39 +03:00
|
|
|
|
else if(response.StatusCode == System.Net.HttpStatusCode.Forbidden
|
|
|
|
|
|| response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
|
|
|
|
|
{
|
2016-07-20 05:00:28 +03:00
|
|
|
|
MessagingCenter.Send(Application.Current, "Logout", (string)null);
|
2016-07-20 01:46:39 +03:00
|
|
|
|
}
|
2016-05-04 02:49:49 +03:00
|
|
|
|
|
2016-05-06 07:17:38 +03:00
|
|
|
|
return response;
|
2016-05-04 02:49:49 +03:00
|
|
|
|
}
|
2017-02-14 06:10:34 +03:00
|
|
|
|
|
2017-07-13 06:09:44 +03:00
|
|
|
|
public async Task<byte[]> DownloadAndDecryptAttachmentAsync(SymmetricCryptoKey key, string url)
|
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return _cryptoService.DecryptToBytes(data, key);
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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 bool UriIsAndroidApp(string uriString)
|
|
|
|
|
{
|
|
|
|
|
return uriString.StartsWith(Constants.AndroidAppProtocol);
|
|
|
|
|
}
|
2016-05-02 09:52:09 +03:00
|
|
|
|
}
|
|
|
|
|
}
|