Skip to content

Commit 537b7a7

Browse files
committed
Add class for SAML provider config. (#419)
Adds a class for the SAML provider config.
1 parent 99b07bd commit 537b7a7

File tree

4 files changed

+350
-3
lines changed

4 files changed

+350
-3
lines changed
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
/*
2+
* Copyright 2020 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.firebase.auth;
18+
19+
import static com.google.common.base.Preconditions.checkArgument;
20+
import static com.google.common.base.Preconditions.checkNotNull;
21+
22+
import com.google.api.client.json.GenericJson;
23+
import com.google.api.client.util.Key;
24+
import com.google.common.base.Strings;
25+
import com.google.common.collect.ImmutableList;
26+
import com.google.common.collect.ImmutableMap;
27+
import com.google.firebase.auth.ProviderConfig.AbstractCreateRequest;
28+
import com.google.firebase.auth.ProviderConfig.AbstractUpdateRequest;
29+
import java.util.ArrayList;
30+
import java.util.HashMap;
31+
import java.util.List;
32+
import java.util.Map;
33+
34+
/**
35+
* Contains metadata associated with a SAML Auth provider.
36+
*
37+
* <p>Instances of this class are immutable and thread safe.
38+
*/
39+
public final class SamlProviderConfig extends ProviderConfig {
40+
41+
@Key("idpConfig")
42+
private GenericJson idpConfig;
43+
44+
@Key("spConfig")
45+
private GenericJson spConfig;
46+
47+
public String getIdpEntityId() {
48+
return (String) idpConfig.get("idpEntityId");
49+
}
50+
51+
public String getSsoUrl() {
52+
return (String) idpConfig.get("ssoUrl");
53+
}
54+
55+
public List<String> getX509Certificates() {
56+
List<Map<String, String>> idpCertificates =
57+
(List<Map<String, String>>) idpConfig.get("idpCertificates");
58+
checkNotNull(idpCertificates);
59+
ImmutableList.Builder<String> certificates = ImmutableList.<String>builder();
60+
for (Map<String, String> idpCertificate : idpCertificates) {
61+
certificates.add(idpCertificate.get("x509Certificate"));
62+
}
63+
return certificates.build();
64+
}
65+
66+
public String getRpEntityId() {
67+
return (String) spConfig.get("spEntityId");
68+
}
69+
70+
public String getCallbackUrl() {
71+
return (String) spConfig.get("callbackUri");
72+
}
73+
74+
private static List<Object> ensureNestedList(Map<String, Object> outerMap, String id) {
75+
List<Object> list = (List<Object>) outerMap.get(id);
76+
if (list == null) {
77+
list = new ArrayList<Object>();
78+
outerMap.put(id, list);
79+
}
80+
return list;
81+
}
82+
83+
private static Map<String, Object> ensureNestedMap(Map<String, Object> outerMap, String id) {
84+
Map<String, Object> map = (Map<String, Object>) outerMap.get(id);
85+
if (map == null) {
86+
map = new HashMap<String, Object>();
87+
outerMap.put(id, map);
88+
}
89+
return map;
90+
}
91+
92+
/**
93+
* A specification class for creating a new SAML Auth provider.
94+
*
95+
* <p>Set the initial attributes of the new provider by calling various setter methods available
96+
* in this class.
97+
*/
98+
public static final class CreateRequest extends AbstractCreateRequest<CreateRequest> {
99+
100+
/**
101+
* Creates a new {@link CreateRequest}, which can be used to create a new SAML Auth provider.
102+
*
103+
* <p>The returned object should be passed to
104+
* {@link AbstractFirebaseAuth#createSamlProviderConfig(CreateRequest)} to register the provider
105+
* information persistently.
106+
*/
107+
public CreateRequest() { }
108+
109+
/**
110+
* Sets the IDP entity ID for the new provider.
111+
*
112+
* @param idpEntityId A non-null, non-empty IDP entity ID string.
113+
* @throws IllegalArgumentException If the IDP entity ID is null or empty.
114+
*/
115+
public CreateRequest setIdpEntityId(String idpEntityId) {
116+
checkArgument(!Strings.isNullOrEmpty(idpEntityId),
117+
"IDP entity ID must not be null or empty.");
118+
ensureNestedMap(properties, "idpConfig").put("idpEntityId", idpEntityId);
119+
return this;
120+
}
121+
122+
/**
123+
* Sets the SSO URL for the new provider.
124+
*
125+
* @param ssoUrl A non-null, non-empty SSO URL string.
126+
* @throws IllegalArgumentException If the SSO URL is null or empty, or if the format is
127+
* invalid.
128+
*/
129+
public CreateRequest setSsoUrl(String ssoUrl) {
130+
checkArgument(!Strings.isNullOrEmpty(ssoUrl), "SSO URL must not be null or empty.");
131+
assertValidUrl(ssoUrl);
132+
ensureNestedMap(properties, "idpConfig").put("ssoUrl", ssoUrl);
133+
return this;
134+
}
135+
136+
/**
137+
* Adds a x509 certificate to the new provider.
138+
*
139+
* @param x509Certificate A non-null, non-empty x509 certificate string.
140+
* @throws IllegalArgumentException If the x509 certificate is null or empty.
141+
*/
142+
public CreateRequest addX509Certificate(String x509Certificate) {
143+
checkArgument(!Strings.isNullOrEmpty(x509Certificate),
144+
"The x509 certificate must not be null or empty.");
145+
Map<String, Object> idpConfigProperties = ensureNestedMap(properties, "idpConfig");
146+
List<Object> x509Certificates = ensureNestedList(idpConfigProperties, "idpCertificates");
147+
x509Certificates.add(ImmutableMap.<String, Object>of("x509Certificate", x509Certificate));
148+
return this;
149+
}
150+
151+
// TODO(micahstairs): Add 'addAllX509Certificates' method.
152+
153+
/**
154+
* Sets the RP entity ID for the new provider.
155+
*
156+
* @param rpEntityId A non-null, non-empty RP entity ID string.
157+
* @throws IllegalArgumentException If the RP entity ID is null or empty.
158+
*/
159+
public CreateRequest setRpEntityId(String rpEntityId) {
160+
checkArgument(!Strings.isNullOrEmpty(rpEntityId), "RP entity ID must not be null or empty.");
161+
ensureNestedMap(properties, "spConfig").put("spEntityId", rpEntityId);
162+
return this;
163+
}
164+
165+
/**
166+
* Sets the callback URL for the new provider.
167+
*
168+
* @param callbackUrl A non-null, non-empty callback URL string.
169+
* @throws IllegalArgumentException If the callback URL is null or empty, or if the format is
170+
* invalid.
171+
*/
172+
public CreateRequest setCallbackUrl(String callbackUrl) {
173+
checkArgument(!Strings.isNullOrEmpty(callbackUrl), "Callback URL must not be null or empty.");
174+
assertValidUrl(callbackUrl);
175+
ensureNestedMap(properties, "spConfig").put("callbackUri", callbackUrl);
176+
return this;
177+
}
178+
179+
// TODO(micahstairs): Add 'setRequestSigningEnabled' method.
180+
181+
CreateRequest getThis() {
182+
return this;
183+
}
184+
185+
void assertValidProviderIdFormat(String providerId) {
186+
checkArgument(providerId.startsWith("saml."), "Invalid SAML provider ID: " + providerId);
187+
}
188+
}
189+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public class OidcProviderConfigTest {
4141
+ "}").replace("'", "\"");
4242

4343
@Test
44-
public void testJsonSerialization() throws IOException {
44+
public void testJsonDeserialization() throws IOException {
4545
OidcProviderConfig config = jsonFactory.fromString(OIDC_JSON_STRING, OidcProviderConfig.class);
4646

4747
assertEquals("oidc.provider-id", config.getProviderId());
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
/*
2+
* Copyright 2020 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.firebase.auth;
18+
19+
import static org.junit.Assert.assertEquals;
20+
import static org.junit.Assert.assertFalse;
21+
import static org.junit.Assert.assertNotNull;
22+
import static org.junit.Assert.assertNull;
23+
import static org.junit.Assert.assertTrue;
24+
25+
import com.google.api.client.googleapis.util.Utils;
26+
import com.google.api.client.json.JsonFactory;
27+
import com.google.common.collect.ImmutableList;
28+
import com.google.common.collect.ImmutableMap;
29+
import java.io.IOException;
30+
import java.util.List;
31+
import java.util.Map;
32+
import org.junit.Test;
33+
34+
public class SamlProviderConfigTest {
35+
36+
private static final JsonFactory jsonFactory = Utils.getDefaultJsonFactory();
37+
38+
private static final String SAML_JSON_STRING =
39+
("{"
40+
+ " 'name': 'projects/projectId/inboundSamlConfigs/saml.provider-id',"
41+
+ " 'displayName': 'DISPLAY_NAME',"
42+
+ " 'enabled': true,"
43+
+ " 'idpConfig': {"
44+
+ " 'idpEntityId': 'IDP_ENTITY_ID',"
45+
+ " 'ssoUrl': 'https://example.com/login',"
46+
+ " 'idpCertificates': ["
47+
+ " { 'x509Certificate': 'certificate1' },"
48+
+ " { 'x509Certificate': 'certificate2' }"
49+
+ " ]"
50+
+ " },"
51+
+ " 'spConfig': {"
52+
+ " 'spEntityId': 'RP_ENTITY_ID',"
53+
+ " 'callbackUri': 'https://projectId.firebaseapp.com/__/auth/handler'"
54+
+ " }"
55+
+ "}").replace("'", "\"");
56+
57+
@Test
58+
public void testJsonDeserialization() throws IOException {
59+
SamlProviderConfig config = jsonFactory.fromString(SAML_JSON_STRING, SamlProviderConfig.class);
60+
61+
assertEquals("saml.provider-id", config.getProviderId());
62+
assertEquals("DISPLAY_NAME", config.getDisplayName());
63+
assertTrue(config.isEnabled());
64+
assertEquals("IDP_ENTITY_ID", config.getIdpEntityId());
65+
assertEquals("https://example.com/login", config.getSsoUrl());
66+
assertEquals(ImmutableList.of("certificate1", "certificate2"), config.getX509Certificates());
67+
assertEquals("RP_ENTITY_ID", config.getRpEntityId());
68+
assertEquals("https://projectId.firebaseapp.com/__/auth/handler", config.getCallbackUrl());
69+
}
70+
71+
@Test
72+
public void testCreateRequest() throws IOException {
73+
SamlProviderConfig.CreateRequest createRequest =
74+
new SamlProviderConfig.CreateRequest()
75+
.setProviderId("saml.provider-id")
76+
.setDisplayName("DISPLAY_NAME")
77+
.setEnabled(false)
78+
.setIdpEntityId("IDP_ENTITY_ID")
79+
.setSsoUrl("https://example.com/login")
80+
.addX509Certificate("certificate1")
81+
.addX509Certificate("certificate2")
82+
.setRpEntityId("RP_ENTITY_ID")
83+
.setCallbackUrl("https://projectId.firebaseapp.com/__/auth/handler");
84+
85+
assertEquals("saml.provider-id", createRequest.getProviderId());
86+
Map<String,Object> properties = createRequest.getProperties();
87+
assertEquals(4, properties.size());
88+
assertEquals("DISPLAY_NAME", (String) properties.get("displayName"));
89+
assertFalse((boolean) properties.get("enabled"));
90+
91+
Map<String, Object> idpConfig = (Map<String, Object>) properties.get("idpConfig");
92+
assertNotNull(idpConfig);
93+
assertEquals(3, idpConfig.size());
94+
assertEquals("IDP_ENTITY_ID", idpConfig.get("idpEntityId"));
95+
assertEquals("https://example.com/login", idpConfig.get("ssoUrl"));
96+
List<Object> idpCertificates = (List<Object>) idpConfig.get("idpCertificates");
97+
assertNotNull(idpCertificates);
98+
assertEquals(2, idpCertificates.size());
99+
assertEquals(ImmutableMap.of("x509Certificate", "certificate1"), idpCertificates.get(0));
100+
assertEquals(ImmutableMap.of("x509Certificate", "certificate2"), idpCertificates.get(1));
101+
102+
Map<String, Object> spConfig = (Map<String, Object>) properties.get("spConfig");
103+
assertNotNull(spConfig);
104+
assertEquals(2, spConfig.size());
105+
assertEquals("RP_ENTITY_ID", spConfig.get("spEntityId"));
106+
assertEquals("https://projectId.firebaseapp.com/__/auth/handler", spConfig.get("callbackUri"));
107+
}
108+
109+
@Test(expected = IllegalArgumentException.class)
110+
public void testCreateRequestMissingProviderId() {
111+
new SamlProviderConfig.CreateRequest().setProviderId(null);
112+
}
113+
114+
@Test(expected = IllegalArgumentException.class)
115+
public void testCreateRequestInvalidProviderId() {
116+
new SamlProviderConfig.CreateRequest().setProviderId("oidc.provider-id");
117+
}
118+
119+
@Test(expected = IllegalArgumentException.class)
120+
public void testCreateRequestMissingDisplayName() {
121+
new SamlProviderConfig.CreateRequest().setDisplayName(null);
122+
}
123+
124+
@Test(expected = IllegalArgumentException.class)
125+
public void testCreateRequestMissingIdpEntityId() {
126+
new SamlProviderConfig.CreateRequest().setIdpEntityId(null);
127+
}
128+
129+
@Test(expected = IllegalArgumentException.class)
130+
public void testCreateRequestMissingSsoUrl() {
131+
new SamlProviderConfig.CreateRequest().setSsoUrl(null);
132+
}
133+
134+
@Test(expected = IllegalArgumentException.class)
135+
public void testCreateRequestInvalidSsoUrl() {
136+
new SamlProviderConfig.CreateRequest().setSsoUrl("not a valid url");
137+
}
138+
139+
@Test(expected = IllegalArgumentException.class)
140+
public void testCreateRequestMissingX509Certificate() {
141+
new SamlProviderConfig.CreateRequest().addX509Certificate(null);
142+
}
143+
144+
@Test(expected = IllegalArgumentException.class)
145+
public void testCreateRequestMissingRpEntityId() {
146+
new SamlProviderConfig.CreateRequest().setRpEntityId(null);
147+
}
148+
149+
@Test(expected = IllegalArgumentException.class)
150+
public void testCreateRequestMissingCallbackUrl() {
151+
new SamlProviderConfig.CreateRequest().setCallbackUrl(null);
152+
}
153+
154+
@Test(expected = IllegalArgumentException.class)
155+
public void testCreateRequestInvalidCallbackUrl() {
156+
new SamlProviderConfig.CreateRequest().setCallbackUrl("not a valid url");
157+
}
158+
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public class TenantTest {
3232

3333
private static final JsonFactory jsonFactory = Utils.getDefaultJsonFactory();
3434

35-
private static final String TENANT_JSON_STRING =
35+
private static final String TENANT_JSON_STRING =
3636
"{"
3737
+ "\"name\":\"projects/project-id/resource/TENANT_ID\","
3838
+ "\"displayName\":\"DISPLAY_NAME\","
@@ -41,7 +41,7 @@ public class TenantTest {
4141
+ "}";
4242

4343
@Test
44-
public void testJsonSerialization() throws IOException {
44+
public void testJsonDeserialization() throws IOException {
4545
Tenant tenant = jsonFactory.fromString(TENANT_JSON_STRING, Tenant.class);
4646

4747
assertEquals(tenant.getTenantId(), "TENANT_ID");

0 commit comments

Comments
 (0)