Skip to content

Commit

Permalink
Merge branch 'develop' into releases/1.4.x
Browse files Browse the repository at this point in the history
  • Loading branch information
kober32 committed Aug 25, 2022
2 parents 064ba55 + 8333c32 commit 8632bee
Show file tree
Hide file tree
Showing 11 changed files with 129 additions and 18 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,10 @@ Library/
# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
#Carthage/Checkouts
Carthage/Checkouts
Carthage
#ignoring gitmodules to disable dependency propagation of the carthage
.gitmodules

*.jar

Expand Down
2 changes: 1 addition & 1 deletion Cartfile
Original file line number Diff line number Diff line change
@@ -1 +1 @@
github "wultra/networking-apple" "release/1.1.x"
github "wultra/networking-apple" "1.1.4"
6 changes: 3 additions & 3 deletions Cartfile.resolved
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
binary "https://raw.githubusercontent.com/wultra/powerauth-mobile-sdk-spm/develop/PowerAuth2.json" "1.7.1"
binary "https://raw.githubusercontent.com/wultra/powerauth-mobile-sdk-spm/develop/PowerAuthCore.json" "1.7.1"
github "wultra/networking-apple" "eaf413b0ef0fcf2c5cef9c3ff0735b2643450e8c"
binary "https://raw.githubusercontent.com/wultra/powerauth-mobile-sdk-spm/develop/PowerAuth2.json" "1.7.2"
binary "https://raw.githubusercontent.com/wultra/powerauth-mobile-sdk-spm/develop/PowerAuthCore.json" "1.7.2"
github "wultra/networking-apple" "1.1.4"
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import Foundation
/// `WMTUserOperation` is object returned from the backend that can be either approved or rejected.
/// It is usually visually presented to the user as a non-editable form with information, about
/// the real-world operation (for example login or payment).
public class WMTUserOperation: WMTOperation, Codable {
open class WMTUserOperation: WMTOperation, Codable {

/// Unique operation identifier
public let id: String
Expand Down
9 changes: 9 additions & 0 deletions WultraMobileTokenSDK/Operations/QR/WMTQROperation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,15 @@ public struct QROperationFlags {

/// If true, then 2FA signature with biometry factor can be used for operation confirmation.
public let allowBiometryFactor: Bool

/// If confirm/reject buttons should be flipped in the UI. This can be useful to test users attention.
public let flipButtons: Bool

/// When the operation is considered a "potential fraud" on the server, a warning UI should be displayed to the user.
public let fraudWarning: Bool

/// Block confirmation when call is active.
public let blockWhenOnCall: Bool
}

/// The `WMTQROperationData` structure defines operation data in QR operation.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,11 @@ public class WMTQROperationParser {
/// Parses given string into QROperationFlags structure
private func parseOperationFlags(string: String) -> QROperationFlags {
return QROperationFlags(
allowBiometryFactor: string.contains("B"))
allowBiometryFactor: string.contains("B"),
flipButtons: string.contains("X"),
fraudWarning: string.contains("F"),
blockWhenOnCall: string.contains("C")
)
}

/// Returns true if provided string is in Base64 format and encoded data's length
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ import WultraPowerAuthNetworking

enum WMTOperationEndpoints {

enum List {
typealias EndpointType = WPNEndpointSignedWithToken<WPNRequestBase, WPNResponseArray<WMTUserOperation>>
static let endpoint: EndpointType = WPNEndpointSignedWithToken(endpointURLPath: "/api/auth/token/app/operation/list", tokenName: "possession_universal")
enum List<T: WMTUserOperation> {
typealias EndpointType = WPNEndpointSignedWithToken<WPNRequestBase, WPNResponseArray<T>>
static var endpoint: EndpointType { WPNEndpointSignedWithToken(endpointURLPath: "/api/auth/token/app/operation/list", tokenName: "possession_universal") }
}

enum History {
Expand Down
31 changes: 28 additions & 3 deletions WultraMobileTokenSDK/Operations/Service/WMTOperationsImpl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,21 @@ public extension PowerAuthSDK {
/// - pollingOptions: Polling feature configuration
/// - Returns: Operations service
func createWMTOperations(networkingConfig: WPNConfig, pollingOptions: WMTOperationsPollingOptions = []) -> WMTOperations {
return WMTOperationsImpl(networking: WPNNetworkingService(powerAuth: self, config: networkingConfig, serviceName: "WMTOperations"), pollingOptions: pollingOptions)
return createWMTOperations(networkingConfig: networkingConfig, pollingOptions: pollingOptions, customUserOperationType: WMTUserOperation.self)
}

/// Creates instance of the `WMTOperations` on top of the PowerAuth instance.
/// - Parameters:
/// - networkingConfig: Networking service config
/// - pollingOptions: Polling feature configuration
/// - customUserOperationType: All user operations fetched from the server will be decoded as the given type. Make sure such type properly conforms to the Codable protocol.
/// - Returns: Operations service
func createWMTOperations<T: WMTUserOperation>(
networkingConfig: WPNConfig,
pollingOptions: WMTOperationsPollingOptions = [],
customUserOperationType: T.Type
) -> WMTOperations {
return WMTOperationsImpl<T>(networking: WPNNetworkingService(powerAuth: self, config: networkingConfig, serviceName: "WMTOperations"), pollingOptions: pollingOptions)
}
}

Expand All @@ -40,7 +54,16 @@ public extension WPNNetworkingService {
/// - pollingOptions: Polling feature configuration
/// - Returns: Operations service
func createWMTOperations(pollingOptions: WMTOperationsPollingOptions = []) -> WMTOperations {
return WMTOperationsImpl(networking: self, pollingOptions: pollingOptions)
return createWMTOperations(pollingOptions: pollingOptions, customUserOperationType: WMTUserOperation.self)
}

/// Creates instance of the `WMTOperations` on top of the WPNNetworkingService/PowerAuth instance.
/// - Parameters:
/// - pollingOptions: Polling feature configuration
/// - customUserOperationType: All user operations fetched from the server will be decoded as the given type. Make sure such type properly conforms to the Codable protocol.
/// - Returns: Operations service
func createWMTOperations<T: WMTUserOperation>(pollingOptions: WMTOperationsPollingOptions = [], customUserOperationType: T.Type) -> WMTOperations {
return WMTOperationsImpl<T>(networking: self, pollingOptions: pollingOptions)
}
}

Expand All @@ -64,7 +87,7 @@ public extension WMTErrorReason {
static let operations_QROperationFailed = WMTErrorReason(rawValue: "operations_QRFailed")
}

class WMTOperationsImpl: WMTOperations {
class WMTOperationsImpl<T: WMTUserOperation>: WMTOperations {

// Dependencies
private lazy var powerAuth = networking.powerAuth
Expand Down Expand Up @@ -211,6 +234,7 @@ class WMTOperationsImpl: WMTOperations {
}
}

// DEPRECATED, REMOVE IN THE FUTURE
func authorize(operation: WMTOperation, authentication: PowerAuthAuthentication, completion: @escaping(WMTError?) -> Void) -> Operation? {
return authorize(operation: operation, with: authentication) { result in
switch result {
Expand Down Expand Up @@ -244,6 +268,7 @@ class WMTOperationsImpl: WMTOperations {
}
}

// DEPRECATED, REMOVE IN THE FUTURE
func reject(operation: WMTOperation, reason: WMTRejectionReason, completion: @escaping(WMTError?) -> Void) -> Operation? {
return reject(operation: operation, with: reason) { result in
switch result {
Expand Down
26 changes: 22 additions & 4 deletions WultraMobileTokenSDKTests/QROperationParserTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class QROperationParserTests: XCTestCase {
title: String = "Payment",
message: String = "Please confirm this payment",
operationData: String = "A1*A100CZK*ICZ2730300000001165254011*D20180425*Thello world",
flags: String = "B",
flags: String = "BCFX",
otherAttrs: [String]? = nil,
nonce: String = "AD8bOO0Df73kNaIGb3Vmpg==",
signingKey: String = "0",
Expand All @@ -44,7 +44,7 @@ class QROperationParserTests: XCTestCase {
"Payment\n" +
"Please confirm this payment\n" +
"A1*A100CZK*ICZ2730300000001165254011*D20180425*Thello world\n" +
"B\n" +
"BCFX\n" +
"AD8bOO0Df73kNaIGb3Vmpg==\n" +
"0").data(using: .utf8)

Expand All @@ -57,6 +57,9 @@ class QROperationParserTests: XCTestCase {
XCTAssertTrue(operation.title == "Payment")
XCTAssertTrue(operation.message == "Please confirm this payment")
XCTAssertTrue(operation.flags.allowBiometryFactor == true)
XCTAssertTrue(operation.flags.flipButtons == true)
XCTAssertTrue(operation.flags.fraudWarning == true)
XCTAssertTrue(operation.flags.blockWhenOnCall == true)
XCTAssertTrue(operation.nonce == "AD8bOO0Df73kNaIGb3Vmpg==")
XCTAssertTrue(operation.signature.signature == "MEYCIQDby1Uq+MaxiAAGzKmE/McHzNOUrvAP2qqGBvSgcdtyjgIhAMo1sgqNa1pPZTFBhhKvCKFLGDuHuTTYexdmHFjUUIJW")
XCTAssertTrue(operation.signature.signingKey == .master)
Expand Down Expand Up @@ -107,7 +110,7 @@ class QROperationParserTests: XCTestCase {
"Payment\n" +
"Please confirm this payment\n" +
"B2*Xtest\n" +
"B\n" +
"BCFX\n" +
"Some Additional Information\n" +
"AD8bOO0Df73kNaIGb3Vmpg==\n" +
"0").data(using: .utf8)
Expand Down Expand Up @@ -167,6 +170,22 @@ class QROperationParserTests: XCTestCase {
return
}
XCTAssertFalse(operation.flags.allowBiometryFactor)
XCTAssertFalse(operation.flags.blockWhenOnCall)
XCTAssertFalse(operation.flags.flipButtons)
XCTAssertFalse(operation.flags.fraudWarning)
}

func testSomeMissingFlags() {
let parser = WMTQROperationParser()
let qrcode = makeCode(flags: "FX")
guard case .success(let operation) = parser.parse(string: qrcode) else {
XCTFail("This should be parsed")
return
}
XCTAssertFalse(operation.flags.allowBiometryFactor)
XCTAssertFalse(operation.flags.blockWhenOnCall)
XCTAssertTrue(operation.flags.flipButtons)
XCTAssertTrue(operation.flags.fraudWarning)
}

func testMissingOrBadNonce() {
Expand Down Expand Up @@ -347,5 +366,4 @@ class QROperationParserTests: XCTestCase {
return
}
}

}
53 changes: 53 additions & 0 deletions docs/Using-Operations-Service.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- [Reject an Operation](#reject-an-operation)
- [Off-line Authorization](#off-line-authorization)
- [Operations API Reference](#operations-api-reference)
- [WMTUserOperation](#WMTUserOperation)
- [Creating a Custom Operation](#creating-a-custom-operation)
- [Error handling](#error-handling)
<!-- end -->
Expand Down Expand Up @@ -53,6 +54,17 @@ The `pollingOptions` parameter is used for polling feature configuration. The de

- `WMTOperationsPollingOptions.pauseWhenOnBackground`

### With custom WMTUserOperation objects

To retreive custom user operations, both `createWMTOperations` methods offer optional parameter `customUserOperationType` where you can setup requested type.

```swift
// networkingService is instance of WPNNetworkingService
let opsService = networkingService.createWMTOperations(customUserOperationType: CustomUserOperation.self).
```

When [custom operation type](#subclassing-WMTUserOperation) is set, all `WMTUserOperation` objects from such service can be explicitly unboxed to this type.

## Retrieve Pending Operations

To fetch the list with pending operations, can call the `WMTOperations` API:
Expand Down Expand Up @@ -447,6 +459,47 @@ Attributes types:
- `HEADING` single highlighted text, written in a larger font, used as a section heading
- `PARTY_INFO` providing structured information about third-party data (for example known e-shop)

### Subclassing WMTUserOperation

`WMTUserOperation` class is `open` and can be subclassed. This is useful when your backend adds additional properties to operations retrieved via the `getOperations` API.

Example of such class:

```swift
class CustomUserOperation: WMTUserOperation {

enum CodingKeys: CodingKey {
case playSound
}

/// Should we play a sound when the operation is displayed?
let playSound: Bool

required init(from decoder: Decoder) throws {
/// Decode the playSound property
playSound = try decoder.container(keyedBy: CodingKeys.self).decode(Bool, forKey: . playSound)
/// Decode the rest of the properties by the super class
try super.init(from: decoder)
}
```

To set up the Operation Service to receive such objects, you need to create it with a [`customUserOperationType` parameter](#with-custom-WMTUserOperation-objects). After that, all `WMTUserOperation` objects can be unboxed into your custom objects.

Example of the unboxing:

```swift
opsService.getOperations { result in
switch result {
case .success(let ops):
// unbox operations into the [CustomUserOperation]
let unboxed = ops.map { $0 as! CustomUserOperation }
case .failure(let error):
// do something with the error
break
}
}
```

## Creating a Custom Operation

In some specific scenarios, you might need to approve or reject an operation that you received through a different channel than `getOperations`. In such cases, you can implement the `WMTOperation` protocol in your custom class and then feed created objects to both `authorize` and `reject` methods.
Expand Down
2 changes: 1 addition & 1 deletion scripts/cart-update.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,5 @@ set -e # stop sript when error occures
SCRIPT_FOLDER=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )

pushd "${SCRIPT_FOLDER}/.."
carthage update --use-xcframeworks --platform ios
carthage update --use-xcframeworks --use-submodules --platform ios
popd

0 comments on commit 8632bee

Please sign in to comment.