* Initial service implementation for using a PIN code. * Tweak Danger for commit size 600-800 lines is perfectly normal for our PRs, up it to 1000.
179 lines
7.4 KiB
Swift
179 lines
7.4 KiB
Swift
//
|
|
// Copyright 2023 New Vector Ltd
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
|
|
import XCTest
|
|
|
|
@testable import ElementX
|
|
|
|
@MainActor
|
|
class AppLockServiceTests: XCTestCase {
|
|
var appSettings: AppSettings!
|
|
var service: AppLockService!
|
|
|
|
override func setUp() {
|
|
AppSettings.reset()
|
|
appSettings = AppSettings()
|
|
appSettings.appLockFlowEnabled = true
|
|
|
|
let keychainController = KeychainController(service: .tests, accessGroup: InfoPlistReader.main.keychainAccessGroupIdentifier)
|
|
|
|
service = AppLockService(keychainController: keychainController, appSettings: appSettings)
|
|
service.disable()
|
|
}
|
|
|
|
override func tearDown() {
|
|
AppSettings.reset()
|
|
}
|
|
|
|
func testValidPINCode() {
|
|
// Given a service that hasn't been enabled.
|
|
XCTAssertFalse(service.isEnabled, "The service shouldn't be enabled to begin with.")
|
|
|
|
// When setting a PIN code.
|
|
let pinCode = "2023" // Highly secure PIN that is rotated every 12 months.
|
|
guard case .success = service.setupPINCode(pinCode) else {
|
|
XCTFail("The PIN should be valid.")
|
|
return
|
|
}
|
|
|
|
// Then service should be enabled and only the provided PIN should work to unlock the app.
|
|
XCTAssertTrue(service.isEnabled, "The service should become enabled when setting a PIN.")
|
|
XCTAssertTrue(service.unlock(with: pinCode), "The provided PIN code should work.")
|
|
XCTAssertFalse(service.unlock(with: "2024"), "No other PIN code should work.")
|
|
XCTAssertFalse(service.unlock(with: "1234"), "No other PIN code should work.")
|
|
XCTAssertFalse(service.unlock(with: "9999"), "No other PIN code should work.")
|
|
}
|
|
|
|
func testWeakPINCode() {
|
|
// Given a service that hasn't been enabled.
|
|
XCTAssertFalse(service.isEnabled, "The service shouldn't be enabled to begin with.")
|
|
|
|
// When setting a PIN code that is in the block list.
|
|
let pinCode = appSettings.appLockPINCodeBlockList[0]
|
|
let result = service.setupPINCode(pinCode)
|
|
|
|
// Then the setup should fail and the service be left as disabled.
|
|
guard case let .failure(error) = result else {
|
|
XCTFail("The call should have failed.")
|
|
return
|
|
}
|
|
XCTAssertEqual(error, .weakPIN, "The PIN should be rejected as weak.")
|
|
XCTAssertFalse(service.isEnabled, "The service should remain disabled.")
|
|
}
|
|
|
|
func testShortPINCode() {
|
|
// Given a service that hasn't been enabled.
|
|
XCTAssertFalse(service.isEnabled, "The service shouldn't be enabled to begin with.")
|
|
|
|
// When setting a PIN code that is too short
|
|
let pinCode = "123"
|
|
let result = service.setupPINCode(pinCode)
|
|
|
|
// Then the setup should fail and the service be left as disabled.
|
|
guard case let .failure(error) = result else {
|
|
XCTFail("The call should have failed.")
|
|
return
|
|
}
|
|
XCTAssertEqual(error, .invalidPIN, "The PIN should be rejected as invalid.")
|
|
XCTAssertFalse(service.isEnabled, "The service should remain disabled.")
|
|
}
|
|
|
|
func testNonNumericPINCode() {
|
|
// Given a service that hasn't been enabled.
|
|
XCTAssertFalse(service.isEnabled, "The service shouldn't be enabled to begin with.")
|
|
|
|
// When setting a PIN code that is too short
|
|
let pinCode = "abcd"
|
|
let result = service.setupPINCode(pinCode)
|
|
|
|
// Then the setup should fail and the service be left as disabled.
|
|
guard case let .failure(error) = result else {
|
|
XCTFail("The call should have failed.")
|
|
return
|
|
}
|
|
XCTAssertEqual(error, .invalidPIN, "The PIN should be rejected as invalid.")
|
|
XCTAssertFalse(service.isEnabled, "The service should remain disabled.")
|
|
}
|
|
|
|
func testChangePINCode() {
|
|
// Given a service that is already enabled with a PIN.
|
|
let pinCode = "2023"
|
|
let newPINCode = "2024"
|
|
guard case .success = service.setupPINCode(pinCode) else {
|
|
XCTFail("The PIN should be valid.")
|
|
return
|
|
}
|
|
XCTAssertTrue(service.isEnabled, "The service should be enabled.")
|
|
XCTAssertTrue(service.unlock(with: pinCode), "The initial PIN should work.")
|
|
XCTAssertFalse(service.unlock(with: newPINCode), "The PIN we're about to set should not work.")
|
|
|
|
// When updating the PIN code.
|
|
guard case .success = service.setupPINCode(newPINCode) else {
|
|
XCTFail("The PIN should be valid.")
|
|
return
|
|
}
|
|
|
|
// Then the old code should not be accepted.
|
|
XCTAssertTrue(service.isEnabled, "The service should remain enabled.")
|
|
XCTAssertTrue(service.unlock(with: newPINCode), "The new PIN should work.")
|
|
XCTAssertFalse(service.unlock(with: pinCode), "The original PIN should be rejected.")
|
|
}
|
|
|
|
func testInvalidChangePINCode() {
|
|
// Given a service that is already enabled with a PIN.
|
|
let pinCode = "2023"
|
|
let invalidPIN = appSettings.appLockPINCodeBlockList[0]
|
|
guard case .success = service.setupPINCode(pinCode) else {
|
|
XCTFail("The PIN should be valid.")
|
|
return
|
|
}
|
|
XCTAssertTrue(service.isEnabled, "The service should be enabled.")
|
|
XCTAssertTrue(service.unlock(with: pinCode), "The initial PIN should work.")
|
|
XCTAssertFalse(service.unlock(with: invalidPIN), "The PIN we're about to set should not work.")
|
|
|
|
// When updating the PIN code that is in the block list.
|
|
let result = service.setupPINCode(invalidPIN)
|
|
|
|
// Then it should fail and nothing should change.
|
|
guard case let .failure(error) = result else {
|
|
XCTFail("The call should have failed.")
|
|
return
|
|
}
|
|
XCTAssertEqual(error, .weakPIN, "The PIN should be rejected as weak.")
|
|
XCTAssertTrue(service.isEnabled, "The service should remain enabled.")
|
|
XCTAssertFalse(service.unlock(with: invalidPIN), "The rejected PIN shouldn't work.")
|
|
XCTAssertTrue(service.unlock(with: pinCode), "The original PIN should continue to work.")
|
|
}
|
|
|
|
func testDisablePINCode() {
|
|
// Given a service that is already enabled with a PIN.
|
|
let pinCode = "2023"
|
|
guard case .success = service.setupPINCode(pinCode) else {
|
|
XCTFail("The PIN should be valid.")
|
|
return
|
|
}
|
|
XCTAssertTrue(service.isEnabled, "The service should be enabled.")
|
|
XCTAssertTrue(service.unlock(with: pinCode), "The initial PIN should work.")
|
|
|
|
// When disabling the PIN code.
|
|
service.disable()
|
|
|
|
// Then the PIN code should be removed.
|
|
XCTAssertFalse(service.isEnabled, "The service should no longer be enabled.")
|
|
XCTAssertFalse(service.unlock(with: pinCode), "The initial PIN shouldn't work any more.")
|
|
}
|
|
}
|