diff --git a/src/main/java/com/google/firebase/auth/AuthErrorCode.java b/src/main/java/com/google/firebase/auth/AuthErrorCode.java index c1625187f..bea067eee 100644 --- a/src/main/java/com/google/firebase/auth/AuthErrorCode.java +++ b/src/main/java/com/google/firebase/auth/AuthErrorCode.java @@ -26,6 +26,11 @@ public enum AuthErrorCode { */ CERTIFICATE_FETCH_FAILED, + /** + * No IdP configuration found for the given identifier. + */ + CONFIGURATION_NOT_FOUND, + /** * A user already exists with the provided email. */ @@ -71,8 +76,16 @@ public enum AuthErrorCode { */ REVOKED_SESSION_COOKIE, + /** + * Tenant ID in the JWT does not match. + */ TENANT_ID_MISMATCH, + /** + * No tenant found for the given identifier. + */ + TENANT_NOT_FOUND, + /** * A user already exists with the provided UID. */ 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 0fc5ef3ea..e98911407 100644 --- a/src/main/java/com/google/firebase/auth/internal/AuthErrorHandler.java +++ b/src/main/java/com/google/firebase/auth/internal/AuthErrorHandler.java @@ -36,6 +36,12 @@ final class AuthErrorHandler extends AbstractHttpErrorHandler ERROR_CODES = ImmutableMap.builder() + .put( + "CONFIGURATION_NOT_FOUND", + new AuthError( + ErrorCode.NOT_FOUND, + "No IdP configuration found corresponding to the provided identifier", + AuthErrorCode.CONFIGURATION_NOT_FOUND)) .put( "DUPLICATE_EMAIL", new AuthError( @@ -67,6 +73,12 @@ final class AuthErrorHandler extends AbstractHttpErrorHandler httpClient; + private final JsonFactory jsonFactory; private HttpResponseInterceptor interceptor; public AuthHttpClient(JsonFactory jsonFactory, HttpRequestFactory requestFactory) { - this.jsonFactory = jsonFactory; AuthErrorHandler authErrorHandler = new AuthErrorHandler(jsonFactory); this.httpClient = new ErrorHandlingHttpClient<>(requestFactory, jsonFactory, authErrorHandler); + this.jsonFactory = jsonFactory; } public static Set generateMask(Map properties) { 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 1f6256cff..640cc2a2f 100644 --- a/src/main/java/com/google/firebase/auth/multitenancy/FirebaseTenantClient.java +++ b/src/main/java/com/google/firebase/auth/multitenancy/FirebaseTenantClient.java @@ -46,15 +46,19 @@ final class FirebaseTenantClient { private final AuthHttpClient httpClient; FirebaseTenantClient(FirebaseApp app) { - checkNotNull(app, "FirebaseApp must not be null"); - String projectId = ImplFirebaseTrampolines.getProjectId(app); + this( + ImplFirebaseTrampolines.getProjectId(checkNotNull(app)), + app.getOptions().getJsonFactory(), + ApiClientUtils.newAuthorizedRequestFactory(app)); + } + + FirebaseTenantClient( + String projectId, JsonFactory jsonFactory, HttpRequestFactory requestFactory) { 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."); this.tenantMgtBaseUrl = String.format(ID_TOOLKIT_URL, "v2", projectId); - JsonFactory jsonFactory = app.getOptions().getJsonFactory(); - HttpRequestFactory requestFactory = ApiClientUtils.newAuthorizedRequestFactory(app); this.httpClient = new AuthHttpClient(jsonFactory, requestFactory); } diff --git a/src/main/java/com/google/firebase/auth/multitenancy/TenantManager.java b/src/main/java/com/google/firebase/auth/multitenancy/TenantManager.java index 11f26b096..febe666f3 100644 --- a/src/main/java/com/google/firebase/auth/multitenancy/TenantManager.java +++ b/src/main/java/com/google/firebase/auth/multitenancy/TenantManager.java @@ -54,8 +54,13 @@ public final class TenantManager { * @hide */ public TenantManager(FirebaseApp firebaseApp) { - this.firebaseApp = firebaseApp; - this.tenantClient = new FirebaseTenantClient(firebaseApp); + this(firebaseApp, new FirebaseTenantClient(firebaseApp)); + } + + @VisibleForTesting + TenantManager(FirebaseApp firebaseApp, FirebaseTenantClient tenantClient) { + this.firebaseApp = checkNotNull(firebaseApp); + this.tenantClient = checkNotNull(tenantClient); this.tenantAwareAuths = new HashMap<>(); } diff --git a/src/test/java/com/google/firebase/auth/FirebaseUserManagerTest.java b/src/test/java/com/google/firebase/auth/FirebaseUserManagerTest.java index a71280b87..99157455d 100644 --- a/src/test/java/com/google/firebase/auth/FirebaseUserManagerTest.java +++ b/src/test/java/com/google/firebase/auth/FirebaseUserManagerTest.java @@ -45,7 +45,6 @@ import com.google.firebase.FirebaseOptions; import com.google.firebase.TestOnlyImplFirebaseTrampolines; import com.google.firebase.auth.FirebaseUserManager.EmailLinkType; -import com.google.firebase.auth.internal.AuthHttpClient; import com.google.firebase.auth.multitenancy.TenantAwareFirebaseAuth; import com.google.firebase.auth.multitenancy.TenantManager; import com.google.firebase.internal.SdkUtils; @@ -92,6 +91,10 @@ public class FirebaseUserManagerTest { private static final String TENANTS_BASE_URL = PROJECT_BASE_URL + "/tenants"; + private static final String SAML_RESPONSE = TestUtils.loadResource("saml.json"); + + private static final String OIDC_RESPONSE = TestUtils.loadResource("oidc.json"); + @After public void tearDown() { TestOnlyImplFirebaseTrampolines.clearInstancesForTest(); @@ -279,7 +282,7 @@ public void testGetUsersMultipleIdentifierTypes() throws Exception { ).replace("'", "\"")); UidIdentifier doesntExist = new UidIdentifier("this-uid-doesnt-exist"); - List ids = ImmutableList.of( + List ids = ImmutableList.of( new UidIdentifier("uid1"), new EmailIdentifier("user2@example.com"), new PhoneIdentifier("+15555550003"), @@ -301,7 +304,7 @@ private Collection userRecordsToUids(Collection userRecords) } @Test - public void testInvalidUidIdentifier() throws Exception { + public void testInvalidUidIdentifier() { try { new UidIdentifier("too long " + Strings.repeat(".", 128)); fail("No error thrown for invalid uid"); @@ -311,7 +314,7 @@ public void testInvalidUidIdentifier() throws Exception { } @Test - public void testInvalidEmailIdentifier() throws Exception { + public void testInvalidEmailIdentifier() { try { new EmailIdentifier("invalid email addr"); fail("No error thrown for invalid email"); @@ -321,7 +324,7 @@ public void testInvalidEmailIdentifier() throws Exception { } @Test - public void testInvalidPhoneIdentifier() throws Exception { + public void testInvalidPhoneIdentifier() { try { new PhoneIdentifier("invalid phone number"); fail("No error thrown for invalid phone number"); @@ -331,7 +334,7 @@ public void testInvalidPhoneIdentifier() throws Exception { } @Test - public void testInvalidProviderIdentifier() throws Exception { + public void testInvalidProviderIdentifier() { try { new ProviderIdentifier("", "valid-uid"); fail("No error thrown for invalid provider id"); @@ -454,7 +457,7 @@ public void testDeleteUser() throws Exception { } @Test - public void testDeleteUsersExceeds1000() throws Exception { + public void testDeleteUsersExceeds1000() { FirebaseApp.initializeApp(new FirebaseOptions.Builder() .setCredentials(credentials) .build()); @@ -471,7 +474,7 @@ public void testDeleteUsersExceeds1000() throws Exception { } @Test - public void testDeleteUsersInvalidId() throws Exception { + public void testDeleteUsersInvalidId() { FirebaseApp.initializeApp(new FirebaseOptions.Builder() .setCredentials(credentials) .build()); @@ -1481,8 +1484,7 @@ public void testUnexpectedHttpError() { @Test public void testCreateOidcProvider() throws Exception { - TestResponseInterceptor interceptor = initializeAppForUserManagement( - TestUtils.loadResource("oidc.json")); + TestResponseInterceptor interceptor = initializeAppForUserManagement(OIDC_RESPONSE); OidcProviderConfig.CreateRequest createRequest = new OidcProviderConfig.CreateRequest() .setProviderId("oidc.provider-id") @@ -1507,8 +1509,7 @@ public void testCreateOidcProvider() throws Exception { @Test public void testCreateOidcProviderAsync() throws Exception { - TestResponseInterceptor interceptor = initializeAppForUserManagement( - TestUtils.loadResource("oidc.json")); + TestResponseInterceptor interceptor = initializeAppForUserManagement(OIDC_RESPONSE); OidcProviderConfig.CreateRequest createRequest = new OidcProviderConfig.CreateRequest() .setProviderId("oidc.provider-id") @@ -1534,8 +1535,7 @@ public void testCreateOidcProviderAsync() throws Exception { @Test public void testCreateOidcProviderMinimal() throws Exception { - TestResponseInterceptor interceptor = initializeAppForUserManagement( - TestUtils.loadResource("oidc.json")); + TestResponseInterceptor interceptor = initializeAppForUserManagement(OIDC_RESPONSE); // Only the 'enabled' and 'displayName' fields can be omitted from an OIDC provider config // creation request. OidcProviderConfig.CreateRequest createRequest = @@ -1557,25 +1557,32 @@ public void testCreateOidcProviderMinimal() throws Exception { } @Test - public void testCreateOidcProviderError() throws Exception { - TestResponseInterceptor interceptor = - initializeAppForUserManagementWithStatusCode(404, - "{\"error\": {\"message\": \"INTERNAL_ERROR\"}}"); + public void testCreateOidcProviderError() { + String message = "{\"error\": {\"message\": \"INTERNAL_ERROR\"}}"; + MockLowLevelHttpResponse response = new MockLowLevelHttpResponse() + .setContent(message) + .setStatusCode(500); + FirebaseAuth auth = getRetryDisabledAuth(response); OidcProviderConfig.CreateRequest createRequest = new OidcProviderConfig.CreateRequest().setProviderId("oidc.provider-id"); + try { - FirebaseAuth.getInstance().createOidcProviderConfig(createRequest); + auth.createOidcProviderConfig(createRequest); fail("No error thrown for invalid response"); } catch (FirebaseAuthException e) { - assertEquals(ErrorCode.NOT_FOUND, e.getErrorCode()); + assertEquals(ErrorCode.INTERNAL, e.getErrorCode()); + assertEquals( + "Unexpected HTTP response with status: 500\n" + message, + e.getMessage()); + assertTrue(e.getCause() instanceof HttpResponseException); + assertNotNull(e.getHttpResponse()); + assertNull(e.getAuthErrorCode()); } - - checkUrl(interceptor, "POST", PROJECT_BASE_URL + "/oauthIdpConfigs"); } @Test public void testCreateOidcProviderMissingId() throws Exception { - initializeAppForUserManagement(TestUtils.loadResource("oidc.json")); + initializeAppForUserManagement(OIDC_RESPONSE); OidcProviderConfig.CreateRequest createRequest = new OidcProviderConfig.CreateRequest() .setDisplayName("DISPLAY_NAME") @@ -1593,8 +1600,7 @@ public void testCreateOidcProviderMissingId() throws Exception { @Test public void testTenantAwareCreateOidcProvider() throws Exception { TestResponseInterceptor interceptor = initializeAppForTenantAwareUserManagement( - "TENANT_ID", - TestUtils.loadResource("oidc.json")); + "TENANT_ID", OIDC_RESPONSE); OidcProviderConfig.CreateRequest createRequest = new OidcProviderConfig.CreateRequest() .setProviderId("oidc.provider-id") @@ -1613,8 +1619,7 @@ public void testTenantAwareCreateOidcProvider() throws Exception { @Test public void testUpdateOidcProvider() throws Exception { - TestResponseInterceptor interceptor = initializeAppForUserManagement( - TestUtils.loadResource("oidc.json")); + TestResponseInterceptor interceptor = initializeAppForUserManagement(OIDC_RESPONSE); OidcProviderConfig.UpdateRequest request = new OidcProviderConfig.UpdateRequest("oidc.provider-id") .setDisplayName("DISPLAY_NAME") @@ -1638,8 +1643,7 @@ public void testUpdateOidcProvider() throws Exception { @Test public void testUpdateOidcProviderAsync() throws Exception { - TestResponseInterceptor interceptor = initializeAppForUserManagement( - TestUtils.loadResource("oidc.json")); + TestResponseInterceptor interceptor = initializeAppForUserManagement(OIDC_RESPONSE); OidcProviderConfig.UpdateRequest request = new OidcProviderConfig.UpdateRequest("oidc.provider-id") .setDisplayName("DISPLAY_NAME") @@ -1664,8 +1668,7 @@ public void testUpdateOidcProviderAsync() throws Exception { @Test public void testUpdateOidcProviderMinimal() throws Exception { - TestResponseInterceptor interceptor = initializeAppForUserManagement( - TestUtils.loadResource("oidc.json")); + TestResponseInterceptor interceptor = initializeAppForUserManagement(OIDC_RESPONSE); OidcProviderConfig.UpdateRequest request = new OidcProviderConfig.UpdateRequest("oidc.provider-id").setDisplayName("DISPLAY_NAME"); @@ -1683,7 +1686,7 @@ public void testUpdateOidcProviderMinimal() throws Exception { @Test public void testUpdateOidcProviderConfigNoValues() throws Exception { - initializeAppForUserManagement(TestUtils.loadResource("oidc.json")); + initializeAppForUserManagement(OIDC_RESPONSE); try { FirebaseAuth.getInstance().updateOidcProviderConfig( new OidcProviderConfig.UpdateRequest("oidc.provider-id")); @@ -1695,25 +1698,32 @@ public void testUpdateOidcProviderConfigNoValues() throws Exception { @Test public void testUpdateOidcProviderConfigError() { - TestResponseInterceptor interceptor = - initializeAppForUserManagementWithStatusCode(404, - "{\"error\": {\"message\": \"INTERNAL_ERROR\"}}"); + String message = "{\"error\": {\"message\": \"INTERNAL_ERROR\"}}"; + MockLowLevelHttpResponse response = new MockLowLevelHttpResponse() + .setContent(message) + .setStatusCode(500); + FirebaseAuth auth = getRetryDisabledAuth(response); OidcProviderConfig.UpdateRequest request = new OidcProviderConfig.UpdateRequest("oidc.provider-id").setDisplayName("DISPLAY_NAME"); + try { - FirebaseAuth.getInstance().updateOidcProviderConfig(request); + auth.updateOidcProviderConfig(request); fail("No error thrown for invalid response"); } catch (FirebaseAuthException e) { - assertEquals(ErrorCode.NOT_FOUND, e.getErrorCode()); + assertEquals(ErrorCode.INTERNAL, e.getErrorCode()); + assertEquals( + "Unexpected HTTP response with status: 500\n" + message, + e.getMessage()); + assertTrue(e.getCause() instanceof HttpResponseException); + assertNotNull(e.getHttpResponse()); + assertNull(e.getAuthErrorCode()); } - checkUrl(interceptor, "PATCH", PROJECT_BASE_URL + "/oauthIdpConfigs/oidc.provider-id"); } @Test public void testTenantAwareUpdateOidcProvider() throws Exception { TestResponseInterceptor interceptor = initializeAppForTenantAwareUserManagement( - "TENANT_ID", - TestUtils.loadResource("oidc.json")); + "TENANT_ID", OIDC_RESPONSE); TenantAwareFirebaseAuth tenantAwareAuth = FirebaseAuth.getInstance().getTenantManager().getAuthForTenant("TENANT_ID"); OidcProviderConfig.UpdateRequest request = @@ -1740,8 +1750,7 @@ public void testTenantAwareUpdateOidcProvider() throws Exception { @Test public void testGetOidcProviderConfig() throws Exception { - TestResponseInterceptor interceptor = initializeAppForUserManagement( - TestUtils.loadResource("oidc.json")); + TestResponseInterceptor interceptor = initializeAppForUserManagement(OIDC_RESPONSE); OidcProviderConfig config = FirebaseAuth.getInstance().getOidcProviderConfig("oidc.provider-id"); @@ -1753,8 +1762,7 @@ public void testGetOidcProviderConfig() throws Exception { @Test public void testGetOidcProviderConfigAsync() throws Exception { - TestResponseInterceptor interceptor = initializeAppForUserManagement( - TestUtils.loadResource("oidc.json")); + TestResponseInterceptor interceptor = initializeAppForUserManagement(OIDC_RESPONSE); OidcProviderConfig config = FirebaseAuth.getInstance().getOidcProviderConfigAsync("oidc.provider-id").get(); @@ -1766,7 +1774,7 @@ public void testGetOidcProviderConfigAsync() throws Exception { @Test public void testGetOidcProviderConfigMissingId() throws Exception { - initializeAppForUserManagement(TestUtils.loadResource("oidc.json")); + initializeAppForUserManagement(OIDC_RESPONSE); try { FirebaseAuth.getInstance().getOidcProviderConfig(null); @@ -1778,7 +1786,7 @@ public void testGetOidcProviderConfigMissingId() throws Exception { @Test public void testGetOidcProviderConfigInvalidId() throws Exception { - initializeAppForUserManagement(TestUtils.loadResource("oidc.json")); + initializeAppForUserManagement(OIDC_RESPONSE); try { FirebaseAuth.getInstance().getOidcProviderConfig("saml.invalid-oidc-provider-id"); @@ -1789,7 +1797,7 @@ public void testGetOidcProviderConfigInvalidId() throws Exception { } @Test - public void testGetOidcProviderConfigWithNotFoundError() throws Exception { + public void testGetOidcProviderConfigWithNotFoundError() { TestResponseInterceptor interceptor = initializeAppForUserManagementWithStatusCode(404, "{\"error\": {\"message\": \"CONFIGURATION_NOT_FOUND\"}}"); @@ -1798,6 +1806,13 @@ public void testGetOidcProviderConfigWithNotFoundError() throws Exception { fail("No error thrown for invalid response"); } catch (FirebaseAuthException e) { assertEquals(ErrorCode.NOT_FOUND, e.getErrorCode()); + assertEquals( + "No IdP configuration found corresponding to the provided identifier " + + "(CONFIGURATION_NOT_FOUND).", + e.getMessage()); + assertTrue(e.getCause() instanceof HttpResponseException); + assertNotNull(e.getHttpResponse()); + assertEquals(AuthErrorCode.CONFIGURATION_NOT_FOUND, e.getAuthErrorCode()); } checkUrl(interceptor, "GET", PROJECT_BASE_URL + "/oauthIdpConfigs/oidc.provider-id"); } @@ -1805,8 +1820,7 @@ public void testGetOidcProviderConfigWithNotFoundError() throws Exception { @Test public void testGetTenantAwareOidcProviderConfig() throws Exception { TestResponseInterceptor interceptor = initializeAppForTenantAwareUserManagement( - "TENANT_ID", - TestUtils.loadResource("oidc.json")); + "TENANT_ID", OIDC_RESPONSE); TenantAwareFirebaseAuth tenantAwareAuth = FirebaseAuth.getInstance().getTenantManager().getAuthForTenant("TENANT_ID"); @@ -1856,18 +1870,25 @@ public void testListOidcProviderConfigsAsync() throws Exception { } @Test - public void testListOidcProviderConfigsError() throws Exception { - TestResponseInterceptor interceptor = - initializeAppForUserManagementWithStatusCode(404, - "{\"error\": {\"message\": \"INTERNAL_ERROR\"}}"); + public void testListOidcProviderConfigsError() { + String message = "{\"error\": {\"message\": \"INTERNAL_ERROR\"}}"; + MockLowLevelHttpResponse response = new MockLowLevelHttpResponse() + .setContent(message) + .setStatusCode(500); + FirebaseAuth auth = getRetryDisabledAuth(response); try { - FirebaseAuth.getInstance().listOidcProviderConfigs(null, 99); + auth.listOidcProviderConfigs(null, 99); fail("No error thrown for invalid response"); } catch (FirebaseAuthException e) { - assertEquals(ErrorCode.NOT_FOUND, e.getErrorCode()); + assertEquals(ErrorCode.INTERNAL, e.getErrorCode()); + assertEquals( + "Unexpected HTTP response with status: 500\n" + message, + e.getMessage()); + assertTrue(e.getCause() instanceof HttpResponseException); + assertNotNull(e.getHttpResponse()); + assertNull(e.getAuthErrorCode()); } - checkUrl(interceptor, "GET", PROJECT_BASE_URL + "/oauthIdpConfigs"); } @Test @@ -1975,6 +1996,13 @@ public void testDeleteOidcProviderConfigWithNotFoundError() { fail("No error thrown for invalid response"); } catch (FirebaseAuthException e) { assertEquals(ErrorCode.NOT_FOUND, e.getErrorCode()); + assertEquals( + "No IdP configuration found corresponding to the provided identifier " + + "(CONFIGURATION_NOT_FOUND).", + e.getMessage()); + assertTrue(e.getCause() instanceof HttpResponseException); + assertNotNull(e.getHttpResponse()); + assertEquals(AuthErrorCode.CONFIGURATION_NOT_FOUND, e.getAuthErrorCode()); } checkUrl(interceptor, "DELETE", PROJECT_BASE_URL + "/oauthIdpConfigs/oidc.UNKNOWN"); } @@ -1996,8 +2024,7 @@ public void testTenantAwareDeleteOidcProviderConfig() throws Exception { @Test public void testCreateSamlProvider() throws Exception { - TestResponseInterceptor interceptor = initializeAppForUserManagement( - TestUtils.loadResource("saml.json")); + TestResponseInterceptor interceptor = initializeAppForUserManagement(SAML_RESPONSE); SamlProviderConfig.CreateRequest createRequest = new SamlProviderConfig.CreateRequest() .setProviderId("saml.provider-id") @@ -2042,8 +2069,7 @@ public void testCreateSamlProvider() throws Exception { @Test public void testCreateSamlProviderAsync() throws Exception { - TestResponseInterceptor interceptor = initializeAppForUserManagement( - TestUtils.loadResource("saml.json")); + TestResponseInterceptor interceptor = initializeAppForUserManagement(SAML_RESPONSE); SamlProviderConfig.CreateRequest createRequest = new SamlProviderConfig.CreateRequest() .setProviderId("saml.provider-id") @@ -2089,8 +2115,7 @@ public void testCreateSamlProviderAsync() throws Exception { @Test public void testCreateSamlProviderMinimal() throws Exception { - TestResponseInterceptor interceptor = initializeAppForUserManagement( - TestUtils.loadResource("saml.json")); + TestResponseInterceptor interceptor = initializeAppForUserManagement(SAML_RESPONSE); // Only the 'enabled', 'displayName', and 'signRequest' fields can be omitted from a SAML // provider config creation request. SamlProviderConfig.CreateRequest createRequest = @@ -2130,23 +2155,31 @@ public void testCreateSamlProviderMinimal() throws Exception { @Test public void testCreateSamlProviderError() { - TestResponseInterceptor interceptor = - initializeAppForUserManagementWithStatusCode(404, - "{\"error\": {\"message\": \"INTERNAL_ERROR\"}}"); + String message = "{\"error\": {\"message\": \"INTERNAL_ERROR\"}}"; + MockLowLevelHttpResponse response = new MockLowLevelHttpResponse() + .setContent(message) + .setStatusCode(500); + FirebaseAuth auth = getRetryDisabledAuth(response); SamlProviderConfig.CreateRequest createRequest = new SamlProviderConfig.CreateRequest().setProviderId("saml.provider-id"); + try { - FirebaseAuth.getInstance().createSamlProviderConfig(createRequest); + auth.createSamlProviderConfig(createRequest); fail("No error thrown for invalid response"); } catch (FirebaseAuthException e) { - assertEquals(ErrorCode.NOT_FOUND, e.getErrorCode()); + assertEquals(ErrorCode.INTERNAL, e.getErrorCode()); + assertEquals( + "Unexpected HTTP response with status: 500\n" + message, + e.getMessage()); + assertTrue(e.getCause() instanceof HttpResponseException); + assertNotNull(e.getHttpResponse()); + assertNull(e.getAuthErrorCode()); } - checkUrl(interceptor, "POST", PROJECT_BASE_URL + "/inboundSamlConfigs"); } @Test public void testCreateSamlProviderMissingId() throws Exception { - initializeAppForUserManagement(TestUtils.loadResource("saml.json")); + initializeAppForUserManagement(SAML_RESPONSE); SamlProviderConfig.CreateRequest createRequest = new SamlProviderConfig.CreateRequest() .setDisplayName("DISPLAY_NAME") @@ -2168,8 +2201,7 @@ public void testCreateSamlProviderMissingId() throws Exception { @Test public void testTenantAwareCreateSamlProvider() throws Exception { TestResponseInterceptor interceptor = initializeAppForTenantAwareUserManagement( - "TENANT_ID", - TestUtils.loadResource("saml.json")); + "TENANT_ID", SAML_RESPONSE); SamlProviderConfig.CreateRequest createRequest = new SamlProviderConfig.CreateRequest() .setProviderId("saml.provider-id") @@ -2192,8 +2224,7 @@ public void testTenantAwareCreateSamlProvider() throws Exception { @Test public void testUpdateSamlProvider() throws Exception { - TestResponseInterceptor interceptor = initializeAppForUserManagement( - TestUtils.loadResource("saml.json")); + TestResponseInterceptor interceptor = initializeAppForUserManagement(SAML_RESPONSE); SamlProviderConfig.UpdateRequest updateRequest = new SamlProviderConfig.UpdateRequest("saml.provider-id") .setDisplayName("DISPLAY_NAME") @@ -2240,8 +2271,7 @@ public void testUpdateSamlProvider() throws Exception { @Test public void testUpdateSamlProviderAsync() throws Exception { - TestResponseInterceptor interceptor = initializeAppForUserManagement( - TestUtils.loadResource("saml.json")); + TestResponseInterceptor interceptor = initializeAppForUserManagement(SAML_RESPONSE); SamlProviderConfig.UpdateRequest updateRequest = new SamlProviderConfig.UpdateRequest("saml.provider-id") .setDisplayName("DISPLAY_NAME") @@ -2289,8 +2319,7 @@ public void testUpdateSamlProviderAsync() throws Exception { @Test public void testUpdateSamlProviderMinimal() throws Exception { - TestResponseInterceptor interceptor = initializeAppForUserManagement( - TestUtils.loadResource("saml.json")); + TestResponseInterceptor interceptor = initializeAppForUserManagement(SAML_RESPONSE); SamlProviderConfig.UpdateRequest request = new SamlProviderConfig.UpdateRequest("saml.provider-id").setDisplayName("DISPLAY_NAME"); @@ -2308,7 +2337,7 @@ public void testUpdateSamlProviderMinimal() throws Exception { @Test public void testUpdateSamlProviderConfigNoValues() throws Exception { - initializeAppForUserManagement(TestUtils.loadResource("saml.json")); + initializeAppForUserManagement(SAML_RESPONSE); try { FirebaseAuth.getInstance().updateSamlProviderConfig( new SamlProviderConfig.UpdateRequest("saml.provider-id")); @@ -2319,26 +2348,33 @@ public void testUpdateSamlProviderConfigNoValues() throws Exception { } @Test - public void testUpdateSamlProviderConfigError() throws Exception { - TestResponseInterceptor interceptor = - initializeAppForUserManagementWithStatusCode(404, - "{\"error\": {\"message\": \"INTERNAL_ERROR\"}}"); + public void testUpdateSamlProviderConfigError() { + String message = "{\"error\": {\"message\": \"INTERNAL_ERROR\"}}"; + MockLowLevelHttpResponse response = new MockLowLevelHttpResponse() + .setContent(message) + .setStatusCode(500); + FirebaseAuth auth = getRetryDisabledAuth(response); SamlProviderConfig.UpdateRequest request = new SamlProviderConfig.UpdateRequest("saml.provider-id").setDisplayName("DISPLAY_NAME"); + try { - FirebaseAuth.getInstance().updateSamlProviderConfig(request); + auth.updateSamlProviderConfig(request); fail("No error thrown for invalid response"); } catch (FirebaseAuthException e) { - assertEquals(ErrorCode.NOT_FOUND, e.getErrorCode()); + assertEquals(ErrorCode.INTERNAL, e.getErrorCode()); + assertEquals( + "Unexpected HTTP response with status: 500\n" + message, + e.getMessage()); + assertTrue(e.getCause() instanceof HttpResponseException); + assertNotNull(e.getHttpResponse()); + assertNull(e.getAuthErrorCode()); } - checkUrl(interceptor, "PATCH", PROJECT_BASE_URL + "/inboundSamlConfigs/saml.provider-id"); } @Test public void testTenantAwareUpdateSamlProvider() throws Exception { TestResponseInterceptor interceptor = initializeAppForTenantAwareUserManagement( - "TENANT_ID", - TestUtils.loadResource("saml.json")); + "TENANT_ID", SAML_RESPONSE); TenantAwareFirebaseAuth tenantAwareAuth = FirebaseAuth.getInstance().getTenantManager().getAuthForTenant("TENANT_ID"); SamlProviderConfig.UpdateRequest updateRequest = @@ -2370,8 +2406,7 @@ public void testTenantAwareUpdateSamlProvider() throws Exception { @Test public void testGetSamlProviderConfig() throws Exception { - TestResponseInterceptor interceptor = initializeAppForUserManagement( - TestUtils.loadResource("saml.json")); + TestResponseInterceptor interceptor = initializeAppForUserManagement(SAML_RESPONSE); SamlProviderConfig config = FirebaseAuth.getInstance().getSamlProviderConfig("saml.provider-id"); @@ -2383,8 +2418,7 @@ public void testGetSamlProviderConfig() throws Exception { @Test public void testGetSamlProviderConfigAsync() throws Exception { - TestResponseInterceptor interceptor = initializeAppForUserManagement( - TestUtils.loadResource("saml.json")); + TestResponseInterceptor interceptor = initializeAppForUserManagement(SAML_RESPONSE); SamlProviderConfig config = FirebaseAuth.getInstance().getSamlProviderConfigAsync("saml.provider-id").get(); @@ -2408,7 +2442,7 @@ public void testGetSamlProviderConfigMissingId() throws Exception { @Test public void testGetSamlProviderConfigInvalidId() throws Exception { - initializeAppForUserManagement(TestUtils.loadResource("saml.json")); + initializeAppForUserManagement(SAML_RESPONSE); try { FirebaseAuth.getInstance().getSamlProviderConfig("oidc.invalid-saml-provider-id"); @@ -2428,6 +2462,13 @@ public void testGetSamlProviderConfigWithNotFoundError() { fail("No error thrown for invalid response"); } catch (FirebaseAuthException e) { assertEquals(ErrorCode.NOT_FOUND, e.getErrorCode()); + assertEquals( + "No IdP configuration found corresponding to the provided identifier " + + "(CONFIGURATION_NOT_FOUND).", + e.getMessage()); + assertTrue(e.getCause() instanceof HttpResponseException); + assertNotNull(e.getHttpResponse()); + assertEquals(AuthErrorCode.CONFIGURATION_NOT_FOUND, e.getAuthErrorCode()); } checkUrl(interceptor, "GET", PROJECT_BASE_URL + "/inboundSamlConfigs/saml.provider-id"); } @@ -2435,8 +2476,7 @@ public void testGetSamlProviderConfigWithNotFoundError() { @Test public void testGetTenantAwareSamlProviderConfig() throws Exception { TestResponseInterceptor interceptor = initializeAppForTenantAwareUserManagement( - "TENANT_ID", - TestUtils.loadResource("saml.json")); + "TENANT_ID", SAML_RESPONSE); TenantAwareFirebaseAuth tenantAwareAuth = FirebaseAuth.getInstance().getTenantManager().getAuthForTenant("TENANT_ID"); @@ -2487,18 +2527,25 @@ public void testListSamlProviderConfigsAsync() throws Exception { } @Test - public void testListSamlProviderConfigsError() throws Exception { - TestResponseInterceptor interceptor = - initializeAppForUserManagementWithStatusCode(404, - "{\"error\": {\"message\": \"INTERNAL_ERROR\"}}"); + public void testListSamlProviderConfigsError() { + String message = "{\"error\": {\"message\": \"INTERNAL_ERROR\"}}"; + MockLowLevelHttpResponse response = new MockLowLevelHttpResponse() + .setContent(message) + .setStatusCode(500); + FirebaseAuth auth = getRetryDisabledAuth(response); try { - FirebaseAuth.getInstance().listSamlProviderConfigs(null, 99); + auth.listSamlProviderConfigs(null, 99); fail("No error thrown for invalid response"); } catch (FirebaseAuthException e) { - assertEquals(ErrorCode.NOT_FOUND, e.getErrorCode()); + assertEquals(ErrorCode.INTERNAL, e.getErrorCode()); + assertEquals( + "Unexpected HTTP response with status: 500\n" + message, + e.getMessage()); + assertTrue(e.getCause() instanceof HttpResponseException); + assertNotNull(e.getHttpResponse()); + assertNull(e.getAuthErrorCode()); } - checkUrl(interceptor, "GET", PROJECT_BASE_URL + "/inboundSamlConfigs"); } @Test @@ -2606,6 +2653,13 @@ public void testDeleteSamlProviderConfigWithNotFoundError() { fail("No error thrown for invalid response"); } catch (FirebaseAuthException e) { assertEquals(ErrorCode.NOT_FOUND, e.getErrorCode()); + assertEquals( + "No IdP configuration found corresponding to the provided identifier " + + "(CONFIGURATION_NOT_FOUND).", + e.getMessage()); + assertTrue(e.getCause() instanceof HttpResponseException); + assertNotNull(e.getHttpResponse()); + assertEquals(AuthErrorCode.CONFIGURATION_NOT_FOUND, e.getAuthErrorCode()); } checkUrl(interceptor, "DELETE", PROJECT_BASE_URL + "/inboundSamlConfigs/saml.UNKNOWN"); } diff --git a/src/test/java/com/google/firebase/auth/ProviderConfigTestUtils.java b/src/test/java/com/google/firebase/auth/ProviderConfigTestUtils.java index 588ce3317..e027882c0 100644 --- a/src/test/java/com/google/firebase/auth/ProviderConfigTestUtils.java +++ b/src/test/java/com/google/firebase/auth/ProviderConfigTestUtils.java @@ -36,8 +36,9 @@ public static void assertOidcProviderConfigDoesNotExist( fail("No error thrown for getting a deleted OIDC provider config."); } catch (ExecutionException e) { assertTrue(e.getCause() instanceof FirebaseAuthException); - assertEquals(ErrorCode.NOT_FOUND, - ((FirebaseAuthException) e.getCause()).getErrorCode()); + FirebaseAuthException authException = (FirebaseAuthException) e.getCause(); + assertEquals(ErrorCode.NOT_FOUND, authException.getErrorCode()); + assertEquals(AuthErrorCode.CONFIGURATION_NOT_FOUND, authException.getAuthErrorCode()); } } @@ -48,8 +49,9 @@ public static void assertSamlProviderConfigDoesNotExist( fail("No error thrown for getting a deleted SAML provider config."); } catch (ExecutionException e) { assertTrue(e.getCause() instanceof FirebaseAuthException); - assertEquals(ErrorCode.NOT_FOUND, - ((FirebaseAuthException) e.getCause()).getErrorCode()); + FirebaseAuthException authException = (FirebaseAuthException) e.getCause(); + assertEquals(ErrorCode.NOT_FOUND, authException.getErrorCode()); + assertEquals(AuthErrorCode.CONFIGURATION_NOT_FOUND, authException.getAuthErrorCode()); } } diff --git a/src/test/java/com/google/firebase/auth/multitenancy/FirebaseTenantClientTest.java b/src/test/java/com/google/firebase/auth/multitenancy/FirebaseTenantClientTest.java index 86eb6b4a8..b1fb0aaa6 100644 --- a/src/test/java/com/google/firebase/auth/multitenancy/FirebaseTenantClientTest.java +++ b/src/test/java/com/google/firebase/auth/multitenancy/FirebaseTenantClientTest.java @@ -18,6 +18,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -26,6 +27,7 @@ import com.google.api.client.http.GenericUrl; import com.google.api.client.http.HttpHeaders; import com.google.api.client.http.HttpRequest; +import com.google.api.client.http.HttpResponseException; import com.google.api.client.json.GenericJson; import com.google.api.client.json.JsonFactory; import com.google.api.client.testing.http.MockHttpTransport; @@ -37,6 +39,7 @@ import com.google.firebase.FirebaseApp; import com.google.firebase.FirebaseOptions; import com.google.firebase.TestOnlyImplFirebaseTrampolines; +import com.google.firebase.auth.AuthErrorCode; import com.google.firebase.auth.FirebaseAuth; import com.google.firebase.auth.FirebaseAuthException; import com.google.firebase.auth.MockGoogleCredentials; @@ -91,6 +94,11 @@ public void testGetTenantWithNotFoundError() { fail("No error thrown for invalid response"); } catch (FirebaseAuthException e) { assertEquals(ErrorCode.NOT_FOUND, e.getErrorCode()); + assertEquals( + "No tenant found for the given identifier (TENANT_NOT_FOUND).", e.getMessage()); + assertTrue(e.getCause() instanceof HttpResponseException); + assertNotNull(e.getHttpResponse()); + assertEquals(AuthErrorCode.TENANT_NOT_FOUND, e.getAuthErrorCode()); } checkUrl(interceptor, "GET", TENANTS_BASE_URL + "/UNKNOWN"); } @@ -183,16 +191,21 @@ public void testCreateTenantMinimal() throws Exception { @Test public void testCreateTenantError() { - TestResponseInterceptor interceptor = - initializeAppForTenantManagementWithStatusCode(404, - "{\"error\": {\"message\": \"INTERNAL_ERROR\"}}"); + String message = "{\"error\": {\"message\": \"INTERNAL_ERROR\"}}"; + TenantManager tenantManager = createRetryDisabledTenantManager(new MockLowLevelHttpResponse() + .setStatusCode(500) + .setContent(message)); + try { - FirebaseAuth.getInstance().getTenantManager().createTenant(new Tenant.CreateRequest()); + tenantManager.createTenant(new Tenant.CreateRequest()); fail("No error thrown for invalid response"); } catch (FirebaseAuthException e) { - assertEquals(ErrorCode.NOT_FOUND, e.getErrorCode()); + assertEquals(ErrorCode.INTERNAL, e.getErrorCode()); + assertEquals("Unexpected HTTP response with status: 500\n" + message, e.getMessage()); + assertTrue(e.getCause() instanceof HttpResponseException); + assertNotNull(e.getHttpResponse()); + assertNull(e.getAuthErrorCode()); } - checkUrl(interceptor, "POST", TENANTS_BASE_URL); } @Test @@ -252,18 +265,23 @@ public void testUpdateTenantNoValues() throws Exception { @Test public void testUpdateTenantError() { - TestResponseInterceptor interceptor = - initializeAppForTenantManagementWithStatusCode(404, - "{\"error\": {\"message\": \"INTERNAL_ERROR\"}}"); + String message = "{\"error\": {\"message\": \"INTERNAL_ERROR\"}}"; + TenantManager tenantManager = createRetryDisabledTenantManager(new MockLowLevelHttpResponse() + .setStatusCode(500) + .setContent(message)); Tenant.UpdateRequest request = new Tenant.UpdateRequest("TENANT_1").setDisplayName("DISPLAY_NAME"); + try { - FirebaseAuth.getInstance().getTenantManager().updateTenant(request); + tenantManager.updateTenant(request); fail("No error thrown for invalid response"); } catch (FirebaseAuthException e) { - assertEquals(ErrorCode.NOT_FOUND, e.getErrorCode()); + assertEquals(ErrorCode.INTERNAL, e.getErrorCode()); + assertEquals("Unexpected HTTP response with status: 500\n" + message, e.getMessage()); + assertTrue(e.getCause() instanceof HttpResponseException); + assertNotNull(e.getHttpResponse()); + assertNull(e.getAuthErrorCode()); } - checkUrl(interceptor, "PATCH", TENANTS_BASE_URL + "/TENANT_1"); } @Test @@ -286,6 +304,10 @@ public void testDeleteTenantWithNotFoundError() { fail("No error thrown for invalid response"); } catch (FirebaseAuthException e) { assertEquals(ErrorCode.NOT_FOUND, e.getErrorCode()); + assertEquals("No tenant found for the given identifier (TENANT_NOT_FOUND).", e.getMessage()); + assertTrue(e.getCause() instanceof HttpResponseException); + assertNotNull(e.getHttpResponse()); + assertEquals(AuthErrorCode.TENANT_NOT_FOUND, e.getAuthErrorCode()); } checkUrl(interceptor, "DELETE", TENANTS_BASE_URL + "/UNKNOWN"); } @@ -319,7 +341,17 @@ private static void checkUrl(TestResponseInterceptor interceptor, String method, } private static TestResponseInterceptor initializeAppForTenantManagement(String... responses) { - initializeAppWithResponses(responses); + List mocks = new ArrayList<>(); + for (String response : responses) { + mocks.add(new MockLowLevelHttpResponse().setContent(response)); + } + MockHttpTransport transport = new MultiRequestMockHttpTransport(mocks); + FirebaseApp.initializeApp(FirebaseOptions.builder() + .setCredentials(credentials) + .setHttpTransport(transport) + .setProjectId("test-project-id") + .build()); + TestResponseInterceptor interceptor = new TestResponseInterceptor(); FirebaseAuth.getInstance().getTenantManager().setInterceptor(interceptor); return interceptor; @@ -327,7 +359,7 @@ private static TestResponseInterceptor initializeAppForTenantManagement(String.. private static TestResponseInterceptor initializeAppForTenantManagementWithStatusCode( int statusCode, String response) { - FirebaseApp.initializeApp(new FirebaseOptions.Builder() + FirebaseApp.initializeApp(FirebaseOptions.builder() .setCredentials(credentials) .setHttpTransport( new MockHttpTransport.Builder() @@ -341,17 +373,16 @@ private static TestResponseInterceptor initializeAppForTenantManagementWithStatu return interceptor; } - private static void initializeAppWithResponses(String... responses) { - List mocks = new ArrayList<>(); - for (String response : responses) { - mocks.add(new MockLowLevelHttpResponse().setContent(response)); - } - MockHttpTransport transport = new MultiRequestMockHttpTransport(mocks); - FirebaseApp.initializeApp(new FirebaseOptions.Builder() + private static TenantManager createRetryDisabledTenantManager(MockLowLevelHttpResponse response) { + MockHttpTransport transport = new MockHttpTransport.Builder() + .setLowLevelHttpResponse(response) + .build(); + FirebaseApp app = FirebaseApp.initializeApp(FirebaseOptions.builder() .setCredentials(credentials) - .setHttpTransport(transport) - .setProjectId("test-project-id") .build()); + FirebaseTenantClient tenantClient = new FirebaseTenantClient( + "test-project-id", Utils.getDefaultJsonFactory(), transport.createRequestFactory()); + return new TenantManager(app, tenantClient); } private static GenericJson parseRequestContent(TestResponseInterceptor interceptor) diff --git a/src/test/java/com/google/firebase/auth/multitenancy/TenantManagerIT.java b/src/test/java/com/google/firebase/auth/multitenancy/TenantManagerIT.java index 1949dc5a2..44d75e830 100644 --- a/src/test/java/com/google/firebase/auth/multitenancy/TenantManagerIT.java +++ b/src/test/java/com/google/firebase/auth/multitenancy/TenantManagerIT.java @@ -28,6 +28,7 @@ import com.google.api.core.ApiFutures; import com.google.common.util.concurrent.MoreExecutors; import com.google.firebase.ErrorCode; +import com.google.firebase.auth.AuthErrorCode; import com.google.firebase.auth.FirebaseAuth; import com.google.firebase.auth.FirebaseAuthException; import com.google.firebase.testing.IntegrationTestUtils; @@ -81,8 +82,9 @@ public void testTenantLifecycle() throws Exception { fail("No error thrown for getting a deleted tenant"); } catch (ExecutionException e) { assertTrue(e.getCause() instanceof FirebaseAuthException); - assertEquals(ErrorCode.NOT_FOUND, - ((FirebaseAuthException) e.getCause()).getErrorCode()); + FirebaseAuthException authException = (FirebaseAuthException) e.getCause(); + assertEquals(ErrorCode.NOT_FOUND, authException.getErrorCode()); + assertEquals(AuthErrorCode.TENANT_NOT_FOUND, authException.getAuthErrorCode()); } }