1 /*
2  * Copyright (C) 2016 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 android.accounts.cts;
18 
19 import android.accounts.Account;
20 import android.accounts.AccountManager;
21 import android.accounts.AccountManagerFuture;
22 import android.accounts.AuthenticatorException;
23 import android.accounts.OperationCanceledException;
24 import android.accounts.cts.common.AuthenticatorContentProvider;
25 import android.accounts.cts.common.Fixtures;
26 import android.accounts.cts.common.tx.AddAccountTx;
27 import android.accounts.cts.common.tx.UpdateCredentialsTx;
28 import android.content.ContentProviderClient;
29 import android.content.ContentResolver;
30 import android.os.Bundle;
31 import android.os.RemoteException;
32 import android.platform.test.annotations.AppModeFull;
33 import android.test.AndroidTestCase;
34 
35 import java.io.IOException;
36 
37 /**
38  * Tests for AccountManager and AbstractAccountAuthenticator. This is to test
39  * default implementation of account session api in
40  * {@link android.accounts.AbstractAccountAuthenticator}.
41  * <p>
42  * You can run those unit tests with the following command line:
43  * <p>
44  *  adb shell am instrument
45  *   -e debug false -w
46  *   -e class android.accounts.cts.AbstractAuthenticatorTests
47  * android.accounts.cts/androidx.test.runner.AndroidJUnitRunner
48  */
49 public class AbstractAuthenticatorTests extends AndroidTestCase {
50 
51     private AccountManager mAccountManager;
52     private ContentProviderClient mProviderClient;
53 
54     @Override
setUp()55     public void setUp() throws Exception {
56         // bind to the diagnostic service and set it up.
57         mAccountManager = AccountManager.get(getContext());
58         ContentResolver resolver = getContext().getContentResolver();
59         mProviderClient = resolver.acquireContentProviderClient(
60                 AuthenticatorContentProvider.AUTHORITY);
61     }
62 
tearDown()63     public void tearDown() throws RemoteException {
64         if (mProviderClient != null) {
65             // mProviderClient is null in case of instant test
66             mProviderClient.release();
67         }
68     }
69 
70     /**
71      * Tests startAddAccountSession default implementation. An encrypted session
72      * bundle should always be returned without password or status token.
73      */
testStartAddAccountSessionDefaultImpl()74     public void testStartAddAccountSessionDefaultImpl()
75             throws OperationCanceledException, AuthenticatorException, IOException {
76         Bundle options = new Bundle();
77         String accountName = Fixtures.PREFIX_NAME_SUCCESS + "@" + Fixtures.SUFFIX_NAME_FIXTURE;
78         options.putString(Fixtures.KEY_ACCOUNT_NAME, accountName);
79 
80         AccountManagerFuture<Bundle> future = mAccountManager.startAddAccountSession(
81                 Fixtures.TYPE_DEFAULT,
82                 null /* authTokenType */,
83                 null /* requiredFeatures */,
84                 options,
85                 null /* activity */,
86                 null /* callback */,
87                 null /* handler */);
88 
89         Bundle result = future.getResult();
90 
91         // Validate that auth token was stripped from result.
92         assertNull(result.get(AccountManager.KEY_AUTHTOKEN));
93 
94         // Validate that no password nor status token is returned in the result
95         // for default implementation.
96         validateNullPasswordAndStatusToken(result);
97 
98         Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
99         // Validate session bundle is returned but data in the bundle is
100         // encrypted and hence not visible.
101         assertNotNull(sessionBundle);
102         assertNull(sessionBundle.getString(AccountManager.KEY_ACCOUNT_TYPE));
103     }
104 
105 
106     /**
107      * Tests startUpdateCredentialsSession default implementation. An encrypted session
108      * bundle should always be returned without password or status token.
109      */
testStartUpdateCredentialsSessionDefaultImpl()110     public void testStartUpdateCredentialsSessionDefaultImpl()
111             throws OperationCanceledException, AuthenticatorException, IOException {
112         Bundle options = new Bundle();
113         String accountName = Fixtures.PREFIX_NAME_SUCCESS + "@" + Fixtures.SUFFIX_NAME_FIXTURE;
114         options.putString(Fixtures.KEY_ACCOUNT_NAME, accountName);
115 
116         AccountManagerFuture<Bundle> future = mAccountManager.startUpdateCredentialsSession(
117                 Fixtures.ACCOUNT_DEFAULT,
118                 null /* authTokenType */,
119                 options,
120                 null /* activity */,
121                 null /* callback */,
122                 null /* handler */);
123 
124         Bundle result = future.getResult();
125         assertTrue(future.isDone());
126         assertNotNull(result);
127 
128         // Validate no auth token in result.
129         assertNull(result.get(AccountManager.KEY_AUTHTOKEN));
130 
131         // Validate that no password nor status token is returned in the result
132         // for default implementation.
133         validateNullPasswordAndStatusToken(result);
134 
135         Bundle sessionBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
136         // Validate session bundle is returned but data in the bundle is
137         // encrypted and hence not visible.
138         assertNotNull(sessionBundle);
139         assertNull(sessionBundle.getString(Fixtures.KEY_ACCOUNT_NAME));
140     }
141 
142     /**
143      * Tests finishSession default implementation with default startAddAccountSession.
144      * Only account name and account type should be returned as a bundle.
145      */
146     @AppModeFull
testFinishSessionAndStartAddAccountSessionDefaultImpl()147     public void testFinishSessionAndStartAddAccountSessionDefaultImpl()
148             throws OperationCanceledException, AuthenticatorException, IOException,
149             RemoteException {
150         Bundle options = new Bundle();
151         String accountName = Fixtures.PREFIX_NAME_SUCCESS + "@" + Fixtures.SUFFIX_NAME_FIXTURE;
152         options.putString(Fixtures.KEY_ACCOUNT_NAME, accountName);
153 
154         // First obtain an encrypted session bundle from startAddAccountSession(...) default
155         // implementation.
156         AccountManagerFuture<Bundle> future = mAccountManager.startAddAccountSession(
157                 Fixtures.TYPE_DEFAULT,
158                 null /* authTokenType */,
159                 null /* requiredFeatures */,
160                 options,
161                 null /* activity */,
162                 null /* callback */,
163                 null /* handler */);
164 
165         Bundle result = future.getResult();
166         assertTrue(future.isDone());
167         assertNotNull(result);
168 
169         // Assert that result contains a non-null session bundle.
170         Bundle escrowBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
171         assertNotNull(escrowBundle);
172 
173         // Now call finishSession(...) with the session bundle we just obtained.
174         future = mAccountManager.finishSession(
175                 escrowBundle,
176                 null /* activity */,
177                 null /* callback */,
178                 null /* handler */);
179 
180         result = future.getResult();
181         assertTrue(future.isDone());
182         assertNotNull(result);
183 
184         // Validate that parameters are passed to addAccount(...) correctly in default finishSession
185         // implementation.
186         Bundle providerBundle = mProviderClient.call(
187                 AuthenticatorContentProvider.METHOD_GET,
188                 null /* arg */,
189                 null /* extras */);
190         providerBundle.setClassLoader(AddAccountTx.class.getClassLoader());
191         AddAccountTx addAccountTx = providerBundle
192                 .getParcelable(AuthenticatorContentProvider.KEY_TX);
193         assertNotNull(addAccountTx);
194 
195         // Assert parameters has been passed to addAccount(...) correctly
196         assertEquals(Fixtures.TYPE_DEFAULT, addAccountTx.accountType);
197         assertNull(addAccountTx.authTokenType);
198 
199         validateSystemOptions(addAccountTx.options);
200         // Validate options
201         assertNotNull(addAccountTx.options);
202         assertEquals(accountName, addAccountTx.options.getString(Fixtures.KEY_ACCOUNT_NAME));
203         // Validate features.
204         assertEquals(0, addAccountTx.requiredFeatures.size());
205 
206         // Assert returned result contains correct account name, account type and null auth token.
207         assertEquals(accountName, result.get(AccountManager.KEY_ACCOUNT_NAME));
208         assertEquals(Fixtures.TYPE_DEFAULT, result.get(AccountManager.KEY_ACCOUNT_TYPE));
209         assertNull(result.get(AccountManager.KEY_AUTHTOKEN));
210     }
211 
212     /**
213      * Tests finishSession default implementation with default startUpdateCredentialsSession.
214      * Only account name and account type should be returned as a bundle.
215      */
216     @AppModeFull
testFinishSessionAndStartUpdateCredentialsSessionDefaultImpl()217     public void testFinishSessionAndStartUpdateCredentialsSessionDefaultImpl()
218             throws OperationCanceledException, AuthenticatorException, IOException,
219             RemoteException {
220         Bundle options = new Bundle();
221         String accountName = Fixtures.PREFIX_NAME_SUCCESS + "@" + Fixtures.SUFFIX_NAME_FIXTURE;
222         options.putString(Fixtures.KEY_ACCOUNT_NAME, accountName);
223 
224         // First obtain an encrypted session bundle from startUpdateCredentialsSession(...) default
225         // implementation.
226         AccountManagerFuture<Bundle> future = mAccountManager.startUpdateCredentialsSession(
227                 Fixtures.ACCOUNT_DEFAULT,
228                 null /* authTokenTYpe */,
229                 options,
230                 null /* activity */,
231                 null /* callback */,
232                 null /* handler */);
233 
234         Bundle result = future.getResult();
235         assertTrue(future.isDone());
236         assertNotNull(result);
237 
238         // Assert that result contains a non-null session bundle.
239         Bundle escrowBundle = result.getBundle(AccountManager.KEY_ACCOUNT_SESSION_BUNDLE);
240         assertNotNull(escrowBundle);
241 
242         // Now call finishSession(...) with the session bundle we just obtained.
243         future = mAccountManager.finishSession(
244                 escrowBundle,
245                 null /* activity */,
246                 null /* callback */,
247                 null /* handler */);
248 
249         result = future.getResult();
250         assertTrue(future.isDone());
251         assertNotNull(result);
252 
253         // Validate that parameters are passed to updateCredentials(...) correctly in default
254         // finishSession implementation.
255         Bundle providerBundle = mProviderClient.call(
256                 AuthenticatorContentProvider.METHOD_GET,
257                 null /* arg */,
258                 null /* extras */);
259         providerBundle.setClassLoader(UpdateCredentialsTx.class.getClassLoader());
260         UpdateCredentialsTx updateCredentialsTx = providerBundle
261                 .getParcelable(AuthenticatorContentProvider.KEY_TX);
262         assertNotNull(updateCredentialsTx);
263 
264         // Assert parameters has been passed to updateCredentials(...) correctly
265         assertEquals(Fixtures.ACCOUNT_DEFAULT, updateCredentialsTx.account);
266         assertNull(updateCredentialsTx.authTokenType);
267 
268         validateSystemOptions(updateCredentialsTx.options);
269         // Validate options
270         assertNotNull(updateCredentialsTx.options);
271         assertEquals(accountName, updateCredentialsTx.options.getString(Fixtures.KEY_ACCOUNT_NAME));
272 
273         // Assert returned result contains correct account name, account type and null auth token.
274         assertEquals(accountName, result.get(AccountManager.KEY_ACCOUNT_NAME));
275         assertEquals(Fixtures.TYPE_DEFAULT, result.get(AccountManager.KEY_ACCOUNT_TYPE));
276         assertNull(result.get(AccountManager.KEY_AUTHTOKEN));
277     }
278 
validateSystemOptions(Bundle options)279     private void validateSystemOptions(Bundle options) {
280         assertNotNull(options.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME));
281         assertTrue(options.containsKey(AccountManager.KEY_CALLER_UID));
282         assertTrue(options.containsKey(AccountManager.KEY_CALLER_PID));
283     }
284 
validateNullPasswordAndStatusToken(Bundle result)285     private void validateNullPasswordAndStatusToken(Bundle result) {
286         assertNull(result.getString(AccountManager.KEY_PASSWORD));
287         assertNull(result.getString(AccountManager.KEY_ACCOUNT_STATUS_TOKEN));
288     }
289 
290     /**
291      * Tests isCredentialsUpdateSuggested default implementation.
292      * A bundle with boolean false should be returned.
293      */
testIsCredentialsUpdateSuggestedDefaultImpl()294     public void testIsCredentialsUpdateSuggestedDefaultImpl()
295             throws OperationCanceledException, AuthenticatorException, IOException,
296             RemoteException {
297         String accountName = Fixtures.PREFIX_NAME_SUCCESS + "@" + Fixtures.SUFFIX_NAME_FIXTURE;
298         Account account = new Account(accountName, Fixtures.TYPE_DEFAULT);
299         String statusToken = Fixtures.PREFIX_STATUS_TOKEN + accountName;
300 
301         AccountManagerFuture<Boolean> future = mAccountManager.isCredentialsUpdateSuggested(
302                 account,
303                 statusToken,
304                 null /* callback */,
305                 null /* handler */);
306 
307         assertFalse(future.getResult());
308         assertTrue(future.isDone());
309     }
310 }
311