Skip to content

Commit cdd8db2

Browse files
chris4490claude
andauthored
feat: add ssoId support to loadSettings and add loadAllSettings for multi-SSO tenants (#333)
* feat: add ssoId support to loadSettings and add loadAllSettings for multi-SSO tenants Adds parity with the Node.js and Go SDKs: - loadSettings(tenantId, ssoId): optional ssoId param to fetch a specific SSO config - loadAllSettings(tenantId): new method returning all SSO configs for a tenant via /v2/mgmt/sso/settings/all The existing loadSettings(tenantId) overload is preserved for backward compatibility and now delegates to the two-argument form with ssoId=null. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: sort SSOAllSettingsResponse import in lexicographical order Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 4905839 commit cdd8db2

6 files changed

Lines changed: 136 additions & 4 deletions

File tree

README.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -910,9 +910,24 @@ You can manage SSO settings and map SSO group roles and user attributes.
910910

911911
```java
912912
SsoService ss = descopeClient.getManagementServices().getSsoService();
913-
// You can get SSO settings for a specific tenant ID
913+
// Load the default SSO settings for a tenant
914914
try {
915-
SSOSettingsResponse resp = ss.loadSettings("tenant-id");
915+
SSOTenantSettingsResponse resp = ss.loadSettings("tenant-id");
916+
} catch (DescopeException de) {
917+
// Handle the error
918+
}
919+
920+
// Load a specific SSO configuration by ssoId (multi-SSO tenants)
921+
try {
922+
SSOTenantSettingsResponse resp = ss.loadSettings("tenant-id", "sso-config-id");
923+
} catch (DescopeException de) {
924+
// Handle the error
925+
}
926+
927+
// Load all SSO configurations for a tenant
928+
try {
929+
SSOAllSettingsResponse resp = ss.loadAllSettings("tenant-id");
930+
List<SSOTenantSettingsResponse> allConfigs = resp.getSsoSettings();
916931
} catch (DescopeException de) {
917932
// Handle the error
918933
}

src/main/java/com/descope/literals/Routes.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ public static class ManagementEndPoints {
140140
public static final String SSO_CONFIGURE_METADATA_LINK = "/v1/mgmt/sso/metadata";
141141
public static final String SSO_CONFIGURE_MAPPING_LINK = "/v1/mgmt/sso/mapping";
142142
public static final String SSO_GET_SETTINGS_V2_LINK = "/v2/mgmt/sso/settings";
143+
public static final String SSO_GET_ALL_SETTINGS_V2_LINK = "/v2/mgmt/sso/settings/all";
143144
public static final String SSO_CONFIGURE_SAML_SETTINGS_LINK = "/v1/mgmt/sso/saml";
144145
public static final String SSO_CONFIGURE_SAML_SETTINGS_BY_MD_LINK = "/v1/mgmt/sso/saml/metadata";
145146
public static final String SSO_CONFIGURE_OIDC_SETTINGS_LINK = "/v1/mgmt/sso/oidc";
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.descope.model.sso;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import java.util.List;
5+
import lombok.AllArgsConstructor;
6+
import lombok.Builder;
7+
import lombok.Data;
8+
import lombok.NoArgsConstructor;
9+
10+
@Data
11+
@Builder
12+
@NoArgsConstructor
13+
@AllArgsConstructor
14+
public class SSOAllSettingsResponse {
15+
@JsonProperty("SSOSettings")
16+
private List<SSOTenantSettingsResponse> ssoSettings;
17+
}

src/main/java/com/descope/sdk/mgmt/SsoService.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.descope.exception.DescopeException;
44
import com.descope.model.sso.AttributeMapping;
55
import com.descope.model.sso.RoleMapping;
6+
import com.descope.model.sso.SSOAllSettingsResponse;
67
import com.descope.model.sso.SSOOIDCSettings;
78
import com.descope.model.sso.SSOSAMLSettings;
89
import com.descope.model.sso.SSOSAMLSettingsByMetadata;
@@ -12,14 +13,34 @@
1213

1314
public interface SsoService {
1415
/**
15-
* Load all tenant SSO setting.
16+
* Load SSO settings for the given tenant. When {@code ssoId} is blank the default SSO
17+
* configuration is returned (same behavior as {@link #loadSettings(String)}).
18+
*
19+
* @param tenantId the tenant ID to load settings for
20+
* @param ssoId optional SSO configuration ID; pass {@code null} or empty to get the default
21+
* @return {@link SSOTenantSettingsResponse} the matching SSO settings
22+
* @throws DescopeException If error, a subtype of this exception will be thrown
23+
*/
24+
SSOTenantSettingsResponse loadSettings(String tenantId, String ssoId) throws DescopeException;
25+
26+
/**
27+
* Load SSO settings for the given tenant (returns the default SSO configuration).
1628
*
1729
* @param tenantId the tenant ID we are loading settings for
1830
* @return {@link SSOTenantSettingsResponse} all SSO settings for the tenant
1931
* @throws DescopeException If error, a subtype of this exception will be thrown
2032
*/
2133
SSOTenantSettingsResponse loadSettings(String tenantId) throws DescopeException;
2234

35+
/**
36+
* Load all SSO configurations for the given tenant.
37+
*
38+
* @param tenantId the tenant ID to load all SSO configurations for
39+
* @return {@link SSOAllSettingsResponse} all SSO configurations for the tenant
40+
* @throws DescopeException If error, a subtype of this exception will be thrown
41+
*/
42+
SSOAllSettingsResponse loadAllSettings(String tenantId) throws DescopeException;
43+
2344
/**
2445
* Configure SSO SAML settings for a tenant manually.
2546
*

src/main/java/com/descope/sdk/mgmt/impl/SsoServiceImpl.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import static com.descope.literals.Routes.ManagementEndPoints.SSO_CONFIGURE_SAML_SETTINGS_LINK;
88
import static com.descope.literals.Routes.ManagementEndPoints.SSO_CONFIGURE_SETTINGS_LINK;
99
import static com.descope.literals.Routes.ManagementEndPoints.SSO_DELETE_SETTINGS_LINK;
10+
import static com.descope.literals.Routes.ManagementEndPoints.SSO_GET_ALL_SETTINGS_V2_LINK;
1011
import static com.descope.literals.Routes.ManagementEndPoints.SSO_GET_SETTINGS_LINK;
1112
import static com.descope.literals.Routes.ManagementEndPoints.SSO_GET_SETTINGS_V2_LINK;
1213
import static com.descope.utils.CollectionUtils.mapOf;
@@ -16,13 +17,15 @@
1617
import com.descope.model.client.Client;
1718
import com.descope.model.sso.AttributeMapping;
1819
import com.descope.model.sso.RoleMapping;
20+
import com.descope.model.sso.SSOAllSettingsResponse;
1921
import com.descope.model.sso.SSOOIDCSettings;
2022
import com.descope.model.sso.SSOSAMLSettings;
2123
import com.descope.model.sso.SSOSAMLSettingsByMetadata;
2224
import com.descope.model.sso.SSOSettingsResponse;
2325
import com.descope.model.sso.SSOTenantSettingsResponse;
2426
import com.descope.proxy.ApiProxy;
2527
import com.descope.sdk.mgmt.SsoService;
28+
import java.util.HashMap;
2629
import java.util.List;
2730
import java.util.Map;
2831
import org.apache.commons.lang3.StringUtils;
@@ -32,14 +35,33 @@ class SsoServiceImpl extends ManagementsBase implements SsoService {
3235
super(client);
3336
}
3437

38+
@Override
39+
public SSOTenantSettingsResponse loadSettings(String tenantId, String ssoId) throws DescopeException {
40+
if (StringUtils.isBlank(tenantId)) {
41+
throw ServerCommonException.invalidArgument("TenantId");
42+
}
43+
Map<String, String> params = new HashMap<>();
44+
params.put("tenantId", tenantId);
45+
if (StringUtils.isNotBlank(ssoId)) {
46+
params.put("ssoId", ssoId);
47+
}
48+
ApiProxy apiProxy = getApiProxy();
49+
return apiProxy.get(getQueryParamUri(SSO_GET_SETTINGS_V2_LINK, params), SSOTenantSettingsResponse.class);
50+
}
51+
3552
@Override
3653
public SSOTenantSettingsResponse loadSettings(String tenantId) throws DescopeException {
54+
return loadSettings(tenantId, null);
55+
}
56+
57+
@Override
58+
public SSOAllSettingsResponse loadAllSettings(String tenantId) throws DescopeException {
3759
if (StringUtils.isBlank(tenantId)) {
3860
throw ServerCommonException.invalidArgument("TenantId");
3961
}
4062
Map<String, String> params = mapOf("tenantId", tenantId);
4163
ApiProxy apiProxy = getApiProxy();
42-
return apiProxy.get(getQueryParamUri(SSO_GET_SETTINGS_V2_LINK, params), SSOTenantSettingsResponse.class);
64+
return apiProxy.get(getQueryParamUri(SSO_GET_ALL_SETTINGS_V2_LINK, params), SSOAllSettingsResponse.class);
4365
}
4466

4567
@Override

src/test/java/com/descope/sdk/mgmt/impl/SsoServiceImplTest.java

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import com.descope.model.sso.GroupsMapping;
2020
import com.descope.model.sso.OIDCAttributeMapping;
2121
import com.descope.model.sso.RoleMapping;
22+
import com.descope.model.sso.SSOAllSettingsResponse;
2223
import com.descope.model.sso.SSOOIDCSettings;
2324
import com.descope.model.sso.SSOSAMLSettings;
2425
import com.descope.model.sso.SSOSAMLSettingsByMetadata;
@@ -77,6 +78,61 @@ void testGetSettingsForSuccess() {
7778
}
7879
}
7980

81+
@Test
82+
void testLoadSettingsWithSsoIdForEmptyTenantId() {
83+
ServerCommonException thrown =
84+
assertThrows(ServerCommonException.class, () -> ssoService.loadSettings("", "someSsoId"));
85+
assertNotNull(thrown);
86+
assertEquals("The TenantId argument is invalid", thrown.getMessage());
87+
}
88+
89+
@Test
90+
void testLoadSettingsWithSsoIdForSuccess() {
91+
SSOTenantSettingsResponse ssoTenantSettingsResponse = mock(SSOTenantSettingsResponse.class);
92+
ApiProxy apiProxy = mock(ApiProxy.class);
93+
doReturn(ssoTenantSettingsResponse).when(apiProxy).get(any(), any());
94+
try (MockedStatic<ApiProxyBuilder> mockedApiProxyBuilder = mockStatic(ApiProxyBuilder.class)) {
95+
mockedApiProxyBuilder.when(
96+
() -> ApiProxyBuilder.buildProxy(any(), any())).thenReturn(apiProxy);
97+
SSOTenantSettingsResponse response = ssoService.loadSettings("someTenantID", "someSsoId");
98+
Assertions.assertThat(response).isNotNull();
99+
}
100+
}
101+
102+
@Test
103+
void testLoadSettingsWithNullSsoIdForSuccess() {
104+
SSOTenantSettingsResponse ssoTenantSettingsResponse = mock(SSOTenantSettingsResponse.class);
105+
ApiProxy apiProxy = mock(ApiProxy.class);
106+
doReturn(ssoTenantSettingsResponse).when(apiProxy).get(any(), any());
107+
try (MockedStatic<ApiProxyBuilder> mockedApiProxyBuilder = mockStatic(ApiProxyBuilder.class)) {
108+
mockedApiProxyBuilder.when(
109+
() -> ApiProxyBuilder.buildProxy(any(), any())).thenReturn(apiProxy);
110+
SSOTenantSettingsResponse response = ssoService.loadSettings("someTenantID", null);
111+
Assertions.assertThat(response).isNotNull();
112+
}
113+
}
114+
115+
@Test
116+
void testLoadAllSettingsForEmptyTenantId() {
117+
ServerCommonException thrown =
118+
assertThrows(ServerCommonException.class, () -> ssoService.loadAllSettings(""));
119+
assertNotNull(thrown);
120+
assertEquals("The TenantId argument is invalid", thrown.getMessage());
121+
}
122+
123+
@Test
124+
void testLoadAllSettingsForSuccess() {
125+
SSOAllSettingsResponse ssoAllSettingsResponse = mock(SSOAllSettingsResponse.class);
126+
ApiProxy apiProxy = mock(ApiProxy.class);
127+
doReturn(ssoAllSettingsResponse).when(apiProxy).get(any(), any());
128+
try (MockedStatic<ApiProxyBuilder> mockedApiProxyBuilder = mockStatic(ApiProxyBuilder.class)) {
129+
mockedApiProxyBuilder.when(
130+
() -> ApiProxyBuilder.buildProxy(any(), any())).thenReturn(apiProxy);
131+
SSOAllSettingsResponse response = ssoService.loadAllSettings("someTenantID");
132+
Assertions.assertThat(response).isNotNull();
133+
}
134+
}
135+
80136
@Test
81137
void testDeleteSettingsForEmptyTenantId() {
82138
ServerCommonException thrown =

0 commit comments

Comments
 (0)