Home | History | Annotate | Download | only in internal
      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