amplify-swift/AmplifyPlugins/Auth/Tests/AWSCognitoAuthPluginUnitTests/Support/DefaultConfig.swift

468 lines
20 KiB
Swift

//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
import Foundation
@testable import AWSCognitoAuthPlugin
import AWSCognitoIdentityProvider
import AWSCognitoIdentity
import ClientRuntime
@testable import Amplify
import AWSPluginsCore
enum Defaults {
static let regionString = "us-east-1"
static let identityPoolId = "XXX"
static let userPoolId = "XXX_XX"
static let appClientId = "XXX"
static let appClientSecret = "XXX"
static let validAccessToken = "eyJraWQiOiJRbWZIcnYyS1F2ZEtyRm5WYUNxbzd4MWM1ZjA3TFhFaFhZQ1VSSXU2eitvPSIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiI0MjEzZGY1ZS1mNzBiLTQ0MDUtYjhiNC05NjMzMjRhNmUwYjgiLCJkZXZpY2Vfa2V5IjoidXMtZWFzdC0xXzUyMDYxOTFjLTY5N2QtNGEzNC1iYTZmLWVmMDcwOGFkMzk3OSIsImlzcyI6Imh0dHBzOlwvXC9jb2duaXRvLWlkcC51cy1lYXN0LTEuYW1hem9uYXdzLmNvbVwvdXMtZWFzdC0xX2w2bksxOUFMUSIsImNsaWVudF9pZCI6IjY5OW91OHRhcXZhaDVvYzA3M29zZmo4bzgyIiwib3JpZ2luX2p0aSI6ImViMDRiOTcwLTY5NDEtNDVlZS05NTQ1LTY4ODk1ZTNlMDAwZiIsImV2ZW50X2lkIjoiODYyM2JlZDctMTBhYi00YzM1LTk0MzctMzdlMWY2YTkxZDg1IiwidG9rZW5fdXNlIjoiYWNjZXNzIiwic2NvcGUiOiJhd3MuY29nbml0by5zaWduaW4udXNlci5hZG1pbiIsImF1dGhfdGltZSI6MTY1OTA2OTMwNywiZXhwIjoxNjU5MDcyOTA3LCJpYXQiOjE2NTkwNjkzMDcsImp0aSI6ImE2ZWQ2MWE1LTFiMGUtNDgyZC04YmY1LTI1M2JiYWRhNjFhYyIsInVzZXJuYW1lIjoiaW50ZWd0ZXN0OWRlMmVlNDctY2I4MS00YjdhLWI4OTAtOGMyYzczZjRkN2RmIn0.Mjl-G9QGXF8KwZbQrxd3uNaOf4EChzltfklRp7inuxLTFLuKQqena8VctiSUQp4jDnBEBXw2Hu3D5ZvVyGoL0FQamxMvtPRIVl050XEir_RKk6M_d9Qp4pdDNH1HwJ-id9CgpvA3xpgpIH09n2voTMbgGGLO-ivuCsJCa0IbsRUJwrua-wkr5g3-3mmFFqrNrqyFhvuQRWQ6DoVo_bjwp3WVYmNq69PaxxYYXw7b-86DGGOC4kqAvQD9WiZtu8ad63kc5zJ-MjtbKfJLK8L4cyW6ga-kZn-6MjDIn8UoToWOtLncfFM1sJiucFCcPdZoM2jBJA5WDT_0QDwAOBQjMg"
static let validIdToken = "eyJraWQiOiJ1Z05CbHphK0tuQ2ZoT2l6a0ZSXC9SWldcL2lCZVwveEd2N1ZIZjR3eUxxSFhFPSIsImFsZyI6IlJTMjU2In0.eyJzdWIiOiI0MjEzZGY1ZS1mNzBiLTQ0MDUtYjhiNC05NjMzMjRhNmUwYjgiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImlzcyI6Imh0dHBzOlwvXC9jb2duaXRvLWlkcC51cy1lYXN0LTEuYW1hem9uYXdzLmNvbVwvdXMtZWFzdC0xX2w2bksxOUFMUSIsImNvZ25pdG86dXNlcm5hbWUiOiJpbnRlZ3Rlc3Q5ZGUyZWU0Ny1jYjgxLTRiN2EtYjg5MC04YzJjNzNmNGQ3ZGYiLCJvcmlnaW5fanRpIjoiZWIwNGI5NzAtNjk0MS00NWVlLTk1NDUtNjg4OTVlM2UwMDBmIiwiYXVkIjoiNjk5b3U4dGFxdmFoNW9jMDczb3NmajhvODIiLCJldmVudF9pZCI6Ijg2MjNiZWQ3LTEwYWItNGMzNS05NDM3LTM3ZTFmNmE5MWQ4NSIsInRva2VuX3VzZSI6ImlkIiwiYXV0aF90aW1lIjoxNjU5MDY5MzA3LCJleHAiOjE2NTkwNzI5MDcsImlhdCI6MTY1OTA2OTMwNywianRpIjoiN2M3Y2ZlMzEtZjQ0NC00MjE4LWFmOGMtOGRlNjk1YmQ5Yzk5IiwiZW1haWwiOiJyb3lqaStwZW50ZXN0MUBhbWF6b24uY29tIn0.ZMtZEFv7K8tbCvsM6QcD1czC2KmCm-LGjrE_ew5a4cTk9kgGhy1JlArWA691YDapA9Humyk3EX2PBVSGam6-DJTqV13JhwzPKLsZFhm5u1QiddD8bqZRxJ_Wc2MFZnntox2iKUpE2fsmrxpSKsJtVLnUxjpy2s-4A-v6T-6MzSCWPmcC1lzi69ETd8cfqCVx2QH5BuE5aKQtUNB1LL-cOfg_LhMACJovwRvP3kGNyAjSmJp88GXQyayN4JO4zZU3vJ_Xx1P8tvsohvOMRiB69gA46uApSOhC1SWK5WBRKO8oYK969nxT9yvX2hv9yTQq1HyhYXtShdwzaGru3LY3Nw"
static func authConfig() -> [String: Any] {
let authConfig = [
"UserAgent": "aws-amplify/cli",
"Version": "0.1.0",
"IdentityManager": [
"Default": []
],
"CredentialsProvider": [
"CognitoIdentity": [
"Default": [
"PoolId": identityPoolId,
"Region": regionString
]
]
],
"CognitoIdentityProvider": [
"Default": [
"PoolId": userPoolId,
"AppClientId": appClientId,
"AppClientSecret": appClientSecret,
"Region": regionString
]
],
"Auth": [
"Default": [
"authenticationFlowType": "USER_SRP_AUTH"
]
]
] as [String: Any]
return authConfig
}
static func makeCredentialStoreOperationBehavior() -> CredentialStoreStateBehavior {
return MockCredentialStoreOperationClient()
}
static func makeDefaultUserPool() throws -> CognitoUserPoolBehavior {
return try CognitoIdentityProviderClient(region: regionString)
}
static func makeDefaultASF() -> AdvancedSecurityBehavior {
return MockASF()
}
static func makeUserPoolAnalytics() -> UserPoolAnalyticsBehavior {
MockAnalyticsHandler()
}
static func makeIdentity() throws -> CognitoIdentityBehavior {
let getId: MockIdentity.MockGetIdResponse = { _ in
return .init(identityId: "mockIdentityId")
}
let getCredentials: MockIdentity.MockGetCredentialsResponse = { _ in
let credentials = CognitoIdentityClientTypes.Credentials(accessKeyId: "accessKey",
expiration: Date(),
secretKey: "secret",
sessionToken: "session")
return .init(credentials: credentials, identityId: "responseIdentityID")
}
return MockIdentity(mockGetIdResponse: getId, mockGetCredentialsResponse: getCredentials)
}
static func makeDefaultUserPoolConfigData(withHostedUI: HostedUIConfigurationData? = nil)
-> UserPoolConfigurationData {
UserPoolConfigurationData(poolId: userPoolId,
clientId: appClientId,
region: regionString,
clientSecret: appClientSecret,
pinpointAppId: "",
hostedUIConfig: withHostedUI)
}
static func makeIdentityConfigData() -> IdentityPoolConfigurationData {
IdentityPoolConfigurationData(poolId: identityPoolId,
region: regionString)
}
static func makeDefaultAuthConfigData(withHostedUI: HostedUIConfigurationData? = nil) -> AuthConfiguration {
let userPoolConfigData = makeDefaultUserPoolConfigData(withHostedUI: withHostedUI)
let identityConfigDate = makeIdentityConfigData()
return .userPoolsAndIdentityPools(userPoolConfigData, identityConfigDate)
}
static func makeAmplifyStore() -> AmplifyAuthCredentialStoreBehavior {
return MockAmplifyStore()
}
static func makeLegacyStore(service: String) -> KeychainStoreBehavior {
return MockLegacyStore()
}
static func makeDefaultCredentialStoreEnvironment(
amplifyStoreFactory: @escaping () -> AmplifyAuthCredentialStoreBehavior = makeAmplifyStore,
legacyStoreFactory: @escaping (String) -> KeychainStoreBehavior = makeLegacyStore(service: )
) -> CredentialEnvironment {
CredentialEnvironment(
authConfiguration: makeDefaultAuthConfigData(),
credentialStoreEnvironment: BasicCredentialStoreEnvironment(
amplifyCredentialStoreFactory: amplifyStoreFactory,
legacyKeychainStoreFactory: legacyStoreFactory
),
logger: Amplify.Logging.logger(forCategory: "awsCognitoAuthPluginTest")
)
}
static func makeDefaultAuthEnvironment(
authZEnvironment: BasicAuthorizationEnvironment? = nil,
identityPoolFactory: @escaping () throws -> CognitoIdentityBehavior = makeIdentity,
userPoolFactory: @escaping () throws -> CognitoUserPoolBehavior = makeDefaultUserPool,
hostedUIEnvironment: HostedUIEnvironment? = nil
) -> AuthEnvironment {
let userPoolConfigData = makeDefaultUserPoolConfigData()
let identityPoolConfigData = makeIdentityConfigData()
let srpAuthEnvironment = BasicSRPAuthEnvironment(
userPoolConfiguration: userPoolConfigData,
cognitoUserPoolFactory: userPoolFactory
)
let srpSignInEnvironment = BasicSRPSignInEnvironment(srpAuthEnvironment: srpAuthEnvironment)
let userPoolEnvironment = BasicUserPoolEnvironment(
userPoolConfiguration: userPoolConfigData,
cognitoUserPoolFactory: userPoolFactory,
cognitoUserPoolASFFactory: makeDefaultASF,
cognitoUserPoolAnalyticsHandlerFactory: makeUserPoolAnalytics)
let authenticationEnvironment = BasicAuthenticationEnvironment(srpSignInEnvironment: srpSignInEnvironment,
userPoolEnvironment: userPoolEnvironment,
hostedUIEnvironment: hostedUIEnvironment)
let authorizationEnvironment = BasicAuthorizationEnvironment(
identityPoolConfiguration: identityPoolConfigData,
cognitoIdentityFactory: identityPoolFactory)
let authEnv = AuthEnvironment(
configuration: Defaults.makeDefaultAuthConfigData(),
userPoolConfigData: userPoolConfigData,
identityPoolConfigData: identityPoolConfigData,
authenticationEnvironment: authenticationEnvironment,
authorizationEnvironment: authZEnvironment ?? authorizationEnvironment,
credentialsClient: makeCredentialStoreOperationBehavior(),
logger: Amplify.Logging.logger(forCategory: "awsCognitoAuthPluginTest")
)
Amplify.Logging.logLevel = .verbose
return authEnv
}
static func makeDefaultAuthStateMachine(
initialState: AuthState? = nil,
identityPoolFactory: @escaping () throws -> CognitoIdentityBehavior = makeIdentity,
userPoolFactory: @escaping () throws -> CognitoUserPoolBehavior = makeDefaultUserPool,
hostedUIEnvironment: HostedUIEnvironment? = nil) ->
AuthStateMachine {
let environment = makeDefaultAuthEnvironment(identityPoolFactory: identityPoolFactory,
userPoolFactory: userPoolFactory,
hostedUIEnvironment: hostedUIEnvironment)
return AuthStateMachine(resolver: AuthState.Resolver(),
environment: environment,
initialState: initialState)
}
static func makeDefaultCredentialStateMachine() -> CredentialStoreStateMachine {
return CredentialStoreStateMachine(resolver: CredentialStoreState.Resolver(),
environment: makeDefaultCredentialStoreEnvironment(),
initialState: .idle)
}
static func authStateMachineWith(environment: AuthEnvironment = makeDefaultAuthEnvironment(),
initialState: AuthState? = nil)
-> AuthStateMachine {
return AuthStateMachine(resolver: AuthState.Resolver(),
environment: environment,
initialState: initialState)
}
static func makeAuthState(tokens: AWSCognitoUserPoolTokens,
signedInDate: Date = Date(),
signInMethod: SignInMethod = .apiBased(.userSRP)) -> AuthState {
let signedInData = SignedInData(signedInDate: signedInDate,
signInMethod: signInMethod,
cognitoUserPoolTokens: tokens)
let authNState: AuthenticationState = .signedIn(signedInData)
let authZState: AuthorizationState = .configured
let authState: AuthState = .configured(authNState, authZState)
return authState
}
static func makeAuthConfiguration() -> AuthConfiguration {
AuthConfiguration.userPoolsAndIdentityPools(
Defaults.makeDefaultUserPoolConfigData(),
Defaults.makeIdentityConfigData()
)
}
static func makeCognitoUserPoolTokens(idToken: String = "XX",
accessToken: String = "",
refreshToken: String = "XX",
expiresIn: Int = 300) -> AWSCognitoUserPoolTokens {
AWSCognitoUserPoolTokens(idToken: idToken, accessToken: accessToken, refreshToken: refreshToken, expiresIn: expiresIn)
}
}
struct MockCredentialStoreOperationClient: CredentialStoreStateBehavior {
let mockAmplifyStore = MockAmplifyStore()
func fetchData(type: CredentialStoreDataType) async throws -> CredentialStoreData {
do {
switch type {
case .amplifyCredentials:
let amplifyCredentials = try mockAmplifyStore.retrieveCredential()
return .amplifyCredentials(amplifyCredentials)
case .deviceMetadata(username: let username):
let deviceMetadata = try mockAmplifyStore.retrieveDevice(for: username)
return .deviceMetadata(deviceMetadata, username)
case .asfDeviceId(username: let username):
let device = try mockAmplifyStore.retrieveASFDevice(for: username)
return .asfDeviceId(device, username)
}
}
catch KeychainStoreError.itemNotFound {
switch type {
case .amplifyCredentials:
return .amplifyCredentials(.testData)
case .deviceMetadata(username: let username):
return .deviceMetadata(.metadata(.init(
deviceKey: "key",
deviceGroupKey: "key",
deviceSecret: "secret")), username)
case .asfDeviceId(username: let username):
return .asfDeviceId("id", username)
}
}
catch {
fatalError()
}
}
func storeData(data: CredentialStoreData) async throws {
switch data {
case .amplifyCredentials(let amplifyCredentials):
try mockAmplifyStore.saveCredential(amplifyCredentials)
case .deviceMetadata(let deviceMetadata, let username):
try mockAmplifyStore.saveDevice(deviceMetadata, for: username)
case .asfDeviceId(let string, let username):
try mockAmplifyStore.saveASFDevice(string, for: username)
}
}
func deleteData(type: CredentialStoreDataType) async throws {
}
}
class MockAmplifyStore: AmplifyAuthCredentialStoreBehavior {
let credentialsKey = "amplifyCredentials"
static var dict = AtomicDictionary<String, Data>()
func saveCredential(_ credential: AmplifyCredentials) throws {
let value = (try? JSONEncoder().encode(credential)) ?? Data()
Self.dict.set(value: value, forKey: credentialsKey)
}
func retrieveCredential() throws -> AmplifyCredentials {
guard let data = Self.dict.getValue(forKey: credentialsKey),
let cred = (try? JSONDecoder().decode(AmplifyCredentials.self, from: data)) else {
throw KeychainStoreError.itemNotFound
}
return cred
}
func deleteCredential() throws {
Self.dict.removeValue(forKey: credentialsKey)
}
func getKeychainStore() -> KeychainStoreBehavior {
return MockLegacyStore()
}
func saveDevice(_ deviceMetadata: DeviceMetadata, for username: String) throws {
let value = (try? JSONEncoder().encode(deviceMetadata)) ?? Data()
Self.dict.set(value: value, forKey: username)
}
func retrieveDevice(for username: String) throws -> DeviceMetadata {
guard let data = Self.dict.getValue(forKey: username),
let device = (try? JSONDecoder().decode(DeviceMetadata.self, from: data)) else {
throw KeychainStoreError.itemNotFound
}
return device
}
func removeDevice(for username: String) throws {
Self.dict.removeValue(forKey: username)
}
func saveASFDevice(_ deviceId: String, for username: String) throws {
let value = (try? JSONEncoder().encode(deviceId)) ?? Data()
Self.dict.set(value: value, forKey: username)
}
func retrieveASFDevice(for username: String) throws -> String {
guard let data = Self.dict.getValue(forKey: username),
let device = (try? JSONDecoder().decode(String.self, from: data)) else {
throw KeychainStoreError.itemNotFound
}
return device
}
func removeASFDevice(for username: String) throws {
Self.dict.removeValue(forKey: username)
}
}
struct MockLegacyStore: KeychainStoreBehavior {
func _getString(_ key: String) throws -> String {
return ""
}
func _getData(_ key: String) throws -> Data {
return Data()
}
func _set(_ value: String, key: String) throws {
}
func _set(_ value: Data, key: String) throws {
}
func _remove(_ key: String) throws {
}
func _removeAll() throws {
}
}
struct MockASF: AdvancedSecurityBehavior {
func userContextData(for username: String,
deviceInfo: ASFDeviceBehavior,
appInfo: ASFAppInfoBehavior,
configuration: UserPoolConfigurationData) throws -> String {
return ""
}
}
extension AmplifyConfiguration {
static func testData() -> AmplifyConfiguration {
return try! AmplifyConfiguration.decodeAmplifyConfiguration(from: json.data(using: .utf8)!)
}
static let json: String = """
{
"UserAgent": "aws-amplify-cli/2.0",
"Version": "1.0",
"auth": {
"plugins": {
"awsCognitoAuthPlugin": {
"UserAgent": "aws-amplify/cli",
"Version": "0.1.0",
"IdentityManager": {
"Default": {}
},
"CredentialsProvider": {
"CognitoIdentity": {
"Default": {
"PoolId": "us-east-1:XXX",
"Region": "us-east-1"
}
}
},
"CognitoUserPool": {
"Default": {
"PoolId": "us-east-1_XXX",
"AppClientId": "XXX",
"Region": "us-east-1"
}
},
"Auth": {
"Default": {
"OAuth": {
"WebDomain": "XXX-dev.auth.us-east-1.amazoncognito.com",
"AppClientId": "XXX",
"SignInRedirectURI": "myapp://",
"SignOutRedirectURI": "myapp://",
"Scopes": [
"phone",
"email",
"openid",
"profile",
"aws.cognito.signin.user.admin"
]
},
"authenticationFlowType": "USER_SRP_AUTH",
"socialProviders": [
"GOOGLE"
],
"usernameAttributes": [],
"signupAttributes": [
"EMAIL"
],
"passwordProtectionSettings": {
"passwordPolicyMinLength": 8,
"passwordPolicyCharacters": []
},
"mfaConfiguration": "OFF",
"mfaTypes": [
"SMS"
],
"verificationMechanisms": [
"EMAIL"
]
}
},
"PinpointAnalytics": {
"Default": {
"AppId": "XXX",
"Region": "us-east-1"
}
},
"PinpointTargeting": {
"Default": {
"Region": "us-east-1"
}
}
}
}
}
}
"""
}