44
44
import com .google .firebase .auth .internal .GetAccountInfoRequest ;
45
45
import com .google .firebase .auth .internal .GetAccountInfoResponse ;
46
46
import com .google .firebase .auth .internal .HttpErrorResponse ;
47
+ import com .google .firebase .auth .internal .ListOidcProviderConfigsResponse ;
48
+ import com .google .firebase .auth .internal .ListSamlProviderConfigsResponse ;
47
49
import com .google .firebase .auth .internal .ListTenantsResponse ;
48
50
import com .google .firebase .auth .internal .UploadAccountResponse ;
49
51
import com .google .firebase .internal .ApiClientUtils ;
69
71
*/
70
72
class FirebaseUserManager {
71
73
74
+ static final String CONFIGURATION_NOT_FOUND_ERROR = "configuration-not-found" ;
72
75
static final String TENANT_ID_MISMATCH_ERROR = "tenant-id-mismatch" ;
73
76
static final String TENANT_NOT_FOUND_ERROR = "tenant-not-found" ;
74
77
static final String USER_NOT_FOUND_ERROR = "user-not-found" ;
@@ -78,7 +81,7 @@ class FirebaseUserManager {
78
81
// SDK error codes defined at: https://firebase.google.com/docs/auth/admin/errors
79
82
private static final Map <String , String > ERROR_CODES = ImmutableMap .<String , String >builder ()
80
83
.put ("CLAIMS_TOO_LARGE" , "claims-too-large" )
81
- .put ("CONFIGURATION_NOT_FOUND" , "project-not-found" )
84
+ .put ("CONFIGURATION_NOT_FOUND" , CONFIGURATION_NOT_FOUND_ERROR )
82
85
.put ("INSUFFICIENT_PERMISSION" , "insufficient-permission" )
83
86
.put ("DUPLICATE_EMAIL" , "email-already-exists" )
84
87
.put ("DUPLICATE_LOCAL_ID" , "uid-already-exists" )
@@ -97,6 +100,7 @@ class FirebaseUserManager {
97
100
.put ("INVALID_DYNAMIC_LINK_DOMAIN" , "invalid-dynamic-link-domain" )
98
101
.build ();
99
102
103
+ static final int MAX_LIST_PROVIDER_CONFIGS_RESULTS = 100 ;
100
104
static final int MAX_LIST_TENANTS_RESULTS = 1000 ;
101
105
static final int MAX_GET_ACCOUNTS_BATCH_SIZE = 100 ;
102
106
static final int MAX_DELETE_ACCOUNTS_BATCH_SIZE = 1000 ;
@@ -112,6 +116,7 @@ class FirebaseUserManager {
112
116
private static final String CLIENT_VERSION_HEADER = "X-Client-Version" ;
113
117
114
118
private final String userMgtBaseUrl ;
119
+ private final String idpConfigMgtBaseUrl ;
115
120
private final String tenantMgtBaseUrl ;
116
121
private final JsonFactory jsonFactory ;
117
122
private final HttpRequestFactory requestFactory ;
@@ -126,15 +131,18 @@ class FirebaseUserManager {
126
131
"Project ID is required to access the auth service. Use a service account credential or "
127
132
+ "set the project ID explicitly via FirebaseOptions. Alternatively you can also "
128
133
+ "set the project ID via the GOOGLE_CLOUD_PROJECT environment variable." );
129
- String tenantId = builder .tenantId ;
130
- if (builder .tenantId == null ) {
131
- this .userMgtBaseUrl = String .format (ID_TOOLKIT_URL , "v1" , projectId );
134
+ final String idToolkitUrlV1 = String .format (ID_TOOLKIT_URL , "v1" , projectId );
135
+ final String idToolkitUrlV2 = String .format (ID_TOOLKIT_URL , "v2" , projectId );
136
+ final String tenantId = builder .tenantId ;
137
+ if (tenantId == null ) {
138
+ this .userMgtBaseUrl = idToolkitUrlV1 ;
139
+ this .idpConfigMgtBaseUrl = idToolkitUrlV2 ;
132
140
} else {
133
- checkArgument (!tenantId .isEmpty (), "Tenant ID must not be empty" );
134
- this .userMgtBaseUrl =
135
- String . format ( ID_TOOLKIT_URL , "v1" , projectId ) + getTenantUrlSuffix (tenantId );
141
+ checkArgument (!tenantId .isEmpty (), "Tenant ID must not be empty. " );
142
+ this .userMgtBaseUrl = idToolkitUrlV1 + getTenantUrlSuffix ( tenantId );
143
+ this . idpConfigMgtBaseUrl = idToolkitUrlV2 + getTenantUrlSuffix (tenantId );
136
144
}
137
- this .tenantMgtBaseUrl = String . format ( ID_TOOLKIT_URL , "v2" , projectId ) ;
145
+ this .tenantMgtBaseUrl = idToolkitUrlV2 ;
138
146
this .jsonFactory = app .getOptions ().getJsonFactory ();
139
147
this .requestFactory = builder .requestFactory == null
140
148
? ApiClientUtils .newAuthorizedRequestFactory (app ) : builder .requestFactory ;
@@ -295,20 +303,11 @@ Tenant createTenant(Tenant.CreateRequest request) throws FirebaseAuthException {
295
303
296
304
Tenant updateTenant (Tenant .UpdateRequest request ) throws FirebaseAuthException {
297
305
Map <String , Object > properties = request .getProperties ();
298
- checkArgument (!properties .isEmpty (), "Tenant update must have at least one property set" );
299
306
GenericUrl url = new GenericUrl (tenantMgtBaseUrl + getTenantUrlSuffix (request .getTenantId ()));
300
- url .put ("updateMask" , generateMask (properties ));
307
+ url .put ("updateMask" , Joiner . on ( "," ). join ( generateMask (properties ) ));
301
308
return sendRequest ("PATCH" , url , properties , Tenant .class );
302
309
}
303
310
304
- private static String generateMask (Map <String , Object > properties ) {
305
- // This implementation does not currently handle the case of nested properties. This is fine
306
- // since we do not currently generate masks for any properties with nested values. When it
307
- // comes time to implement this, we can check if a property has nested properties by checking
308
- // if it is an instance of the Map class.
309
- return Joiner .on ("," ).join (ImmutableSortedSet .copyOf (properties .keySet ()));
310
- }
311
-
312
311
void deleteTenant (String tenantId ) throws FirebaseAuthException {
313
312
GenericUrl url = new GenericUrl (tenantMgtBaseUrl + getTenantUrlSuffix (tenantId ));
314
313
sendRequest ("DELETE" , url , null , GenericJson .class );
@@ -366,11 +365,128 @@ String getEmailActionLink(EmailLinkType type, String email,
366
365
throw new FirebaseAuthException (INTERNAL_ERROR , "Failed to create email action link" );
367
366
}
368
367
368
+ OidcProviderConfig createOidcProviderConfig (
369
+ OidcProviderConfig .CreateRequest request ) throws FirebaseAuthException {
370
+ GenericUrl url = new GenericUrl (idpConfigMgtBaseUrl + "/oauthIdpConfigs" );
371
+ url .set ("oauthIdpConfigId" , request .getProviderId ());
372
+ return sendRequest ("POST" , url , request .getProperties (), OidcProviderConfig .class );
373
+ }
374
+
375
+ SamlProviderConfig createSamlProviderConfig (
376
+ SamlProviderConfig .CreateRequest request ) throws FirebaseAuthException {
377
+ GenericUrl url = new GenericUrl (idpConfigMgtBaseUrl + "/inboundSamlConfigs" );
378
+ url .set ("inboundSamlConfigId" , request .getProviderId ());
379
+ return sendRequest ("POST" , url , request .getProperties (), SamlProviderConfig .class );
380
+ }
381
+
382
+ OidcProviderConfig updateOidcProviderConfig (OidcProviderConfig .UpdateRequest request )
383
+ throws FirebaseAuthException {
384
+ Map <String , Object > properties = request .getProperties ();
385
+ GenericUrl url =
386
+ new GenericUrl (idpConfigMgtBaseUrl + getOidcUrlSuffix (request .getProviderId ()));
387
+ url .put ("updateMask" , Joiner .on ("," ).join (generateMask (properties )));
388
+ return sendRequest ("PATCH" , url , properties , OidcProviderConfig .class );
389
+ }
390
+
391
+ SamlProviderConfig updateSamlProviderConfig (SamlProviderConfig .UpdateRequest request )
392
+ throws FirebaseAuthException {
393
+ Map <String , Object > properties = request .getProperties ();
394
+ GenericUrl url =
395
+ new GenericUrl (idpConfigMgtBaseUrl + getSamlUrlSuffix (request .getProviderId ()));
396
+ url .put ("updateMask" , Joiner .on ("," ).join (generateMask (properties )));
397
+ return sendRequest ("PATCH" , url , properties , SamlProviderConfig .class );
398
+ }
399
+
400
+ OidcProviderConfig getOidcProviderConfig (String providerId ) throws FirebaseAuthException {
401
+ GenericUrl url = new GenericUrl (idpConfigMgtBaseUrl + getOidcUrlSuffix (providerId ));
402
+ return sendRequest ("GET" , url , null , OidcProviderConfig .class );
403
+ }
404
+
405
+ SamlProviderConfig getSamlProviderConfig (String providerId ) throws FirebaseAuthException {
406
+ GenericUrl url = new GenericUrl (idpConfigMgtBaseUrl + getSamlUrlSuffix (providerId ));
407
+ return sendRequest ("GET" , url , null , SamlProviderConfig .class );
408
+ }
409
+
410
+ ListOidcProviderConfigsResponse listOidcProviderConfigs (int maxResults , String pageToken )
411
+ throws FirebaseAuthException {
412
+ ImmutableMap .Builder <String , Object > builder =
413
+ ImmutableMap .<String , Object >builder ().put ("pageSize" , maxResults );
414
+ if (pageToken != null ) {
415
+ checkArgument (!pageToken .equals (
416
+ ListProviderConfigsPage .END_OF_LIST ), "Invalid end of list page token." );
417
+ builder .put ("nextPageToken" , pageToken );
418
+ }
419
+
420
+ GenericUrl url = new GenericUrl (idpConfigMgtBaseUrl + "/oauthIdpConfigs" );
421
+ url .putAll (builder .build ());
422
+ ListOidcProviderConfigsResponse response =
423
+ sendRequest ("GET" , url , null , ListOidcProviderConfigsResponse .class );
424
+ if (response == null ) {
425
+ throw new FirebaseAuthException (INTERNAL_ERROR , "Failed to retrieve provider configs." );
426
+ }
427
+ return response ;
428
+ }
429
+
430
+ ListSamlProviderConfigsResponse listSamlProviderConfigs (int maxResults , String pageToken )
431
+ throws FirebaseAuthException {
432
+ ImmutableMap .Builder <String , Object > builder =
433
+ ImmutableMap .<String , Object >builder ().put ("pageSize" , maxResults );
434
+ if (pageToken != null ) {
435
+ checkArgument (!pageToken .equals (
436
+ ListProviderConfigsPage .END_OF_LIST ), "Invalid end of list page token." );
437
+ builder .put ("nextPageToken" , pageToken );
438
+ }
439
+
440
+ GenericUrl url = new GenericUrl (idpConfigMgtBaseUrl + "/inboundSamlConfigs" );
441
+ url .putAll (builder .build ());
442
+ ListSamlProviderConfigsResponse response =
443
+ sendRequest ("GET" , url , null , ListSamlProviderConfigsResponse .class );
444
+ if (response == null ) {
445
+ throw new FirebaseAuthException (INTERNAL_ERROR , "Failed to retrieve provider configs." );
446
+ }
447
+ return response ;
448
+ }
449
+
450
+ void deleteOidcProviderConfig (String providerId ) throws FirebaseAuthException {
451
+ GenericUrl url = new GenericUrl (idpConfigMgtBaseUrl + getOidcUrlSuffix (providerId ));
452
+ sendRequest ("DELETE" , url , null , GenericJson .class );
453
+ }
454
+
455
+ void deleteSamlProviderConfig (String providerId ) throws FirebaseAuthException {
456
+ GenericUrl url = new GenericUrl (idpConfigMgtBaseUrl + getSamlUrlSuffix (providerId ));
457
+ sendRequest ("DELETE" , url , null , GenericJson .class );
458
+ }
459
+
460
+ private static Set <String > generateMask (Map <String , Object > properties ) {
461
+ ImmutableSortedSet .Builder <String > maskBuilder = ImmutableSortedSet .naturalOrder ();
462
+ for (Map .Entry <String , Object > entry : properties .entrySet ()) {
463
+ if (entry .getValue () instanceof Map ) {
464
+ Set <String > childMask = generateMask ((Map <String , Object >) entry .getValue ());
465
+ for (String childProperty : childMask ) {
466
+ maskBuilder .add (entry .getKey () + "." + childProperty );
467
+ }
468
+ } else {
469
+ maskBuilder .add (entry .getKey ());
470
+ }
471
+ }
472
+ return maskBuilder .build ();
473
+ }
474
+
369
475
private static String getTenantUrlSuffix (String tenantId ) {
370
- checkArgument (!Strings .isNullOrEmpty (tenantId ));
476
+ checkArgument (!Strings .isNullOrEmpty (tenantId ), "Tenant ID must not be null or empty." );
371
477
return "/tenants/" + tenantId ;
372
478
}
373
479
480
+ private static String getOidcUrlSuffix (String providerId ) {
481
+ checkArgument (!Strings .isNullOrEmpty (providerId ), "Provider ID must not be null or empty." );
482
+ return "/oauthIdpConfigs/" + providerId ;
483
+ }
484
+
485
+ private static String getSamlUrlSuffix (String providerId ) {
486
+ checkArgument (!Strings .isNullOrEmpty (providerId ), "Provider ID must not be null or empty." );
487
+ return "/inboundSamlConfigs/" + providerId ;
488
+ }
489
+
374
490
private <T > T post (String path , Object content , Class <T > clazz ) throws FirebaseAuthException {
375
491
checkArgument (!Strings .isNullOrEmpty (path ), "path must not be null or empty" );
376
492
checkNotNull (content , "content must not be null for POST requests" );
0 commit comments