From 3bf57801e72871b3d2e9fe2602da0eeccb13b082 Mon Sep 17 00:00:00 2001 From: Michael Law <1365977+lawmicha@users.noreply.github.com> Date: Fri, 15 Sep 2023 16:38:26 -0400 Subject: [PATCH] fix(datastore): filter authrules with invalid ownerfield --- .../Model/Decorator/AuthRuleDecorator.swift | 36 +++++++--- .../ModelMultipleOwnerAuthRuleTests.swift | 45 +++---------- ...egoryPluginAuthOwnerIntegrationTests.swift | 43 +++++++++++- .../Models/TodoCognitoMultiOwner+Schema.swift | 57 ++++++++++++++++ .../Models/TodoCognitoMultiOwner.swift | 49 ++++++++++++++ .../Models/TodoCognitoPrivate+Schema.swift | 26 ++++--- .../TodoCustomOwnerExplicit+Schema.swift | 24 +++++-- .../TodoCustomOwnerImplicit+Schema.swift | 24 +++++-- .../TodoExplicitOwnerField+Schema.swift | 24 +++++-- .../TodoImplicitOwnerField+Schema.swift | 24 +++++-- .../README.md | 67 +++++++------------ .../singleauth-cognito-schema.graphql | 11 +++ .../project.pbxproj | 8 +++ 13 files changed, 314 insertions(+), 124 deletions(-) create mode 100644 AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/Models/TodoCognitoMultiOwner+Schema.swift create mode 100644 AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/Models/TodoCognitoMultiOwner.swift diff --git a/AmplifyPlugins/Core/AWSPluginsCore/Model/Decorator/AuthRuleDecorator.swift b/AmplifyPlugins/Core/AWSPluginsCore/Model/Decorator/AuthRuleDecorator.swift index 6e7fa24037..4c70689a10 100644 --- a/AmplifyPlugins/Core/AWSPluginsCore/Model/Decorator/AuthRuleDecorator.swift +++ b/AmplifyPlugins/Core/AWSPluginsCore/Model/Decorator/AuthRuleDecorator.swift @@ -50,19 +50,13 @@ public struct AuthRuleDecorator: ModelBasedGraphQLDocumentDecorator { public func decorate(_ document: SingleDirectiveGraphQLDocument, modelSchema: ModelSchema) -> SingleDirectiveGraphQLDocument { - let authRules = modelSchema.authRules.filterBy(authType: authType) + let authRules = modelSchema.authRules + .filterBy(authType: authType) + .filterBy(ownerFieldType: .string, modelSchema: modelSchema) guard !authRules.isEmpty else { return document } var decorateDocument = document - if authRules.readRestrictingOwnerRules().count > 1 { - log.error(""" - Detected multiple owner type auth rules \ - with a READ operation. We currently do not support this use case. Please \ - limit your type to just one owner auth rule with a READ operation restriction. - """) - return decorateDocument - } let readRestrictingStaticGroups = authRules.groupClaimsToReadRestrictingStaticGroups() authRules.forEach { authRule in @@ -179,6 +173,15 @@ public struct AuthRuleDecorator: ModelBasedGraphQLDocumentDecorator { } } +private extension AuthRule { + func ownerField(inSchema schema: ModelSchema) -> ModelField? { + guard let fieldName = self.ownerField else { + return nil + } + return schema.field(withName: fieldName) + } +} + private extension AuthRules { func filterBy(authType: AWSAuthorizationType?) -> AuthRules { guard let authType = authType else { @@ -195,6 +198,21 @@ private extension AuthRules { return authType == provider.toAWSAuthorizationType() } } + + func filterBy(ownerFieldType: ModelFieldType, + modelSchema: ModelSchema) -> AuthRules { + return filter { + guard let modelField = $0.ownerField(inSchema: modelSchema) else { + // if we couldn't find the owner field means it has been implicitly + // declared in the model schema, therefore has the correct type "string" + return true + } + if case .string = modelField.type { + return true + } + return false + } + } } extension AuthRuleDecorator: DefaultLogger { diff --git a/AmplifyPlugins/Core/AWSPluginsCoreTests/Model/Decorator/AuthRuleDecorator/ModelMultipleOwnerAuthRuleTests.swift b/AmplifyPlugins/Core/AWSPluginsCoreTests/Model/Decorator/AuthRuleDecorator/ModelMultipleOwnerAuthRuleTests.swift index 47646a3322..b6ae079862 100644 --- a/AmplifyPlugins/Core/AWSPluginsCoreTests/Model/Decorator/AuthRuleDecorator/ModelMultipleOwnerAuthRuleTests.swift +++ b/AmplifyPlugins/Core/AWSPluginsCoreTests/Model/Decorator/AuthRuleDecorator/ModelMultipleOwnerAuthRuleTests.swift @@ -71,31 +71,7 @@ class ModelMultipleOwnerAuthRuleTests: XCTestCase { override func tearDown() { ModelRegistry.reset() } - // This is a test case to demostrate if we attempt to use a model with multiple auth rules - // with a read operation, we effectively create a subscription without decorating it with auth. - // We should delete this use case when the AppSync service supports this use case. - func testUnsupportedModelMultipleOwner_CreateMutation() { - var documentBuilder = ModelBasedGraphQLDocumentBuilder(modelType: ModelMultipleOwner.self, - operationType: .mutation) - documentBuilder.add(decorator: DirectiveNameDecorator(type: .create)) - documentBuilder.add(decorator: AuthRuleDecorator(.mutation)) - let document = documentBuilder.build() - let expectedQueryDocument = """ - mutation CreateModelMultipleOwner { - createModelMultipleOwner { - id - content - editors - __typename - } - } - """ - XCTAssertEqual(document.name, "createModelMultipleOwner") - XCTAssertEqual(document.stringValue, expectedQueryDocument) - XCTAssertTrue(document.variables.isEmpty) - } - -/* + // Ensure that the `owner` field is added to the model fields func testModelMultipleOwner_CreateMutation() { var documentBuilder = ModelBasedGraphQLDocumentBuilder(modelSchema: ModelMultipleOwner.schema, @@ -219,7 +195,7 @@ class ModelMultipleOwnerAuthRuleTests: XCTestCase { operationType: .query) documentBuilder.add(decorator: DirectiveNameDecorator(type: .sync)) documentBuilder.add(decorator: PaginationDecorator()) - documentBuilder.add(decorator: ConflictResolutionDecorator()) + documentBuilder.add(decorator: ConflictResolutionDecorator(graphQLType: .query)) documentBuilder.add(decorator: AuthRuleDecorator(.query)) let document = documentBuilder.build() let expectedQueryDocument = """ @@ -243,7 +219,7 @@ class ModelMultipleOwnerAuthRuleTests: XCTestCase { XCTAssertEqual(document.name, "syncModelMultipleOwners") XCTAssertEqual(document.stringValue, expectedQueryDocument) } - + // Only the 'owner' inherently has `.create` operation, requiring the subscription operation to contain the input func testModelMultipleOwner_OnCreateSubscription() { let claims = ["username": "user1", @@ -254,8 +230,8 @@ class ModelMultipleOwnerAuthRuleTests: XCTestCase { documentBuilder.add(decorator: AuthRuleDecorator(.subscription(.onCreate, claims))) let document = documentBuilder.build() let expectedQueryDocument = """ - subscription OnCreateModelMultipleOwner($editors: String!, $owner: String!) { - onCreateModelMultipleOwner(editors: $editors, owner: $owner) { + subscription OnCreateModelMultipleOwner($owner: String!) { + onCreateModelMultipleOwner(owner: $owner) { id content editors @@ -283,8 +259,8 @@ class ModelMultipleOwnerAuthRuleTests: XCTestCase { documentBuilder.add(decorator: AuthRuleDecorator(.subscription(.onUpdate, claims))) let document = documentBuilder.build() let expectedQueryDocument = """ - subscription OnUpdateModelMultipleOwner($editors: String!, $owner: String!) { - onUpdateModelMultipleOwner(editors: $editors, owner: $owner) { + subscription OnUpdateModelMultipleOwner($owner: String!) { + onUpdateModelMultipleOwner(owner: $owner) { id content editors @@ -300,7 +276,6 @@ class ModelMultipleOwnerAuthRuleTests: XCTestCase { return } XCTAssertEqual(variables["owner"] as? String, "user1") - XCTAssertEqual(variables["editors"] as? String, "user1") } // Only the 'owner' inherently has `.delete` operation, requiring the subscription operation to contain the input @@ -313,8 +288,8 @@ class ModelMultipleOwnerAuthRuleTests: XCTestCase { documentBuilder.add(decorator: AuthRuleDecorator(.subscription(.onDelete, claims))) let document = documentBuilder.build() let expectedQueryDocument = """ - subscription OnDeleteModelMultipleOwner($editors: String!, $owner: String!) { - onDeleteModelMultipleOwner(editors: $editors, owner: $owner) { + subscription OnDeleteModelMultipleOwner($owner: String!) { + onDeleteModelMultipleOwner(owner: $owner) { id content editors @@ -331,5 +306,5 @@ class ModelMultipleOwnerAuthRuleTests: XCTestCase { } XCTAssertEqual(variables["owner"] as? String, "user1") } -*/ + } diff --git a/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/AWSDataStoreCategoryPluginAuthOwnerIntegrationTests.swift b/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/AWSDataStoreCategoryPluginAuthOwnerIntegrationTests.swift index a6e4a68055..aef8908f97 100644 --- a/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/AWSDataStoreCategoryPluginAuthOwnerIntegrationTests.swift +++ b/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/AWSDataStoreCategoryPluginAuthOwnerIntegrationTests.swift @@ -92,15 +92,47 @@ class AWSDataStoreCategoryPluginAuthOwnerIntegrationTests: AWSDataStoreAuthBaseT } let todo = TodoExplicitOwnerField(content: "content") - + // Mutations try await assertMutations(model: todo, expectations) { error in XCTFail("Error mutation \(error)") } - + + assertUsedAuthTypes([.amazonCognitoUserPools]) + } + + /// Given: a user signed in with CognitoUserPools, a model with multiple rules with + /// explicit owner field + /// When: DataStore query/mutation operations are sent with CognitoUserPools + /// Then: DataStore is successfully initialized, query returns a result, + /// mutation is processed for an authenticated users + func testExplicitMultipleOwner() async throws { + try await setup(withModels: ExplicitMultipleOwnerModelRegistration(), + testType: .defaultAuthCognito) + + try await signIn(user: user1) + + let expectations = makeExpectations() + + try await assertDataStoreReady(expectations) + + // Query + try await assertQuerySuccess(modelType: TodoCognitoMultiOwner.self, + expectations) { error in + XCTFail("Error query \(error)") + } + + let post = TodoCognitoMultiOwner(title: "title") + + // Mutations + try await assertMutations(model: post, expectations) { error in + XCTFail("Error mutation \(error)") + } + assertUsedAuthTypes([.amazonCognitoUserPools]) } + /// Given: a user signed in with CognitoUserPools, a model with an implicit owner field /// When: DataStore query/mutation operations are sent with CognitoUserPools /// Then: DataStore is successfully initialized, query returns a result, @@ -187,6 +219,13 @@ extension AWSDataStoreCategoryPluginAuthOwnerIntegrationTests { } } + struct ExplicitMultipleOwnerModelRegistration: AmplifyModelRegistration { + public let version: String = "version" + func registerModels(registry: ModelRegistry.Type) { + ModelRegistry.register(modelType: TodoCognitoMultiOwner.self) + } + } + struct ImplicitOwnerModelRegistration: AmplifyModelRegistration { public let version: String = "version" func registerModels(registry: ModelRegistry.Type) { diff --git a/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/Models/TodoCognitoMultiOwner+Schema.swift b/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/Models/TodoCognitoMultiOwner+Schema.swift new file mode 100644 index 0000000000..a42c5ea85a --- /dev/null +++ b/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/Models/TodoCognitoMultiOwner+Schema.swift @@ -0,0 +1,57 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +// swiftlint:disable all +import Amplify +import Foundation + +extension TodoCognitoMultiOwner { + // MARK: - CodingKeys + public enum CodingKeys: String, ModelKey { + case id + case title + case content + case owner + case editors + case createdAt + case updatedAt + } + + public static let keys = CodingKeys.self + // MARK: - ModelSchema + + public static let schema = defineSchema { model in + let todoCognitoMultiOwner = TodoCognitoMultiOwner.keys + + model.authRules = [ + rule(allow: .owner, ownerField: "owner", identityClaim: "cognito:username", provider: .userPools, operations: [.create, .update, .delete, .read]), + rule(allow: .owner, ownerField: "editors", identityClaim: "cognito:username", provider: .userPools, operations: [.update, .read]) + ] + + model.listPluralName = "TodoCognitoMultiOwners" + model.syncPluralName = "TodoCognitoMultiOwners" + + model.attributes( + .primaryKey(fields: [todoCognitoMultiOwner.id]) + ) + + model.fields( + .field(todoCognitoMultiOwner.id, is: .required, ofType: .string), + .field(todoCognitoMultiOwner.title, is: .required, ofType: .string), + .field(todoCognitoMultiOwner.content, is: .optional, ofType: .string), + .field(todoCognitoMultiOwner.owner, is: .optional, ofType: .string), + .field(todoCognitoMultiOwner.editors, is: .optional, ofType: .embeddedCollection(of: String.self)), + .field(todoCognitoMultiOwner.createdAt, is: .optional, isReadOnly: true, ofType: .dateTime), + .field(todoCognitoMultiOwner.updatedAt, is: .optional, isReadOnly: true, ofType: .dateTime) + ) + } +} + +extension TodoCognitoMultiOwner: ModelIdentifiable { + public typealias IdentifierFormat = ModelIdentifierFormat.Default + public typealias IdentifierProtocol = DefaultModelIdentifier +} diff --git a/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/Models/TodoCognitoMultiOwner.swift b/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/Models/TodoCognitoMultiOwner.swift new file mode 100644 index 0000000000..ad45c098b8 --- /dev/null +++ b/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/Models/TodoCognitoMultiOwner.swift @@ -0,0 +1,49 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +// swiftlint:disable all +import Amplify +import Foundation + +public struct TodoCognitoMultiOwner: Model { + public let id: String + public var title: String + public var content: String? + public var owner: String? + public var editors: [String?]? + public var createdAt: Temporal.DateTime? + public var updatedAt: Temporal.DateTime? + + public init(id: String = UUID().uuidString, + title: String, + content: String? = nil, + owner: String? = nil, + editors: [String?]? = nil) { + self.init(id: id, + title: title, + content: content, + owner: owner, + editors: editors, + createdAt: nil, + updatedAt: nil) + } + internal init(id: String = UUID().uuidString, + title: String, + content: String? = nil, + owner: String? = nil, + editors: [String?]? = nil, + createdAt: Temporal.DateTime? = nil, + updatedAt: Temporal.DateTime? = nil) { + self.id = id + self.title = title + self.content = content + self.owner = owner + self.editors = editors + self.createdAt = createdAt + self.updatedAt = updatedAt + } +} diff --git a/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/Models/TodoCognitoPrivate+Schema.swift b/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/Models/TodoCognitoPrivate+Schema.swift index 45ce97d351..27b73152a8 100644 --- a/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/Models/TodoCognitoPrivate+Schema.swift +++ b/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/Models/TodoCognitoPrivate+Schema.swift @@ -17,24 +17,34 @@ extension TodoCognitoPrivate { case createdAt case updatedAt } - + public static let keys = CodingKeys.self // MARK: - ModelSchema - + public static let schema = defineSchema { model in let todoCognitoPrivate = TodoCognitoPrivate.keys - + model.authRules = [ - rule(allow: .private, provider: .userPools, operations: [.create, .update, .delete, .read]) + rule(allow: .private, operations: [.create, .update, .delete, .read]) ] - - model.pluralName = "TodoCognitoPrivates" - + + model.listPluralName = "TodoCognitoPrivates" + model.syncPluralName = "TodoCognitoPrivates" + + model.attributes( + .primaryKey(fields: [todoCognitoPrivate.id]) + ) + model.fields( - .id(), + .field(todoCognitoPrivate.id, is: .required, ofType: .string), .field(todoCognitoPrivate.title, is: .required, ofType: .string), .field(todoCognitoPrivate.createdAt, is: .optional, isReadOnly: true, ofType: .dateTime), .field(todoCognitoPrivate.updatedAt, is: .optional, isReadOnly: true, ofType: .dateTime) ) } } + +extension TodoCognitoPrivate: ModelIdentifiable { + public typealias IdentifierFormat = ModelIdentifierFormat.Default + public typealias IdentifierProtocol = DefaultModelIdentifier +} diff --git a/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/Models/TodoCustomOwnerExplicit+Schema.swift b/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/Models/TodoCustomOwnerExplicit+Schema.swift index ae82e9c3c2..3688748647 100644 --- a/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/Models/TodoCustomOwnerExplicit+Schema.swift +++ b/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/Models/TodoCustomOwnerExplicit+Schema.swift @@ -18,21 +18,26 @@ extension TodoCustomOwnerExplicit { case createdAt case updatedAt } - + public static let keys = CodingKeys.self // MARK: - ModelSchema - + public static let schema = defineSchema { model in let todoCustomOwnerExplicit = TodoCustomOwnerExplicit.keys - + model.authRules = [ rule(allow: .owner, ownerField: "dominus", identityClaim: "cognito:username", provider: .userPools, operations: [.create, .update, .delete, .read]) ] - - model.pluralName = "TodoCustomOwnerExplicits" - + + model.listPluralName = "TodoCustomOwnerExplicits" + model.syncPluralName = "TodoCustomOwnerExplicits" + + model.attributes( + .primaryKey(fields: [todoCustomOwnerExplicit.id]) + ) + model.fields( - .id(), + .field(todoCustomOwnerExplicit.id, is: .required, ofType: .string), .field(todoCustomOwnerExplicit.title, is: .required, ofType: .string), .field(todoCustomOwnerExplicit.dominus, is: .optional, ofType: .string), .field(todoCustomOwnerExplicit.createdAt, is: .optional, isReadOnly: true, ofType: .dateTime), @@ -40,3 +45,8 @@ extension TodoCustomOwnerExplicit { ) } } + +extension TodoCustomOwnerExplicit: ModelIdentifiable { + public typealias IdentifierFormat = ModelIdentifierFormat.Default + public typealias IdentifierProtocol = DefaultModelIdentifier +} diff --git a/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/Models/TodoCustomOwnerImplicit+Schema.swift b/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/Models/TodoCustomOwnerImplicit+Schema.swift index bfd1e4b035..4aa97d8ab0 100644 --- a/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/Models/TodoCustomOwnerImplicit+Schema.swift +++ b/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/Models/TodoCustomOwnerImplicit+Schema.swift @@ -17,24 +17,34 @@ extension TodoCustomOwnerImplicit { case createdAt case updatedAt } - + public static let keys = CodingKeys.self // MARK: - ModelSchema - + public static let schema = defineSchema { model in let todoCustomOwnerImplicit = TodoCustomOwnerImplicit.keys - + model.authRules = [ rule(allow: .owner, ownerField: "dominus", identityClaim: "cognito:username", provider: .userPools, operations: [.create, .update, .delete, .read]) ] - - model.pluralName = "TodoCustomOwnerImplicits" - + + model.listPluralName = "TodoCustomOwnerImplicits" + model.syncPluralName = "TodoCustomOwnerImplicits" + + model.attributes( + .primaryKey(fields: [todoCustomOwnerImplicit.id]) + ) + model.fields( - .id(), + .field(todoCustomOwnerImplicit.id, is: .required, ofType: .string), .field(todoCustomOwnerImplicit.title, is: .required, ofType: .string), .field(todoCustomOwnerImplicit.createdAt, is: .optional, isReadOnly: true, ofType: .dateTime), .field(todoCustomOwnerImplicit.updatedAt, is: .optional, isReadOnly: true, ofType: .dateTime) ) } } + +extension TodoCustomOwnerImplicit: ModelIdentifiable { + public typealias IdentifierFormat = ModelIdentifierFormat.Default + public typealias IdentifierProtocol = DefaultModelIdentifier +} diff --git a/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/Models/TodoExplicitOwnerField+Schema.swift b/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/Models/TodoExplicitOwnerField+Schema.swift index e3b12c5e2d..c31e9d89a0 100644 --- a/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/Models/TodoExplicitOwnerField+Schema.swift +++ b/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/Models/TodoExplicitOwnerField+Schema.swift @@ -18,21 +18,26 @@ extension TodoExplicitOwnerField { case createdAt case updatedAt } - + public static let keys = CodingKeys.self // MARK: - ModelSchema - + public static let schema = defineSchema { model in let todoExplicitOwnerField = TodoExplicitOwnerField.keys - + model.authRules = [ rule(allow: .owner, ownerField: "owner", identityClaim: "cognito:username", provider: .userPools, operations: [.read, .create, .update, .delete]) ] - - model.pluralName = "TodoExplicitOwnerFields" - + + model.listPluralName = "TodoExplicitOwnerFields" + model.syncPluralName = "TodoExplicitOwnerFields" + + model.attributes( + .primaryKey(fields: [todoExplicitOwnerField.id]) + ) + model.fields( - .id(), + .field(todoExplicitOwnerField.id, is: .required, ofType: .string), .field(todoExplicitOwnerField.content, is: .required, ofType: .string), .field(todoExplicitOwnerField.owner, is: .optional, ofType: .string), .field(todoExplicitOwnerField.createdAt, is: .optional, isReadOnly: true, ofType: .dateTime), @@ -40,3 +45,8 @@ extension TodoExplicitOwnerField { ) } } + +extension TodoExplicitOwnerField: ModelIdentifiable { + public typealias IdentifierFormat = ModelIdentifierFormat.Default + public typealias IdentifierProtocol = DefaultModelIdentifier +} diff --git a/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/Models/TodoImplicitOwnerField+Schema.swift b/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/Models/TodoImplicitOwnerField+Schema.swift index 73d8cef141..4c4faa491f 100644 --- a/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/Models/TodoImplicitOwnerField+Schema.swift +++ b/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/Models/TodoImplicitOwnerField+Schema.swift @@ -17,24 +17,34 @@ extension TodoImplicitOwnerField { case createdAt case updatedAt } - + public static let keys = CodingKeys.self // MARK: - ModelSchema - + public static let schema = defineSchema { model in let todoImplicitOwnerField = TodoImplicitOwnerField.keys - + model.authRules = [ rule(allow: .owner, ownerField: "owner", identityClaim: "cognito:username", provider: .userPools, operations: [.create, .update, .delete, .read]) ] - - model.pluralName = "TodoImplicitOwnerFields" - + + model.listPluralName = "TodoImplicitOwnerFields" + model.syncPluralName = "TodoImplicitOwnerFields" + + model.attributes( + .primaryKey(fields: [todoImplicitOwnerField.id]) + ) + model.fields( - .id(), + .field(todoImplicitOwnerField.id, is: .required, ofType: .string), .field(todoImplicitOwnerField.content, is: .required, ofType: .string), .field(todoImplicitOwnerField.createdAt, is: .optional, isReadOnly: true, ofType: .dateTime), .field(todoImplicitOwnerField.updatedAt, is: .optional, isReadOnly: true, ofType: .dateTime) ) } } + +extension TodoImplicitOwnerField: ModelIdentifiable { + public typealias IdentifierFormat = ModelIdentifierFormat.Default + public typealias IdentifierProtocol = DefaultModelIdentifier +} diff --git a/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/README.md b/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/README.md index 0b864e1c1b..2350ee8e79 100644 --- a/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/README.md +++ b/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/README.md @@ -9,28 +9,23 @@ This configuration is used to run the tests in `AWSDataStoreCategoryPluginAuthIn 2. Make sure the correct CLI version is used for this test. -- `amplify --v` should be at least 8.5.2 -- cli.json "transformerversion": 2 -- cli.json "useexperimentalpipelinedtransformer": true -- cli.json "usesubusernamefordefaultidentityclaim": true +- `amplify --v` should be at least 12.4.0 +- cli.json "generatemodelsforlazyloadandcustomselectionset": true 3. `amplify add api` ```perl -? Select from one of the below mentioned services: GraphQL - Authorization modes: Amazon Cognito User Pool (default) - Conflict detection (required for DataStore): Enabled - Conflict resolution strategy: Auto Merge -? Choose a schema template: Blank Schema -? Do you want to edit the schema now? `Yes` +? Select from one of the below mentioned services: `GraphQL` +? Here is the GraphQL API that we will create. Select a setting to edit or continue `Authorization mode` +? Choose the default authorization type for the API `Amazon Cognito User Pool` +? Configure additional auth types? `No` +? Enable conflict detection? `Yes` +? Select the default resolution strategy `Auto Merge` +? Choose a schema template: `Blank Schema` ``` Copy the content of the schema from `AWSDataStoreCategoryPluginAuthIntegrationTests/DefaultAuthCognito/singleauth-cognito-schema.graphql` into the newly created `schema.graphql` file -3. `amplify update api` -? Please select from one of the below mentioned services: `GraphQL` -? Select from the options below `Enable DataStore for entire API` - 4. `amplify push` ```perl ? Are you sure you want to continue? `Yes` @@ -42,42 +37,30 @@ Copy the content of the schema from `AWSDataStoreCategoryPluginAuthIntegrationTe cp amplifyconfiguration.json ~/.aws-amplify/amplify-ios/testconfiguration/AWSDataStoreCategoryPluginAuthIntegrationTests-amplifyconfiguration.json ``` -6. Create `AWSDataStoreCategoryPluginAuthIntegrationTests-credentials.json` inside the same folder with a json object containing `user1`, and `password`, used to create the cognito user in the userpool. In step 2, the cognito userpool is configured to allow users to sign up with their email as the username. - -```json -{ - "user1": "", - "passwordUser1": "", - "user2": "", - "passwordUser2": "" -} +6. Creating users through AWS CLI, run the following commands ``` - -### Creating users through AWS Console - -7. `amplify console auth` -```perl -? Which console `User Pool` +aws cognito-idp admin-create-user --user-pool-id [POOL_ID] --username [USERNAME] +aws cognito-idp admin-set-user-password --user-pool-id [POOL_ID] --username [USERNAME] --password [PASSWORD] --permanent ``` -8. Click on `Users and groups`, Sign up the two new users with the email and a temporary password. - -9. Click on App clients, and keep note of the app client web's `App client id`. This can be used the AWS AppSync console Queries. - -10. `amplify console api` -Click on Queries tab, and click on Log in. This will prompt you to enter the app client id, username, and temporary password. After logging in successfully, it will ask you to enter a new password. Make sure those are the same as the one specified in the credentials json file from step 5. Do this for both users. - - -### Creating users through AWS CLI +The `[POOL_ID]` can be found in `amplifyconfiguration.json` under `auth.plugin.awsCognitoAuthPlugin.CognitoUserPool.Default.PoolId` -7. Run the following commands +7. Create `AWSDataStoreCategoryPluginAuthIntegrationTests-credentials.json` inside the same folder, containing a json object of the user information from the users in the cognito user in the userpool. ``` -aws cognito-idp admin-create-user --user-pool-id [POOL_ID] --username [USER EMAIL] -aws cognito-idp admin-set-user-password --user-pool-id [POOL_ID] --username [USER EMAIL] --password [PASSWORD] --permanent +touch ~/.aws-amplify/amplify-ios/testconfiguration/AWSDataStoreCategoryPluginAuthIntegrationTests-credentials.json ``` -The `[POOL_ID]` can be found in `amplifyconfiguration.json` under `auth.plugin.awsCognitoAuthPlugin.CognitoUserPool.Default.PoolId` +```json +{ + "user1": "", + "passwordUser1": "", + "user2": "", + "passwordUser2": "" +} +``` + +8. Optionally, re-run `amplify codegen models` if you want to replace the existing model files under the Models directory in this project with the latest codegen output. You generally don't have to do this unless adding and testing new functionality. Now you can run the AWSDataStoreCategoryPluginAuthIntegrationTests diff --git a/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/singleauth-cognito-schema.graphql b/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/singleauth-cognito-schema.graphql index 20e0aae659..2361666492 100644 --- a/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/singleauth-cognito-schema.graphql +++ b/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/AWSDataStorePluginAuthCognitoTests/singleauth-cognito-schema.graphql @@ -34,3 +34,14 @@ type TodoCognitoPrivate @model @auth(rules: [{ allow: private }]) { id: ID! title: String! } + +type TodoCognitoMultiOwner @model + @auth(rules: [ + { allow: owner }, + { allow: owner, ownerField: "editors", operations: [update, read]} ]) { + id: ID! + title: String! + content: String + owner: String + editors: [String] +} diff --git a/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/DataStoreHostApp.xcodeproj/project.pbxproj b/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/DataStoreHostApp.xcodeproj/project.pbxproj index e14baef926..057b8f8b12 100644 --- a/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/DataStoreHostApp.xcodeproj/project.pbxproj +++ b/AmplifyPlugins/DataStore/Tests/DataStoreHostApp/DataStoreHostApp.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + 210445632AB4F6C900420AF9 /* TodoCognitoMultiOwner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 210445612AB4F6C900420AF9 /* TodoCognitoMultiOwner.swift */; }; + 210445642AB4F6C900420AF9 /* TodoCognitoMultiOwner+Schema.swift in Sources */ = {isa = PBXBuildFile; fileRef = 210445622AB4F6C900420AF9 /* TodoCognitoMultiOwner+Schema.swift */; }; 210D5D4A2982EF6B00D748FE /* AWSDataStoreLazyLoadPostCommentWithCompositeKeySnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 210D5D492982EF6B00D748FE /* AWSDataStoreLazyLoadPostCommentWithCompositeKeySnapshotTests.swift */; }; 210D5D4C2982F0A300D748FE /* AWSDataStoreLazyLoadPostComment4SnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 210D5D4B2982F0A300D748FE /* AWSDataStoreLazyLoadPostComment4SnapshotTests.swift */; }; 210D5D4E2982F1E700D748FE /* AWSDataStoreLazyLoadPostComment7SnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 210D5D4D2982F1E700D748FE /* AWSDataStoreLazyLoadPostComment7SnapshotTests.swift */; }; @@ -1537,6 +1539,8 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 210445612AB4F6C900420AF9 /* TodoCognitoMultiOwner.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TodoCognitoMultiOwner.swift; sourceTree = ""; }; + 210445622AB4F6C900420AF9 /* TodoCognitoMultiOwner+Schema.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TodoCognitoMultiOwner+Schema.swift"; sourceTree = ""; }; 210D5D492982EF6B00D748FE /* AWSDataStoreLazyLoadPostCommentWithCompositeKeySnapshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AWSDataStoreLazyLoadPostCommentWithCompositeKeySnapshotTests.swift; sourceTree = ""; }; 210D5D4B2982F0A300D748FE /* AWSDataStoreLazyLoadPostComment4SnapshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AWSDataStoreLazyLoadPostComment4SnapshotTests.swift; sourceTree = ""; }; 210D5D4D2982F1E700D748FE /* AWSDataStoreLazyLoadPostComment7SnapshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AWSDataStoreLazyLoadPostComment7SnapshotTests.swift; sourceTree = ""; }; @@ -2994,6 +2998,8 @@ 21BBFA94289BFE4E00B32A39 /* Models */ = { isa = PBXGroup; children = ( + 210445612AB4F6C900420AF9 /* TodoCognitoMultiOwner.swift */, + 210445622AB4F6C900420AF9 /* TodoCognitoMultiOwner+Schema.swift */, 21BBFA96289BFE4E00B32A39 /* TodoCognitoPrivate.swift */, 21BBFA97289BFE4E00B32A39 /* TodoCognitoPrivate+Schema.swift */, 21BBFA9E289BFE4E00B32A39 /* TodoCustomOwnerExplicit.swift */, @@ -4956,8 +4962,10 @@ 213DBBFF28A577B200B30280 /* AWSDataStoreCategoryPluginAuthIntegrationTests+Support.swift in Sources */, 213DBC0728A577C900B30280 /* TodoCustomOwnerImplicit+Schema.swift in Sources */, 213DBC5928A57FFE00B30280 /* TestConfigHelper.swift in Sources */, + 210445642AB4F6C900420AF9 /* TodoCognitoMultiOwner+Schema.swift in Sources */, 213DBC0328A577C300B30280 /* TodoExplicitOwnerField.swift in Sources */, 213DBC0928A577C900B30280 /* TodoExplicitOwnerField+Schema.swift in Sources */, + 210445632AB4F6C900420AF9 /* TodoCognitoMultiOwner.swift in Sources */, 681DFE9028E746FD0000C36A /* AsyncExpectation.swift in Sources */, 681DFE8F28E746FD0000C36A /* XCTestCase+AsyncTesting.swift in Sources */, 213DBC0128A577BC00B30280 /* TodoCognitoPrivate.swift in Sources */,