Refactor sign in policy (#1085)
This commit is contained in:
parent
9ebcbe9728
commit
645eb6c516
|
@ -15,7 +15,7 @@ final class UserSignInCoordinator: NavigationCoordinatable {
|
|||
|
||||
struct SecurityParameters {
|
||||
let pinHint: Binding<String>
|
||||
let signInPolicy: Binding<UserAccessPolicy>
|
||||
let accessPolicy: Binding<UserAccessPolicy>
|
||||
}
|
||||
|
||||
let stack = NavigationStack(initial: \UserSignInCoordinator.start)
|
||||
|
@ -48,7 +48,7 @@ final class UserSignInCoordinator: NavigationCoordinatable {
|
|||
NavigationViewCoordinator {
|
||||
UserSignInView.SecurityView(
|
||||
pinHint: parameters.pinHint,
|
||||
signInPolicy: parameters.signInPolicy
|
||||
accessPolicy: parameters.accessPolicy
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ extension StoredValues.Keys {
|
|||
|
||||
enum Temp {
|
||||
|
||||
static let userSignInPolicy: Key<UserAccessPolicy> = TempKey(
|
||||
static let userAccessPolicy: Key<UserAccessPolicy> = TempKey(
|
||||
"userSignInPolicy",
|
||||
ownerID: "temporary",
|
||||
domain: "userSignInPolicy",
|
||||
|
|
|
@ -72,8 +72,7 @@ extension UserState {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: rename to accessPolicy and fix all uses
|
||||
var signInPolicy: UserAccessPolicy {
|
||||
var accessPolicy: UserAccessPolicy {
|
||||
get {
|
||||
StoredValues[.User.accessPolicy(id: id)]
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ class SelectUserViewModel: ViewModel, Eventful, Stateful {
|
|||
}
|
||||
case let .signIn(user, pin):
|
||||
|
||||
if user.signInPolicy == .requirePin, let storedPin = keychain.get("\(user.id)-pin") {
|
||||
if user.accessPolicy == .requirePin, let storedPin = keychain.get("\(user.id)-pin") {
|
||||
if pin != storedPin {
|
||||
eventSubject.send(.error(.init("Incorrect pin for \(user.username)")))
|
||||
|
||||
|
|
|
@ -31,7 +31,7 @@ class UserLocalSecurityViewModel: ViewModel, Eventful {
|
|||
// Will throw and send event if needing to prompt for old auth.
|
||||
func checkForOldPolicy() throws {
|
||||
|
||||
let oldPolicy = userSession.user.signInPolicy
|
||||
let oldPolicy = userSession.user.accessPolicy
|
||||
|
||||
switch oldPolicy {
|
||||
case .requireDeviceAuthentication:
|
||||
|
@ -75,7 +75,7 @@ class UserLocalSecurityViewModel: ViewModel, Eventful {
|
|||
keychain.delete(StoredValues[.Temp.userLocalPin])
|
||||
}
|
||||
|
||||
userSession.user.signInPolicy = newPolicy
|
||||
userSession.user.accessPolicy = newPolicy
|
||||
userSession.user.pinHint = newPinHint
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,7 +92,7 @@ final class UserSignInViewModel: ViewModel, Eventful, Stateful {
|
|||
guard let self else { return }
|
||||
|
||||
Task {
|
||||
await self.send(.signInQuickConnect(secret: secret, policy: StoredValues[.Temp.userSignInPolicy]))
|
||||
await self.send(.signInQuickConnect(secret: secret, policy: StoredValues[.Temp.userAccessPolicy]))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -236,7 +236,7 @@ final class UserSignInViewModel: ViewModel, Eventful, Stateful {
|
|||
}
|
||||
|
||||
StoredValues[.Temp.userData] = userData
|
||||
StoredValues[.Temp.userSignInPolicy] = policy
|
||||
StoredValues[.Temp.userAccessPolicy] = policy
|
||||
|
||||
let newState = UserState(
|
||||
id: id,
|
||||
|
@ -263,7 +263,7 @@ final class UserSignInViewModel: ViewModel, Eventful, Stateful {
|
|||
}
|
||||
|
||||
StoredValues[.Temp.userData] = userData
|
||||
StoredValues[.Temp.userSignInPolicy] = policy
|
||||
StoredValues[.Temp.userAccessPolicy] = policy
|
||||
|
||||
let newState = UserState(
|
||||
id: id,
|
||||
|
@ -304,13 +304,13 @@ final class UserSignInViewModel: ViewModel, Eventful, Stateful {
|
|||
}
|
||||
|
||||
user.data = StoredValues[.Temp.userData]
|
||||
user.signInPolicy = StoredValues[.Temp.userSignInPolicy]
|
||||
user.accessPolicy = StoredValues[.Temp.userAccessPolicy]
|
||||
|
||||
keychain.set(StoredValues[.Temp.userLocalPin], forKey: "\(user.id)-pin")
|
||||
user.pinHint = StoredValues[.Temp.userLocalPinHint]
|
||||
|
||||
// TODO: remove when implemented periodic cleanup elsewhere
|
||||
StoredValues[.Temp.userSignInPolicy] = .none
|
||||
StoredValues[.Temp.userAccessPolicy] = .none
|
||||
StoredValues[.Temp.userLocalPin] = ""
|
||||
StoredValues[.Temp.userLocalPinHint] = ""
|
||||
}
|
||||
|
|
|
@ -139,7 +139,7 @@ struct SelectUserView: View {
|
|||
Task { @MainActor in
|
||||
selectedUsers.insert(user)
|
||||
|
||||
switch user.signInPolicy {
|
||||
switch user.accessPolicy {
|
||||
case .requireDeviceAuthentication:
|
||||
try await performDeviceAuthentication(reason: "User \(user.username) requires device authentication")
|
||||
case .requirePin:
|
||||
|
|
|
@ -158,7 +158,7 @@ struct UserLocalSecurityView: View {
|
|||
.navigationBarTitleDisplayMode(.inline)
|
||||
.onFirstAppear {
|
||||
pinHint = viewModel.userSession.user.pinHint
|
||||
signInPolicy = viewModel.userSession.user.signInPolicy
|
||||
signInPolicy = viewModel.userSession.user.accessPolicy
|
||||
}
|
||||
.onReceive(viewModel.events) { event in
|
||||
switch event {
|
||||
|
@ -210,7 +210,7 @@ struct UserLocalSecurityView: View {
|
|||
checkOldPolicy()
|
||||
} label: {
|
||||
Group {
|
||||
if signInPolicy == .requirePin, signInPolicy == viewModel.userSession.user.signInPolicy {
|
||||
if signInPolicy == .requirePin, signInPolicy == viewModel.userSession.user.accessPolicy {
|
||||
Text("Change Pin")
|
||||
} else {
|
||||
Text("Save")
|
||||
|
|
|
@ -20,7 +20,7 @@ extension UserSignInView {
|
|||
@Binding
|
||||
private var pinHint: String
|
||||
@Binding
|
||||
private var signInPolicy: UserAccessPolicy
|
||||
private var accessPolicy: UserAccessPolicy
|
||||
|
||||
@State
|
||||
private var listSize: CGSize = .zero
|
||||
|
@ -31,12 +31,12 @@ extension UserSignInView {
|
|||
|
||||
init(
|
||||
pinHint: Binding<String>,
|
||||
signInPolicy: Binding<UserAccessPolicy>
|
||||
accessPolicy: Binding<UserAccessPolicy>
|
||||
) {
|
||||
self._pinHint = pinHint
|
||||
self._signInPolicy = signInPolicy
|
||||
self._accessPolicy = accessPolicy
|
||||
self._updatePinHint = State(initialValue: pinHint.wrappedValue)
|
||||
self._updateSignInPolicy = State(initialValue: signInPolicy.wrappedValue)
|
||||
self._updateSignInPolicy = State(initialValue: accessPolicy.wrappedValue)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
|
@ -82,7 +82,7 @@ extension UserSignInView {
|
|||
}
|
||||
}
|
||||
|
||||
if signInPolicy == .requirePin {
|
||||
if accessPolicy == .requirePin {
|
||||
Section {
|
||||
TextField("Hint", text: $updatePinHint)
|
||||
} header: {
|
||||
|
@ -92,7 +92,7 @@ extension UserSignInView {
|
|||
}
|
||||
}
|
||||
}
|
||||
.animation(.linear, value: signInPolicy)
|
||||
.animation(.linear, value: accessPolicy)
|
||||
.navigationTitle("Security")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationBarCloseButton {
|
||||
|
@ -107,7 +107,7 @@ extension UserSignInView {
|
|||
pinHint = newValue
|
||||
}
|
||||
.onChange(of: updateSignInPolicy) { newValue in
|
||||
signInPolicy = newValue
|
||||
accessPolicy = newValue
|
||||
}
|
||||
.trackingSize($listSize)
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ struct UserSignInView: View {
|
|||
@State
|
||||
private var pinHint: String = ""
|
||||
@State
|
||||
private var signInPolicy: UserAccessPolicy = .none
|
||||
private var accessPolicy: UserAccessPolicy = .none
|
||||
@State
|
||||
private var username: String = ""
|
||||
|
||||
|
@ -58,11 +58,32 @@ struct UserSignInView: View {
|
|||
self._viewModel = StateObject(wrappedValue: UserSignInViewModel(server: server))
|
||||
}
|
||||
|
||||
private func handleSignIn(_ event: UserSignInViewModel.Event) {
|
||||
switch event {
|
||||
case let .duplicateUser(duplicateUser):
|
||||
UIDevice.impact(.medium)
|
||||
|
||||
self.duplicateUser = duplicateUser
|
||||
isPresentingDuplicateUser = true
|
||||
case let .error(eventError):
|
||||
UIDevice.feedback(.error)
|
||||
|
||||
error = eventError
|
||||
isPresentingError = true
|
||||
case let .signedIn(user):
|
||||
UIDevice.feedback(.success)
|
||||
|
||||
Defaults[.lastSignedInUserID] = user.id
|
||||
UserSession.current.reset()
|
||||
Notifications[.didSignIn].post()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: don't have multiple ways to handle device authentication vs required pin
|
||||
|
||||
private func openQuickConnect(needsPin: Bool = true) {
|
||||
Task {
|
||||
switch signInPolicy {
|
||||
switch accessPolicy {
|
||||
case .none: ()
|
||||
case .requireDeviceAuthentication:
|
||||
try await performDeviceAuthentication(
|
||||
|
@ -84,27 +105,27 @@ struct UserSignInView: View {
|
|||
|
||||
private func signInUserPassword(needsPin: Bool = true) {
|
||||
Task {
|
||||
switch signInPolicy {
|
||||
switch accessPolicy {
|
||||
case .none: ()
|
||||
case .requireDeviceAuthentication:
|
||||
try await performDeviceAuthentication(reason: "Require device authentication to sign in to \(username) on this device")
|
||||
case .requirePin:
|
||||
if needsPin {
|
||||
onPinCompletion = {
|
||||
viewModel.send(.signIn(username: username, password: password, policy: signInPolicy))
|
||||
viewModel.send(.signIn(username: username, password: password, policy: accessPolicy))
|
||||
}
|
||||
isPresentingLocalPin = true
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.send(.signIn(username: username, password: password, policy: signInPolicy))
|
||||
viewModel.send(.signIn(username: username, password: password, policy: accessPolicy))
|
||||
}
|
||||
}
|
||||
|
||||
private func signInUplicate(user: UserState, needsPin: Bool = true, replace: Bool) {
|
||||
Task {
|
||||
switch user.signInPolicy {
|
||||
switch user.accessPolicy {
|
||||
case .none: ()
|
||||
case .requireDeviceAuthentication:
|
||||
try await performDeviceAuthentication(reason: "User \(user.username) requires device authentication")
|
||||
|
@ -185,7 +206,7 @@ struct UserSignInView: View {
|
|||
} header: {
|
||||
Text(L10n.signInToServer(viewModel.server.name))
|
||||
} footer: {
|
||||
switch signInPolicy {
|
||||
switch accessPolicy {
|
||||
case .requireDeviceAuthentication:
|
||||
HStack {
|
||||
Image(systemName: "exclamationmark.circle.fill")
|
||||
|
@ -298,30 +319,13 @@ struct UserSignInView: View {
|
|||
.onChange(of: pinHint) { newValue in
|
||||
StoredValues[.Temp.userLocalPinHint] = newValue
|
||||
}
|
||||
.onChange(of: signInPolicy) { newValue in
|
||||
.onChange(of: accessPolicy) { newValue in
|
||||
// necessary for Quick Connect sign in, but could
|
||||
// just use for general sign in
|
||||
StoredValues[.Temp.userSignInPolicy] = newValue
|
||||
StoredValues[.Temp.userAccessPolicy] = newValue
|
||||
}
|
||||
.onReceive(viewModel.events) { event in
|
||||
switch event {
|
||||
case let .duplicateUser(duplicateUser):
|
||||
UIDevice.impact(.medium)
|
||||
|
||||
self.duplicateUser = duplicateUser
|
||||
isPresentingDuplicateUser = true
|
||||
case let .error(eventError):
|
||||
UIDevice.feedback(.error)
|
||||
|
||||
error = eventError
|
||||
isPresentingError = true
|
||||
case let .signedIn(user):
|
||||
UIDevice.feedback(.success)
|
||||
|
||||
Defaults[.lastSignedInUserID] = user.id
|
||||
UserSession.current.reset()
|
||||
Notifications[.didSignIn].post()
|
||||
}
|
||||
handleSignIn(event)
|
||||
}
|
||||
.onFirstAppear {
|
||||
focusedTextField = 0
|
||||
|
@ -335,7 +339,7 @@ struct UserSignInView: View {
|
|||
Button("Security", systemImage: "gearshape.fill") {
|
||||
let parameters = UserSignInCoordinator.SecurityParameters(
|
||||
pinHint: $pinHint,
|
||||
signInPolicy: $signInPolicy
|
||||
accessPolicy: $accessPolicy
|
||||
)
|
||||
router.route(to: \.security, parameters)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue