Skip to content

Commit 8ce8836

Browse files
author
Karthikeyan
authored
Fix a bug where migrating expirationDate in CognitoCachingCredentialsProvider crashes (#781)
* Secure information stored in SharedPreferences * Lower aws-android-sdk-core-test compile and target sdk version to 27 * Add a symlink to android-23.jar for core * Add a gradle task that creates a symlink to android-23.jar for AWS Core * Fix the gradle task that creates symbolic link to android-23.jar * Change config.yml to setup android-23 * Enable core, cognitoidentityprovider and cognitoauth integration tests on CircleCI * Enable core, cognitoidentityprovider and cognitoauth integration tests on CircleCI * Fix pom.xml * Improve exception handling in AWSKeyValueStore * [2.12.3] Bump the patch version of 2.12.z * Update 2.12.3 CHANGELOG * Add the missing bucket prefixes to CleanupBucketIntegrationTests * Fix a bug where migrating expirationDate in CognitoCachingCredentialsProvider crashes
1 parent 7688e51 commit 8ce8836

File tree

4 files changed

+228
-8
lines changed

4 files changed

+228
-8
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ __pycache__/
7676
# Credentials
7777
**/testconfiguration.json
7878
**/awsconfiguration.json
79+
aws-android-sdk-testutils/src/main/res/raw/testconfiguration.json
7980

8081
# AWSCoreRuntime libs
8182
aws-android-sdk-core/libs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
/*
2+
* Copyright 2019-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package com.amazonaws.internal.keyvaluestore;
17+
18+
import org.junit.After;
19+
import org.junit.AfterClass;
20+
import org.junit.Before;
21+
import org.junit.BeforeClass;
22+
import org.junit.Test;
23+
import org.junit.runner.RunWith;
24+
25+
import static org.junit.Assert.assertEquals;
26+
import static org.junit.Assert.assertNotNull;
27+
import static org.junit.Assert.assertNull;
28+
29+
import android.content.Context;
30+
import android.content.SharedPreferences;
31+
import android.support.test.InstrumentationRegistry;
32+
import android.support.test.runner.AndroidJUnit4;
33+
import android.util.Log;
34+
35+
import com.amazonaws.auth.CognitoCachingCredentialsProvider;
36+
import com.amazonaws.regions.Regions;
37+
38+
import java.util.ArrayList;
39+
40+
41+
@RunWith(AndroidJUnit4.class)
42+
public class AWSKeyValueStoreMigrationIntegrationTest extends CoreIntegrationTestBase {
43+
44+
private static String TAG = CognitoCachingCredentialsProviderIntegrationTest.class.getSimpleName();
45+
46+
private ArrayList<CognitoCachingCredentialsProvider> credentialsProviders =new ArrayList<CognitoCachingCredentialsProvider>();
47+
private CognitoCachingCredentialsProvider credentialsProvider;
48+
49+
private static SharedPreferences sharedPreferencesForAuth;
50+
private String identityPoolId;
51+
private long time;
52+
53+
@BeforeClass
54+
public static void setupBeforeClass() {
55+
sharedPreferencesForAuth = InstrumentationRegistry.getTargetContext()
56+
.getSharedPreferences("com.amazonaws.android.auth", Context.MODE_PRIVATE);
57+
}
58+
59+
@AfterClass
60+
public static void tearDownAfterClass() {
61+
sharedPreferencesForAuth
62+
.edit()
63+
.clear()
64+
.commit();
65+
}
66+
67+
@Before
68+
public void setUp() throws Exception {
69+
time = System.currentTimeMillis();
70+
identityPoolId = getPackageConfigure().getString("identity_pool_id");
71+
sharedPreferencesForAuth.edit()
72+
.putString(identityPoolId + ".accessKey" , "accessKey")
73+
.putString(identityPoolId + ".secretKey" , "secretKey")
74+
.putString(identityPoolId + ".sessionToken" , "sessionToken")
75+
.putString(identityPoolId + ".identityId" , "identityId")
76+
.putLong(identityPoolId + ".expirationDate" , time)
77+
.commit();
78+
}
79+
80+
@After
81+
public void tearDown() {
82+
sharedPreferencesForAuth
83+
.edit()
84+
.clear()
85+
.commit();
86+
87+
AWSKeyValueStore.cacheFactory.clear();
88+
}
89+
90+
@Test
91+
public void testCachedAWSCredentialsMigration() throws Exception {
92+
Log.d(TAG, "SharedPreferences contents before migration for com.amazonaws.android.auth => " +
93+
sharedPreferencesForAuth.getAll().toString());
94+
95+
assertEquals("accessKey", sharedPreferencesForAuth.getString(identityPoolId + ".accessKey", null));
96+
assertEquals("secretKey", sharedPreferencesForAuth.getString(identityPoolId + ".secretKey", null));
97+
assertEquals("sessionToken", sharedPreferencesForAuth.getString(identityPoolId + ".sessionToken", null));
98+
assertEquals("identityId", sharedPreferencesForAuth.getString(identityPoolId + ".identityId", null));
99+
assertEquals(time, sharedPreferencesForAuth.getLong(identityPoolId + ".expirationDate", 0));
100+
101+
credentialsProvider = new CognitoCachingCredentialsProvider(
102+
InstrumentationRegistry.getTargetContext(),
103+
getPackageConfigure().getString("identity_pool_id"),
104+
Regions.US_EAST_1);
105+
credentialsProviders.add(credentialsProvider);
106+
107+
AWSKeyValueStore awsKeyValueStore = new AWSKeyValueStore(InstrumentationRegistry.getTargetContext(),
108+
"com.amazonaws.android.auth",
109+
true);
110+
111+
Log.d(TAG, "SharedPreferences contents after migration for com.amazonaws.android.auth => " +
112+
sharedPreferencesForAuth.getAll().toString());
113+
114+
assertEquals("accessKey", awsKeyValueStore.get(identityPoolId + ".accessKey"));
115+
assertEquals("secretKey", awsKeyValueStore.get(identityPoolId + ".secretKey"));
116+
assertEquals("sessionToken", awsKeyValueStore.get(identityPoolId + ".sessionToken"));
117+
assertEquals("identityId", awsKeyValueStore.get(identityPoolId + ".identityId"));
118+
assertEquals(String.valueOf(time), awsKeyValueStore.get(identityPoolId + ".expirationDate"));
119+
120+
credentialsProvider.clearCredentials();
121+
credentialsProvider.clear();
122+
assertNull(credentialsProvider.getCachedIdentityId());
123+
124+
assertNotNull(credentialsProvider.getCredentials());
125+
assertNotNull(credentialsProvider.getIdentityId());
126+
assertNotNull(credentialsProvider.getCachedIdentityId());
127+
128+
assertEquals(credentialsProvider.getIdentityId(), credentialsProvider.getCachedIdentityId());
129+
130+
verifySharedPreferencesContents();
131+
verifyCredentialsProviderClear();
132+
}
133+
134+
private void verifySharedPreferencesContents() {
135+
assert sharedPreferencesForAuth.getAll().keySet().size() == credentialsProviders.size() * 5;
136+
137+
Log.d(TAG, "SharedPreferences Keys = " +
138+
sharedPreferencesForAuth.getAll().keySet().toString());
139+
140+
for (int iterator = 0; iterator < credentialsProviders.size(); iterator++) {
141+
final CognitoCachingCredentialsProvider cccp = credentialsProviders.get(iterator);
142+
assertNotNull(sharedPreferencesForAuth.getString(cccp.getIdentityPoolId() + ".accessKey.encrypted" , null));
143+
assertNotNull(sharedPreferencesForAuth.getString(cccp.getIdentityPoolId() + ".secretKey.encrypted", null));
144+
assertNotNull(sharedPreferencesForAuth.getString(cccp.getIdentityPoolId() + ".sessionToken.encrypted", null));
145+
assertNotNull(sharedPreferencesForAuth.getString(cccp.getIdentityPoolId() + ".identityId.encrypted", null));
146+
assertNotNull(sharedPreferencesForAuth.getString(cccp.getIdentityPoolId() + ".expirationDate.encrypted", null));
147+
148+
assertNotNull(sharedPreferencesForAuth.getString(cccp.getIdentityPoolId() + ".accessKey.encrypted.iv" , null));
149+
assertNotNull(sharedPreferencesForAuth.getString(cccp.getIdentityPoolId() + ".secretKey.encrypted.iv", null));
150+
assertNotNull(sharedPreferencesForAuth.getString(cccp.getIdentityPoolId() + ".sessionToken.encrypted.iv", null));
151+
assertNotNull(sharedPreferencesForAuth.getString(cccp.getIdentityPoolId() + ".identityId.encrypted.iv", null));
152+
assertNotNull(sharedPreferencesForAuth.getString(cccp.getIdentityPoolId() + ".expirationDate.encrypted.iv", null));
153+
154+
assertNotNull(sharedPreferencesForAuth.getString(cccp.getIdentityPoolId() + ".accessKey.encrypted.keyvaluestoreversion" , null));
155+
assertNotNull(sharedPreferencesForAuth.getString(cccp.getIdentityPoolId() + ".secretKey.encrypted.keyvaluestoreversion", null));
156+
assertNotNull(sharedPreferencesForAuth.getString(cccp.getIdentityPoolId() + ".sessionToken.encrypted.keyvaluestoreversion", null));
157+
assertNotNull(sharedPreferencesForAuth.getString(cccp.getIdentityPoolId() + ".identityId.encrypted.keyvaluestoreversion", null));
158+
assertNotNull(sharedPreferencesForAuth.getString(cccp.getIdentityPoolId() + ".expirationDate.encrypted.keyvaluestoreversion", null));
159+
160+
assertNull(sharedPreferencesForAuth.getString(cccp.getIdentityPoolId() + ".accessKey", null));
161+
assertNull(sharedPreferencesForAuth.getString(cccp.getIdentityPoolId() + ".secretKey", null));
162+
assertNull(sharedPreferencesForAuth.getString(cccp.getIdentityPoolId() + ".sessionToken", null));
163+
assertNull(sharedPreferencesForAuth.getString(cccp.getIdentityPoolId() + ".identityId", null));
164+
assertNull(sharedPreferencesForAuth.getString(cccp.getIdentityPoolId() + ".expirationDate", null));
165+
}
166+
}
167+
168+
private void verifyCredentialsProviderClear() {
169+
for (int iterator = 0; iterator < credentialsProviders.size(); iterator++){
170+
final CognitoCachingCredentialsProvider cccp = credentialsProviders.get(iterator);
171+
172+
cccp.clearCredentials();
173+
cccp.clear();
174+
175+
assertNull(cccp.getCachedIdentityId());
176+
assertNotNull(cccp.getIdentityId());
177+
assertNotNull(cccp.getCachedIdentityId());
178+
}
179+
}
180+
}

aws-android-sdk-core/src/main/java/com/amazonaws/internal/keyvaluestore/AWSKeyValueStore.java

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
/**
2+
* Copyright 2019-2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at:
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES
11+
* OR CONDITIONS OF ANY KIND, either express or implied. See the
12+
* License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
116
package com.amazonaws.internal.keyvaluestore;
217

318
import android.content.Context;
@@ -258,14 +273,34 @@ public void clear() {
258273
}
259274

260275
/**
261-
* Migrate all the keys except for the encryption metadata
276+
* Migrate all the keys in the SharedPreferences namespace
277+
* except for the encryption metadata
262278
*/
263279
private void onMigrateFromNoEncryption() {
264-
for (final String spKey: sharedPreferences.getAll().keySet()) {
280+
Map<String, ?> map = sharedPreferences.getAll();
281+
for (String spKey : map.keySet()) {
265282
if (!spKey.endsWith(SHARED_PREFERENCES_DATA_IDENTIFIER_SUFFIX) &&
266-
!spKey.endsWith(SHARED_PREFERENCES_IV_SUFFIX) &&
267-
!spKey.endsWith(SHARED_PREFERENCES_STORE_VERSION_SUFFIX)) {
268-
put(spKey, sharedPreferences.getString(spKey, null));
283+
!spKey.endsWith(SHARED_PREFERENCES_IV_SUFFIX) &&
284+
!spKey.endsWith(SHARED_PREFERENCES_STORE_VERSION_SUFFIX)) {
285+
286+
// Check if its an instance of the dataType.
287+
if (map.get(spKey) instanceof Long) {
288+
Long longValue = sharedPreferences.getLong(spKey, 0);
289+
put(spKey, String.valueOf(longValue));
290+
} else if (map.get(spKey) instanceof String) {
291+
put(spKey, sharedPreferences.getString(spKey, null));
292+
} else if (map.get(spKey) instanceof Float) {
293+
Float floatValue = sharedPreferences.getFloat(spKey, 0);
294+
put(spKey, String.valueOf(floatValue));
295+
} else if (map.get(spKey) instanceof Boolean) {
296+
Boolean booleanValue = sharedPreferences.getBoolean(spKey, false);
297+
put(spKey, String.valueOf(booleanValue));
298+
} else if (map.get(spKey) instanceof Integer) {
299+
Integer intValue = sharedPreferences.getInt(spKey, 0);
300+
put(spKey, String.valueOf(intValue));
301+
}
302+
303+
// Remove the key since key.encrypted is written.
269304
sharedPreferences.edit().remove(spKey).apply();
270305
}
271306
}

aws-android-sdk-s3-test/src/androidTest/java/com/amazonaws/services/s3/CleanupBucketIntegrationTests.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
package com.amazonaws.services.s3;
32

43
import com.amazonaws.regions.Region;
@@ -27,7 +26,7 @@ public class CleanupBucketIntegrationTests extends AWSTestBase {
2726
public void setup() throws FileNotFoundException, IOException {
2827
setUpCredentials();
2928
s3 = new AmazonS3Client(credentials);
30-
s3.setRegion(Region.getRegion(Regions.US_WEST_2));
29+
s3.setRegion(Region.getRegion(Regions.US_WEST_1));
3130
}
3231

3332
@Test
@@ -58,7 +57,12 @@ public void testCleanup() {
5857
|| bucket.getName().startsWith("java-get-object-integ-test")
5958
|| bucket.getName().startsWith("java-multiget-object-iteration-test")
6059
|| bucket.getName().startsWith("amazon-s3-client-integ-test")
61-
|| bucket.getName().startsWith("android-sdk-mp-upload")) {
60+
|| bucket.getName().startsWith("android-sdk-mp-upload")
61+
|| bucket.getName().startsWith("java-sts-integ-test")
62+
|| bucket.getName().startsWith("s3-low-level-presigned-url")
63+
|| bucket.getName().startsWith("java-bucket-policy-integ-test")
64+
|| bucket.getName().startsWith("copy-object-integ-test")
65+
|| bucket.getName().startsWith("java-server-side-encryption-integ-test")) {
6266

6367
final String bucket_name = bucket.getName();
6468
try {

0 commit comments

Comments
 (0)