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