Bug 1250782 - Added FxAccountClient20#accountStatus and renamed status to recoveryEmailStatus draft
authorEdouard Oger <eoger@fastmail.com>
Wed, 18 May 2016 12:28:51 -0700
changeset 369918 ea9aac9e4e188fa5e2ac80a73f0ddf62109a199a
parent 369917 af73c7d91ed120b146fca3d2cc9730ff5819361e
child 369919 52031b38fb9259d7086cc5a43bcbba2d2423e2ca
push id18951
push userbmo:edouard.oger@gmail.com
push dateTue, 24 May 2016 00:09:32 +0000
bugs1250782
milestone49.0a1
Bug 1250782 - Added FxAccountClient20#accountStatus and renamed status to recoveryEmailStatus MozReview-Commit-ID: 3fFQaFe14x2
mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountClient.java
mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountClient20.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/fxa/login/MockFxAccountClient.java
mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/net/test/TestUserAgentHeaders.java
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountClient.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/background/fxa/FxAccountClient.java
@@ -1,16 +1,18 @@
 /* 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.mozilla.gecko.background.fxa.FxAccountClient20.AccountStatusResponse;
 import org.mozilla.gecko.background.fxa.FxAccountClient20.RequestDelegate;
-import org.mozilla.gecko.background.fxa.FxAccountClient20.StatusResponse;
+import org.mozilla.gecko.background.fxa.FxAccountClient20.RecoveryEmailStatusResponse;
 import org.mozilla.gecko.background.fxa.FxAccountClient20.TwoKeys;
 import org.mozilla.gecko.sync.ExtendedJSONObject;
 
 public interface FxAccountClient {
-  public void status(byte[] sessionToken, RequestDelegate<StatusResponse> requestDelegate);
+  public void accountStatus(String uid, RequestDelegate<AccountStatusResponse> requestDelegate);
+  public void recoveryEmailStatus(byte[] sessionToken, RequestDelegate<RecoveryEmailStatusResponse> requestDelegate);
   public void keys(byte[] keyFetchToken, RequestDelegate<TwoKeys> requestDelegate);
   public void sign(byte[] sessionToken, ExtendedJSONObject publicKey, long certificateDurationInMilliseconds, RequestDelegate<String> requestDelegate);
 }
--- 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
@@ -63,16 +63,18 @@ public class FxAccountClient20 implement
   public static final String JSON_KEY_SESSIONTOKEN = "sessionToken";
   public static final String JSON_KEY_UID = "uid";
   public static final String JSON_KEY_VERIFIED = "verified";
   public static final String JSON_KEY_ERROR = "error";
   public static final String JSON_KEY_MESSAGE = "message";
   public static final String JSON_KEY_INFO = "info";
   public static final String JSON_KEY_CODE = "code";
   public static final String JSON_KEY_ERRNO = "errno";
+  public static final String JSON_KEY_EXISTS = "exists";
+  public static final String JSON_KEY_LOCKED = "locked";
 
   protected static final String[] requiredErrorStringFields = { JSON_KEY_ERROR, JSON_KEY_MESSAGE, JSON_KEY_INFO };
   protected static final String[] requiredErrorLongFields = { JSON_KEY_CODE, JSON_KEY_ERRNO };
 
   /**
    * The server's URI.
    * <p>
    * We assume throughout that this ends with a trailing slash (and guarantee as
@@ -441,41 +443,83 @@ public class FxAccountClient20 implement
         unbundleBody(body, requestKey, FxAccountUtils.KW("account/keys"), kA, wrapkB);
         delegate.handleSuccess(new TwoKeys(kA, wrapkB));
       }
     };
     resource.get();
   }
 
   /**
-   * Thin container for status response.
+   * Thin container for account status response.
+   */
+  public static class AccountStatusResponse {
+    public final boolean exists;
+    public final boolean locked;
+    public AccountStatusResponse(boolean exists, boolean locked) {
+      this.exists = exists;
+      this.locked = locked;
+    }
+  }
+
+  /**
+   * Query the account status of an account given a uid.
+   *
+   * @param uid
+   *          to query.
+   * @param delegate
+   *          to invoke callbacks.
    */
-  public static class StatusResponse {
+  public void accountStatus(String uid, final RequestDelegate<AccountStatusResponse> delegate) {
+    BaseResource resource;
+    try {
+      final Map<String, String> params = new HashMap<>();
+      params.put("uid", uid);
+      resource = getBaseResource("account/status", params);
+    } catch (URISyntaxException | UnsupportedEncodingException e) {
+      invokeHandleError(delegate, e);
+      return;
+    }
+
+    resource.delegate = new ResourceDelegate<AccountStatusResponse>(resource, delegate, ResponseType.JSON_OBJECT) {
+      @Override
+      public void handleSuccess(int status, HttpResponse response, ExtendedJSONObject body) throws Exception {
+        Boolean exists = body.getBoolean(JSON_KEY_EXISTS);
+        Boolean locked = body.getBoolean(JSON_KEY_LOCKED);
+        delegate.handleSuccess(new AccountStatusResponse(exists, locked));
+      }
+    };
+    resource.get();
+  }
+
+  /**
+   * Thin container for recovery email status response.
+   */
+  public static class RecoveryEmailStatusResponse {
     public final String email;
     public final boolean verified;
-    public StatusResponse(String email, boolean verified) {
+    public RecoveryEmailStatusResponse(String email, boolean verified) {
       this.email = email;
       this.verified = verified;
     }
   }
 
   /**
-   * Query the status of an account given a valid session token.
+   * Query the recovery email status of an account given a valid session token.
    * <p>
    * This API is a little odd: the auth server returns the email and
    * verification state of the account that corresponds to the (opaque) session
    * token. It might fail if the session token is unknown (or invalid, or
    * revoked).
    *
    * @param sessionToken
    *          to query.
    * @param delegate
    *          to invoke callbacks.
    */
-  public void status(byte[] sessionToken, final RequestDelegate<StatusResponse> delegate) {
+  public void recoveryEmailStatus(byte[] sessionToken, final RequestDelegate<RecoveryEmailStatusResponse> delegate) {
     final byte[] tokenId = new byte[32];
     final byte[] reqHMACKey = new byte[32];
     final byte[] requestKey = new byte[32];
     try {
       HKDF.deriveMany(sessionToken, new byte[0], FxAccountUtils.KW("sessionToken"), tokenId, reqHMACKey, requestKey);
     } catch (Exception e) {
       invokeHandleError(delegate, e);
       return;
@@ -484,24 +528,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, ResponseType.JSON_OBJECT, tokenId, reqHMACKey) {
+    resource.delegate = new ResourceDelegate<RecoveryEmailStatusResponse>(resource, delegate, ResponseType.JSON_OBJECT, tokenId, reqHMACKey) {
       @Override
       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));
+        delegate.handleSuccess(new RecoveryEmailStatusResponse(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();
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/fxa/login/MockFxAccountClient.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/fxa/login/MockFxAccountClient.java
@@ -1,16 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 package org.mozilla.gecko.fxa.login;
 
 import org.mozilla.gecko.background.fxa.FxAccountClient;
+import org.mozilla.gecko.background.fxa.FxAccountClient20.AccountStatusResponse;
 import org.mozilla.gecko.background.fxa.FxAccountClient20.RequestDelegate;
-import org.mozilla.gecko.background.fxa.FxAccountClient20.StatusResponse;
+import org.mozilla.gecko.background.fxa.FxAccountClient20.RecoveryEmailStatusResponse;
 import org.mozilla.gecko.background.fxa.FxAccountClient20.TwoKeys;
 import org.mozilla.gecko.background.fxa.FxAccountClient20.LoginResponse;
 import org.mozilla.gecko.background.fxa.FxAccountClientException.FxAccountClientRemoteException;
 import org.mozilla.gecko.background.fxa.FxAccountRemoteError;
 import org.mozilla.gecko.background.fxa.FxAccountUtils;
 import org.mozilla.gecko.browserid.MockMyIDTokenFactory;
 import org.mozilla.gecko.browserid.RSACryptoImplementation;
 import org.mozilla.gecko.sync.ExtendedJSONObject;
@@ -89,24 +90,35 @@ public class MockFxAccountClient impleme
   }
 
   protected <T> void handleFailure(RequestDelegate<T> requestDelegate, int code, int errno, String message) {
     requestDelegate.handleFailure(new FxAccountClientRemoteException(makeHttpResponse(code, message),
         code, errno, "Bad authorization", message, null, new ExtendedJSONObject()));
   }
 
   @Override
-  public void status(byte[] sessionToken, RequestDelegate<StatusResponse> requestDelegate) {
+  public void accountStatus(String uid, RequestDelegate<AccountStatusResponse> requestDelegate) {
+    for (User user : users.values()) {
+      if (user.uid.equals(uid)) {
+        requestDelegate.handleSuccess(new AccountStatusResponse(true, false));
+        return;
+      }
+    }
+    requestDelegate.handleSuccess(new AccountStatusResponse(false, false));
+  }
+
+  @Override
+  public void recoveryEmailStatus(byte[] sessionToken, RequestDelegate<RecoveryEmailStatusResponse> requestDelegate) {
     String email = sessionTokens.get(Utils.byte2Hex(sessionToken));
     User user = users.get(email);
     if (email == null || user == null) {
       handleFailure(requestDelegate, HttpStatus.SC_UNAUTHORIZED, FxAccountRemoteError.INVALID_AUTHENTICATION_TOKEN, "invalid sessionToken");
       return;
     }
-    requestDelegate.handleSuccess(new StatusResponse(email, user.verified));
+    requestDelegate.handleSuccess(new RecoveryEmailStatusResponse(email, user.verified));
   }
 
   @Override
   public void keys(byte[] keyFetchToken, RequestDelegate<TwoKeys> requestDelegate) {
     String email = keyFetchTokens.get(Utils.byte2Hex(keyFetchToken));
     User user = users.get(email);
     if (email == null || user == null) {
       handleFailure(requestDelegate, HttpStatus.SC_UNAUTHORIZED, FxAccountRemoteError.INVALID_AUTHENTICATION_TOKEN, "invalid keyFetchToken");
--- a/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/net/test/TestUserAgentHeaders.java
+++ b/mobile/android/tests/background/junit4/src/org/mozilla/gecko/sync/net/test/TestUserAgentHeaders.java
@@ -8,17 +8,17 @@ import junit.framework.Assert;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mozilla.android.sync.test.helpers.HTTPServerTestHelper;
 import org.mozilla.android.sync.test.helpers.MockServer;
 import org.mozilla.gecko.background.fxa.FxAccountClient20;
 import org.mozilla.gecko.background.fxa.FxAccountClient20.RequestDelegate;
-import org.mozilla.gecko.background.fxa.FxAccountClient20.StatusResponse;
+import org.mozilla.gecko.background.fxa.FxAccountClient20.RecoveryEmailStatusResponse;
 import org.mozilla.gecko.background.fxa.FxAccountClientException.FxAccountClientRemoteException;
 import org.mozilla.gecko.background.testhelpers.TestRunner;
 import org.mozilla.gecko.background.testhelpers.WaitHelper;
 import org.mozilla.gecko.fxa.FxAccountConstants;
 import org.mozilla.gecko.sync.SyncConstants;
 import org.mozilla.gecko.sync.net.AuthHeaderProvider;
 import org.mozilla.gecko.sync.net.BaseResource;
 import org.mozilla.gecko.sync.net.SyncStorageRecordRequest;
@@ -101,19 +101,19 @@ public class TestUserAgentHeaders {
   }
 
   @Test
   public void testFxAccountClientUserAgent() throws Exception {
     final FxAccountClient20 client = new FxAccountClient20(TEST_SERVER, Executors.newSingleThreadExecutor());
     WaitHelper.getTestWaiter().performWait(new Runnable() {
       @Override
       public void run() {
-        client.status(new byte[] { 0 }, new RequestDelegate<FxAccountClient20.StatusResponse>() {
+        client.recoveryEmailStatus(new byte[] { 0 }, new RequestDelegate<RecoveryEmailStatusResponse>() {
           @Override
-          public void handleSuccess(StatusResponse result) {
+          public void handleSuccess(RecoveryEmailStatusResponse result) {
             WaitHelper.getTestWaiter().performNotify();
           }
 
           @Override
           public void handleFailure(FxAccountClientRemoteException e) {
             WaitHelper.getTestWaiter().performNotify();
           }