Skip to content

Commit ee98321

Browse files
authored
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 ae4ac85 commit ee98321

File tree

8 files changed

+367
-25
lines changed

8 files changed

+367
-25
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
@@ -978,6 +978,46 @@ protected OidcProviderConfig execute() throws FirebaseAuthException {
978978
};
979979
}
980980

981+
/**
982+
* Updates an existing OIDC Auth provider config with the attributes contained in the specified
983+
* {@link OidcProviderConfig.UpdateRequest}.
984+
*
985+
* @param request A non-null {@link OidcProviderConfig.UpdateRequest} instance.
986+
* @return A {@link OidcProviderConfig} instance corresponding to the updated provider config.
987+
* @throws NullPointerException if the provided update request is null.
988+
* @throws FirebaseAuthException if an error occurs while updating the provider config.
989+
*/
990+
public OidcProviderConfig updateOidcProviderConfig(
991+
@NonNull OidcProviderConfig.UpdateRequest request) throws FirebaseAuthException {
992+
return updateOidcProviderConfigOp(request).call();
993+
}
994+
995+
/**
996+
* Similar to {@link #updateOidcProviderConfig} but performs the operation asynchronously.
997+
*
998+
* @param request A non-null {@link OidcProviderConfig.UpdateRequest} instance.
999+
* @return An {@code ApiFuture} which will complete successfully with a {@link OidcProviderConfig}
1000+
* instance corresponding to the updated provider config. If an error occurs while updating
1001+
* the provider config, the future throws a {@link FirebaseAuthException}.
1002+
*/
1003+
public ApiFuture<OidcProviderConfig> updateOidcProviderConfigAsync(
1004+
@NonNull OidcProviderConfig.UpdateRequest request) {
1005+
return updateOidcProviderConfigOp(request).callAsync(firebaseApp);
1006+
}
1007+
1008+
private CallableOperation<OidcProviderConfig, FirebaseAuthException> updateOidcProviderConfigOp(
1009+
final OidcProviderConfig.UpdateRequest request) {
1010+
checkNotDestroyed();
1011+
checkNotNull(request, "update request must not be null");
1012+
final FirebaseUserManager userManager = getUserManager();
1013+
return new CallableOperation<OidcProviderConfig, FirebaseAuthException>() {
1014+
@Override
1015+
protected OidcProviderConfig execute() throws FirebaseAuthException {
1016+
return userManager.updateOidcProviderConfig(request);
1017+
}
1018+
};
1019+
}
1020+
9811021
/**
9821022
* Gets the provider OIDC Auth config corresponding to the specified provider ID.
9831023
*

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

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

259-
private static String generateMask(Map<String, Object> properties) {
260-
// This implementation does not currently handle the case of nested properties. This is fine
261-
// since we do not currently generate masks for any properties with nested values. When it
262-
// comes time to implement this, we can check if a property has nested properties by checking
263-
// if it is an instance of the Map class.
264-
return Joiner.on(",").join(ImmutableSortedSet.copyOf(properties.keySet()));
265-
}
266-
267259
void deleteTenant(String tenantId) throws FirebaseAuthException {
268260
GenericUrl url = new GenericUrl(tenantMgtBaseUrl + getTenantUrlSuffix(tenantId));
269261
sendRequest("DELETE", url, null, GenericJson.class);
@@ -330,21 +322,45 @@ OidcProviderConfig createOidcProviderConfig(
330322
return sendRequest("POST", url, request.getProperties(), OidcProviderConfig.class);
331323
}
332324

325+
OidcProviderConfig updateOidcProviderConfig(OidcProviderConfig.UpdateRequest request)
326+
throws FirebaseAuthException {
327+
Map<String, Object> properties = request.getProperties();
328+
checkArgument(!properties.isEmpty(),
329+
"provider config update must have at least one property set");
330+
GenericUrl url =
331+
new GenericUrl(idpConfigMgtBaseUrl + getOidcUrlSuffix(request.getProviderId()));
332+
url.put("updateMask", generateMask(properties));
333+
return sendRequest("PATCH", url, properties, OidcProviderConfig.class);
334+
}
335+
333336
OidcProviderConfig getOidcProviderConfig(String providerId) throws FirebaseAuthException {
334-
GenericUrl url = new GenericUrl(idpConfigMgtBaseUrl + "/oauthIdpConfigs/" + providerId);
337+
GenericUrl url = new GenericUrl(idpConfigMgtBaseUrl + getOidcUrlSuffix(providerId));
335338
return sendRequest("GET", url, null, OidcProviderConfig.class);
336339
}
337340

338341
void deleteProviderConfig(String providerId) throws FirebaseAuthException {
339-
GenericUrl url = new GenericUrl(idpConfigMgtBaseUrl + "/oauthIdpConfigs/" + providerId);
342+
GenericUrl url = new GenericUrl(idpConfigMgtBaseUrl + getOidcUrlSuffix(providerId));
340343
sendRequest("DELETE", url, null, GenericJson.class);
341344
}
342345

346+
private static String generateMask(Map<String, Object> properties) {
347+
// This implementation does not currently handle the case of nested properties. This is fine
348+
// since we do not currently generate masks for any properties with nested values. When it
349+
// comes time to implement this, we can check if a property has nested properties by checking
350+
// if it is an instance of the Map class.
351+
return Joiner.on(",").join(ImmutableSortedSet.copyOf(properties.keySet()));
352+
}
353+
343354
private static String getTenantUrlSuffix(String tenantId) {
344-
checkArgument(!Strings.isNullOrEmpty(tenantId));
355+
checkArgument(!Strings.isNullOrEmpty(tenantId), "tenant ID must not be null or empty");
345356
return "/tenants/" + tenantId;
346357
}
347358

359+
private static String getOidcUrlSuffix(String providerId) {
360+
checkArgument(!Strings.isNullOrEmpty(providerId), "provider ID must not be null or empty");
361+
return "/oauthIdpConfigs/" + providerId;
362+
}
363+
348364
private <T> T post(String path, Object content, Class<T> clazz) throws FirebaseAuthException {
349365
checkArgument(!Strings.isNullOrEmpty(path), "path must not be null or empty");
350366
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: 3 additions & 4 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());
@@ -157,7 +156,7 @@ String getTenantId() {
157156
}
158157

159158
/**
160-
* Sets the display name of the existingtenant.
159+
* Sets the display name of the existing tenant.
161160
*
162161
* @param displayName a non-null, non-empty display name string.
163162
*/

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

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -973,6 +973,7 @@ public void testOidcProviderConfigLifecycle() throws Exception {
973973
OidcProviderConfig config = auth.createOidcProviderConfigAsync(createRequest).get();
974974
assertEquals(providerId, config.getProviderId());
975975
assertEquals("DisplayName", config.getDisplayName());
976+
assertTrue(config.isEnabled());
976977
assertEquals("ClientId", config.getClientId());
977978
assertEquals("https://oidc.com/issuer", config.getIssuer());
978979

@@ -981,11 +982,23 @@ public void testOidcProviderConfigLifecycle() throws Exception {
981982
config = auth.getOidcProviderConfigAsync(providerId).get();
982983
assertEquals(providerId, config.getProviderId());
983984
assertEquals("DisplayName", config.getDisplayName());
985+
assertTrue(config.isEnabled());
984986
assertEquals("ClientId", config.getClientId());
985987
assertEquals("https://oidc.com/issuer", config.getIssuer());
986988

987-
// TODO(micahstairs): Test updateProviderConfig operation
988-
989+
// Update config provider
990+
OidcProviderConfig.UpdateRequest updateRequest =
991+
new OidcProviderConfig.UpdateRequest(providerId)
992+
.setDisplayName("NewDisplayName")
993+
.setEnabled(false)
994+
.setClientId("NewClientId")
995+
.setIssuer("https://oidc.com/new-issuer");
996+
config = auth.updateOidcProviderConfigAsync(updateRequest).get();
997+
assertEquals(providerId, config.getProviderId());
998+
assertEquals("NewDisplayName", config.getDisplayName());
999+
assertFalse(config.isEnabled());
1000+
assertEquals("NewClientId", config.getClientId());
1001+
assertEquals("https://oidc.com/new-issuer", config.getIssuer());
9891002
} finally {
9901003
// Delete config provider
9911004
auth.deleteProviderConfigAsync(providerId).get();
@@ -1028,8 +1041,19 @@ public void testTenantAwareOidcProviderConfigLifecycle() throws Exception {
10281041
assertEquals("ClientId", config.getClientId());
10291042
assertEquals("https://oidc.com/issuer", config.getIssuer());
10301043

1031-
// TODO(micahstairs): Test updateProviderConfig operation
1032-
1044+
// Update config provider
1045+
OidcProviderConfig.UpdateRequest updateRequest =
1046+
new OidcProviderConfig.UpdateRequest(providerId)
1047+
.setDisplayName("NewDisplayName")
1048+
.setEnabled(false)
1049+
.setClientId("NewClientId")
1050+
.setIssuer("https://oidc.com/new-issuer");
1051+
config = tenantAwareAuth.updateOidcProviderConfigAsync(updateRequest).get();
1052+
assertEquals(providerId, config.getProviderId());
1053+
assertEquals("NewDisplayName", config.getDisplayName());
1054+
assertFalse(config.isEnabled());
1055+
assertEquals("NewClientId", config.getClientId());
1056+
assertEquals("https://oidc.com/new-issuer", config.getIssuer());
10331057
} finally {
10341058
// Delete config provider
10351059
tenantAwareAuth.deleteProviderConfigAsync(providerId).get();

0 commit comments

Comments
 (0)