8000 [BITAU-174] [BITAU-151] Remove shared items and key when a user disables sync by brant-livefront · Pull Request #1004 · bitwarden/ios · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

[BITAU-174] [BITAU-151] Remove shared items and key when a user disables sync #1004

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
95 commits
Select commit Hold shift + click to select a range
ce280a3
[BITAU-171] Rename Package to AuthenticatorBridgeKit
brant-livefront Sep 12, 2024
74212c6
Added Initial CoreData structure and tests
brant-livefront Sep 12, 2024
acf1c5e
Merge branch 'main' into brant/create-coredata-store
brant-livefront Sep 12, 2024
7838129
Merge branch 'main' into brant/create-coredata-store
brant-livefront Sep 13, 2024
61163d7
Updated tests to match the style guide, implemented additional tests
brant-livefront Sep 13, 2024
d0ad4c9
Remove cryptography serivce (for now). Clean up comments and other li…
brant-livefront Sep 13, 2024
cf4e610
Moved to using a more direct batvh insert. Cleaned up tests. Cleaned …
brant-livefront Sep 16, 2024
1bcf93a
Merge branch 'main' into brant/create-coredata-store
brant-livefront Sep 16, 2024
4a15366
]BITAU-122] [BITAU-133] Add encryption to/from shared the CoreData store
brant-livefront Sep 16, 2024
1dc0757
Added name to list of encrypted pieces of the data model
brant-livefront Sep 16, 2024
0113798
Merge branch 'main' into brant/create-coredata-store
brant-livefront Sep 16, 2024
603c70b
Merge branch 'brant/create-coredata-store' into brant/add-encryption-…
brant-livefront Sep 16, 2024
fd6e158
Merge branch 'main' into brant/create-coredata-store
brant-livefront Sep 17, 2024
f78ab0a
Address comments from PR review
brant-livefront Sep 17, 2024
3d1beda
Merged in latest from brant/create-coredata-store - reverted all chan…
brant-livefront Sep 17, 2024
f68fac0
First step of refactor - move convenience methods out to new service.…
brant-livefront Sep 17, 2024
15add85
Pull in reusable CoreData code from main project. Refactor to adopt P…
brant-livefront Sep 17, 2024
18c014c
Reuse model name for sqlite reference as well
brant-livefront Sep 17, 2024
7ed13ae
Merge branch 'main' into brant/create-coredata-store
brant-livefront Sep 17, 2024
1c5d2b3
Merge branch 'main' into brant/create-coredata-store
brant-livefront Sep 17, 2024
cef0741
Merged in latest from brant/create-coredata-store fixed conflicts
brant-livefront Sep 17, 2024
3d0c1fb
Implement encryption in the new item service
brant-livefront Sep 17, 2024
af008bc
[BITAU-149] Add Setting to Turn On Authenticator Syncing for an Account
brant-livefront Sep 18, 2024
ee63c9d
[BITAU-149] Add Setting to Turn On Authenticator Syncing for an Account
brant-livefront Sep 18, 2024
3e5dd06
[BITAU-148] First pass at adding the sync service to the PM app
brant-livefront Sep 18, 2024
47a98d2
Added tests for all of the new Settings pieces
brant-livefront Sep 18, 2024
1fa2fbc
Merge branch 'brant/add-authenticator-sync-setting' into brant/add-pm…
brant-livefront Sep 18, 2024
f4fbc16
Checking in progress on tests and subscribing to sync setting updates
brant-livefront Sep 19, 2024
3e8508e
Merged in latest from main, fixed conflicts
brant-livefront Sep 19, 2024
7efb7f3
Merge in latest from main
brant-livefront Sep 19, 2024
3ede938
Implement encryption in the new item service
brant-livefront Sep 17, 2024
d83f2e2
Fix missing declaration
brant-livefront Sep 19, 2024
0c3167e
Cleaned up merge issues
brant-livefront Sep 19, 2024
6bca7ab
Added doc comment for new param
brant-livefront Sep 19, 2024
cb3fa10
Merge branch 'brant/add-encryption-to-coredata-store' into brant/add-…
brant-livefront Sep 19, 2024
12f399b
Merge branch 'brant/add-authenticator-sync-setting' into brant/add-pm…
brant-livefront Sep 19, 2024
605fc3b
Merge branch 'main' into brant/add-encryption-to-coredata-store
brant-livefront Sep 19, 2024
3ecc76a
Further tests and progress
brant-livefront Sep 19, 2024
f7ed8d7
Merge branch 'main' into brant/add-encryption-to-coredata-store
brant-livefront Sep 19, 2024
c165ba7
Added new AuthenticatorBridgeItemDataView to differentiate unencrypte…
brant-livefront Sep 20, 2024
6441c3e
Merge branch 'brant/add-encryption-to-coredata-store' into brant/add-…
brant-livefront Sep 20, 2024
aa5987e
Merge branch 'brant/add-authenticator-sync-setting' into brant/add-pm…
brant-livefront Sep 20, 2024
7122d7d
Fix typo in doc comment
brant-livefront Sep 20, 2024
48b9db6
Merge branch 'brant/add-encryption-to-coredata-store' into brant/add-…
brant-livefront Sep 20, 2024
40713f6
Merge branch 'brant/add-encryption-to-coredata-store' into brant/add-…
brant-livefront Sep 20, 2024
5236618
Merge branch 'brant/add-authenticator-sync-setting' into brant/add-pm…
brant-livefront Sep 20, 2024
e129b11
[BITAU-149] Add Setting to Turn On Authenticator Syncing for an Account
brant-livefront Sep 20, 2024
d815124
Updated doc comments
brant-livefront Sep 20, 2024
80f4c20
Merge in latest; fix conflicts
brant-livefront Sep 20, 2024
1462e77
Added tests for most of the cases in the sync service
brant-livefront Sep 23, 2024
19f971f
Updated tests for 100% coverage
brant-livefront Sep 23, 2024
4012c32
Doc comment cleanup
brant-livefront Sep 23, 2024
66a74d2
Update to use MainActor so that Application returns the correct value…
brant-livefront Sep 23, 2024
b9f0646
Pulling application out of the SyncService entirely. Was too unreliab…
brant-livefront Sep 23, 2024
dd969c7
Merge branch 'main' into brant/add-authenticator-sync-setting
brant-livefront Sep 24, 2024
13162c0
Respond to PR feedback
brant-livefront Sep 24, 2024
011eebb
Merge branch 'main' into brant/add-authenticator-sync-setting
brant-livefront Sep 24, 2024
ed19b6c
Updated to use assert true with == operator
brant-livefront Sep 24, 2024
cc79977
Added cancel before creating new settings task
brant-livefront Sep 24, 2024
c27e92a
Added start() method to allow ServiceContainer a way to explicitly st…
brant-livefront Sep 24, 2024
aac820e
Add sharedItemsPublisher to BridgeItemService
brant-livefront Sep 25, 2024
8f2bdf8
Added more tests, fixed issue with Future extension not being present…
brant-livefront Sep 25, 2024
329d4de
Merge branch 'main' into brant/add-authenticator-sync-setting
brant-livefront Sep 25, 2024
70cc033
Merge branch 'brant/add-authenticator-sync-setting' into brant/add-pm…
brant-livefront Sep 25, 2024
74c1de8
Reverted to XCTAssertEqual
brant-livefront Sep 25, 2024
b3bdf51
Merge branch 'brant/add-authenticator-sync-setting' into brant/add-pm…
brant-livefront Sep 25, 2024
75021cc
Changed to remove AsyncThrowingPublisher wrapper, which makes things …
brant-livefront Sep 26, 2024
e3df37a
Merge branch 'main' into brant/add-pm-sync-service
brant-livefront Sep 26, 2024
96c7cc5
Merge branch 'main' into brant/add-pm-sync-service
brant-livefront Sep 26, 2024
774def9
Added test coverage for new Cryptography method
brant-livefront Sep 26, 2024
e2e99ed
Incorporate suggestions from PR feedback
brant-livefront Sep 27, 2024
26b3952
Fixed typo
brant-livefront Sep 27, 2024
a099e89
Removed debug logs from code; fixed concurrnecy issue with tests
brant-livefront Sep 27, 2024
c3c7902
Merge branch 'main' into brant/add-pm-sync-service
brant-livefront Sep 30, 2024
eb567e1
Repsond to PR feedback
brant-livefront Oct 1, 2024
fad1b70
Merge branch 'main' into brant/add-pm-sync-service
brant-livefront Oct 1, 2024
d7a1fd3
Update to support new MainActor requirement for configService
brant-livefront Oct 1, 2024
d5ed7c4
Merge branch 'main' into brant/add-pm-sync-service
brant-livefront Oct 1, 2024
41f0b1a
Merge branch 'main' into brant/add-pm-sync-service
brant-livefront Oct 1, 2024
79cc61b
Added new approach to Vault unlocking and new tests.
brant-livefront Oct 2, 2024
7a6e7ee
Merge in latest from main, fix conflicts
brant-livefront Oct 2, 2024
162b1a4
Cleaned up tests, fixed bug that tests revealed
brant-livefront Oct 3, 2024
8b4efe8
Merge branch 'main' into brant/add-vault-key-handling
brant-livefront Oct 3, 2024
019ba96
Clean up doc comments
brant-livefront Oct 3, 2024
cb2fd64
Fixed test for feature flag off
brant-livefront Oct 3, 2024
f877aa3
Removed property references to the two long-running tasks, per PR sug…
brant-livefront Oct 3, 2024
c17da1d
Merge branch 'main' into brant/BITAU-152-handle-vault-lock-unlock
brant-livefront Oct 3, 2024
40638c4
[BITAU-174] [BITAU-151] Remove shared items and key when a user disab…
brant-livefront Oct 3, 2024
73ea91a
Respond to PR feedback
brant-livefront Oct 4, 2024
8c11ece
Upgraded test coverage to verify lots of different error scenarios
brant-livefront Oct 4, 2024
1a1fc5d
Merge branch 'main' into brant/BITAU-152-handle-vault-lock-unlock
brant-livefront Oct 4, 2024
5735a98
Respond to PR feedback - ensure Vault is unlocked when decrypting
brant-livefront Oct 7, 2024
3346c96
Merge branch 'main' into brant/BITAU-152-handle-vault-lock-unlock
brant-livefront Oct 7, 2024
982993b
Merge in latest from brant/BITAU-152-handle-vault-lock-unlock fix mer…
brant-livefront Oct 7, 2024
1615d2e
Merged in latest from main; fixed conflicts
brant-livefront Oct 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -174,22 +174,37 @@ actor DefaultAuthenticatorSyncService: NSObject, AuthenticatorSyncService {
}
}

/// If sync has been turned off for all accounts, delete the Authenticator key from the shared keychain.
///
private func deleteKeyIfSyncingIsOff() async throws {
for account in try await stateService.getAccounts() {
let hasAccountWithSync = try await stateService.getSyncToAuthenticator(userId: account.profile.userId)
guard !hasAccountWithSync else {
return
}
}
try sharedKeychainRepository.deleteAuthenticatorKey()
}

/// Determine if the given userId has sync turned on and an unlocked vault. This method serves as the
/// integration point of both the sync settings subscriber and the vault subscriber. When the user has sync turned
/// on and the vault unlocked, we can proceed with the sync.
///
/// - Parameter userId: The userId of the user whose sync status is being determined.
///
private func determineSyncForUserId(_ userId: String) async throws {
guard try await stateService.getSyncToAuthenticator(userId: userId),
!vaultTimeoutService.isLocked(userId: userId) else {
if try await !stateService.getSyncToAuthenticator(userId: userId) {
cipherPublisherTasks[userId]?.cancel()
cipherPublisherTasks.removeValue(forKey: userId)
return
try await authBridgeItemService.deleteAllForUserId(userId)
try await deleteKeyIfSyncingIsOff()
} else if vaultTimeoutService.isLocked(userId: userId) {
cipherPublisherTasks[userId]?.cancel()
cipherPublisherTasks.removeValue(forKey: userId)
} else {
try await createAuthenticatorKeyIfNeeded()
subscribeToCipherUpdates(userId: userId)
}

try await createAuthenticatorKeyIfNeeded()
subscribeToCipherUpdates(userId: userId)
}

/// Create a task for the given userId to listen for Cipher updates and sync to the Authenticator store.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,10 +206,107 @@ final class AuthenticatorSyncServiceTests: BitwardenTestCase { // swiftlint:disa
XCTAssertEqual(item.username, "user@bitwarden.com")
}

/// Verifies that the AuthSyncService handles and reports errors when sync is turned On..
/// Verifies that the AuthSyncService handles an error when attempting to fetch the accounts to check
/// if any are left with sync.
///
@MainActor
func test_determineSyncForUserId_error() async throws {
func test_deleteKeyIfSyncingIsOff_errorFetchingAccounts() async throws {
setupInitialState()
await subject.start()
stateService.syncToAuthenticatorSubject.send(("1", true))

waitFor(sharedKeychainRepository.authenticatorKey != nil)

stateService.accounts = nil
stateService.syncToAuthenticatorByUserId["1"] = false
stateService.syncToAuthenticatorSubject.send(("1", false))

waitFor(!errorReporter.errors.isEmpty)
}

/// Verifies that the AuthSyncService handles a keychain error when attempting to remove the Authenticator key.
///
@MainActor
func test_deleteKeyIfSyncingIsOff_errorInKeychain() async throws {
setupInitialState()
await subject.start()
stateService.syncToAuthenticatorSubject.send(("1", true))

waitFor(sharedKeychainRepository.authenticatorKey != nil)

sharedKeychainRepository.errorToThrow = BitwardenTestError.example
stateService.syncToAuthenticatorByUserId["1"] = false
stateService.syncToAuthenticatorSubject.send(("1", false))

waitFor(!errorReporter.errors.isEmpty)
}

/// Verifies that the AuthSyncService removes the Authenticator key when the last account to sync is turned off.
///
@MainActor
func test_deleteKeyIfSyncingIsOff_lastAccountSyncTurnedOff() async throws {
setupInitialState()
await subject.start()
stateService.syncToAuthenticatorSubject.send(("1", true))

waitFor(sharedKeychainRepository.authenticatorKey != nil)
stateService.syncToAuthenticatorByUserId["1"] = false
stateService.syncToAuthenticatorSubject.send(("1", false))

waitFor(sharedKeychainRepository.authenticatorKey == nil)
}

/// Verifies that the AuthSyncService does not removes the Authenticator key there are still
/// accounts with sync is turned on.
///
@MainActor
func test_deleteKeyIfSyncingIsOff_notLastAccount() async throws {
setupInitialState()
stateService.accounts?.append(.fixture(profile: .fixture(userId: "2")))
stateService.syncToAuthenticatorByUserId["2"] = true
await subject.start()
stateService.syncToAuthenticatorSubject.send(("1", true))
waitFor(sharedKeychainRepository.authenticatorKey != nil)

stateService.syncToAuthenticatorByUserId["1"] = false
stateService.syncToAuthenticatorSubject.send(("1", false))
try await Task.sleep(nanoseconds: 10_000_000)

XCTAssertNotNil(sharedKeychainRepository.authenticatorKey)
}

/// Verifies that the AuthSyncService handles and reports errors when sync is turned off and the
/// service attempts to delete this account's items from the Store.
///
@MainActor
func test_determineSyncForUserId_errorFromDeleteAllItems() async throws {
setupInitialState()
await subject.start()

authBridgeItemService.errorToThrow = BitwardenTestError.example
stateService.syncToAuthenticatorByUserId["1"] = false
stateService.syncToAuthenticatorSubject.send(("1", false))
waitFor(!errorReporter.errors.isEmpty)
}

/// Verifies that the AuthSyncService handles and reports errors when and there is an error
/// thrown while accessing the sync setting for the account.
///
@MainActor
func test_determineSyncForUserId_errorFromFetchingSyncSetting() async throws {
setupInitialState()
await subject.start()

stateService.syncToAuthenticatorResult = .failure(BitwardenTestError.example)
stateService.syncToAuthenticatorSubject.send(("1", true)) 67F4
waitFor(!errorReporter.errors.isEmpty)
}

/// Verifies that the AuthSyncService handles and reports errors when sync is turned On and the
/// keychain throws an error.
///
@MainActor
func test_determineSyncForUserId_errorFromKeychain() async throws {
setupInitialState()
await subject.start()
sharedKeychainRepository.errorToThrow = BitwardenTestError.example
Expand All @@ -232,14 +329,33 @@ final class AuthenticatorSyncServiceTests: BitwardenTestCase { // swiftlint:disa
waitFor(!errorReporter.errors.isEmpty)
}

/// Verifies that the AuthSyncService stops listening for Cipher updates when the user has sync turned off.
/// Verifies that the AuthSyncService stops listening for Cipher updates and removes all data in the shared store
/// for a user when the user has sync turned off.
///
@MainActor
func test_determineSyncForUserId_syncOff() async throws {
func test_determineSyncForUserId_syncTurnedOff() async throws {
setupInitialState()
await subject.start()

// Send initial updates, record in Store
stateService.syncToAuthenticatorSubject.send(("1", true))
cipherDataStore.cipherSubjectByUserId["1"]?.send([
.fixture(
id: "1234",
login: .fixture(
username: "user@bitwarden.com",
totp: "totp"
)
),
])
waitFor(authBridgeItemService.storedItems["1"]?.first != nil)

// Unsubscribe from sync, wait for items to be deleted
stateService.syncToAuthenticatorByUserId["1"] = false
stateService.syncToAuthenticatorSubject.send(("1", false))
waitFor((authBridgeItemService.storedItems["1"]?.isEmpty) ?? false)

// Sending additional updates should not appear in Store
cipherDataStore.cipherSubjectByUserId["1"]?.send([
.fixture(
id: "1234",
Expand All @@ -250,9 +366,8 @@ final class AuthenticatorSyncServiceTests: BitwardenTestCase { // swiftlint:disa
),
])

try await Task.sleep(nanoseconds: 100_000_000)

XCTAssertFalse(authBridgeItemService.replaceAllCalled)
try await Task.sleep(nanoseconds: 10_000_000)
XCTAssertTrue(authBridgeItemService.storedItems["1"]?.isEmpty ?? false)
}

/// When user "1" has sync turned on and user "2" unlocks their vault, the service should not take
Expand Down Expand Up @@ -498,6 +613,7 @@ final class AuthenticatorSyncServiceTests: BitwardenTestCase { // swiftlint:disa
cipherDataStore.cipherSubjectByUserId["1"] = CurrentValueSubject<[Cipher], Error>([])
configService.featureFlagsBool[.enableAuthenticatorSync] = true
stateService.activeAccount = .fixture()
stateService.accounts = [.fixture()]
stateService.syncToAuthenticatorByUserId["1"] = syncOn
vaultTimeoutService.isClientLocked["1"] = vaultLocked
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,24 @@ import BitwardenShared
import Combine

class MockAuthenticatorBridgeItemService: AuthenticatorBridgeItemService {
var errorToThrow: Error?
var replaceAllCalled = false
var sharedItemsPublisherError: Error?
var sharedItemsSubject = CurrentValueSubject<[AuthenticatorBridgeItemDataView], Error>([])
var storedItems: [String: [AuthenticatorBridgeItemDataView]] = [:]
var syncOn = false

func deleteAllForUserId(_ userId: String) async throws {
guard errorToThrow == nil else { throw errorToThrow! }
storedItems[userId] = []
}

func fetchAllForUserId(_ userId: String) async throws -> [AuthenticatorBridgeItemDataView] {
storedItems[userId] ?? []
guard errorToThrow == nil else { throw errorToThrow! }
return storedItems[userId] ?? []
}

func insertItems(_ items: [AuthenticatorBridgeItemDataView], forUserId userId: String) async throws {
guard errorToThrow == nil else { throw errorToThrow! }
storedItems[userId] = items
}

Expand All @@ -26,15 +29,15 @@ class MockAuthenticatorBridgeItemService: AuthenticatorBridgeItemService {
}

func replaceAllItems(with items: [AuthenticatorBridgeItemDataView], forUserId userId: String) async throws {
guard errorToThrow == nil else { throw errorToThrow! }
storedItems[userId] = items
replaceAllCalled = true
}

func sharedItemsPublisher() async throws ->
AnyPublisher<[AuthenticatorBridgeKit.AuthenticatorBridgeItemDataView], any Error> {
if let sharedItemsPublisherError {
throw sharedItemsPublisherError
}
guard errorToThrow == nil else { throw errorToThrow! }

return sharedItemsSubject.eraseToAnyPublisher()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ extension MockSharedKeychainRepository: SharedKeychainRepository {
}

func deleteAuthenticatorKey() throws {
guard errorToThrow == nil else { throw errorToThrow! }

authenticatorKey = nil
}

Expand Down
Loading
0