Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
fd3dd9d
Adding the sync APIs for FirebaseAuth
hiranya911 Feb 22, 2018
ac3453b
Added more tests; Added sync APIs for FirebaseMessaging
hiranya911 Feb 22, 2018
ded167e
Removing Task references from database, iid and fcm APIs
hiranya911 Feb 22, 2018
765f209
Fixing a typo
hiranya911 Feb 26, 2018
01ce79e
Minor code clean up
hiranya911 Mar 1, 2018
3913688
Merged with master (auth unit test improvements)
hiranya911 Mar 2, 2018
d5ec1c3
Updated javadocs; Renamed internal helpers of FirebaseMessaging for c…
hiranya911 Mar 8, 2018
f8f33b4
Removed the deprecated FirebaseCredential API (#149)
hiranya911 Mar 9, 2018
27eddb6
Removing the Task API (#152)
hiranya911 Mar 12, 2018
edd825e
Dropping Support for App Engine Java 7 Runtime (#153)
hiranya911 Mar 20, 2018
559ce44
Removing Deprecated LogWrapper API (#154)
hiranya911 Mar 23, 2018
3979c02
Merged with master
hiranya911 Mar 29, 2018
ef036bb
merged with master
hiranya911 Apr 14, 2018
0523bf3
Initial code for the importUsers() API
hiranya911 Apr 18, 2018
0db88c5
Added documentation and more tests
hiranya911 Apr 19, 2018
fe3cc03
Added integration tests; Scrypt hash; more documentation
hiranya911 Apr 19, 2018
46e51ab
Added more tests
hiranya911 Apr 19, 2018
5ede505
Merge branch 'master' into v6
hiranya911 Apr 19, 2018
efc9016
updated test
hiranya911 Apr 24, 2018
dbc1492
Merged with master
hiranya911 May 3, 2018
0633d37
Merged with v6
hiranya911 May 4, 2018
2514703
Adding other hash types
hiranya911 May 4, 2018
ebe905c
Added the remaining hash algorithms
hiranya911 May 4, 2018
1182576
Renamed to ImportUserRecord
hiranya911 May 4, 2018
421a86c
Updated documentation
hiranya911 May 5, 2018
7b085df
Merged with master
hiranya911 May 8, 2018
501faf8
Some minor cleanup
hiranya911 May 8, 2018
dccc454
Updated documentation
hiranya911 May 11, 2018
a4cf116
Updated documentation; Made hash required in UserImportOptions; Other…
hiranya911 May 17, 2018
9130ab2
Renamed BasicHash to RepeatableHash; Made rounds range test configurable
hiranya911 May 22, 2018
3f30183
Added javadoc comment
hiranya911 May 22, 2018
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
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Unreleased

-
- [added] Added new `importUsersAsync()` API for bulk importing users
into Firebase Auth.

# v6.0.0

Expand Down
52 changes: 52 additions & 0 deletions src/main/java/com/google/firebase/auth/ErrorInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright 2018 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.firebase.auth;

import java.util.List;

/**
* Represents an error encountered while importing an {@link ImportUserRecord}.
*/
public final class ErrorInfo {

private final int index;
private final String reason;

ErrorInfo(int index, String reason) {
this.index = index;
this.reason = reason;
}

/**
* The index of the failed user in the list passed to the
* {@link FirebaseAuth#importUsersAsync(List, UserImportOptions)} method.
*
* @return an integer index.
*/
public int getIndex() {
return index;
}

/**
* A string describing the error.
*
* @return A string error message.
*/
public String getReason() {
return reason;
}
}
82 changes: 82 additions & 0 deletions src/main/java/com/google/firebase/auth/FirebaseAuth.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import com.google.common.base.Strings;
import com.google.firebase.FirebaseApp;
import com.google.firebase.ImplFirebaseTrampolines;
import com.google.firebase.auth.FirebaseUserManager.UserImportRequest;
import com.google.firebase.auth.ListUsersPage.DefaultUserSource;
import com.google.firebase.auth.ListUsersPage.PageFactory;
import com.google.firebase.auth.UserRecord.CreateRequest;
Expand All @@ -42,6 +43,7 @@
import com.google.firebase.internal.Nullable;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

Expand Down Expand Up @@ -852,6 +854,86 @@ protected Void execute() throws FirebaseAuthException {
};
}

/**
* Imports the provided list of users into Firebase Auth. At most 1000 users can be imported at a
* time. This operation is optimized for bulk imports and will ignore checks on identifier
* uniqueness which could result in duplications.
*
* <p>{@link UserImportOptions} is required to import users with passwords. See
* {@link #importUsers(List, UserImportOptions)}.
*
* @param users A non-empty list of users to be imported. Length must not exceed 1000.
* @return A {@link UserImportResult} instance.
* @throws IllegalArgumentException If the users list is null, empty or has more than 1000
* elements. Or if at least one user specifies a password.
* @throws FirebaseAuthException If an error occurs while importing users.
*/
public UserImportResult importUsers(List<ImportUserRecord> users) throws FirebaseAuthException {
return importUsers(users, null);
}

/**
* Imports the provided list of users into Firebase Auth. At most 1000 users can be imported at a
* time. This operation is optimized for bulk imports and will ignore checks on identifier
* uniqueness which could result in duplications.
*
* @param users A non-empty list of users to be imported. Length must not exceed 1000.
* @param options a {@link UserImportOptions} instance or null. Required when importing users
* with passwords.
* @return A {@link UserImportResult} instance.
* @throws IllegalArgumentException If the users list is null, empty or has more than 1000
* elements. Or if at least one user specifies a password, and options is null.
* @throws FirebaseAuthException If an error occurs while importing users.
*/
public UserImportResult importUsers(List<ImportUserRecord> users,
@Nullable UserImportOptions options) throws FirebaseAuthException {
return importUsersOp(users, options).call();
}

/**
* Similar to {@link #importUsers(List)} but performs the operation asynchronously.
*
* @param users A non-empty list of users to be imported. Length must not exceed 1000.
* @return An {@code ApiFuture} which will complete successfully when the user accounts are
* imported. If an error occurs while importing the users, the future throws a
* {@link FirebaseAuthException}.
* @throws IllegalArgumentException If the users list is null, empty or has more than 1000
* elements. Or if at least one user specifies a password.
*/
public ApiFuture<UserImportResult> importUsersAsync(List<ImportUserRecord> users) {
return importUsersAsync(users, null);
}

/**
* Similar to {@link #importUsers(List, UserImportOptions)} but performs the operation
* asynchronously.
*
* @param users A non-empty list of users to be imported. Length must not exceed 1000.
* @param options a {@link UserImportOptions} instance or null. Required when importing users
* with passwords.
* @return An {@code ApiFuture} which will complete successfully when the user accounts are
* imported. If an error occurs while importing the users, the future throws a
* {@link FirebaseAuthException}.
* @throws IllegalArgumentException If the users list is null, empty or has more than 1000
* elements. Or if at least one user specifies a password, and options is null.
*/
public ApiFuture<UserImportResult> importUsersAsync(List<ImportUserRecord> users,
@Nullable UserImportOptions options) {
return importUsersOp(users, options).callAsync(firebaseApp);
}

private CallableOperation<UserImportResult, FirebaseAuthException> importUsersOp(
List<ImportUserRecord> users, UserImportOptions options) {
checkNotDestroyed();
final UserImportRequest request = new UserImportRequest(users, options, jsonFactory);
return new CallableOperation<UserImportResult, FirebaseAuthException>() {
@Override
protected UserImportResult execute() throws FirebaseAuthException {
return userManager.importUsers(request);
}
};
}

@VisibleForTesting
FirebaseUserManager getUserManager() {
return this.userManager;
Expand Down
46 changes: 46 additions & 0 deletions src/main/java/com/google/firebase/auth/FirebaseUserManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import com.google.api.client.json.GenericJson;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.JsonObjectParser;
import com.google.api.client.util.Key;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
Expand All @@ -41,6 +42,7 @@
import com.google.firebase.auth.internal.GetAccountInfoResponse;

import com.google.firebase.auth.internal.HttpErrorResponse;
import com.google.firebase.auth.internal.UploadAccountResponse;
import com.google.firebase.internal.FirebaseRequestInitializer;
import com.google.firebase.internal.NonNull;
import com.google.firebase.internal.SdkUtils;
Expand Down Expand Up @@ -82,6 +84,7 @@ class FirebaseUserManager {
.build();

static final int MAX_LIST_USERS_RESULTS = 1000;
static final int MAX_IMPORT_USERS = 1000;

static final List<String> RESERVED_CLAIMS = ImmutableList.of(
"amr", "at_hash", "aud", "auth_time", "azp", "cnf", "c_hash", "exp", "iat",
Expand Down Expand Up @@ -195,6 +198,15 @@ DownloadAccountResponse listUsers(int maxResults, String pageToken) throws Fireb
return response;
}

UserImportResult importUsers(UserImportRequest request) throws FirebaseAuthException {
checkNotNull(request);
UploadAccountResponse response = post("uploadAccount", request, UploadAccountResponse.class);
if (response == null) {
throw new FirebaseAuthException(INTERNAL_ERROR, "Failed to import users.");
}
return new UserImportResult(request.getUsersCount(), response);
}

String createSessionCookie(String idToken,
SessionCookieOptions options) throws FirebaseAuthException {
final Map<String, Object> payload = ImmutableMap.<String, Object>of(
Expand Down Expand Up @@ -257,4 +269,38 @@ private void handleHttpError(HttpResponseException e) throws FirebaseAuthExcepti
"Unexpected HTTP response with status: %d; body: %s", e.getStatusCode(), e.getContent());
throw new FirebaseAuthException(INTERNAL_ERROR, msg, e);
}

static class UserImportRequest extends GenericJson {

@Key("users")
private final List<Map<String, Object>> users;

UserImportRequest(List<ImportUserRecord> users, UserImportOptions options,
JsonFactory jsonFactory) {
checkArgument(users != null && !users.isEmpty(), "users must not be null or empty");
checkArgument(users.size() <= FirebaseUserManager.MAX_IMPORT_USERS,
"users list must not contain more than %s items", FirebaseUserManager.MAX_IMPORT_USERS);

boolean hasPassword = false;
ImmutableList.Builder<Map<String, Object>> usersBuilder = ImmutableList.builder();
for (ImportUserRecord user : users) {
if (user.hasPassword()) {
hasPassword = true;
}
usersBuilder.add(user.getProperties(jsonFactory));
}
this.users = usersBuilder.build();

if (hasPassword) {
checkArgument(options != null && options.getHash() != null,
"UserImportHash option is required when at least one user has a password. Provide "
+ "a UserImportHash via UserImportOptions.withHash().");
this.putAll(options.getProperties());
}
}

int getUsersCount() {
return users.size();
}
}
}
Loading