Skip to content

Commit

Permalink
fix(datastore): multi auth rule for read subscription
Browse files Browse the repository at this point in the history
  • Loading branch information
thisisabhash committed Oct 20, 2023
1 parent bdc9b4a commit c444b07
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,7 @@ public struct AuthRule {
self.operations = operations
}
}

extension AuthRule: Hashable {

}
32 changes: 27 additions & 5 deletions AmplifyPlugins/Core/AWSPluginsCore/Auth/AWSAuthModeStrategy.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ public protocol AuthModeStrategy: AnyObject {
init()

func authTypesFor(schema: ModelSchema, operation: ModelOperation) async -> AWSAuthorizationTypeIterator

func authTypesFor(schema: ModelSchema, operations: [ModelOperation]) async -> AWSAuthorizationTypeIterator
}

/// AuthorizationType iterator with an extra `count` property used
Expand Down Expand Up @@ -93,6 +95,11 @@ public class AWSDefaultAuthModeStrategy: AuthModeStrategy {
operation: ModelOperation) -> AWSAuthorizationTypeIterator {
return AWSAuthorizationTypeIterator(withValues: [])
}

public func authTypesFor(schema: ModelSchema,
operations: [ModelOperation]) -> AWSAuthorizationTypeIterator {
return AWSAuthorizationTypeIterator(withValues: [])
}
}

// MARK: - AWSMultiAuthModeStrategy
Expand Down Expand Up @@ -188,19 +195,34 @@ public class AWSMultiAuthModeStrategy: AuthModeStrategy {
/// - 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)
return await authTypesFor(schema: schema, operations: [operation])
}

/// Returns the union of authorization types for the provided schema for the given list of operations
/// - Parameters:
/// - schema: model schema
/// - operations: model operations
/// - Returns: an iterator for the applicable auth rules
public func authTypesFor(schema: ModelSchema,
operations: [ModelOperation]) async -> AWSAuthorizationTypeIterator {
var applicableAuthRules = Set<AuthRule>()
for operation in operations {
let rules = schema.authRules.filter(modelOperation: operation)
applicableAuthRules = applicableAuthRules.union(Set(rules))
}

var sortedRules = applicableAuthRules.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
sortedRules = sortedRules.filter { rule in
return rule.allow == .public || rule.allow == .custom
}
}
let applicableAuthTypes = applicableAuthRules.map {
let applicableAuthTypes = sortedRules.map {
AWSMultiAuthModeStrategy.authTypeFor(authRule: $0)
}
return AWSAuthorizationTypeIterator(withValues: applicableAuthTypes)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import XCTest
@testable import AWSPluginsCore

class AuthModeStrategyTests: XCTestCase {

// Given: default strategy and a model schema
// When: authTypesFor for .create operation is called
// Then: an empty iterator is returned
Expand All @@ -20,7 +20,7 @@ class AuthModeStrategyTests: XCTestCase {
let authTypesIterator = authMode.authTypesFor(schema: AnyModelTester.schema, operation: .create)
XCTAssertEqual(authTypesIterator.count, 0)
}

// Given: multi-auth strategy and a model schema
// When: authTypesFor for .create operation is called
// Then: auth types are returned in order according to priority rules
Expand All @@ -31,7 +31,7 @@ class AuthModeStrategyTests: XCTestCase {
XCTAssertEqual(authTypesIterator.next(), .amazonCognitoUserPools)
XCTAssertEqual(authTypesIterator.next(), .apiKey)
}

// Given: multi-auth strategy and a model schema without auth provider
// When: auth types are requested
// Then: default values based on the auth strategy should be returned
Expand All @@ -42,7 +42,7 @@ class AuthModeStrategyTests: XCTestCase {
XCTAssertEqual(authTypesIterator.next(), .amazonCognitoUserPools)
XCTAssertEqual(authTypesIterator.next(), .apiKey)
}

// Given: multi-auth strategy and a model schema with 4 auth rules
// When: authTypesFor for .create operation is called
// Then: applicable auth types are ordered according to priority rules
Expand All @@ -55,7 +55,7 @@ class AuthModeStrategyTests: XCTestCase {
XCTAssertEqual(authTypesIterator.next(), .amazonCognitoUserPools)
XCTAssertEqual(authTypesIterator.next(), .awsIAM)
}

// Given: multi-auth strategy and a model schema multiple public rules
// When: authTypesFor for .create operation is called
// Then: applicable auth types are ordered according to priority rules
Expand All @@ -68,7 +68,7 @@ class AuthModeStrategyTests: XCTestCase {
XCTAssertEqual(authTypesIterator.next(), .awsIAM)
XCTAssertEqual(authTypesIterator.next(), .apiKey)
}

// Given: multi-auth strategy and a model schema
// When: authTypesFor for .create operation is called
// Then: applicable auth types returned are only the
Expand All @@ -80,49 +80,72 @@ class AuthModeStrategyTests: XCTestCase {
XCTAssertEqual(authTypesIterator.next(), .amazonCognitoUserPools)
XCTAssertEqual(authTypesIterator.next(), .amazonCognitoUserPools)
}

// Given: multi-auth strategy a model schema
// When: authTypesFor for .create operation is called for unauthenticated user
// Then: applicable auth types returned are only public rules
func testMultiAuthPriorityUnauthenticatedUser() async {
let authMode = AWSMultiAuthModeStrategy()
let delegate = UnauthenticatedUserDelegate()
authMode.authDelegate = delegate

var authTypesIterator = await authMode.authTypesFor(schema: ModelWithOwnerAndPublicAuth.schema,
operation: .create)
operation: .create)
XCTAssertEqual(authTypesIterator.count, 1)
XCTAssertEqual(authTypesIterator.next(), .apiKey)
}

// Given: multi-auth model schema with a custom strategy
// When: authTypesFor for .create operation is called
// Then: applicable auth types returned respect the priority rules
func testMultiAuthPriorityWithCustomStrategy() async {
let authMode = AWSMultiAuthModeStrategy()
var authTypesIterator = await authMode.authTypesFor(schema: ModelWithCustomStrategy.schema,
operation: .create)
operation: .create)
XCTAssertEqual(authTypesIterator.count, 3)
XCTAssertEqual(authTypesIterator.next(), .function)
XCTAssertEqual(authTypesIterator.next(), .amazonCognitoUserPools)
XCTAssertEqual(authTypesIterator.next(), .awsIAM)
}

// Given: multi-auth model schema with a custom strategy
// When: authTypesFor for .create operation is called for unauthenticated user
// Then: applicable auth types returned are public rules or custom
func testMultiAuthPriorityUnauthenticatedUserWithCustom() async {
let authMode = AWSMultiAuthModeStrategy()
let delegate = UnauthenticatedUserDelegate()
authMode.authDelegate = delegate

var authTypesIterator = await authMode.authTypesFor(schema: ModelWithCustomStrategy.schema,
operation: .create)
operation: .create)
XCTAssertEqual(authTypesIterator.count, 2)
XCTAssertEqual(authTypesIterator.next(), .function)
XCTAssertEqual(authTypesIterator.next(), .awsIAM)
}


// Given: multi-auth strategy and a model schema without auth provider
// When: auth types are requested with multiple operation
// Then: default values based on the auth strategy should be returned
func testMultiAuthShouldReturnDefaultAuthTypesForMultipleOperation() async {
let authMode = AWSMultiAuthModeStrategy()
var authTypesIterator = await authMode.authTypesFor(schema: ModelNoProvider.schema, operations: [.read, .create])
XCTAssertEqual(authTypesIterator.count, 2)
XCTAssertEqual(authTypesIterator.next(), .amazonCognitoUserPools)
XCTAssertEqual(authTypesIterator.next(), .apiKey)
}

// Given: multi-auth strategy and a model schema with auth provider
// When: auth types are requested with multiple operation
// Then: auth rule for public access should be returned
func testMultiAuthReturnDefaultAuthTypesForMultipleOperationWithProvider() async {
let authMode = AWSMultiAuthModeStrategy()
let delegate = UnauthenticatedUserDelegate()
authMode.authDelegate = delegate
var authTypesIterator = await authMode.authTypesFor(schema: ModelNoProvider.schema, operations: [.read, .create])
XCTAssertEqual(authTypesIterator.count, 1)
XCTAssertEqual(authTypesIterator.next(), .apiKey)
}

}

// MARK: - Test models
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ final class IncomingAsyncSubscriptionEventPublisher: AmplifyCancellable {
// onCreate operation
let onCreateValueListener = onCreateValueListenerHandler(event:)
let onCreateAuthTypeProvider = await authModeStrategy.authTypesFor(schema: modelSchema,
operation: .create)
operations: [.create, .read])
self.onCreateValueListener = onCreateValueListener
self.onCreateOperation = RetryableGraphQLSubscriptionOperation(
requestFactory: IncomingAsyncSubscriptionEventPublisher.apiRequestFactoryFor(
Expand All @@ -94,7 +94,7 @@ final class IncomingAsyncSubscriptionEventPublisher: AmplifyCancellable {
// onUpdate operation
let onUpdateValueListener = onUpdateValueListenerHandler(event:)
let onUpdateAuthTypeProvider = await authModeStrategy.authTypesFor(schema: modelSchema,
operation: .update)
operations: [.update, .read])
self.onUpdateValueListener = onUpdateValueListener
self.onUpdateOperation = RetryableGraphQLSubscriptionOperation(
requestFactory: IncomingAsyncSubscriptionEventPublisher.apiRequestFactoryFor(
Expand All @@ -115,7 +115,7 @@ final class IncomingAsyncSubscriptionEventPublisher: AmplifyCancellable {
// onDelete operation
let onDeleteValueListener = onDeleteValueListenerHandler(event:)
let onDeleteAuthTypeProvider = await authModeStrategy.authTypesFor(schema: modelSchema,
operation: .delete)
operations: [.delete, .read])
self.onDeleteValueListener = onDeleteValueListener
self.onDeleteOperation = RetryableGraphQLSubscriptionOperation(
requestFactory: IncomingAsyncSubscriptionEventPublisher.apiRequestFactoryFor(
Expand Down

0 comments on commit c444b07

Please sign in to comment.