8000 PM-10979: Add remove master password view for key connector by matt-livefront · Pull Request #843 · bitwarden/ios · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

PM-10979: Add remove master password view for key connector #843

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
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
17 changes: 17 additions & 0 deletions BitwardenShared/UI/Auth/AuthCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,8 @@ final class AuthCoordinator: NSObject, // swiftlint:disable:this type_body_lengt
showLoginWithDevice(email: email, type: type, isAuthenticated: isAuthenticated)
case let .masterPasswordHint(username):
showMasterPasswordHint(for: username)
case let .removeMasterPassword(organizationName):
showRemoveMasterPassword(organizationName: organizationName)
case let .selfHosted(region):
showSelfHostedView(delegate: context as? SelfHostedProcessorDelegate, currentRegion: region)
case let .setMasterPassword(organizationIdentifier):
Expand Down Expand Up @@ -524,6 +526,21 @@ final class AuthCoordinator: NSObject, // swiftlint:disable:this type_body_lengt
stackNavigator?.present(navigationController)
}

/// Shows the remove master password screen.
///
/// - Parameter organizationName: The organization's name.
///
private func showRemoveMasterPassword(organizationName: String) {
let processor = RemoveMasterPasswordProcessor(
coordinator: asAnyCoordinator(),
state: RemoveMasterPasswordState(
organizationName: organizationName
)
)
let view = RemoveMasterPasswordView(store: Store(processor: processor))
stackNavigator?.push(view)
}

/// Shows the self-hosted settings view.
///
/// - Parameters:
Expand Down
9 changes: 9 additions & 0 deletions BitwardenShared/UI/Auth/AuthCoordinatorTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,15 @@ class AuthCoordinatorTests: BitwardenTestCase { // swiftlint:disable:this type_b
XCTAssertTrue(navigationController.viewControllers.first is UIHostingController<SelfHostedView>)
}

/// `navigate(to:)` with `.removeMasterPassword` pushes the remove master password view onto the stack navigator.
@MainActor
func test_navigate_removeMasterPassword() throws {
subject.navigate(to: .removeMasterPassword(organizationName: "Example Org"))

XCTAssertTrue(stackNavigator.actions.last?.view is RemoveMasterPasswordView)
XCTAssertEqual(stackNavigator.actions.last?.type, .pushed)
}

/// `navigate(to:)` with `.setMasterPassword` pushes the set master password view onto the stack navigator.
@MainActor
func test_navigate_setMasterPassword() throws {
Expand Down
3 changes: 3 additions & 0 deletions BitwardenShared/UI/Auth/AuthRoute.swift
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ public enum AuthRoute: Equatable {
/// - Parameter username: The username to display on the password hint screen.
case masterPasswordHint(username: String)

/// A route to the remove master password screen.
case removeMasterPassword(organizationName: String)

/// A route to the self-hosted settings view.
///
/// - Parameter currentRegion: The user's region type prior to navigating to the self-hosted view.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// MARK: - RemoveMasterPasswordAction

/// Actions that can be processed by a `RemoveMasterPasswordProcessor`.
///
enum RemoveMasterPasswordAction: Equatable {
/// The continue button was tapped.
case continueFlow
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// MARK: - RemoveMasterPasswordProcessor

/// The processor used to manage state and handle actions for the remove master password screen.
///
class RemoveMasterPasswordProcessor: StateProcessor<RemoveMasterPasswordState, RemoveMasterPasswordAction, Void> {
// MARK: Private Properties

/// The coordinator that handles navigation.
private let coordinator: AnyCoordinator<AuthRoute, AuthEvent>

// MARK: Initialization

/// Creates a new `RemoveMasterPasswordProcessor`.
///
/// - Parameters:
/// - coordinator: The coordinator that handles navigation.
/// - state: The initial state of the processor.
///
init(
coordinator: AnyCoordinator<AuthRoute, AuthEvent>,
state: RemoveMasterPasswordState
) {
self.coordinator = coordinator
super.init(state: state)
}

// MARK: Methods

override func receive(_ action: RemoveMasterPasswordAction) {
switch action {
case .continueFlow:
// TODO: PM-11152 Key connector migration
break
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import XCTest

@testable import BitwardenShared

class RemoveMasterPasswordProcessorTests: BitwardenTestCase {
// MARK: Properties

var coordinator: MockCoordinator<AuthRoute, AuthEvent>!
var subject: RemoveMasterPasswordProcessor!

// MARK: Setup & Teardown

override func setUp() {
super.setUp()

coordinator = MockCoordinator()

subject = RemoveMasterPasswordProcessor(
coordinator: coordinator.asAnyCoordinator(),
state: RemoveMasterPasswordState(
organizationName: "Example Org"
)
)
}

override func tearDown() {
super.tearDown()

coordinator = nil
subject = nil
}

// MARK: Tests

/// `receive(_:)` with `.continueFlow` removes the user's master password and completes auth.
@MainActor
func test_receive_continueFlow() {
subject.receive(.continueFlow)
// TODO: PM-11152 Migrate user and complete auth
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// MARK: - RemoveMasterPasswordState

/// An object that defines the current state of a `RemoveMasterPasswordView`.
///
struct RemoveMasterPasswordState: Equatable {
// MARK: Properties

/// The organization's name.
let organizationName: String
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import SwiftUI

// MARK: - RemoveMasterPasswordView

/// A view that notifies the user that they need to remove their master password.
///
struct RemoveMasterPasswordView: View {
// MARK: Properties

/// The `Store` for this view.
@ObservedObject var store: Store<RemoveMasterPasswordState, RemoveMasterPasswordAction, Void>

// MARK: View

var body: some View {
VStack(spacing: 24) {
Text(Localizations.removeMasterPasswordMessage(store.state.organizationName))

Button(Localizations.continue) {
store.send(.continueFlow)
}
.buttonStyle(.primary())
}
.navigationBar(title: Localizations.removeMasterPassword, titleDisplayMode: .inline)
.scrollView()
}
}

// MARK: - Previews

#if DEBUG
#Preview("RemoveMasterPassword") {
RemoveMasterPasswordView(
store: Store(
processor: StateProcessor(
state: RemoveMasterPasswordState(
organizationName: "Example Org"
)
)
)
)
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import SnapshotTesting
import ViewInspector
import XCTest

@testable import BitwardenShared

class RemoveMasterPasswordViewTests: BitwardenTestCase {
// MARK: Properties

var processor: MockProcessor<RemoveMasterPasswordState, RemoveMasterPasswordAction, Void>!
var subject: RemoveMasterPasswordView!

// MARK: Setup & Teardown

override func setUp() {
super.setUp()

processor = MockProcessor(state: RemoveMasterPasswordState(organizationName: "Example Org"))

subject = RemoveMasterPasswordView(store: Store(processor: processor))
}

override func tearDown() {
super.tearDown()

processor = nil
subject = nil
}

// MARK: Tests

/// Tapping the continue button dispatches the continue flow action.
@MainActor
func test_continue_tap() throws {
let button = try subject.inspect().find(button: Localizations.continue)
try button.tap()
XCTAssertEqual(processor.dispatchedActions.last, .continueFlow)
}

// MARK: Snapshots

/// The remove master password view renders correctly.
@MainActor
func test_snapshot_removeMasterPassword() {
assertSnapshots(
of: subject.navStackWrapped,
as: [.defaultPortrait, .defaultPortraitDark, .tallPortraitAX5(heightMultiple: 1.5)]
)
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -944,3 +944,4 @@
"SetUpUnlock" = "Set up unlock";
"SetUpBiometricsOrChooseAPinCodeToQuicklyAccessYourVaultAndAutofillYourLogins" = "Set up biometrics or choose a PIN code to quickly access your vault and autofill your logins.";
"SetUpLater" = "Set up later";
"RemoveMasterPasswordMessage" = "%1$@ is using SSO with a self-hosted key server. A master password is no longer required to log in for members of this organization.";
Loading
0