Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,13 @@ Create a new project in the [Firebase console](https://console.firebase.google.c
not already have one. Use a separate, dedicated project for integration tests since the test suite
makes a large number of writes to the Firebase realtime database. Download the service account
private key from the "Settings > Service Accounts" page of the project, and save it as
`integration_cert.json` at the root of the codebase. Also obtain the web API key of the project
from the "Settings > General" page, and save it as `integration_apikey.txt` at the root of the
codebase. Now run the following command to invoke the integration test suite:
`integration_cert.json` at the root of the codebase. Grant your service account the `Firebase
Authentication Admin` role at
[Google Cloud Platform Console / IAM & admin](https://console.cloud.google.com/iam-admin). This is
required to ensure that exported user records contain the password hashes of the user accounts.
Also obtain the web API key of the project from the "Settings > General" page, and save it as
`integration_apikey.txt` at the root of the codebase. Now run the following command to invoke the
integration test suite:

```
mvn verify
Expand Down
10 changes: 9 additions & 1 deletion src/main/java/com/google/firebase/auth/ExportedUserRecord.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.google.firebase.auth;

import com.google.api.client.json.JsonFactory;
import com.google.common.io.BaseEncoding;
import com.google.firebase.auth.internal.DownloadAccountResponse.User;
import com.google.firebase.internal.Nullable;

Expand All @@ -28,10 +29,17 @@ public class ExportedUserRecord extends UserRecord {

private final String passwordHash;
private final String passwordSalt;
private static final String REDACTED_BASE64 = BaseEncoding.base64Url().encode(
"REDACTED".getBytes());

ExportedUserRecord(User response, JsonFactory jsonFactory) {
super(response, jsonFactory);
this.passwordHash = response.getPasswordHash();
String passwordHash = response.getPasswordHash();
if (passwordHash != null && !passwordHash.equals(REDACTED_BASE64)) {
this.passwordHash = passwordHash;
} else {
this.passwordHash = null;
}
this.passwordSalt = response.getPasswordSalt();
}

Expand Down
4 changes: 3 additions & 1 deletion src/test/java/com/google/firebase/auth/FirebaseAuthIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,9 @@ public void testListUsers() throws Exception {
for (ExportedUserRecord user : page.getValues()) {
if (uids.contains(user.getUid())) {
collected.incrementAndGet();
assertNotNull(user.getPasswordHash());
assertNotNull("Missing passwordHash field. A common cause would be "
+ "forgetting to add the \"Firebase Authentication Admin\" permission. See "
+ "instructions in CONTRIBUTING.md", user.getPasswordHash());
assertNotNull(user.getPasswordSalt());
}
}
Expand Down
36 changes: 36 additions & 0 deletions src/test/java/com/google/firebase/auth/ListUsersPageTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import com.google.api.client.googleapis.util.Utils;
import com.google.api.client.json.JsonFactory;
import com.google.common.collect.ImmutableList;
import com.google.common.io.BaseEncoding;
import com.google.firebase.auth.ListUsersPage.ListUsersResult;
import com.google.firebase.auth.internal.DownloadAccountResponse;
import java.io.IOException;
Expand All @@ -38,6 +39,9 @@

public class ListUsersPageTest {

private static final String REDACTED_BASE64 = BaseEncoding.base64Url().encode(
"REDACTED".getBytes());

@Test
public void testSinglePage() throws FirebaseAuthException, IOException {
TestUserSource source = new TestUserSource(3);
Expand All @@ -55,6 +59,30 @@ public void testSinglePage() throws FirebaseAuthException, IOException {
assertNull(source.calls.get(0));
}

@Test
public void testRedactedPasswords() throws FirebaseAuthException, IOException {
ListUsersResult result = new ListUsersResult(
ImmutableList.of(
newUser("user0", REDACTED_BASE64),
newUser("user1", REDACTED_BASE64),
newUser("user2", REDACTED_BASE64)),
ListUsersPage.END_OF_LIST);
TestUserSource source = new TestUserSource(result);
ListUsersPage page = new ListUsersPage.PageFactory(source).create();
assertFalse(page.hasNextPage());
assertEquals(ListUsersPage.END_OF_LIST, page.getNextPageToken());
assertNull(page.getNextPage());

ImmutableList<ExportedUserRecord> users = ImmutableList.copyOf(page.getValues());
assertEquals(3, users.size());
for (int i = 0; i < 3; i++) {
assertEquals("user" + i, users.get(i).getUid());
assertNull(users.get(i).getPasswordHash());
}
assertEquals(1, source.calls.size());
assertNull(source.calls.get(0));
}

@Test
public void testMultiplePages() throws FirebaseAuthException, IOException {
ListUsersResult result = new ListUsersResult(
Expand Down Expand Up @@ -326,6 +354,14 @@ private static ExportedUserRecord newUser(String uid) throws IOException {
return new ExportedUserRecord(parsed, jsonFactory);
}

private static ExportedUserRecord newUser(String uid, String passwordHash) throws IOException {
JsonFactory jsonFactory = Utils.getDefaultJsonFactory();
DownloadAccountResponse.User parsed = jsonFactory.fromString(
String.format("{\"localId\":\"%s\", \"passwordHash\":\"%s\"}", uid, passwordHash),
DownloadAccountResponse.User.class);
return new ExportedUserRecord(parsed, jsonFactory);
}

private static class TestUserSource implements ListUsersPage.UserSource {

private ListUsersResult result;
Expand Down