1 /* 2 * Copyright (C) 2017 The Android Open Source Project 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://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.keychain.internal; 18 19 import static org.mockito.Mockito.mock; 20 import static org.mockito.Mockito.when; 21 22 import android.content.ContentValues; 23 import android.content.Context; 24 import android.content.pm.PackageManager; 25 import android.database.sqlite.SQLiteDatabase; 26 import android.database.sqlite.SQLiteOpenHelper; 27 import com.android.keychain.TestConfig; 28 import org.junit.Assert; 29 import org.junit.Before; 30 import org.junit.Test; 31 import org.junit.runner.RunWith; 32 import org.robolectric.RobolectricTestRunner; 33 import org.robolectric.RuntimeEnvironment; 34 import org.robolectric.annotation.Config; 35 36 /** Unit tests for {@link com.android.keychain.internal.GrantsDatabase}. */ 37 @RunWith(RobolectricTestRunner.class) 38 @Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION) 39 public final class GrantsDatabaseTest { 40 private static final String DUMMY_ALIAS = "dummy_alias"; 41 private static final String DUMMY_ALIAS2 = "another_dummy_alias"; 42 private static final int DUMMY_UID = 1000; 43 private static final int DUMMY_UID2 = 1001; 44 // Constants duplicated from GrantsDatabase to make sure the upgrade tests catch if the 45 // name of one of the fields in the DB changes. 46 static final String DATABASE_NAME = "grants.db"; 47 static final String TABLE_GRANTS = "grants"; 48 static final String GRANTS_ALIAS = "alias"; 49 static final String GRANTS_GRANTEE_UID = "uid"; 50 static final String TABLE_SELECTABLE = "userselectable"; 51 static final String SELECTABLE_IS_SELECTABLE = "is_selectable"; 52 53 private GrantsDatabase mGrantsDB; 54 55 @Before 56 public void setUp() { 57 mGrantsDB = new GrantsDatabase(RuntimeEnvironment.application); 58 } 59 60 @Test 61 public void testSetGrant_notMixingUIDs() { 62 mGrantsDB.setGrant(DUMMY_UID, DUMMY_ALIAS, true); 63 Assert.assertFalse(mGrantsDB.hasGrant(DUMMY_UID2, DUMMY_ALIAS)); 64 } 65 66 @Test 67 public void testSetGrant_notMixingAliases() { 68 mGrantsDB.setGrant(DUMMY_UID, DUMMY_ALIAS, true); 69 Assert.assertFalse(mGrantsDB.hasGrant(DUMMY_UID, DUMMY_ALIAS2)); 70 } 71 72 @Test 73 public void testSetGrantTrue() { 74 Assert.assertFalse(mGrantsDB.hasGrant(DUMMY_UID, DUMMY_ALIAS)); 75 mGrantsDB.setGrant(DUMMY_UID, DUMMY_ALIAS, true); 76 Assert.assertTrue(mGrantsDB.hasGrant(DUMMY_UID, DUMMY_ALIAS)); 77 } 78 79 @Test 80 public void testSetGrantFalse() { 81 mGrantsDB.setGrant(DUMMY_UID, DUMMY_ALIAS, false); 82 Assert.assertFalse(mGrantsDB.hasGrant(DUMMY_UID, DUMMY_ALIAS)); 83 } 84 85 @Test 86 public void testSetGrantTrueThenFalse() { 87 mGrantsDB.setGrant(DUMMY_UID, DUMMY_ALIAS, true); 88 Assert.assertTrue(mGrantsDB.hasGrant(DUMMY_UID, DUMMY_ALIAS)); 89 mGrantsDB.setGrant(DUMMY_UID, DUMMY_ALIAS, false); 90 Assert.assertFalse(mGrantsDB.hasGrant(DUMMY_UID, DUMMY_ALIAS)); 91 } 92 93 @Test 94 public void testRemoveAliasInformation() { 95 mGrantsDB.setGrant(DUMMY_UID, DUMMY_ALIAS, true); 96 mGrantsDB.setGrant(DUMMY_UID2, DUMMY_ALIAS, true); 97 mGrantsDB.setIsUserSelectable(DUMMY_ALIAS, true); 98 Assert.assertTrue(mGrantsDB.hasGrant(DUMMY_UID, DUMMY_ALIAS)); 99 mGrantsDB.removeAliasInformation(DUMMY_ALIAS); 100 Assert.assertFalse(mGrantsDB.hasGrant(DUMMY_UID, DUMMY_ALIAS)); 101 Assert.assertFalse(mGrantsDB.hasGrant(DUMMY_UID2, DUMMY_ALIAS)); 102 Assert.assertFalse(mGrantsDB.isUserSelectable(DUMMY_ALIAS)); 103 } 104 105 @Test 106 public void testRemoveAllAliasesInformation() { 107 mGrantsDB.setGrant(DUMMY_UID, DUMMY_ALIAS, true); 108 mGrantsDB.setGrant(DUMMY_UID2, DUMMY_ALIAS, true); 109 mGrantsDB.setGrant(DUMMY_UID, DUMMY_ALIAS2, true); 110 mGrantsDB.setIsUserSelectable(DUMMY_ALIAS, true); 111 mGrantsDB.removeAllAliasesInformation(); 112 Assert.assertFalse(mGrantsDB.hasGrant(DUMMY_UID, DUMMY_ALIAS)); 113 Assert.assertFalse(mGrantsDB.hasGrant(DUMMY_UID2, DUMMY_ALIAS)); 114 Assert.assertFalse(mGrantsDB.hasGrant(DUMMY_UID, DUMMY_ALIAS2)); 115 Assert.assertFalse(mGrantsDB.isUserSelectable(DUMMY_ALIAS)); 116 } 117 118 @Test 119 public void testPurgeOldGrantsDoesNotDeleteGrantsForExistingPackages() { 120 mGrantsDB.setGrant(DUMMY_UID, DUMMY_ALIAS, true); 121 PackageManager pm = mock(PackageManager.class); 122 when(pm.getPackagesForUid(DUMMY_UID)).thenReturn(new String[] {"p"}); 123 mGrantsDB.purgeOldGrants(pm); 124 Assert.assertTrue(mGrantsDB.hasGrant(DUMMY_UID, DUMMY_ALIAS)); 125 } 126 127 @Test 128 public void testPurgeOldGrantsPurgesAllNonExistingPackages() { 129 mGrantsDB.setGrant(DUMMY_UID, DUMMY_ALIAS, true); 130 mGrantsDB.setGrant(DUMMY_UID2, DUMMY_ALIAS, true); 131 PackageManager pm = mock(PackageManager.class); 132 when(pm.getPackagesForUid(DUMMY_UID)).thenReturn(null); 133 when(pm.getPackagesForUid(DUMMY_UID2)).thenReturn(null); 134 mGrantsDB.purgeOldGrants(pm); 135 Assert.assertFalse(mGrantsDB.hasGrant(DUMMY_UID, DUMMY_ALIAS)); 136 Assert.assertFalse(mGrantsDB.hasGrant(DUMMY_UID2, DUMMY_ALIAS)); 137 } 138 139 @Test 140 public void testPurgeOldGrantsWorksOnEmptyDatabase() { 141 // Check that NPE is not thrown. 142 mGrantsDB.purgeOldGrants(null); 143 } 144 145 @Test 146 public void testIsUserSelectable() { 147 Assert.assertFalse(mGrantsDB.isUserSelectable(DUMMY_ALIAS)); 148 mGrantsDB.setIsUserSelectable(DUMMY_ALIAS, true); 149 Assert.assertTrue(mGrantsDB.isUserSelectable(DUMMY_ALIAS)); 150 } 151 152 @Test 153 public void testSetUserSelectable() { 154 mGrantsDB.setIsUserSelectable(DUMMY_ALIAS, true); 155 Assert.assertTrue(mGrantsDB.isUserSelectable(DUMMY_ALIAS)); 156 mGrantsDB.setIsUserSelectable(DUMMY_ALIAS, false); 157 Assert.assertFalse(mGrantsDB.isUserSelectable(DUMMY_ALIAS)); 158 mGrantsDB.setIsUserSelectable(DUMMY_ALIAS, true); 159 Assert.assertTrue(mGrantsDB.isUserSelectable(DUMMY_ALIAS)); 160 } 161 162 private abstract class BaseGrantsDatabaseHelper extends SQLiteOpenHelper { 163 private final boolean mCreateUserSelectableTable; 164 165 public BaseGrantsDatabaseHelper( 166 Context context, int dbVersion, boolean createUserSelectableTable) { 167 super(context, DATABASE_NAME, null /* CursorFactory */, dbVersion); 168 mCreateUserSelectableTable = createUserSelectableTable; 169 } 170 171 void createUserSelectableTable(final SQLiteDatabase db) { 172 db.execSQL( 173 "CREATE TABLE " 174 + TABLE_SELECTABLE 175 + " ( " 176 + GRANTS_ALIAS 177 + " STRING NOT NULL, " 178 + SELECTABLE_IS_SELECTABLE 179 + " STRING NOT NULL, " 180 + "UNIQUE (" 181 + GRANTS_ALIAS 182 + "))"); 183 } 184 185 @Override 186 public void onCreate(final SQLiteDatabase db) { 187 db.execSQL( 188 "CREATE TABLE " 189 + TABLE_GRANTS 190 + " ( " 191 + GRANTS_ALIAS 192 + " STRING NOT NULL, " 193 + GRANTS_GRANTEE_UID 194 + " INTEGER NOT NULL, " 195 + "UNIQUE (" 196 + GRANTS_ALIAS 197 + "," 198 + GRANTS_GRANTEE_UID 199 + "))"); 200 201 if (mCreateUserSelectableTable) { 202 createUserSelectableTable(db); 203 } 204 } 205 206 @Override 207 public void onUpgrade(final SQLiteDatabase db, int oldVersion, final int newVersion) { 208 throw new IllegalStateException("Existing DB must be dropped first."); 209 } 210 211 public void insertIntoGrantsTable(final SQLiteDatabase db, String alias, int uid) { 212 final ContentValues values = new ContentValues(); 213 values.put(GRANTS_ALIAS, alias); 214 values.put(GRANTS_GRANTEE_UID, uid); 215 db.insert(TABLE_GRANTS, GRANTS_ALIAS, values); 216 } 217 218 public void insertIntoSelectableTable( 219 final SQLiteDatabase db, String alias, boolean isSelectable) { 220 final ContentValues values = new ContentValues(); 221 values.put(GRANTS_ALIAS, alias); 222 values.put(SELECTABLE_IS_SELECTABLE, Boolean.toString(isSelectable)); 223 db.insert(TABLE_SELECTABLE, null, values); 224 } 225 } 226 227 private class V1DatabaseHelper extends BaseGrantsDatabaseHelper { 228 public V1DatabaseHelper(Context context) { 229 super(context, 1, false); 230 } 231 } 232 233 private class V2DatabaseHelper extends BaseGrantsDatabaseHelper { 234 public V2DatabaseHelper(Context context) { 235 super(context, 2, true); 236 } 237 } 238 239 private class IncorrectlyVersionedV2DatabaseHelper extends BaseGrantsDatabaseHelper { 240 public IncorrectlyVersionedV2DatabaseHelper(Context context) { 241 super(context, 1, true); 242 } 243 } 244 245 @Test 246 public void testUpgradeDatabase() { 247 // Close old DB 248 mGrantsDB.destroy(); 249 // Create a new, V1 database. 250 Context context = RuntimeEnvironment.application; 251 context.deleteDatabase(DATABASE_NAME); 252 V1DatabaseHelper v1DBHelper = new V1DatabaseHelper(context); 253 // Fill it up with a few records 254 final SQLiteDatabase db = v1DBHelper.getWritableDatabase(); 255 String[] aliases = {"alias-1", "alias-2", "alias-3"}; 256 for (String alias : aliases) { 257 v1DBHelper.insertIntoGrantsTable(db, alias, 123456); 258 } 259 260 // Test that the aliases were made user-selectable during the upgrade. 261 mGrantsDB = new GrantsDatabase(RuntimeEnvironment.application); 262 for (String alias : aliases) { 263 Assert.assertTrue(mGrantsDB.isUserSelectable(alias)); 264 } 265 } 266 267 @Test 268 public void testSelectabilityInV2DatabaseNotChanged() { 269 // Close old DB 270 mGrantsDB.destroy(); 271 Context context = RuntimeEnvironment.application; 272 context.deleteDatabase(DATABASE_NAME); 273 // Create a new, V2 database. 274 V2DatabaseHelper v2DBHelper = new V2DatabaseHelper(context); 275 // Fill it up with a few records 276 final SQLiteDatabase db = v2DBHelper.getWritableDatabase(); 277 String[] aliases = {"alias-1", "alias-2", "alias-3"}; 278 for (String alias : aliases) { 279 v2DBHelper.insertIntoGrantsTable(db, alias, 123456); 280 v2DBHelper.insertIntoSelectableTable(db, alias, false); 281 } 282 String selectableAlias = "alias-selectable-1"; 283 v2DBHelper.insertIntoGrantsTable(db, selectableAlias, 123457); 284 v2DBHelper.insertIntoSelectableTable(db, selectableAlias, true); 285 286 // Test that the aliases were made user-selectable during the upgrade. 287 mGrantsDB = new GrantsDatabase(RuntimeEnvironment.application); 288 for (String alias : aliases) { 289 Assert.assertFalse(mGrantsDB.isUserSelectable(alias)); 290 } 291 Assert.assertTrue(mGrantsDB.isUserSelectable(selectableAlias)); 292 } 293 294 @Test 295 public void testV1AndAHalfDBUpgradedCorrectly() { 296 // Close old DB 297 mGrantsDB.destroy(); 298 Context context = RuntimeEnvironment.application; 299 context.deleteDatabase(DATABASE_NAME); 300 // Create a new, V2 database that's incorrectly versioned as v1. 301 IncorrectlyVersionedV2DatabaseHelper dbHelper = 302 new IncorrectlyVersionedV2DatabaseHelper(context); 303 // Fill it up with a few records 304 final SQLiteDatabase db = dbHelper.getWritableDatabase(); 305 String[] aliases = {"alias-1", "alias-2", "alias-3"}; 306 for (String alias : aliases) { 307 dbHelper.insertIntoGrantsTable(db, alias, 123456); 308 dbHelper.insertIntoSelectableTable(db, alias, false); 309 } 310 311 // Insert one alias explicitly selectable 312 String selectableAlias = "alias-selectable-1"; 313 dbHelper.insertIntoGrantsTable(db, selectableAlias, 123456); 314 dbHelper.insertIntoSelectableTable(db, selectableAlias, true); 315 316 // Insert one alias without explicitl user-selectability, which should 317 // default to true when upgrading from V1 to V2. 318 String defaultSelectableAlias = "alias-selectable-2"; 319 dbHelper.insertIntoGrantsTable(db, defaultSelectableAlias, 123456); 320 321 // Test that the aliases were made user-selectable during the upgrade. 322 mGrantsDB = new GrantsDatabase(RuntimeEnvironment.application); 323 for (String alias : aliases) { 324 Assert.assertFalse(mGrantsDB.isUserSelectable(alias)); 325 } 326 Assert.assertTrue(mGrantsDB.isUserSelectable(selectableAlias)); 327 Assert.assertTrue(mGrantsDB.isUserSelectable(defaultSelectableAlias)); 328 } 329 } 330