Skip to content

Commit f728f63

Browse files
committed
Add operation to update OIDC provider configs. (#402)
This adds an operation to update OIDC provider configs (can be done using either the tenant-aware or standard Firebase client). This work is part of adding multi-tenancy support (see issue #332).
1 parent 424d9ce commit f728f63

File tree

8 files changed

+366
-24
lines changed

8 files changed

+366
-24
lines changed

src/main/java/com/google/firebase/auth/AbstractFirebaseAuth.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1115,6 +1115,46 @@ protected OidcProviderConfig execute() throws FirebaseAuthException {
11151115
};
11161116
}
11171117

1118+
/**
1119+
* Updates an existing OIDC Auth provider config with the attributes contained in the specified
1120+
* {@link OidcProviderConfig.UpdateRequest}.
1121+
*
1122+
* @param request A non-null {@link OidcProviderConfig.UpdateRequest} instance.
1123+
* @return A {@link OidcProviderConfig} instance corresponding to the updated provider config.
1124+
* @throws NullPointerException if the provided update request is null.
1125+
* @throws FirebaseAuthException if an error occurs while updating the provider config.
1126+
*/
1127+
public OidcProviderConfig updateOidcProviderConfig(
1128+
@NonNull OidcProviderConfig.UpdateRequest request) throws FirebaseAuthException {
1129+
return updateOidcProviderConfigOp(request).call();
1130+
}
1131+
1132+
/**
1133+
* Similar to {@link #updateOidcProviderConfig} but performs the operation asynchronously.
1134+
*
1135+
* @param request A non-null {@link OidcProviderConfig.UpdateRequest} instance.
1136+
* @return An {@code ApiFuture} which will complete successfully with a {@link OidcProviderConfig}
1137+
* instance corresponding to the updated provider config. If an error occurs while updating
1138+
* the provider config, the future throws a {@link FirebaseAuthException}.
1139+
*/
1140+
public ApiFuture<OidcProviderConfig> updateOidcProviderConfigAsync(
1141+
@NonNull OidcProviderConfig.UpdateRequest request) {
1142+
return updateOidcProviderConfigOp(request).callAsync(firebaseApp);
1143+
}
1144+
1145+
private CallableOperation<OidcProviderConfig, FirebaseAuthException> updateOidcProviderConfigOp(
1146+
final OidcProviderConfig.UpdateRequest request) {
1147+
checkNotDestroyed();
1148+
checkNotNull(request, "update request must not be null");
1149+
final FirebaseUserManager userManager = getUserManager();
1150+
return new CallableOperation<OidcProviderConfig, FirebaseAuthException>() {
1151+
@Override
1152+
protected OidcProviderConfig execute() throws FirebaseAuthException {
1153+
return userManager.updateOidcProviderConfig(request);
1154+
}
1155+
};
1156+
}
1157+
11181158
/**
11191159
* Gets the provider OIDC Auth config corresponding to the specified provider ID.
11201160
*

src/main/java/com/google/firebase/auth/FirebaseUserManager.java

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -306,14 +306,6 @@ Tenant updateTenant(Tenant.UpdateRequest request) throws FirebaseAuthException {
306306
return sendRequest("PATCH", url, properties, Tenant.class);
307307
}
308308

309-
private static String generateMask(Map<String, Object> properties) {
310-
// This implementation does not currently handle the case of nested properties. This is fine
311-
// since we do not currently generate masks for any properties with nested values. When it
312-
// comes time to implement this, we can check if a property has nested properties by checking
313-
// if it is an instance of the Map class.
314-
return Joiner.on(",").join(ImmutableSortedSet.copyOf(properties.keySet()));
315-
}
316-
317309
void deleteTenant(String tenantId) throws FirebaseAuthException {
318310
GenericUrl url = new GenericUrl(tenantMgtBaseUrl + getTenantUrlSuffix(tenantId));
319311
sendRequest("DELETE", url, null, GenericJson.class);
@@ -380,21 +372,45 @@ OidcProviderConfig createOidcProviderConfig(
380372
return sendRequest("POST", url, request.getProperties(), OidcProviderConfig.class);
381373
}
382374

375+
OidcProviderConfig updateOidcProviderConfig(OidcProviderConfig.UpdateRequest request)
376+
throws FirebaseAuthException {
377+
Map<String, Object> properties = request.getProperties();
378+
checkArgument(!properties.isEmpty(),
379+
"provider config update must have at least one property set");
380+
GenericUrl url =
381+
new GenericUrl(idpConfigMgtBaseUrl + getOidcUrlSuffix(request.getProviderId()));
382+
url.put("updateMask", generateMask(properties));
383+
return sendRequest("PATCH", url, properties, OidcProviderConfig.class);
384+
}
385+
383386
OidcProviderConfig getOidcProviderConfig(String providerId) throws FirebaseAuthException {
384-
GenericUrl url = new GenericUrl(idpConfigMgtBaseUrl + "/oauthIdpConfigs/" + providerId);
387+
GenericUrl url = new GenericUrl(idpConfigMgtBaseUrl + getOidcUrlSuffix(providerId));
385388
return sendRequest("GET", url, null, OidcProviderConfig.class);
386389
}
387390

388391
void deleteProviderConfig(String providerId) throws FirebaseAuthException {
389-
GenericUrl url = new GenericUrl(idpConfigMgtBaseUrl + "/oauthIdpConfigs/" + providerId);
392+
GenericUrl url = new GenericUrl(idpConfigMgtBaseUrl + getOidcUrlSuffix(providerId));
390393
sendRequest("DELETE", url, null, GenericJson.class);
391394
}
392395

396+
private static String generateMask(Map<String, Object> properties) {
397+
// This implementation does not currently handle the case of nested properties. This is fine
398+
// since we do not currently generate masks for any properties with nested values. When it
399+
// comes time to implement this, we can check if a property has nested properties by checking
400+
// if it is an instance of the Map class.
401+
return Joiner.on(",").join(ImmutableSortedSet.copyOf(properties.keySet()));
402+
}
403+
393404
private static String getTenantUrlSuffix(String tenantId) {
394-
checkArgument(!Strings.isNullOrEmpty(tenantId));
405+
checkArgument(!Strings.isNullOrEmpty(tenantId), "tenant ID must not be null or empty");
395406
return "/tenants/" + tenantId;
396407
}
397408

409+
private static String getOidcUrlSuffix(String providerId) {
410+
checkArgument(!Strings.isNullOrEmpty(providerId), "provider ID must not be null or empty");
411+
return "/oauthIdpConfigs/" + providerId;
412+
}
413+
398414
private <T> T post(String path, Object content, Class<T> clazz) throws FirebaseAuthException {
399415
checkArgument(!Strings.isNullOrEmpty(path), "path must not be null or empty");
400416
checkNotNull(content, "content must not be null for POST requests");

src/main/java/com/google/firebase/auth/OidcProviderConfig.java

Lines changed: 72 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import com.google.common.base.Strings;
2323
import com.google.common.collect.ImmutableMap;
2424
import com.google.firebase.auth.ProviderConfig.AbstractCreateRequest;
25+
import com.google.firebase.auth.ProviderConfig.AbstractUpdateRequest;
2526
import java.net.MalformedURLException;
2627
import java.net.URL;
2728
import java.util.HashMap;
@@ -48,6 +49,24 @@ public String getIssuer() {
4849
return issuer;
4950
}
5051

52+
/**
53+
* Returns a new {@link UpdateRequest}, which can be used to update the attributes of this
54+
* provider config.
55+
*
56+
* @return a non-null {@link UpdateRequest} instance.
57+
*/
58+
public UpdateRequest updateRequest() {
59+
return new UpdateRequest(getProviderId());
60+
}
61+
62+
private static void assertValidUrl(String url) throws IllegalArgumentException {
63+
try {
64+
new URL(url);
65+
} catch (MalformedURLException e) {
66+
throw new IllegalArgumentException(url + " is a malformed URL", e);
67+
}
68+
}
69+
5170
/**
5271
* A specification class for creating a new OIDC Auth provider.
5372
*
@@ -83,11 +102,7 @@ public CreateRequest setClientId(String clientId) {
83102
*/
84103
public CreateRequest setIssuer(String issuer) {
85104
checkArgument(!Strings.isNullOrEmpty(issuer), "issuer must not be null or empty");
86-
try {
87-
new URL(issuer);
88-
} catch (MalformedURLException e) {
89-
throw new IllegalArgumentException(issuer + " is a malformed URL", e);
90-
}
105+
assertValidUrl(issuer);
91106
properties.put("issuer", issuer);
92107
return this;
93108
}
@@ -96,4 +111,56 @@ CreateRequest getThis() {
96111
return this;
97112
}
98113
}
114+
115+
/**
116+
* A specification class for updating an existing OIDC Auth provider.
117+
*
118+
* <p>An instance of this class can be obtained via a {@link OidcProviderConfig} object, or from
119+
* a provider ID string. Specify the changes to be made to the provider config by calling the
120+
* various setter methods available in this class.
121+
*/
122+
public static final class UpdateRequest extends AbstractUpdateRequest<UpdateRequest> {
123+
124+
/**
125+
* Creates a new {@link UpdateRequest}, which can be used to updates an existing OIDC Auth
126+
* provider.
127+
*
128+
* <p>The returned object should be passed to
129+
* {@link AbstractFirebaseAuth#updateOidcProviderConfig(CreateRequest)} to update the provider
130+
* information persistently.
131+
*
132+
* @param tenantId a non-null, non-empty provider ID string.
133+
* @throws IllegalArgumentException If the provider ID is null or empty.
134+
*/
135+
public UpdateRequest(String providerId) {
136+
super(providerId);
137+
}
138+
139+
/**
140+
* Sets the client ID for the exsting provider.
141+
*
142+
* @param clientId a non-null, non-empty client ID string.
143+
*/
144+
public UpdateRequest setClientId(String clientId) {
145+
checkArgument(!Strings.isNullOrEmpty(clientId), "client ID must not be null or empty");
146+
properties.put("clientId", clientId);
147+
return this;
148+
}
149+
150+
/**
151+
* Sets the issuer for the existing provider.
152+
*
153+
* @param issuer a non-null, non-empty issuer string.
154+
*/
155+
public UpdateRequest setIssuer(String issuer) {
156+
checkArgument(!Strings.isNullOrEmpty(issuer), "issuer must not be null or empty");
157+
assertValidUrl(issuer);
158+
properties.put("issuer", issuer);
159+
return this;
160+
}
161+
162+
UpdateRequest getThis() {
163+
return this;
164+
}
165+
}
99166
}

src/main/java/com/google/firebase/auth/ProviderConfig.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,4 +104,49 @@ Map<String, Object> getProperties() {
104104

105105
abstract T getThis();
106106
}
107+
108+
/**
109+
* A base class for updating the attributes of an existing provider.
110+
*/
111+
public abstract static class AbstractUpdateRequest<T extends AbstractUpdateRequest<T>> {
112+
113+
final String providerId;
114+
final Map<String,Object> properties = new HashMap<>();
115+
116+
AbstractUpdateRequest(String providerId) {
117+
checkArgument(!Strings.isNullOrEmpty(providerId), "provider ID must not be null or empty");
118+
this.providerId = providerId;
119+
}
120+
121+
String getProviderId() {
122+
return providerId;
123+
}
124+
125+
/**
126+
* Sets the display name for the existing provider.
127+
*
128+
* @param displayName a non-null, non-empty display name string.
129+
*/
130+
public T setDisplayName(String displayName) {
131+
checkArgument(!Strings.isNullOrEmpty(displayName), "display name must not be null or empty");
132+
properties.put("displayName", displayName);
133+
return getThis();
134+
}
135+
136+
/**
137+
* Sets whether to allow the user to sign in with the provider.
138+
*
139+
* @param enabled a boolean indicating whether the user can sign in with the provider
140+
*/
141+
public T setEnabled(boolean enabled) {
142+
properties.put("enabled", enabled);
143+
return getThis();
144+
}
145+
146+
Map<String, Object> getProperties() {
147+
return ImmutableMap.copyOf(properties);
148+
}
149+
150+
abstract T getThis();
151+
}
107152
}

src/main/java/com/google/firebase/auth/Tenant.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,9 @@ public boolean isEmailLinkSignInEnabled() {
6060
}
6161

6262
/**
63-
* Returns a new {@link UpdateRequest}, which can be used to update the attributes
64-
* of this tenant.
63+
* Returns a new {@link UpdateRequest}, which can be used to update the attributes of this tenant.
6564
*
66-
* @return a non-null Tenant.UpdateRequest instance.
65+
* @return a non-null {@link UpdateRequest} instance.
6766
*/
6867
public UpdateRequest updateRequest() {
6968
return new UpdateRequest(getTenantId());

src/test/java/com/google/firebase/auth/FirebaseAuthIT.java

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,6 +1074,7 @@ public void testOidcProviderConfigLifecycle() throws Exception {
10741074
OidcProviderConfig config = auth.createOidcProviderConfigAsync(createRequest).get();
10751075
assertEquals(providerId, config.getProviderId());
10761076
assertEquals("DisplayName", config.getDisplayName());
1077+
assertTrue(config.isEnabled());
10771078
assertEquals("ClientId", config.getClientId());
10781079
assertEquals("https://oidc.com/issuer", config.getIssuer());
10791080

@@ -1082,11 +1083,23 @@ public void testOidcProviderConfigLifecycle() throws Exception {
10821083
config = auth.getOidcProviderConfigAsync(providerId).get();
10831084
assertEquals(providerId, config.getProviderId());
10841085
assertEquals("DisplayName", config.getDisplayName());
1086+
assertTrue(config.isEnabled());
10851087
assertEquals("ClientId", config.getClientId());
10861088
assertEquals("https://oidc.com/issuer", config.getIssuer());
10871089

1088-
// TODO(micahstairs): Test updateProviderConfig operation
1089-
1090+
// Update config provider
1091+
OidcProviderConfig.UpdateRequest updateRequest =
1092+
new OidcProviderConfig.UpdateRequest(providerId)
1093+
.setDisplayName("NewDisplayName")
1094+
.setEnabled(false)
1095+
.setClientId("NewClientId")
1096+
.setIssuer("https://oidc.com/new-issuer");
1097+
config = auth.updateOidcProviderConfigAsync(updateRequest).get();
1098+
assertEquals(providerId, config.getProviderId());
1099+
assertEquals("NewDisplayName", config.getDisplayName());
1100+
assertFalse(config.isEnabled());
1101+
assertEquals("NewClientId", config.getClientId());
1102+
assertEquals("https://oidc.com/new-issuer", config.getIssuer());
10901103
} finally {
10911104
// Delete config provider
10921105
auth.deleteProviderConfigAsync(providerId).get();
@@ -1129,8 +1142,19 @@ public void testTenantAwareOidcProviderConfigLifecycle() throws Exception {
11291142
assertEquals("ClientId", config.getClientId());
11301143
assertEquals("https://oidc.com/issuer", config.getIssuer());
11311144

1132-
// TODO(micahstairs): Test updateProviderConfig operation
1133-
1145+
// Update config provider
1146+
OidcProviderConfig.UpdateRequest updateRequest =
1147+
new OidcProviderConfig.UpdateRequest(providerId)
1148+
.setDisplayName("NewDisplayName")
1149+
.setEnabled(false)
1150+
.setClientId("NewClientId")
1151+
.setIssuer("https://oidc.com/new-issuer");
1152+
config = tenantAwareAuth.updateOidcProviderConfigAsync(updateRequest).get();
1153+
assertEquals(providerId, config.getProviderId());
1154+
assertEquals("NewDisplayName", config.getDisplayName());
1155+
assertFalse(config.isEnabled());
1156+
assertEquals("NewClientId", config.getClientId());
1157+
assertEquals("https://oidc.com/new-issuer", config.getIssuer());
11341158
} finally {
11351159
// Delete config provider
11361160
tenantAwareAuth.deleteProviderConfigAsync(providerId).get();

0 commit comments

Comments
 (0)