Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Webpage Action Extension #234

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from
Open
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
324 changes: 324 additions & 0 deletions Authenticator.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions Authenticator/Authenticator.entitlements
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)me.mattrubin.authenticator.dev</string>
</array>
</dict>
</plist>
17 changes: 17 additions & 0 deletions Authenticator/Source/ModelBasedViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//
// ModelBasedViewController.swift
// Authenticator
//
// Created by Beau Collins on 11/10/17.
// Copyright © 2017 Matt Rubin. All rights reserved.
//

import Foundation

protocol ModelBasedViewController {
associatedtype ViewModel
associatedtype Action

init(viewModel: ViewModel, dispatchAction: @escaping (Action) -> Void)
func updateWithViewModel(_ viewModel: ViewModel)
}
31 changes: 31 additions & 0 deletions Authenticator/Source/OpaqueNavigationController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// OpaqueNavigationController.swift
// Authenticator
//
// Created by Beau Collins on 11/10/17.
// Copyright © 2017 Matt Rubin. All rights reserved.
//

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
}
}
30 changes: 0 additions & 30 deletions Authenticator/Source/RootViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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 {}
Expand Down
6 changes: 4 additions & 2 deletions Authenticator/Source/TokenRowModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}

Expand Down
10 changes: 10 additions & 0 deletions AuthenticatorTokenProvider/AuthenticatorTokenProvider.entitlements
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)me.mattrubin.authenticator.dev</string>
</array>
</dict>
</plist>
43 changes: 43 additions & 0 deletions AuthenticatorTokenProvider/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Choose One Time Password</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>NSExtensionActivationRule</key>
<dict>
<key>NSExtensionActivationSupportsWebPageWithMaxCount</key>
<integer>1</integer>
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
<integer>1</integer>
</dict>
<key>NSExtensionJavaScriptPreprocessingFile</key>
<string>Picker</string>
</dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.ui-services</string>
<key>NSExtensionPrincipalClass</key>
<string>ActionViewController</string>
</dict>
</dict>
</plist>
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Original file line number Diff line number Diff line change
@@ -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"
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
41 changes: 41 additions & 0 deletions AuthenticatorTokenProvider/Source/ActionViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// ActionViewController.swift
// AuthenticatorTokenProvider
//
// Created by Beau Collins on 11/9/17.
// Copyright © 2017 Matt Rubin. All rights reserved.
//

import UIKit
import Foundation
import MobileCoreServices

@objc(ActionViewController)

class ActionViewController: UIViewController {

lazy var extensionController: ExtensionController = {
return ExtensionController(withContext: self.extensionContext!)
}()

override func viewDidLoad() {
// Get context from the webpage to highlight potential passwords first
super.viewDidLoad()
present(extensionController.rootViewController, animated: false, completion: nil)
}

override var preferredStatusBarStyle: UIStatusBarStyle {
return self.extensionController.rootViewController.preferredStatusBarStyle
}

override func loadView() {
self.view = UIView(frame: UIScreen.main.bounds)
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

}

85 changes: 85 additions & 0 deletions AuthenticatorTokenProvider/Source/Components/Root.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
//
// Root.swift
// AuthenticatorTokenProvider
//
// Created by Beau Collins on 11/11/17.
// Copyright © 2017 Matt Rubin. All rights reserved.
//

import Foundation
import OneTimePassword
import MobileCoreServices

struct Picker: Component {

enum Action {
case cancel
case tokenListAction(action: TokenList.Action)
}

enum Effect {

}

var tokenList: TokenList
let extensionContext: NSExtensionContext

init(extensionContext context: NSExtensionContext) {
tokenList = TokenList()
extensionContext = context
}

mutating func update(_ action: Picker.Action) -> 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)
}
}

Loading