1 // Copyright 2015 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.net.test; 6 7 import android.accounts.AbstractAccountAuthenticator; 8 import android.accounts.Account; 9 import android.accounts.AccountAuthenticatorResponse; 10 import android.accounts.AccountManager; 11 import android.accounts.AuthenticatorException; 12 import android.accounts.OperationCanceledException; 13 import android.app.Activity; 14 import android.content.Context; 15 import android.os.Bundle; 16 17 import org.jni_zero.CalledByNative; 18 import org.jni_zero.JNINamespace; 19 import org.jni_zero.NativeClassQualifiedName; 20 import org.jni_zero.NativeMethods; 21 22 import org.chromium.base.ApplicationStatus; 23 import org.chromium.net.HttpNegotiateConstants; 24 25 import java.io.IOException; 26 27 /** 28 * Dummy Android authenticator, to test SPNEGO/Keberos support on Android. This is deliberately 29 * minimal, and is not intended as an example of how to write a real SPNEGO Authenticator. 30 */ 31 @JNINamespace("net::android") 32 public class DummySpnegoAuthenticator extends AbstractAccountAuthenticator { 33 private static final String ACCOUNT_TYPE = "org.chromium.test.DummySpnegoAuthenticator"; 34 private static final String ACCOUNT_NAME = "DummySpnegoAccount"; 35 private static int sResult; 36 private static String sToken; 37 private static boolean sCheckArguments; 38 private static long sNativeDummySpnegoAuthenticator; 39 private static final int GSS_S_COMPLETE = 0; 40 private static final int GSS_S_CONTINUE_NEEDED = 1; 41 private static final int GSS_S_FAILURE = 2; 42 43 /** 44 * @param context 45 */ DummySpnegoAuthenticator(Context context)46 public DummySpnegoAuthenticator(Context context) { 47 super(context); 48 } 49 50 @Override addAccount( AccountAuthenticatorResponse arg0, String accountType, String arg2, String[] arg3, Bundle arg4)51 public Bundle addAccount( 52 AccountAuthenticatorResponse arg0, 53 String accountType, 54 String arg2, 55 String[] arg3, 56 Bundle arg4) { 57 Bundle result = new Bundle(); 58 result.putInt(AccountManager.KEY_ERROR_CODE, AccountManager.ERROR_CODE_BAD_REQUEST); 59 result.putString(AccountManager.KEY_ERROR_MESSAGE, "Can't add new SPNEGO accounts"); 60 return result; 61 } 62 63 @Override confirmCredentials(AccountAuthenticatorResponse arg0, Account arg1, Bundle arg2)64 public Bundle confirmCredentials(AccountAuthenticatorResponse arg0, Account arg1, Bundle arg2) { 65 Bundle result = new Bundle(); 66 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true); 67 return result; 68 } 69 70 @Override editProperties(AccountAuthenticatorResponse arg0, String arg1)71 public Bundle editProperties(AccountAuthenticatorResponse arg0, String arg1) { 72 return new Bundle(); 73 } 74 75 @Override getAuthToken( AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options)76 public Bundle getAuthToken( 77 AccountAuthenticatorResponse response, 78 Account account, 79 String authTokenType, 80 Bundle options) { 81 long nativeQuery = 82 DummySpnegoAuthenticatorJni.get().getNextQuery(sNativeDummySpnegoAuthenticator); 83 String incomingToken = options.getString(HttpNegotiateConstants.KEY_INCOMING_AUTH_TOKEN); 84 DummySpnegoAuthenticatorJni.get().checkGetTokenArguments(nativeQuery, incomingToken); 85 Bundle result = new Bundle(); 86 result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name); 87 result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type); 88 result.putString( 89 AccountManager.KEY_AUTHTOKEN, 90 DummySpnegoAuthenticatorJni.get().getTokenToReturn(nativeQuery)); 91 result.putInt( 92 HttpNegotiateConstants.KEY_SPNEGO_RESULT, 93 decodeResult(DummySpnegoAuthenticatorJni.get().getResult(nativeQuery))); 94 return result; 95 } 96 97 /** 98 * @param DummySpnegoAuthenticatorJni.get().getResult 99 * @return 100 */ decodeResult(int gssApiResult)101 private int decodeResult(int gssApiResult) { 102 // This only handles the result values currently used in the tests. 103 switch (gssApiResult) { 104 case GSS_S_COMPLETE: 105 case GSS_S_CONTINUE_NEEDED: 106 return 0; 107 case GSS_S_FAILURE: 108 return HttpNegotiateConstants.ERR_MISSING_AUTH_CREDENTIALS; 109 default: 110 return HttpNegotiateConstants.ERR_UNEXPECTED; 111 } 112 } 113 114 @Override getAuthTokenLabel(String arg0)115 public String getAuthTokenLabel(String arg0) { 116 return "Spnego " + arg0; 117 } 118 119 @Override hasFeatures(AccountAuthenticatorResponse arg0, Account arg1, String[] features)120 public Bundle hasFeatures(AccountAuthenticatorResponse arg0, Account arg1, String[] features) { 121 Bundle result = new Bundle(); 122 for (String feature : features) { 123 if (!feature.equals(HttpNegotiateConstants.SPNEGO_FEATURE)) { 124 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false); 125 return result; 126 } 127 } 128 result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true); 129 return result; 130 } 131 132 @Override updateCredentials( AccountAuthenticatorResponse arg0, Account arg1, String arg2, Bundle arg3)133 public Bundle updateCredentials( 134 AccountAuthenticatorResponse arg0, Account arg1, String arg2, Bundle arg3) { 135 Bundle result = new Bundle(); 136 result.putInt(AccountManager.KEY_ERROR_CODE, AccountManager.ERROR_CODE_BAD_REQUEST); 137 result.putString(AccountManager.KEY_ERROR_MESSAGE, "Can't add new SPNEGO accounts"); 138 return result; 139 } 140 141 /** Called from tests, sets up the test account, if it doesn't already exist */ 142 @CalledByNative ensureTestAccountExists()143 private static void ensureTestAccountExists() { 144 Activity activity = ApplicationStatus.getLastTrackedFocusedActivity(); 145 AccountManager am = AccountManager.get(activity); 146 Account account = new Account(ACCOUNT_NAME, ACCOUNT_TYPE); 147 am.addAccountExplicitly(account, null, null); 148 } 149 150 /** Called from tests to tidy up test accounts. */ 151 @SuppressWarnings("deprecation") 152 @CalledByNative removeTestAccounts()153 private static void removeTestAccounts() { 154 Activity activity = ApplicationStatus.getLastTrackedFocusedActivity(); 155 AccountManager am = AccountManager.get(activity); 156 String features[] = {HttpNegotiateConstants.SPNEGO_FEATURE}; 157 try { 158 Account accounts[] = 159 am.getAccountsByTypeAndFeatures(ACCOUNT_TYPE, features, null, null).getResult(); 160 for (Account account : accounts) { 161 // Deprecated, but the replacement not available on Android JB. 162 am.removeAccount(account, null, null).getResult(); 163 } 164 } catch (OperationCanceledException | AuthenticatorException | IOException e) { 165 // Should never happen. This is tidy-up after the tests. Ignore. 166 } 167 } 168 169 @CalledByNative setNativeAuthenticator(long nativeDummySpnegoAuthenticator)170 private static void setNativeAuthenticator(long nativeDummySpnegoAuthenticator) { 171 sNativeDummySpnegoAuthenticator = nativeDummySpnegoAuthenticator; 172 } 173 174 @NativeMethods 175 interface Natives { 176 /** 177 * Send the relevant decoded arguments of getAuthToken to C++ for checking by googletest 178 * checks If the checks fail then the C++ unit test using this authenticator will fail. 179 * 180 * @param authTokenType 181 * @param spn 182 * @param incomingToken 183 */ 184 @NativeClassQualifiedName("DummySpnegoAuthenticator::SecurityContextQuery") checkGetTokenArguments(long nativeQuery, String incomingToken)185 void checkGetTokenArguments(long nativeQuery, String incomingToken); 186 187 @NativeClassQualifiedName("DummySpnegoAuthenticator::SecurityContextQuery") getTokenToReturn(long nativeQuery)188 String getTokenToReturn(long nativeQuery); 189 190 @NativeClassQualifiedName("DummySpnegoAuthenticator::SecurityContextQuery") getResult(long nativeQuery)191 int getResult(long nativeQuery); 192 getNextQuery(long nativeDummySpnegoAuthenticator)193 long getNextQuery(long nativeDummySpnegoAuthenticator); 194 } 195 } 196