Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(763)

Side by Side Diff: net/android/java/src/org/chromium/net/HttpNegotiateAuthenticator.java

Issue 1422693002: Make Android HttpNegotiateAuthenticator work without an activity (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@AppStatusWebview
Patch Set: Add more tests Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.net; 5 package org.chromium.net;
6 6
7 import android.accounts.Account;
7 import android.accounts.AccountManager; 8 import android.accounts.AccountManager;
8 import android.accounts.AccountManagerCallback; 9 import android.accounts.AccountManagerCallback;
9 import android.accounts.AccountManagerFuture; 10 import android.accounts.AccountManagerFuture;
10 import android.accounts.AuthenticatorException; 11 import android.accounts.AuthenticatorException;
11 import android.accounts.OperationCanceledException; 12 import android.accounts.OperationCanceledException;
12 import android.app.Activity; 13 import android.app.Activity;
14 import android.content.BroadcastReceiver;
15 import android.content.Context;
16 import android.content.Intent;
17 import android.content.IntentFilter;
13 import android.os.Bundle; 18 import android.os.Bundle;
14 import android.os.Handler; 19 import android.os.Handler;
15 20
16 import org.chromium.base.ApplicationStatus; 21 import org.chromium.base.ApplicationStatus;
22 import org.chromium.base.Log;
17 import org.chromium.base.ThreadUtils; 23 import org.chromium.base.ThreadUtils;
18 import org.chromium.base.VisibleForTesting; 24 import org.chromium.base.VisibleForTesting;
19 import org.chromium.base.annotations.CalledByNative; 25 import org.chromium.base.annotations.CalledByNative;
20 import org.chromium.base.annotations.JNINamespace; 26 import org.chromium.base.annotations.JNINamespace;
21 27
22 import java.io.IOException; 28 import java.io.IOException;
23 29
24 /** 30 /**
25 * Class to get Auth Tokens for HTTP Negotiate authentication (typically used fo r Kerberos) An 31 * Class to get Auth Tokens for HTTP Negotiate authentication (typically used fo r Kerberos) An
26 * instance of this class is created for each separate negotiation. 32 * instance of this class is created for each separate negotiation.
27 */ 33 */
28 @JNINamespace("net::android") 34 @JNINamespace("net::android")
29 public class HttpNegotiateAuthenticator { 35 public class HttpNegotiateAuthenticator {
36 private static final String TAG = "net_auth";
30 private Bundle mSpnegoContext = null; 37 private Bundle mSpnegoContext = null;
31 private final String mAccountType; 38 private final String mAccountType;
32 private AccountManagerFuture<Bundle> mFuture;
33 39
34 private HttpNegotiateAuthenticator(String accountType) { 40 /**
41 * Structure designed to hold the data related to a specific request across the various
42 * callbacks needed to complete it.
43 */
44 static class RequestData {
45 /** Native object to post the result to. */
46 public long nativeResultObject;
47
48 /** Reference to the account manager to use for the various requests. */
49 public AccountManager accountManager;
50
51 /** Authenticator-specific options for the request, used for AccountMana ger#getAuthToken. */
52 public Bundle options;
53
54 /** Desired token type, used for AccountManager#getAuthToken. */
55 public String authTokenType;
56
57 /** Account to fetch an auth token for. */
58 public Account account;
59 }
60
61 /**
62 * Expects to receive a single account as result, and uses that account to r equest a token
63 * from the {@link AccountManager} provided via the {@link RequestData}
64 */
65 @VisibleForTesting
66 class GetAccountsCallback implements AccountManagerCallback<Account[]> {
67 private final RequestData mRequestData;
68 public GetAccountsCallback(RequestData requestData) {
69 mRequestData = requestData;
70 }
71
72 @Override
73 public void run(AccountManagerFuture<Account[]> future) {
74 Account[] accounts;
75 try {
76 accounts = future.getResult();
77 } catch (OperationCanceledException | AuthenticatorException | IOExc eption e) {
78 notifyFailure("Unable to retrieve the results for the getAccount s call.", e,
79 mRequestData);
80 return;
81 }
82
83 if (accounts.length != 1) {
asanka 2015/11/03 17:45:51 Is there no way to select an account in this case?
dgn 2015/11/05 14:39:14 There isn't any. Showing any kind of UI requires h
84 // TODO(dgn) Maybe should fail with an error like ERR_INVALID_AU TH_CREDENTIALS
85 // instead of UNEXPECTED?
asanka 2015/11/03 17:45:51 Yeah. ERR_INVALID_AUTH_CREDENTIALS would be better
dgn 2015/11/05 14:39:14 Done.
86 notifyFailure("Unable to obtain a unique eligible account for ne gotiate auth.",
87 null, mRequestData);
88 return;
89 }
90
91 mRequestData.account = accounts[0];
92 mRequestData.accountManager.getAuthToken(mRequestData.account,
93 mRequestData.authTokenType, mRequestData.options, true /* no tifyAuthFailure */,
94 new GetTokenCallback(mRequestData),
95 new Handler(ThreadUtils.getUiThreadLooper()));
96 }
97 }
98
99 @VisibleForTesting
100 class GetTokenCallback implements AccountManagerCallback<Bundle> {
101 private final RequestData mRequestData;
102 public GetTokenCallback(RequestData requestData) {
103 mRequestData = requestData;
104 }
105
106 @Override
107 public void run(AccountManagerFuture<Bundle> future) {
108 Bundle result;
109 try {
110 result = future.getResult();
111 } catch (OperationCanceledException | AuthenticatorException | IOExc eption e) {
112 notifyFailure("Operation did not complete", e, mRequestData);
113 return;
114 }
115
116 if (result.containsKey(AccountManager.KEY_INTENT)) {
117 final Context appContext = ApplicationStatus.getApplicationConte xt();
dgn 2015/10/30 12:38:56 Note: torne@ is adding a replacement for this, I'l
aberent 2015/10/30 16:38:10 nit: Maybe add a TODO if this is landing first.
dgn 2015/10/30 17:04:40 I'll wait. It's broken otherwise, even though the
118
119 // We wait for a broadcast that should be sent once the user is done interacting
120 // with the notification
121 // TODO(dgn) We currently hang around if the notification is swi ped away, until
122 // a LOGIN_ACCOUNTS_CHANGED_ACTION filter is received. It might be for something
123 // unrelated then we would wait again here. Maybe we should limi t the number of
124 // retries in some way?
125 BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
126
127 @Override
128 public void onReceive(Context context, Intent intent) {
129 appContext.unregisterReceiver(this);
130 mRequestData.accountManager.getAuthToken(mRequestData.ac count,
131 mRequestData.authTokenType, mRequestData.options ,
132 true /* notifyAuthFailure */, new GetTokenCallba ck(mRequestData),
133 null);
134 }
135
136 };
137 appContext.registerReceiver(broadcastReceiver,
138 new IntentFilter(AccountManager.LOGIN_ACCOUNTS_CHANGED_A CTION));
139 } else {
140 setResult(result, mRequestData);
141 }
142 }
143 }
144
145 protected HttpNegotiateAuthenticator(String accountType) {
35 assert !android.text.TextUtils.isEmpty(accountType); 146 assert !android.text.TextUtils.isEmpty(accountType);
36 mAccountType = accountType; 147 mAccountType = accountType;
37 } 148 }
38 149
39 /** 150 /**
40 * @param accountType The Android account type to use. 151 * @param accountType The Android account type to use.
41 */ 152 */
42 @VisibleForTesting 153 @VisibleForTesting
43 @CalledByNative 154 @CalledByNative
44 static HttpNegotiateAuthenticator create(String accountType) { 155 static HttpNegotiateAuthenticator create(String accountType) {
45 return new HttpNegotiateAuthenticator(accountType); 156 return new HttpNegotiateAuthenticator(accountType);
46 } 157 }
47 158
48 /** 159 /**
49 * @param nativeResultObject The C++ object used to return the result. For c orrect C++ memory 160 * @param nativeResultObject The C++ object used to return the result. For c orrect C++ memory
50 * management we must call nativeSetResult precisely once with th is object. 161 * management we must call nativeSetResult precisely once with th is object.
51 * @param principal The principal (must be host based). 162 * @param principal The principal (must be host based).
52 * @param authToken The incoming auth token. 163 * @param authToken The incoming auth token.
53 * @param canDelegate True if we can delegate. 164 * @param canDelegate True if we can delegate.
54 */ 165 */
55 @VisibleForTesting 166 @VisibleForTesting
56 @CalledByNative 167 @CalledByNative
57 void getNextAuthToken(final long nativeResultObject, final String principal, String authToken, 168 void getNextAuthToken(final long nativeResultObject, final String principal, String authToken,
58 boolean canDelegate) { 169 boolean canDelegate) {
59 assert principal != null; 170 assert principal != null;
60 String authTokenType = HttpNegotiateConstants.SPNEGO_TOKEN_TYPE_BASE + p rincipal; 171
172 RequestData requestData = new RequestData();
173 requestData.authTokenType = HttpNegotiateConstants.SPNEGO_TOKEN_TYPE_BAS E + principal;
174 requestData.accountManager = AccountManager.get(ApplicationStatus.getApp licationContext());
175 requestData.nativeResultObject = nativeResultObject;
176 String features[] = {HttpNegotiateConstants.SPNEGO_FEATURE};
177
178 requestData.options = new Bundle();
179 if (authToken != null) {
180 requestData.options.putString(
181 HttpNegotiateConstants.KEY_INCOMING_AUTH_TOKEN, authToken);
182 }
183 if (mSpnegoContext != null) {
184 requestData.options.putBundle(
185 HttpNegotiateConstants.KEY_SPNEGO_CONTEXT, mSpnegoContext);
186 }
187 requestData.options.putBoolean(HttpNegotiateConstants.KEY_CAN_DELEGATE, canDelegate);
188 Handler uiThreadHandler = new Handler(ThreadUtils.getUiThreadLooper());
189
61 Activity activity = ApplicationStatus.getLastTrackedFocusedActivity(); 190 Activity activity = ApplicationStatus.getLastTrackedFocusedActivity();
62 if (activity == null) { 191 if (activity == null) {
63 nativeSetResult(nativeResultObject, NetError.ERR_UNEXPECTED, null); 192 // This code path is not as able to get user input as the one that h as an activity. It
64 return; 193 // doesn't handle 0 or multiple accounts and will just result in a f ailure in those
194 // cases.
195 requestData.accountManager.getAccountsByTypeAndFeatures(
196 mAccountType, features, new GetAccountsCallback(requestData) , uiThreadHandler);
197 } else {
198 // Behavior in Chrome
199 // If there is more than one account, it will show the disambig for each query
asanka 2015/11/03 17:45:51 expn disambig
dgn 2015/11/06 16:51:48 Done.
200 // (e.g. main page, then favicon ...)
201 // Same if the credentials need to be confirmed/
202 // If there is a failure, it will retry automatically.
203 requestData.accountManager.getAuthTokenByFeatures(mAccountType,
204 requestData.authTokenType, features, activity, null, request Data.options,
205 new GetTokenCallback(requestData), uiThreadHandler);
65 } 206 }
66 AccountManager am = AccountManager.get(activity); 207 }
67 String features[] = {HttpNegotiateConstants.SPNEGO_FEATURE};
68 208
69 Bundle options = new Bundle(); 209 private void notifyFailure(String message, Exception e, RequestData requestD ata) {
210 Log.w(TAG, message, e);
211 nativeSetResult(requestData.nativeResultObject, NetError.ERR_UNEXPECTED, null);
212 }
70 213
71 if (authToken != null) { 214 private void setResult(Bundle result, RequestData requestData) {
72 options.putString(HttpNegotiateConstants.KEY_INCOMING_AUTH_TOKEN, au thToken); 215 mSpnegoContext = result.getBundle(HttpNegotiateConstants.KEY_SPNEGO_CONT EXT);
216 int status;
217 switch (result.getInt(
218 HttpNegotiateConstants.KEY_SPNEGO_RESULT, HttpNegotiateConstants .ERR_UNEXPECTED)) {
219 case HttpNegotiateConstants.OK:
asanka 2015/11/03 17:45:51 How permanent are the error codes in HttpNegotiate
dgn 2015/11/05 14:39:14 They are supposed to not change as 3p devs will us
asanka 2015/11/05 17:41:32 To give some background, the net::Error values wer
dgn 2015/11/06 16:51:48 Moved this discussion to http://crbug.com/552414.
220 status = 0;
221 break;
222 case HttpNegotiateConstants.ERR_UNEXPECTED:
223 status = NetError.ERR_UNEXPECTED;
224 break;
225 case HttpNegotiateConstants.ERR_ABORTED:
226 status = NetError.ERR_ABORTED;
227 break;
228 case HttpNegotiateConstants.ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS:
229 status = NetError.ERR_UNEXPECTED_SECURITY_LIBRARY_STATUS;
230 break;
231 case HttpNegotiateConstants.ERR_INVALID_RESPONSE:
232 status = NetError.ERR_INVALID_RESPONSE;
233 break;
234 case HttpNegotiateConstants.ERR_INVALID_AUTH_CREDENTIALS:
235 status = NetError.ERR_INVALID_AUTH_CREDENTIALS;
236 break;
237 case HttpNegotiateConstants.ERR_UNSUPPORTED_AUTH_SCHEME:
238 status = NetError.ERR_UNSUPPORTED_AUTH_SCHEME;
239 break;
240 case HttpNegotiateConstants.ERR_MISSING_AUTH_CREDENTIALS:
241 status = NetError.ERR_MISSING_AUTH_CREDENTIALS;
242 break;
243 case HttpNegotiateConstants.ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS :
244 status = NetError.ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATUS;
245 break;
246 case HttpNegotiateConstants.ERR_MALFORMED_IDENTITY:
247 status = NetError.ERR_MALFORMED_IDENTITY;
248 break;
249 default:
250 status = NetError.ERR_UNEXPECTED;
73 } 251 }
74 if (mSpnegoContext != null) { 252 nativeSetResult(requestData.nativeResultObject, status,
75 options.putBundle(HttpNegotiateConstants.KEY_SPNEGO_CONTEXT, mSpnego Context); 253 result.getString(AccountManager.KEY_AUTHTOKEN));
76 }
77 options.putBoolean(HttpNegotiateConstants.KEY_CAN_DELEGATE, canDelegate) ;
78
79 mFuture = am.getAuthTokenByFeatures(mAccountType, authTokenType, feature s, activity, null,
80 options, new AccountManagerCallback<Bundle>() {
81
82 @Override
83 public void run(AccountManagerFuture<Bundle> future) {
84 try {
85 Bundle result = future.getResult();
86 mSpnegoContext =
87 result.getBundle(HttpNegotiateConstants.KEY_ SPNEGO_CONTEXT);
88 int status;
89 switch (result.getInt(HttpNegotiateConstants.KEY_SPN EGO_RESULT,
90 HttpNegotiateConstants.ERR_UNEXPECTED)) {
91 case HttpNegotiateConstants.OK:
92 status = 0;
93 break;
94 case HttpNegotiateConstants.ERR_UNEXPECTED:
95 status = NetError.ERR_UNEXPECTED;
96 break;
97 case HttpNegotiateConstants.ERR_ABORTED:
98 status = NetError.ERR_ABORTED;
99 break;
100 case HttpNegotiateConstants.ERR_UNEXPECTED_SECUR ITY_LIBRARY_STATUS:
101 status = NetError.ERR_UNEXPECTED_SECURITY_LI BRARY_STATUS;
102 break;
103 case HttpNegotiateConstants.ERR_INVALID_RESPONSE :
104 status = NetError.ERR_INVALID_RESPONSE;
105 break;
106 case HttpNegotiateConstants.ERR_INVALID_AUTH_CRE DENTIALS:
107 status = NetError.ERR_INVALID_AUTH_CREDENTIA LS;
108 break;
109 case HttpNegotiateConstants.ERR_UNSUPPORTED_AUTH _SCHEME:
110 status = NetError.ERR_UNSUPPORTED_AUTH_SCHEM E;
111 break;
112 case HttpNegotiateConstants.ERR_MISSING_AUTH_CRE DENTIALS:
113 status = NetError.ERR_MISSING_AUTH_CREDENTIA LS;
114 break;
115 case HttpNegotiateConstants
116 .ERR_UNDOCUMENTED_SECURITY_LIBRARY_STATU S:
117 status = NetError.ERR_UNDOCUMENTED_SECURITY_ LIBRARY_STATUS;
118 break;
119 case HttpNegotiateConstants.ERR_MALFORMED_IDENTI TY:
120 status = NetError.ERR_MALFORMED_IDENTITY;
121 break;
122 default:
123 status = NetError.ERR_UNEXPECTED;
124 }
125 nativeSetResult(nativeResultObject, status,
126 result.getString(AccountManager.KEY_AUTHTOKE N));
127 } catch (OperationCanceledException | AuthenticatorExcep tion
128 | IOException e) {
129 nativeSetResult(nativeResultObject, NetError.ERR_ABO RTED, null);
130 }
131 }
132
133 }, new Handler(ThreadUtils.getUiThreadLooper()));
134 } 254 }
135 255
136 @VisibleForTesting 256 @VisibleForTesting
137 native void nativeSetResult( 257 native void nativeSetResult(
138 long nativeJavaNegotiateResultWrapper, int status, String authToken) ; 258 long nativeJavaNegotiateResultWrapper, int status, String authToken) ;
139 } 259 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698