Repository: firebase/firebase-admin-java
Pull request #114 (2017-12-13 00:33:13)

Removing all usages of org.json from the auth package.
This is the first step towards removing `org.json:json` dependency from the codebase. This dependency has caused issues for many first-time users as indicated in #113 


Patch:
diff --git a/src/main/java/com/google/firebase/auth/ExportedUserRecord.java b/src/main/java/com/google/firebase/auth/ExportedUserRecord.java
index 3cf385c..e367f55 100644
--- a/src/main/java/com/google/firebase/auth/ExportedUserRecord.java
+++ b/src/main/java/com/google/firebase/auth/ExportedUserRecord.java
@@ -16,6 +16,7 @@
 
 package com.google.firebase.auth;
 
+import com.google.api.client.json.JsonFactory;
 import com.google.firebase.auth.internal.DownloadAccountResponse.User;
 import com.google.firebase.internal.Nullable;
 
@@ -28,8 +29,8 @@ public class ExportedUserRecord extends UserRecord {
   private final String passwordHash;
   private final String passwordSalt;
 
-  ExportedUserRecord(User response) {
-    super(response);
+  ExportedUserRecord(User response, JsonFactory jsonFactory) {
+    super(response, jsonFactory);
     this.passwordHash = response.getPasswordHash();
     this.passwordSalt = response.getPasswordSalt();
   }
diff --git a/src/main/java/com/google/firebase/auth/FirebaseAuth.java b/src/main/java/com/google/firebase/auth/FirebaseAuth.java
index dedb034..5b07850 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseAuth.java
+++ b/src/main/java/com/google/firebase/auth/FirebaseAuth.java
@@ -352,7 +352,7 @@ public class FirebaseAuth {
   private Task<ListUsersPage> listUsers(@Nullable String pageToken, int maxResults) {
     checkNotDestroyed();
     final PageFactory factory = new PageFactory(
-        new DefaultUserSource(userManager), maxResults, pageToken);
+        new DefaultUserSource(userManager, jsonFactory), maxResults, pageToken);
     return call(new Callable<ListUsersPage>() {
       @Override
       public ListUsersPage call() throws Exception {
diff --git a/src/main/java/com/google/firebase/auth/FirebaseUserManager.java b/src/main/java/com/google/firebase/auth/FirebaseUserManager.java
index a55ca5f..5ee2f9b 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseUserManager.java
+++ b/src/main/java/com/google/firebase/auth/FirebaseUserManager.java
@@ -109,7 +109,7 @@ class FirebaseUserManager {
       throw new FirebaseAuthException(USER_NOT_FOUND_ERROR,
           "No user record found for the provided user ID: " + uid);
     }
-    return new UserRecord(response.getUsers().get(0));
+    return new UserRecord(response.getUsers().get(0), jsonFactory);
   }
 
   UserRecord getUserByEmail(String email) throws FirebaseAuthException {
@@ -127,7 +127,7 @@ class FirebaseUserManager {
       throw new FirebaseAuthException(USER_NOT_FOUND_ERROR,
           "No user record found for the provided email: " + email);
     }
-    return new UserRecord(response.getUsers().get(0));
+    return new UserRecord(response.getUsers().get(0), jsonFactory);
   }
 
   UserRecord getUserByPhoneNumber(String phoneNumber) throws FirebaseAuthException {
@@ -145,7 +145,7 @@ class FirebaseUserManager {
       throw new FirebaseAuthException(USER_NOT_FOUND_ERROR,
           "No user record found for the provided phone number: " + phoneNumber);
     }
-    return new UserRecord(response.getUsers().get(0));
+    return new UserRecord(response.getUsers().get(0), jsonFactory);
   }
 
   String createUser(CreateRequest request) throws FirebaseAuthException {
diff --git a/src/main/java/com/google/firebase/auth/ListUsersPage.java b/src/main/java/com/google/firebase/auth/ListUsersPage.java
index 866d9bd..c0fc14a 100644
--- a/src/main/java/com/google/firebase/auth/ListUsersPage.java
+++ b/src/main/java/com/google/firebase/auth/ListUsersPage.java
@@ -19,6 +19,7 @@ package com.google.firebase.auth;
 import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 
+import com.google.api.client.json.JsonFactory;
 import com.google.api.gax.paging.Page;
 import com.google.common.collect.ImmutableList;
 import com.google.firebase.auth.internal.DownloadAccountResponse;
@@ -184,9 +185,11 @@ public class ListUsersPage implements Page<ExportedUserRecord> {
   static class DefaultUserSource implements UserSource {
 
     private final FirebaseUserManager userManager;
+    private final JsonFactory jsonFactory;
 
-    DefaultUserSource(FirebaseUserManager userManager) {
+    DefaultUserSource(FirebaseUserManager userManager, JsonFactory jsonFactory) {
       this.userManager = checkNotNull(userManager, "user manager must not be null");
+      this.jsonFactory = checkNotNull(jsonFactory, "json factory must not be null");
     }
 
     @Override
@@ -196,7 +199,7 @@ public class ListUsersPage implements Page<ExportedUserRecord> {
         ImmutableList.Builder<ExportedUserRecord> builder = ImmutableList.builder();
         if (response.hasUsers()) {
           for (DownloadAccountResponse.User user : response.getUsers()) {
-            builder.add(new ExportedUserRecord(user));
+            builder.add(new ExportedUserRecord(user, jsonFactory));
           }
         }
         String nextPageToken = response.getPageToken() != null
diff --git a/src/main/java/com/google/firebase/auth/UserRecord.java b/src/main/java/com/google/firebase/auth/UserRecord.java
index 7d634b3..e6d0927 100644
--- a/src/main/java/com/google/firebase/auth/UserRecord.java
+++ b/src/main/java/com/google/firebase/auth/UserRecord.java
@@ -35,7 +35,6 @@ import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import org.json.JSONObject;
 
 /**
  * Contains metadata associated with a Firebase user account. Instances of this class are immutable
@@ -61,8 +60,9 @@ public class UserRecord implements UserInfo {
   private final UserMetadata userMetadata;
   private final Map<String, Object> customClaims;
 
-  UserRecord(User response) {
-    checkNotNull(response, "Response must not be null");
+  UserRecord(User response, JsonFactory jsonFactory) {
+    checkNotNull(response, "response must not be null");
+    checkNotNull(jsonFactory, "jsonFactory must not be null");
     checkArgument(!Strings.isNullOrEmpty(response.getUid()), "uid must not be null or empty");
     this.uid = response.getUid();
     this.email = response.getEmail();
@@ -80,12 +80,19 @@ public class UserRecord implements UserInfo {
       }
     }
     this.userMetadata = new UserMetadata(response.getCreatedAt(), response.getLastLoginAt());
-    String customClaims = response.getCustomClaims();
-    if (!Strings.isNullOrEmpty(customClaims)) {
-      Map<String, Object> parsed = new JSONObject(customClaims).toMap();
-      this.customClaims = ImmutableMap.copyOf(parsed);
-    } else {
-      this.customClaims = ImmutableMap.of();
+    this.customClaims = parseCustomClaims(response.getCustomClaims(), jsonFactory);
+  }
+
+  private Map<String, Object> parseCustomClaims(String customClaims, JsonFactory jsonFactory) {
+    if (Strings.isNullOrEmpty(customClaims)) {
+      return ImmutableMap.of();
+    }
+    try {
+      Map<String, Object> parsed = new HashMap<>();
+      jsonFactory.createJsonParser(customClaims).parseAndClose(parsed);
+      return ImmutableMap.copyOf(parsed);
+    } catch (IOException e) {
+      throw new IllegalArgumentException("Failed to parse custom claims json", e);
     }
   }
 
diff --git a/src/main/java/com/google/firebase/auth/internal/DownloadAccountResponse.java b/src/main/java/com/google/firebase/auth/internal/DownloadAccountResponse.java
index c469a4a..7b56d6d 100644
--- a/src/main/java/com/google/firebase/auth/internal/DownloadAccountResponse.java
+++ b/src/main/java/com/google/firebase/auth/internal/DownloadAccountResponse.java
@@ -17,7 +17,6 @@
 package com.google.firebase.auth.internal;
 
 import com.google.api.client.util.Key;
-import com.google.firebase.auth.internal.GetAccountInfoResponse.User;
 import java.util.List;
 
 /**


Test patch:
diff --git a/src/test/java/com/google/firebase/auth/FirebaseAuthTest.java b/src/test/java/com/google/firebase/auth/FirebaseAuthTest.java
index 1e82ea9..49f5ef9 100644
--- a/src/test/java/com/google/firebase/auth/FirebaseAuthTest.java
+++ b/src/test/java/com/google/firebase/auth/FirebaseAuthTest.java
@@ -28,6 +28,7 @@ import static org.junit.Assert.fail;
 import com.google.api.client.googleapis.testing.auth.oauth2.MockTokenServerTransport;
 import com.google.api.client.googleapis.util.Utils;
 import com.google.api.client.http.HttpTransport;
+import com.google.api.client.json.JsonFactory;
 import com.google.api.client.json.gson.GsonFactory;
 import com.google.api.core.ApiFuture;
 import com.google.auth.http.HttpTransportFactory;
@@ -58,14 +59,13 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
-import org.json.JSONException;
-import org.json.JSONObject;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Assume;
@@ -82,6 +82,8 @@ public class FirebaseAuthTest {
   private static final String CLIENT_SECRET = "mockclientsecret";
   private static final String CLIENT_ID = "mockclientid";
   private static final String REFRESH_TOKEN = "mockrefreshtoken";
+  private static final JsonFactory JSON_FACTORY = Utils.getDefaultJsonFactory();
+
   private final FirebaseOptions firebaseOptions;
   private final boolean isCertCredential;
 
@@ -148,20 +150,19 @@ public class FirebaseAuthTest {
     });
   }
 
-  private static GoogleCredentials createRefreshTokenCredential()
-      throws JSONException, IOException {
+  private static GoogleCredentials createRefreshTokenCredential() throws IOException {
 
     final MockTokenServerTransport transport = new MockTokenServerTransport();
     transport.addClient(CLIENT_ID, CLIENT_SECRET);
     transport.addRefreshToken(REFRESH_TOKEN, ACCESS_TOKEN);
 
-    JSONObject secretJson = new JSONObject();
+    Map<String, Object> secretJson = new HashMap<>();
     secretJson.put("client_id", CLIENT_ID);
     secretJson.put("client_secret", CLIENT_SECRET);
     secretJson.put("refresh_token", REFRESH_TOKEN);
     secretJson.put("type", "authorized_user");
     InputStream refreshTokenStream =
-        new ByteArrayInputStream(secretJson.toString(0).getBytes("UTF-8"));
+        new ByteArrayInputStream(JSON_FACTORY.toByteArray(secretJson));
 
     return UserCredentials.fromStream(refreshTokenStream, new HttpTransportFactory() {
       @Override
@@ -172,22 +173,20 @@ public class FirebaseAuthTest {
   }
 
   private static FirebaseCredential createFirebaseRefreshTokenCredential()
-      throws JSONException, IOException {
+      throws IOException {
 
     final MockTokenServerTransport transport = new MockTokenServerTransport();
     transport.addClient(CLIENT_ID, CLIENT_SECRET);
     transport.addRefreshToken(REFRESH_TOKEN, ACCESS_TOKEN);
 
-    JSONObject secretJson = new JSONObject();
+    Map<String, Object> secretJson = new HashMap<>();
     secretJson.put("client_id", CLIENT_ID);
     secretJson.put("client_secret", CLIENT_SECRET);
     secretJson.put("refresh_token", REFRESH_TOKEN);
     secretJson.put("type", "authorized_user");
     InputStream refreshTokenStream =
-        new ByteArrayInputStream(secretJson.toString(0).getBytes("UTF-8"));
-
-    return FirebaseCredentials.fromRefreshToken(refreshTokenStream, transport,
-        Utils.getDefaultJsonFactory());
+        new ByteArrayInputStream(JSON_FACTORY.toByteArray(secretJson));
+    return FirebaseCredentials.fromRefreshToken(refreshTokenStream, transport, JSON_FACTORY);
   }
 
   private static GoogleCredentials createCertificateCredential() throws IOException {
@@ -206,7 +205,7 @@ public class FirebaseAuthTest {
     final MockTokenServerTransport transport = new MockTokenServerTransport();
     transport.addServiceAccount(ServiceAccount.EDITOR.getEmail(), ACCESS_TOKEN);
     return FirebaseCredentials.fromCertificate(ServiceAccount.EDITOR.asStream(),
-        transport, Utils.getDefaultJsonFactory());
+        transport, JSON_FACTORY);
   }
 
   @Before
diff --git a/src/test/java/com/google/firebase/auth/FirebaseCredentialsTest.java b/src/test/java/com/google/firebase/auth/FirebaseCredentialsTest.java
index ed2b9da..c384487 100644
--- a/src/test/java/com/google/firebase/auth/FirebaseCredentialsTest.java
+++ b/src/test/java/com/google/firebase/auth/FirebaseCredentialsTest.java
@@ -20,6 +20,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
 
 import com.google.api.client.googleapis.testing.auth.oauth2.MockTokenServerTransport;
 import com.google.api.client.googleapis.util.Utils;
+import com.google.api.client.json.JsonFactory;
 import com.google.auth.oauth2.AccessToken;
 import com.google.auth.oauth2.GoogleCredentials;
 import com.google.common.collect.ImmutableMap;
@@ -37,11 +38,10 @@ import java.io.PrintWriter;
 import java.nio.charset.Charset;
 import java.nio.file.Files;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.atomic.AtomicInteger;
-import org.json.JSONException;
-import org.json.JSONObject;
 import org.junit.Assert;
 import org.junit.Rule;
 import org.junit.Test;
@@ -56,6 +56,8 @@ public class FirebaseCredentialsTest {
   private static final String CLIENT_SECRET = "mockclientsecret";
   private static final String CLIENT_ID = "mockclientid";
   private static final String REFRESH_TOKEN = "mockrefreshtoken";
+  private static final JsonFactory JSON_FACTORY = Utils.getDefaultJsonFactory();
+
   @Rule public final ExpectedException thrown = ExpectedException.none();
 
   @Test(expected = NullPointerException.class)
@@ -195,17 +197,17 @@ public class FirebaseCredentialsTest {
 
   @Test
   public void refreshTokenReadIsDoneSynchronously()
-      throws ExecutionException, InterruptedException, IOException, JSONException {
+      throws ExecutionException, InterruptedException, IOException {
     MockTokenServerTransport transport = new MockTokenServerTransport();
     transport.addClient(CLIENT_ID, CLIENT_SECRET);
     transport.addRefreshToken(REFRESH_TOKEN, ACCESS_TOKEN);
 
-    JSONObject secretJson = new JSONObject();
+    Map<String, Object> secretJson = new HashMap<>();
     secretJson.put("client_id", CLIENT_ID);
     secretJson.put("client_secret", CLIENT_SECRET);
     secretJson.put("refresh_token", REFRESH_TOKEN);
     secretJson.put("type", "authorized_user");
-    InputStream inputStream = new ByteArrayInputStream(secretJson.toString(0).getBytes("UTF-8"));
+    InputStream inputStream = new ByteArrayInputStream(JSON_FACTORY.toByteArray(secretJson));
 
     FirebaseCredential credential =
         FirebaseCredentials.fromRefreshToken(inputStream, transport, Utils.getDefaultJsonFactory());
diff --git a/src/test/java/com/google/firebase/auth/FirebaseUserManagerTest.java b/src/test/java/com/google/firebase/auth/FirebaseUserManagerTest.java
index 5fe5bc8..07c550b 100644
--- a/src/test/java/com/google/firebase/auth/FirebaseUserManagerTest.java
+++ b/src/test/java/com/google/firebase/auth/FirebaseUserManagerTest.java
@@ -158,10 +158,11 @@ public class FirebaseUserManagerTest {
     TestResponseInterceptor interceptor = new TestResponseInterceptor();
     userManager.setInterceptor(interceptor);
 
+    JsonFactory jsonFactory = Utils.getDefaultJsonFactory();
     DownloadAccountResponse download = userManager.listUsers(999, null);
     assertEquals(2, download.getUsers().size());
     for (User user : download.getUsers()) {
-      ExportedUserRecord userRecord = new ExportedUserRecord(user);
+      ExportedUserRecord userRecord = new ExportedUserRecord(user, jsonFactory);
       checkUserRecord(userRecord);
       assertEquals("passwordHash", userRecord.getPasswordHash());
       assertEquals("passwordSalt", userRecord.getPasswordSalt());
@@ -169,7 +170,6 @@ public class FirebaseUserManagerTest {
     assertNull(download.getPageToken());
     checkRequestHeaders(interceptor);
 
-    JsonFactory jsonFactory = Utils.getDefaultJsonFactory();
     ByteArrayOutputStream out = new ByteArrayOutputStream();
     interceptor.response.getRequest().getContent().writeTo(out);
     GenericJson parsed = jsonFactory.fromString(new String(out.toByteArray()), GenericJson.class);
@@ -188,10 +188,11 @@ public class FirebaseUserManagerTest {
     TestResponseInterceptor interceptor = new TestResponseInterceptor();
     userManager.setInterceptor(interceptor);
 
+    JsonFactory jsonFactory = Utils.getDefaultJsonFactory();
     DownloadAccountResponse download = userManager.listUsers(999, "token");
     assertEquals(2, download.getUsers().size());
     for (User user : download.getUsers()) {
-      ExportedUserRecord userRecord = new ExportedUserRecord(user);
+      ExportedUserRecord userRecord = new ExportedUserRecord(user, jsonFactory);
       checkUserRecord(userRecord);
       assertEquals("passwordHash", userRecord.getPasswordHash());
       assertEquals("passwordSalt", userRecord.getPasswordSalt());
@@ -199,7 +200,6 @@ public class FirebaseUserManagerTest {
     assertNull(download.getPageToken());
     checkRequestHeaders(interceptor);
 
-    JsonFactory jsonFactory = Utils.getDefaultJsonFactory();
     ByteArrayOutputStream out = new ByteArrayOutputStream();
     interceptor.response.getRequest().getContent().writeTo(out);
     GenericJson parsed = jsonFactory.fromString(new String(out.toByteArray()), GenericJson.class);
diff --git a/src/test/java/com/google/firebase/auth/ListUsersPageTest.java b/src/test/java/com/google/firebase/auth/ListUsersPageTest.java
index e70c414..449afc1 100644
--- a/src/test/java/com/google/firebase/auth/ListUsersPageTest.java
+++ b/src/test/java/com/google/firebase/auth/ListUsersPageTest.java
@@ -323,7 +323,7 @@ public class ListUsersPageTest {
     JsonFactory jsonFactory = Utils.getDefaultJsonFactory();
     DownloadAccountResponse.User parsed = jsonFactory.fromString(
         String.format("{\"localId\":\"%s\"}", uid), DownloadAccountResponse.User.class);
-    return new ExportedUserRecord(parsed);
+    return new ExportedUserRecord(parsed, jsonFactory);
   }
 
   private static class TestUserSource implements ListUsersPage.UserSource {
diff --git a/src/test/java/com/google/firebase/auth/UserRecordTest.java b/src/test/java/com/google/firebase/auth/UserRecordTest.java
index a849465..ce187a4 100644
--- a/src/test/java/com/google/firebase/auth/UserRecordTest.java
+++ b/src/test/java/com/google/firebase/auth/UserRecordTest.java
@@ -24,7 +24,7 @@ public class UserRecordTest {
 
   @Test(expected = NullPointerException.class)
   public void testNullResponse() {
-    new UserRecord(null);
+    new UserRecord(null, JSON_FACTORY);
   }
 
   @Test
@@ -184,14 +184,13 @@ public class UserRecordTest {
     InputStream stream = new ByteArrayInputStream(json.getBytes(Charset.defaultCharset()));
     GetAccountInfoResponse.User user = JSON_FACTORY.createJsonObjectParser()
         .parseAndClose(stream, Charset.defaultCharset(), GetAccountInfoResponse.User.class);
-    return new UserRecord(user);
+    return new UserRecord(user, JSON_FACTORY);
   }
 
   private ExportedUserRecord parseExportedUser(String json) throws IOException {
     InputStream stream = new ByteArrayInputStream(json.getBytes(Charset.defaultCharset()));
     DownloadAccountResponse.User user = JSON_FACTORY.createJsonObjectParser()
-        .parseAndClose(stream, Charset.defaultCharset(),
-            DownloadAccountResponse.User.class);
-    return new ExportedUserRecord(user);
+        .parseAndClose(stream, Charset.defaultCharset(), DownloadAccountResponse.User.class);
+    return new ExportedUserRecord(user, JSON_FACTORY);
   }
 }