Refactor sign in policy (#1085)

This commit is contained in:
Daniel Chick 2024-06-08 22:22:18 -05:00 committed by GitHub
parent 9ebcbe9728
commit 645eb6c516
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 54 additions and 51 deletions

View File

@ -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
)
}
}

View File

@ -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",

View File

@ -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)]
}

View File

@ -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)")))

View File

@ -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
}
}

View File

@ -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] = ""
}

View File

@ -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:

View File

@ -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")

View File

@ -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)
}

View File

@ -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)
}