From 68edeb3dc1a8f6b143197e32a76ec524431039f6 Mon Sep 17 00:00:00 2001 From: Micah Stairs Date: Mon, 4 May 2020 16:59:24 -0400 Subject: [PATCH 1/2] Add operation to list OIDC provider configs. --- .../firebase/auth/AbstractFirebaseAuth.java | 80 +++++- .../firebase/auth/FirebaseUserManager.java | 22 ++ .../auth/ListProviderConfigsPage.java | 257 ++++++++++++++++++ .../google/firebase/auth/ListTenantsPage.java | 2 +- .../google/firebase/auth/ListUsersPage.java | 9 +- .../ListOidcProviderConfigsResponse.java | 63 +++++ .../internal/ListProviderConfigsResponse.java | 37 +++ .../auth/internal/ListTenantsResponse.java | 1 - .../google/firebase/auth/FirebaseAuthIT.java | 131 +++++++++ .../auth/FirebaseUserManagerTest.java | 91 ++++++- .../firebase/auth/ListUsersPageTest.java | 33 +-- src/test/resources/listOidc.json | 15 + 12 files changed, 705 insertions(+), 36 deletions(-) create mode 100644 src/main/java/com/google/firebase/auth/ListProviderConfigsPage.java create mode 100644 src/main/java/com/google/firebase/auth/internal/ListOidcProviderConfigsResponse.java create mode 100644 src/main/java/com/google/firebase/auth/internal/ListProviderConfigsResponse.java create mode 100644 src/test/resources/listOidc.json diff --git a/src/main/java/com/google/firebase/auth/AbstractFirebaseAuth.java b/src/main/java/com/google/firebase/auth/AbstractFirebaseAuth.java index 40166dd28..7f10cd96d 100644 --- a/src/main/java/com/google/firebase/auth/AbstractFirebaseAuth.java +++ b/src/main/java/com/google/firebase/auth/AbstractFirebaseAuth.java @@ -29,8 +29,10 @@ import com.google.firebase.FirebaseApp; import com.google.firebase.auth.FirebaseUserManager.EmailLinkType; import com.google.firebase.auth.FirebaseUserManager.UserImportRequest; +import com.google.firebase.auth.ListProviderConfigsPage.DefaultOidcProviderConfigSource; +import com.google.firebase.auth.ListProviderConfigsPage.ProviderConfigPageFactory; import com.google.firebase.auth.ListUsersPage.DefaultUserSource; -import com.google.firebase.auth.ListUsersPage.PageFactory; +import com.google.firebase.auth.ListUsersPage.UserPageFactory; import com.google.firebase.auth.UserRecord; import com.google.firebase.auth.internal.FirebaseTokenFactory; import com.google.firebase.internal.CallableOperation; @@ -500,8 +502,8 @@ private CallableOperation listUsersOp( @Nullable final String pageToken, final int maxResults) { checkNotDestroyed(); final FirebaseUserManager userManager = getUserManager(); - final PageFactory factory = - new PageFactory(new DefaultUserSource(userManager, jsonFactory), maxResults, pageToken); + final DefaultUserSource source = new DefaultUserSource(userManager, jsonFactory); + final UserPageFactory factory = new UserPageFactory(source, maxResults, pageToken); return new CallableOperation() { @Override protected ListUsersPage execute() throws FirebaseAuthException { @@ -1058,6 +1060,78 @@ protected OidcProviderConfig execute() throws FirebaseAuthException { }; } + /** + * Gets a page of OIDC Auth provider configs starting from the specified {@code pageToken}. + * + * @param pageToken A non-empty page token string, or null to retrieve the first page of provider + * configs. + * @param maxResults Maximum number of provider configs to include in the returned page. This may + * not exceed 100. + * @return A {@link ListProviderConfigsPage} 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 ListProviderConfigsPage listOidcProviderConfigs( + @Nullable String pageToken, int maxResults) throws FirebaseAuthException { + return listOidcProviderConfigsOp(pageToken, maxResults).call(); + } + + /** + * Similar to {@link #listlistOidcProviderConfigs(String)} but performs the operation + * asynchronously. + * + * @param pageToken A non-empty page token string, or null to retrieve the first page of provider + * configs. + * @return An {@code ApiFuture} which will complete successfully with a + * {@link ListProviderConfigsPage} instance. If an error occurs while retrieving provider + * config data, the future throws an exception. + * @throws IllegalArgumentException If the specified page token is empty. + */ + public ApiFuture> listOidcProviderConfigsAsync( + @Nullable String pageToken) { + return listOidcProviderConfigsAsync( + pageToken, + FirebaseUserManager.MAX_LIST_PROVIDER_CONFIGS_RESULTS); + } + + /** + * Similar to {@link #listOidcProviderConfigs(String, int)} but performs the operation + * asynchronously. + * + * @param pageToken A non-empty page token string, or null to retrieve the first page of provider + * configs. + * @param maxResults Maximum number of provider configs to include in the returned page. This may + * not exceed 100. + * @return An {@code ApiFuture} which will complete successfully with a + * {@link ListProviderConfigsPage} instance. If an error occurs while retrieving provider + * config data, the future throws an exception. + * @throws IllegalArgumentException If the specified page token is empty, or max results value is + * invalid. + */ + public ApiFuture> listOidcProviderConfigsAsync( + @Nullable String pageToken, + int maxResults) { + return listOidcProviderConfigsOp(pageToken, maxResults).callAsync(firebaseApp); + } + + private CallableOperation, FirebaseAuthException> + listOidcProviderConfigsOp(@Nullable final String pageToken, final int maxResults) { + checkNotDestroyed(); + final FirebaseUserManager userManager = getUserManager(); + final DefaultOidcProviderConfigSource source = new DefaultOidcProviderConfigSource(userManager); + final ProviderConfigPageFactory factory = + new ProviderConfigPageFactory(source, maxResults, pageToken); + return + new CallableOperation, FirebaseAuthException>() { + @Override + protected ListProviderConfigsPage execute() + throws FirebaseAuthException { + return factory.create(); + } + }; + } + /** * Deletes the provider config identified by the specified provider ID. * diff --git a/src/main/java/com/google/firebase/auth/FirebaseUserManager.java b/src/main/java/com/google/firebase/auth/FirebaseUserManager.java index d3c4c6186..2605d62b8 100644 --- a/src/main/java/com/google/firebase/auth/FirebaseUserManager.java +++ b/src/main/java/com/google/firebase/auth/FirebaseUserManager.java @@ -43,6 +43,7 @@ import com.google.firebase.auth.internal.DownloadAccountResponse; import com.google.firebase.auth.internal.GetAccountInfoResponse; import com.google.firebase.auth.internal.HttpErrorResponse; +import com.google.firebase.auth.internal.ListOidcProviderConfigsResponse; import com.google.firebase.auth.internal.ListTenantsResponse; import com.google.firebase.auth.internal.UploadAccountResponse; import com.google.firebase.internal.ApiClientUtils; @@ -94,6 +95,7 @@ class FirebaseUserManager { .put("INVALID_DYNAMIC_LINK_DOMAIN", "invalid-dynamic-link-domain") .build(); + static final int MAX_LIST_PROVIDER_CONFIGS_RESULTS = 100; static final int MAX_LIST_TENANTS_RESULTS = 1000; static final int MAX_LIST_USERS_RESULTS = 1000; static final int MAX_IMPORT_USERS = 1000; @@ -338,6 +340,26 @@ OidcProviderConfig getOidcProviderConfig(String providerId) throws FirebaseAuthE return sendRequest("GET", url, null, OidcProviderConfig.class); } + ListOidcProviderConfigsResponse listOidcProviderConfigs(int maxResults, String pageToken) + throws FirebaseAuthException { + ImmutableMap.Builder builder = + ImmutableMap.builder().put("pageSize", maxResults); + if (pageToken != null) { + checkArgument(!pageToken.equals( + ListTenantsPage.END_OF_LIST), "invalid end of list page token"); + builder.put("nextPageToken", pageToken); + } + + GenericUrl url = new GenericUrl(idpConfigMgtBaseUrl + "/oauthIdpConfigs"); + url.putAll(builder.build()); + ListOidcProviderConfigsResponse response = + sendRequest("GET", url, null, ListOidcProviderConfigsResponse.class); + if (response == null) { + throw new FirebaseAuthException(INTERNAL_ERROR, "Failed to retrieve provider configs."); + } + return response; + } + void deleteProviderConfig(String providerId) throws FirebaseAuthException { GenericUrl url = new GenericUrl(idpConfigMgtBaseUrl + getOidcUrlSuffix(providerId)); sendRequest("DELETE", url, null, GenericJson.class); diff --git a/src/main/java/com/google/firebase/auth/ListProviderConfigsPage.java b/src/main/java/com/google/firebase/auth/ListProviderConfigsPage.java new file mode 100644 index 000000000..0d3f41525 --- /dev/null +++ b/src/main/java/com/google/firebase/auth/ListProviderConfigsPage.java @@ -0,0 +1,257 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.firebase.auth; + +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.api.client.json.JsonFactory; +import com.google.api.gax.paging.Page; +import com.google.common.collect.ImmutableList; +import com.google.firebase.auth.internal.DownloadAccountResponse; +import com.google.firebase.auth.internal.ListOidcProviderConfigsResponse; +import com.google.firebase.auth.internal.ListProviderConfigsResponse; +import com.google.firebase.internal.NonNull; +import com.google.firebase.internal.Nullable; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; + +/** + * Represents a page of {@link ProviderConfig} instances. + * + *

Provides methods for iterating over the provider configs in the current page, and calling up + * subsequent pages of provider configs. + * + *

Instances of this class are thread-safe and immutable. + */ +public class ListProviderConfigsPage implements Page { + + static final String END_OF_LIST = ""; + + private final ListProviderConfigsResponse currentBatch; + private final ProviderConfigSource source; + private final int maxResults; + + private ListProviderConfigsPage( + @NonNull ListProviderConfigsResponse currentBatch, + @NonNull ProviderConfigSource source, + int maxResults) { + this.currentBatch = checkNotNull(currentBatch); + this.source = checkNotNull(source); + this.maxResults = maxResults; + } + + /** + * Checks if there is another page of provider configs available to retrieve. + * + * @return true if another page is available, or false otherwise. + */ + @Override + public boolean hasNextPage() { + return !END_OF_LIST.equals(currentBatch.getPageToken()); + } + + /** + * Returns the string token that identifies the next page. + * + *

Never returns null. Returns empty string if there are no more pages available to be + * retrieved. + * + * @return A non-null string token (possibly empty, representing no more pages) + */ + @NonNull + @Override + public String getNextPageToken() { + return currentBatch.getPageToken(); + } + + /** + * Returns the next page of provider configs. + * + * @return A new {@link ListProviderConfigsPage} instance, or null if there are no more pages. + */ + @Nullable + @Override + public ListProviderConfigsPage getNextPage() { + if (hasNextPage()) { + ProviderConfigPageFactory factory = + new ProviderConfigPageFactory(source, maxResults, currentBatch.getPageToken()); + try { + return factory.create(); + } catch (FirebaseAuthException e) { + throw new RuntimeException(e); + } + } + return null; + } + + /** + * Returns an {@link Iterable} that facilitates transparently iterating over all the provider + * configs in the current Firebase project, starting from this page. + * + *

The {@link Iterator} instances produced by the returned {@link Iterable} never buffers more + * than one page of provider configs at a time. It is safe to abandon the iterators (i.e. break + * the loops) at any time. + * + * @return a new {@link Iterable} instance. + */ + @NonNull + @Override + public Iterable iterateAll() { + return new ProviderConfigIterable(this); + } + + /** + * Returns an {@link Iterable} over the provider configs in this page. + * + * @return a {@link Iterable} instance. + */ + @NonNull + @Override + public Iterable getValues() { + return currentBatch.getProviderConfigs(); + } + + private static class ProviderConfigIterable implements Iterable { + + private final ListProviderConfigsPage startingPage; + + ProviderConfigIterable(@NonNull ListProviderConfigsPage startingPage) { + this.startingPage = checkNotNull(startingPage, "starting page must not be null"); + } + + @Override + @NonNull + public Iterator iterator() { + return new ProviderConfigIterator(startingPage); + } + + /** + * An {@link Iterator} that cycles through provider configs, one at a time. + * + *

It buffers the last retrieved batch of provider configs in memory. The {@code maxResults} + * parameter is an upper bound on the batch size. + */ + private static class ProviderConfigIterator implements Iterator { + + private ListProviderConfigsPage currentPage; + private List batch; + private int index = 0; + + private ProviderConfigIterator(ListProviderConfigsPage startingPage) { + setCurrentPage(startingPage); + } + + @Override + public boolean hasNext() { + if (index == batch.size()) { + if (currentPage.hasNextPage()) { + setCurrentPage(currentPage.getNextPage()); + } else { + return false; + } + } + + return index < batch.size(); + } + + @Override + public T next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + return batch.get(index++); + } + + @Override + public void remove() { + throw new UnsupportedOperationException("remove operation not supported"); + } + + private void setCurrentPage(ListProviderConfigsPage page) { + this.currentPage = checkNotNull(page); + this.batch = ImmutableList.copyOf(page.getValues()); + this.index = 0; + } + } + } + + /** + * Represents a source of provider config data that can be queried to load a batch of provider + * configs. + */ + interface ProviderConfigSource { + @NonNull + ListProviderConfigsResponse fetch(int maxResults, String pageToken) + throws FirebaseAuthException; + } + + static class DefaultOidcProviderConfigSource implements ProviderConfigSource { + + private final FirebaseUserManager userManager; + + DefaultOidcProviderConfigSource(FirebaseUserManager userManager) { + this.userManager = checkNotNull(userManager, "user manager must not be null"); + } + + @Override + public ListOidcProviderConfigsResponse fetch(int maxResults, String pageToken) + throws FirebaseAuthException { + return userManager.listOidcProviderConfigs(maxResults, pageToken); + } + } + + // TODO(micahstairs): Add DefaultSamlProviderConfigSource class. + + /** + * A simple factory class for {@link ProviderConfigsPage} instances. + * + *

Performs argument validation before attempting to load any provider config data (which is + * expensive, and hence may be performed asynchronously on a separate thread). + */ + static class ProviderConfigPageFactory { + + private final ProviderConfigSource source; + private final int maxResults; + private final String pageToken; + + ProviderConfigPageFactory(@NonNull ProviderConfigSource source) { + this(source, FirebaseUserManager.MAX_LIST_PROVIDER_CONFIGS_RESULTS, null); + } + + ProviderConfigPageFactory( + @NonNull ProviderConfigSource source, + int maxResults, + @Nullable String pageToken) { + checkArgument( + maxResults > 0 && maxResults <= FirebaseUserManager.MAX_LIST_PROVIDER_CONFIGS_RESULTS, + "maxResults must be a positive integer that does not exceed %s", + FirebaseUserManager.MAX_LIST_PROVIDER_CONFIGS_RESULTS); + checkArgument(!END_OF_LIST.equals(pageToken), "invalid end of list page token"); + this.source = checkNotNull(source, "source must not be null"); + this.maxResults = maxResults; + this.pageToken = pageToken; + } + + ListProviderConfigsPage create() throws FirebaseAuthException { + ListProviderConfigsResponse batch = source.fetch(maxResults, pageToken); + return new ListProviderConfigsPage(batch, source, maxResults); + } + } +} + diff --git a/src/main/java/com/google/firebase/auth/ListTenantsPage.java b/src/main/java/com/google/firebase/auth/ListTenantsPage.java index 5b07c16be..851d54526 100644 --- a/src/main/java/com/google/firebase/auth/ListTenantsPage.java +++ b/src/main/java/com/google/firebase/auth/ListTenantsPage.java @@ -113,7 +113,7 @@ public Iterable iterateAll() { } /** - * Returns an {@link Iterable} over the users in this page. + * Returns an {@link Iterable} over the tenants in this page. * * @return a {@link Iterable} instance. */ diff --git a/src/main/java/com/google/firebase/auth/ListUsersPage.java b/src/main/java/com/google/firebase/auth/ListUsersPage.java index f406366ba..a298db64a 100644 --- a/src/main/java/com/google/firebase/auth/ListUsersPage.java +++ b/src/main/java/com/google/firebase/auth/ListUsersPage.java @@ -80,7 +80,8 @@ public String getNextPageToken() { @Override public ListUsersPage getNextPage() { if (hasNextPage()) { - PageFactory factory = new PageFactory(source, maxResults, currentBatch.getNextPageToken()); + UserPageFactory factory = + new UserPageFactory(source, maxResults, currentBatch.getNextPageToken()); try { return factory.create(); } catch (FirebaseAuthException e) { @@ -237,17 +238,17 @@ String getNextPageToken() { * before attempting to load any user data (which is expensive, and hence may be performed * asynchronously on a separate thread). */ - static class PageFactory { + static class UserPageFactory { private final UserSource source; private final int maxResults; private final String pageToken; - PageFactory(@NonNull UserSource source) { + UserPageFactory(@NonNull UserSource source) { this(source, FirebaseUserManager.MAX_LIST_USERS_RESULTS, null); } - PageFactory(@NonNull UserSource source, int maxResults, @Nullable String pageToken) { + UserPageFactory(@NonNull UserSource source, int maxResults, @Nullable String pageToken) { checkArgument(maxResults > 0 && maxResults <= FirebaseUserManager.MAX_LIST_USERS_RESULTS, "maxResults must be a positive integer that does not exceed %s", FirebaseUserManager.MAX_LIST_USERS_RESULTS); diff --git a/src/main/java/com/google/firebase/auth/internal/ListOidcProviderConfigsResponse.java b/src/main/java/com/google/firebase/auth/internal/ListOidcProviderConfigsResponse.java new file mode 100644 index 000000000..dc1df5c77 --- /dev/null +++ b/src/main/java/com/google/firebase/auth/internal/ListOidcProviderConfigsResponse.java @@ -0,0 +1,63 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.firebase.auth.internal; + +import com.google.api.client.util.Key; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableList; +import com.google.firebase.auth.OidcProviderConfig; +import com.google.firebase.auth.Tenant; +import java.util.List; + +/** + * JSON data binding for ListOAuthIdpConfigsResponse messages sent by Google identity toolkit + * service. + */ +public final class ListOidcProviderConfigsResponse + implements ListProviderConfigsResponse { + + @Key("oauthIdpConfigs") + private List providerConfigs; + + @Key("nextPageToken") + private String pageToken; + + @VisibleForTesting + public ListOidcProviderConfigsResponse( + List providerConfigs, + String pageToken) { + this.providerConfigs = providerConfigs; + this.pageToken = pageToken; + } + + public ListOidcProviderConfigsResponse() { } + + @Override + public List getProviderConfigs() { + return providerConfigs == null ? ImmutableList.of() : providerConfigs; + } + + @Override + public boolean hasProviderConfigs() { + return providerConfigs != null && !providerConfigs.isEmpty(); + } + + @Override + public String getPageToken() { + return pageToken == null ? "" : pageToken; + } +} diff --git a/src/main/java/com/google/firebase/auth/internal/ListProviderConfigsResponse.java b/src/main/java/com/google/firebase/auth/internal/ListProviderConfigsResponse.java new file mode 100644 index 000000000..c81205a56 --- /dev/null +++ b/src/main/java/com/google/firebase/auth/internal/ListProviderConfigsResponse.java @@ -0,0 +1,37 @@ +/* + * Copyright 2020 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.firebase.auth.internal; + +import com.google.api.client.util.Key; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableList; +import com.google.firebase.auth.ListProviderConfigsPage; +import com.google.firebase.auth.ProviderConfig; +import com.google.firebase.auth.Tenant; +import java.util.List; + +/** + * Interface for config list response messages sent by Google identity toolkit service. + */ +public interface ListProviderConfigsResponse { + + public List getProviderConfigs(); + + public boolean hasProviderConfigs(); + + public String getPageToken(); +} diff --git a/src/main/java/com/google/firebase/auth/internal/ListTenantsResponse.java b/src/main/java/com/google/firebase/auth/internal/ListTenantsResponse.java index 4977061e0..b612793d2 100644 --- a/src/main/java/com/google/firebase/auth/internal/ListTenantsResponse.java +++ b/src/main/java/com/google/firebase/auth/internal/ListTenantsResponse.java @@ -19,7 +19,6 @@ import com.google.api.client.util.Key; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.ImmutableList; -import com.google.firebase.auth.ListTenantsPage; import com.google.firebase.auth.Tenant; import java.util.List; diff --git a/src/test/java/com/google/firebase/auth/FirebaseAuthIT.java b/src/test/java/com/google/firebase/auth/FirebaseAuthIT.java index b7a2d6db2..24cf7a277 100644 --- a/src/test/java/com/google/firebase/auth/FirebaseAuthIT.java +++ b/src/test/java/com/google/firebase/auth/FirebaseAuthIT.java @@ -1066,6 +1066,137 @@ public void testTenantAwareOidcProviderConfigLifecycle() throws Exception { } } + @Test + public void testListOidcProviderConfigs() throws Exception { + final List providerIds = new ArrayList<>(); + + try { + // Create provider configs + for (int i = 0; i < 3; i++) { + String providerId = "oidc.provider-id" + i; + providerIds.add(providerId); + OidcProviderConfig.CreateRequest createRequest = new OidcProviderConfig.CreateRequest() + .setProviderId(providerId) + .setClientId("CLIENT_ID") + .setIssuer("https://oidc.com/issuer"); + auth.createOidcProviderConfig(createRequest); + } + + // Test list by batches + final AtomicInteger collected = new AtomicInteger(0); + ListProviderConfigsPage page = + auth.listOidcProviderConfigsAsync(null).get(); + while (page != null) { + for (OidcProviderConfig providerConfig : page.getValues()) { + if (providerIds.contains(providerConfig.getProviderId())) { + collected.incrementAndGet(); + assertEquals("CLIENT_ID", providerConfig.getClientId()); + assertEquals("https://oidc.com/issuer", providerConfig.getIssuer()); + } + } + page = page.getNextPage(); + } + assertEquals(providerIds.size(), collected.get()); + + // Test iterate all + collected.set(0); + page = auth.listOidcProviderConfigsAsync(null).get(); + for (OidcProviderConfig providerConfig : page.iterateAll()) { + if (providerIds.contains(providerConfig.getProviderId())) { + collected.incrementAndGet(); + assertEquals("CLIENT_ID", providerConfig.getClientId()); + assertEquals("https://oidc.com/issuer", providerConfig.getIssuer()); + } + } + assertEquals(providerIds.size(), collected.get()); + + // Test iterate async + collected.set(0); + final Semaphore semaphore = new Semaphore(0); + final AtomicReference error = new AtomicReference<>(); + ApiFuture> pageFuture = + auth.listOidcProviderConfigsAsync(null); + ApiFutures.addCallback( + pageFuture, + new ApiFutureCallback>() { + @Override + public void onFailure(Throwable t) { + error.set(t); + semaphore.release(); + } + + @Override + public void onSuccess(ListProviderConfigsPage result) { + for (OidcProviderConfig providerConfig : result.iterateAll()) { + if (providerIds.contains(providerConfig.getProviderId())) { + collected.incrementAndGet(); + assertEquals("CLIENT_ID", providerConfig.getClientId()); + assertEquals("https://oidc.com/issuer", providerConfig.getIssuer()); + } + } + semaphore.release(); + } + }, MoreExecutors.directExecutor()); + semaphore.acquire(); + assertEquals(providerIds.size(), collected.get()); + assertNull(error.get()); + } finally { + // Delete provider configs + for (String providerId : providerIds) { + auth.deleteProviderConfigAsync(providerId).get(); + } + } + } + + @Test + public void testTenantAwareListOidcProviderConfigs() throws Exception { + // Create tenant to use + TenantManager tenantManager = auth.getTenantManager(); + Tenant.CreateRequest tenantCreateRequest = + new Tenant.CreateRequest().setDisplayName("DisplayName"); + String tenantId = tenantManager.createTenant(tenantCreateRequest).getTenantId(); + TenantAwareFirebaseAuth tenantAwareAuth = auth.getTenantManager().getAuthForTenant(tenantId); + + try { + final List providerIds = new ArrayList<>(); + try { + + // Create provider configs + for (int i = 0; i < 3; i++) { + String providerId = "oidc.provider-id" + i; + providerIds.add(providerId); + OidcProviderConfig.CreateRequest createRequest = new OidcProviderConfig.CreateRequest() + .setProviderId(providerId) + .setClientId("CLIENT_ID") + .setIssuer("https://oidc.com/issuer"); + tenantAwareAuth.createOidcProviderConfig(createRequest); + } + + // List provider configs + final AtomicInteger collected = new AtomicInteger(0); + ListProviderConfigsPage page = + tenantAwareAuth.listOidcProviderConfigsAsync(null).get(); + for (OidcProviderConfig providerConfig : page.iterateAll()) { + if (providerIds.contains(providerConfig.getProviderId())) { + collected.incrementAndGet(); + assertEquals("CLIENT_ID", providerConfig.getClientId()); + assertEquals("https://oidc.com/issuer", providerConfig.getIssuer()); + } + } + assertEquals(providerIds.size(), collected.get()); + + } finally { + // Delete provider configs + for (String providerId : providerIds) { + tenantAwareAuth.deleteProviderConfigAsync(providerId).get(); + } + } + } finally { + // Delete tenant + tenantManager.deleteTenantAsync(tenantId).get(); + } + } + private Map parseLinkParameters(String link) throws Exception { Map result = new HashMap<>(); int queryBegin = link.indexOf('?'); diff --git a/src/test/java/com/google/firebase/auth/FirebaseUserManagerTest.java b/src/test/java/com/google/firebase/auth/FirebaseUserManagerTest.java index ed6ea15c1..31738dfa6 100644 --- a/src/test/java/com/google/firebase/auth/FirebaseUserManagerTest.java +++ b/src/test/java/com/google/firebase/auth/FirebaseUserManagerTest.java @@ -1400,7 +1400,7 @@ public void testCreateOidcProvider() throws Exception { OidcProviderConfig config = FirebaseAuth.getInstance().createOidcProviderConfig(createRequest); - checkOidcProviderConfig(config); + checkOidcProviderConfig(config, "oidc.provider-id"); checkRequestHeaders(interceptor); checkUrl(interceptor, "POST", PROJECT_BASE_URL + "/oauthIdpConfigs"); GenericJson parsed = parseRequestContent(interceptor); @@ -1416,11 +1416,11 @@ public void testCreateOidcProvider() throws Exception { public void testCreateOidcProviderMinimal() throws Exception { TestResponseInterceptor interceptor = initializeAppForUserManagement( TestUtils.loadResource("oidc.json")); - // Only the 'enabled' field can be omitted from an OIDC provider config creation request. + // Only the 'enabled' and 'displayName' fields can be omitted from an OIDC provider config + // creation request. OidcProviderConfig.CreateRequest createRequest = new OidcProviderConfig.CreateRequest() .setProviderId("oidc.provider-id") - .setDisplayName("DISPLAY_NAME") .setClientId("CLIENT_ID") .setIssuer("https://oidc.com/issuer"); @@ -1428,7 +1428,7 @@ public void testCreateOidcProviderMinimal() throws Exception { checkRequestHeaders(interceptor); checkUrl(interceptor, "POST", PROJECT_BASE_URL + "/oauthIdpConfigs"); GenericJson parsed = parseRequestContent(interceptor); - assertEquals("DISPLAY_NAME", parsed.get("displayName")); + assertNull(parsed.get("displayName")); assertNull(parsed.get("enabled")); assertEquals("CLIENT_ID", parsed.get("clientId")); assertEquals("https://oidc.com/issuer", parsed.get("issuer")); @@ -1504,7 +1504,7 @@ public void testUpdateOidcProvider() throws Exception { OidcProviderConfig config = FirebaseAuth.getInstance().updateOidcProviderConfig(request); - checkOidcProviderConfig(config); + checkOidcProviderConfig(config, "oidc.provider-id"); checkRequestHeaders(interceptor); checkUrl(interceptor, "PATCH", PROJECT_BASE_URL + "/oauthIdpConfigs/oidc.provider-id"); GenericUrl url = interceptor.getResponse().getRequest().getUrl(); @@ -1525,7 +1525,7 @@ public void testUpdateOidcProviderMinimal() throws Exception { OidcProviderConfig config = FirebaseAuth.getInstance().updateOidcProviderConfig(request); - checkOidcProviderConfig(config); + checkOidcProviderConfig(config, "oidc.provider-id"); checkRequestHeaders(interceptor); checkUrl(interceptor, "PATCH", PROJECT_BASE_URL + "/oauthIdpConfigs/oidc.provider-id"); GenericUrl url = interceptor.getResponse().getRequest().getUrl(); @@ -1580,7 +1580,7 @@ public void testTenantAwareUpdateOidcProvider() throws Exception { OidcProviderConfig config = tenantAwareAuth.updateOidcProviderConfig(request); - checkOidcProviderConfig(config); + checkOidcProviderConfig(config, "oidc.provider-id"); checkRequestHeaders(interceptor); String expectedUrl = TENANTS_BASE_URL + "/TENANT_ID/oauthIdpConfigs/oidc.provider-id"; checkUrl(interceptor, "PATCH", expectedUrl); @@ -1601,7 +1601,7 @@ public void testGetOidcProviderConfig() throws Exception { OidcProviderConfig config = FirebaseAuth.getInstance().getOidcProviderConfig("oidc.provider-id"); - checkOidcProviderConfig(config); + checkOidcProviderConfig(config, "oidc.provider-id"); checkRequestHeaders(interceptor); checkUrl(interceptor, "GET", PROJECT_BASE_URL + "/oauthIdpConfigs/oidc.provider-id"); } @@ -1630,11 +1630,80 @@ public void testGetTenantAwareOidcProviderConfig() throws Exception { OidcProviderConfig config = tenantAwareAuth.getOidcProviderConfig("oidc.provider-id"); - checkOidcProviderConfig(config); + checkOidcProviderConfig(config, "oidc.provider-id"); checkRequestHeaders(interceptor); checkUrl(interceptor, "GET", TENANTS_BASE_URL + "/TENANT_ID/oauthIdpConfigs/oidc.provider-id"); } + @Test + public void testListOidcProviderConfigs() throws Exception { + final TestResponseInterceptor interceptor = initializeAppForUserManagement( + TestUtils.loadResource("listOidc.json")); + ListProviderConfigsPage page = + FirebaseAuth.getInstance().listOidcProviderConfigsAsync(null, 99).get(); + + ImmutableList providerConfigs = ImmutableList.copyOf(page.getValues()); + assertEquals(2, providerConfigs.size()); + checkOidcProviderConfig(providerConfigs.get(0), "oidc.provider-id1"); + checkOidcProviderConfig(providerConfigs.get(1), "oidc.provider-id2"); + assertEquals("", page.getNextPageToken()); + checkRequestHeaders(interceptor); + checkUrl(interceptor, "GET", PROJECT_BASE_URL + "/oauthIdpConfigs"); + GenericUrl url = interceptor.getResponse().getRequest().getUrl(); + assertEquals(99, url.getFirst("pageSize")); + assertNull(url.getFirst("nextPageToken")); + } + + @Test + public void testListOidcProviderConfigsWithPageToken() throws Exception { + final TestResponseInterceptor interceptor = initializeAppForUserManagement( + TestUtils.loadResource("listOidc.json")); + ListProviderConfigsPage page = + FirebaseAuth.getInstance().listOidcProviderConfigsAsync("token", 99).get(); + + ImmutableList providerConfigs = ImmutableList.copyOf(page.getValues()); + assertEquals(2, providerConfigs.size()); + checkOidcProviderConfig(providerConfigs.get(0), "oidc.provider-id1"); + checkOidcProviderConfig(providerConfigs.get(1), "oidc.provider-id2"); + assertEquals("", page.getNextPageToken()); + checkRequestHeaders(interceptor); + checkUrl(interceptor, "GET", PROJECT_BASE_URL + "/oauthIdpConfigs"); + GenericUrl url = interceptor.getResponse().getRequest().getUrl(); + assertEquals(99, url.getFirst("pageSize")); + assertEquals("token", url.getFirst("nextPageToken")); + } + + @Test + public void testListZeroOidcProviderConfigs() throws Exception { + TestResponseInterceptor interceptor = initializeAppForUserManagement("{}"); + ListProviderConfigsPage page = + FirebaseAuth.getInstance().listOidcProviderConfigsAsync(null).get(); + assertTrue(Iterables.isEmpty(page.getValues())); + assertEquals("", page.getNextPageToken()); + checkRequestHeaders(interceptor); + } + + @Test + public void testTenantAwareListOidcProviderConfigs() throws Exception { + final TestResponseInterceptor interceptor = initializeAppForTenantAwareUserManagement( + "TENANT_ID", + TestUtils.loadResource("listOidc.json")); + TenantAwareFirebaseAuth tenantAwareAuth = + FirebaseAuth.getInstance().getTenantManager().getAuthForTenant("TENANT_ID"); + ListProviderConfigsPage page = + tenantAwareAuth.listOidcProviderConfigsAsync(null, 99).get(); + + ImmutableList providerConfigs = ImmutableList.copyOf(page.getValues()); + assertEquals(2, providerConfigs.size()); + checkOidcProviderConfig(providerConfigs.get(0), "oidc.provider-id1"); + checkOidcProviderConfig(providerConfigs.get(1), "oidc.provider-id2"); + assertEquals("", page.getNextPageToken()); + checkRequestHeaders(interceptor); + checkUrl(interceptor, "GET", TENANTS_BASE_URL + "/TENANT_ID/oauthIdpConfigs"); + GenericUrl url = interceptor.getResponse().getRequest().getUrl(); + assertEquals(99, url.getFirst("pageSize")); + assertNull(url.getFirst("nextPageToken")); + } @Test public void testDeleteProviderConfig() throws Exception { @@ -1790,8 +1859,8 @@ private static void checkTenant(Tenant tenant, String tenantId) { assertFalse(tenant.isEmailLinkSignInEnabled()); } - private static void checkOidcProviderConfig(OidcProviderConfig config) { - assertEquals("oidc.provider-id", config.getProviderId()); + private static void checkOidcProviderConfig(OidcProviderConfig config, String providerId) { + assertEquals(providerId, config.getProviderId()); assertEquals("DISPLAY_NAME", config.getDisplayName()); assertTrue(config.isEnabled()); assertEquals("CLIENT_ID", config.getClientId()); diff --git a/src/test/java/com/google/firebase/auth/ListUsersPageTest.java b/src/test/java/com/google/firebase/auth/ListUsersPageTest.java index fb4a7d275..ee1935485 100644 --- a/src/test/java/com/google/firebase/auth/ListUsersPageTest.java +++ b/src/test/java/com/google/firebase/auth/ListUsersPageTest.java @@ -28,6 +28,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.io.BaseEncoding; import com.google.firebase.auth.ListUsersPage.ListUsersResult; +import com.google.firebase.auth.ListUsersPage.UserPageFactory; import com.google.firebase.auth.internal.DownloadAccountResponse; import java.io.IOException; import java.util.ArrayList; @@ -45,7 +46,7 @@ public class ListUsersPageTest { @Test public void testSinglePage() throws FirebaseAuthException, IOException { TestUserSource source = new TestUserSource(3); - ListUsersPage page = new ListUsersPage.PageFactory(source).create(); + ListUsersPage page = new UserPageFactory(source).create(); assertFalse(page.hasNextPage()); assertEquals(ListUsersPage.END_OF_LIST, page.getNextPageToken()); assertNull(page.getNextPage()); @@ -68,7 +69,7 @@ public void testRedactedPasswords() throws FirebaseAuthException, IOException { newUser("user2", REDACTED_BASE64)), ListUsersPage.END_OF_LIST); TestUserSource source = new TestUserSource(result); - ListUsersPage page = new ListUsersPage.PageFactory(source).create(); + ListUsersPage page = new UserPageFactory(source).create(); assertFalse(page.hasNextPage()); assertEquals(ListUsersPage.END_OF_LIST, page.getNextPageToken()); assertNull(page.getNextPage()); @@ -89,7 +90,7 @@ public void testMultiplePages() throws FirebaseAuthException, IOException { ImmutableList.of(newUser("user0"), newUser("user1"), newUser("user2")), "token"); TestUserSource source = new TestUserSource(result); - ListUsersPage page1 = new ListUsersPage.PageFactory(source).create(); + ListUsersPage page1 = new UserPageFactory(source).create(); assertTrue(page1.hasNextPage()); assertEquals("token", page1.getNextPageToken()); ImmutableList users = ImmutableList.copyOf(page1.getValues()); @@ -136,7 +137,7 @@ public void testMultiplePages() throws FirebaseAuthException, IOException { @Test public void testListUsersIterable() throws FirebaseAuthException, IOException { TestUserSource source = new TestUserSource(3); - ListUsersPage page = new ListUsersPage.PageFactory(source).create(); + ListUsersPage page = new UserPageFactory(source).create(); Iterable users = page.iterateAll(); int iterations = 0; @@ -162,7 +163,7 @@ public void testListUsersIterable() throws FirebaseAuthException, IOException { @Test public void testListUsersIterator() throws FirebaseAuthException, IOException { TestUserSource source = new TestUserSource(3); - ListUsersPage page = new ListUsersPage.PageFactory(source).create(); + ListUsersPage page = new UserPageFactory(source).create(); Iterable users = page.iterateAll(); Iterator iterator = users.iterator(); int iterations = 0; @@ -192,7 +193,7 @@ public void testListUsersPagedIterable() throws FirebaseAuthException, IOExcepti ImmutableList.of(newUser("user0"), newUser("user1"), newUser("user2")), "token"); TestUserSource source = new TestUserSource(result); - ListUsersPage page = new ListUsersPage.PageFactory(source).create(); + ListUsersPage page = new UserPageFactory(source).create(); int iterations = 0; for (ExportedUserRecord user : page.iterateAll()) { assertEquals("user" + iterations, user.getUid()); @@ -218,7 +219,7 @@ public void testListUsersPagedIterator() throws FirebaseAuthException, IOExcepti ImmutableList.of(newUser("user0"), newUser("user1"), newUser("user2")), "token"); TestUserSource source = new TestUserSource(result); - ListUsersPage page = new ListUsersPage.PageFactory(source).create(); + ListUsersPage page = new UserPageFactory(source).create(); Iterator users = page.iterateAll().iterator(); int iterations = 0; while (users.hasNext()) { @@ -251,7 +252,7 @@ public void testPageWithNoUsers() throws FirebaseAuthException { ImmutableList.of(), ListUsersPage.END_OF_LIST); TestUserSource source = new TestUserSource(result); - ListUsersPage page = new ListUsersPage.PageFactory(source).create(); + ListUsersPage page = new UserPageFactory(source).create(); assertFalse(page.hasNextPage()); assertEquals(ListUsersPage.END_OF_LIST, page.getNextPageToken()); assertNull(page.getNextPage()); @@ -265,7 +266,7 @@ public void testIterableWithNoUsers() throws FirebaseAuthException { ImmutableList.of(), ListUsersPage.END_OF_LIST); TestUserSource source = new TestUserSource(result); - ListUsersPage page = new ListUsersPage.PageFactory(source).create(); + ListUsersPage page = new UserPageFactory(source).create(); for (ExportedUserRecord user : page.iterateAll()) { fail("Should not be able to iterate, but got: " + user); } @@ -279,7 +280,7 @@ public void testIteratorWithNoUsers() throws FirebaseAuthException { ListUsersPage.END_OF_LIST); TestUserSource source = new TestUserSource(result); - ListUsersPage page = new ListUsersPage.PageFactory(source).create(); + ListUsersPage page = new UserPageFactory(source).create(); Iterator iterator = page.iterateAll().iterator(); while (iterator.hasNext()) { fail("Should not be able to iterate"); @@ -294,7 +295,7 @@ public void testRemove() throws FirebaseAuthException, IOException { ListUsersPage.END_OF_LIST); TestUserSource source = new TestUserSource(result); - ListUsersPage page = new ListUsersPage.PageFactory(source).create(); + ListUsersPage page = new UserPageFactory(source).create(); Iterator iterator = page.iterateAll().iterator(); while (iterator.hasNext()) { assertNotNull(iterator.next()); @@ -308,14 +309,14 @@ public void testRemove() throws FirebaseAuthException, IOException { @Test(expected = NullPointerException.class) public void testNullSource() { - new ListUsersPage.PageFactory(null); + new UserPageFactory(null); } @Test public void testInvalidPageToken() throws IOException { TestUserSource source = new TestUserSource(1); try { - new ListUsersPage.PageFactory(source, 1000, ""); + new UserPageFactory(source, 1000, ""); fail("No error thrown for empty page token"); } catch (IllegalArgumentException expected) { // expected @@ -326,21 +327,21 @@ public void testInvalidPageToken() throws IOException { public void testInvalidMaxResults() throws IOException { TestUserSource source = new TestUserSource(1); try { - new ListUsersPage.PageFactory(source, 1001, ""); + new UserPageFactory(source, 1001, ""); fail("No error thrown for maxResult > 1000"); } catch (IllegalArgumentException expected) { // expected } try { - new ListUsersPage.PageFactory(source, 0, "next"); + new UserPageFactory(source, 0, "next"); fail("No error thrown for maxResult = 0"); } catch (IllegalArgumentException expected) { // expected } try { - new ListUsersPage.PageFactory(source, -1, "next"); + new UserPageFactory(source, -1, "next"); fail("No error thrown for maxResult < 0"); } catch (IllegalArgumentException expected) { // expected diff --git a/src/test/resources/listOidc.json b/src/test/resources/listOidc.json new file mode 100644 index 000000000..0c13ea48b --- /dev/null +++ b/src/test/resources/listOidc.json @@ -0,0 +1,15 @@ +{ + "oauthIdpConfigs" : [ { + "name": "projects/projectId/oauthIdpConfigs/oidc.provider-id1", + "displayName" : "DISPLAY_NAME", + "enabled" : true, + "clientId" : "CLIENT_ID", + "issuer" : "https://oidc.com/issuer" + }, { + "name": "projects/projectId/oauthIdpConfigs/oidc.provider-id2", + "displayName" : "DISPLAY_NAME", + "enabled" : true, + "clientId" : "CLIENT_ID", + "issuer" : "https://oidc.com/issuer" + } ] +} From c346de1173d7aacf177039b4cb18525dbb22cbcd Mon Sep 17 00:00:00 2001 From: Micah Stairs Date: Mon, 4 May 2020 19:34:05 -0400 Subject: [PATCH 2/2] Address pull request feedback. --- .../firebase/auth/AbstractFirebaseAuth.java | 13 +++---- .../auth/ListProviderConfigsPage.java | 9 +++-- .../google/firebase/auth/ListUsersPage.java | 9 +++-- .../google/firebase/auth/FirebaseAuthIT.java | 27 ++++++++------- .../firebase/auth/ListUsersPageTest.java | 34 +++++++++---------- 5 files changed, 47 insertions(+), 45 deletions(-) diff --git a/src/main/java/com/google/firebase/auth/AbstractFirebaseAuth.java b/src/main/java/com/google/firebase/auth/AbstractFirebaseAuth.java index 7f10cd96d..de9575eb0 100644 --- a/src/main/java/com/google/firebase/auth/AbstractFirebaseAuth.java +++ b/src/main/java/com/google/firebase/auth/AbstractFirebaseAuth.java @@ -29,10 +29,10 @@ import com.google.firebase.FirebaseApp; import com.google.firebase.auth.FirebaseUserManager.EmailLinkType; import com.google.firebase.auth.FirebaseUserManager.UserImportRequest; +import com.google.firebase.auth.ListProviderConfigsPage; import com.google.firebase.auth.ListProviderConfigsPage.DefaultOidcProviderConfigSource; -import com.google.firebase.auth.ListProviderConfigsPage.ProviderConfigPageFactory; +import com.google.firebase.auth.ListUsersPage; import com.google.firebase.auth.ListUsersPage.DefaultUserSource; -import com.google.firebase.auth.ListUsersPage.UserPageFactory; import com.google.firebase.auth.UserRecord; import com.google.firebase.auth.internal.FirebaseTokenFactory; import com.google.firebase.internal.CallableOperation; @@ -503,7 +503,7 @@ private CallableOperation listUsersOp( checkNotDestroyed(); final FirebaseUserManager userManager = getUserManager(); final DefaultUserSource source = new DefaultUserSource(userManager, jsonFactory); - final UserPageFactory factory = new UserPageFactory(source, maxResults, pageToken); + final ListUsersPage.Factory factory = new ListUsersPage.Factory(source, maxResults, pageToken); return new CallableOperation() { @Override protected ListUsersPage execute() throws FirebaseAuthException { @@ -1035,6 +1035,7 @@ public OidcProviderConfig getOidcProviderConfig(@NonNull String providerId) /** * Similar to {@link #getOidcProviderConfig(String)} but performs the operation asynchronously. + * Page size will be limited to 100 provider configs. * * @param providerId A provider ID string. * @return An {@code ApiFuture} which will complete successfully with an @@ -1079,7 +1080,7 @@ public ListProviderConfigsPage listOidcProviderConfigs( /** * Similar to {@link #listlistOidcProviderConfigs(String)} but performs the operation - * asynchronously. + * asynchronously. Page size will be limited to 100 provider configs. * * @param pageToken A non-empty page token string, or null to retrieve the first page of provider * configs. @@ -1120,8 +1121,8 @@ public ApiFuture> listOidcProviderCo checkNotDestroyed(); final FirebaseUserManager userManager = getUserManager(); final DefaultOidcProviderConfigSource source = new DefaultOidcProviderConfigSource(userManager); - final ProviderConfigPageFactory factory = - new ProviderConfigPageFactory(source, maxResults, pageToken); + final ListProviderConfigsPage.Factory factory = + new ListProviderConfigsPage.Factory(source, maxResults, pageToken); return new CallableOperation, FirebaseAuthException>() { @Override diff --git a/src/main/java/com/google/firebase/auth/ListProviderConfigsPage.java b/src/main/java/com/google/firebase/auth/ListProviderConfigsPage.java index 0d3f41525..2ab7f736f 100644 --- a/src/main/java/com/google/firebase/auth/ListProviderConfigsPage.java +++ b/src/main/java/com/google/firebase/auth/ListProviderConfigsPage.java @@ -89,8 +89,7 @@ public String getNextPageToken() { @Override public ListProviderConfigsPage getNextPage() { if (hasNextPage()) { - ProviderConfigPageFactory factory = - new ProviderConfigPageFactory(source, maxResults, currentBatch.getPageToken()); + Factory factory = new Factory(source, maxResults, currentBatch.getPageToken()); try { return factory.create(); } catch (FirebaseAuthException e) { @@ -224,17 +223,17 @@ public ListOidcProviderConfigsResponse fetch(int maxResults, String pageToken) *

Performs argument validation before attempting to load any provider config data (which is * expensive, and hence may be performed asynchronously on a separate thread). */ - static class ProviderConfigPageFactory { + static class Factory { private final ProviderConfigSource source; private final int maxResults; private final String pageToken; - ProviderConfigPageFactory(@NonNull ProviderConfigSource source) { + Factory(@NonNull ProviderConfigSource source) { this(source, FirebaseUserManager.MAX_LIST_PROVIDER_CONFIGS_RESULTS, null); } - ProviderConfigPageFactory( + Factory( @NonNull ProviderConfigSource source, int maxResults, @Nullable String pageToken) { diff --git a/src/main/java/com/google/firebase/auth/ListUsersPage.java b/src/main/java/com/google/firebase/auth/ListUsersPage.java index a298db64a..ba727af5a 100644 --- a/src/main/java/com/google/firebase/auth/ListUsersPage.java +++ b/src/main/java/com/google/firebase/auth/ListUsersPage.java @@ -80,8 +80,7 @@ public String getNextPageToken() { @Override public ListUsersPage getNextPage() { if (hasNextPage()) { - UserPageFactory factory = - new UserPageFactory(source, maxResults, currentBatch.getNextPageToken()); + Factory factory = new Factory(source, maxResults, currentBatch.getNextPageToken()); try { return factory.create(); } catch (FirebaseAuthException e) { @@ -238,17 +237,17 @@ String getNextPageToken() { * before attempting to load any user data (which is expensive, and hence may be performed * asynchronously on a separate thread). */ - static class UserPageFactory { + static class Factory { private final UserSource source; private final int maxResults; private final String pageToken; - UserPageFactory(@NonNull UserSource source) { + Factory(@NonNull UserSource source) { this(source, FirebaseUserManager.MAX_LIST_USERS_RESULTS, null); } - UserPageFactory(@NonNull UserSource source, int maxResults, @Nullable String pageToken) { + Factory(@NonNull UserSource source, int maxResults, @Nullable String pageToken) { checkArgument(maxResults > 0 && maxResults <= FirebaseUserManager.MAX_LIST_USERS_RESULTS, "maxResults must be a positive integer that does not exceed %s", FirebaseUserManager.MAX_LIST_USERS_RESULTS); diff --git a/src/test/java/com/google/firebase/auth/FirebaseAuthIT.java b/src/test/java/com/google/firebase/auth/FirebaseAuthIT.java index 24cf7a277..290499d49 100644 --- a/src/test/java/com/google/firebase/auth/FirebaseAuthIT.java +++ b/src/test/java/com/google/firebase/auth/FirebaseAuthIT.java @@ -66,6 +66,8 @@ import org.junit.BeforeClass; import org.junit.Test; +// TODO(micahstairs): Move tenant-aware tests into a seperate class, so that we only need to +// create and destroy the tenant once. public class FirebaseAuthIT { private static final String VERIFY_CUSTOM_TOKEN_URL = @@ -1088,10 +1090,8 @@ public void testListOidcProviderConfigs() throws Exception { auth.listOidcProviderConfigsAsync(null).get(); while (page != null) { for (OidcProviderConfig providerConfig : page.getValues()) { - if (providerIds.contains(providerConfig.getProviderId())) { + if (checkProviderConfig(providerIds, providerConfig)) { collected.incrementAndGet(); - assertEquals("CLIENT_ID", providerConfig.getClientId()); - assertEquals("https://oidc.com/issuer", providerConfig.getIssuer()); } } page = page.getNextPage(); @@ -1102,10 +1102,8 @@ public void testListOidcProviderConfigs() throws Exception { collected.set(0); page = auth.listOidcProviderConfigsAsync(null).get(); for (OidcProviderConfig providerConfig : page.iterateAll()) { - if (providerIds.contains(providerConfig.getProviderId())) { + if (checkProviderConfig(providerIds, providerConfig)) { collected.incrementAndGet(); - assertEquals("CLIENT_ID", providerConfig.getClientId()); - assertEquals("https://oidc.com/issuer", providerConfig.getIssuer()); } } assertEquals(providerIds.size(), collected.get()); @@ -1128,10 +1126,8 @@ public void onFailure(Throwable t) { @Override public void onSuccess(ListProviderConfigsPage result) { for (OidcProviderConfig providerConfig : result.iterateAll()) { - if (providerIds.contains(providerConfig.getProviderId())) { + if (checkProviderConfig(providerIds, providerConfig)) { collected.incrementAndGet(); - assertEquals("CLIENT_ID", providerConfig.getClientId()); - assertEquals("https://oidc.com/issuer", providerConfig.getIssuer()); } } semaphore.release(); @@ -1177,10 +1173,8 @@ public void testTenantAwareListOidcProviderConfigs() throws Exception { ListProviderConfigsPage page = tenantAwareAuth.listOidcProviderConfigsAsync(null).get(); for (OidcProviderConfig providerConfig : page.iterateAll()) { - if (providerIds.contains(providerConfig.getProviderId())) { + if (checkProviderConfig(providerIds, providerConfig)) { collected.incrementAndGet(); - assertEquals("CLIENT_ID", providerConfig.getClientId()); - assertEquals("https://oidc.com/issuer", providerConfig.getIssuer()); } } assertEquals(providerIds.size(), collected.get()); @@ -1327,6 +1321,15 @@ static RandomUser create() { } } + private boolean checkProviderConfig(List providerIds, OidcProviderConfig config) { + if (providerIds.contains(config.getProviderId())) { + assertEquals("CLIENT_ID", config.getClientId()); + assertEquals("https://oidc.com/issuer", config.getIssuer()); + return true; + } + return false; + } + private static void assertOidcProviderConfigDoesNotExist( AbstractFirebaseAuth firebaseAuth, String providerId) throws Exception { diff --git a/src/test/java/com/google/firebase/auth/ListUsersPageTest.java b/src/test/java/com/google/firebase/auth/ListUsersPageTest.java index ee1935485..5e848069d 100644 --- a/src/test/java/com/google/firebase/auth/ListUsersPageTest.java +++ b/src/test/java/com/google/firebase/auth/ListUsersPageTest.java @@ -27,8 +27,8 @@ import com.google.api.client.json.JsonFactory; import com.google.common.collect.ImmutableList; import com.google.common.io.BaseEncoding; +import com.google.firebase.auth.ListUsersPage; import com.google.firebase.auth.ListUsersPage.ListUsersResult; -import com.google.firebase.auth.ListUsersPage.UserPageFactory; import com.google.firebase.auth.internal.DownloadAccountResponse; import java.io.IOException; import java.util.ArrayList; @@ -46,7 +46,7 @@ public class ListUsersPageTest { @Test public void testSinglePage() throws FirebaseAuthException, IOException { TestUserSource source = new TestUserSource(3); - ListUsersPage page = new UserPageFactory(source).create(); + ListUsersPage page = new ListUsersPage.Factory(source).create(); assertFalse(page.hasNextPage()); assertEquals(ListUsersPage.END_OF_LIST, page.getNextPageToken()); assertNull(page.getNextPage()); @@ -69,7 +69,7 @@ public void testRedactedPasswords() throws FirebaseAuthException, IOException { newUser("user2", REDACTED_BASE64)), ListUsersPage.END_OF_LIST); TestUserSource source = new TestUserSource(result); - ListUsersPage page = new UserPageFactory(source).create(); + ListUsersPage page = new ListUsersPage.Factory(source).create(); assertFalse(page.hasNextPage()); assertEquals(ListUsersPage.END_OF_LIST, page.getNextPageToken()); assertNull(page.getNextPage()); @@ -90,7 +90,7 @@ public void testMultiplePages() throws FirebaseAuthException, IOException { ImmutableList.of(newUser("user0"), newUser("user1"), newUser("user2")), "token"); TestUserSource source = new TestUserSource(result); - ListUsersPage page1 = new UserPageFactory(source).create(); + ListUsersPage page1 = new ListUsersPage.Factory(source).create(); assertTrue(page1.hasNextPage()); assertEquals("token", page1.getNextPageToken()); ImmutableList users = ImmutableList.copyOf(page1.getValues()); @@ -137,7 +137,7 @@ public void testMultiplePages() throws FirebaseAuthException, IOException { @Test public void testListUsersIterable() throws FirebaseAuthException, IOException { TestUserSource source = new TestUserSource(3); - ListUsersPage page = new UserPageFactory(source).create(); + ListUsersPage page = new ListUsersPage.Factory(source).create(); Iterable users = page.iterateAll(); int iterations = 0; @@ -163,7 +163,7 @@ public void testListUsersIterable() throws FirebaseAuthException, IOException { @Test public void testListUsersIterator() throws FirebaseAuthException, IOException { TestUserSource source = new TestUserSource(3); - ListUsersPage page = new UserPageFactory(source).create(); + ListUsersPage page = new ListUsersPage.Factory(source).create(); Iterable users = page.iterateAll(); Iterator iterator = users.iterator(); int iterations = 0; @@ -193,7 +193,7 @@ public void testListUsersPagedIterable() throws FirebaseAuthException, IOExcepti ImmutableList.of(newUser("user0"), newUser("user1"), newUser("user2")), "token"); TestUserSource source = new TestUserSource(result); - ListUsersPage page = new UserPageFactory(source).create(); + ListUsersPage page = new ListUsersPage.Factory(source).create(); int iterations = 0; for (ExportedUserRecord user : page.iterateAll()) { assertEquals("user" + iterations, user.getUid()); @@ -219,7 +219,7 @@ public void testListUsersPagedIterator() throws FirebaseAuthException, IOExcepti ImmutableList.of(newUser("user0"), newUser("user1"), newUser("user2")), "token"); TestUserSource source = new TestUserSource(result); - ListUsersPage page = new UserPageFactory(source).create(); + ListUsersPage page = new ListUsersPage.Factory(source).create(); Iterator users = page.iterateAll().iterator(); int iterations = 0; while (users.hasNext()) { @@ -252,7 +252,7 @@ public void testPageWithNoUsers() throws FirebaseAuthException { ImmutableList.of(), ListUsersPage.END_OF_LIST); TestUserSource source = new TestUserSource(result); - ListUsersPage page = new UserPageFactory(source).create(); + ListUsersPage page = new ListUsersPage.Factory(source).create(); assertFalse(page.hasNextPage()); assertEquals(ListUsersPage.END_OF_LIST, page.getNextPageToken()); assertNull(page.getNextPage()); @@ -266,7 +266,7 @@ public void testIterableWithNoUsers() throws FirebaseAuthException { ImmutableList.of(), ListUsersPage.END_OF_LIST); TestUserSource source = new TestUserSource(result); - ListUsersPage page = new UserPageFactory(source).create(); + ListUsersPage page = new ListUsersPage.Factory(source).create(); for (ExportedUserRecord user : page.iterateAll()) { fail("Should not be able to iterate, but got: " + user); } @@ -280,7 +280,7 @@ public void testIteratorWithNoUsers() throws FirebaseAuthException { ListUsersPage.END_OF_LIST); TestUserSource source = new TestUserSource(result); - ListUsersPage page = new UserPageFactory(source).create(); + ListUsersPage page = new ListUsersPage.Factory(source).create(); Iterator iterator = page.iterateAll().iterator(); while (iterator.hasNext()) { fail("Should not be able to iterate"); @@ -295,7 +295,7 @@ public void testRemove() throws FirebaseAuthException, IOException { ListUsersPage.END_OF_LIST); TestUserSource source = new TestUserSource(result); - ListUsersPage page = new UserPageFactory(source).create(); + ListUsersPage page = new ListUsersPage.Factory(source).create(); Iterator iterator = page.iterateAll().iterator(); while (iterator.hasNext()) { assertNotNull(iterator.next()); @@ -309,14 +309,14 @@ public void testRemove() throws FirebaseAuthException, IOException { @Test(expected = NullPointerException.class) public void testNullSource() { - new UserPageFactory(null); + new ListUsersPage.Factory(null); } @Test public void testInvalidPageToken() throws IOException { TestUserSource source = new TestUserSource(1); try { - new UserPageFactory(source, 1000, ""); + new ListUsersPage.Factory(source, 1000, ""); fail("No error thrown for empty page token"); } catch (IllegalArgumentException expected) { // expected @@ -327,21 +327,21 @@ public void testInvalidPageToken() throws IOException { public void testInvalidMaxResults() throws IOException { TestUserSource source = new TestUserSource(1); try { - new UserPageFactory(source, 1001, ""); + new ListUsersPage.Factory(source, 1001, ""); fail("No error thrown for maxResult > 1000"); } catch (IllegalArgumentException expected) { // expected } try { - new UserPageFactory(source, 0, "next"); + new ListUsersPage.Factory(source, 0, "next"); fail("No error thrown for maxResult = 0"); } catch (IllegalArgumentException expected) { // expected } try { - new UserPageFactory(source, -1, "next"); + new ListUsersPage.Factory(source, -1, "next"); fail("No error thrown for maxResult < 0"); } catch (IllegalArgumentException expected) { // expected