From dc449349d95e4e74b8567f234f5b5c3302b3e90b Mon Sep 17 00:00:00 2001 From: Robert Collins Date: Thu, 9 Nov 2017 15:57:04 -0800 Subject: [PATCH 1/7] Setting up bare minimum safari share extension --- Authenticator.xcodeproj/project.pbxproj | 259 ++++++++++++++++++ Authenticator/Authenticator.entitlements | 10 + .../ActionViewController.swift | 137 +++++++++ .../AuthenticatorTokenProvider.entitlements | 10 + AuthenticatorTokenProvider/Info.plist | 43 +++ AuthenticatorTokenProvider/Picker.js | 42 +++ 6 files changed, 501 insertions(+) create mode 100644 Authenticator/Authenticator.entitlements create mode 100644 AuthenticatorTokenProvider/ActionViewController.swift create mode 100644 AuthenticatorTokenProvider/AuthenticatorTokenProvider.entitlements create mode 100644 AuthenticatorTokenProvider/Info.plist create mode 100644 AuthenticatorTokenProvider/Picker.js diff --git a/Authenticator.xcodeproj/project.pbxproj b/Authenticator.xcodeproj/project.pbxproj index 29523f81..8f70c631 100644 --- a/Authenticator.xcodeproj/project.pbxproj +++ b/Authenticator.xcodeproj/project.pbxproj @@ -65,6 +65,21 @@ CC471EED1DC1377F006858AC /* MockTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC471EEC1DC1377F006858AC /* MockTableView.swift */; }; CCC409761DB28BFE000A050D /* TableDiffTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC409751DB28BFE000A050D /* TableDiffTests.swift */; }; CCCD668B1E1C74B4005FE96E /* OneTimePasswordExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCCD668A1E1C74B4005FE96E /* OneTimePasswordExtensions.swift */; }; + CCFF7C231FB4FCA100FFBD89 /* AuthenticatorTokenProvider.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = CCFF7C191FB4FCA100FFBD89 /* AuthenticatorTokenProvider.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + CCFF7C2B1FB4FF2E00FFBD89 /* Picker.js in Resources */ = {isa = PBXBuildFile; fileRef = CCFF7C2A1FB4FF2E00FFBD89 /* Picker.js */; }; + CCFF7C301FB50AC000FFBD89 /* OneTimePassword.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CCFF7C311FB50AC000FFBD89 /* OneTimePassword.framework */; }; + CCFF7C561FB50E3500FFBD89 /* TokenStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9F7A8601C4D90B50082E5AE /* TokenStore.swift */; }; + CCFF7C581FB50EE100FFBD89 /* ActionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCFF7C1B1FB4FCA100FFBD89 /* ActionViewController.swift */; }; + CCFF7C591FB5102500FFBD89 /* TokenList.swift in Sources */ = {isa = PBXBuildFile; fileRef = C910ADC01BF0315A00C988F5 /* TokenList.swift */; }; + CCFF7C5A1FB5102C00FFBD89 /* TokenListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9B7328E1C0A8AE60076F77E /* TokenListViewModel.swift */; }; + CCFF7C5B1FB5103800FFBD89 /* TokenRowModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C98B1EC91AB2CEC300C59E53 /* TokenRowModel.swift */; }; + CCFF7C5C1FB5104200FFBD89 /* TableDiff.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9B7328C1C09495D0076F77E /* TableDiff.swift */; }; + CCFF7C5D1FB5105500FFBD89 /* ProgressRingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9D6C84B19075044004F0E08 /* ProgressRingView.swift */; }; + CCFF7C5E1FB5105E00FFBD89 /* DisplayTime.swift in Sources */ = {isa = PBXBuildFile; fileRef = C968D1141CB4C639004ED7BB /* DisplayTime.swift */; }; + CCFF7C5F1FB5108100FFBD89 /* Component.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9EB448D1C52A74200ACFA87 /* Component.swift */; }; + CCFF7C601FB5125700FFBD89 /* TokenRowCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C98B1ECB1AB3CF0700C59E53 /* TokenRowCell.swift */; }; + CCFF7C611FB5126400FFBD89 /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = C93AD15119CD51BE007480E9 /* Colors.swift */; }; + CCFF7C621FB5137900FFBD89 /* UITableView+ReusableCells.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9BA64E91C0C4FBC00610C7C /* UITableView+ReusableCells.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -82,6 +97,13 @@ remoteGlobalIDString = 1D6058900D05DD3D006BFB54; remoteInfo = Authenticator; }; + CCFF7C211FB4FCA100FFBD89 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */; + proxyType = 1; + remoteGlobalIDString = CCFF7C181FB4FCA100FFBD89; + remoteInfo = AuthenticatorTokenProvider; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -98,6 +120,17 @@ name = "Copy Frameworks"; runOnlyForDeploymentPostprocessing = 0; }; + CCFF7C271FB4FCA100FFBD89 /* Embed App Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + CCFF7C231FB4FCA100FFBD89 /* AuthenticatorTokenProvider.appex in Embed App Extensions */, + ); + name = "Embed App Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ @@ -188,6 +221,13 @@ CC471EEC1DC1377F006858AC /* MockTableView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockTableView.swift; sourceTree = ""; }; CCC409751DB28BFE000A050D /* TableDiffTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableDiffTests.swift; sourceTree = ""; }; CCCD668A1E1C74B4005FE96E /* OneTimePasswordExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OneTimePasswordExtensions.swift; sourceTree = ""; }; + CCFF7C191FB4FCA100FFBD89 /* AuthenticatorTokenProvider.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = AuthenticatorTokenProvider.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + CCFF7C1B1FB4FCA100FFBD89 /* ActionViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActionViewController.swift; sourceTree = ""; }; + CCFF7C201FB4FCA100FFBD89 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + CCFF7C281FB4FCCA00FFBD89 /* Authenticator.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Authenticator.entitlements; sourceTree = ""; }; + CCFF7C291FB4FCD200FFBD89 /* AuthenticatorTokenProvider.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = AuthenticatorTokenProvider.entitlements; sourceTree = ""; }; + CCFF7C2A1FB4FF2E00FFBD89 /* Picker.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = Picker.js; sourceTree = ""; }; + CCFF7C311FB50AC000FFBD89 /* OneTimePassword.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = OneTimePassword.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -213,6 +253,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + CCFF7C161FB4FCA100FFBD89 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + CCFF7C301FB50AC000FFBD89 /* OneTimePassword.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -239,6 +287,7 @@ 1D6058910D05DD3D006BFB54 /* Authenticator.app */, C9906A2E1812522100BAEF53 /* AuthenticatorTests.xctest */, C9A262CD1E170BD4004E6CEB /* AuthenticatorScreenshots.xctest */, + CCFF7C191FB4FCA100FFBD89 /* AuthenticatorTokenProvider.appex */, ); name = Products; sourceTree = ""; @@ -249,6 +298,7 @@ C97B68CB17D964D4005D1FE0 /* Authenticator */, C9906A331812522100BAEF53 /* AuthenticatorTests */, C9A262CE1E170BD4004E6CEB /* AuthenticatorScreenshots */, + CCFF7C1A1FB4FCA100FFBD89 /* AuthenticatorTokenProvider */, 29B97323FDCFA39411CA2CEA /* Frameworks */, C944A5661A7F772500E08B1E /* Configuration */, 19C28FACFE9D520D11CA2CBB /* Products */, @@ -276,6 +326,7 @@ 29B97323FDCFA39411CA2CEA /* Frameworks */ = { isa = PBXGroup; children = ( + CCFF7C311FB50AC000FFBD89 /* OneTimePassword.framework */, C944A5581A7ECB3100E08B1E /* Base32.framework */, C944A5561A7ECB0800E08B1E /* OneTimePassword.framework */, C97CDF241BEEA49300D64406 /* SVProgressHUD.framework */, @@ -356,6 +407,7 @@ C97B68CB17D964D4005D1FE0 /* Authenticator */ = { isa = PBXGroup; children = ( + CCFF7C281FB4FCCA00FFBD89 /* Authenticator.entitlements */, 080E96DDFE201D6D7F000001 /* Source */, 29B97317FDCFA39411CA2CEA /* Resources */, ); @@ -505,6 +557,17 @@ name = "Data Store"; sourceTree = ""; }; + CCFF7C1A1FB4FCA100FFBD89 /* AuthenticatorTokenProvider */ = { + isa = PBXGroup; + children = ( + CCFF7C291FB4FCD200FFBD89 /* AuthenticatorTokenProvider.entitlements */, + CCFF7C1B1FB4FCA100FFBD89 /* ActionViewController.swift */, + CCFF7C201FB4FCA100FFBD89 /* Info.plist */, + CCFF7C2A1FB4FF2E00FFBD89 /* Picker.js */, + ); + path = AuthenticatorTokenProvider; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -517,10 +580,12 @@ C910ADBF1BF00ABF00C988F5 /* Lint */, 1D60588F0D05DD3D006BFB54 /* Frameworks */, C944A5531A7ECA5000E08B1E /* Copy Frameworks */, + CCFF7C271FB4FCA100FFBD89 /* Embed App Extensions */, ); buildRules = ( ); dependencies = ( + CCFF7C221FB4FCA100FFBD89 /* PBXTargetDependency */, ); name = Authenticator; productName = Authenticator; @@ -562,6 +627,23 @@ productReference = C9A262CD1E170BD4004E6CEB /* AuthenticatorScreenshots.xctest */; productType = "com.apple.product-type.bundle.ui-testing"; }; + CCFF7C181FB4FCA100FFBD89 /* AuthenticatorTokenProvider */ = { + isa = PBXNativeTarget; + buildConfigurationList = CCFF7C261FB4FCA100FFBD89 /* Build configuration list for PBXNativeTarget "AuthenticatorTokenProvider" */; + buildPhases = ( + CCFF7C151FB4FCA100FFBD89 /* Sources */, + CCFF7C161FB4FCA100FFBD89 /* Frameworks */, + CCFF7C171FB4FCA100FFBD89 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = AuthenticatorTokenProvider; + productName = AuthenticatorTokenProvider; + productReference = CCFF7C191FB4FCA100FFBD89 /* AuthenticatorTokenProvider.appex */; + productType = "com.apple.product-type.app-extension"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -578,6 +660,11 @@ DevelopmentTeam = WD7ETSN9J9; LastSwiftMigration = 0830; ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Keychain = { + enabled = 1; + }; + }; }; C9906A2D1812522100BAEF53 = { LastSwiftMigration = 0830; @@ -603,6 +690,7 @@ 1D6058900D05DD3D006BFB54 /* Authenticator */, C9906A2D1812522100BAEF53 /* AuthenticatorTests */, C9A262CC1E170BD4004E6CEB /* AuthenticatorScreenshots */, + CCFF7C181FB4FCA100FFBD89 /* AuthenticatorTokenProvider */, ); }; /* End PBXProject section */ @@ -626,6 +714,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + CCFF7C171FB4FCA100FFBD89 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CCFF7C2B1FB4FF2E00FFBD89 /* Picker.js in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -717,6 +813,25 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + CCFF7C151FB4FCA100FFBD89 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CCFF7C5D1FB5105500FFBD89 /* ProgressRingView.swift in Sources */, + CCFF7C611FB5126400FFBD89 /* Colors.swift in Sources */, + CCFF7C5F1FB5108100FFBD89 /* Component.swift in Sources */, + CCFF7C5E1FB5105E00FFBD89 /* DisplayTime.swift in Sources */, + CCFF7C621FB5137900FFBD89 /* UITableView+ReusableCells.swift in Sources */, + CCFF7C5A1FB5102C00FFBD89 /* TokenListViewModel.swift in Sources */, + CCFF7C601FB5125700FFBD89 /* TokenRowCell.swift in Sources */, + CCFF7C581FB50EE100FFBD89 /* ActionViewController.swift in Sources */, + CCFF7C591FB5102500FFBD89 /* TokenList.swift in Sources */, + CCFF7C5B1FB5103800FFBD89 /* TokenRowModel.swift in Sources */, + CCFF7C561FB50E3500FFBD89 /* TokenStore.swift in Sources */, + CCFF7C5C1FB5104200FFBD89 /* TableDiff.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -730,6 +845,11 @@ target = 1D6058900D05DD3D006BFB54 /* Authenticator */; targetProxy = C9A262D21E170BD4004E6CEB /* PBXContainerItemProxy */; }; + CCFF7C221FB4FCA100FFBD89 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = CCFF7C181FB4FCA100FFBD89 /* AuthenticatorTokenProvider */; + targetProxy = CCFF7C211FB4FCA100FFBD89 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ @@ -737,8 +857,11 @@ isa = XCBuildConfiguration; baseConfigurationReference = C944A5731A7F772600E08B1E /* iOS-Application.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; BUNDLE_DISPLAY_NAME = "${PRODUCT_NAME} ∆"; + CODE_SIGN_ENTITLEMENTS = Authenticator/Authenticator.entitlements; + CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CURRENT_PROJECT_VERSION = 0; DEVELOPMENT_TEAM = WD7ETSN9J9; @@ -755,8 +878,11 @@ isa = XCBuildConfiguration; baseConfigurationReference = C944A5731A7F772600E08B1E /* iOS-Application.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; BUNDLE_DISPLAY_NAME = "${PRODUCT_NAME}"; + CODE_SIGN_ENTITLEMENTS = Authenticator/Authenticator.entitlements; + CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CURRENT_PROJECT_VERSION = 0; DEVELOPMENT_TEAM = WD7ETSN9J9; @@ -840,6 +966,130 @@ }; name = Release; }; + CCFF7C241FB4FCA100FFBD89 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = AuthenticatorTokenProvider/AuthenticatorTokenProvider.entitlements; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = 99KV9Z6BKV; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = AuthenticatorTokenProvider/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = com.automattic.authenticator.dev.AuthenticatorTokenProvider; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Debug; + }; + CCFF7C251FB4FCA100FFBD89 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = AuthenticatorTokenProvider/AuthenticatorTokenProvider.entitlements; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = 99KV9Z6BKV; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_FILE = AuthenticatorTokenProvider/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_BUNDLE_IDENTIFIER = com.automattic.authenticator.dev.AuthenticatorTokenProvider; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + TARGETED_DEVICE_FAMILY = 1; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -879,6 +1129,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + CCFF7C261FB4FCA100FFBD89 /* Build configuration list for PBXNativeTarget "AuthenticatorTokenProvider" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CCFF7C241FB4FCA100FFBD89 /* Debug */, + CCFF7C251FB4FCA100FFBD89 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; diff --git a/Authenticator/Authenticator.entitlements b/Authenticator/Authenticator.entitlements new file mode 100644 index 00000000..567c298e --- /dev/null +++ b/Authenticator/Authenticator.entitlements @@ -0,0 +1,10 @@ + + + + + keychain-access-groups + + $(AppIdentifierPrefix)com.automattic.authenticator.dev + + + diff --git a/AuthenticatorTokenProvider/ActionViewController.swift b/AuthenticatorTokenProvider/ActionViewController.swift new file mode 100644 index 00000000..5c80b2fb --- /dev/null +++ b/AuthenticatorTokenProvider/ActionViewController.swift @@ -0,0 +1,137 @@ +// +// ActionViewController.swift +// AuthenticatorTokenProvider +// +// Created by Beau Collins on 11/9/17. +// Copyright © 2017 Matt Rubin. All rights reserved. +// + +import UIKit +import Foundation +import MobileCoreServices +import OneTimePassword + +@objc(ActionViewController) + +class ActionViewController: UITableViewController { + + let store: TokenStore + + var viewModel: TokenListViewModel + var nextRefreshTime: Date = Date.distantPast + let component: TokenList = TokenList() + + override init(style: UITableViewStyle) { + do { + store = try KeychainTokenStore( + keychain: Keychain.sharedInstance, + userDefaults: UserDefaults.standard + ) + } catch { + // If the TokenStore could not be created, the app is unusable. + fatalError("Failed to load token store: \(error)") + } + (viewModel, nextRefreshTime) = component.viewModel(for: store.persistentTokens, at: DisplayTime(date: Date())) + super.init(style: style) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override var preferredStatusBarStyle: UIStatusBarStyle { + return .lightContent + } + + override func viewDidLoad() { + super.viewDidLoad() + + self.title = "Authenticator" + self.view.backgroundColor = UIColor.otpBackgroundColor + + // Configure table view + self.tableView.separatorStyle = .none + self.tableView.indicatorStyle = .white + self.tableView.contentInset = UIEdgeInsets(top: 10, left: 0, bottom: 0, right: 0) + self.tableView.allowsSelectionDuringEditing = true + + // Get context from the webpage to highlight potential passwords first + for item in self.extensionContext!.inputItems as! [NSExtensionItem] { + print("Input item \(item.attachments ?? [])") + if let provider = item.attachments?.first as? NSItemProvider { + provider.loadItem(forTypeIdentifier: kUTTypePropertyList as String, options: nil) { (data, error) in + guard error == nil else { + print("failure \(error)") + return + } + guard let result = data as? NSDictionary else { + print("not valid data") + return; + } + print("Provider: \(result["baseURI"] ?? "no uri")") + } + } + } + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return viewModel.rowModels.count + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCellWithClass(TokenRowCell.self) + updateCell(cell, forRowAtIndexPath: indexPath) + return cell + } + + fileprivate func updateCell(_ cell: TokenRowCell, forRowAtIndexPath indexPath: IndexPath) { + let rowModel = viewModel.rowModels[indexPath.row] + cell.updateWithRowModel(rowModel) + // cell.dispatchAction = dispatchAction + } + + override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + return 85 + } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let rowModel = viewModel.rowModels[indexPath.row] + if isEditing { + dispatchAction(rowModel.editAction) + } else { + dispatchAction(rowModel.selectAction) + } + } + + override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + // Dispose of any resources that can be recreated. + } + + @IBAction func done() { + // Return any edited content to the host app. + // This template doesn't do anything, so we just echo the passed in items. + self.extensionContext!.completeRequest(returningItems: self.extensionContext!.inputItems, completionHandler: nil) + } + + func dispatchAction(_ action: TokenList.Action) { + switch action { + case .copyPassword(let password): + self.providePassword(password) + default: + print("action not handled \(action)") + } + } + + func providePassword(_ password: String) { + let item = NSExtensionItem() + let contents: NSDictionary = [ + NSExtensionJavaScriptFinalizeArgumentKey: ["password" : password ] + ] + let passwordItemProvider = NSItemProvider(item: contents, typeIdentifier: kUTTypePropertyList as String) + item.attachments = [passwordItemProvider] + self.extensionContext!.completeRequest(returningItems: [item]) + } + +} + diff --git a/AuthenticatorTokenProvider/AuthenticatorTokenProvider.entitlements b/AuthenticatorTokenProvider/AuthenticatorTokenProvider.entitlements new file mode 100644 index 00000000..567c298e --- /dev/null +++ b/AuthenticatorTokenProvider/AuthenticatorTokenProvider.entitlements @@ -0,0 +1,10 @@ + + + + + keychain-access-groups + + $(AppIdentifierPrefix)com.automattic.authenticator.dev + + + diff --git a/AuthenticatorTokenProvider/Info.plist b/AuthenticatorTokenProvider/Info.plist new file mode 100644 index 00000000..3ad2511b --- /dev/null +++ b/AuthenticatorTokenProvider/Info.plist @@ -0,0 +1,43 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + AuthenticatorTokenProvider + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + XPC! + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + NSExtension + + NSExtensionAttributes + + NSExtensionJavaScriptPreprocessingFile + Picker + NSExtensionActivationRule + + NSExtensionActivationSupportsWebURLWithMaxCount + 1 + NSExtensionActivationSupportsWebPageWithMaxCount + 1 + + + NSExtensionPrincipalClass + ActionViewController + NSExtensionPointIdentifier + com.apple.ui-services + + + diff --git a/AuthenticatorTokenProvider/Picker.js b/AuthenticatorTokenProvider/Picker.js new file mode 100644 index 00000000..955d1bee --- /dev/null +++ b/AuthenticatorTokenProvider/Picker.js @@ -0,0 +1,42 @@ + +function setPassword(password) { + return function (inputNode) { + inputNode.value = password; + } +} + +/* + * Javascript files used for action processing are expected to + * define a global variable of ExtensionPreprocessingJS that can + * have two methods/functions: + * + * - run: executed by the action extension when it wants to get data from the webpage + * - finalize: executed by the action extension when it completes its activity + */ +var ExtensionPreprocessingJS = { + /* + * When an action is initialized it can ask to run this script + * to provide context to the action + */ + run: function(arguments) { + // provide the current URI + // the share extension can use this information to + // highlight what it thinks is the correct password + arguments.completionFunction({ + baseURI: document.baseURI + }); + }, + /* + * Called when the action has completed picking a password + */ + finalize: function(arguments) { + // usually OTP fields are type=tel, but we can't assume this is the case + // as a fallback, the extension should copy the password to the clipboard + // as well + const potentialFields = document.querySelectorAll( 'input[type=text],input[type=tel]' ); + // Use the Array forEach iterator to set the input's value to + // the provided password + [].forEach.call(potentialFields, setPassword(arguments.password)) + } +}; + From 5860468e1b9c401bdb209c8841453c078e46d417 Mon Sep 17 00:00:00 2001 From: Robert Collins Date: Fri, 10 Nov 2017 14:43:16 -0800 Subject: [PATCH 2/7] Fixes an api deprecation problem --- Authenticator/Source/TokenRowModel.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Authenticator/Source/TokenRowModel.swift b/Authenticator/Source/TokenRowModel.swift index d7466e56..37fefb52 100644 --- a/Authenticator/Source/TokenRowModel.swift +++ b/Authenticator/Source/TokenRowModel.swift @@ -64,12 +64,14 @@ struct TokenRowModel: Identifiable { // Group the password into chunks of two digits, separated by spaces. private static func chunkPassword(_ password: String) -> String { - var characters = password.characters + guard var characters = String(password) else { + return password + } let chunkSize = 2 for i in stride(from: chunkSize, to: characters.count, by: chunkSize).reversed() { characters.insert(" ", at: characters.index(characters.startIndex, offsetBy: i)) } - return String(characters) + return characters } } From 092669c6cae61e820da03126bc62ccca250abd17 Mon Sep 17 00:00:00 2001 From: Robert Collins Date: Sun, 12 Nov 2017 00:01:30 -0800 Subject: [PATCH 3/7] mvp - extension icon - lists passwords - selection password fills in from - copies password when selected --- .../Source/ModelBasedViewController.swift | 9 ++ .../Source/OpaqueNavigationController.swift | 9 ++ .../ActionViewController.swift | 137 ------------------ AuthenticatorTokenProvider/Info.plist | 2 +- .../Resources/Images.xcassets/Contents.json | 6 + .../ExtensionIcon.appiconset/Contents.json | 55 +++++++ .../ExtensionIcon2x.png | Bin 0 -> 2401 bytes .../ExtensionIcon3x.png | Bin 0 -> 3823 bytes .../Source/ActionViewController.swift | 41 ++++++ .../Source/Components/Root.swift | 85 +++++++++++ .../Source/ExtensionController.swift | 111 ++++++++++++++ .../Source/PasswordPickerViewController.swift | 119 +++++++++++++++ .../{ => Source}/Picker.js | 0 13 files changed, 436 insertions(+), 138 deletions(-) create mode 100644 Authenticator/Source/ModelBasedViewController.swift create mode 100644 Authenticator/Source/OpaqueNavigationController.swift delete mode 100644 AuthenticatorTokenProvider/ActionViewController.swift create mode 100644 AuthenticatorTokenProvider/Resources/Images.xcassets/Contents.json create mode 100644 AuthenticatorTokenProvider/Resources/Images.xcassets/ExtensionIcon.appiconset/Contents.json create mode 100644 AuthenticatorTokenProvider/Resources/Images.xcassets/ExtensionIcon.appiconset/ExtensionIcon2x.png create mode 100644 AuthenticatorTokenProvider/Resources/Images.xcassets/ExtensionIcon.appiconset/ExtensionIcon3x.png create mode 100644 AuthenticatorTokenProvider/Source/ActionViewController.swift create mode 100644 AuthenticatorTokenProvider/Source/Components/Root.swift create mode 100644 AuthenticatorTokenProvider/Source/ExtensionController.swift create mode 100644 AuthenticatorTokenProvider/Source/PasswordPickerViewController.swift rename AuthenticatorTokenProvider/{ => Source}/Picker.js (100%) diff --git a/Authenticator/Source/ModelBasedViewController.swift b/Authenticator/Source/ModelBasedViewController.swift new file mode 100644 index 00000000..29391249 --- /dev/null +++ b/Authenticator/Source/ModelBasedViewController.swift @@ -0,0 +1,9 @@ +// +// ModelBasedViewController.swift +// Authenticator +// +// Created by Beau Collins on 11/10/17. +// Copyright © 2017 Matt Rubin. All rights reserved. +// + +import Foundation diff --git a/Authenticator/Source/OpaqueNavigationController.swift b/Authenticator/Source/OpaqueNavigationController.swift new file mode 100644 index 00000000..d2505adb --- /dev/null +++ b/Authenticator/Source/OpaqueNavigationController.swift @@ -0,0 +1,9 @@ +// +// OpaqueNavigationController.swift +// Authenticator +// +// Created by Beau Collins on 11/10/17. +// Copyright © 2017 Matt Rubin. All rights reserved. +// + +import Foundation diff --git a/AuthenticatorTokenProvider/ActionViewController.swift b/AuthenticatorTokenProvider/ActionViewController.swift deleted file mode 100644 index 5c80b2fb..00000000 --- a/AuthenticatorTokenProvider/ActionViewController.swift +++ /dev/null @@ -1,137 +0,0 @@ -// -// ActionViewController.swift -// AuthenticatorTokenProvider -// -// Created by Beau Collins on 11/9/17. -// Copyright © 2017 Matt Rubin. All rights reserved. -// - -import UIKit -import Foundation -import MobileCoreServices -import OneTimePassword - -@objc(ActionViewController) - -class ActionViewController: UITableViewController { - - let store: TokenStore - - var viewModel: TokenListViewModel - var nextRefreshTime: Date = Date.distantPast - let component: TokenList = TokenList() - - override init(style: UITableViewStyle) { - do { - store = try KeychainTokenStore( - keychain: Keychain.sharedInstance, - userDefaults: UserDefaults.standard - ) - } catch { - // If the TokenStore could not be created, the app is unusable. - fatalError("Failed to load token store: \(error)") - } - (viewModel, nextRefreshTime) = component.viewModel(for: store.persistentTokens, at: DisplayTime(date: Date())) - super.init(style: style) - } - - required init?(coder aDecoder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override var preferredStatusBarStyle: UIStatusBarStyle { - return .lightContent - } - - override func viewDidLoad() { - super.viewDidLoad() - - self.title = "Authenticator" - self.view.backgroundColor = UIColor.otpBackgroundColor - - // Configure table view - self.tableView.separatorStyle = .none - self.tableView.indicatorStyle = .white - self.tableView.contentInset = UIEdgeInsets(top: 10, left: 0, bottom: 0, right: 0) - self.tableView.allowsSelectionDuringEditing = true - - // Get context from the webpage to highlight potential passwords first - for item in self.extensionContext!.inputItems as! [NSExtensionItem] { - print("Input item \(item.attachments ?? [])") - if let provider = item.attachments?.first as? NSItemProvider { - provider.loadItem(forTypeIdentifier: kUTTypePropertyList as String, options: nil) { (data, error) in - guard error == nil else { - print("failure \(error)") - return - } - guard let result = data as? NSDictionary else { - print("not valid data") - return; - } - print("Provider: \(result["baseURI"] ?? "no uri")") - } - } - } - } - - override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return viewModel.rowModels.count - } - - override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCellWithClass(TokenRowCell.self) - updateCell(cell, forRowAtIndexPath: indexPath) - return cell - } - - fileprivate func updateCell(_ cell: TokenRowCell, forRowAtIndexPath indexPath: IndexPath) { - let rowModel = viewModel.rowModels[indexPath.row] - cell.updateWithRowModel(rowModel) - // cell.dispatchAction = dispatchAction - } - - override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { - return 85 - } - - override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { - let rowModel = viewModel.rowModels[indexPath.row] - if isEditing { - dispatchAction(rowModel.editAction) - } else { - dispatchAction(rowModel.selectAction) - } - } - - override func didReceiveMemoryWarning() { - super.didReceiveMemoryWarning() - // Dispose of any resources that can be recreated. - } - - @IBAction func done() { - // Return any edited content to the host app. - // This template doesn't do anything, so we just echo the passed in items. - self.extensionContext!.completeRequest(returningItems: self.extensionContext!.inputItems, completionHandler: nil) - } - - func dispatchAction(_ action: TokenList.Action) { - switch action { - case .copyPassword(let password): - self.providePassword(password) - default: - print("action not handled \(action)") - } - } - - func providePassword(_ password: String) { - let item = NSExtensionItem() - let contents: NSDictionary = [ - NSExtensionJavaScriptFinalizeArgumentKey: ["password" : password ] - ] - let passwordItemProvider = NSItemProvider(item: contents, typeIdentifier: kUTTypePropertyList as String) - item.attachments = [passwordItemProvider] - self.extensionContext!.completeRequest(returningItems: [item]) - } - -} - diff --git a/AuthenticatorTokenProvider/Info.plist b/AuthenticatorTokenProvider/Info.plist index 3ad2511b..6395dfcc 100644 --- a/AuthenticatorTokenProvider/Info.plist +++ b/AuthenticatorTokenProvider/Info.plist @@ -5,7 +5,7 @@ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName - AuthenticatorTokenProvider + Choose One Time Password CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier diff --git a/AuthenticatorTokenProvider/Resources/Images.xcassets/Contents.json b/AuthenticatorTokenProvider/Resources/Images.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/AuthenticatorTokenProvider/Resources/Images.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/AuthenticatorTokenProvider/Resources/Images.xcassets/ExtensionIcon.appiconset/Contents.json b/AuthenticatorTokenProvider/Resources/Images.xcassets/ExtensionIcon.appiconset/Contents.json new file mode 100644 index 00000000..d76ba1a6 --- /dev/null +++ b/AuthenticatorTokenProvider/Resources/Images.xcassets/ExtensionIcon.appiconset/Contents.json @@ -0,0 +1,55 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "ExtensionIcon2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "ExtensionIcon3x.png", + "scale" : "3x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/AuthenticatorTokenProvider/Resources/Images.xcassets/ExtensionIcon.appiconset/ExtensionIcon2x.png b/AuthenticatorTokenProvider/Resources/Images.xcassets/ExtensionIcon.appiconset/ExtensionIcon2x.png new file mode 100644 index 0000000000000000000000000000000000000000..fefc063a7949923e955935341190122ea201a5d6 GIT binary patch literal 2401 zcmb`J`9Bkk1ILlF&78S;V)Dd>EH`WQWXiqV*U%hU8cE0;-(2HywP&v8+#Xjk(qd5> zPeX{cax_Y5lX47?98Zqq+xPYQ{t3?ypU?ZZ_s{QKSG1iJNDU++A|mBrZ|nY(g#Qh2 zQo6ZQ&3_9+)>_Lp(98u4;dm_maucFm{PrJ?KjoY=2#e z=kA}z(FCoB5B93tC(k55*uR9kh^Ha2Y5696u;n3c+P{IVa4aRM@}sUW!TZ`f$(u{# z#b(_1%4~tiOs)d0Zf&=O%7Clorj#rHX)8= zwPzI*yNTpAu}zRsKNY#)@+qM?tu9sZmhx5o_;@%;RsX$Kz?<0nwDBLJmho&p}4d?@U3OqluO6Q9N6B%ezMSEMyz) z?)z=qH8LmI?96_?sQ{fWgbn>L4ryGgd$tzn*VLA|c&a7)lEQ!w`P^fK^OTP-#Iw!9 zC%#fVu@mqIa1T%oXy!Nc$_JM}%iFf7aq`t+xQ0sO6s4yw6!LQ6x~WEyt8)TvT2pN% zB)RLlhlJFD^DCAiOV>blcdVJ8v7%@+A)8(rcWLhIE0uY!{E4uz@cZjX=xKcg)q-cE62K{nmpANFMN4@; z$7kSXU0||`G!8&vdUVvw1%(OB9Ln?+eo4`aeBoG50pM_NliSy{zdP8rcsAL{WW%$k z)J@>*jyZopA2>08NbJ&Pc=oDkF#3l#_MuNj$%jnH@=3uDFqq>;&F!^NT~{i!5k^8T(=vJq4UNg1cY04O8UKst{H`bF2*@`(a;oZUAO z(qY=4XqXrjM8Fw>T70h9V|=aQ=hdJq%5x)5B@k437Xy;8k|TQ?YPl0#%lt z&=6+_tjy{fW*ff;5oP5tYRxgHlHurT7_rK!Qd4PZIpEz|JGMF`#^6PY#(9Ib4fC{` zT(7yc-`M&kXWX^^!6XD@Z0${SSmMn#!>7kX|KaP05r&>H7b9wb+y=uc9l!3fjW2rg z<7|qWVaVt2Z8Z-n^STjktz_BF9Qo~>&sfr-SAa*}i?PH<**zqBJoJK$xkU!5R&=czr_4;ME%3QV|#ScJ{FxQbhuTqouG%Jsqa!m3n?KW?EVH zIL*f#y~viD=BIfcrcPsG5MFzE+$oI$$VU}k0fmIMKl=RL!A1<~_!q23=j*j&I#m%1 z!j?9YxE0zUAY_Zbb2V3y@K?*)oq-2wJCoMV40OefcJn)#J30H3eM@+7V4xSu0~}qF z>33x`AJwa@$rW6urCz0wan;73c|2c~>5vhd$bJU|rH>wVqQk=4iW4g$Gq#ZI|8;Lk>cw==3sw7_bBWR~pU!HGgx+6Ed%eWrC3obo0Q-(w^M`9n3wV3c zrT6l11665AJ;r+UBdf$$lLmv5`|f2=dlZU@%%?#Or*YmnkYy^BeeKf!HkRRcnmhDG0eKK73KJ69%RQlxPi>n zjoVLOUpeu36AnNBS4y3Nwm~_XajSyyyDL++%+k$%`pB;p=8rcOLf->AlsB65hL_n%C%j^RoL(nSBp>=Z&C)Zf%c z#ANM=iBgzd#k*F+AEPr=ui^)9gpDiX07S@k${Bg8Z zkt%yet^?#Jbo}z^aGf)y0DOJc)XBbRYH=&66!m%zA6&!m+;&(PiIb%#*9`|>jaf_+ zO;W=gSMq{nj5r_I-@GjsaFZ;W?Wct3wu`xJUE1X%>2a7+rHYj^^U)g_6?%TF>+Q*K zN(Wcb+kk|)F0CaYfDsUn)C0>0XdA;l<4@V8!Zzcbiob*<37dNp#qjC=j!{z4edf)p z`B{?UN!xnC4DxxEb>O+SVX7`isY`qeZ|HL4I>N5Nb@pNJby1v2zx+%X{t%!sPBQ;+ lt=Gwarz`$%Q4Hm$nv3?lco>m#?PoKIIGjh@w%TAb{tHDVcH{s6 literal 0 HcmV?d00001 diff --git a/AuthenticatorTokenProvider/Resources/Images.xcassets/ExtensionIcon.appiconset/ExtensionIcon3x.png b/AuthenticatorTokenProvider/Resources/Images.xcassets/ExtensionIcon.appiconset/ExtensionIcon3x.png new file mode 100644 index 0000000000000000000000000000000000000000..5ffcbca4d1781936e42c5396e6c8241131ff0b06 GIT binary patch literal 3823 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 Picker.Effect? { + switch action { + case .tokenListAction(let action): + let effect = tokenList.update(action).flatMap { effect in handleTokenListEffect(effect) } + switch action { + case .copyPassword(let password): + completeRequest(withPassword: password) + return nil + default: + return effect + } + case .cancel: + completeRequest() + return nil + } + } + + private func handleTokenListEffect(_ effect: TokenList.Effect) -> Picker.Effect? { + // honestly nothing needs to happen + return nil + } + + struct ViewModel { + var pageContext: [String] + var tokenList: TokenListViewModel; + } + + func completeRequest(withPassword password: String? = nil) { + guard let password = password else { + extensionContext.completeRequest(returningItems: nil, completionHandler: nil) + return + } + + let item = NSExtensionItem() + let contents: NSDictionary = [ + NSExtensionJavaScriptFinalizeArgumentKey: ["password" : password ] + ] + let passwordItemProvider = NSItemProvider(item: contents, typeIdentifier: kUTTypePropertyList as String) + item.attachments = [passwordItemProvider] + extensionContext.completeRequest(returningItems: [item]) + } +} + +extension Picker { + func viewModel(for tokens: [PersistentToken], at time: DisplayTime) -> (viewModel: ViewModel, nextRefreshTime: Date) { + let (tokenListViewModel, nextRefreshTime) = tokenList.viewModel(for: tokens, at: time) + let viewModel = ViewModel( + pageContext: [], + tokenList: tokenListViewModel + ) + return (viewModel: viewModel, nextRefreshTime: nextRefreshTime) + } +} + diff --git a/AuthenticatorTokenProvider/Source/ExtensionController.swift b/AuthenticatorTokenProvider/Source/ExtensionController.swift new file mode 100644 index 00000000..df6bc485 --- /dev/null +++ b/AuthenticatorTokenProvider/Source/ExtensionController.swift @@ -0,0 +1,111 @@ +// +// AppController.swift +// AuthenticatorTokenProvider +// +// Created by Beau Collins on 11/11/17. +// Copyright © 2017 Matt Rubin. All rights reserved. +// + +import Foundation +import OneTimePassword +import MobileCoreServices + +class ExtensionController { + + var component: Picker { + didSet { updateView() } + } + + let store: TokenStore + + var nextRefreshTime: Date = Date.distantPast { + willSet { + self.timer?.invalidate() + } + didSet { + switch self.nextRefreshTime { + case .distantPast: + return + case .distantFuture: + return + default: + let timer = Timer(fireAt: self.nextRefreshTime, + interval: 0, + target: self, + selector: #selector(updateView), + userInfo: nil, + repeats: false) + // Add the new timer to the main run loop + RunLoop.main.add(timer, forMode: .commonModes) + self.timer = timer + } + } + } + + var timer: Timer? + + lazy var rootViewController: OpaqueNavigationController = { + return OpaqueNavigationController(rootViewController: self.passwordPicker) + }(); + + lazy var passwordPicker: PasswordPickerViewController = { + let (viewModel, nextRefreshTime) = self.component.viewModel(for: self.store.persistentTokens, at: self.now()) + self.nextRefreshTime = nextRefreshTime + return PasswordPickerViewController(viewModel: viewModel, dispatchAction: self.handleAction) + }() + + init(withContext context: NSExtensionContext) { + do { + store = try KeychainTokenStore( + keychain: Keychain.sharedInstance, + userDefaults: UserDefaults.standard + ) + } catch { + // If the TokenStore could not be created, the app is unusable. + fatalError("Failed to load token store: \(error)") + } + component = Picker(extensionContext: context) + prepareContext() + } + + func handleAction(_ action: Picker.Action) { + print("handle action \(action)") + if let effect = component.update(action) { + print("handle side effect \(effect)") + handleEffect(effect) + } + } + + func handleEffect(_ effect: Picker.Effect) { + } + + @objc + func updateView() { + let (viewModel, nextRefreshTime) = component.viewModel(for: store.persistentTokens, at: now()) + self.nextRefreshTime = nextRefreshTime + passwordPicker.updateWithViewModel(viewModel) + } + + func prepareContext() { + for item in self.component.extensionContext.inputItems as! [NSExtensionItem] { + print("Input item \(item.attachments ?? [])") + if let provider = item.attachments?.first as? NSItemProvider { + provider.loadItem(forTypeIdentifier: kUTTypePropertyList as String, options: nil) { (data, error) in + guard error == nil else { + print("failure \(error)") + return + } + guard let result = data as? NSDictionary else { + print("not valid data") + return; + } + print("Provider: \(result["baseURI"] ?? "no uri")") + } + } + } + } + + func now() -> DisplayTime { + return DisplayTime(date: Date()) + } +} diff --git a/AuthenticatorTokenProvider/Source/PasswordPickerViewController.swift b/AuthenticatorTokenProvider/Source/PasswordPickerViewController.swift new file mode 100644 index 00000000..5e0fe968 --- /dev/null +++ b/AuthenticatorTokenProvider/Source/PasswordPickerViewController.swift @@ -0,0 +1,119 @@ +// +// PasswordPickerController.swift +// AuthenticatorTokenProvider +// +// Created by Beau Collins on 11/10/17. +// Copyright © 2017 Matt Rubin. All rights reserved. +// + +import UIKit + +class PasswordPickerViewController: UITableViewController { + + fileprivate let dispatchAction: (Picker.Action) -> Void + fileprivate var viewModel: Picker.ViewModel + fileprivate var ignoreTableViewUpdates = false + + fileprivate var searchBar = SearchField( + frame: CGRect( + origin: .zero, + size: CGSize(width: 0, height: 44) + ) + ) + + required init(viewModel: Picker.ViewModel, dispatchAction: @escaping (Picker.Action) -> Void) { + self.viewModel = viewModel + self.dispatchAction = dispatchAction + super.init(nibName: nil, bundle: nil) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func updateWithViewModel(_ viewModel: Picker.ViewModel) { + self.viewModel = viewModel + tableView.reloadData() + updatePeripheralViews() + } + + func updatePeripheralViews() { + self.searchBar.updateWithViewModel(viewModel.tokenList) + } + + override var preferredStatusBarStyle: UIStatusBarStyle { + return .lightContent + } + + override func viewDidLoad() { + super.viewDidLoad() + + self.title = "Authenticator" + self.view.backgroundColor = UIColor.otpBackgroundColor + + // Configure table view + self.tableView.separatorStyle = .none + self.tableView.indicatorStyle = .white + self.tableView.contentInset = UIEdgeInsets(top: 10, left: 0, bottom: 0, right: 0) + self.tableView.allowsSelectionDuringEditing = true + + self.navigationItem.titleView = searchBar + self.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, + target: self, + action: #selector(cancelPicker)) + + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + self.updatePeripheralViews() + + self.searchBar.textField.addTarget(self, action: #selector(filterTokens), for: .editingChanged) + } + + override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + // Dispose of any resources that can be recreated. + } + + func filterTokens() { + guard let filter = searchBar.text else { + return dispatchAction(.tokenListAction(action: .clearFilter)) + } + dispatchAction(.tokenListAction(action: .filter(filter))) + } + + func cancelPicker() { + dispatchAction(.cancel) + } + + // MARK: - Table view data source + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return viewModel.tokenList.rowModels.count + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCellWithClass(TokenRowCell.self) + updateCell(cell, forRowAtIndexPath: indexPath) + return cell + } + + fileprivate func updateCell(_ cell: TokenRowCell, forRowAtIndexPath indexPath: IndexPath) { + let rowModel = viewModel.tokenList.rowModels[indexPath.row] + cell.updateWithRowModel(rowModel) + } + + override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + return 85 + } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let rowModel = viewModel.tokenList.rowModels[indexPath.row] + dispatchAction(.tokenListAction(action: rowModel.selectAction)) + } + +} + +extension PasswordPickerViewController: ModelBasedViewController { +} diff --git a/AuthenticatorTokenProvider/Picker.js b/AuthenticatorTokenProvider/Source/Picker.js similarity index 100% rename from AuthenticatorTokenProvider/Picker.js rename to AuthenticatorTokenProvider/Source/Picker.js From e4d3b9b7911614769f9257d0bd8fe8948f982355 Mon Sep 17 00:00:00 2001 From: Robert Collins Date: Sun, 12 Nov 2017 00:04:35 -0800 Subject: [PATCH 4/7] Project file changes --- Authenticator.xcodeproj/project.pbxproj | 67 +++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 4 deletions(-) diff --git a/Authenticator.xcodeproj/project.pbxproj b/Authenticator.xcodeproj/project.pbxproj index 8f70c631..aa8ac680 100644 --- a/Authenticator.xcodeproj/project.pbxproj +++ b/Authenticator.xcodeproj/project.pbxproj @@ -59,6 +59,15 @@ C9EB448E1C52A74200ACFA87 /* Component.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9EB448D1C52A74200ACFA87 /* Component.swift */; }; C9EB44901C52AE4500ACFA87 /* AppController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9EB448F1C52AE4500ACFA87 /* AppController.swift */; }; C9F7A8611C4D90B50082E5AE /* TokenStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9F7A8601C4D90B50082E5AE /* TokenStore.swift */; }; + CC00FE311FB66BD600A327C0 /* PasswordPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC00FE301FB66BD600A327C0 /* PasswordPickerViewController.swift */; }; + CC00FE321FB66C1C00A327C0 /* SearchField.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC46C4701DB3007D00EB4605 /* SearchField.swift */; }; + CC00FE371FB670D100A327C0 /* OpaqueNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC00FE361FB670D100A327C0 /* OpaqueNavigationController.swift */; }; + CC00FE391FB6712400A327C0 /* ModelBasedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC00FE381FB6712400A327C0 /* ModelBasedViewController.swift */; }; + CC00FE3A1FB6719200A327C0 /* OpaqueNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC00FE361FB670D100A327C0 /* OpaqueNavigationController.swift */; }; + CC00FE3D1FB7884100A327C0 /* Root.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC00FE3C1FB7884100A327C0 /* Root.swift */; }; + CC00FE3E1FB790E500A327C0 /* ModelBasedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC00FE381FB6712400A327C0 /* ModelBasedViewController.swift */; }; + CC00FE401FB806BF00A327C0 /* ExtensionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC00FE3F1FB806BF00A327C0 /* ExtensionController.swift */; }; + CC00FE441FB8275300A327C0 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CC00FE431FB8275300A327C0 /* Images.xcassets */; }; CC46C4711DB3007D00EB4605 /* SearchField.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC46C4701DB3007D00EB4605 /* SearchField.swift */; }; CC46C4731DB3040300EB4605 /* TokenListTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC46C4721DB3040300EB4605 /* TokenListTests.swift */; }; CC471EEB1DC027A6006858AC /* TokenListViewControllerTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC471EEA1DC027A6006858AC /* TokenListViewControllerTest.swift */; }; @@ -215,6 +224,12 @@ C9EB448D1C52A74200ACFA87 /* Component.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Component.swift; sourceTree = ""; }; C9EB448F1C52AE4500ACFA87 /* AppController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppController.swift; sourceTree = ""; }; C9F7A8601C4D90B50082E5AE /* TokenStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokenStore.swift; sourceTree = ""; }; + CC00FE301FB66BD600A327C0 /* PasswordPickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordPickerViewController.swift; sourceTree = ""; }; + CC00FE361FB670D100A327C0 /* OpaqueNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpaqueNavigationController.swift; sourceTree = ""; }; + CC00FE381FB6712400A327C0 /* ModelBasedViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModelBasedViewController.swift; sourceTree = ""; }; + CC00FE3C1FB7884100A327C0 /* Root.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Root.swift; sourceTree = ""; }; + CC00FE3F1FB806BF00A327C0 /* ExtensionController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionController.swift; sourceTree = ""; }; + CC00FE431FB8275300A327C0 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; CC46C4701DB3007D00EB4605 /* SearchField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchField.swift; sourceTree = ""; }; CC46C4721DB3040300EB4605 /* TokenListTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokenListTests.swift; sourceTree = ""; }; CC471EEA1DC027A6006858AC /* TokenListViewControllerTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TokenListViewControllerTest.swift; sourceTree = ""; }; @@ -431,6 +446,8 @@ C93BD6221C167CD100FFFB8F /* Root.swift */, C93BD6281C168EBF00FFFB8F /* RootViewModel.swift */, C93BD6241C16841D00FFFB8F /* RootViewController.swift */, + CC00FE361FB670D100A327C0 /* OpaqueNavigationController.swift */, + CC00FE381FB6712400A327C0 /* ModelBasedViewController.swift */, ); name = "Root Component"; sourceTree = ""; @@ -557,13 +574,41 @@ name = "Data Store"; sourceTree = ""; }; + CC00FE3B1FB7881300A327C0 /* Components */ = { + isa = PBXGroup; + children = ( + CC00FE3C1FB7884100A327C0 /* Root.swift */, + ); + path = Components; + sourceTree = ""; + }; + CC00FE411FB817CB00A327C0 /* Source */ = { + isa = PBXGroup; + children = ( + CCFF7C1B1FB4FCA100FFBD89 /* ActionViewController.swift */, + CC00FE301FB66BD600A327C0 /* PasswordPickerViewController.swift */, + CC00FE3F1FB806BF00A327C0 /* ExtensionController.swift */, + CCFF7C2A1FB4FF2E00FFBD89 /* Picker.js */, + CC00FE3B1FB7881300A327C0 /* Components */, + ); + path = Source; + sourceTree = ""; + }; + CC00FE421FB8186800A327C0 /* Resources */ = { + isa = PBXGroup; + children = ( + CC00FE431FB8275300A327C0 /* Images.xcassets */, + ); + path = Resources; + sourceTree = ""; + }; CCFF7C1A1FB4FCA100FFBD89 /* AuthenticatorTokenProvider */ = { isa = PBXGroup; children = ( CCFF7C291FB4FCD200FFBD89 /* AuthenticatorTokenProvider.entitlements */, - CCFF7C1B1FB4FCA100FFBD89 /* ActionViewController.swift */, CCFF7C201FB4FCA100FFBD89 /* Info.plist */, - CCFF7C2A1FB4FF2E00FFBD89 /* Picker.js */, + CC00FE421FB8186800A327C0 /* Resources */, + CC00FE411FB817CB00A327C0 /* Source */, ); path = AuthenticatorTokenProvider; sourceTree = ""; @@ -674,6 +719,9 @@ CreatedOnToolsVersion = 8.2.1; TestTargetID = 1D6058900D05DD3D006BFB54; }; + CCFF7C181FB4FCA100FFBD89 = { + ProvisioningStyle = Automatic; + }; }; }; buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Authenticator" */; @@ -719,6 +767,7 @@ buildActionMask = 2147483647; files = ( CCFF7C2B1FB4FF2E00FFBD89 /* Picker.js in Resources */, + CC00FE441FB8275300A327C0 /* Images.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -746,7 +795,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + CC00FE391FB6712400A327C0 /* ModelBasedViewController.swift in Sources */, C93AD15219CD51BE007480E9 /* Colors.swift in Sources */, + CC00FE371FB670D100A327C0 /* OpaqueNavigationController.swift in Sources */, C9DE02E71ED2234D00D7E01C /* InfoList.swift in Sources */, C93BD6251C16841D00FFFB8F /* RootViewController.swift in Sources */, C9919CE01BA721A1006237C1 /* ButtonHeaderView.swift in Sources */, @@ -819,13 +870,19 @@ files = ( CCFF7C5D1FB5105500FFBD89 /* ProgressRingView.swift in Sources */, CCFF7C611FB5126400FFBD89 /* Colors.swift in Sources */, + CC00FE311FB66BD600A327C0 /* PasswordPickerViewController.swift in Sources */, CCFF7C5F1FB5108100FFBD89 /* Component.swift in Sources */, + CC00FE321FB66C1C00A327C0 /* SearchField.swift in Sources */, + CC00FE401FB806BF00A327C0 /* ExtensionController.swift in Sources */, + CC00FE3A1FB6719200A327C0 /* OpaqueNavigationController.swift in Sources */, CCFF7C5E1FB5105E00FFBD89 /* DisplayTime.swift in Sources */, CCFF7C621FB5137900FFBD89 /* UITableView+ReusableCells.swift in Sources */, CCFF7C5A1FB5102C00FFBD89 /* TokenListViewModel.swift in Sources */, + CC00FE3D1FB7884100A327C0 /* Root.swift in Sources */, CCFF7C601FB5125700FFBD89 /* TokenRowCell.swift in Sources */, CCFF7C581FB50EE100FFBD89 /* ActionViewController.swift in Sources */, CCFF7C591FB5102500FFBD89 /* TokenList.swift in Sources */, + CC00FE3E1FB790E500A327C0 /* ModelBasedViewController.swift in Sources */, CCFF7C5B1FB5103800FFBD89 /* TokenRowModel.swift in Sources */, CCFF7C561FB50E3500FFBD89 /* TokenStore.swift in Sources */, CCFF7C5C1FB5104200FFBD89 /* TableDiff.swift in Sources */, @@ -971,6 +1028,7 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = ExtensionIcon; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -1001,7 +1059,7 @@ CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; - DEVELOPMENT_TEAM = 99KV9Z6BKV; + DEVELOPMENT_TEAM = ""; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -1036,6 +1094,7 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_APPICON_NAME = ExtensionIcon; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -1066,7 +1125,7 @@ CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEVELOPMENT_TEAM = 99KV9Z6BKV; + DEVELOPMENT_TEAM = ""; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; From 6bf53be085ed1f3fd77859350c12d4380388ae0b Mon Sep 17 00:00:00 2001 From: Robert Collins Date: Sun, 12 Nov 2017 00:05:24 -0800 Subject: [PATCH 5/7] Splits files that can be reused by the app extension --- .../Source/ModelBasedViewController.swift | 8 +++++ .../Source/OpaqueNavigationController.swift | 24 ++++++++++++++- Authenticator/Source/RootViewController.swift | 30 ------------------- 3 files changed, 31 insertions(+), 31 deletions(-) diff --git a/Authenticator/Source/ModelBasedViewController.swift b/Authenticator/Source/ModelBasedViewController.swift index 29391249..4a62c768 100644 --- a/Authenticator/Source/ModelBasedViewController.swift +++ b/Authenticator/Source/ModelBasedViewController.swift @@ -7,3 +7,11 @@ // import Foundation + +protocol ModelBasedViewController { + associatedtype ViewModel + associatedtype Action + + init(viewModel: ViewModel, dispatchAction: @escaping (Action) -> Void) + func updateWithViewModel(_ viewModel: ViewModel) +} diff --git a/Authenticator/Source/OpaqueNavigationController.swift b/Authenticator/Source/OpaqueNavigationController.swift index d2505adb..24888370 100644 --- a/Authenticator/Source/OpaqueNavigationController.swift +++ b/Authenticator/Source/OpaqueNavigationController.swift @@ -6,4 +6,26 @@ // Copyright © 2017 Matt Rubin. All rights reserved. // -import Foundation +import UIKit + +class OpaqueNavigationController: UINavigationController { + override func viewDidLoad() { + super.viewDidLoad() + + navigationBar.isTranslucent = false + navigationBar.barTintColor = UIColor.otpBarBackgroundColor + navigationBar.tintColor = UIColor.otpBarForegroundColor + navigationBar.titleTextAttributes = [ + NSForegroundColorAttributeName: UIColor.otpBarForegroundColor, + NSFontAttributeName: UIFont.systemFont(ofSize: 20, weight: UIFontWeightLight), + ] + + toolbar.isTranslucent = false + toolbar.barTintColor = UIColor.otpBarBackgroundColor + toolbar.tintColor = UIColor.otpBarForegroundColor + } + + override var preferredStatusBarStyle: UIStatusBarStyle { + return .lightContent + } +} diff --git a/Authenticator/Source/RootViewController.swift b/Authenticator/Source/RootViewController.swift index 7fadf21f..8356a9b1 100644 --- a/Authenticator/Source/RootViewController.swift +++ b/Authenticator/Source/RootViewController.swift @@ -25,28 +25,6 @@ import UIKit -class OpaqueNavigationController: UINavigationController { - override func viewDidLoad() { - super.viewDidLoad() - - navigationBar.isTranslucent = false - navigationBar.barTintColor = UIColor.otpBarBackgroundColor - navigationBar.tintColor = UIColor.otpBarForegroundColor - navigationBar.titleTextAttributes = [ - NSForegroundColorAttributeName: UIColor.otpBarForegroundColor, - NSFontAttributeName: UIFont.systemFont(ofSize: 20, weight: UIFontWeightLight), - ] - - toolbar.isTranslucent = false - toolbar.barTintColor = UIColor.otpBarBackgroundColor - toolbar.tintColor = UIColor.otpBarForegroundColor - } - - override var preferredStatusBarStyle: UIStatusBarStyle { - return .lightContent - } -} - class RootViewController: OpaqueNavigationController { fileprivate var currentViewModel: Root.ViewModel @@ -101,14 +79,6 @@ class RootViewController: OpaqueNavigationController { } } -protocol ModelBasedViewController { - associatedtype ViewModel - associatedtype Action - - init(viewModel: ViewModel, dispatchAction: @escaping (Action) -> Void) - func updateWithViewModel(_ viewModel: ViewModel) -} - extension TokenScannerViewController: ModelBasedViewController {} extension TokenFormViewController: ModelBasedViewController {} extension InfoListViewController: ModelBasedViewController {} From 62cea35acc2a1290f0594c3c5e74a42dcc17c2f5 Mon Sep 17 00:00:00 2001 From: Robert Collins Date: Sun, 12 Nov 2017 00:11:45 -0800 Subject: [PATCH 6/7] Sets up correct app identifiers and keychain group identifiers --- Authenticator.xcodeproj/project.pbxproj | 16 ++++++++++------ Authenticator/Authenticator.entitlements | 2 +- .../AuthenticatorTokenProvider.entitlements | 2 +- AuthenticatorTokenProvider/Info.plist | 12 ++++++------ 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/Authenticator.xcodeproj/project.pbxproj b/Authenticator.xcodeproj/project.pbxproj index aa8ac680..11fb6d94 100644 --- a/Authenticator.xcodeproj/project.pbxproj +++ b/Authenticator.xcodeproj/project.pbxproj @@ -702,7 +702,6 @@ ORGANIZATIONNAME = "Matt Rubin"; TargetAttributes = { 1D6058900D05DD3D006BFB54 = { - DevelopmentTeam = WD7ETSN9J9; LastSwiftMigration = 0830; ProvisioningStyle = Automatic; SystemCapabilities = { @@ -721,6 +720,11 @@ }; CCFF7C181FB4FCA100FFBD89 = { ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Keychain = { + enabled = 1; + }; + }; }; }; }; @@ -921,7 +925,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CURRENT_PROJECT_VERSION = 0; - DEVELOPMENT_TEAM = WD7ETSN9J9; + DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = Authenticator/Resources/Info.plist; PRODUCT_BUNDLE_IDENTIFIER = me.mattrubin.authenticator.dev; PRODUCT_NAME = Authenticator; @@ -942,9 +946,9 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CURRENT_PROJECT_VERSION = 0; - DEVELOPMENT_TEAM = WD7ETSN9J9; + DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = Authenticator/Resources/Info.plist; - PRODUCT_BUNDLE_IDENTIFIER = me.mattrubin.authenticator; + PRODUCT_BUNDLE_IDENTIFIER = me.mattrubin.authenticator.dev; PRODUCT_NAME = Authenticator; TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; @@ -1079,7 +1083,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.automattic.authenticator.dev.AuthenticatorTokenProvider; + PRODUCT_BUNDLE_IDENTIFIER = me.mattrubin.authenticator.AuthenticatorTokenProvider.dev; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -1139,7 +1143,7 @@ INFOPLIST_FILE = AuthenticatorTokenProvider/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; MTL_ENABLE_DEBUG_INFO = NO; - PRODUCT_BUNDLE_IDENTIFIER = com.automattic.authenticator.dev.AuthenticatorTokenProvider; + PRODUCT_BUNDLE_IDENTIFIER = me.mattrubin.authenticator.AuthenticatorTokenProvider.dev; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; SKIP_INSTALL = YES; diff --git a/Authenticator/Authenticator.entitlements b/Authenticator/Authenticator.entitlements index 567c298e..4dabe27e 100644 --- a/Authenticator/Authenticator.entitlements +++ b/Authenticator/Authenticator.entitlements @@ -4,7 +4,7 @@ keychain-access-groups - $(AppIdentifierPrefix)com.automattic.authenticator.dev + $(AppIdentifierPrefix)me.mattrubin.authenticator.dev diff --git a/AuthenticatorTokenProvider/AuthenticatorTokenProvider.entitlements b/AuthenticatorTokenProvider/AuthenticatorTokenProvider.entitlements index 567c298e..4dabe27e 100644 --- a/AuthenticatorTokenProvider/AuthenticatorTokenProvider.entitlements +++ b/AuthenticatorTokenProvider/AuthenticatorTokenProvider.entitlements @@ -4,7 +4,7 @@ keychain-access-groups - $(AppIdentifierPrefix)com.automattic.authenticator.dev + $(AppIdentifierPrefix)me.mattrubin.authenticator.dev diff --git a/AuthenticatorTokenProvider/Info.plist b/AuthenticatorTokenProvider/Info.plist index 6395dfcc..ce878dd8 100644 --- a/AuthenticatorTokenProvider/Info.plist +++ b/AuthenticatorTokenProvider/Info.plist @@ -24,20 +24,20 @@ NSExtensionAttributes - NSExtensionJavaScriptPreprocessingFile - Picker NSExtensionActivationRule - NSExtensionActivationSupportsWebURLWithMaxCount - 1 NSExtensionActivationSupportsWebPageWithMaxCount 1 + NSExtensionActivationSupportsWebURLWithMaxCount + 1 + NSExtensionJavaScriptPreprocessingFile + Picker - NSExtensionPrincipalClass - ActionViewController NSExtensionPointIdentifier com.apple.ui-services + NSExtensionPrincipalClass + ActionViewController From 1fbaf51414e2226ad4d0d8be22e9efeb1347abb9 Mon Sep 17 00:00:00 2001 From: Robert Collins Date: Mon, 13 Nov 2017 12:12:05 -0800 Subject: [PATCH 7/7] Fixing build file --- Authenticator.xcodeproj/project.pbxproj | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Authenticator.xcodeproj/project.pbxproj b/Authenticator.xcodeproj/project.pbxproj index 11fb6d94..1c88730a 100644 --- a/Authenticator.xcodeproj/project.pbxproj +++ b/Authenticator.xcodeproj/project.pbxproj @@ -702,6 +702,7 @@ ORGANIZATIONNAME = "Matt Rubin"; TargetAttributes = { 1D6058900D05DD3D006BFB54 = { + DevelopmentTeam = WD7ETSN9J9; LastSwiftMigration = 0830; ProvisioningStyle = Automatic; SystemCapabilities = { @@ -719,6 +720,7 @@ TestTargetID = 1D6058900D05DD3D006BFB54; }; CCFF7C181FB4FCA100FFBD89 = { + DevelopmentTeam = WD7ETSN9J9; ProvisioningStyle = Automatic; SystemCapabilities = { com.apple.Keychain = { @@ -925,7 +927,7 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CURRENT_PROJECT_VERSION = 0; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = WD7ETSN9J9; INFOPLIST_FILE = Authenticator/Resources/Info.plist; PRODUCT_BUNDLE_IDENTIFIER = me.mattrubin.authenticator.dev; PRODUCT_NAME = Authenticator; @@ -946,9 +948,9 @@ CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CURRENT_PROJECT_VERSION = 0; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = WD7ETSN9J9; INFOPLIST_FILE = Authenticator/Resources/Info.plist; - PRODUCT_BUNDLE_IDENTIFIER = me.mattrubin.authenticator.dev; + PRODUCT_BUNDLE_IDENTIFIER = me.mattrubin.authenticator; PRODUCT_NAME = Authenticator; TARGETED_DEVICE_FAMILY = 1; VERSIONING_SYSTEM = "apple-generic"; @@ -1063,7 +1065,7 @@ CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = WD7ETSN9J9; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -1083,7 +1085,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; - PRODUCT_BUNDLE_IDENTIFIER = me.mattrubin.authenticator.AuthenticatorTokenProvider.dev; + PRODUCT_BUNDLE_IDENTIFIER = me.mattrubin.authenticator.dev.AuthenticatorTokenProvider; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; SKIP_INSTALL = YES; @@ -1129,7 +1131,7 @@ CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = WD7ETSN9J9; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -1143,7 +1145,7 @@ INFOPLIST_FILE = AuthenticatorTokenProvider/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; MTL_ENABLE_DEBUG_INFO = NO; - PRODUCT_BUNDLE_IDENTIFIER = me.mattrubin.authenticator.AuthenticatorTokenProvider.dev; + PRODUCT_BUNDLE_IDENTIFIER = me.mattrubin.authenticator.AuthenticatorTokenProvider; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; SKIP_INSTALL = YES;