-
Notifications
You must be signed in to change notification settings - Fork 28
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
BIT-2346: Add inline autofill #645
Conversation
Bitwarden code coverageTotal coverage:
|
File | Coverage |
---|---|
BitwardenAutoFillExtension/CredentialProviderViewController.swift | 0.00% |
BitwardenShareExtension/ShareViewController.swift | 0.00% |
BitwardenShared/Core/Autofill/Services/AutofillCredentialService.swift | 95.73% |
BitwardenShared/Core/Platform/Services/ServiceContainer.swift | 97.30% |
BitwardenShared/Core/Platform/Services/StateService.swift | 97.46% |
BitwardenShared/Core/Tools/Repositories/SendRepository.swift | 86.79% |
BitwardenShared/Core/Vault/Repositories/VaultRepository.swift | 96.28% |
BitwardenShared/Core/Vault/Services/VaultTimeoutService.swift | 100.00% |
BitwardenShared/UI/Platform/Application/AppCoordinator.swift | 82.35% |
BitwardenShared/UI/Platform/Application/AppExtensionDelegate.swift | 0.00% |
BitwardenShared/UI/Platform/Application/AppProcessor.swift | 83.69% |
Powered by Slather
Generated by 🚫 Danger
--varattributes same-line | ||
--varattributes preserve |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This rule was conflicting with SwiftLint, but I think SwiftLint has better handing for this since it allows certain attributes (e.g. @Environment
) to be on the same line and others to be on the previous line.
SwiftFormat wanted to change this:
Before | After |
---|---|
@available(iOS 17, *) |
@available(iOS 17, *) var credentialIdentity: (any ASCredentialIdentity)? { |
No New Or Fixed Issues Found |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎉 Awesome job! Some improvements I saw and perhaps it's being done elsewhere but is it possible that incremental updates for the ASCredentialIdentityStore
is missing? I.e. when a cipher is added/modified/removed then it should update the Store.
MAUI sources:
let disableAutoTotpCopy = try await stateService.getDisableAutoTotpCopy() | ||
if !disableAutoTotpCopy, | ||
let totp = cipher.login?.totp { | ||
let codeModel = try await clientService.vault().generateTOTPCode(for: totp, date: nil) | ||
pasteboardService.copy(codeModel.code) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -1024,6 +1031,12 @@ extension DefaultVaultRepository: VaultRepository { | |||
await vaultTimeoutService.remove(userId: userId) | |||
} | |||
|
|||
func repromptRequiredForCipher(id: String) async throws -> Bool { | |||
guard try await stateService.getUserHasMasterPassword() else { return false } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔 I think there's an issue for this, so not sure if the idea is to keep using stateService.getUserHasMasterPassword
and then when the issue is grabbed change all occurrences to use the hash but making a comment just in case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The SDK has a method validatePasswordUserKey
to validate the user's password using the user's key without the master password hash. We use that if the master password hash doesn't exist. I believe that was added for this use case. Wouldn't that work here if the user has a master password but no master password hash?
ios/BitwardenShared/Core/Auth/Repositories/AuthRepository.swift
Lines 717 to 728 in 6904487
func validatePassword(_ password: String) async throws -> Bool { | |
if let passwordHash = try await stateService.getMasterPasswordHash() { | |
return try await clientService.auth().validatePassword(password: password, passwordHash: passwordHash) | |
} else { | |
let encryptionKeys = try await stateService.getAccountEncryptionKeys() | |
guard let encUserKey = encryptionKeys.encryptedUserKey else { throw StateServiceError.noEncUserKey } | |
do { | |
let passwordHash = try await clientService.auth().validatePasswordUserKey( | |
password: password, | |
encryptedUserKey: encUserKey | |
) | |
try await stateService.setMasterPasswordHash(passwordHash) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔 AFAIK it wouldn't work given that you don't have the hash to compare against the password string the user is entering in the prompt and/or the UserKey
may not be based on the password thus you can't compare it. However, I'll ask the SDK team to see if there's something I don't know and that's a valid use case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Talked with the SDK team and they said it should work as you describe but we'd need to test it in order to ensure that's the case. So let's keep it like you did it and if then doesn't work we'll change it. Thank you Matt for the feedback!!
@@ -1196,6 +1196,32 @@ class VaultRepositoryTests: BitwardenTestCase { // swiftlint:disable:this type_b | |||
} | |||
} | |||
|
|||
/// `repromptRequiredForCipher(id:)` returns `true` if reprompt is required for a cipher. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎨 Could a test be added for the case cipher is nil
|
||
/// `repromptForCredentialIfNecessary(for:)` reprompts the user for their master password if | ||
/// reprompt is enabled for the cipher. | ||
func test_repromptForCredentialIfNecessary() throws { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
⛏️ Could a test be added to take into account an error thrown that is logged by the error reporter?
Adding/modifying/removing a cipher does update the store with the current approach. That's handled by the cipher's publisher. It's not using incremental updates though, I wasn't sure how critical that was and this was already getting pretty large. We could consider that as a future enhancement. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great! I agree incremental store can be done in a separate PR as an improvement.
Just noticed some small things
} | ||
|
||
/// `provideCredential(for:)` doesn't copy the cipher's TOTP code if the user doesn't have premium access. | ||
func test_provideCredential_totpCopyNotPremium() async throws { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
⛏️ I think a test should be added when active account doesn't have premium but cipher.organizationUseTotp
is true
func test_doesActiveAccountHavePremium_personalFalse_organizationTrue() async throws { | ||
await subject.addAccount(.fixture(profile: .fixture(hasPremiumPersonally: false))) | ||
try await dataStore.replaceOrganizations([.fixture(usersGetPremium: true)], userId: "1") | ||
let organizations = try await dataStore.fetchAllOrganizations(userId: "1") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔 Can this line be removed?
|
||
/// `doesActiveAccountHavePremium()` with no premium personally and an organization with premium | ||
/// but disabled returns false. | ||
func test_doesActiveAccountHavePremium_personalFalse_organizationTrueDisabled() async throws { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔 Maybe I'm missing them but shouldn't tests be added to test the case where there are organizations but no one matches the current userId
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approving in case this needs to be merged for the beta release and we can improve the tests later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 🎉
🎟️ Tracking
BIT-2346
📔 Objective
Implements inline autofill so that vault items are exposed as autofill suggestions on the keyboard.
📸 Screenshots
RPReplay_Final1717000513.MP4
⏰ Reminders before review
🦮 Reviewer guidelines
:+1:
) or similar for great changes:memo:
) or ℹ️ (:information_source:
) for notes or general info:question:
) for questions:thinking:
) or 💭 (:thought_balloon:
) for more open inquiry that's not quite a confirmed issue and could potentially benefit from discussion:art:
) for suggestions / improvements:x:
) or:warning:
) for more significant problems or concerns needing attention:seedling:
) or ♻️ (:recycle:
) for future improvements or indications of technical debt:pick:
) for minor or nitpick changes