diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index c346c5b9d..62a194733 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -141,6 +141,10 @@ 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
+ <<<<<<< redacted-passwords
+`integration_apikey.txt` at the root of the codebase. Now run the following command to invoke the
+integration test suite:
+ =======
`integration_apikey.txt` at the root of the codebase.
Some of the integration tests require an
@@ -152,6 +156,7 @@ functionality via the
charges may be incurred for active users beyond the Identity Platform free tier.
Now run the following command to invoke the integration test suite:
+ >>>>>>> master
```
mvn verify
diff --git a/README.md b/README.md
index 406223d06..95f2d8d04 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,20 @@
+ <<<<<<< revert-555-hkj-release-730
+ <<<<<<< revert-555-hkj-release-730
[](https://github.com/firebase/firebase-admin-java/actions)
+ =======
+test move pull request
+
+[](https://travis-ci.org/firebase/firebase-admin-java)
+ >>>>>>> test-qiao-public
+ =======
+ <<<<<<< test-yue-public
+test new moving process
+
+[](https://travis-ci.org/firebase/firebase-admin-java)
+ =======
+[](https://github.com/firebase/firebase-admin-java/actions)
+ >>>>>>> master
+ >>>>>>> master
[](https://maven-badges.herokuapp.com/maven-central/com.google.firebase/firebase-admin)
[](https://firebase.google.com/docs/reference/admin/java/reference/packages)
diff --git a/pom.xml b/pom.xml
index 5e206c322..1e20edbd4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -19,7 +19,15 @@
com.google.firebasefirebase-admin
+ <<<<<<< revert-555-hkj-release-730
+ 7.2.0
+ =======
+ <<<<<<< redacted-passwords
+ 6.14.0
+ =======
7.3.0
+ >>>>>>> master
+ >>>>>>> master
jarfirebase-admin
@@ -59,7 +67,11 @@
UTF-8UTF-8${skipTests}
+ <<<<<<< redacted-passwords
+ 4.1.50.Final
+ =======
4.1.65.Final
+ >>>>>>> master
@@ -99,7 +111,11 @@
maven-javadoc-plugin
- 3.2.0
+ <<<<<<< redacted-passwords
+ 2.10.4
+ =======
+ 3.3.0
+ >>>>>>> master
site
@@ -257,7 +273,7 @@
maven-checkstyle-plugin
- 2.17
+ 3.1.2validate
@@ -298,7 +314,11 @@
maven-source-plugin
+ <<<<<<< redacted-passwords
2.2.1
+ =======
+ 3.2.1
+ >>>>>>> master
attach-sources
@@ -310,7 +330,11 @@
maven-javadoc-plugin
- 3.2.0
+ <<<<<<< redacted-passwords
+ 2.10.4
+ =======
+ 3.3.0
+ >>>>>>> master
attach-javadocs
@@ -345,14 +369,22 @@
maven-failsafe-plugin
+ <<<<<<< redacted-passwords
+ 2.19.1
+ =======
2.22.2
+ >>>>>>> master
integration-testverify
+ <<<<<<< redacted-passwords
+
+ =======
+ >>>>>>> master
@@ -393,7 +425,7 @@
com.google.cloudlibraries-bom
- 20.4.0
+ 20.5.0pomimport
@@ -412,30 +444,58 @@
com.google.api-clientgoogle-api-client
+ <<<<<<< redacted-passwords
+ 1.30.9
+ =======
+ >>>>>>> master
com.google.api-clientgoogle-api-client-gson
+ <<<<<<< redacted-passwords
+ 1.30.9
+ =======
+ >>>>>>> master
com.google.http-clientgoogle-http-client
+ <<<<<<< redacted-passwords
+ 1.35.0
+ =======
+ >>>>>>> master
com.google.apiapi-common
+ <<<<<<< redacted-passwords
+ 1.9.2
+ =======
+ >>>>>>> master
com.google.authgoogle-auth-library-oauth2-http
+ <<<<<<< redacted-passwords
+ 0.20.0
+ =======
+ >>>>>>> master
com.google.cloudgoogle-cloud-storage
+ <<<<<<< redacted-passwords
+ 1.108.0
+ =======
+ >>>>>>> master
com.google.cloudgoogle-cloud-firestore
+ <<<<<<< redacted-passwords
+ 1.34.0
+ =======
+ >>>>>>> master
@@ -468,7 +528,7 @@
org.mockitomockito-core
- 2.28.2
+ 3.10.0test
diff --git a/src/main/java/com/google/firebase/ErrorCode.java b/src/main/java/com/google/firebase/ErrorCode.java
index 0eaa9cec2..c24745c3a 100644
--- a/src/main/java/com/google/firebase/ErrorCode.java
+++ b/src/main/java/com/google/firebase/ErrorCode.java
@@ -1,5 +1,9 @@
/*
- * Copyright 2020 Google Inc.
+ <<<<<<< hkj-error-handling
+ * Copyright 2019 Google Inc.
+ =======
+ * Copyright 2021 Google Inc.
+ >>>>>>> master
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,6 +20,40 @@
package com.google.firebase;
+ <<<<<<< hkj-error-handling
+public enum ErrorCode {
+
+ INVALID_ARGUMENT,
+
+ FAILED_PRECONDITION,
+
+ OUT_OF_RANGE,
+
+ UNAUTHENTICATED,
+
+ PERMISSION_DENIED,
+
+ NOT_FOUND,
+
+ CONFLICT,
+
+ ABORTED,
+
+ ALREADY_EXISTS,
+
+ RESOURCE_EXHAUSTED,
+
+ CANCELLED,
+
+ DATA_LOSS,
+
+ UNKNOWN,
+
+ INTERNAL,
+
+ UNAVAILABLE,
+
+ =======
/**
* Platform-wide error codes that can be raised by Admin SDK APIs.
*/
@@ -105,5 +143,6 @@ public enum ErrorCode {
* shorter than the method's default deadline (i.e. requested deadline is not enough for the
* server to process the request) and the request did not finish within the deadline.
*/
+ >>>>>>> master
DEADLINE_EXCEEDED,
}
diff --git a/src/main/java/com/google/firebase/FirebaseApp.java b/src/main/java/com/google/firebase/FirebaseApp.java
index d525d0673..832af7665 100644
--- a/src/main/java/com/google/firebase/FirebaseApp.java
+++ b/src/main/java/com/google/firebase/FirebaseApp.java
@@ -34,7 +34,10 @@
import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
+ <<<<<<< v7
+ =======
import com.google.firebase.internal.ApiClientUtils;
+ >>>>>>> master
import com.google.firebase.internal.FirebaseScheduledExecutor;
import com.google.firebase.internal.FirebaseService;
import com.google.firebase.internal.ListenableFuture2ApiFuture;
@@ -316,10 +319,17 @@ public String toString() {
}
/**
+ <<<<<<< redacted-passwords
+ * Deletes this {@link FirebaseApp} object and releases any local state and managed resources
+ * associated with it. All calls to this {@link FirebaseApp} instance will throw once this method
+ * has been called. This also releases any managed resources allocated by other services
+ * (e.g. {@code FirebaseAuth}, {@code FirebaseDatabase}) attached to this object instance.
+ =======
* Deletes this {@link FirebaseApp} object, and releases any local state and managed resources
* associated with it. All calls to this {@link FirebaseApp} instance will throw once this method
* has been called. This also releases any managed resources allocated by other services
* attached to this object instance (e.g. {@code FirebaseAuth}).
+ >>>>>>> master
*
*
A no-op if delete was called before.
*/
@@ -569,7 +579,11 @@ private static FirebaseOptions getOptionsFromEnvironment() throws IOException {
.setCredentials(APPLICATION_DEFAULT_CREDENTIALS)
.build();
}
+ <<<<<<< v7
+ JsonFactory jsonFactory = Utils.getDefaultJsonFactory();
+ =======
JsonFactory jsonFactory = ApiClientUtils.getDefaultJsonFactory();
+ >>>>>>> master
FirebaseOptions.Builder builder = FirebaseOptions.builder();
JsonParser parser;
if (defaultConfig.startsWith("{")) {
diff --git a/src/main/java/com/google/firebase/FirebaseException.java b/src/main/java/com/google/firebase/FirebaseException.java
index a5bb80424..e5c82b4ff 100644
--- a/src/main/java/com/google/firebase/FirebaseException.java
+++ b/src/main/java/com/google/firebase/FirebaseException.java
@@ -29,6 +29,35 @@
public class FirebaseException extends Exception {
private final ErrorCode errorCode;
+ <<<<<<< hkj-error-handling
+ private final FirebaseHttpResponse response;
+
+ public FirebaseException(@NonNull String detailMessage) {
+ this(detailMessage, null);
+ }
+
+ public FirebaseException(@NonNull String detailMessage, Throwable cause) {
+ this(ErrorCode.UNKNOWN, detailMessage, null, cause);
+ }
+
+ public FirebaseException(
+ @NonNull ErrorCode errorCode,
+ @NonNull String message,
+ @Nullable FirebaseHttpResponse response,
+ @Nullable Throwable cause) {
+ super(message, cause);
+ checkArgument(!Strings.isNullOrEmpty(message), "Message must not be null or empty");
+ this.errorCode = checkNotNull(errorCode);
+ this.response = response;
+ }
+
+ public ErrorCode getCode() {
+ return errorCode;
+ }
+
+ @Nullable public FirebaseHttpResponse getHttpResponse() {
+ return response;
+ =======
private final IncomingHttpResponse httpResponse;
public FirebaseException(
@@ -67,5 +96,6 @@ public final ErrorCode getErrorCode() {
@Nullable
public final IncomingHttpResponse getHttpResponse() {
return httpResponse;
+ >>>>>>> master
}
}
diff --git a/src/main/java/com/google/firebase/FirebaseHttpRequest.java b/src/main/java/com/google/firebase/FirebaseHttpRequest.java
new file mode 100644
index 000000000..217c8ca9e
--- /dev/null
+++ b/src/main/java/com/google/firebase/FirebaseHttpRequest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2019 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;
+
+import com.google.api.client.http.HttpContent;
+import com.google.api.client.http.HttpRequest;
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+
+public final class FirebaseHttpRequest {
+
+ private final String method;
+ private final String url;
+ private final HttpContent content;
+ private final Map headers;
+
+ public FirebaseHttpRequest(String method, String url) {
+ this.method = method;
+ this.url = url;
+ this.content = null;
+ this.headers = ImmutableMap.of();
+ }
+
+ FirebaseHttpRequest(HttpRequest request) {
+ this.method = request.getRequestMethod();
+ this.url = request.getUrl().toString();
+ this.content = request.getContent();
+ this.headers = ImmutableMap.copyOf(request.getHeaders());
+ }
+
+ public String getMethod() {
+ return method;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public HttpContent getContent() {
+ return content;
+ }
+
+ public Map getHeaders() {
+ return headers;
+ }
+}
diff --git a/src/main/java/com/google/firebase/FirebaseHttpResponse.java b/src/main/java/com/google/firebase/FirebaseHttpResponse.java
new file mode 100644
index 000000000..d2961ee0b
--- /dev/null
+++ b/src/main/java/com/google/firebase/FirebaseHttpResponse.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2019 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;
+
+import com.google.api.client.http.HttpRequest;
+import com.google.api.client.http.HttpResponse;
+import com.google.api.client.http.HttpResponseException;
+import com.google.common.collect.ImmutableMap;
+import java.util.Map;
+
+public final class FirebaseHttpResponse {
+
+ private final int statusCode;
+ private final String content;
+ private final Map headers;
+ private final FirebaseHttpRequest request;
+
+ public FirebaseHttpResponse(HttpResponse response, String content) {
+ this.statusCode = response.getStatusCode();
+ this.content = content;
+ this.headers = ImmutableMap.copyOf(response.getHeaders());
+ this.request = new FirebaseHttpRequest(response.getRequest());
+ }
+
+ public FirebaseHttpResponse(HttpResponseException e, HttpRequest request) {
+ this(e, new FirebaseHttpRequest(request));
+ }
+
+ public FirebaseHttpResponse(HttpResponseException e, FirebaseHttpRequest request) {
+ this.statusCode = e.getStatusCode();
+ this.content = e.getContent();
+ this.headers = ImmutableMap.copyOf(e.getHeaders());
+ this.request = request;
+ }
+
+ public int getStatusCode() {
+ return this.statusCode;
+ }
+
+ public String getContent() {
+ return this.content;
+ }
+
+ public Map getHeaders() {
+ return this.headers;
+ }
+
+ public FirebaseHttpRequest getRequest() {
+ return request;
+ }
+}
diff --git a/src/main/java/com/google/firebase/FirebaseOptions.java b/src/main/java/com/google/firebase/FirebaseOptions.java
index f13f022db..de6a3fba4 100644
--- a/src/main/java/com/google/firebase/FirebaseOptions.java
+++ b/src/main/java/com/google/firebase/FirebaseOptions.java
@@ -234,7 +234,11 @@ public Builder toBuilder() {
}
/**
+ <<<<<<< v7
+ * Builder for constructing {@link FirebaseOptions}.
+ =======
* Builder for constructing {@link FirebaseOptions}.
+ >>>>>>> master
*/
public static final class Builder {
@Key("databaseAuthVariableOverride")
diff --git a/src/main/java/com/google/firebase/auth/AuthErrorCode.java b/src/main/java/com/google/firebase/auth/AuthErrorCode.java
index aa2821475..f558c4943 100644
--- a/src/main/java/com/google/firebase/auth/AuthErrorCode.java
+++ b/src/main/java/com/google/firebase/auth/AuthErrorCode.java
@@ -37,6 +37,8 @@ public enum AuthErrorCode {
EMAIL_ALREADY_EXISTS,
/**
+ <<<<<<< v7
+ =======
* No user record found for the given email, typically raised when
* generating a password reset link using an email for a user that
* is not already registered.
@@ -44,6 +46,7 @@ public enum AuthErrorCode {
EMAIL_NOT_FOUND,
/**
+ >>>>>>> master
* The specified ID token is expired.
*/
EXPIRED_ID_TOKEN,
diff --git a/src/main/java/com/google/firebase/auth/FirebaseAuth.java b/src/main/java/com/google/firebase/auth/FirebaseAuth.java
index 27e79960d..5246ae782 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseAuth.java
+++ b/src/main/java/com/google/firebase/auth/FirebaseAuth.java
@@ -16,11 +16,34 @@
package com.google.firebase.auth;
+ <<<<<<< redacted-passwords
+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.client.util.Clock;
+import com.google.api.core.ApiFuture;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Strings;
+ =======
+ >>>>>>> master
import com.google.common.base.Supplier;
import com.google.firebase.FirebaseApp;
import com.google.firebase.ImplFirebaseTrampolines;
import com.google.firebase.auth.multitenancy.TenantManager;
import com.google.firebase.internal.FirebaseService;
+ <<<<<<< redacted-passwords
+import com.google.firebase.internal.NonNull;
+import com.google.firebase.internal.Nullable;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+ =======
+ >>>>>>> master
/**
* This class is the entry point for all server-side Firebase Authentication actions.
@@ -34,15 +57,31 @@ public final class FirebaseAuth extends AbstractFirebaseAuth {
private static final String SERVICE_ID = FirebaseAuth.class.getName();
+ <<<<<<< redacted-passwords
+ private static final String ERROR_CUSTOM_TOKEN = "ERROR_CUSTOM_TOKEN";
+
+ private final Object lock = new Object();
+ =======
private final Supplier tenantManager;
+ >>>>>>> master
private FirebaseAuth(final Builder builder) {
super(builder);
tenantManager = threadSafeMemoize(builder.tenantManager);
}
+ <<<<<<< redacted-passwords
+ private FirebaseAuth(Builder builder) {
+ this.firebaseApp = checkNotNull(builder.firebaseApp);
+ this.tokenFactory = threadSafeMemoize(builder.tokenFactory);
+ this.idTokenVerifier = threadSafeMemoize(builder.idTokenVerifier);
+ this.cookieVerifier = threadSafeMemoize(builder.cookieVerifier);
+ this.userManager = threadSafeMemoize(builder.userManager);
+ this.jsonFactory = firebaseApp.getOptions().getJsonFactory();
+ =======
public TenantManager getTenantManager() {
return tenantManager.get();
+ >>>>>>> master
}
/**
@@ -69,6 +108,1138 @@ public static synchronized FirebaseAuth getInstance(FirebaseApp app) {
return service.getInstance();
}
+ <<<<<<< redacted-passwords
+ /**
+ * Creates a new Firebase session cookie from the given ID token and options. The returned JWT
+ * can be set as a server-side session cookie with a custom cookie policy.
+ *
+ * @param idToken The Firebase ID token to exchange for a session cookie.
+ * @param options Additional options required to create the cookie.
+ * @return A Firebase session cookie string.
+ * @throws IllegalArgumentException If the ID token is null or empty, or if options is null.
+ * @throws FirebaseAuthException If an error occurs while generating the session cookie.
+ */
+ public String createSessionCookie(
+ @NonNull String idToken, @NonNull SessionCookieOptions options) throws FirebaseAuthException {
+ return createSessionCookieOp(idToken, options).call();
+ }
+
+ /**
+ * Similar to {@link #createSessionCookie(String, SessionCookieOptions)} but performs the
+ * operation asynchronously.
+ *
+ * @param idToken The Firebase ID token to exchange for a session cookie.
+ * @param options Additional options required to create the cookie.
+ * @return An {@code ApiFuture} which will complete successfully with a session cookie string.
+ * If an error occurs while generating the cookie or if the specified ID token is invalid,
+ * the future throws a {@link FirebaseAuthException}.
+ * @throws IllegalArgumentException If the ID token is null or empty, or if options is null.
+ */
+ public ApiFuture createSessionCookieAsync(
+ @NonNull String idToken, @NonNull SessionCookieOptions options) {
+ return createSessionCookieOp(idToken, options).callAsync(firebaseApp);
+ }
+
+ private CallableOperation createSessionCookieOp(
+ final String idToken, final SessionCookieOptions options) {
+ checkArgument(!Strings.isNullOrEmpty(idToken), "idToken must not be null or empty");
+ checkNotNull(options, "options must not be null");
+ final FirebaseUserManager userManager = getUserManager();
+ return new CallableOperation() {
+ @Override
+ protected String execute() throws FirebaseAuthException {
+ return userManager.createSessionCookie(idToken, options);
+ }
+ };
+ }
+
+ /**
+ * Parses and verifies a Firebase session cookie.
+ *
+ *
If verified successfully, returns a parsed version of the cookie from which the UID and the
+ * other claims can be read. If the cookie is invalid, throws a {@link FirebaseAuthException}.
+ *
+ *
This method does not check whether the cookie has been revoked. See
+ * {@link #verifySessionCookie(String, boolean)}.
+ *
+ * @param cookie A Firebase session cookie string to verify and parse.
+ * @return A {@link FirebaseToken} representing the verified and decoded cookie.
+ */
+ public FirebaseToken verifySessionCookie(String cookie) throws FirebaseAuthException {
+ return verifySessionCookie(cookie, false);
+ }
+
+ /**
+ * Parses and verifies a Firebase session cookie.
+ *
+ *
If {@code checkRevoked} is true, additionally verifies that the cookie has not been
+ * revoked.
+ *
+ *
If verified successfully, returns a parsed version of the cookie from which the UID and the
+ * other claims can be read. If the cookie is invalid or has been revoked while
+ * {@code checkRevoked} is true, throws a {@link FirebaseAuthException}.
+ *
+ * @param cookie A Firebase session cookie string to verify and parse.
+ * @param checkRevoked A boolean indicating whether to check if the cookie was explicitly
+ * revoked.
+ * @return A {@link FirebaseToken} representing the verified and decoded cookie.
+ */
+ public FirebaseToken verifySessionCookie(
+ String cookie, boolean checkRevoked) throws FirebaseAuthException {
+ return verifySessionCookieOp(cookie, checkRevoked).call();
+ }
+
+ /**
+ * Similar to {@link #verifySessionCookie(String)} but performs the operation asynchronously.
+ *
+ * @param cookie A Firebase session cookie string to verify and parse.
+ * @return An {@code ApiFuture} which will complete successfully with the parsed cookie, or
+ * unsuccessfully with the failure Exception.
+ */
+ public ApiFuture verifySessionCookieAsync(String cookie) {
+ return verifySessionCookieAsync(cookie, false);
+ }
+
+ /**
+ * Similar to {@link #verifySessionCookie(String, boolean)} but performs the operation
+ * asynchronously.
+ *
+ * @param cookie A Firebase session cookie string to verify and parse.
+ * @param checkRevoked A boolean indicating whether to check if the cookie was explicitly
+ * revoked.
+ * @return An {@code ApiFuture} which will complete successfully with the parsed cookie, or
+ * unsuccessfully with the failure Exception.
+ */
+ public ApiFuture verifySessionCookieAsync(String cookie, boolean checkRevoked) {
+ return verifySessionCookieOp(cookie, checkRevoked).callAsync(firebaseApp);
+ }
+
+ private CallableOperation verifySessionCookieOp(
+ final String cookie, final boolean checkRevoked) {
+ checkArgument(!Strings.isNullOrEmpty(cookie), "Session cookie must not be null or empty");
+ final FirebaseTokenVerifier sessionCookieVerifier = getSessionCookieVerifier(checkRevoked);
+ return new CallableOperation() {
+ @Override
+ public FirebaseToken execute() throws FirebaseAuthException {
+ return sessionCookieVerifier.verifyToken(cookie);
+ }
+ };
+ }
+
+ @VisibleForTesting
+ FirebaseTokenVerifier getSessionCookieVerifier(boolean checkRevoked) {
+ FirebaseTokenVerifier verifier = cookieVerifier.get();
+ if (checkRevoked) {
+ FirebaseUserManager userManager = getUserManager();
+ verifier = RevocationCheckDecorator.decorateSessionCookieVerifier(verifier, userManager);
+ }
+ return verifier;
+ }
+
+ /**
+ * Creates a Firebase custom token for the given UID. This token can then be sent back to a client
+ * application to be used with the
+ * signInWithCustomToken
+ * authentication API.
+ *
+ *
{@link FirebaseApp} must have been initialized with service account credentials to use
+ * call this method.
+ *
+ * @param uid The UID to store in the token. This identifies the user to other Firebase services
+ * (Realtime Database, Firebase Auth, etc.). Should be less than 128 characters.
+ * @return A Firebase custom token string.
+ * @throws IllegalArgumentException If the specified uid is null or empty, or if the app has not
+ * been initialized with service account credentials.
+ * @throws FirebaseAuthException If an error occurs while generating the custom token.
+ */
+ public String createCustomToken(@NonNull String uid) throws FirebaseAuthException {
+ return createCustomToken(uid, null);
+ }
+
+ /**
+ * Creates a Firebase custom token for the given UID, containing the specified additional
+ * claims. This token can then be sent back to a client application to be used with the
+ * signInWithCustomToken
+ * authentication API.
+ *
+ *
This method attempts to generate a token using:
+ *
+ *
the private key of {@link FirebaseApp}'s service account credentials, if provided at
+ * initialization.
+ *
the IAM service
+ * if a service account email was specified via
+ * {@link com.google.firebase.FirebaseOptions.Builder#setServiceAccountId(String)}.
+ *
the App Identity
+ * service if the code is deployed in the Google App Engine standard environment.
+ *
the
+ * local Metadata server if the code is deployed in a different GCP-managed environment
+ * like Google Compute Engine.
+ *
+ *
+ *
This method throws an exception when all the above fail.
+ *
+ * @param uid The UID to store in the token. This identifies the user to other Firebase services
+ * (Realtime Database, Firebase Auth, etc.). Should be less than 128 characters.
+ * @param developerClaims Additional claims to be stored in the token (and made available to
+ * security rules in Database, Storage, etc.). These must be able to be serialized to JSON
+ * (e.g. contain only Maps, Arrays, Strings, Booleans, Numbers, etc.)
+ * @return A Firebase custom token string.
+ * @throws IllegalArgumentException If the specified uid is null or empty.
+ * @throws IllegalStateException If the SDK fails to discover a viable approach for signing
+ * tokens.
+ * @throws FirebaseAuthException If an error occurs while generating the custom token.
+ */
+ public String createCustomToken(@NonNull String uid,
+ @Nullable Map developerClaims) throws FirebaseAuthException {
+ return createCustomTokenOp(uid, developerClaims).call();
+ }
+
+ /**
+ * Similar to {@link #createCustomToken(String)} but performs the operation asynchronously.
+ *
+ * @param uid The UID to store in the token. This identifies the user to other Firebase services
+ * (Realtime Database, Firebase Auth, etc.). Should be less than 128 characters.
+ * @return An {@code ApiFuture} which will complete successfully with the created Firebase custom
+ * token, or unsuccessfully with the failure Exception.
+ * @throws IllegalArgumentException If the specified uid is null or empty, or if the app has not
+ * been initialized with service account credentials.
+ */
+ public ApiFuture createCustomTokenAsync(@NonNull String uid) {
+ return createCustomTokenAsync(uid, null);
+ }
+
+ /**
+ * Similar to {@link #createCustomToken(String, Map)} but performs the operation
+ * asynchronously.
+ *
+ * @param uid The UID to store in the token. This identifies the user to other Firebase services
+ * (Realtime Database, Storage, etc.). Should be less than 128 characters.
+ * @param developerClaims Additional claims to be stored in the token (and made available to
+ * security rules in Database, Storage, etc.). These must be able to be serialized to JSON
+ * (e.g. contain only Maps, Arrays, Strings, Booleans, Numbers, etc.)
+ * @return An {@code ApiFuture} which will complete successfully with the created Firebase custom
+ * token, or unsuccessfully with the failure Exception.
+ * @throws IllegalArgumentException If the specified uid is null or empty, or if the app has not
+ * been initialized with service account credentials.
+ */
+ public ApiFuture createCustomTokenAsync(
+ @NonNull String uid, @Nullable Map developerClaims) {
+ return createCustomTokenOp(uid, developerClaims).callAsync(firebaseApp);
+ }
+
+ private CallableOperation createCustomTokenOp(
+ final String uid, final Map developerClaims) {
+ checkArgument(!Strings.isNullOrEmpty(uid), "uid must not be null or empty");
+ final FirebaseTokenFactory tokenFactory = this.tokenFactory.get();
+ return new CallableOperation() {
+ @Override
+ public String execute() throws FirebaseAuthException {
+ try {
+ return tokenFactory.createSignedCustomAuthTokenForUser(uid, developerClaims);
+ } catch (IOException e) {
+ throw new FirebaseAuthException(ERROR_CUSTOM_TOKEN,
+ "Failed to generate a custom token", e);
+ }
+ }
+ };
+ }
+
+ /**
+ * Parses and verifies a Firebase ID Token.
+ *
+ *
A Firebase application can identify itself to a trusted backend server by sending its
+ * Firebase ID Token (accessible via the {@code getToken} API in the Firebase Authentication
+ * client) with its requests. The backend server can then use the {@code verifyIdToken()} method
+ * to verify that the token is valid. This method ensures that the token is correctly signed,
+ * has not expired, and it was issued to the Firebase project associated with this
+ * {@link FirebaseAuth} instance.
+ *
+ *
This method does not check whether a token has been revoked. Use
+ * {@link #verifyIdToken(String, boolean)} to perform an additional revocation check.
+ *
+ * @param token A Firebase ID token string to parse and verify.
+ * @return A {@link FirebaseToken} representing the verified and decoded token.
+ * @throws IllegalArgumentException If the token is null, empty, or if the {@link FirebaseApp}
+ * instance does not have a project ID associated with it.
+ * @throws FirebaseAuthException If an error occurs while parsing or validating the token.
+ */
+ public FirebaseToken verifyIdToken(@NonNull String token) throws FirebaseAuthException {
+ return verifyIdToken(token, false);
+ }
+
+ /**
+ * Parses and verifies a Firebase ID Token.
+ *
+ *
A Firebase application can identify itself to a trusted backend server by sending its
+ * Firebase ID Token (accessible via the {@code getToken} API in the Firebase Authentication
+ * client) with its requests. The backend server can then use the {@code verifyIdToken()} method
+ * to verify that the token is valid. This method ensures that the token is correctly signed,
+ * has not expired, and it was issued to the Firebase project associated with this
+ * {@link FirebaseAuth} instance.
+ *
+ *
If {@code checkRevoked} is set to true, this method performs an additional check to see
+ * if the ID token has been revoked since it was issues. This requires making an additional
+ * remote API call.
+ *
+ * @param token A Firebase ID token string to parse and verify.
+ * @param checkRevoked A boolean denoting whether to check if the tokens were revoked.
+ * @return A {@link FirebaseToken} representing the verified and decoded token.
+ * @throws IllegalArgumentException If the token is null, empty, or if the {@link FirebaseApp}
+ * instance does not have a project ID associated with it.
+ * @throws FirebaseAuthException If an error occurs while parsing or validating the token.
+ */
+ public FirebaseToken verifyIdToken(
+ @NonNull String token, boolean checkRevoked) throws FirebaseAuthException {
+ return verifyIdTokenOp(token, checkRevoked).call();
+ }
+
+ /**
+ * Similar to {@link #verifyIdToken(String)} but performs the operation asynchronously.
+ *
+ * @param token A Firebase ID Token to verify and parse.
+ * @return An {@code ApiFuture} which will complete successfully with the parsed token, or
+ * unsuccessfully with a {@link FirebaseAuthException}.
+ * @throws IllegalArgumentException If the token is null, empty, or if the {@link FirebaseApp}
+ * instance does not have a project ID associated with it.
+ */
+ public ApiFuture verifyIdTokenAsync(@NonNull String token) {
+ return verifyIdTokenAsync(token, false);
+ }
+
+ /**
+ * Similar to {@link #verifyIdToken(String, boolean)} but performs the operation asynchronously.
+ *
+ * @param token A Firebase ID Token to verify and parse.
+ * @param checkRevoked A boolean denoting whether to check if the tokens were revoked.
+ * @return An {@code ApiFuture} which will complete successfully with the parsed token, or
+ * unsuccessfully with a {@link FirebaseAuthException}.
+ * @throws IllegalArgumentException If the token is null, empty, or if the {@link FirebaseApp}
+ * instance does not have a project ID associated with it.
+ */
+ public ApiFuture verifyIdTokenAsync(@NonNull String token, boolean checkRevoked) {
+ return verifyIdTokenOp(token, checkRevoked).callAsync(firebaseApp);
+ }
+
+ private CallableOperation verifyIdTokenOp(
+ final String token, final boolean checkRevoked) {
+ checkArgument(!Strings.isNullOrEmpty(token), "ID token must not be null or empty");
+ final FirebaseTokenVerifier verifier = getIdTokenVerifier(checkRevoked);
+ return new CallableOperation() {
+ @Override
+ protected FirebaseToken execute() throws FirebaseAuthException {
+ return verifier.verifyToken(token);
+ }
+ };
+ }
+
+ @VisibleForTesting
+ FirebaseTokenVerifier getIdTokenVerifier(boolean checkRevoked) {
+ FirebaseTokenVerifier verifier = idTokenVerifier.get();
+ if (checkRevoked) {
+ FirebaseUserManager userManager = getUserManager();
+ verifier = RevocationCheckDecorator.decorateIdTokenVerifier(verifier, userManager);
+ }
+ return verifier;
+ }
+
+ /**
+ * Revokes all refresh tokens for the specified user.
+ *
+ *
Updates the user's tokensValidAfterTimestamp to the current UTC time expressed in
+ * milliseconds since the epoch and truncated to 1 second accuracy. It is important that the
+ * server on which this is called has its clock set correctly and synchronized.
+ *
+ *
While this will revoke all sessions for a specified user and disable any new ID tokens for
+ * existing sessions from getting minted, existing ID tokens may remain active until their
+ * natural expiration (one hour).
+ * To verify that ID tokens are revoked, use {@link #verifyIdTokenAsync(String, boolean)}.
+ *
+ * @param uid The user id for which tokens are revoked.
+ * @throws IllegalArgumentException If the user ID is null or empty.
+ * @throws FirebaseAuthException If an error occurs while revoking tokens.
+ */
+ public void revokeRefreshTokens(@NonNull String uid) throws FirebaseAuthException {
+ revokeRefreshTokensOp(uid).call();
+ }
+
+ /**
+ * Similar to {@link #revokeRefreshTokens(String)} but performs the operation asynchronously.
+ *
+ * @param uid The user id for which tokens are revoked.
+ * @return An {@code ApiFuture} which will complete successfully or fail with a
+ * {@link FirebaseAuthException} in the event of an error.
+ * @throws IllegalArgumentException If the user ID is null or empty.
+ */
+ public ApiFuture revokeRefreshTokensAsync(@NonNull String uid) {
+ return revokeRefreshTokensOp(uid).callAsync(firebaseApp);
+ }
+
+ private CallableOperation revokeRefreshTokensOp(final String uid) {
+ checkArgument(!Strings.isNullOrEmpty(uid), "uid must not be null or empty");
+ final FirebaseUserManager userManager = getUserManager();
+ return new CallableOperation() {
+ @Override
+ protected Void execute() throws FirebaseAuthException {
+ int currentTimeSeconds = (int) (System.currentTimeMillis() / 1000);
+ UpdateRequest request = new UpdateRequest(uid).setValidSince(currentTimeSeconds);
+ userManager.updateUser(request, jsonFactory);
+ return null;
+ }
+ };
+ }
+
+ /**
+ * Gets the user data corresponding to the specified user ID.
+ *
+ * @param uid A user ID string.
+ * @return A {@link UserRecord} instance.
+ * @throws IllegalArgumentException If the user ID string is null or empty.
+ * @throws FirebaseAuthException If an error occurs while retrieving user data.
+ */
+ public UserRecord getUser(@NonNull String uid) throws FirebaseAuthException {
+ return getUserOp(uid).call();
+ }
+
+ /**
+ * Similar to {@link #getUser(String)} but performs the operation asynchronously.
+ *
+ * @param uid A user ID string.
+ * @return An {@code ApiFuture} which will complete successfully with a {@link UserRecord}
+ * instance. If an error occurs while retrieving user data or if the specified user ID does
+ * not exist, the future throws a {@link FirebaseAuthException}.
+ * @throws IllegalArgumentException If the user ID string is null or empty.
+ */
+ public ApiFuture getUserAsync(@NonNull String uid) {
+ return getUserOp(uid).callAsync(firebaseApp);
+ }
+
+ private CallableOperation getUserOp(final String uid) {
+ checkArgument(!Strings.isNullOrEmpty(uid), "uid must not be null or empty");
+ final FirebaseUserManager userManager = getUserManager();
+ return new CallableOperation() {
+ @Override
+ protected UserRecord execute() throws FirebaseAuthException {
+ return userManager.getUserById(uid);
+ }
+ };
+ }
+
+ /**
+ * Gets the user data corresponding to the specified user email.
+ *
+ * @param email A user email address string.
+ * @return A {@link UserRecord} instance.
+ * @throws IllegalArgumentException If the email is null or empty.
+ * @throws FirebaseAuthException If an error occurs while retrieving user data.
+ */
+ public UserRecord getUserByEmail(@NonNull String email) throws FirebaseAuthException {
+ return getUserByEmailOp(email).call();
+ }
+
+ /**
+ * Similar to {@link #getUserByEmail(String)} but performs the operation asynchronously.
+ *
+ * @param email A user email address string.
+ * @return An {@code ApiFuture} which will complete successfully with a {@link UserRecord}
+ * instance. If an error occurs while retrieving user data or if the email address does not
+ * correspond to a user, the future throws a {@link FirebaseAuthException}.
+ * @throws IllegalArgumentException If the email is null or empty.
+ */
+ public ApiFuture getUserByEmailAsync(@NonNull String email) {
+ return getUserByEmailOp(email).callAsync(firebaseApp);
+ }
+
+ private CallableOperation getUserByEmailOp(
+ final String email) {
+ checkArgument(!Strings.isNullOrEmpty(email), "email must not be null or empty");
+ final FirebaseUserManager userManager = getUserManager();
+ return new CallableOperation() {
+ @Override
+ protected UserRecord execute() throws FirebaseAuthException {
+ return userManager.getUserByEmail(email);
+ }
+ };
+ }
+
+ /**
+ * Gets the user data corresponding to the specified user phone number.
+ *
+ * @param phoneNumber A user phone number string.
+ * @return A a {@link UserRecord} instance.
+ * @throws IllegalArgumentException If the phone number is null or empty.
+ * @throws FirebaseAuthException If an error occurs while retrieving user data.
+ */
+ public UserRecord getUserByPhoneNumber(@NonNull String phoneNumber) throws FirebaseAuthException {
+ return getUserByPhoneNumberOp(phoneNumber).call();
+ }
+
+ /**
+ * Gets the user data corresponding to the specified user phone number.
+ *
+ * @param phoneNumber A user phone number string.
+ * @return An {@code ApiFuture} which will complete successfully with a {@link UserRecord}
+ * instance. If an error occurs while retrieving user data or if the phone number does not
+ * correspond to a user, the future throws a {@link FirebaseAuthException}.
+ * @throws IllegalArgumentException If the phone number is null or empty.
+ */
+ public ApiFuture getUserByPhoneNumberAsync(@NonNull String phoneNumber) {
+ return getUserByPhoneNumberOp(phoneNumber).callAsync(firebaseApp);
+ }
+
+ private CallableOperation getUserByPhoneNumberOp(
+ final String phoneNumber) {
+ checkArgument(!Strings.isNullOrEmpty(phoneNumber), "phone number must not be null or empty");
+ final FirebaseUserManager userManager = getUserManager();
+ return new CallableOperation() {
+ @Override
+ protected UserRecord execute() throws FirebaseAuthException {
+ return userManager.getUserByPhoneNumber(phoneNumber);
+ }
+ };
+ }
+
+ /**
+ * Gets the user data corresponding to the specified identifiers.
+ *
+ *
There are no ordering guarantees; in particular, the nth entry in the users result list is
+ * not guaranteed to correspond to the nth entry in the input parameters list.
+ *
+ *
A maximum of 100 identifiers may be specified. If more than 100 identifiers are
+ * supplied, this method throws an {@link IllegalArgumentException}.
+ *
+ * @param identifiers The identifiers used to indicate which user records should be returned. Must
+ * have 100 or fewer entries.
+ * @return The corresponding user records.
+ * @throws IllegalArgumentException If any of the identifiers are invalid or if more than 100
+ * identifiers are specified.
+ * @throws NullPointerException If the identifiers parameter is null.
+ * @throws FirebaseAuthException If an error occurs while retrieving user data.
+ */
+ public GetUsersResult getUsers(@NonNull Collection identifiers)
+ throws FirebaseAuthException {
+ return getUsersOp(identifiers).call();
+ }
+
+ /**
+ * Gets the user data corresponding to the specified identifiers.
+ *
+ *
There are no ordering guarantees; in particular, the nth entry in the users result list is
+ * not guaranteed to correspond to the nth entry in the input parameters list.
+ *
+ *
A maximum of 100 identifiers may be specified. If more than 100 identifiers are
+ * supplied, this method throws an {@link IllegalArgumentException}.
+ *
+ * @param identifiers The identifiers used to indicate which user records should be returned.
+ * Must have 100 or fewer entries.
+ * @return An {@code ApiFuture} that resolves to the corresponding user records.
+ * @throws IllegalArgumentException If any of the identifiers are invalid or if more than 100
+ * identifiers are specified.
+ * @throws NullPointerException If the identifiers parameter is null.
+ */
+ public ApiFuture getUsersAsync(@NonNull Collection identifiers) {
+ return getUsersOp(identifiers).callAsync(firebaseApp);
+ }
+
+ private CallableOperation getUsersOp(
+ @NonNull final Collection identifiers) {
+ checkNotNull(identifiers, "identifiers must not be null");
+ checkArgument(identifiers.size() <= FirebaseUserManager.MAX_GET_ACCOUNTS_BATCH_SIZE,
+ "identifiers parameter must have <= " + FirebaseUserManager.MAX_GET_ACCOUNTS_BATCH_SIZE
+ + " entries.");
+
+ final FirebaseUserManager userManager = getUserManager();
+ return new CallableOperation() {
+ @Override
+ protected GetUsersResult execute() throws FirebaseAuthException {
+ Set users = userManager.getAccountInfo(identifiers);
+ Set notFound = new HashSet<>();
+ for (UserIdentifier id : identifiers) {
+ if (!isUserFound(id, users)) {
+ notFound.add(id);
+ }
+ }
+ return new GetUsersResult(users, notFound);
+ }
+ };
+ }
+
+ private boolean isUserFound(UserIdentifier id, Collection userRecords) {
+ for (UserRecord userRecord : userRecords) {
+ if (id.matches(userRecord)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Gets a page of users starting from the specified {@code pageToken}. Page size is
+ * limited to 1000 users.
+ *
+ * @param pageToken A non-empty page token string, or null to retrieve the first page of users.
+ * @return A {@link ListUsersPage} instance.
+ * @throws IllegalArgumentException If the specified page token is empty.
+ * @throws FirebaseAuthException If an error occurs while retrieving user data.
+ */
+ public ListUsersPage listUsers(@Nullable String pageToken) throws FirebaseAuthException {
+ return listUsers(pageToken, FirebaseUserManager.MAX_LIST_USERS_RESULTS);
+ }
+
+ /**
+ * Gets a page of users starting from the specified {@code pageToken}.
+ *
+ * @param pageToken A non-empty page token string, or null to retrieve the first page of users.
+ * @param maxResults Maximum number of users to include in the returned page. This may not
+ * exceed 1000.
+ * @return A {@link ListUsersPage} instance.
+ * @throws IllegalArgumentException If the specified page token is empty, or max results value
+ * is invalid.
+ * @throws FirebaseAuthException If an error occurs while retrieving user data.
+ */
+ public ListUsersPage listUsers(
+ @Nullable String pageToken, int maxResults) throws FirebaseAuthException {
+ return listUsersOp(pageToken, maxResults).call();
+ }
+
+ /**
+ * Similar to {@link #listUsers(String)} but performs the operation asynchronously.
+ *
+ * @param pageToken A non-empty page token string, or null to retrieve the first page of users.
+ * @return An {@code ApiFuture} which will complete successfully with a {@link ListUsersPage}
+ * instance. If an error occurs while retrieving user data, the future throws an exception.
+ * @throws IllegalArgumentException If the specified page token is empty.
+ */
+ public ApiFuture listUsersAsync(@Nullable String pageToken) {
+ return listUsersAsync(pageToken, FirebaseUserManager.MAX_LIST_USERS_RESULTS);
+ }
+
+ /**
+ * Similar to {@link #listUsers(String, int)} but performs the operation asynchronously.
+ *
+ * @param pageToken A non-empty page token string, or null to retrieve the first page of users.
+ * @param maxResults Maximum number of users to include in the returned page. This may not
+ * exceed 1000.
+ * @return An {@code ApiFuture} which will complete successfully with a {@link ListUsersPage}
+ * instance. If an error occurs while retrieving user data, the future throws an exception.
+ * @throws IllegalArgumentException If the specified page token is empty, or max results value
+ * is invalid.
+ */
+ public ApiFuture listUsersAsync(@Nullable String pageToken, int maxResults) {
+ return listUsersOp(pageToken, maxResults).callAsync(firebaseApp);
+ }
+
+ private CallableOperation listUsersOp(
+ @Nullable final String pageToken, final int maxResults) {
+ final FirebaseUserManager userManager = getUserManager();
+ final PageFactory factory = new PageFactory(
+ new DefaultUserSource(userManager, jsonFactory), maxResults, pageToken);
+ return new CallableOperation() {
+ @Override
+ protected ListUsersPage execute() throws FirebaseAuthException {
+ return factory.create();
+ }
+ };
+ }
+
+ /**
+ * Creates a new user account with the attributes contained in the specified
+ * {@link CreateRequest}.
+ *
+ * @param request A non-null {@link CreateRequest} instance.
+ * @return A {@link UserRecord} instance corresponding to the newly created account.
+ * @throws NullPointerException if the provided request is null.
+ * @throws FirebaseAuthException if an error occurs while creating the user account.
+ */
+ public UserRecord createUser(@NonNull CreateRequest request) throws FirebaseAuthException {
+ return createUserOp(request).call();
+ }
+
+ /**
+ * Similar to {@link #createUser(CreateRequest)} but performs the operation asynchronously.
+ *
+ * @param request A non-null {@link CreateRequest} instance.
+ * @return An {@code ApiFuture} which will complete successfully with a {@link UserRecord}
+ * instance corresponding to the newly created account. If an error occurs while creating the
+ * user account, the future throws a {@link FirebaseAuthException}.
+ * @throws NullPointerException if the provided request is null.
+ */
+ public ApiFuture createUserAsync(@NonNull CreateRequest request) {
+ return createUserOp(request).callAsync(firebaseApp);
+ }
+
+ private CallableOperation createUserOp(
+ final CreateRequest request) {
+ checkNotNull(request, "create request must not be null");
+ final FirebaseUserManager userManager = getUserManager();
+ return new CallableOperation() {
+ @Override
+ protected UserRecord execute() throws FirebaseAuthException {
+ String uid = userManager.createUser(request);
+ return userManager.getUserById(uid);
+ }
+ };
+ }
+
+ /**
+ * Updates an existing user account with the attributes contained in the specified
+ * {@link UpdateRequest}.
+ *
+ * @param request A non-null {@link UpdateRequest} instance.
+ * @return A {@link UserRecord} instance corresponding to the updated user account.
+ * account, the task fails with a {@link FirebaseAuthException}.
+ * @throws NullPointerException if the provided update request is null.
+ * @throws FirebaseAuthException if an error occurs while updating the user account.
+ */
+ public UserRecord updateUser(@NonNull UpdateRequest request) throws FirebaseAuthException {
+ return updateUserOp(request).call();
+ }
+
+ /**
+ * Similar to {@link #updateUser(UpdateRequest)} but performs the operation asynchronously.
+ *
+ * @param request A non-null {@link UpdateRequest} instance.
+ * @return An {@code ApiFuture} which will complete successfully with a {@link UserRecord}
+ * instance corresponding to the updated user account. If an error occurs while updating the
+ * user account, the future throws a {@link FirebaseAuthException}.
+ */
+ public ApiFuture updateUserAsync(@NonNull UpdateRequest request) {
+ return updateUserOp(request).callAsync(firebaseApp);
+ }
+
+ private CallableOperation updateUserOp(
+ final UpdateRequest request) {
+ checkNotNull(request, "update request must not be null");
+ final FirebaseUserManager userManager = getUserManager();
+ return new CallableOperation() {
+ @Override
+ protected UserRecord execute() throws FirebaseAuthException {
+ userManager.updateUser(request, jsonFactory);
+ return userManager.getUserById(request.getUid());
+ }
+ };
+ }
+
+ /**
+ * Sets the specified custom claims on an existing user account. A null claims value removes
+ * any claims currently set on the user account. The claims should serialize into a valid JSON
+ * string. The serialized claims must not be larger than 1000 characters.
+ *
+ * @param uid A user ID string.
+ * @param claims A map of custom claims or null.
+ * @throws FirebaseAuthException If an error occurs while updating custom claims.
+ * @throws IllegalArgumentException If the user ID string is null or empty, or the claims
+ * payload is invalid or too large.
+ */
+ public void setCustomUserClaims(@NonNull String uid,
+ @Nullable Map claims) throws FirebaseAuthException {
+ setCustomUserClaimsOp(uid, claims).call();
+ }
+
+ /**
+ * @deprecated Use {@link #setCustomUserClaims(String, Map)} instead.
+ */
+ public void setCustomClaims(@NonNull String uid,
+ @Nullable Map claims) throws FirebaseAuthException {
+ setCustomUserClaims(uid, claims);
+ }
+
+ /**
+ * Similar to {@link #setCustomUserClaims(String, Map)} but performs the operation asynchronously.
+ *
+ * @param uid A user ID string.
+ * @param claims A map of custom claims or null.
+ * @return An {@code ApiFuture} which will complete successfully when the user account has been
+ * updated. If an error occurs while deleting the user account, the future throws a
+ * {@link FirebaseAuthException}.
+ * @throws IllegalArgumentException If the user ID string is null or empty.
+ */
+ public ApiFuture setCustomUserClaimsAsync(
+ @NonNull String uid, @Nullable Map claims) {
+ return setCustomUserClaimsOp(uid, claims).callAsync(firebaseApp);
+ }
+
+ private CallableOperation setCustomUserClaimsOp(
+ final String uid, final Map claims) {
+ checkArgument(!Strings.isNullOrEmpty(uid), "uid must not be null or empty");
+ final FirebaseUserManager userManager = getUserManager();
+ return new CallableOperation() {
+ @Override
+ protected Void execute() throws FirebaseAuthException {
+ final UpdateRequest request = new UpdateRequest(uid).setCustomClaims(claims);
+ userManager.updateUser(request, jsonFactory);
+ return null;
+ }
+ };
+ }
+
+ /**
+ * Deletes the user identified by the specified user ID.
+ *
+ * @param uid A user ID string.
+ * @throws IllegalArgumentException If the user ID string is null or empty.
+ * @throws FirebaseAuthException If an error occurs while deleting the user.
+ */
+ public void deleteUser(@NonNull String uid) throws FirebaseAuthException {
+ deleteUserOp(uid).call();
+ }
+
+ /**
+ * Similar to {@link #deleteUser(String)} but performs the operation asynchronously.
+ *
+ * @param uid A user ID string.
+ * @return An {@code ApiFuture} which will complete successfully when the specified user account
+ * has been deleted. If an error occurs while deleting the user account, the future throws a
+ * {@link FirebaseAuthException}.
+ * @throws IllegalArgumentException If the user ID string is null or empty.
+ */
+ public ApiFuture deleteUserAsync(String uid) {
+ return deleteUserOp(uid).callAsync(firebaseApp);
+ }
+
+ private CallableOperation deleteUserOp(final String uid) {
+ checkArgument(!Strings.isNullOrEmpty(uid), "uid must not be null or empty");
+ final FirebaseUserManager userManager = getUserManager();
+ return new CallableOperation() {
+ @Override
+ protected Void execute() throws FirebaseAuthException {
+ userManager.deleteUser(uid);
+ return null;
+ }
+ };
+ }
+
+ /**
+ * Deletes the users specified by the given identifiers.
+ *
+ *
Deleting a non-existing user does not generate an error (the method is idempotent).
+ * Non-existing users are considered to be successfully deleted and are therefore included in the
+ * DeleteUsersResult.getSuccessCount() value.
+ *
+ *
A maximum of 1000 identifiers may be supplied. If more than 1000 identifiers are
+ * supplied, this method throws an {@link IllegalArgumentException}.
+ *
+ *
This API has a rate limit of 1 QPS. Exceeding the limit may result in a quota exceeded
+ * error. If you want to delete more than 1000 users, we suggest adding a delay to ensure you
+ * don't exceed this limit.
+ *
+ * @param uids The uids of the users to be deleted. Must have <= 1000 entries.
+ * @return The total number of successful/failed deletions, as well as the array of errors that
+ * correspond to the failed deletions.
+ * @throw IllegalArgumentException If any of the identifiers are invalid or if more than 1000
+ * identifiers are specified.
+ * @throws FirebaseAuthException If an error occurs while deleting users.
+ */
+ public DeleteUsersResult deleteUsers(List uids) throws FirebaseAuthException {
+ return deleteUsersOp(uids).call();
+ }
+
+ /**
+ * Similar to {@link #deleteUsers(List)} but performs the operation asynchronously.
+ *
+ * @param uids The uids of the users to be deleted. Must have <= 1000 entries.
+ * @return An {@code ApiFuture} that resolves to the total number of successful/failed
+ * deletions, as well as the array of errors that correspond to the failed deletions. If an
+ * error occurs while deleting the user account, the future throws a
+ * {@link FirebaseAuthException}.
+ * @throw IllegalArgumentException If any of the identifiers are invalid or if more than 1000
+ * identifiers are specified.
+ */
+ public ApiFuture deleteUsersAsync(List uids) {
+ return deleteUsersOp(uids).callAsync(firebaseApp);
+ }
+
+ private CallableOperation deleteUsersOp(
+ final List uids) {
+ checkNotNull(uids, "uids must not be null");
+ for (String uid : uids) {
+ UserRecord.checkUid(uid);
+ }
+ checkArgument(uids.size() <= FirebaseUserManager.MAX_DELETE_ACCOUNTS_BATCH_SIZE,
+ "uids parameter must have <= " + FirebaseUserManager.MAX_DELETE_ACCOUNTS_BATCH_SIZE
+ + " entries.");
+ final FirebaseUserManager userManager = getUserManager();
+ return new CallableOperation() {
+ @Override
+ protected DeleteUsersResult execute() throws FirebaseAuthException {
+ return userManager.deleteUsers(uids);
+ }
+ };
+ }
+
+ /**
+ * Imports the provided list of users into Firebase Auth. You can import a maximum of 1000 users
+ * at a time. This operation is optimized for bulk imports and does not check identifier
+ * uniqueness which could result in duplications.
+ *
+ *
{@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 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 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 importUsersAsync(List 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 importUsersAsync(List users,
+ @Nullable UserImportOptions options) {
+ return importUsersOp(users, options).callAsync(firebaseApp);
+ }
+
+ private CallableOperation importUsersOp(
+ final List users, final UserImportOptions options) {
+ final UserImportRequest request = new UserImportRequest(users, options, jsonFactory);
+ final FirebaseUserManager userManager = getUserManager();
+ return new CallableOperation() {
+ @Override
+ protected UserImportResult execute() throws FirebaseAuthException {
+ return userManager.importUsers(request);
+ }
+ };
+ }
+
+ /**
+ * Generates the out-of-band email action link for password reset flows for the specified email
+ * address.
+ *
+ * @param email The email of the user whose password is to be reset.
+ * @return A password reset link.
+ * @throws IllegalArgumentException If the email address is null or empty.
+ * @throws FirebaseAuthException If an error occurs while generating the link.
+ */
+ public String generatePasswordResetLink(@NonNull String email) throws FirebaseAuthException {
+ return generatePasswordResetLink(email, null);
+ }
+
+ /**
+ * Generates the out-of-band email action link for password reset flows for the specified email
+ * address.
+ *
+ * @param email The email of the user whose password is to be reset.
+ * @param settings The action code settings object which defines whether
+ * the link is to be handled by a mobile app and the additional state information to be
+ * passed in the deep link.
+ * @return A password reset link.
+ * @throws IllegalArgumentException If the email address is null or empty.
+ * @throws FirebaseAuthException If an error occurs while generating the link.
+ */
+ public String generatePasswordResetLink(
+ @NonNull String email, @Nullable ActionCodeSettings settings) throws FirebaseAuthException {
+ return generateEmailActionLinkOp(EmailLinkType.PASSWORD_RESET, email, settings).call();
+ }
+
+ /**
+ * Similar to {@link #generatePasswordResetLink(String)} but performs the operation
+ * asynchronously.
+ *
+ * @param email The email of the user whose password is to be reset.
+ * @return An {@code ApiFuture} which will complete successfully with the generated email action
+ * link. If an error occurs while generating the link, the future throws a
+ * {@link FirebaseAuthException}.
+ * @throws IllegalArgumentException If the email address is null or empty.
+ */
+ public ApiFuture generatePasswordResetLinkAsync(@NonNull String email) {
+ return generatePasswordResetLinkAsync(email, null);
+ }
+
+ /**
+ * Similar to {@link #generatePasswordResetLink(String, ActionCodeSettings)} but performs the
+ * operation asynchronously.
+ *
+ * @param email The email of the user whose password is to be reset.
+ * @param settings The action code settings object which defines whether
+ * the link is to be handled by a mobile app and the additional state information to be
+ * passed in the deep link.
+ * @return An {@code ApiFuture} which will complete successfully with the generated email action
+ * link. If an error occurs while generating the link, the future throws a
+ * {@link FirebaseAuthException}.
+ * @throws IllegalArgumentException If the email address is null or empty.
+ */
+ public ApiFuture generatePasswordResetLinkAsync(
+ @NonNull String email, @Nullable ActionCodeSettings settings) {
+ return generateEmailActionLinkOp(EmailLinkType.PASSWORD_RESET, email, settings)
+ .callAsync(firebaseApp);
+ }
+
+ /**
+ * Generates the out-of-band email action link for email verification flows for the specified
+ * email address.
+ *
+ * @param email The email of the user to be verified.
+ * @return An email verification link.
+ * @throws IllegalArgumentException If the email address is null or empty.
+ * @throws FirebaseAuthException If an error occurs while generating the link.
+ */
+ public String generateEmailVerificationLink(@NonNull String email) throws FirebaseAuthException {
+ return generateEmailVerificationLink(email, null);
+ }
+
+ /**
+ * Generates the out-of-band email action link for email verification flows for the specified
+ * email address, using the action code settings provided.
+ *
+ * @param email The email of the user to be verified.
+ * @return An email verification link.
+ * @throws IllegalArgumentException If the email address is null or empty.
+ * @throws FirebaseAuthException If an error occurs while generating the link.
+ */
+ public String generateEmailVerificationLink(
+ @NonNull String email, @Nullable ActionCodeSettings settings) throws FirebaseAuthException {
+ return generateEmailActionLinkOp(EmailLinkType.VERIFY_EMAIL, email, settings).call();
+ }
+
+ /**
+ * Similar to {@link #generateEmailVerificationLink(String)} but performs the
+ * operation asynchronously.
+ *
+ * @param email The email of the user to be verified.
+ * @return An {@code ApiFuture} which will complete successfully with the generated email action
+ * link. If an error occurs while generating the link, the future throws a
+ * {@link FirebaseAuthException}.
+ * @throws IllegalArgumentException If the email address is null or empty.
+ */
+ public ApiFuture generateEmailVerificationLinkAsync(@NonNull String email) {
+ return generateEmailVerificationLinkAsync(email, null);
+ }
+
+ /**
+ * Similar to {@link #generateEmailVerificationLink(String, ActionCodeSettings)} but performs the
+ * operation asynchronously.
+ *
+ * @param email The email of the user to be verified.
+ * @param settings The action code settings object which defines whether
+ * the link is to be handled by a mobile app and the additional state information to be
+ * passed in the deep link.
+ * @return An {@code ApiFuture} which will complete successfully with the generated email action
+ * link. If an error occurs while generating the link, the future throws a
+ * {@link FirebaseAuthException}.
+ * @throws IllegalArgumentException If the email address is null or empty.
+ */
+ public ApiFuture generateEmailVerificationLinkAsync(
+ @NonNull String email, @Nullable ActionCodeSettings settings) {
+ return generateEmailActionLinkOp(EmailLinkType.VERIFY_EMAIL, email, settings)
+ .callAsync(firebaseApp);
+ }
+
+ /**
+ * Generates the out-of-band email action link for email link sign-in flows, using the action
+ * code settings provided.
+ *
+ * @param email The email of the user signing in.
+ * @param settings The action code settings object which defines whether
+ * the link is to be handled by a mobile app and the additional state information to be
+ * passed in the deep link.
+ * @return An email verification link.
+ * @throws IllegalArgumentException If the email address is null or empty.
+ * @throws FirebaseAuthException If an error occurs while generating the link.
+ */
+ public String generateSignInWithEmailLink(
+ @NonNull String email, @NonNull ActionCodeSettings settings) throws FirebaseAuthException {
+ return generateEmailActionLinkOp(EmailLinkType.EMAIL_SIGNIN, email, settings).call();
+ }
+
+ /**
+ * Similar to {@link #generateSignInWithEmailLink(String, ActionCodeSettings)} but performs the
+ * operation asynchronously.
+ *
+ * @param email The email of the user signing in.
+ * @param settings The action code settings object which defines whether
+ * the link is to be handled by a mobile app and the additional state information to be
+ * passed in the deep link.
+ * @return An {@code ApiFuture} which will complete successfully with the generated email action
+ * link. If an error occurs while generating the link, the future throws a
+ * {@link FirebaseAuthException}.
+ * @throws IllegalArgumentException If the email address is null or empty.
+ * @throws NullPointerException If the settings is null.
+ */
+ public ApiFuture generateSignInWithEmailLinkAsync(
+ String email, @NonNull ActionCodeSettings settings) {
+ return generateEmailActionLinkOp(EmailLinkType.EMAIL_SIGNIN, email, settings)
+ .callAsync(firebaseApp);
+ }
+
+ @VisibleForTesting
+ FirebaseUserManager getUserManager() {
+ return this.userManager.get();
+ }
+
+ private CallableOperation generateEmailActionLinkOp(
+ final EmailLinkType type, final String email, final ActionCodeSettings settings) {
+ checkArgument(!Strings.isNullOrEmpty(email), "email must not be null or empty");
+ if (type == EmailLinkType.EMAIL_SIGNIN) {
+ checkNotNull(settings, "ActionCodeSettings must not be null when generating sign-in links");
+ }
+ final FirebaseUserManager userManager = getUserManager();
+ return new CallableOperation() {
+ @Override
+ protected String execute() throws FirebaseAuthException {
+ return userManager.getEmailActionLink(type, email, settings);
+ }
+ };
+ }
+
+ private Supplier threadSafeMemoize(final Supplier supplier) {
+ return Suppliers.memoize(new Supplier() {
+ @Override
+ public T get() {
+ checkNotNull(supplier);
+ synchronized (lock) {
+ return supplier.get();
+ }
+ }
+ });
+ }
+
+ =======
+ >>>>>>> master
private static FirebaseAuth fromApp(final FirebaseApp app) {
return populateBuilderFromApp(builder(), app, null)
.setTenantManager(new Supplier() {
@@ -77,6 +1248,12 @@ public TenantManager get() {
return new TenantManager(app);
}
})
+ .setUserManager(new Supplier() {
+ @Override
+ public FirebaseUserManager get() {
+ return new FirebaseUserManager(app);
+ }
+ })
.build();
}
@@ -91,7 +1268,16 @@ static Builder builder() {
return new Builder();
}
+ <<<<<<< redacted-passwords
+ static class Builder {
+ private FirebaseApp firebaseApp;
+ private Supplier tokenFactory;
+ private Supplier extends FirebaseTokenVerifier> idTokenVerifier;
+ private Supplier extends FirebaseTokenVerifier> cookieVerifier;
+ private Supplier userManager;
+ =======
static class Builder extends AbstractFirebaseAuth.Builder {
+ >>>>>>> master
private Supplier tenantManager;
@@ -107,8 +1293,27 @@ public Builder setTenantManager(Supplier tenantManager) {
return this;
}
+ <<<<<<< redacted-passwords
+ Builder setUserManager(Supplier userManager) {
+ this.userManager = userManager;
+ return this;
+ }
+
+ FirebaseAuth build() {
+ return new FirebaseAuth(this);
+ }
+ }
+
+ private static class FirebaseAuthService extends FirebaseService {
+
+ FirebaseAuthService(FirebaseApp app) {
+ super(SERVICE_ID, FirebaseAuth.fromApp(app));
+ }
+ }
+ =======
public FirebaseAuth build() {
return new FirebaseAuth(this);
}
}
+ >>>>>>> master
}
diff --git a/src/main/java/com/google/firebase/auth/FirebaseTokenVerifierImpl.java b/src/main/java/com/google/firebase/auth/FirebaseTokenVerifierImpl.java
index 9a3e1ae15..6a82be250 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseTokenVerifierImpl.java
+++ b/src/main/java/com/google/firebase/auth/FirebaseTokenVerifierImpl.java
@@ -29,7 +29,10 @@
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.firebase.ErrorCode;
+ <<<<<<< v7
+ =======
import com.google.firebase.auth.internal.Utils;
+ >>>>>>> master
import com.google.firebase.internal.Nullable;
import java.io.IOException;
import java.math.BigDecimal;
@@ -72,7 +75,11 @@ private FirebaseTokenVerifierImpl(Builder builder) {
this.docUrl = builder.docUrl;
this.invalidTokenErrorCode = checkNotNull(builder.invalidTokenErrorCode);
this.expiredTokenErrorCode = checkNotNull(builder.expiredTokenErrorCode);
+ <<<<<<< v7
+ this.tenantId = Strings.nullToEmpty(builder.tenantId);
+ =======
this.tenantId = builder.tenantId;
+ >>>>>>> master
}
/**
@@ -164,8 +171,12 @@ private void checkSignature(IdToken token) throws FirebaseAuthException {
}
}
+ <<<<<<< v7
+ private void checkContents(final IdToken idToken) throws FirebaseAuthException {
+ =======
private void checkContents(final IdToken idToken, boolean isEmulatorMode)
throws FirebaseAuthException {
+ >>>>>>> master
final Header header = idToken.getHeader();
final Payload payload = idToken.getPayload();
@@ -173,7 +184,11 @@ private void checkContents(final IdToken idToken, boolean isEmulatorMode)
String errorMessage = null;
AuthErrorCode errorCode = invalidTokenErrorCode;
+ <<<<<<< v7
+ if (header.getKeyId() == null) {
+ =======
if (!isEmulatorMode && header.getKeyId() == null) {
+ >>>>>>> master
errorMessage = getErrorForTokenWithoutKid(header, payload);
} else if (!isEmulatorMode && !RS256.equals(header.getAlgorithm())) {
errorMessage = String.format(
@@ -227,11 +242,19 @@ private void checkContents(final IdToken idToken, boolean isEmulatorMode)
throw newException(detailedError, errorCode);
}
}
+ <<<<<<< v7
private FirebaseAuthException newException(String message, AuthErrorCode errorCode) {
return newException(message, errorCode, null);
}
+ =======
+
+ private FirebaseAuthException newException(String message, AuthErrorCode errorCode) {
+ return newException(message, errorCode, null);
+ }
+
+ >>>>>>> master
private FirebaseAuthException newException(
String message, AuthErrorCode errorCode, Throwable cause) {
return new FirebaseAuthException(
@@ -328,11 +351,19 @@ private boolean containsLegacyUidField(IdToken.Payload payload) {
}
private void checkTenantId(final FirebaseToken firebaseToken) throws FirebaseAuthException {
+ <<<<<<< v7
+ String tokenTenantId = Strings.nullToEmpty(firebaseToken.getTenantId());
+ if (!this.tenantId.equals(tokenTenantId)) {
+ String message = String.format(
+ "The tenant ID ('%s') of the token did not match the expected value ('%s')",
+ tokenTenantId,
+ =======
String tokenTenantId = firebaseToken.getTenantId();
if (this.tenantId != null && !this.tenantId.equals(tokenTenantId)) {
String message = String.format(
"The tenant ID ('%s') of the token did not match the expected value ('%s')",
Strings.nullToEmpty(tokenTenantId),
+ >>>>>>> master
tenantId);
throw newException(message, AuthErrorCode.TENANT_ID_MISMATCH);
}
diff --git a/src/main/java/com/google/firebase/auth/FirebaseUserManager.java b/src/main/java/com/google/firebase/auth/FirebaseUserManager.java
index 195527841..c81c7ce8d 100644
--- a/src/main/java/com/google/firebase/auth/FirebaseUserManager.java
+++ b/src/main/java/com/google/firebase/auth/FirebaseUserManager.java
@@ -21,6 +21,10 @@
import com.google.api.client.http.HttpRequestFactory;
import com.google.api.client.http.HttpResponseInterceptor;
+ <<<<<<< redacted-passwords
+import com.google.api.client.http.json.JsonHttpContent;
+ =======
+ >>>>>>> master
import com.google.api.client.json.GenericJson;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.util.Key;
@@ -32,12 +36,27 @@
import com.google.firebase.ErrorCode;
import com.google.firebase.FirebaseApp;
import com.google.firebase.ImplFirebaseTrampolines;
+ <<<<<<< redacted-passwords
+import com.google.firebase.auth.UserRecord.CreateRequest;
+import com.google.firebase.auth.UserRecord.UpdateRequest;
+ =======
import com.google.firebase.IncomingHttpResponse;
import com.google.firebase.auth.internal.AuthHttpClient;
+ >>>>>>> master
import com.google.firebase.auth.internal.BatchDeleteResponse;
import com.google.firebase.auth.internal.DownloadAccountResponse;
import com.google.firebase.auth.internal.GetAccountInfoRequest;
import com.google.firebase.auth.internal.GetAccountInfoResponse;
+ <<<<<<< redacted-passwords
+import com.google.firebase.auth.internal.HttpErrorResponse;
+import com.google.firebase.auth.internal.UploadAccountResponse;
+import com.google.firebase.internal.ApiClientUtils;
+import com.google.firebase.internal.NonNull;
+import com.google.firebase.internal.Nullable;
+import com.google.firebase.internal.SdkUtils;
+
+import java.io.IOException;
+ =======
import com.google.firebase.auth.internal.ListOidcProviderConfigsResponse;
import com.google.firebase.auth.internal.ListSamlProviderConfigsResponse;
import com.google.firebase.auth.internal.UploadAccountResponse;
@@ -46,6 +65,7 @@
import com.google.firebase.internal.HttpRequestInfo;
import com.google.firebase.internal.NonNull;
import com.google.firebase.internal.Nullable;
+ >>>>>>> master
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
@@ -61,7 +81,10 @@
*/
final class FirebaseUserManager {
+ <<<<<<< redacted-passwords
+ =======
static final int MAX_LIST_PROVIDER_CONFIGS_RESULTS = 100;
+ >>>>>>> master
static final int MAX_GET_ACCOUNTS_BATCH_SIZE = 100;
static final int MAX_DELETE_ACCOUNTS_BATCH_SIZE = 1000;
static final int MAX_LIST_USERS_RESULTS = 1000;
@@ -79,17 +102,52 @@ final class FirebaseUserManager {
private final String userMgtBaseUrl;
private final String idpConfigMgtBaseUrl;
private final JsonFactory jsonFactory;
+ <<<<<<< redacted-passwords
+ private final HttpRequestFactory requestFactory;
+ private final String clientVersion = "Java/Admin/" + SdkUtils.getVersion();
+
+ private HttpResponseInterceptor interceptor;
+
+ /**
+ * Creates a new FirebaseUserManager instance.
+ *
+ * @param app A non-null {@link FirebaseApp}.
+ */
+ FirebaseUserManager(@NonNull FirebaseApp app) {
+ this(app, null);
+ }
+
+ FirebaseUserManager(@NonNull FirebaseApp app, @Nullable HttpRequestFactory requestFactory) {
+ checkNotNull(app, "FirebaseApp must not be null");
+ String projectId = ImplFirebaseTrampolines.getProjectId(app);
+ =======
private final AuthHttpClient httpClient;
private FirebaseUserManager(Builder builder) {
String projectId = builder.projectId;
+ >>>>>>> master
checkArgument(!Strings.isNullOrEmpty(projectId),
"Project ID is required to access the auth service. Use a service account credential or "
+ "set the project ID explicitly via FirebaseOptions. Alternatively you can also "
+ "set the project ID via the GOOGLE_CLOUD_PROJECT environment variable.");
+ <<<<<<< redacted-passwords
+ this.baseUrl = String.format(ID_TOOLKIT_URL, projectId);
+ this.jsonFactory = app.getOptions().getJsonFactory();
+
+ if (requestFactory == null) {
+ requestFactory = ApiClientUtils.newAuthorizedRequestFactory(app);
+ }
+
+ this.requestFactory = requestFactory;
+ =======
this.jsonFactory = checkNotNull(builder.jsonFactory, "JsonFactory must not be null");
+ <<<<<<< v7
+ final String idToolkitUrlV1 = String.format(ID_TOOLKIT_URL, "v1", projectId);
+ final String idToolkitUrlV2 = String.format(ID_TOOLKIT_URL, "v2", projectId);
+ =======
final String idToolkitUrlV1 = getIdToolkitUrl(projectId, "v1");
final String idToolkitUrlV2 = getIdToolkitUrl(projectId, "v2");
+ >>>>>>> master
final String tenantId = builder.tenantId;
if (tenantId == null) {
this.userMgtBaseUrl = idToolkitUrlV1;
@@ -101,6 +159,8 @@ private FirebaseUserManager(Builder builder) {
}
this.httpClient = new AuthHttpClient(jsonFactory, builder.requestFactory);
+ <<<<<<< v7
+ =======
}
private String getIdToolkitUrl(String projectId, String version) {
@@ -108,6 +168,8 @@ private String getIdToolkitUrl(String projectId, String version) {
return String.format(ID_TOOLKIT_URL_EMULATOR, Utils.getEmulatorHost(), version, projectId);
}
return String.format(ID_TOOLKIT_URL, version, projectId);
+ >>>>>>> master
+ >>>>>>> master
}
@VisibleForTesting
@@ -155,6 +217,45 @@ Set getAccountInfo(@NonNull Collection identifiers)
return results;
}
+ <<<<<<< redacted-passwords
+ Set getAccountInfo(@NonNull Collection identifiers)
+ throws FirebaseAuthException {
+ if (identifiers.isEmpty()) {
+ return new HashSet();
+ }
+
+ GetAccountInfoRequest payload = new GetAccountInfoRequest();
+ for (UserIdentifier id : identifiers) {
+ id.populate(payload);
+ }
+
+ GetAccountInfoResponse response = post(
+ "/accounts:lookup", payload, GetAccountInfoResponse.class);
+
+ if (response == null) {
+ throw new FirebaseAuthException(INTERNAL_ERROR, "Failed to parse server response");
+ }
+
+ Set results = new HashSet<>();
+ if (response.getUsers() != null) {
+ for (GetAccountInfoResponse.User user : response.getUsers()) {
+ results.add(new UserRecord(user, jsonFactory));
+ }
+ }
+ return results;
+ }
+
+ String createUser(CreateRequest request) throws FirebaseAuthException {
+ GenericJson response = post(
+ "/accounts", request.getProperties(), GenericJson.class);
+ if (response != null) {
+ String uid = (String) response.get("localId");
+ if (!Strings.isNullOrEmpty(uid)) {
+ return uid;
+ }
+ }
+ throw new FirebaseAuthException(INTERNAL_ERROR, "Failed to create new user");
+ =======
UserRecord getUserByProviderUid(
String providerId, String uid) throws FirebaseAuthException {
final Map payload = ImmutableMap.of(
@@ -162,6 +263,7 @@ UserRecord getUserByProviderUid(
ImmutableMap.builder()
.put("rawId", uid).put("providerId", providerId).build()));
return lookupUserAccount(payload, uid);
+ >>>>>>> master
}
String createUser(UserRecord.CreateRequest request) throws FirebaseAuthException {
@@ -188,6 +290,23 @@ DeleteUsersResult deleteUsers(@NonNull List uids) throws FirebaseAuthExc
return new DeleteUsersResult(uids.size(), response);
}
+ /**
+ * @pre uids != null
+ * @pre uids.size() <= MAX_DELETE_ACCOUNTS_BATCH_SIZE
+ */
+ DeleteUsersResult deleteUsers(@NonNull List uids) throws FirebaseAuthException {
+ final Map payload = ImmutableMap.of(
+ "localIds", uids,
+ "force", true);
+ BatchDeleteResponse response = post(
+ "/accounts:batchDelete", payload, BatchDeleteResponse.class);
+ if (response == null) {
+ throw new FirebaseAuthException(INTERNAL_ERROR, "Failed to delete users");
+ }
+
+ return new DeleteUsersResult(uids.size(), response);
+ }
+
DownloadAccountResponse listUsers(int maxResults, String pageToken) throws FirebaseAuthException {
ImmutableMap.Builder builder = ImmutableMap.builder()
.put("maxResults", maxResults);
@@ -410,9 +529,15 @@ static class Builder {
private String tenantId;
private HttpRequestFactory requestFactory;
private JsonFactory jsonFactory;
+ <<<<<<< v7
+
+ private Builder() { }
+
+ =======
private Builder() { }
+ >>>>>>> master
public Builder setProjectId(String projectId) {
this.projectId = projectId;
return this;
diff --git a/src/main/java/com/google/firebase/auth/internal/AuthErrorHandler.java b/src/main/java/com/google/firebase/auth/internal/AuthErrorHandler.java
index 4b9523c83..43d46a07e 100644
--- a/src/main/java/com/google/firebase/auth/internal/AuthErrorHandler.java
+++ b/src/main/java/com/google/firebase/auth/internal/AuthErrorHandler.java
@@ -61,12 +61,15 @@ final class AuthErrorHandler extends AbstractHttpErrorHandler>>>>>> master
"INVALID_DYNAMIC_LINK_DOMAIN",
new AuthError(
ErrorCode.INVALID_ARGUMENT,
diff --git a/src/main/java/com/google/firebase/auth/internal/CryptoSigners.java b/src/main/java/com/google/firebase/auth/internal/CryptoSigners.java
index 543f955c3..756b12c0a 100644
--- a/src/main/java/com/google/firebase/auth/internal/CryptoSigners.java
+++ b/src/main/java/com/google/firebase/auth/internal/CryptoSigners.java
@@ -22,7 +22,10 @@
import com.google.firebase.FirebaseException;
import com.google.firebase.ImplFirebaseTrampolines;
import com.google.firebase.auth.FirebaseAuthException;
+ <<<<<<< v7
+ =======
import com.google.firebase.auth.internal.Utils;
+ >>>>>>> master
import com.google.firebase.internal.AbstractPlatformErrorHandler;
import com.google.firebase.internal.ApiClientUtils;
import com.google.firebase.internal.ErrorHandlingHttpClient;
@@ -96,11 +99,19 @@ void setInterceptor(HttpResponseInterceptor interceptor) {
@Override
public byte[] sign(byte[] payload) throws FirebaseAuthException {
String encodedPayload = BaseEncoding.base64().encode(payload);
+ <<<<<<< v7
+ Map content = ImmutableMap.of("bytesToSign", encodedPayload);
+ String encodedUrl = String.format(IAM_SIGN_BLOB_URL, serviceAccount);
+ HttpRequestInfo requestInfo = HttpRequestInfo.buildJsonPostRequest(encodedUrl, content);
+ GenericJson parsed = httpClient.sendAndParse(requestInfo, GenericJson.class);
+ return BaseEncoding.base64().decode((String) parsed.get("signature"));
+ =======
Map content = ImmutableMap.of("payload", encodedPayload);
String encodedUrl = String.format(IAM_SIGN_BLOB_URL, serviceAccount);
HttpRequestInfo requestInfo = HttpRequestInfo.buildJsonPostRequest(encodedUrl, content);
GenericJson parsed = httpClient.sendAndParse(requestInfo, GenericJson.class);
return BaseEncoding.base64().decode((String) parsed.get("signedBlob"));
+ >>>>>>> master
}
@Override
@@ -109,6 +120,8 @@ public String getAccount() {
}
}
+ <<<<<<< v7
+ =======
/**
* A {@link CryptoSigner} implementation that doesn't sign data. For use with the Auth Emulator
* only
@@ -128,6 +141,7 @@ public String getAccount() {
}
}
+ >>>>>>> master
private static class IAMErrorHandler
extends AbstractPlatformErrorHandler {
diff --git a/src/main/java/com/google/firebase/auth/multitenancy/FirebaseTenantClient.java b/src/main/java/com/google/firebase/auth/multitenancy/FirebaseTenantClient.java
index 56ef04eb7..c4f7108b7 100644
--- a/src/main/java/com/google/firebase/auth/multitenancy/FirebaseTenantClient.java
+++ b/src/main/java/com/google/firebase/auth/multitenancy/FirebaseTenantClient.java
@@ -62,7 +62,11 @@ final class FirebaseTenantClient {
"Project ID is required to access the auth service. Use a service account credential or "
+ "set the project ID explicitly via FirebaseOptions. Alternatively you can also "
+ "set the project ID via the GOOGLE_CLOUD_PROJECT environment variable.");
+ <<<<<<< v7
+ this.tenantMgtBaseUrl = String.format(ID_TOOLKIT_URL, "v2", projectId);
+ =======
this.tenantMgtBaseUrl = getTenantMgtBaseUrl(projectId);
+ >>>>>>> master
this.httpClient = new AuthHttpClient(jsonFactory, requestFactory);
}
diff --git a/src/main/java/com/google/firebase/database/DatabaseError.java b/src/main/java/com/google/firebase/database/DatabaseError.java
index 456eb99e6..9aa5ae3c2 100644
--- a/src/main/java/com/google/firebase/database/DatabaseError.java
+++ b/src/main/java/com/google/firebase/database/DatabaseError.java
@@ -16,10 +16,14 @@
package com.google.firebase.database;
+import com.google.common.collect.ImmutableMap;
+import com.google.firebase.ErrorCode;
+import com.google.firebase.database.DatabaseReference.CompletionListener;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.ExecutionException;
/**
* Instances of DatabaseError are passed to callbacks when an operation failed. They contain a
@@ -27,99 +31,78 @@
*/
public class DatabaseError {
- /** Internal use. */
- public static final int DATA_STALE = -1;
- /** The server indicated that this operation failed. */
- public static final int OPERATION_FAILED = -2;
- /** This client does not have permission to perform this operation. */
- public static final int PERMISSION_DENIED = -3;
- /** The operation had to be aborted due to a network disconnect. */
- public static final int DISCONNECTED = -4;
-
- // Preempted was removed, this is for here for completeness and history
- // public static final int PREEMPTED = -5;
-
- /** The supplied auth token has expired. */
- public static final int EXPIRED_TOKEN = -6;
- /**
- * The specified authentication token is invalid. This can occur when the token is malformed,
- * expired, or the secret that was used to generate it has been revoked.
- */
- public static final int INVALID_TOKEN = -7;
- /** The transaction had too many retries */
- public static final int MAX_RETRIES = -8;
- /** The transaction was overridden by a subsequent set */
- public static final int OVERRIDDEN_BY_SET = -9;
- /** The service is unavailable. */
- public static final int UNAVAILABLE = -10;
- /** An exception occurred in user code. */
- public static final int USER_CODE_EXCEPTION = -11;
-
- // client codes
- /** The operation could not be performed due to a network error. */
- public static final int NETWORK_ERROR = -24;
-
- /** The write was canceled locally. */
- public static final int WRITE_CANCELED = -25;
-
- /**
- * An unknown error occurred. Please refer to the error message and error details for more
- * information.
- */
- public static final int UNKNOWN_ERROR = -999;
-
- private static final Map errorReasons = new HashMap<>();
- private static final Map errorCodes = new HashMap<>();
-
- static {
- // Firebase Database error codes
- errorReasons.put(DATA_STALE, "The transaction needs to be run again with current data");
- errorReasons.put(OPERATION_FAILED, "The server indicated that this operation failed");
- errorReasons.put(
- PERMISSION_DENIED, "This client does not have permission to perform this operation");
- errorReasons.put(DISCONNECTED, "The operation had to be aborted due to a network disconnect");
- errorReasons.put(EXPIRED_TOKEN, "The supplied auth token has expired");
- errorReasons.put(INVALID_TOKEN, "The supplied auth token was invalid");
- errorReasons.put(MAX_RETRIES, "The transaction had too many retries");
- errorReasons.put(OVERRIDDEN_BY_SET, "The transaction was overridden by a subsequent set");
- errorReasons.put(UNAVAILABLE, "The service is unavailable");
- errorReasons.put(
- USER_CODE_EXCEPTION,
- "User code called from the Firebase Database runloop threw an exception:\n");
-
- // client codes
- errorReasons.put(NETWORK_ERROR, "The operation could not be performed due to a network error");
- errorReasons.put(WRITE_CANCELED, "The write was canceled by the user.");
- errorReasons.put(UNKNOWN_ERROR, "An unknown error occurred");
- }
+ private static final Map errorReasons =
+ ImmutableMap.builder()
+ .put(DatabaseErrorCode.DATA_STALE,
+ "The transaction needs to be run again with current data")
+ .put(DatabaseErrorCode.OPERATION_FAILED,
+ "The server indicated that this operation failed")
+ .put(DatabaseErrorCode.PERMISSION_DENIED,
+ "This client does not have permission to perform this operation")
+ .put(DatabaseErrorCode.DISCONNECTED,
+ "The operation had to be aborted due to a network disconnect")
+ .put(DatabaseErrorCode.EXPIRED_TOKEN, "The supplied auth token has expired")
+ .put(DatabaseErrorCode.INVALID_TOKEN, "The supplied auth token was invalid")
+ .put(DatabaseErrorCode.MAX_RETRIES, "The transaction had too many retries")
+ .put(DatabaseErrorCode.OVERRIDDEN_BY_SET,
+ "The transaction was overridden by a subsequent set")
+ .put(DatabaseErrorCode.UNAVAILABLE, "The service is unavailable")
+ .put(DatabaseErrorCode.USER_CODE_EXCEPTION,
+ "User code called from the Firebase Database runloop threw an exception:\n")
+ // client codes
+ .put(DatabaseErrorCode.NETWORK_ERROR,
+ "The operation could not be performed due to a network error")
+ .put(DatabaseErrorCode.WRITE_CANCELED, "The write was canceled by the user.")
+ .put(DatabaseErrorCode.UNKNOWN_ERROR, "An unknown error occurred")
+ .build();
+
+ private static final Map platformCodes =
+ ImmutableMap.builder()
+ .put(DatabaseErrorCode.DATA_STALE, ErrorCode.FAILED_PRECONDITION)
+ .put(DatabaseErrorCode.OPERATION_FAILED, ErrorCode.INTERNAL)
+ .put(DatabaseErrorCode.PERMISSION_DENIED, ErrorCode.PERMISSION_DENIED)
+ .put(DatabaseErrorCode.DISCONNECTED, ErrorCode.UNKNOWN)
+ .put(DatabaseErrorCode.EXPIRED_TOKEN, ErrorCode.PERMISSION_DENIED)
+ .put(DatabaseErrorCode.INVALID_TOKEN, ErrorCode.PERMISSION_DENIED)
+ .put(DatabaseErrorCode.MAX_RETRIES, ErrorCode.DEADLINE_EXCEEDED)
+ .put(DatabaseErrorCode.OVERRIDDEN_BY_SET, ErrorCode.FAILED_PRECONDITION)
+ .put(DatabaseErrorCode.UNAVAILABLE, ErrorCode.UNAVAILABLE)
+ .put(DatabaseErrorCode.USER_CODE_EXCEPTION, ErrorCode.UNKNOWN)
+ // client codes
+ .put(DatabaseErrorCode.NETWORK_ERROR, ErrorCode.UNKNOWN)
+ .put(DatabaseErrorCode.WRITE_CANCELED, ErrorCode.CANCELLED)
+ .put(DatabaseErrorCode.UNKNOWN_ERROR, ErrorCode.UNKNOWN)
+ .build();
+
+ private static final Map errorCodes = new HashMap<>();
static {
// Firebase Database error codes
- errorCodes.put("datastale", DATA_STALE);
- errorCodes.put("failure", OPERATION_FAILED);
- errorCodes.put("permission_denied", PERMISSION_DENIED);
- errorCodes.put("disconnected", DISCONNECTED);
- errorCodes.put("expired_token", EXPIRED_TOKEN);
- errorCodes.put("invalid_token", INVALID_TOKEN);
- errorCodes.put("maxretries", MAX_RETRIES);
- errorCodes.put("overriddenbyset", OVERRIDDEN_BY_SET);
- errorCodes.put("unavailable", UNAVAILABLE);
+ errorCodes.put("datastale", DatabaseErrorCode.DATA_STALE);
+ errorCodes.put("failure", DatabaseErrorCode.OPERATION_FAILED);
+ errorCodes.put("permission_denied", DatabaseErrorCode.PERMISSION_DENIED);
+ errorCodes.put("disconnected", DatabaseErrorCode.DISCONNECTED);
+ errorCodes.put("expired_token", DatabaseErrorCode.EXPIRED_TOKEN);
+ errorCodes.put("invalid_token", DatabaseErrorCode.INVALID_TOKEN);
+ errorCodes.put("maxretries", DatabaseErrorCode.MAX_RETRIES);
+ errorCodes.put("overriddenbyset", DatabaseErrorCode.OVERRIDDEN_BY_SET);
+ errorCodes.put("unavailable", DatabaseErrorCode.UNAVAILABLE);
// client codes
- errorCodes.put("network_error", NETWORK_ERROR);
- errorCodes.put("write_canceled", WRITE_CANCELED);
+ errorCodes.put("network_error", DatabaseErrorCode.NETWORK_ERROR);
+ errorCodes.put("write_canceled", DatabaseErrorCode.WRITE_CANCELED);
}
- private final int code;
+ private final DatabaseErrorCode code;
private final String message;
private final String details;
- private DatabaseError(int code, String message) {
+ private DatabaseError(DatabaseErrorCode code, String message) {
this(code, message, null);
}
- private DatabaseError(int code, String message, String details) {
+ private DatabaseError(DatabaseErrorCode code, String message, String details) {
this.code = code;
this.message = message;
this.details = (details == null) ? "" : details;
@@ -158,9 +141,9 @@ public static DatabaseError fromStatus(String status, String reason) {
* @return An error corresponding the to the status
*/
public static DatabaseError fromStatus(String status, String reason, String details) {
- Integer code = errorCodes.get(status.toLowerCase());
+ DatabaseErrorCode code = errorCodes.get(status.toLowerCase());
if (code == null) {
- code = UNKNOWN_ERROR;
+ code = DatabaseErrorCode.UNKNOWN_ERROR;
}
String message = reason == null ? errorReasons.get(code) : reason;
@@ -174,7 +157,7 @@ public static DatabaseError fromStatus(String status, String reason, String deta
* @param code The error code
* @return An error corresponding the to the code
*/
- public static DatabaseError fromCode(int code) {
+ public static DatabaseError fromCode(DatabaseErrorCode code) {
if (!errorReasons.containsKey(code)) {
throw new IllegalArgumentException("Invalid Firebase Database error code: " + code);
}
@@ -186,14 +169,15 @@ public static DatabaseError fromException(Throwable e) {
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter);
e.printStackTrace(printWriter);
- String reason = errorReasons.get(USER_CODE_EXCEPTION) + stringWriter.toString();
- return new DatabaseError(USER_CODE_EXCEPTION, reason);
+ String reason = errorReasons.get(DatabaseErrorCode.USER_CODE_EXCEPTION)
+ + stringWriter.toString();
+ return new DatabaseError(DatabaseErrorCode.USER_CODE_EXCEPTION, reason);
}
/**
* @return One of the defined status codes, depending on the error.
*/
- public int getCode() {
+ public DatabaseErrorCode getCode() {
return code;
}
@@ -223,6 +207,6 @@ public String toString() {
* @return An exception wrapping this error, with an appropriate message and no stack trace.
*/
public DatabaseException toException() {
- return new DatabaseException("Firebase Database error: " + message);
+ return new DatabaseException(platformCodes.get(code), "Firebase Database error: " + message);
}
}
diff --git a/src/main/java/com/google/firebase/database/DatabaseErrorCode.java b/src/main/java/com/google/firebase/database/DatabaseErrorCode.java
new file mode 100644
index 000000000..3ba49754c
--- /dev/null
+++ b/src/main/java/com/google/firebase/database/DatabaseErrorCode.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2019 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.database;
+
+public enum DatabaseErrorCode {
+
+ DATA_STALE,
+ /** The server indicated that this operation failed. */
+ OPERATION_FAILED,
+ /** This client does not have permission to perform this operation. */
+ PERMISSION_DENIED,
+ /** The operation had to be aborted due to a network disconnect. */
+ DISCONNECTED,
+
+ // Preempted was removed, this is for here for completeness and history
+ // public static final int PREEMPTED = -5;
+
+ /** The supplied auth token has expired. */
+ EXPIRED_TOKEN,
+ /**
+ * The specified authentication token is invalid. This can occur when the token is malformed,
+ * expired, or the secret that was used to generate it has been revoked.
+ */
+ INVALID_TOKEN,
+ /** The transaction had too many retries */
+ MAX_RETRIES,
+ /** The transaction was overridden by a subsequent set */
+ OVERRIDDEN_BY_SET,
+ /** The service is unavailable. */
+ UNAVAILABLE,
+ /** An exception occurred in user code. */
+ USER_CODE_EXCEPTION,
+
+ // client codes
+ /** The operation could not be performed due to a network error. */
+ NETWORK_ERROR,
+
+ /** The write was canceled locally. */
+ WRITE_CANCELED,
+ UNKNOWN_ERROR,
+
+}
diff --git a/src/main/java/com/google/firebase/database/DatabaseException.java b/src/main/java/com/google/firebase/database/DatabaseException.java
index c85e93878..715441643 100644
--- a/src/main/java/com/google/firebase/database/DatabaseException.java
+++ b/src/main/java/com/google/firebase/database/DatabaseException.java
@@ -16,30 +16,22 @@
package com.google.firebase.database;
+import com.google.firebase.ErrorCode;
+import com.google.firebase.FirebaseException;
+
/**
* This error is thrown when the Firebase Database library is unable to operate on the input it has
* been given.
*/
-public class DatabaseException extends RuntimeException {
-
- /**
- * For internal use
- *
- * @hide
- * @param message A human readable description of the error
- */
- public DatabaseException(String message) {
- super(message);
- }
+public class DatabaseException extends FirebaseException {
/**
* For internal use
*
* @hide
* @param message A human readable description of the error
- * @param cause The underlying cause for this error
*/
- public DatabaseException(String message, Throwable cause) {
- super(message, cause);
+ public DatabaseException(ErrorCode code, String message) {
+ super(code, message, null, null);
}
}
diff --git a/src/main/java/com/google/firebase/database/DatabaseReference.java b/src/main/java/com/google/firebase/database/DatabaseReference.java
index 10dfe40ba..9bef59da8 100644
--- a/src/main/java/com/google/firebase/database/DatabaseReference.java
+++ b/src/main/java/com/google/firebase/database/DatabaseReference.java
@@ -544,7 +544,7 @@ public String toString() {
try {
return parent.toString() + "/" + URLEncoder.encode(getKey(), "UTF-8").replace("+", "%20");
} catch (UnsupportedEncodingException e) {
- throw new DatabaseException("Failed to URLEncode key: " + getKey(), e);
+ throw new IllegalArgumentException("Failed to URLEncode key: " + getKey(), e);
}
}
}
diff --git a/src/main/java/com/google/firebase/database/FirebaseDatabase.java b/src/main/java/com/google/firebase/database/FirebaseDatabase.java
index 306ce9274..60ffc0bd8 100644
--- a/src/main/java/com/google/firebase/database/FirebaseDatabase.java
+++ b/src/main/java/com/google/firebase/database/FirebaseDatabase.java
@@ -79,7 +79,7 @@ private FirebaseDatabase(FirebaseApp app, RepoInfo repoInfo, DatabaseConfig conf
public static FirebaseDatabase getInstance() {
FirebaseApp instance = FirebaseApp.getInstance();
if (instance == null) {
- throw new DatabaseException("You must call FirebaseApp.initialize() first.");
+ throw new IllegalStateException("You must call FirebaseApp.initialize() first.");
}
return getInstance(instance, instance.getOptions().getDatabaseUrl());
}
@@ -93,7 +93,7 @@ public static FirebaseDatabase getInstance() {
public static FirebaseDatabase getInstance(String url) {
FirebaseApp instance = FirebaseApp.getInstance();
if (instance == null) {
- throw new DatabaseException("You must call FirebaseApp.initialize() first.");
+ throw new IllegalStateException("You must call FirebaseApp.initialize() first.");
}
return getInstance(instance, url);
}
@@ -122,7 +122,7 @@ public static synchronized FirebaseDatabase getInstance(FirebaseApp app, String
service = ImplFirebaseTrampolines.addService(app, new FirebaseDatabaseService());
}
if (url == null || url.isEmpty()) {
- throw new DatabaseException(
+ throw new IllegalArgumentException(
"Failed to get FirebaseDatabase instance: Specify DatabaseURL within "
+ "FirebaseApp or from your getInstance() call.");
}
@@ -137,7 +137,7 @@ public static synchronized FirebaseDatabase getInstance(FirebaseApp app, String
parsedUrl = Utilities.parseUrl(url);
}
if (!parsedUrl.path.isEmpty()) {
- throw new DatabaseException(
+ throw new IllegalArgumentException(
"Specified Database URL '"
+ url
+ "' is invalid. It should point to the root of a "
@@ -232,7 +232,7 @@ public DatabaseReference getReferenceFromUrl(String url) {
ParsedUrl parsedUrl = Utilities.parseUrl(url);
Repo repo = ensureRepo();
if (!parsedUrl.repoInfo.host.equals(repo.getRepoInfo().host)) {
- throw new DatabaseException(
+ throw new IllegalArgumentException(
"Invalid URL ("
+ url
+ ") passed to getReference(). "
@@ -323,7 +323,7 @@ private void assertUnfrozen(String methodCalled) {
synchronized (lock) {
checkNotDestroyed();
if (this.repo != null) {
- throw new DatabaseException(
+ throw new IllegalStateException(
"Calls to "
+ methodCalled
+ "() must be made before any "
diff --git a/src/main/java/com/google/firebase/database/MutableData.java b/src/main/java/com/google/firebase/database/MutableData.java
index 7a0e7983b..5604c6c30 100644
--- a/src/main/java/com/google/firebase/database/MutableData.java
+++ b/src/main/java/com/google/firebase/database/MutableData.java
@@ -298,7 +298,7 @@ public T getValue(Class valueType) {
*
* @param value The value to set at this location
*/
- public void setValue(Object value) throws DatabaseException {
+ public void setValue(Object value) {
ValidationPath.validateWithObject(prefixPath, value);
Object bouncedValue = CustomClassMapper.convertToPlainJavaTypes(value);
Validation.validateWritableObject(bouncedValue);
diff --git a/src/main/java/com/google/firebase/database/Query.java b/src/main/java/com/google/firebase/database/Query.java
index e22a4931a..a9f5348a5 100644
--- a/src/main/java/com/google/firebase/database/Query.java
+++ b/src/main/java/com/google/firebase/database/Query.java
@@ -65,7 +65,7 @@ public class Query {
// but we don't want to allow multiple orderByPriority calls, so track them here
private final boolean orderByCalled;
- Query(Repo repo, Path path, QueryParams params, boolean orderByCalled) throws DatabaseException {
+ Query(Repo repo, Path path, QueryParams params, boolean orderByCalled) {
hardAssert(params.isValid(), "Validation of queries failed.");
this.repo = repo;
this.path = path;
@@ -248,7 +248,7 @@ public void run() {
*/
public void keepSynced(final boolean keepSynced) {
if (!this.path.isEmpty() && this.path.getFront().equals(ChildKey.getInfoKey())) {
- throw new DatabaseException("Can't call keepSynced() on .info paths.");
+ throw new UnsupportedOperationException("Can't call keepSynced() on .info paths.");
}
repo.scheduleNow(
diff --git a/src/main/java/com/google/firebase/database/core/Context.java b/src/main/java/com/google/firebase/database/core/Context.java
index a0025f997..a2111a3e3 100644
--- a/src/main/java/com/google/firebase/database/core/Context.java
+++ b/src/main/java/com/google/firebase/database/core/Context.java
@@ -125,7 +125,7 @@ void stop() {
void assertUnfrozen() {
if (frozen) {
- throw new DatabaseException(
+ throw new IllegalStateException(
"Modifications to DatabaseConfig objects must occur before they are in use");
}
}
diff --git a/src/main/java/com/google/firebase/database/core/DatabaseConfig.java b/src/main/java/com/google/firebase/database/core/DatabaseConfig.java
index 844f59266..af98c98b0 100644
--- a/src/main/java/com/google/firebase/database/core/DatabaseConfig.java
+++ b/src/main/java/com/google/firebase/database/core/DatabaseConfig.java
@@ -101,10 +101,10 @@ public synchronized void setPersistenceCacheSizeBytes(long cacheSizeInBytes) {
assertUnfrozen();
if (cacheSizeInBytes < 1024 * 1024) {
- throw new DatabaseException("The minimum cache size must be at least 1MB");
+ throw new IllegalArgumentException("The minimum cache size must be at least 1MB");
}
if (cacheSizeInBytes > 100 * 1024 * 1024) {
- throw new DatabaseException(
+ throw new IllegalArgumentException(
"Firebase Database currently doesn't support a cache size larger than 100MB");
}
diff --git a/src/main/java/com/google/firebase/database/core/Path.java b/src/main/java/com/google/firebase/database/core/Path.java
index b6fd451b9..4e5c91ff8 100644
--- a/src/main/java/com/google/firebase/database/core/Path.java
+++ b/src/main/java/com/google/firebase/database/core/Path.java
@@ -84,7 +84,7 @@ public static Path getRelative(Path from, Path to) {
} else if (outerFront.equals(innerFront)) {
return getRelative(from.popFront(), to.popFront());
} else {
- throw new DatabaseException("INTERNAL ERROR: " + to + " is not contained in " + from);
+ throw new IllegalArgumentException("INTERNAL ERROR: " + to + " is not contained in " + from);
}
}
diff --git a/src/main/java/com/google/firebase/database/core/Repo.java b/src/main/java/com/google/firebase/database/core/Repo.java
index 98777d04d..0b6c761bc 100644
--- a/src/main/java/com/google/firebase/database/core/Repo.java
+++ b/src/main/java/com/google/firebase/database/core/Repo.java
@@ -20,6 +20,7 @@
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
+import com.google.firebase.database.DatabaseErrorCode;
import com.google.firebase.database.DatabaseException;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
@@ -297,7 +298,7 @@ public void onDataUpdate(
}
postEvents(events);
- } catch (DatabaseException e) {
+ } catch (Exception e) {
logger.error("Firebase internal error", e);
}
}
@@ -353,7 +354,7 @@ public void run() {
}
private void ackWriteAndRerunTransactions(long writeId, Path path, DatabaseError error) {
- if (error != null && error.getCode() == DatabaseError.WRITE_CANCELED) {
+ if (error != null && error.getCode() == DatabaseErrorCode.WRITE_CANCELED) {
// This write was already removed, we just need to ignore it...
} else {
boolean success = error == null;
@@ -394,7 +395,7 @@ public void onRequestResult(String optErrorCode, String optErrorMessage) {
}
});
- Path affectedPath = abortTransactions(path, DatabaseError.OVERRIDDEN_BY_SET);
+ Path affectedPath = abortTransactions(path, DatabaseErrorCode.OVERRIDDEN_BY_SET);
this.rerunTransactions(affectedPath);
}
@@ -437,7 +438,7 @@ public void onRequestResult(String optErrorCode, String optErrorMessage) {
for (Entry update : updates) {
Path pathFromRoot = path.child(update.getKey());
- Path affectedPath = abortTransactions(pathFromRoot, DatabaseError.OVERRIDDEN_BY_SET);
+ Path affectedPath = abortTransactions(pathFromRoot, DatabaseErrorCode.OVERRIDDEN_BY_SET);
rerunTransactions(affectedPath);
}
}
@@ -447,7 +448,7 @@ public void purgeOutstandingWrites() {
List extends Event> events = serverSyncTree.removeAllWrites();
postEvents(events);
// Abort any transactions
- abortTransactions(Path.getEmptyPath(), DatabaseError.WRITE_CANCELED);
+ abortTransactions(Path.getEmptyPath(), DatabaseErrorCode.WRITE_CANCELED);
// Remove outstanding writes from connection
connection.purgeOutstandingWrites();
}
@@ -591,7 +592,7 @@ private void updateInfo(ChildKey childKey, Object value) {
infoData.update(path, node);
List extends Event> events = this.infoSyncTree.applyServerOverwrite(path, node);
this.postEvents(events);
- } catch (DatabaseException e) {
+ } catch (Exception e) {
logger.error("Failed to parse info update", e);
}
}
@@ -612,7 +613,7 @@ private void runOnDisconnectEvents() {
@Override
public void visitTree(Path prefixPath, Node node) {
events.addAll(serverSyncTree.applyServerOverwrite(prefixPath, node));
- Path affectedPath = abortTransactions(prefixPath, DatabaseError.OVERRIDDEN_BY_SET);
+ Path affectedPath = abortTransactions(prefixPath, DatabaseErrorCode.OVERRIDDEN_BY_SET);
rerunTransactions(affectedPath);
}
});
@@ -623,8 +624,8 @@ public void visitTree(Path prefixPath, Node node) {
private void warnIfWriteFailed(String writeType, Path path, DatabaseError error) {
// DATA_STALE is a normal, expected error during transaction processing.
if (error != null
- && !(error.getCode() == DatabaseError.DATA_STALE
- || error.getCode() == DatabaseError.WRITE_CANCELED)) {
+ && !(error.getCode() == DatabaseErrorCode.DATA_STALE
+ || error.getCode() == DatabaseErrorCode.WRITE_CANCELED)) {
logger.warn(writeType + " at " + path.toString() + " failed: " + error.toString());
}
}
@@ -868,7 +869,7 @@ public void run() {
}
} else {
// transactions are no longer sent. Update their status appropriately
- if (error.getCode() == DatabaseError.DATA_STALE) {
+ if (error.getCode() == DatabaseErrorCode.DATA_STALE) {
for (TransactionData transaction : queue) {
if (transaction.status == TransactionStatus.SENT_NEEDS_ABORT) {
transaction.status = TransactionStatus.NEEDS_ABORT;
@@ -961,7 +962,7 @@ private void rerunTransactionQueue(List queue, Path path) {
if (transaction.status == TransactionStatus.NEEDS_ABORT) {
abortTransaction = true;
abortReason = transaction.abortReason;
- if (abortReason.getCode() != DatabaseError.WRITE_CANCELED) {
+ if (abortReason.getCode() != DatabaseErrorCode.WRITE_CANCELED) {
events.addAll(
serverSyncTree.ackUserWrite(
transaction.currentWriteId, /*revert=*/ true, /*persist=*/ false, serverClock));
@@ -1108,7 +1109,7 @@ public void visitTree(Tree> tree) {
});
}
- private Path abortTransactions(Path path, final int reason) {
+ private Path abortTransactions(Path path, final DatabaseErrorCode reason) {
Path affectedPath = getAncestorTransactionNode(path).getPath();
logger.debug("Aborting transactions for path: {}. Affected: {}", path, affectedPath);
@@ -1135,19 +1136,20 @@ public void visitTree(Tree> tree) {
return affectedPath;
}
- private void abortTransactionsAtNode(Tree> node, int reason) {
+ private void abortTransactionsAtNode(Tree> node, DatabaseErrorCode reason) {
List queue = node.getValue();
List events = new ArrayList<>();
if (queue != null) {
List callbacks = new ArrayList<>();
final DatabaseError abortError;
- if (reason == DatabaseError.OVERRIDDEN_BY_SET) {
+ if (reason == DatabaseErrorCode.OVERRIDDEN_BY_SET) {
abortError = DatabaseError.fromStatus(TRANSACTION_OVERRIDE_BY_SET);
} else {
hardAssert(
- reason == DatabaseError.WRITE_CANCELED, "Unknown transaction abort reason: " + reason);
- abortError = DatabaseError.fromCode(DatabaseError.WRITE_CANCELED);
+ reason == DatabaseErrorCode.WRITE_CANCELED,
+ "Unknown transaction abort reason: " + reason);
+ abortError = DatabaseError.fromCode(DatabaseErrorCode.WRITE_CANCELED);
}
int lastSent = -1;
@@ -1170,13 +1172,13 @@ private void abortTransactionsAtNode(Tree> node, int reaso
Repo.this,
transaction.outstandingListener,
QuerySpec.defaultQueryAtPath(transaction.path)));
- if (reason == DatabaseError.OVERRIDDEN_BY_SET) {
+ if (reason == DatabaseErrorCode.OVERRIDDEN_BY_SET) {
events.addAll(
serverSyncTree.ackUserWrite(
transaction.currentWriteId, /*revert=*/ true, /*persist=*/ false, serverClock));
} else {
hardAssert(
- reason == DatabaseError.WRITE_CANCELED,
+ reason == DatabaseErrorCode.WRITE_CANCELED,
"Unknown transaction abort reason: " + reason);
// If it was cancelled, it was already removed from the sync tree
}
diff --git a/src/main/java/com/google/firebase/database/core/RepoManager.java b/src/main/java/com/google/firebase/database/core/RepoManager.java
index a1d0eafce..a7dad878e 100644
--- a/src/main/java/com/google/firebase/database/core/RepoManager.java
+++ b/src/main/java/com/google/firebase/database/core/RepoManager.java
@@ -41,12 +41,11 @@ public RepoManager() {}
* Used for legacy unit tests. The public API should go through FirebaseDatabase which calls
* createRepo.
*/
- public static Repo getRepo(Context ctx, RepoInfo info) throws DatabaseException {
+ public static Repo getRepo(Context ctx, RepoInfo info) {
return instance.getLocalRepo(ctx, info);
}
- public static Repo createRepo(Context ctx, RepoInfo info, FirebaseDatabase database)
- throws DatabaseException {
+ public static Repo createRepo(Context ctx, RepoInfo info, FirebaseDatabase database) {
return instance.createLocalRepo(ctx, info, database);
}
@@ -89,7 +88,7 @@ public static void resume(Context ctx) {
instance.resumeInternal(ctx);
}
- private Repo getLocalRepo(Context ctx, RepoInfo info) throws DatabaseException {
+ private Repo getLocalRepo(Context ctx, RepoInfo info) {
ctx.freeze(); // No-op if it's already frozen
String repoHash = "https://" + info.host + "/" + info.namespace;
synchronized (repos) {
@@ -102,8 +101,7 @@ private Repo getLocalRepo(Context ctx, RepoInfo info) throws DatabaseException {
}
}
- private Repo createLocalRepo(Context ctx, RepoInfo info, FirebaseDatabase database)
- throws DatabaseException {
+ private Repo createLocalRepo(Context ctx, RepoInfo info, FirebaseDatabase database) {
ctx.freeze(); // No-op if it's already frozen
String repoHash = "https://" + info.host + "/" + info.namespace;
synchronized (repos) {
diff --git a/src/main/java/com/google/firebase/database/core/ValidationPath.java b/src/main/java/com/google/firebase/database/core/ValidationPath.java
index 3ea3d03c6..c039cf38e 100644
--- a/src/main/java/com/google/firebase/database/core/ValidationPath.java
+++ b/src/main/java/com/google/firebase/database/core/ValidationPath.java
@@ -38,7 +38,7 @@ public class ValidationPath {
private final List parts = new ArrayList<>();
private int byteLength = 0;
- private ValidationPath(Path path) throws DatabaseException {
+ private ValidationPath(Path path) {
for (ChildKey key : path) {
parts.add(key.asString());
}
@@ -51,7 +51,7 @@ private ValidationPath(Path path) throws DatabaseException {
checkValid();
}
- public static void validateWithObject(Path path, Object value) throws DatabaseException {
+ public static void validateWithObject(Path path, Object value) {
new ValidationPath(path).withObject(value);
}
@@ -89,7 +89,7 @@ private static int utf8Bytes(CharSequence sequence) {
}
@SuppressWarnings({"unchecked", "rawtypes"})
- private void withObject(Object value) throws DatabaseException {
+ private void withObject(Object value) {
if (value instanceof Map) {
Map mapValue = (Map) value;
for (String key : mapValue.keySet()) {
@@ -114,7 +114,7 @@ private void withObject(Object value) throws DatabaseException {
}
}
- private void push(String child) throws DatabaseException {
+ private void push(String child) {
// Count the '/'
if (parts.size() > 0) {
byteLength += 1;
@@ -134,9 +134,9 @@ private String pop() {
return last;
}
- private void checkValid() throws DatabaseException {
+ private void checkValid() {
if (byteLength > MAX_PATH_LENGTH_BYTES) {
- throw new DatabaseException(
+ throw new IllegalArgumentException(
"Data has a key path longer than "
+ MAX_PATH_LENGTH_BYTES
+ " bytes ("
@@ -144,7 +144,7 @@ private void checkValid() throws DatabaseException {
+ ").");
}
if (parts.size() > MAX_PATH_DEPTH) {
- throw new DatabaseException(
+ throw new IllegalArgumentException(
"Path specified exceeds the maximum depth that can be written ("
+ MAX_PATH_DEPTH
+ ") or object contains a cycle "
diff --git a/src/main/java/com/google/firebase/database/snapshot/NodeUtilities.java b/src/main/java/com/google/firebase/database/snapshot/NodeUtilities.java
index 36e1e603c..efd25ac3a 100644
--- a/src/main/java/com/google/firebase/database/snapshot/NodeUtilities.java
+++ b/src/main/java/com/google/firebase/database/snapshot/NodeUtilities.java
@@ -29,11 +29,11 @@
public class NodeUtilities {
// CSOFF: MethodName
- public static Node NodeFromJSON(Object value) throws DatabaseException {
+ public static Node NodeFromJSON(Object value) {
return NodeFromJSON(value, PriorityUtilities.NullPriority());
}
- public static Node NodeFromJSON(Object value, Node priority) throws DatabaseException {
+ public static Node NodeFromJSON(Object value, Node priority) {
try {
if (value instanceof Map) {
Map mapValue = (Map) value;
@@ -104,11 +104,11 @@ public static Node NodeFromJSON(Object value, Node priority) throws DatabaseExce
return new ChildrenNode(childSet, priority);
}
} else {
- throw new DatabaseException(
+ throw new IllegalArgumentException(
"Failed to parse node with class " + value.getClass().toString());
}
} catch (ClassCastException e) {
- throw new DatabaseException("Failed to parse node", e);
+ throw new IllegalArgumentException("Failed to parse node", e);
}
}
// CSON: MethodName
diff --git a/src/main/java/com/google/firebase/database/snapshot/PriorityUtilities.java b/src/main/java/com/google/firebase/database/snapshot/PriorityUtilities.java
index 14c758f67..5976950f5 100644
--- a/src/main/java/com/google/firebase/database/snapshot/PriorityUtilities.java
+++ b/src/main/java/com/google/firebase/database/snapshot/PriorityUtilities.java
@@ -47,7 +47,7 @@ public static Node parsePriority(Path nodePath, Object value) {
Double.valueOf((Long) priority.getValue()), PriorityUtilities.NullPriority());
}
if (!isValidPriority(priority)) {
- throw new DatabaseException(
+ throw new IllegalArgumentException(
(nodePath != null ? "Path '" + nodePath + "'" : "Node")
+ " contains invalid priority: Must be a string, double, ServerValue, or null");
}
diff --git a/src/main/java/com/google/firebase/database/utilities/Utilities.java b/src/main/java/com/google/firebase/database/utilities/Utilities.java
index 5a3c64822..a6928f24a 100644
--- a/src/main/java/com/google/firebase/database/utilities/Utilities.java
+++ b/src/main/java/com/google/firebase/database/utilities/Utilities.java
@@ -42,7 +42,7 @@
public class Utilities {
private static final char[] HEX_CHARACTERS = "0123456789abcdef".toCharArray();
- public static ParsedUrl parseUrl(String url) throws DatabaseException {
+ public static ParsedUrl parseUrl(String url) {
try {
URI uri = URI.create(url);
@@ -86,7 +86,7 @@ public static ParsedUrl parseUrl(String url) throws DatabaseException {
return parsedUrl;
} catch (Exception e) {
- throw new DatabaseException("Invalid Firebase Database url specified: " + url, e);
+ throw new IllegalArgumentException("Invalid Firebase Database url specified: " + url, e);
}
}
diff --git a/src/main/java/com/google/firebase/database/utilities/Validation.java b/src/main/java/com/google/firebase/database/utilities/Validation.java
index 8a0bfcf12..573d1cc49 100644
--- a/src/main/java/com/google/firebase/database/utilities/Validation.java
+++ b/src/main/java/com/google/firebase/database/utilities/Validation.java
@@ -45,16 +45,16 @@ private static boolean isValidPathString(String pathString) {
return !INVALID_PATH_REGEX.matcher(pathString).find();
}
- public static void validatePathString(String pathString) throws DatabaseException {
+ public static void validatePathString(String pathString) {
if (!isValidPathString(pathString)) {
- throw new DatabaseException(
+ throw new IllegalArgumentException(
"Invalid Firebase Database path: "
+ pathString
+ ". Firebase Database paths must not contain '.', '#', '$', '[', or ']'");
}
}
- public static void validateRootPathString(String pathString) throws DatabaseException {
+ public static void validateRootPathString(String pathString) {
if (pathString.startsWith(".info")) {
validatePathString(pathString.substring(5));
} else if (pathString.startsWith("/.info")) {
@@ -76,16 +76,16 @@ private static boolean isValidKey(String key) {
return key.equals(".info") || !INVALID_KEY_REGEX.matcher(key).find();
}
- public static void validateNullableKey(String key) throws DatabaseException {
+ public static void validateNullableKey(String key) {
if (!(key == null || isValidKey(key))) {
- throw new DatabaseException(
+ throw new IllegalArgumentException(
"Invalid key: " + key + ". Keys must not contain '/', '.', '#', '$', '[', or ']'");
}
}
private static void validateDoubleValue(double d) {
if (Double.isInfinite(d) || Double.isNaN(d)) {
- throw new DatabaseException("Invalid value: Value cannot be NaN, Inf or -Inf.");
+ throw new IllegalArgumentException("Invalid value: Value cannot be NaN, Inf or -Inf.");
}
}
@@ -120,21 +120,20 @@ public static void validateWritableObject(Object object) {
}
}
- public static void validateWritableKey(String key) throws DatabaseException {
+ public static void validateWritableKey(String key) {
if (!isWritableKey(key)) {
- throw new DatabaseException(
+ throw new IllegalArgumentException(
"Invalid key: " + key + ". Keys must not contain '/', '.', '#', '$', '[', or ']'");
}
}
- public static void validateWritablePath(Path path) throws DatabaseException {
+ public static void validateWritablePath(Path path) throws IllegalArgumentException {
if (!isWritablePath(path)) {
- throw new DatabaseException("Invalid write location: " + path.toString());
+ throw new IllegalArgumentException("Invalid write location: " + path.toString());
}
}
- public static Map parseAndValidateUpdate(Path path, Map update)
- throws DatabaseException {
+ public static Map parseAndValidateUpdate(Path path, Map update) {
final SortedMap parsedUpdate = new TreeMap<>();
for (Map.Entry entry : update.entrySet()) {
Path updatePath = new Path(entry.getKey());
@@ -143,7 +142,7 @@ public static Map parseAndValidateUpdate(Path path, Map parseAndValidateUpdate(Path path, Map T convertToCustomClass(Object object, GenericTypeIndicator
if (genericTypeIndicatorType instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) genericTypeIndicatorType;
if (!parameterizedType.getRawType().equals(GenericTypeIndicator.class)) {
- throw new DatabaseException(
+ throw new IllegalArgumentException(
"Not a direct subclass of GenericTypeIndicator: " + genericTypeIndicatorType);
}
// We are guaranteed to have exactly one type parameter
Type type = parameterizedType.getActualTypeArguments()[0];
return deserializeToType(object, type);
} else {
- throw new DatabaseException(
+ throw new IllegalArgumentException(
"Not a direct subclass of GenericTypeIndicator: " + genericTypeIndicatorType);
}
}
@@ -126,7 +126,7 @@ private static Object serialize(T obj) {
} else if (obj instanceof Long || obj instanceof Integer) {
return obj;
} else {
- throw new DatabaseException(
+ throw new IllegalArgumentException(
String.format(
"Numbers of type %s are not supported, please use an int, long, float or double",
obj.getClass().getSimpleName()));
@@ -136,7 +136,7 @@ private static Object serialize(T obj) {
} else if (obj instanceof Boolean) {
return obj;
} else if (obj instanceof Character) {
- throw new DatabaseException("Characters are not supported, please use Strings");
+ throw new IllegalArgumentException("Characters are not supported, please use Strings");
} else if (obj instanceof Map) {
Map result = new HashMap<>();
for (Map.Entry