207 lines
6.9 KiB
Swift
207 lines
6.9 KiB
Swift
//
|
|
// Copyright Amazon.com Inc. or its affiliates.
|
|
// All Rights Reserved.
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
|
|
import Foundation
|
|
import Amplify
|
|
|
|
/// Represents different auth strategies supported by a client
|
|
/// interfacing with an AppSync backend
|
|
public enum AuthModeStrategyType {
|
|
/// Default authorization type read from API configuration
|
|
case `default`
|
|
|
|
/// Uses schema metadata to create a prioritized list of potential authorization types
|
|
/// that could be used for a request. The client iterates through that list until one of the
|
|
/// avaialable types succeeds or all of them fail.
|
|
case multiAuth
|
|
|
|
public func resolveStrategy() -> AuthModeStrategy {
|
|
switch self {
|
|
case .default:
|
|
return AWSDefaultAuthModeStrategy()
|
|
case .multiAuth:
|
|
return AWSMultiAuthModeStrategy()
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Methods for checking user current status
|
|
public protocol AuthModeStrategyDelegate: AnyObject {
|
|
func isUserLoggedIn() async -> Bool
|
|
}
|
|
|
|
/// Represents an authorization strategy used by DataStore
|
|
public protocol AuthModeStrategy: AnyObject {
|
|
|
|
var authDelegate: AuthModeStrategyDelegate? { get set }
|
|
|
|
init()
|
|
|
|
func authTypesFor(schema: ModelSchema, operation: ModelOperation) async -> AWSAuthorizationTypeIterator
|
|
}
|
|
|
|
/// AuthorizationType iterator with an extra `count` property used
|
|
/// to predict the number of values
|
|
public protocol AuthorizationTypeIterator {
|
|
associatedtype AuthorizationType
|
|
|
|
init(withValues: [AuthorizationType])
|
|
|
|
/// Total number of values
|
|
var count: Int { get }
|
|
|
|
/// Next available `AuthorizationType` or `nil` if exhausted
|
|
mutating func next() -> AuthorizationType?
|
|
}
|
|
|
|
/// AuthorizationTypeIterator for values of type `AWSAuthorizationType`
|
|
public struct AWSAuthorizationTypeIterator: AuthorizationTypeIterator {
|
|
public typealias AuthorizationType = AWSAuthorizationType
|
|
|
|
private var values: IndexingIterator<[AWSAuthorizationType]>
|
|
private var _count: Int
|
|
|
|
public init(withValues values: [AWSAuthorizationType]) {
|
|
self.values = values.makeIterator()
|
|
self._count = values.count
|
|
}
|
|
|
|
public var count: Int {
|
|
_count
|
|
}
|
|
|
|
public mutating func next() -> AWSAuthorizationType? {
|
|
values.next()
|
|
}
|
|
}
|
|
|
|
// MARK: - AWSDefaultAuthModeStrategy
|
|
|
|
/// AWS default auth mode strategy.
|
|
///
|
|
/// Returns an empty AWSAuthorizationTypeIterator, so we can just rely on the default authorization type
|
|
/// registered as interceptor for the API
|
|
public class AWSDefaultAuthModeStrategy: AuthModeStrategy {
|
|
public weak var authDelegate: AuthModeStrategyDelegate?
|
|
required public init() {}
|
|
|
|
public func authTypesFor(schema: ModelSchema,
|
|
operation: ModelOperation) -> AWSAuthorizationTypeIterator {
|
|
return AWSAuthorizationTypeIterator(withValues: [])
|
|
}
|
|
}
|
|
|
|
// MARK: - AWSMultiAuthModeStrategy
|
|
|
|
/// Multi-auth strategy implementation based on schema metadata
|
|
public class AWSMultiAuthModeStrategy: AuthModeStrategy {
|
|
public weak var authDelegate: AuthModeStrategyDelegate?
|
|
|
|
private typealias AuthPriority = Int
|
|
|
|
required public init() {}
|
|
|
|
private static func defaultAuthTypeFor(authStrategy: AuthStrategy) -> AWSAuthorizationType {
|
|
var defaultAuthType: AWSAuthorizationType
|
|
switch authStrategy {
|
|
case .owner:
|
|
defaultAuthType = .amazonCognitoUserPools
|
|
case .groups:
|
|
defaultAuthType = .amazonCognitoUserPools
|
|
case .private:
|
|
defaultAuthType = .amazonCognitoUserPools
|
|
case .public:
|
|
defaultAuthType = .apiKey
|
|
case .custom:
|
|
defaultAuthType = .function
|
|
}
|
|
return defaultAuthType
|
|
}
|
|
|
|
/// Given an auth rule, returns the corresponding AWSAuthorizationType
|
|
/// - Parameter authRule: authorization rule
|
|
/// - Returns: returns corresponding AWSAuthorizationType or a default
|
|
private static func authTypeFor(authRule: AuthRule) -> AWSAuthorizationType {
|
|
if let authProvider = authRule.provider {
|
|
return authProvider.toAWSAuthorizationType()
|
|
}
|
|
|
|
return defaultAuthTypeFor(authStrategy: authRule.allow)
|
|
}
|
|
|
|
/// Given an auth rule strategy returns its corresponding priority.
|
|
/// Strategies are ordered from "most specific" to "least specific".
|
|
/// - Parameter authStrategy: auth rule strategy
|
|
/// - Returns: priority
|
|
private static func priorityOf(authStrategy: AuthStrategy) -> AuthPriority {
|
|
switch authStrategy {
|
|
case .custom:
|
|
return 0
|
|
case .owner:
|
|
return 1
|
|
case .groups:
|
|
return 2
|
|
case .private:
|
|
return 3
|
|
case .public:
|
|
return 4
|
|
}
|
|
}
|
|
|
|
/// Given an auth rule provider returns its corresponding priority.
|
|
/// Providers are ordered from "most specific" to "least specific".
|
|
/// - Parameter authRuleProvider: auth rule provider
|
|
/// - Returns: priority
|
|
private static func priorityOf(authRuleProvider provider: AuthRuleProvider) -> AuthPriority {
|
|
switch provider {
|
|
case .function:
|
|
return 0
|
|
case .userPools:
|
|
return 1
|
|
case .oidc:
|
|
return 2
|
|
case .iam:
|
|
return 3
|
|
case .apiKey:
|
|
return 4
|
|
}
|
|
}
|
|
|
|
/// A predicate used to sort Auth rules according to above priority rules
|
|
/// Use provider priority to sort if rules have the same strategy
|
|
private static let comparator = { (rule1: AuthRule, rule2: AuthRule) -> Bool in
|
|
if let providerRule1 = rule1.provider,
|
|
let providerRule2 = rule2.provider, rule1.allow == rule2.allow {
|
|
return priorityOf(authRuleProvider: providerRule1) < priorityOf(authRuleProvider: providerRule2)
|
|
}
|
|
return priorityOf(authStrategy: rule1.allow) < priorityOf(authStrategy: rule2.allow)
|
|
}
|
|
|
|
/// Returns the proper authorization type for the provided schema according to a set of priority rules
|
|
/// - Parameters:
|
|
/// - schema: model schema
|
|
/// - operation: model operation
|
|
/// - Returns: an iterator for the applicable auth rules
|
|
public func authTypesFor(schema: ModelSchema,
|
|
operation: ModelOperation) async -> AWSAuthorizationTypeIterator {
|
|
var applicableAuthRules = schema.authRules
|
|
.filter(modelOperation: operation)
|
|
.sorted(by: AWSMultiAuthModeStrategy.comparator)
|
|
|
|
// if there isn't a user signed in, returns only public or custom rules
|
|
if let authDelegate = authDelegate, await !authDelegate.isUserLoggedIn() {
|
|
applicableAuthRules = applicableAuthRules.filter { rule in
|
|
return rule.allow == .public || rule.allow == .custom
|
|
}
|
|
}
|
|
let applicableAuthTypes = applicableAuthRules.map {
|
|
AWSMultiAuthModeStrategy.authTypeFor(authRule: $0)
|
|
}
|
|
return AWSAuthorizationTypeIterator(withValues: applicableAuthTypes)
|
|
}
|
|
}
|