author | Edouard Oger <eoger@fastmail.com> |
Wed, 18 May 2016 12:32:55 -0700 | |
changeset 304069 | 346623206d14008a0877ee57f3a2a4e396386da6 |
parent 304068 | ec25edc09a602ae0abf6b957d603018598e3ef7b |
child 304070 | 985cadcc9ed5a98f520e6988f556dadbb26266a9 |
push id | 30411 |
push user | kwierso@gmail.com |
push date | Fri, 08 Jul 2016 00:26:45 +0000 |
treeherder | mozilla-central@23dc78b7b57e [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | mcomella |
bugs | 1250782 |
milestone | 50.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountClient20.java | file | annotate | diff | comparison | revisions |
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountClient20.java +++ b/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountClient20.java @@ -1,14 +1,15 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.gecko.background.fxa; +import org.json.simple.JSONArray; import org.mozilla.gecko.background.common.log.Logger; import org.mozilla.gecko.background.fxa.FxAccountClientException.FxAccountClientMalformedResponseException; import org.mozilla.gecko.background.fxa.FxAccountClientException.FxAccountClientRemoteException; import org.mozilla.gecko.fxa.FxAccountConstants; import org.mozilla.gecko.Locales; import org.mozilla.gecko.sync.ExtendedJSONObject; import org.mozilla.gecko.sync.Utils; import org.mozilla.gecko.sync.crypto.HKDF; @@ -163,52 +164,66 @@ public class FxAccountClient20 implement executor.execute(new Runnable() { @Override public void run() { delegate.handleError(e); } }); } + enum ResponseType { + JSON_ARRAY, + JSON_OBJECT + } + /** * Translate resource callbacks into request callbacks invoked on the provided * executor. * <p> * Override <code>handleSuccess</code> to parse the body of the resource * request and call the request callback. <code>handleSuccess</code> is * invoked via the executor, so you don't need to delegate further. */ protected abstract class ResourceDelegate<T> extends BaseResourceDelegate { - protected abstract void handleSuccess(final int status, HttpResponse response, final ExtendedJSONObject body); + + protected void handleSuccess(final int status, HttpResponse response, final ExtendedJSONObject body) throws Exception { + throw new UnsupportedOperationException(); + } + + protected void handleSuccess(final int status, HttpResponse response, final JSONArray body) throws Exception { + throw new UnsupportedOperationException(); + } protected final RequestDelegate<T> delegate; protected final byte[] tokenId; protected final byte[] reqHMACKey; protected final SkewHandler skewHandler; + protected final ResponseType responseType; /** * Create a delegate for an un-authenticated resource. */ - public ResourceDelegate(final Resource resource, final RequestDelegate<T> delegate) { - this(resource, delegate, null, null); + public ResourceDelegate(final Resource resource, final RequestDelegate<T> delegate, ResponseType responseType) { + this(resource, delegate, responseType, null, null); } /** * Create a delegate for a Hawk-authenticated resource. * <p> * Every Hawk request that encloses an entity (PATCH, POST, and PUT) will * include the payload verification hash. */ - public ResourceDelegate(final Resource resource, final RequestDelegate<T> delegate, final byte[] tokenId, final byte[] reqHMACKey) { + public ResourceDelegate(final Resource resource, final RequestDelegate<T> delegate, ResponseType responseType, final byte[] tokenId, final byte[] reqHMACKey) { super(resource); this.delegate = delegate; this.reqHMACKey = reqHMACKey; this.tokenId = tokenId; this.skewHandler = SkewHandler.getSkewHandlerForResource(resource); + this.responseType = responseType; } @Override public AuthHeaderProvider getAuthHeaderProvider() { if (tokenId != null && reqHMACKey != null) { // We always include the payload verification hash for FxA Hawk-authenticated requests. final boolean includePayloadVerificationHash = true; return new HawkAuthHeaderProvider(Utils.byte2Hex(tokenId), reqHMACKey, includePayloadVerificationHash, skewHandler.getSkewInSeconds()); @@ -245,18 +260,24 @@ public class FxAccountClient20 implement }); } protected void invokeHandleSuccess(final int status, final HttpResponse response) { executor.execute(new Runnable() { @Override public void run() { try { - ExtendedJSONObject body = new SyncResponse(response).jsonObjectBody(); - ResourceDelegate.this.handleSuccess(status, response, body); + SyncResponse syncResponse = new SyncResponse(response); + if (responseType == ResponseType.JSON_ARRAY) { + JSONArray body = syncResponse.jsonArrayBody(); + ResourceDelegate.this.handleSuccess(status, response, body); + } else { + ExtendedJSONObject body = syncResponse.jsonObjectBody(); + ResourceDelegate.this.handleSuccess(status, response, body); + } } catch (Exception e) { delegate.handleError(e); } } }); } @Override @@ -408,29 +429,23 @@ public class FxAccountClient20 implement BaseResource resource; try { resource = getBaseResource("account/keys"); } catch (URISyntaxException | UnsupportedEncodingException e) { invokeHandleError(delegate, e); return; } - resource.delegate = new ResourceDelegate<TwoKeys>(resource, delegate, tokenId, reqHMACKey) { + resource.delegate = new ResourceDelegate<TwoKeys>(resource, delegate, ResponseType.JSON_OBJECT, tokenId, reqHMACKey) { @Override - public void handleSuccess(int status, HttpResponse response, ExtendedJSONObject body) { - try { - byte[] kA = new byte[FxAccountUtils.CRYPTO_KEY_LENGTH_BYTES]; - byte[] wrapkB = new byte[FxAccountUtils.CRYPTO_KEY_LENGTH_BYTES]; - unbundleBody(body, requestKey, FxAccountUtils.KW("account/keys"), kA, wrapkB); - delegate.handleSuccess(new TwoKeys(kA, wrapkB)); - return; - } catch (Exception e) { - delegate.handleError(e); - return; - } + public void handleSuccess(int status, HttpResponse response, ExtendedJSONObject body) throws Exception { + byte[] kA = new byte[FxAccountUtils.CRYPTO_KEY_LENGTH_BYTES]; + byte[] wrapkB = new byte[FxAccountUtils.CRYPTO_KEY_LENGTH_BYTES]; + unbundleBody(body, requestKey, FxAccountUtils.KW("account/keys"), kA, wrapkB); + delegate.handleSuccess(new TwoKeys(kA, wrapkB)); } }; resource.get(); } /** * Thin container for status response. */ @@ -470,30 +485,24 @@ public class FxAccountClient20 implement BaseResource resource; try { resource = getBaseResource("recovery_email/status"); } catch (URISyntaxException | UnsupportedEncodingException e) { invokeHandleError(delegate, e); return; } - resource.delegate = new ResourceDelegate<StatusResponse>(resource, delegate, tokenId, reqHMACKey) { + resource.delegate = new ResourceDelegate<StatusResponse>(resource, delegate, ResponseType.JSON_OBJECT, tokenId, reqHMACKey) { @Override - public void handleSuccess(int status, HttpResponse response, ExtendedJSONObject body) { - try { - String[] requiredStringFields = new String[] { JSON_KEY_EMAIL }; - body.throwIfFieldsMissingOrMisTyped(requiredStringFields, String.class); - String email = body.getString(JSON_KEY_EMAIL); - Boolean verified = body.getBoolean(JSON_KEY_VERIFIED); - delegate.handleSuccess(new StatusResponse(email, verified)); - return; - } catch (Exception e) { - delegate.handleError(e); - return; - } + public void handleSuccess(int status, HttpResponse response, ExtendedJSONObject body) throws Exception { + String[] requiredStringFields = new String[] { JSON_KEY_EMAIL }; + body.throwIfFieldsMissingOrMisTyped(requiredStringFields, String.class); + String email = body.getString(JSON_KEY_EMAIL); + Boolean verified = body.getBoolean(JSON_KEY_VERIFIED); + delegate.handleSuccess(new StatusResponse(email, verified)); } }; resource.get(); } @SuppressWarnings("unchecked") public void sign(final byte[] sessionToken, final ExtendedJSONObject publicKey, long durationInMilliseconds, final RequestDelegate<String> delegate) { final ExtendedJSONObject body = new ExtendedJSONObject(); @@ -512,19 +521,19 @@ public class FxAccountClient20 implement BaseResource resource; try { resource = getBaseResource("certificate/sign"); } catch (URISyntaxException | UnsupportedEncodingException e) { invokeHandleError(delegate, e); return; } - resource.delegate = new ResourceDelegate<String>(resource, delegate, tokenId, reqHMACKey) { + resource.delegate = new ResourceDelegate<String>(resource, delegate, ResponseType.JSON_OBJECT, tokenId, reqHMACKey) { @Override - public void handleSuccess(int status, HttpResponse response, ExtendedJSONObject body) { + public void handleSuccess(int status, HttpResponse response, ExtendedJSONObject body) throws Exception { String cert = body.getString("cert"); if (cert == null) { delegate.handleError(new FxAccountClientException("cert must be a non-null string")); return; } delegate.handleSuccess(cert); } }; @@ -575,41 +584,35 @@ public class FxAccountClient20 implement } resource = getBaseResource(path, modifiedParameters); body = new FxAccount20LoginDelegate(emailUTF8, quickStretchedPW).getCreateBody(); } catch (Exception e) { invokeHandleError(delegate, e); return; } - resource.delegate = new ResourceDelegate<LoginResponse>(resource, delegate) { + resource.delegate = new ResourceDelegate<LoginResponse>(resource, delegate, ResponseType.JSON_OBJECT) { @Override - public void handleSuccess(int status, HttpResponse response, ExtendedJSONObject body) { - try { - final String[] requiredStringFields = getKeys ? LOGIN_RESPONSE_REQUIRED_STRING_FIELDS_KEYS : LOGIN_RESPONSE_REQUIRED_STRING_FIELDS; - body.throwIfFieldsMissingOrMisTyped(requiredStringFields, String.class); + public void handleSuccess(int status, HttpResponse response, ExtendedJSONObject body) throws Exception { + final String[] requiredStringFields = getKeys ? LOGIN_RESPONSE_REQUIRED_STRING_FIELDS_KEYS : LOGIN_RESPONSE_REQUIRED_STRING_FIELDS; + body.throwIfFieldsMissingOrMisTyped(requiredStringFields, String.class); - final String[] requiredBooleanFields = LOGIN_RESPONSE_REQUIRED_BOOLEAN_FIELDS; - body.throwIfFieldsMissingOrMisTyped(requiredBooleanFields, Boolean.class); + final String[] requiredBooleanFields = LOGIN_RESPONSE_REQUIRED_BOOLEAN_FIELDS; + body.throwIfFieldsMissingOrMisTyped(requiredBooleanFields, Boolean.class); - String uid = body.getString(JSON_KEY_UID); - boolean verified = body.getBoolean(JSON_KEY_VERIFIED); - byte[] sessionToken = Utils.hex2Byte(body.getString(JSON_KEY_SESSIONTOKEN)); - byte[] keyFetchToken = null; - if (getKeys) { - keyFetchToken = Utils.hex2Byte(body.getString(JSON_KEY_KEYFETCHTOKEN)); - } - LoginResponse loginResponse = new LoginResponse(new String(emailUTF8, "UTF-8"), uid, verified, sessionToken, keyFetchToken); + String uid = body.getString(JSON_KEY_UID); + boolean verified = body.getBoolean(JSON_KEY_VERIFIED); + byte[] sessionToken = Utils.hex2Byte(body.getString(JSON_KEY_SESSIONTOKEN)); + byte[] keyFetchToken = null; + if (getKeys) { + keyFetchToken = Utils.hex2Byte(body.getString(JSON_KEY_KEYFETCHTOKEN)); + } + LoginResponse loginResponse = new LoginResponse(new String(emailUTF8, "UTF-8"), uid, verified, sessionToken, keyFetchToken); - delegate.handleSuccess(loginResponse); - return; - } catch (Exception e) { - delegate.handleError(e); - return; - } + delegate.handleSuccess(loginResponse); } }; post(resource, body, delegate); } public void createAccount(final byte[] emailUTF8, final byte[] quickStretchedPW, final boolean getKeys, @@ -630,40 +633,36 @@ public class FxAccountClient20 implement resource = getBaseResource(path, modifiedParameters); body = new FxAccount20CreateDelegate(emailUTF8, quickStretchedPW, preVerified).getCreateBody(); } catch (Exception e) { invokeHandleError(delegate, e); return; } // This is very similar to login, except verified is not required. - resource.delegate = new ResourceDelegate<LoginResponse>(resource, delegate) { + resource.delegate = new ResourceDelegate<LoginResponse>(resource, delegate, ResponseType.JSON_OBJECT) { @Override - public void handleSuccess(int status, HttpResponse response, ExtendedJSONObject body) { - try { - final String[] requiredStringFields = getKeys ? LOGIN_RESPONSE_REQUIRED_STRING_FIELDS_KEYS : LOGIN_RESPONSE_REQUIRED_STRING_FIELDS; - body.throwIfFieldsMissingOrMisTyped(requiredStringFields, String.class); + public void handleSuccess(int status, HttpResponse response, ExtendedJSONObject body) throws Exception { + final String[] requiredStringFields = getKeys ? LOGIN_RESPONSE_REQUIRED_STRING_FIELDS_KEYS : LOGIN_RESPONSE_REQUIRED_STRING_FIELDS; + body.throwIfFieldsMissingOrMisTyped(requiredStringFields, String.class); - String uid = body.getString(JSON_KEY_UID); - boolean verified = false; // In production, we're definitely not verified immediately upon creation. - Boolean tempVerified = body.getBoolean(JSON_KEY_VERIFIED); - if (tempVerified != null) { - verified = tempVerified; - } - byte[] sessionToken = Utils.hex2Byte(body.getString(JSON_KEY_SESSIONTOKEN)); - byte[] keyFetchToken = null; - if (getKeys) { - keyFetchToken = Utils.hex2Byte(body.getString(JSON_KEY_KEYFETCHTOKEN)); - } - LoginResponse loginResponse = new LoginResponse(new String(emailUTF8, "UTF-8"), uid, verified, sessionToken, keyFetchToken); + String uid = body.getString(JSON_KEY_UID); + boolean verified = false; // In production, we're definitely not verified immediately upon creation. + Boolean tempVerified = body.getBoolean(JSON_KEY_VERIFIED); + if (tempVerified != null) { + verified = tempVerified; + } + byte[] sessionToken = Utils.hex2Byte(body.getString(JSON_KEY_SESSIONTOKEN)); + byte[] keyFetchToken = null; + if (getKeys) { + keyFetchToken = Utils.hex2Byte(body.getString(JSON_KEY_KEYFETCHTOKEN)); + } + LoginResponse loginResponse = new LoginResponse(new String(emailUTF8, "UTF-8"), uid, verified, sessionToken, keyFetchToken); - delegate.handleSuccess(loginResponse); - } catch (Exception e) { - delegate.handleError(e); - } + delegate.handleSuccess(loginResponse); } }; post(resource, body, delegate); } /** * We want users to be able to enter their email address case-insensitively.