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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ private FirebaseUserManager(Builder builder) {
this.userMgtBaseUrl = idToolkitUrlV1;
this.idpConfigMgtBaseUrl = idToolkitUrlV2;
} else {
checkArgument(!tenantId.isEmpty(), "Tenant ID must not be empty.");
Utils.checkTenantId(tenantId);
this.userMgtBaseUrl = idToolkitUrlV1 + "/tenants/" + tenantId;
this.idpConfigMgtBaseUrl = idToolkitUrlV2 + "/tenants/" + tenantId;
}
Expand Down Expand Up @@ -335,12 +335,12 @@ void deleteSamlProviderConfig(String providerId) throws FirebaseAuthException {
}

private static String getOidcUrlSuffix(String providerId) {
checkArgument(!Strings.isNullOrEmpty(providerId), "Provider ID must not be null or empty.");
OidcProviderConfig.checkOidcProviderId(providerId);
return "/oauthIdpConfigs/" + providerId;
}

private static String getSamlUrlSuffix(String providerId) {
checkArgument(!Strings.isNullOrEmpty(providerId), "Provider ID must not be null or empty.");
SamlProviderConfig.checkSamlProviderId(providerId);
return "/inboundSamlConfigs/" + providerId;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.google.api.client.json.GenericJson;
import com.google.api.client.util.Key;
import com.google.common.base.Strings;
import com.google.firebase.auth.internal.Utils;
import java.util.HashMap;
import java.util.Map;

Expand Down Expand Up @@ -77,6 +78,7 @@ static void checkOidcProviderId(String providerId) {
checkArgument(!Strings.isNullOrEmpty(providerId), "Provider ID must not be null or empty.");
checkArgument(providerId.startsWith("oidc."),
"Invalid OIDC provider ID (must be prefixed with 'oidc.'): " + providerId);
checkArgument(Utils.isSafeUrlSegment(providerId), "Invalid OIDC provider ID: " + providerId);
}

static Map<String, Boolean> ensureResponseType(Map<String,Object> properties) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.firebase.auth.internal.Utils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
Expand Down Expand Up @@ -84,6 +85,7 @@ static void checkSamlProviderId(String providerId) {
checkArgument(!Strings.isNullOrEmpty(providerId), "Provider ID must not be null or empty.");
checkArgument(providerId.startsWith("saml."),
"Invalid SAML provider ID (must be prefixed with 'saml.'): " + providerId);
checkArgument(Utils.isSafeUrlSegment(providerId), "Invalid SAML provider ID: " + providerId);
}

private static List<Object> ensureNestedList(Map<String, Object> outerMap, String id) {
Expand Down
17 changes: 17 additions & 0 deletions src/main/java/com/google/firebase/auth/internal/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,20 @@

package com.google.firebase.auth.internal;

import static com.google.common.base.Preconditions.checkArgument;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.firebase.internal.FirebaseProcessEnvironment;
import java.util.regex.Pattern;

public class Utils {
@VisibleForTesting
public static final String AUTH_EMULATOR_HOST = "FIREBASE_AUTH_EMULATOR_HOST";

private static final Pattern SAFE_URL_SEGMENT_PATTERN =
Pattern.compile("^[a-zA-Z0-9\\-_.~]+$");

public static boolean isEmulatorMode() {
return !Strings.isNullOrEmpty(getEmulatorHost());
}
Expand All @@ -32,4 +38,15 @@ public static String getEmulatorHost() {
return FirebaseProcessEnvironment.getenv(AUTH_EMULATOR_HOST);
}

public static boolean isSafeUrlSegment(String segment) {
return !Strings.isNullOrEmpty(segment)
&& !segment.endsWith(".")
&& SAFE_URL_SEGMENT_PATTERN.matcher(segment).matches();
}

public static void checkTenantId(String tenantId) {
checkArgument(!Strings.isNullOrEmpty(tenantId), "Tenant ID must not be null or empty.");
checkArgument(isSafeUrlSegment(tenantId), "Invalid tenant ID: " + tenantId);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ ListTenantsResponse listTenants(int maxResults, String pageToken)
}

private static String getTenantUrlSuffix(String tenantId) {
checkArgument(!Strings.isNullOrEmpty(tenantId), "Tenant ID must not be null or empty.");
Utils.checkTenantId(tenantId);
return "/tenants/" + tenantId;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.google.api.client.util.Key;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.firebase.auth.internal.Utils;
import java.util.HashMap;
import java.util.Map;

Expand Down Expand Up @@ -147,7 +148,7 @@ public static final class UpdateRequest {
* @throws IllegalArgumentException If the tenant ID is null or empty.
*/
public UpdateRequest(String tenantId) {
checkArgument(!Strings.isNullOrEmpty(tenantId), "tenant ID must not be null or empty");
Utils.checkTenantId(tenantId);
this.tenantId = tenantId;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,16 @@

package com.google.firebase.auth.multitenancy;

import static com.google.common.base.Preconditions.checkArgument;

import com.google.api.core.ApiAsyncFunction;
import com.google.api.core.ApiFuture;
import com.google.api.core.ApiFutures;
import com.google.common.base.Strings;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.firebase.FirebaseApp;
import com.google.firebase.auth.AbstractFirebaseAuth;
import com.google.firebase.auth.FirebaseAuthException;
import com.google.firebase.auth.FirebaseToken;
import com.google.firebase.auth.SessionCookieOptions;
import com.google.firebase.auth.internal.Utils;

/**
* The tenant-aware Firebase client.
Expand All @@ -41,7 +39,7 @@ public final class TenantAwareFirebaseAuth extends AbstractFirebaseAuth {

private TenantAwareFirebaseAuth(Builder builder) {
super(builder);
checkArgument(!Strings.isNullOrEmpty(builder.tenantId));
Utils.checkTenantId(builder.tenantId);
this.tenantId = builder.tenantId;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@
import com.google.api.client.http.HttpResponseInterceptor;
import com.google.api.core.ApiFuture;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.firebase.FirebaseApp;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseAuthException;
import com.google.firebase.auth.internal.Utils;
import com.google.firebase.auth.multitenancy.ListTenantsPage.DefaultTenantSource;
import com.google.firebase.auth.multitenancy.ListTenantsPage.PageFactory;
import com.google.firebase.auth.multitenancy.ListTenantsPage.TenantSource;
Expand Down Expand Up @@ -82,7 +82,7 @@ public Tenant getTenant(@NonNull String tenantId) throws FirebaseAuthException {
}

public synchronized TenantAwareFirebaseAuth getAuthForTenant(@NonNull String tenantId) {
checkArgument(!Strings.isNullOrEmpty(tenantId), "Tenant ID must not be null or empty.");
Utils.checkTenantId(tenantId);
if (!tenantAwareAuths.containsKey(tenantId)) {
tenantAwareAuths.put(tenantId, TenantAwareFirebaseAuth.fromApp(firebaseApp, tenantId));
}
Expand All @@ -103,7 +103,7 @@ public ApiFuture<Tenant> getTenantAsync(@NonNull String tenantId) {
}

private CallableOperation<Tenant, FirebaseAuthException> getTenantOp(final String tenantId) {
checkArgument(!Strings.isNullOrEmpty(tenantId), "Tenant ID must not be null or empty.");
Utils.checkTenantId(tenantId);
return new CallableOperation<Tenant, FirebaseAuthException>() {
@Override
protected Tenant execute() throws FirebaseAuthException {
Expand Down Expand Up @@ -280,7 +280,7 @@ public ApiFuture<Void> deleteTenantAsync(String tenantId) {
}

private CallableOperation<Void, FirebaseAuthException> deleteTenantOp(final String tenantId) {
checkArgument(!Strings.isNullOrEmpty(tenantId), "Tenant ID must not be null or empty.");
Utils.checkTenantId(tenantId);
return new CallableOperation<Void, FirebaseAuthException>() {
@Override
protected Void execute() throws FirebaseAuthException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

class FirebaseProjectManagementServiceImpl implements AndroidAppService, IosAppService {

Expand Down Expand Up @@ -190,29 +191,29 @@ private <T> CallableOperation<List<T>, FirebaseProjectManagementException> listA
@Override
protected List<T> execute() throws FirebaseProjectManagementException {
String url = String.format(
"%s/v1beta1/projects/%s/%s?page_size=%d",
"%s/v1beta1/projects/%s/%s",
FIREBASE_PROJECT_MANAGEMENT_URL,
projectId,
platformResourceName,
MAXIMUM_LIST_APPS_PAGE_SIZE);
platformResourceName);
Map<String, Object> queryParams =
ImmutableMap.<String, Object>of("page_size", MAXIMUM_LIST_APPS_PAGE_SIZE);
ImmutableList.Builder<T> builder = ImmutableList.builder();
ListAppsResponse parsedResponse;
do {
parsedResponse = new ListAppsResponse();
httpHelper.makeGetRequest(url, parsedResponse, projectId, "Project ID");
httpHelper.makeGetRequest(
url, queryParams, parsedResponse, projectId, "Project ID");
if (parsedResponse.apps == null) {
break;
}
for (AppResponse app : parsedResponse.apps) {
builder.add(createAppFromAppIdFunction.apply(app.appId));
}
url = String.format(
"%s/v1beta1/projects/%s/%s?page_token=%s&page_size=%d",
FIREBASE_PROJECT_MANAGEMENT_URL,
projectId,
platformResourceName,
parsedResponse.nextPageToken,
MAXIMUM_LIST_APPS_PAGE_SIZE);
if (!Strings.isNullOrEmpty(parsedResponse.nextPageToken)) {
queryParams = ImmutableMap.<String, Object>of(
"page_token", parsedResponse.nextPageToken,
"page_size", MAXIMUM_LIST_APPS_PAGE_SIZE);
}
} while (!Strings.isNullOrEmpty(parsedResponse.nextPageToken));
return builder.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import com.google.firebase.internal.ErrorHandlingHttpClient;
import com.google.firebase.internal.HttpRequestInfo;
import com.google.firebase.internal.SdkUtils;
import java.util.Map;

final class HttpHelper {

Expand Down Expand Up @@ -56,6 +57,19 @@ <T> IncomingHttpResponse makeGetRequest(
requestIdentifierDescription);
}

<T> IncomingHttpResponse makeGetRequest(
String url,
Map<String, Object> queryParams,
T parsedResponseInstance,
String requestIdentifier,
String requestIdentifierDescription) throws FirebaseProjectManagementException {
return makeRequest(
HttpRequestInfo.buildGetRequest(url).addAllParameters(queryParams),
parsedResponseInstance,
requestIdentifier,
requestIdentifierDescription);
}

<T> IncomingHttpResponse makePostRequest(
String url,
Object payload,
Expand Down
39 changes: 39 additions & 0 deletions src/test/java/com/google/firebase/auth/OidcProviderConfigTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,15 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import com.google.api.client.json.JsonFactory;
import com.google.common.collect.ImmutableList;
import com.google.firebase.internal.ApiClientUtils;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Test;

Expand Down Expand Up @@ -123,6 +126,24 @@ public void testCreateRequestInvalidProviderId() {
new OidcProviderConfig.CreateRequest().setProviderId("saml.provider-id");
}

@Test
public void testCreateRequestInvalidProviderIdCharacters() {
List<String> invalidIds = ImmutableList.of(
"oidc.",
"oidc.provider.",
"oidc.provider/sub",
"oidc.provider?param=1",
"oidc.provider#frag");
for (String id : invalidIds) {
try {
new OidcProviderConfig.CreateRequest().setProviderId(id);
fail("No error thrown for invalid provider ID: " + id);
} catch (IllegalArgumentException expected) {
assertEquals("Invalid OIDC provider ID: " + id, expected.getMessage());
}
}
}

@Test(expected = IllegalArgumentException.class)
public void testCreateRequestMissingDisplayName() {
new OidcProviderConfig.CreateRequest().setDisplayName(null);
Expand Down Expand Up @@ -200,6 +221,24 @@ public void testUpdateRequestInvalidProviderId() {
new OidcProviderConfig.UpdateRequest("saml.provider-id");
}

@Test
public void testUpdateRequestInvalidProviderIdCharacters() {
List<String> invalidIds = ImmutableList.of(
"oidc.",
"oidc.provider.",
"oidc.provider/sub",
"oidc.provider?param=1",
"oidc.provider#frag");
for (String id : invalidIds) {
try {
new OidcProviderConfig.UpdateRequest(id);
fail("No error thrown for invalid provider ID: " + id);
} catch (IllegalArgumentException expected) {
assertEquals("Invalid OIDC provider ID: " + id, expected.getMessage());
}
}
}

@Test(expected = IllegalArgumentException.class)
public void testUpdateRequestMissingDisplayName() {
new OidcProviderConfig.UpdateRequest("oidc.provider-id").setDisplayName(null);
Expand Down
37 changes: 37 additions & 0 deletions src/test/java/com/google/firebase/auth/SamlProviderConfigTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import com.google.api.client.json.JsonFactory;
import com.google.common.collect.ImmutableList;
Expand Down Expand Up @@ -139,6 +140,24 @@ public void testCreateRequestInvalidProviderId() {
new SamlProviderConfig.CreateRequest().setProviderId("oidc.provider-id");
}

@Test
public void testCreateRequestInvalidProviderIdCharacters() {
List<String> invalidIds = ImmutableList.of(
"saml.",
"saml.provider.",
"saml.provider/sub",
"saml.provider?param=1",
"saml.provider#frag");
for (String id : invalidIds) {
try {
new SamlProviderConfig.CreateRequest().setProviderId(id);
fail("No error thrown for invalid provider ID: " + id);
} catch (IllegalArgumentException expected) {
assertEquals("Invalid SAML provider ID: " + id, expected.getMessage());
}
}
}

@Test(expected = IllegalArgumentException.class)
public void testCreateRequestMissingDisplayName() {
new SamlProviderConfig.CreateRequest().setDisplayName(null);
Expand Down Expand Up @@ -270,6 +289,24 @@ public void testUpdateRequestInvalidProviderId() {
new SamlProviderConfig.UpdateRequest("oidc.invalid-saml-provider-id");
}

@Test
public void testUpdateRequestInvalidProviderIdCharacters() {
List<String> invalidIds = ImmutableList.of(
"saml.",
"saml.provider.",
"saml.provider/sub",
"saml.provider?param=1",
"saml.provider#frag");
for (String id : invalidIds) {
try {
new SamlProviderConfig.UpdateRequest(id);
fail("No error thrown for invalid provider ID: " + id);
} catch (IllegalArgumentException expected) {
assertEquals("Invalid SAML provider ID: " + id, expected.getMessage());
}
}
}

@Test(expected = IllegalArgumentException.class)
public void testUpdateRequestMissingDisplayName() {
new SamlProviderConfig.UpdateRequest("saml.provider-id").setDisplayName(null);
Expand Down
Loading
Loading