sync org keys and refactors

This commit is contained in:
Kyle Spearrin 2017-04-21 22:33:09 -04:00
parent 439370e25a
commit b26c3d050c
5 changed files with 96 additions and 87 deletions

View file

@ -11,6 +11,7 @@ namespace Bit.App.Abstractions
Task<bool> SyncDeleteFolderAsync(string id, DateTime revisionDate);
Task<bool> SyncDeleteLoginAsync(string id);
Task<bool> SyncSettingsAsync();
Task<bool> SyncProfileAsync();
Task<bool> FullSyncAsync(bool forceSync = false);
Task<bool> FullSyncAsync(TimeSpan syncThreshold, bool forceSync = false);
}

View file

@ -60,11 +60,13 @@ namespace Bit.App.Services
case Enums.PushType.SyncCipherUpdate:
case Enums.PushType.SyncCipherCreate:
var cipherCreateUpdateMessage = values.ToObject<SyncCipherPushNotification>();
if(cipherCreateUpdateMessage.UserId != null && cipherCreateUpdateMessage.UserId != _authService.UserId)
if(cipherCreateUpdateMessage.OrganizationId == null &&
cipherCreateUpdateMessage.UserId != _authService.UserId)
{
break;
}
else if(!_authService.BelongsToOrganization(cipherCreateUpdateMessage.OrganizationId))
else if(cipherCreateUpdateMessage.OrganizationId != null &&
!_authService.BelongsToOrganization(cipherCreateUpdateMessage.OrganizationId))
{
break;
}
@ -89,11 +91,13 @@ namespace Bit.App.Services
break;
case Enums.PushType.SyncLoginDelete:
var loginDeleteMessage = values.ToObject<SyncCipherPushNotification>();
if(loginDeleteMessage.UserId != null && loginDeleteMessage.UserId != _authService.UserId)
if(loginDeleteMessage.OrganizationId == null &&
loginDeleteMessage.UserId != _authService.UserId)
{
break;
}
else if(!_authService.BelongsToOrganization(loginDeleteMessage.OrganizationId))
else if(loginDeleteMessage.OrganizationId != null &&
!_authService.BelongsToOrganization(loginDeleteMessage.OrganizationId))
{
break;
}
@ -116,6 +120,14 @@ namespace Bit.App.Services
}
_syncService.SyncSettingsAsync();
break;
case Enums.PushType.SyncOrgKeys:
var orgKeysMessage = values.ToObject<SyncUserPushNotification>();
if(orgKeysMessage.UserId != _authService.UserId)
{
break;
}
_syncService.SyncProfileAsync();
break;
default:
break;
}

View file

@ -8,6 +8,8 @@ using Bit.App.Models.Api;
using System.Collections.Generic;
using Xamarin.Forms;
using Newtonsoft.Json;
using Bit.App.Models;
using System.Diagnostics;
namespace Bit.App.Services
{
@ -22,6 +24,7 @@ namespace Bit.App.Services
private readonly ILoginRepository _loginRepository;
private readonly ISettingsRepository _settingsRepository;
private readonly IAuthService _authService;
private readonly ICryptoService _cryptoService;
private readonly ISettings _settings;
public SyncService(
@ -34,6 +37,7 @@ namespace Bit.App.Services
ILoginRepository loginRepository,
ISettingsRepository settingsRepository,
IAuthService authService,
ICryptoService cryptoService,
ISettings settings)
{
_cipherApiRepository = cipherApiRepository;
@ -45,6 +49,7 @@ namespace Bit.App.Services
_loginRepository = loginRepository;
_settingsRepository = settingsRepository;
_authService = authService;
_cryptoService = cryptoService;
_settings = settings;
}
@ -60,16 +65,8 @@ namespace Bit.App.Services
SyncStarted();
var cipher = await _cipherApiRepository.GetByIdAsync(id).ConfigureAwait(false);
if(!cipher.Succeeded)
if(!CheckSuccess(cipher))
{
SyncCompleted(false);
if(Application.Current != null && (cipher.StatusCode == System.Net.HttpStatusCode.Forbidden
|| cipher.StatusCode == System.Net.HttpStatusCode.Unauthorized))
{
MessagingCenter.Send(Application.Current, "Logout", (string)null);
}
return false;
}
@ -106,16 +103,8 @@ namespace Bit.App.Services
SyncStarted();
var folder = await _folderApiRepository.GetByIdAsync(id).ConfigureAwait(false);
if(!folder.Succeeded)
if(!CheckSuccess(folder))
{
SyncCompleted(false);
if(Application.Current != null && (folder.StatusCode == System.Net.HttpStatusCode.Forbidden
|| folder.StatusCode == System.Net.HttpStatusCode.Unauthorized))
{
MessagingCenter.Send(Application.Current, "Logout", (string)null);
}
return false;
}
@ -188,16 +177,8 @@ namespace Bit.App.Services
SyncStarted();
var domains = await _settingsApiRepository.GetDomains(false).ConfigureAwait(false);
if(!domains.Succeeded)
if(!CheckSuccess(domains))
{
SyncCompleted(false);
if(Application.Current != null && (domains.StatusCode == System.Net.HttpStatusCode.Forbidden
|| domains.StatusCode == System.Net.HttpStatusCode.Unauthorized))
{
MessagingCenter.Send(Application.Current, "Logout", (string)null);
}
return false;
}
@ -206,6 +187,26 @@ namespace Bit.App.Services
SyncCompleted(true);
return true;
}
public async Task<bool> SyncProfileAsync()
{
if(!_authService.IsAuthenticated)
{
return false;
}
SyncStarted();
var profile = await _accountsApiRepository.GetProfileAsync().ConfigureAwait(false);
if(!CheckSuccess(profile))
{
return false;
}
SyncOrgKeys(profile.Result);
SyncCompleted(true);
return true;
}
public async Task<bool> FullSyncAsync(TimeSpan syncThreshold, bool forceSync = false)
{
@ -234,26 +235,19 @@ namespace Bit.App.Services
SyncStarted();
var now = DateTime.UtcNow;
// Just check profile first to make sure we'll have a success with the API
var profile = await _accountsApiRepository.GetProfileAsync().ConfigureAwait(false);
if(!CheckSuccess(profile))
{
return false;
}
var ciphers = await _cipherApiRepository.GetAsync().ConfigureAwait(false);
var folders = await _folderApiRepository.GetAsync().ConfigureAwait(false);
var domains = await _settingsApiRepository.GetDomains(false).ConfigureAwait(false);
if(!ciphers.Succeeded || !domains.Succeeded)
if(!CheckSuccess(ciphers) || !CheckSuccess(folders) || !CheckSuccess(domains))
{
SyncCompleted(false);
if(Application.Current == null)
{
return false;
}
if(ciphers.StatusCode == System.Net.HttpStatusCode.Forbidden ||
ciphers.StatusCode == System.Net.HttpStatusCode.Unauthorized ||
domains.StatusCode == System.Net.HttpStatusCode.Forbidden ||
domains.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
MessagingCenter.Send(Application.Current, "Logout", (string)null);
}
return false;
}
@ -263,6 +257,7 @@ namespace Bit.App.Services
var loginTask = SyncLoginsAsync(loginsDict);
var folderTask = SyncFoldersAsync(foldersDict);
var domainsTask = SyncDomainsAsync(domains.Result);
SyncOrgKeys(profile.Result);
await Task.WhenAll(loginTask, folderTask, domainsTask).ConfigureAwait(false);
if(folderTask.Exception != null || loginTask.Exception != null || domainsTask.Exception != null)
@ -394,6 +389,29 @@ namespace Bit.App.Services
catch(SQLite.SQLiteException) { }
}
private void SyncOrgKeys(ProfileResponse profile)
{
var orgKeysDict = new Dictionary<string, CryptoKey>();
if(profile.Organizations != null)
{
foreach(var org in profile.Organizations)
{
try
{
var decBytes = _cryptoService.RsaDecryptToBytes(new CipherString(org.Key), null);
orgKeysDict.Add(org.Id, new CryptoKey(decBytes));
}
catch
{
Debug.WriteLine($"Cannot set org key {org.Id}. Decryption failed.");
}
}
}
_cryptoService.OrgKeys = orgKeysDict;
}
private void SyncStarted()
{
if(Application.Current == null)
@ -415,5 +433,23 @@ namespace Bit.App.Services
SyncInProgress = false;
MessagingCenter.Send(Application.Current, "SyncCompleted", successfully);
}
private bool CheckSuccess<T>(ApiResult<T> result)
{
if(!result.Succeeded)
{
SyncCompleted(false);
if(Application.Current != null && (result.StatusCode == System.Net.HttpStatusCode.Forbidden
|| result.StatusCode == System.Net.HttpStatusCode.Unauthorized))
{
MessagingCenter.Send(Application.Current, "Logout", (string)null);
}
return false;
}
return true;
}
}
}

View file

@ -1,39 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6245" systemVersion="13F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="X5k-f2-b5h">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6238"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="gAE-YM-kbH">
<objects>
<viewController id="X5k-f2-b5h" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Y8P-hJ-Z43"/>
<viewControllerLayoutGuide type="bottom" id="9ZL-r4-8FZ"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="yd7-JS-zBw">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" misplaced="YES" image="Icon-60.png" translatesAutoresizingMaskIntoConstraints="NO" id="23">
<rect key="frame" x="270" y="270" width="60" height="60"/>
<rect key="contentStretch" x="0.0" y="0.0" width="0.0" height="0.0"/>
</imageView>
</subviews>
<color key="backgroundColor" red="0.20392156862745098" green="0.59607843137254901" blue="0.85882352941176465" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstItem="23" firstAttribute="centerY" secondItem="yd7-JS-zBw" secondAttribute="centerY" priority="1" id="39"/>
<constraint firstItem="23" firstAttribute="centerX" secondItem="yd7-JS-zBw" secondAttribute="centerX" priority="1" id="41"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="XAI-xm-WK6" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="349" y="339"/>
</scene>
</scenes>
<resources>
<image name="Icon-60.png" width="180" height="180"/>
</resources>
</document>

View file

@ -253,7 +253,6 @@
<BundleResource Include="Resources\Icon-Small.png" />
<BundleResource Include="Resources\Icon-Small%402x.png" />
<BundleResource Include="Resources\Icon-Small%403x.png" />
<InterfaceDefinition Include="Resources\LaunchScreen.storyboard" />
</ItemGroup>
<ItemGroup>
<Reference Include="Acr.Support.iOS, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">