diff --git a/.jazzy.yaml b/.jazzy.yaml index 9eb962d..2772bfb 100644 --- a/.jazzy.yaml +++ b/.jazzy.yaml @@ -50,18 +50,24 @@ custom_categories: - name: Interaction children: - AccountSelectorImpl + - AuthenticatorSelectorImpl - AuthenticationAuthenticatorSelectorName - AuthenticationAuthenticatorSelectorImpl - BiometricUserVerifierImpl + - DevicePasscodeUserVerifierImpl - OutOfBandOperationHandler - OutOfBandOperationHandlerImpl - PinChangerImpl - PinEnrollerImpl - PinUserVerifierImpl + - PasswordChangerImpl + - PasswordEnrollerImpl + - PasswordUserVerifierImpl - RegistrationAuthenticatorSelectorName - RegistrationAuthenticatorSelectorImpl - name: Model children: + - AuthenticatorItem - DeepLink - Operation - OperationError @@ -87,11 +93,13 @@ custom_categories: - SelectAuthenticatorParameter - SelectAuthenticatorItemViewModel - AuthenticatorCell - - PinScreen - - PinView - - PinPresenter + - CredentialScreen + - CredentialView + - CredentialPresenter + - CredentialParameter - PinParameter - - PinProtectionInformation + - PasswordParameter + - CredentialProtectionInformation - AuthCloudApiRegistrationScreen - AuthCloudApiRegistrationPresenter - ChangeDeviceInformationScreen @@ -102,6 +110,9 @@ custom_categories: - TransactionConfirmationScreen - TransactionConfirmationPresenter - TransactionConfirmationParameter + - ConfirmationScreen + - ConfirmationPresenter + - ConfirmationParameter - ResultScreen - ResultPresenter - ResultParameter @@ -118,10 +129,12 @@ custom_categories: - name: Utility / Extensions children: - Authenticator + - AuthenticatorAaid - '[String: String]' - Notification - OSLog - PinAuthenticatorProtectionStatus + - PasswordAuthenticatorProtectionStatus - String - String? - UIApplication @@ -153,6 +166,8 @@ custom_categories: - name: Utility / Validation children: - AccountValidator + - AuthenticatorValidator - AuthCloudApiRegistrationValidator - LoginValidator - - ValidationResult \ No newline at end of file + - ValidationResult + - PasswordPolicyImpl \ No newline at end of file diff --git a/Gemfile b/Gemfile index b88ee45..8d5a62d 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,7 @@ source "https://rubygems.org" gem 'cocoapods', '~> 1.15.2' -gem 'fastlane', '~> 2.219' +gem 'fastlane', '~> 2.221' plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') eval_gemfile(plugins_path) if File.exist?(plugins_path) diff --git a/Gemfile.lock b/Gemfile.lock index d9c1608..28394e2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -23,19 +23,19 @@ GEM artifactory (3.0.17) atomos (0.1.3) aws-eventstream (1.3.0) - aws-partitions (1.946.0) - aws-sdk-core (3.197.2) + aws-partitions (1.955.0) + aws-sdk-core (3.201.1) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.651.0) aws-sigv4 (~> 1.8) jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.85.0) - aws-sdk-core (~> 3, >= 3.197.0) - aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.152.3) - aws-sdk-core (~> 3, >= 3.197.0) + aws-sdk-kms (1.88.0) + aws-sdk-core (~> 3, >= 3.201.0) + aws-sigv4 (~> 1.5) + aws-sdk-s3 (1.156.0) + aws-sdk-core (~> 3, >= 3.201.0) aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.8) + aws-sigv4 (~> 1.5) aws-sigv4 (1.8.0) aws-eventstream (~> 1, >= 1.0.2) babosa (1.0.4) @@ -95,7 +95,7 @@ GEM escape (0.0.4) ethon (0.16.0) ffi (>= 1.15.0) - excon (0.110.0) + excon (0.111.0) faraday (1.10.3) faraday-em_http (~> 1.0) faraday-em_synchrony (~> 1.0) @@ -224,9 +224,9 @@ GEM json (2.7.2) jwt (2.8.2) base64 - mini_magick (4.13.1) + mini_magick (4.13.2) mini_mime (1.1.5) - minitest (5.24.0) + minitest (5.24.1) molinillo (0.8.0) multi_json (1.15.0) multipart-post (2.4.1) @@ -291,12 +291,14 @@ GEM PLATFORMS arm64-darwin-21 + arm64-darwin-22 + arm64-darwin-23 x86_64-darwin-20 x86_64-darwin-22 DEPENDENCIES cocoapods (~> 1.15.2) - fastlane (~> 2.219) + fastlane (~> 2.221) fastlane-plugin-firebase_app_distribution BUNDLED WITH diff --git a/NevisExampleApp.xcodeproj/project.pbxproj b/NevisExampleApp.xcodeproj/project.pbxproj index 6709164..217ab3c 100644 --- a/NevisExampleApp.xcodeproj/project.pbxproj +++ b/NevisExampleApp.xcodeproj/project.pbxproj @@ -12,6 +12,7 @@ 06000BB02C37EDA100AB53A1 /* PasswordEnroller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06000BAF2C37EDA100AB53A1 /* PasswordEnroller.swift */; }; 06000BB22C37EE6A00AB53A1 /* PasswordUserVerifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06000BB12C37EE6A00AB53A1 /* PasswordUserVerifier.swift */; }; 06471B152C29685C006879F9 /* AuthenticatorAaid+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06471B142C29685C006879F9 /* AuthenticatorAaid+Extensions.swift */; }; + 068293672C3FCE77005563AB /* PasswordPolicyImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 068293662C3FCE77005563AB /* PasswordPolicyImpl.swift */; }; 068E9ABC2C2D60BA0059E33C /* AuthenticatorValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 068E9ABB2C2D60BA0059E33C /* AuthenticatorValidator.swift */; }; 382FE1D72BADD5FC00999625 /* ConfirmationPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 382FE1D62BADD5FC00999625 /* ConfirmationPresenter.swift */; }; 382FE1D92BADD60400999625 /* ConfirmationScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 382FE1D82BADD60400999625 /* ConfirmationScreen.swift */; }; @@ -123,6 +124,7 @@ 06000BAF2C37EDA100AB53A1 /* PasswordEnroller.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordEnroller.swift; sourceTree = ""; }; 06000BB12C37EE6A00AB53A1 /* PasswordUserVerifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordUserVerifier.swift; sourceTree = ""; }; 06471B142C29685C006879F9 /* AuthenticatorAaid+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AuthenticatorAaid+Extensions.swift"; sourceTree = ""; }; + 068293662C3FCE77005563AB /* PasswordPolicyImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordPolicyImpl.swift; sourceTree = ""; }; 068E9ABB2C2D60BA0059E33C /* AuthenticatorValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticatorValidator.swift; sourceTree = ""; }; 382FE1D62BADD5FC00999625 /* ConfirmationPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmationPresenter.swift; sourceTree = ""; }; 382FE1D82BADD60400999625 /* ConfirmationScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmationScreen.swift; sourceTree = ""; }; @@ -643,6 +645,7 @@ 3859EB92292CFA1900013472 /* AuthCloudApiRegistrationValidator.swift */, 068E9ABB2C2D60BA0059E33C /* AuthenticatorValidator.swift */, 38DABD6929D585C0008B8116 /* LoginValidator.swift */, + 068293662C3FCE77005563AB /* PasswordPolicyImpl.swift */, 3859EB93292CFA1900013472 /* ValidationResult.swift */, ); path = Validation; @@ -687,13 +690,6 @@ path = "Username Password Login"; sourceTree = ""; }; - A651FDC4F5C71ADD993EA999 /* Pods */ = { - isa = PBXGroup; - children = ( - ); - path = Pods; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -815,6 +811,7 @@ 3859EB50292CF92000013472 /* Screen.swift in Sources */, 3859EB4E292CF92000013472 /* SelectAccountScreen.swift in Sources */, 3859EB3A292CF92000013472 /* OutOfBandOperationHandlerImpl.swift in Sources */, + 068293672C3FCE77005563AB /* PasswordPolicyImpl.swift in Sources */, 3859EBBC292CFA7000013472 /* UIDevice+Extension.swift in Sources */, 06471B152C29685C006879F9 /* AuthenticatorAaid+Extensions.swift in Sources */, 3859EB40292CF92000013472 /* HomePresenter.swift in Sources */, diff --git a/NevisExampleApp/Dependency Provider/AppAssembly.swift b/NevisExampleApp/Dependency Provider/AppAssembly.swift index 879ad2c..791dcc3 100644 --- a/NevisExampleApp/Dependency Provider/AppAssembly.swift +++ b/NevisExampleApp/Dependency Provider/AppAssembly.swift @@ -254,6 +254,9 @@ private extension AppAssembly { container.autoregister(PasswordUserVerifier.self, initializer: PasswordUserVerifierImpl.init) + container.autoregister(PasswordPolicy.self, + initializer: PasswordPolicyImpl.init) + container.autoregister(BiometricUserVerifier.self, initializer: BiometricUserVerifierImpl.init) diff --git a/NevisExampleApp/Interaction/AccountSelectorImpl.swift b/NevisExampleApp/Interaction/AccountSelectorImpl.swift index d5726a0..fc331b8 100644 --- a/NevisExampleApp/Interaction/AccountSelectorImpl.swift +++ b/NevisExampleApp/Interaction/AccountSelectorImpl.swift @@ -31,7 +31,6 @@ class AccountSelectorImpl { /// - appCoordinator: The application coordinator. /// - logger: The logger. init(appCoordinator: AppCoordinator, - errorHandlerChain: ErrorHandlerChain, logger: SDKLogger) { self.appCoordinator = appCoordinator self.logger = logger diff --git a/NevisExampleApp/Interaction/Password/PasswordChangerImpl.swift b/NevisExampleApp/Interaction/Password/PasswordChangerImpl.swift index b3258fd..95ec298 100644 --- a/NevisExampleApp/Interaction/Password/PasswordChangerImpl.swift +++ b/NevisExampleApp/Interaction/Password/PasswordChangerImpl.swift @@ -19,6 +19,9 @@ class PasswordChangerImpl { /// The logger. private let logger: SDKLogger + /// The password policy. + private let policy: PasswordPolicy + // MARK: - Initialization /// Creates a new instance. @@ -26,10 +29,13 @@ class PasswordChangerImpl { /// - Parameters: /// - appCoordinator: The application coordinator. /// - logger: The logger. + /// - policy: The password policy. init(appCoordinator: AppCoordinator, - logger: SDKLogger) { + logger: SDKLogger, + policy: PasswordPolicy) { self.appCoordinator = appCoordinator self.logger = logger + self.policy = policy } } @@ -51,8 +57,7 @@ extension PasswordChangerImpl: PasswordChanger { appCoordinator.navigateToCredential(with: parameter) } - /// You can add custom Password policy by overriding the `passwordPolicy` getter. -// func passwordPolicy() -> PasswordPolicy { -// // custom PasswordPolicy implementation -// } + func passwordPolicy() -> PasswordPolicy { + policy + } } diff --git a/NevisExampleApp/Interaction/Password/PasswordEnroller.swift b/NevisExampleApp/Interaction/Password/PasswordEnroller.swift index 1bfcc9e..3bbdc46 100644 --- a/NevisExampleApp/Interaction/Password/PasswordEnroller.swift +++ b/NevisExampleApp/Interaction/Password/PasswordEnroller.swift @@ -19,6 +19,9 @@ class PasswordEnrollerImpl { /// The logger. private let logger: SDKLogger + /// The password policy. + private let policy: PasswordPolicy + // MARK: - Initialization /// Creates a new instance. @@ -26,10 +29,13 @@ class PasswordEnrollerImpl { /// - Parameters: /// - appCoordinator: The application coordinator. /// - logger: The logger. + /// - policy: The password policy. init(appCoordinator: AppCoordinator, - logger: SDKLogger) { + logger: SDKLogger, + policy: PasswordPolicy) { self.appCoordinator = appCoordinator self.logger = logger + self.policy = policy } } @@ -49,8 +55,7 @@ extension PasswordEnrollerImpl: PasswordEnroller { appCoordinator.navigateToCredential(with: parameter) } - /// You can add custom Password policy by overriding the `passwordPolicy` getter. -// func passwordPolicy() -> PasswordPolicy { -// // custom PasswordPolicy implementation -// } + func passwordPolicy() -> PasswordPolicy { + policy + } } diff --git a/NevisExampleApp/Utility/Validation/PasswordPolicyImpl.swift b/NevisExampleApp/Utility/Validation/PasswordPolicyImpl.swift new file mode 100644 index 0000000..d003e17 --- /dev/null +++ b/NevisExampleApp/Utility/Validation/PasswordPolicyImpl.swift @@ -0,0 +1,42 @@ +// +// Nevis Mobile Authentication SDK Example App +// +// Copyright © 2024 Nevis Security AG. All rights reserved. +// + +import NevisMobileAuthentication + +/// Implementation of the ``PasswordPolicy``. +/// This policy validates the password entered by the user during registration or password changing, +/// and allows only passwords longer than 6 characters. +final class PasswordPolicyImpl {} + +// MARK: PasswordPolicy + +extension PasswordPolicyImpl: PasswordPolicy { + func validatePasswordForEnrollment(_ password: String, onSuccess: @escaping () -> (), onError: @escaping (PasswordEnrollmentValidationError) -> ()) { + guard isValid(password) else { + return onError(.InvalidPassword(message: errorMessage)) + } + onSuccess() + } + + func validatePasswordForPasswordChange(_ password: String, onSuccess: @escaping () -> (), onError: @escaping (PasswordChangeValidationError) -> ()) { + guard isValid(password) else { + return onError(.InvalidPassword(message: errorMessage)) + } + onSuccess() + } +} + +// MARK: - Private extension + +private extension PasswordPolicyImpl { + var errorMessage: String { + "The password must be more than 6 characters." + } + + func isValid(_ password: String) -> Bool { + password.trimmingCharacters(in: .whitespacesAndNewlines).count >= 6 + } +}