amplify-swift/AmplifyPlugins/Storage/AWSS3StoragePluginFunctiona.../AWSS3StoragePluginAccessLev...

403 lines
17 KiB
Swift

//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
import XCTest
import Amplify
import AWSS3
import AWSPluginsCore
@testable import AWSS3StoragePlugin
@testable import AmplifyTestCommon
// swiftlint:disable:next type_body_length
class AWSS3StoragePluginAccessLevelTests: AWSS3StoragePluginTestBase {
/// Given: An unauthenticated user
/// When: List API with protected access level
/// Then: Operation completes successfully with no items since there are no keys at that location.
func testListFromProtectedForUnauthenticatedUser() {
let key = UUID().uuidString
let completeInvoked = expectation(description: "Completed is invoked")
let options = StorageListRequest.Options(accessLevel: .protected,
targetIdentityId: nil,
path: key)
let operation = Amplify.Storage.list(options: options) { event in
switch event {
case .success(let result):
XCTAssertNotNil(result)
XCTAssertNotNil(result.items)
XCTAssertEqual(result.items.count, 0)
completeInvoked.fulfill()
case .failure(let error):
XCTFail("Failed with \(error)")
}
}
XCTAssertNotNil(operation)
waitForExpectations(timeout: TestCommonConstants.networkTimeout)
}
/// Given: An unauthenticated user
/// When: List API with private access level
/// Then: Operation fails with access denied service error
func testListFromPrivateForUnauthenticatedUserForReturnAccessDenied() {
let key = UUID().uuidString
let listFailedExpectation = expectation(description: "List Operation should fail")
let options = StorageListRequest.Options(accessLevel: .private,
targetIdentityId: nil,
path: key)
let operation = Amplify.Storage.list(options: options) { event in
switch event {
case .success:
XCTFail("Should not have completed")
case .failure(let error):
// swiftlint:disable:next todo
// TODO: service error, check string?
guard case let .accessDenied(description, _, _) = error else {
XCTFail("Expected accessDenied error")
return
}
XCTAssertEqual(description, StorageErrorConstants.accessDenied.errorDescription)
listFailedExpectation.fulfill()
}
}
XCTAssertNotNil(operation)
waitForExpectations(timeout: TestCommonConstants.networkTimeout)
}
/// Given: `user1` user uploads some data with protected access level
/// When: The user retrieves and removes the data
/// Then: The operations complete successful
func testUploadToProtectedAndListThenGetThenRemove() {
let key = UUID().uuidString
let accessLevel: StorageAccessLevel = .protected
putThenListThenGetThenRemoveForSingleUser(username: AWSS3StoragePluginTestBase.user1,
key: key,
accessLevel: accessLevel)
}
/// Given: `user1` user uploads some data with private access level
/// When: The user retrieves and removes the data
/// Then: The operations complete successful
func testUploadToPrivateAndListThenGetThenRemove() {
let key = UUID().uuidString
let accessLevel: StorageAccessLevel = .private
putThenListThenGetThenRemoveForSingleUser(username: AWSS3StoragePluginTestBase.user1,
key: key,
accessLevel: accessLevel)
}
/// Given: `user1` user uploads some data with public access level
/// When: `user2` lists, gets, and removes the data for `user1`
/// Then: The list, get, and remove operations complete successful and data is retrieved then removed.
func testUploadToPublicAndListThenGetThenRemoveFromOtherUser() {
let key = UUID().uuidString
let accessLevel: StorageAccessLevel = .guest
// Sign into user1
signIn(username: AWSS3StoragePluginTestBase.user1, password: AWSS3StoragePluginTestBase.password)
let user1IdentityId = getIdentityId()
// Upload data
upload(key: key, data: key, accessLevel: accessLevel)
// Sign out of user1 and into user2
signOut()
signIn(username: AWSS3StoragePluginTestBase.user2, password: AWSS3StoragePluginTestBase.password)
let user2IdentityId = getIdentityId()
XCTAssertNotEqual(user1IdentityId, user2IdentityId)
// list keys as user2
let keys = list(path: key, accessLevel: accessLevel)
XCTAssertNotNil(keys)
XCTAssertEqual(keys!.count, 1)
// get key as user2
let data = get(key: key, accessLevel: accessLevel)
XCTAssertNotNil(data)
// remove key as user2
remove(key: key, accessLevel: accessLevel)
// get key after removal should return NotFound
let getFailedExpectation = expectation(description: "Get Operation should fail")
let getOptions = StorageDownloadDataRequest.Options(accessLevel: accessLevel,
targetIdentityId: nil)
_ = Amplify.Storage.downloadData(key: key, options: getOptions) { event in
switch event {
case .success(let results):
XCTFail("Should not have completed with result \(results)")
case .failure(let error):
guard case .keyNotFound = error else {
XCTFail("Expected notFound error")
return
}
getFailedExpectation.fulfill()
}
}
waitForExpectations(timeout: TestCommonConstants.networkTimeout)
}
/// GivenK: `user1` user uploads some data with protected access level
/// When: `user2` lists, gets, and removes the data for `user1`
/// Then: The list and get operations complete successful and data is retrieved.
func testUploadToProtectedAndListThenGetFromOtherUser() {
let key = UUID().uuidString
let accessLevel: StorageAccessLevel = .protected
// Sign into user1
signIn(username: AWSS3StoragePluginTestBase.user1, password: AWSS3StoragePluginTestBase.password)
let user1IdentityId = getIdentityId()
// Upload
upload(key: key, data: key, accessLevel: accessLevel)
// Sign out of user1 and into user2
signOut()
signIn(username: AWSS3StoragePluginTestBase.user2, password: AWSS3StoragePluginTestBase.password)
let user2IdentityId = getIdentityId()
XCTAssertNotEqual(user1IdentityId, user2IdentityId)
// list keys for user1 as user2
let keys = list(path: key, accessLevel: accessLevel, targetIdentityId: user1IdentityId)
XCTAssertNotNil(keys)
XCTAssertEqual(keys!.count, 1)
// get key for user1 as user2
let data = get(key: key, accessLevel: accessLevel, targetIdentityId: user1IdentityId)
XCTAssertNotNil(data)
}
/// Given: `user1` user uploads some data with private access level
/// When: `user2` lists and gets the data for `user1`
/// Then: The list and get operations fail with validation errors
func testUploadToPrivateAndFailListThenFailGetFromOtherUser() {
let key = UUID().uuidString
let accessLevel: StorageAccessLevel = .private
// Sign into user1
signIn(username: AWSS3StoragePluginTestBase.user1, password: AWSS3StoragePluginTestBase.password)
let user1IdentityId = getIdentityId()
// Upload
upload(key: key, data: key, accessLevel: accessLevel)
// Sign out of user1 and into user2
signOut()
signIn(username: AWSS3StoragePluginTestBase.user2, password: AWSS3StoragePluginTestBase.password)
let user2IdentityId = getIdentityId()
XCTAssertNotEqual(user1IdentityId, user2IdentityId)
// list keys for user1 as user2 - should fail with validation error
let listFailedExpectation = expectation(description: "List operation should fail")
let listOptions = StorageListRequest.Options(accessLevel: accessLevel,
targetIdentityId: user1IdentityId,
path: key)
_ = Amplify.Storage.list(options: listOptions) { event in
switch event {
case .success:
XCTFail("Should not have completed")
case .failure(let error):
guard case .validation = error else {
XCTFail("Expected validation error, not \(error)")
return
}
listFailedExpectation.fulfill()
}
}
waitForExpectations(timeout: TestCommonConstants.networkTimeout)
// get key for user1 as user2 - should fail with validation error
let getFailedExpectation = expectation(description: "Get Operation should fail")
let getOptions = StorageDownloadDataRequest.Options(accessLevel: accessLevel,
targetIdentityId: user1IdentityId)
_ = Amplify.Storage.downloadData(key: key, options: getOptions) { event in
switch event {
case .success(let results):
XCTFail("Should not have completed, got \(results)")
case .failure(let error):
guard case .validation = error else {
XCTFail("Expected validation error, not \(error)")
return
}
getFailedExpectation.fulfill()
}
}
waitForExpectations(timeout: TestCommonConstants.networkTimeout)
}
// MARK: - Common test functions
func putThenListThenGetThenRemoveForSingleUser(username: String, key: String, accessLevel: StorageAccessLevel) {
signIn(username: username, password: AWSS3StoragePluginTestBase.password)
// Upload
upload(key: key, data: key, accessLevel: accessLevel)
// List
let keys = list(path: key, accessLevel: accessLevel)
XCTAssertNotNil(keys)
XCTAssertEqual(keys!.count, 1)
// Get
let data = get(key: key, accessLevel: accessLevel)
XCTAssertNotNil(data)
// Remove
remove(key: key, accessLevel: accessLevel)
// Get key after removal should return NotFound
let getFailedExpectation = expectation(description: "Get Operation should fail")
let getOptions = StorageDownloadDataRequest.Options(accessLevel: accessLevel,
targetIdentityId: nil)
_ = Amplify.Storage.downloadData(key: key, options: getOptions) { event in
switch event {
case .success(let results):
XCTFail("Should not have completed with result \(results)")
case .failure(let error):
guard case .keyNotFound = error else {
XCTFail("Expected notFound error, not \(error)")
return
}
getFailedExpectation.fulfill()
}
}
waitForExpectations(timeout: TestCommonConstants.networkTimeout)
}
// MARK: StoragePlugin Helper functions
func list(path: String, accessLevel: StorageAccessLevel, targetIdentityId: String? = nil) ->
[StorageListResult.Item]? {
var items: [StorageListResult.Item]?
let listExpectation = expectation(description: "List operation should be successful")
let listOptions = StorageListRequest.Options(accessLevel: accessLevel,
targetIdentityId: targetIdentityId,
path: path)
_ = Amplify.Storage.list(options: listOptions) { event in
switch event {
case .success(let results):
items = results.items
listExpectation.fulfill()
case .failure(let error):
XCTFail("Failed to list with error \(error)")
}
}
waitForExpectations(timeout: TestCommonConstants.networkTimeout)
return items
}
func get(key: String, accessLevel: StorageAccessLevel, targetIdentityId: String? = nil) -> Data? {
var data: Data?
let getExpectation = expectation(description: "Get Operation should be successful")
let getOptions = StorageDownloadDataRequest.Options(accessLevel: accessLevel,
targetIdentityId: targetIdentityId)
_ = Amplify.Storage.downloadData(key: key, options: getOptions) { event in
switch event {
case .success(let result):
data = result
getExpectation.fulfill()
case .failure(let error):
XCTFail("Failed to get with error \(error)")
}
}
waitForExpectations(timeout: TestCommonConstants.networkTimeout)
return data
}
func upload(key: String, data: String, accessLevel: StorageAccessLevel) {
let uploadExpectation = expectation(description: "Upload operation should be successful")
let options = StorageUploadDataRequest.Options(accessLevel: accessLevel)
_ = Amplify.Storage.uploadData(key: key, data: data.data(using: .utf8)!, options: options) { event in
switch event {
case .success:
uploadExpectation.fulfill()
case .failure(let error):
XCTFail("Failed to put \(key) with error \(error)")
}
}
waitForExpectations(timeout: TestCommonConstants.networkTimeout)
}
func remove(key: String, accessLevel: StorageAccessLevel) {
let removeExpectation = expectation(description: "Remove Operation should be successful")
let removeOptions = StorageRemoveRequest.Options(accessLevel: accessLevel)
_ = Amplify.Storage.remove(key: key, options: removeOptions) { event in
switch event {
case .success:
removeExpectation.fulfill()
case .failure(let error):
XCTFail("Failed to remove with error \(error)")
}
}
waitForExpectations(timeout: TestCommonConstants.networkTimeout)
}
// Auth Helpers
func signIn(username: String, password: String) {
let signInInvoked = expectation(description: "sign in completed")
_ = Amplify.Auth.signIn(username: username, password: password) { event in
switch event {
case .success:
signInInvoked.fulfill()
case .failure(let error):
XCTFail("Failed to Sign in user \(error)")
default:
XCTFail("Unexpected event")
}
}
wait(for: [signInInvoked], timeout: TestCommonConstants.networkTimeout)
}
func getIdentityId() -> String {
let retrieveIdentityCompleted = expectation(description: "retrieve identity completed")
var resultOptional: String?
_ = Amplify.Auth.fetchAuthSession(listener: { event in
switch event {
case .success(let authSession):
guard let cognitoAuthSession = authSession as? AuthCognitoIdentityProvider else {
XCTFail("Could not get auth session as AuthCognitoIdentityProvider")
return
}
switch cognitoAuthSession.getIdentityId() {
case .success(let identityId):
resultOptional = identityId
retrieveIdentityCompleted.fulfill()
case .failure(let error):
XCTFail("Failed to get auth session \(error)")
}
case .failure(let error):
XCTFail("Failed to get auth session \(error)")
default:
XCTFail("Unexpected event")
}
})
wait(for: [retrieveIdentityCompleted], timeout: TestCommonConstants.networkTimeout)
guard let result = resultOptional else {
fatalError("Could not get identityId for user")
}
return result
}
func signOut() {
let signOutCompleted = expectation(description: "sign out completed")
Amplify.Auth.signOut { event in
switch event {
case .success:
signOutCompleted.fulfill()
case .failure(let error):
XCTFail("Could not sign out user \(error)")
default:
XCTFail("Unexpected event")
}
}
wait(for: [signOutCompleted], timeout: TestCommonConstants.networkTimeout)
}
} // swiftlint:disable:this file_length