Skip to content
This repository was archived by the owner on Feb 5, 2025. It is now read-only.
Closed
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
12 changes: 12 additions & 0 deletions WordPressAuthenticator.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
3FFF2FC123D7ED7C00D38C77 /* EmailClients.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3FFF2FC023D7ED7C00D38C77 /* EmailClients.plist */; };
3FFF2FC323D7F53200D38C77 /* AppSelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3FFF2FC223D7F53200D38C77 /* AppSelector.swift */; };
7A7A9B9CD2D81959F9AB9AF6 /* Pods_WordPressAuthenticator.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C736FF243DE333FCAB1C2614 /* Pods_WordPressAuthenticator.framework */; };
80CE9B162754763900871C00 /* OnePasswordFacadeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80CE9B152754763900871C00 /* OnePasswordFacadeTests.swift */; };
80CE9B18275493F700871C00 /* OnePasswordService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80CE9B17275493F700871C00 /* OnePasswordService.swift */; };
80CE9B1A2754952D00871C00 /* MockOnePasswordService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80CE9B192754952C00871C00 /* MockOnePasswordService.swift */; };
982C8E7923021C20003F1BA0 /* LoginPrologueLoginMethodViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 982C8E7823021C20003F1BA0 /* LoginPrologueLoginMethodViewController.swift */; };
984D508224D4B21A00251A63 /* PasswordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 984D508124D4B21A00251A63 /* PasswordViewController.swift */; };
984D508424D4B24500251A63 /* Password.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 984D508324D4B24500251A63 /* Password.storyboard */; };
Expand Down Expand Up @@ -223,6 +226,9 @@
3FFF2FC023D7ED7C00D38C77 /* EmailClients.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = EmailClients.plist; sourceTree = "<group>"; };
3FFF2FC223D7F53200D38C77 /* AppSelector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSelector.swift; sourceTree = "<group>"; };
5A441EC80D2B8D2209C2E228 /* Pods_WordPressAuthenticatorTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_WordPressAuthenticatorTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
80CE9B152754763900871C00 /* OnePasswordFacadeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnePasswordFacadeTests.swift; sourceTree = "<group>"; };
80CE9B17275493F700871C00 /* OnePasswordService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnePasswordService.swift; sourceTree = "<group>"; };
80CE9B192754952C00871C00 /* MockOnePasswordService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockOnePasswordService.swift; sourceTree = "<group>"; };
8F7217C3F7A6285D9C6CF786 /* Pods-WordPressAuthenticator.release-internal.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WordPressAuthenticator.release-internal.xcconfig"; path = "Pods/Target Support Files/Pods-WordPressAuthenticator/Pods-WordPressAuthenticator.release-internal.xcconfig"; sourceTree = "<group>"; };
982C8E7823021C20003F1BA0 /* LoginPrologueLoginMethodViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginPrologueLoginMethodViewController.swift; sourceTree = "<group>"; };
984D508124D4B21A00251A63 /* PasswordViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordViewController.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -549,6 +555,7 @@
isa = PBXGroup;
children = (
B501C040208FC52500D1E58F /* LoginFacadeTests.m */,
80CE9B152754763900871C00 /* OnePasswordFacadeTests.swift */,
);
path = Services;
sourceTree = "<group>";
Expand Down Expand Up @@ -678,6 +685,7 @@
1A21EE9722832BC200C940C6 /* WordPressComOAuthClientFacade+Swift.swift */,
B5609106208A54F800399AE4 /* WordPressXMLRPCAPIFacade.h */,
B5609102208A54F800399AE4 /* WordPressXMLRPCAPIFacade.m */,
80CE9B17275493F700871C00 /* OnePasswordService.swift */,
);
path = Services;
sourceTree = "<group>";
Expand Down Expand Up @@ -825,6 +833,7 @@
BA53D64C24DFE4E6001F1ABF /* ModalViewControllerPresentingSpy.swift */,
BA53D64A24DFE07D001F1ABF /* WordpressAuthenticatorProvider.swift */,
D85C36EB256E10EA00D56E34 /* MockNavigationController.swift */,
80CE9B192754952C00871C00 /* MockOnePasswordService.swift */,
);
path = Mocks;
sourceTree = "<group>";
Expand Down Expand Up @@ -1224,6 +1233,7 @@
B5609116208A555600399AE4 /* LoginTextField.swift in Sources */,
D85C3653256DEDA900D56E34 /* WordPressAuthenticatorResult.swift in Sources */,
F1C96669250BF53400EB529D /* UIViewController+Dismissal.swift in Sources */,
80CE9B18275493F700871C00 /* OnePasswordService.swift in Sources */,
CE811D6724EDC0FB00F4CCD6 /* LoginMagicLinkViewController.swift in Sources */,
F11448EC258B827B0048203D /* URL+JetpackConnect.swift in Sources */,
B56090E2208A4F9D00399AE4 /* WPNUXSecondaryButton.m in Sources */,
Expand Down Expand Up @@ -1330,6 +1340,8 @@
3108613125AFA4830022F75E /* PasteboardTests.swift in Sources */,
D85C36F0256E118D00D56E34 /* NavigationToEnterAccountTests.swift in Sources */,
D85C36E6256E0DDE00D56E34 /* NavigationToEnterSiteTests.swift in Sources */,
80CE9B1A2754952D00871C00 /* MockOnePasswordService.swift in Sources */,
80CE9B162754763900871C00 /* OnePasswordFacadeTests.swift in Sources */,
D85C3882256E3FEC00D56E34 /* WordPressComSiteInfoTests.swift in Sources */,
D8611A672576236800A5DF27 /* NavigateBackTests.swift in Sources */,
F12F9FB824D8A7FC00771BCE /* AnalyticsTrackerTests.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,16 @@ public struct WordPressAuthenticatorConfiguration {
/// Default value is disabled
let continueWithSiteAddressFirst: Bool

/// Flag for 1Password extension integration.
/// If disabled, 1Password buttons are not added to email and username textfields,
/// instead we depend on the key icon in the keyboard to initiate a password manager.
/// If enabled, we add 1Password buttons normally.
/// Default value is enabled
/// Before disabling the flag, make sure to setup the app's associated domains according to this:
/// https://developer.apple.com/documentation/xcode/supporting-associated-domains
///
let enableOnePassword: Bool

/// Designated Initializer
///
public init (wpcomClientId: String,
Expand All @@ -108,7 +118,8 @@ public struct WordPressAuthenticatorConfiguration {
enableUnifiedAuth: Bool = false,
enableUnifiedCarousel: Bool = false,
displayHintButtons: Bool = true,
continueWithSiteAddressFirst: Bool = false) {
continueWithSiteAddressFirst: Bool = false,
enableOnePassword: Bool = true) {

self.wpcomClientId = wpcomClientId
self.wpcomSecret = wpcomSecret
Expand All @@ -128,5 +139,6 @@ public struct WordPressAuthenticatorConfiguration {
self.displayHintButtons = displayHintButtons
self.enableSignupWithGoogle = enableSignupWithGoogle
self.continueWithSiteAddressFirst = continueWithSiteAddressFirst
self.enableOnePassword = enableOnePassword
}
}
4 changes: 2 additions & 2 deletions WordPressAuthenticator/Extensions/WPStyleGuide+Login.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ extension WPStyleGuide {
/// - Note: this is for the old UI.
///
class func configureOnePasswordButtonForTextfield(_ textField: WPWalkthroughTextField, target: NSObject, selector: Selector) {
guard OnePasswordFacade.isOnePasswordEnabled else {
guard OnePasswordFacade().isOnePasswordEnabled else {
return
}

Expand All @@ -66,7 +66,7 @@ extension WPStyleGuide {
/// - Note: this is for the old UI.
///
class func configureOnePasswordButtonForStackView(_ stack: UIStackView, target: NSObject, selector: Selector) {
guard OnePasswordFacade.isOnePasswordEnabled else {
guard OnePasswordFacade().isOnePasswordEnabled else {
return
}

Expand Down
27 changes: 21 additions & 6 deletions WordPressAuthenticator/Services/OnePasswordFacade.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,24 @@ import Foundation
import UIKit
import OnePasswordExtension

// MARK: - This protocol is a Facade that hides some of the implementation details for interacting with 1Password.
//
extension OnePasswordExtension: OnePasswordService {}

/// A facade that hides some of the implementation details for interacting with 1Password.
///
class OnePasswordFacade {

// MARK: - Private Properties
//
private let onePasswordService: OnePasswordService

// MARK: - Initializer
//
init(onePasswordService: OnePasswordService = OnePasswordExtension.shared()) {
self.onePasswordService = onePasswordService
}

// MARK: - Public Functions

/// This method will pull up the 1Password extension and display any logins for the passed in `loginUrl`.
///
/// - Parameters:
Expand All @@ -20,7 +34,7 @@ class OnePasswordFacade {
sender: Any,
success: @escaping (_ username: String, _ password: String, _ otp: String?) -> Void,
failure: @escaping (OnePasswordError) -> Void) {
OnePasswordExtension.shared().findLogin(forURLString: url, for: viewController, sender: sender) { (dictionary, error) in
onePasswordService.findLogin(forURLString: url, for: viewController, sender: sender) { (dictionary, error) in
if let error = error as NSError? {
failure(OnePasswordError(error: error))
return
Expand Down Expand Up @@ -76,7 +90,7 @@ class OnePasswordFacade {
AppExtensionGeneratedPasswordMaxLengthKey: maximumLength
]

OnePasswordExtension.shared().storeLogin(forURLString: url, loginDetails: loginDetails, passwordGenerationOptions: options, for: viewController, sender: sender) { (loginDict, error) in
onePasswordService.storeLogin(forURLString: url, loginDetails: loginDetails, passwordGenerationOptions: options, for: viewController, sender: sender) { (loginDict, error) in
if let error = error as NSError? {
failure(OnePasswordError(error: error))
return
Expand All @@ -95,8 +109,9 @@ class OnePasswordFacade {

/// Indicates if the 1P Extension is enabled, or not.
///
static var isOnePasswordEnabled: Bool {
return OnePasswordExtension.shared().isAppExtensionAvailable()
var isOnePasswordEnabled: Bool {
return WordPressAuthenticator.shared.configuration.enableOnePassword &&
onePasswordService.isAppExtensionAvailable()
}
}

Expand Down
19 changes: 19 additions & 0 deletions WordPressAuthenticator/Services/OnePasswordService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// OnePasswordService.swift
// WordPressAuthenticator
//
// Created by Hassaan El-Garem on 11/29/21.
// Copyright © 2021 Automattic. All rights reserved.
//

import Foundation

/// Protocol that acts as a wrapper around OnePasswordExtension
protocol OnePasswordService {

typealias OnePasswordServiceLoginDictionaryCompletionBlock = ([AnyHashable: Any]?, Error?) -> Void

func findLogin(forURLString URLString: String, for viewController: UIViewController, sender: Any?, completion: @escaping OnePasswordServiceLoginDictionaryCompletionBlock)
func storeLogin(forURLString URLString: String, loginDetails loginDetailsDictionary: [AnyHashable: Any]?, passwordGenerationOptions: [AnyHashable: Any]?, for viewController: UIViewController, sender: Any?, completion: @escaping OnePasswordServiceLoginDictionaryCompletionBlock)
func isAppExtensionAvailable() -> Bool
}
37 changes: 37 additions & 0 deletions WordPressAuthenticatorTests/Mocks/MockOnePasswordService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//
// MockOnePasswordService.swift
// WordPressAuthenticatorTests
//
// Created by Hassaan El-Garem on 11/29/21.
// Copyright © 2021 Automattic. All rights reserved.
//

import Foundation
@testable import WordPressAuthenticator

class MockOnePasswordService: OnePasswordService {

// MARK: - Private Properties
//
var onePasswordAvailable: Bool

// MARK: - Initializer
//
init(onePasswordAvailable: Bool) {
self.onePasswordAvailable = onePasswordAvailable
}

// MARK: - OnePasswordService
//
func findLogin(forURLString URLString: String, for viewController: UIViewController, sender: Any?, completion: @escaping OnePasswordServiceLoginDictionaryCompletionBlock) {
// Do nothing
}

func storeLogin(forURLString URLString: String, loginDetails loginDetailsDictionary: [AnyHashable: Any]?, passwordGenerationOptions: [AnyHashable: Any]?, for viewController: UIViewController, sender: Any?, completion: @escaping OnePasswordServiceLoginDictionaryCompletionBlock) {
// Do nothing
}

func isAppExtensionAvailable() -> Bool {
return onePasswordAvailable
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@

@objc
public class WordpressAuthenticatorProvider: NSObject {
static func wordPressAuthenticatorConfiguration() -> WordPressAuthenticatorConfiguration {
static func wordPressAuthenticatorConfiguration(enableOnePassword: Bool = true) -> WordPressAuthenticatorConfiguration {
return WordPressAuthenticatorConfiguration(wpcomClientId: "23456",
wpcomSecret: "arfv35dj57l3g2323",
wpcomScheme: "https",
wpcomTermsOfServiceURL: "https://wordpress.com/tos/",
googleLoginClientId: "",
googleLoginServerClientId: "",
googleLoginScheme: "com.googleuserconsent.apps",
userAgent: "")
userAgent: "",
enableOnePassword: enableOnePassword)
}

static func wordPressAuthenticatorStyle(_ style: AuthenticatorStyleType) -> WordPressAuthenticatorStyle {
Expand Down
56 changes: 56 additions & 0 deletions WordPressAuthenticatorTests/Services/OnePasswordFacadeTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//
// OnePasswordFacadeTests.swift
// WordPressAuthenticatorTests
//
// Created by Hassaan El-Garem on 11/29/21.
// Copyright © 2021 Automattic. All rights reserved.
//

import XCTest
@testable import WordPressAuthenticator

class OnePasswordFacadeTests: XCTestCase {

func testOnePasswordEnabled() {
// Given
WordPressAuthenticator.initialize(
configuration: WordpressAuthenticatorProvider.wordPressAuthenticatorConfiguration(enableOnePassword: true),
style: WordpressAuthenticatorProvider.wordPressAuthenticatorStyle(.random),
unifiedStyle: WordpressAuthenticatorProvider.wordPressAuthenticatorUnifiedStyle(.random)
)
let mockOnePasswordService = MockOnePasswordService(onePasswordAvailable: true)
let onePasswordFacade = OnePasswordFacade(onePasswordService: mockOnePasswordService)

// When & Then
XCTAssertTrue(onePasswordFacade.isOnePasswordEnabled)
}

func testOnePasswordDisabledIfUnAvailable() {
// Given
WordPressAuthenticator.initialize(
configuration: WordpressAuthenticatorProvider.wordPressAuthenticatorConfiguration(enableOnePassword: true),
style: WordpressAuthenticatorProvider.wordPressAuthenticatorStyle(.random),
unifiedStyle: WordpressAuthenticatorProvider.wordPressAuthenticatorUnifiedStyle(.random)
)
let mockOnePasswordService = MockOnePasswordService(onePasswordAvailable: false)
let onePasswordFacade = OnePasswordFacade(onePasswordService: mockOnePasswordService)

// When & Then
XCTAssertFalse(onePasswordFacade.isOnePasswordEnabled)
}

func testOnePasswordDisabledFromConfiguration() {
// Given
WordPressAuthenticator.initialize(
configuration: WordpressAuthenticatorProvider.wordPressAuthenticatorConfiguration(enableOnePassword: false),
style: WordpressAuthenticatorProvider.wordPressAuthenticatorStyle(.random),
unifiedStyle: WordpressAuthenticatorProvider.wordPressAuthenticatorUnifiedStyle(.random)
)
let mockOnePasswordService = MockOnePasswordService(onePasswordAvailable: true)
let onePasswordFacade = OnePasswordFacade(onePasswordService: mockOnePasswordService)

// When & Then
XCTAssertFalse(onePasswordFacade.isOnePasswordEnabled)
}

}