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

fix(datastore): filter authrules with invalid ownerfield #1606

Closed
wants to merge 1 commit into from
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,14 @@ 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
decorateDocument = decorateAuthStrategy(document: decorateDocument,
Expand Down Expand Up @@ -180,6 +174,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 {
Expand All @@ -196,6 +199,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 { }
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ class AWSDataStoreAuthBaseTest: XCTestCase {
amplifyConfig = try TestConfigHelper.retrieveAmplifyConfiguration(forResource: configFile)

} catch {
XCTFail("Error during setup: \(error)")
fatalError("Error during setup: \(error)")
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,37 @@ class AWSDataStoreCategoryPluginAuthOwnerIntegrationTests: AWSDataStoreAuthBaseT
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() {
setup(withModels: ExplicitMultipleOwnerModelRegistration(),
testType: .defaultAuthCognito)

signIn(user: user1)

let expectations = makeExpectations()

assertDataStoreReady(expectations)

// Query
assertQuerySuccess(modelType: PostDraftCognitoMultiOwner.self,
expectations) { error in
XCTFail("Error query \(error)")
}

let post = PostDraftCognitoMultiOwner(title: "title")

// Mutations
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,
Expand Down Expand Up @@ -189,6 +220,13 @@ extension AWSDataStoreCategoryPluginAuthOwnerIntegrationTests {
}
}

struct ExplicitMultipleOwnerModelRegistration: AmplifyModelRegistration {
public let version: String = "version"
func registerModels(registry: ModelRegistry.Type) {
ModelRegistry.register(modelType: PostDraftCognitoMultiOwner.self)
}
}

struct ImplicitOwnerModelRegistration: AmplifyModelRegistration {
public let version: String = "version"
func registerModels(registry: ModelRegistry.Type) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

// swiftlint:disable all
import Amplify
import Foundation

extension PostDraftCognitoMultiOwner {
// 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 postDraftCognitoMultiOwner = PostDraftCognitoMultiOwner.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.pluralName = "PostDraftCognitoMultiOwners"

model.fields(
.id(),
.field(postDraftCognitoMultiOwner.title, is: .required, ofType: .string),
.field(postDraftCognitoMultiOwner.content, is: .optional, ofType: .string),
.field(postDraftCognitoMultiOwner.owner, is: .optional, ofType: .string),
.field(postDraftCognitoMultiOwner.editors, is: .optional, ofType: .embeddedCollection(of: String.self)),
.field(postDraftCognitoMultiOwner.createdAt, is: .optional, isReadOnly: true, ofType: .dateTime),
.field(postDraftCognitoMultiOwner.updatedAt, is: .optional, isReadOnly: true, ofType: .dateTime)
)
}
}
Original file line number Diff line number Diff line change
@@ -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 PostDraftCognitoMultiOwner: 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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,18 @@ type TodoCognitoPrivate @model @auth(rules: [{ allow: private }]) {
id: ID!
title: String!
}

type PostDraftCognitoMultiOwner @model
@auth(rules: [
# Defaults to use the "owner" field.
{ allow: owner },

# Authorize both the update mutation and queries.
{ allow: owner, ownerField: "editors", operations: [update, read] }
]) {
id: ID!
title: String!
content: String
owner: String
editors: [String]
}
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@
6BE9D6F125A6643100AB5C9A /* StorageEngineTestsHasOne.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE9D6F025A6643100AB5C9A /* StorageEngineTestsHasOne.swift */; };
6BE9D6F325A665EA00AB5C9A /* StorageEngineTestsBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE9D6F225A665EA00AB5C9A /* StorageEngineTestsBase.swift */; };
6BE9D73E25A6800100AB5C9A /* StorageEngineTestsManyToMany.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6BE9D73D25A6800100AB5C9A /* StorageEngineTestsManyToMany.swift */; };
7613F9E3279B7D32009B9007 /* PostDraftCognitoMultiOwner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7613F9E1279B7D31009B9007 /* PostDraftCognitoMultiOwner.swift */; };
7613F9E4279B7D32009B9007 /* PostDraftCognitoMultiOwner+Schema.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7613F9E2279B7D31009B9007 /* PostDraftCognitoMultiOwner+Schema.swift */; };
7617928827558BFA001EABD6 /* TodoCustomOwnerExplicit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7617928627558BFA001EABD6 /* TodoCustomOwnerExplicit.swift */; };
7617928927558BFA001EABD6 /* TodoCustomOwnerExplicit+Schema.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7617928727558BFA001EABD6 /* TodoCustomOwnerExplicit+Schema.swift */; };
7617928B275594B8001EABD6 /* AWSDataStoreCategoryPluginIAMAuthIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7617928A275594B8001EABD6 /* AWSDataStoreCategoryPluginIAMAuthIntegrationTests.swift */; };
Expand Down Expand Up @@ -560,6 +562,8 @@
6BE9D6F025A6643100AB5C9A /* StorageEngineTestsHasOne.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageEngineTestsHasOne.swift; sourceTree = "<group>"; };
6BE9D6F225A665EA00AB5C9A /* StorageEngineTestsBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageEngineTestsBase.swift; sourceTree = "<group>"; };
6BE9D73D25A6800100AB5C9A /* StorageEngineTestsManyToMany.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StorageEngineTestsManyToMany.swift; sourceTree = "<group>"; };
7613F9E1279B7D31009B9007 /* PostDraftCognitoMultiOwner.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PostDraftCognitoMultiOwner.swift; sourceTree = "<group>"; };
7613F9E2279B7D31009B9007 /* PostDraftCognitoMultiOwner+Schema.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "PostDraftCognitoMultiOwner+Schema.swift"; sourceTree = "<group>"; };
7617928627558BFA001EABD6 /* TodoCustomOwnerExplicit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TodoCustomOwnerExplicit.swift; sourceTree = "<group>"; };
7617928727558BFA001EABD6 /* TodoCustomOwnerExplicit+Schema.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TodoCustomOwnerExplicit+Schema.swift"; sourceTree = "<group>"; };
7617928A275594B8001EABD6 /* AWSDataStoreCategoryPluginIAMAuthIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AWSDataStoreCategoryPluginIAMAuthIntegrationTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1299,6 +1303,8 @@
76C0090C2736061200ADA120 /* Models */ = {
isa = PBXGroup;
children = (
7613F9E1279B7D31009B9007 /* PostDraftCognitoMultiOwner.swift */,
7613F9E2279B7D31009B9007 /* PostDraftCognitoMultiOwner+Schema.swift */,
761792A227594339001EABD6 /* TodoCognitoPrivate.swift */,
761792A127594339001EABD6 /* TodoCognitoPrivate+Schema.swift */,
7617928627558BFA001EABD6 /* TodoCustomOwnerExplicit.swift */,
Expand Down Expand Up @@ -2321,6 +2327,7 @@
76C009112736061200ADA120 /* TodoExplicitOwnerField.swift in Sources */,
762547F42735FD0C00E5F6A3 /* GroupPublicUPIAMPost+Schema.swift in Sources */,
769CF2C9266D7F04007843A0 /* AWSDataStoreMultiAuthTwoRulesTests.swift in Sources */,
7613F9E4279B7D32009B9007 /* PostDraftCognitoMultiOwner+Schema.swift in Sources */,
762547E82735FD0C00E5F6A3 /* PrivatePublicComboUPPost.swift in Sources */,
762547D52735FD0C00E5F6A3 /* PrivateIAMPost+Schema.swift in Sources */,
762547CF2735FD0C00E5F6A3 /* OwnerPrivatePublicUPIAMAPIPost+Schema.swift in Sources */,
Expand Down Expand Up @@ -2348,6 +2355,7 @@
762547F02735FD0C00E5F6A3 /* OwnerOIDCPost.swift in Sources */,
762547C52735FD0C00E5F6A3 /* OwnerUPPost+Schema.swift in Sources */,
762547E72735FD0C00E5F6A3 /* PrivatePrivatePublicUPIAMIAMPost+Schema.swift in Sources */,
7613F9E3279B7D32009B9007 /* PostDraftCognitoMultiOwner.swift in Sources */,
762547D12735FD0C00E5F6A3 /* OwnerPublicOIDAPIPost.swift in Sources */,
762547DD2735FD0C00E5F6A3 /* PrivatePrivateUPIAMPost+Schema.swift in Sources */,
761792A327594339001EABD6 /* TodoCognitoPrivate+Schema.swift in Sources */,
Expand Down