Skip to content

Commit 1b5698a

Browse files
toharmslavikm
andauthored
Generation/Revocation of Tenant Admin SSO Link (#219)
* Added to TenantService - generation and revocation of admin/config SSO link & testing. * Fixed request body, revoke link, next: adding proper testing * fix test * Added Request/Response class for mock testing. Fixed lint issues & generate success test. * lint 1 * lint 2 * alphabetical order is hard * addIfNotNull population * more testing * added integration testing * fixed request struct * added a period * Bump version --------- Co-authored-by: Slavik Markovich <s@descope.com>
1 parent 7609c92 commit 1b5698a

File tree

8 files changed

+218
-10
lines changed

8 files changed

+218
-10
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<groupId>com.descope</groupId>
66
<artifactId>java-sdk</artifactId>
77
<modelVersion>4.0.0</modelVersion>
8-
<version>1.0.46</version>
8+
<version>1.0.47</version>
99
<name>${project.groupId}:${project.artifactId}</name>
1010
<description>Java library used to integrate with Descope.</description>
1111
<url>https://github.com/descope/descope-java</url>

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ public static class ManagementEndPoints {
126126
public static final String LOAD_ALL_TENANTS_LINK = "/v1/mgmt/tenant/all";
127127
public static final String TENANT_SEARCH_ALL_LINK = "/v1/mgmt/tenant/search";
128128
public static final String GET_TENANT_SETTINGS_LINK = "/v1/mgmt/tenant/settings";
129+
public static final String GENERATE_SSO_CONFIGURATION_LINK = "/v2/mgmt/tenant/adminlinks/sso/generate";
130+
public static final String REVOKE_SSO_CONFIGURATION_LINK = "/v1/mgmt/tenant/adminlinks/sso/revoke";
129131

130132
// SSO
131133
public static final String SSO_GET_SETTINGS_LINK = "/v1/mgmt/sso/settings";
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.descope.model.tenant.request;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import lombok.AllArgsConstructor;
5+
import lombok.Builder;
6+
import lombok.Data;
7+
import lombok.NoArgsConstructor;
8+
9+
@Data
10+
@Builder
11+
@NoArgsConstructor
12+
@AllArgsConstructor
13+
public class GenerateTenantLinkRequest {
14+
String tenantId;
15+
/** Expiration duration in seconds. */
16+
@JsonProperty("expireTime")
17+
long expireDuration;
18+
String ssoId;
19+
String email;
20+
String templateId;
21+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.descope.model.tenant.response;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Builder;
5+
import lombok.Data;
6+
import lombok.NoArgsConstructor;
7+
8+
@Data
9+
@Builder
10+
@NoArgsConstructor
11+
@AllArgsConstructor
12+
public class GenerateTenantLinkResponse {
13+
String adminSSOConfigurationLink;
14+
}

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.descope.exception.DescopeException;
44
import com.descope.model.tenant.Tenant;
55
import com.descope.model.tenant.TenantSettings;
6+
import com.descope.model.tenant.request.GenerateTenantLinkRequest;
67
import com.descope.model.tenant.request.TenantSearchRequest;
78
import java.util.List;
89
import java.util.Map;
@@ -146,4 +147,22 @@ void update(String id, String name, List<String> selfProvisioningDomains, Map<St
146147
* @throws DescopeException in case of errors
147148
*/
148149
void configureSettings(String id, TenantSettings settings) throws DescopeException;
150+
151+
/**
152+
* Generate a link that can be used to configure SSO for the tenant.
153+
*
154+
* @param request The request object containing all necessary parameters
155+
* @return A link that can be used to configure SSO for the tenant
156+
* @throws DescopeException in case of errors
157+
*/
158+
String generateSSOConfigurationLink(GenerateTenantLinkRequest request) throws DescopeException;
159+
160+
/**
161+
* Revoke an existing SSO configuration link for the tenant.
162+
*
163+
* @param id Tenant ID
164+
* @param ssoID The SSO ID for which the link should be revoked
165+
* @throws DescopeException in case of errors
166+
*/
167+
void revokeSSOConfigurationLink(String id, String ssoID) throws DescopeException;
149168
}

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

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22

33
import static com.descope.literals.Routes.ManagementEndPoints.CREATE_TENANT_LINK;
44
import static com.descope.literals.Routes.ManagementEndPoints.DELETE_TENANT_LINK;
5+
import static com.descope.literals.Routes.ManagementEndPoints.GENERATE_SSO_CONFIGURATION_LINK;
56
import static com.descope.literals.Routes.ManagementEndPoints.GET_TENANT_SETTINGS_LINK;
67
import static com.descope.literals.Routes.ManagementEndPoints.LOAD_ALL_TENANTS_LINK;
78
import static com.descope.literals.Routes.ManagementEndPoints.LOAD_TENANT_LINK;
9+
import static com.descope.literals.Routes.ManagementEndPoints.REVOKE_SSO_CONFIGURATION_LINK;
810
import static com.descope.literals.Routes.ManagementEndPoints.TENANT_SEARCH_ALL_LINK;
911
import static com.descope.literals.Routes.ManagementEndPoints.UPDATE_TENANT_LINK;
1012
import static com.descope.utils.CollectionUtils.addIfNotNull;
@@ -15,7 +17,9 @@
1517
import com.descope.model.client.Client;
1618
import com.descope.model.tenant.Tenant;
1719
import com.descope.model.tenant.TenantSettings;
20+
import com.descope.model.tenant.request.GenerateTenantLinkRequest;
1821
import com.descope.model.tenant.request.TenantSearchRequest;
22+
import com.descope.model.tenant.response.GenerateTenantLinkResponse;
1923
import com.descope.model.tenant.response.GetAllTenantsResponse;
2024
import com.descope.proxy.ApiProxy;
2125
import com.descope.sdk.mgmt.TenantService;
@@ -181,6 +185,42 @@ public void configureSettings(String id, TenantSettings settings) throws Descope
181185
apiProxy.post(configureSettingsUri(), req, Void.class);
182186
}
183187

188+
@Override
189+
public String generateSSOConfigurationLink(GenerateTenantLinkRequest request) throws DescopeException {
190+
if (request == null) {
191+
request = GenerateTenantLinkRequest.builder().build();
192+
}
193+
194+
if (StringUtils.isBlank(request.getTenantId())) {
195+
throw ServerCommonException.invalidArgument("tenantId");
196+
}
197+
198+
Map<String, Object> req = mapOf("tenantId", request.getTenantId());
199+
addIfNotNull(req, "expireTime", request.getExpireDuration());
200+
addIfNotNull(req, "ssoId", request.getSsoId());
201+
addIfNotNull(req, "email", request.getEmail());
202+
addIfNotNull(req, "templateId", request.getTemplateId());
203+
204+
URI generateSSOConfigurationLinkUri = generateSSOConfigurationLinkUri();
205+
ApiProxy apiProxy = getApiProxy();
206+
GenerateTenantLinkResponse response = apiProxy.post(
207+
generateSSOConfigurationLinkUri, req, GenerateTenantLinkResponse.class);
208+
return response.getAdminSSOConfigurationLink();
209+
}
210+
211+
@Override
212+
public void revokeSSOConfigurationLink(String tenantId, String ssoID) throws DescopeException {
213+
if (StringUtils.isBlank(tenantId)) {
214+
throw ServerCommonException.invalidArgument("tenantId");
215+
}
216+
217+
Map<String, Object> req = mapOf("tenantId", tenantId, "ssoId", ssoID);
218+
219+
URI revokeSSOConfigurationLinkUri = revokeSSOConfigurationLinkUri();
220+
ApiProxy apiProxy = getApiProxy();
221+
apiProxy.post(revokeSSOConfigurationLinkUri, req, Void.class);
222+
}
223+
184224
private URI composeCreateTenantUri() {
185225
return getUri(CREATE_TENANT_LINK);
186226
}
@@ -212,4 +252,12 @@ private URI getSettingsUri(String id) {
212252
private URI configureSettingsUri() {
213253
return getUri(GET_TENANT_SETTINGS_LINK);
214254
}
255+
256+
private URI generateSSOConfigurationLinkUri() {
257+
return getUri(GENERATE_SSO_CONFIGURATION_LINK);
258+
}
259+
260+
private URI revokeSSOConfigurationLinkUri() {
261+
return getUri(REVOKE_SSO_CONFIGURATION_LINK);
262+
}
215263
}

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

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,11 @@
11
package com.descope.sdk.mgmt.impl;
22

3-
import static com.descope.literals.Routes.ManagementEndPoints.MANAGEMENT_FGA_CHECK;
4-
import static com.descope.literals.Routes.ManagementEndPoints.MANAGEMENT_FGA_CREATE_RELATIONS;
5-
import static com.descope.literals.Routes.ManagementEndPoints.MANAGEMENT_FGA_DELETE_RELATIONS;
6-
import static com.descope.literals.Routes.ManagementEndPoints.MANAGEMENT_FGA_LOAD_SCHEMA;
7-
import static com.descope.literals.Routes.ManagementEndPoints.MANAGEMENT_FGA_RESOURCES_LOAD;
8-
import static com.descope.literals.Routes.ManagementEndPoints.MANAGEMENT_FGA_RESOURCES_SAVE;
9-
import static com.descope.literals.Routes.ManagementEndPoints.MANAGEMENT_FGA_SAVE_SCHEMA;
103
import static org.junit.jupiter.api.Assertions.assertEquals;
114
import static org.junit.jupiter.api.Assertions.assertNotNull;
125
import static org.junit.jupiter.api.Assertions.assertThrows;
136
import static org.mockito.ArgumentMatchers.any;
147
import static org.mockito.ArgumentMatchers.eq;
158
import static org.mockito.Mockito.lenient;
16-
import static org.mockito.Mockito.mock;
179
import static org.mockito.Mockito.verify;
1810
import static org.mockito.Mockito.when;
1911

@@ -30,7 +22,6 @@
3022
import com.descope.proxy.impl.ApiProxyBuilder;
3123
import com.descope.sdk.TestUtils;
3224
import com.descope.sdk.mgmt.FGAService;
33-
import com.descope.sdk.mgmt.impl.ManagementServiceBuilder;
3425
import com.fasterxml.jackson.core.type.TypeReference;
3526
import java.io.IOException;
3627
import java.nio.file.Files;
@@ -94,6 +85,7 @@ void testSaveSchema_EmptyDSL() {
9485
assertThrows(ServerCommonException.class, () -> fgaService.saveSchema(schema));
9586
}
9687

88+
@SuppressWarnings("unchecked")
9789
@Test
9890
void testLoadSchema_Success() throws Exception {
9991
Map<String, Object> response = new HashMap<>();
@@ -145,6 +137,7 @@ void testDeleteRelations_Success() throws Exception {
145137
}
146138
}
147139

140+
@SuppressWarnings("unchecked")
148141
@Test
149142
void testCheck_Success() throws Exception {
150143
List<FGARelation> relations = Arrays.asList(
@@ -169,6 +162,7 @@ void testCheck_Success() throws Exception {
169162
}
170163
}
171164

165+
@SuppressWarnings("unchecked")
172166
@Test
173167
void testLoadResourcesDetails_Success() throws Exception {
174168
List<FGAResourceIdentifier> identifiers = Arrays.asList(

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

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
import com.descope.model.client.Client;
1919
import com.descope.model.tenant.Tenant;
2020
import com.descope.model.tenant.TenantSettings;
21+
import com.descope.model.tenant.request.GenerateTenantLinkRequest;
2122
import com.descope.model.tenant.request.TenantSearchRequest;
23+
import com.descope.model.tenant.response.GenerateTenantLinkResponse;
2224
import com.descope.model.tenant.response.GetAllTenantsResponse;
2325
import com.descope.proxy.ApiProxy;
2426
import com.descope.proxy.impl.ApiProxyBuilder;
@@ -259,6 +261,16 @@ void testFunctionalFullCycle() {
259261
tenants = tenantService.searchAll(tenantSearchRequest);
260262
assertThat(tenants).isEmpty();
261263

264+
GenerateTenantLinkRequest generateTenantLinkRequest = GenerateTenantLinkRequest.builder()
265+
.tenantId(tenantId)
266+
.expireDuration(60 * 60 * 24)
267+
.build();
268+
269+
String link = tenantService.generateSSOConfigurationLink(generateTenantLinkRequest);
270+
assertThat(link).isNotBlank();
271+
272+
tenantService.revokeSSOConfigurationLink(tenantId, "");
273+
262274
tenantSettings.setJitDisabled(true);
263275
tenantSettings.setAuthType(TenantAuthType.OIDC);
264276
tenantService.configureSettings(tenantId, tenantSettings);
@@ -268,4 +280,102 @@ void testFunctionalFullCycle() {
268280
assertThat(tenantSettings.getAuthType()).isEqualTo(TenantAuthType.OIDC);
269281
tenantService.delete(tenantId);
270282
}
283+
284+
@Test
285+
void testGenerateSSOConfigurationLinkForEmptyParams() {
286+
ServerCommonException thrown = assertThrows(ServerCommonException.class, ()
287+
-> tenantService.generateSSOConfigurationLink(GenerateTenantLinkRequest.builder()
288+
.tenantId("")
289+
.expireDuration(0)
290+
.ssoId("")
291+
.email("")
292+
.templateId("")
293+
.build()));
294+
assertNotNull(thrown);
295+
assertEquals("The tenantId argument is invalid", thrown.getMessage());
296+
}
297+
298+
@Test
299+
void testGenerateSSOConfigurationLinkForSuccess() {
300+
GenerateTenantLinkResponse mockResponse = GenerateTenantLinkResponse.builder()
301+
.adminSSOConfigurationLink("some link")
302+
.build();
303+
304+
ApiProxy apiProxy = mock(ApiProxy.class);
305+
306+
doReturn(mockResponse).when(apiProxy).post(any(), any(), any());
307+
try (MockedStatic<ApiProxyBuilder> mockedApiProxyBuilder = mockStatic(ApiProxyBuilder.class)) {
308+
mockedApiProxyBuilder.when(
309+
() -> ApiProxyBuilder.buildProxy(any(), any())).thenReturn(apiProxy);
310+
String response = tenantService.generateSSOConfigurationLink(
311+
GenerateTenantLinkRequest.builder()
312+
.tenantId("tenant")
313+
.expireDuration(60 * 60 * 24)
314+
.ssoId("")
315+
.email("")
316+
.templateId("")
317+
.build());
318+
319+
assertThat(response).isEqualTo("some link");
320+
verify(apiProxy, times(1)).post(any(), any(), any());
321+
}
322+
}
323+
324+
@Test
325+
void testGenerateSSOConfigurationLinkWithSSOIdForSuccess() {
326+
GenerateTenantLinkResponse mockResponse = GenerateTenantLinkResponse.builder()
327+
.adminSSOConfigurationLink("some link")
328+
.build();
329+
330+
ApiProxy apiProxy = mock(ApiProxy.class);
331+
332+
doReturn(mockResponse).when(apiProxy).post(any(), any(), any());
333+
try (MockedStatic<ApiProxyBuilder> mockedApiProxyBuilder = mockStatic(ApiProxyBuilder.class)) {
334+
mockedApiProxyBuilder.when(
335+
() -> ApiProxyBuilder.buildProxy(any(), any())).thenReturn(apiProxy);
336+
String response = tenantService.generateSSOConfigurationLink(
337+
GenerateTenantLinkRequest.builder()
338+
.tenantId("tenant")
339+
.expireDuration(60 * 60 * 24)
340+
.ssoId("bla")
341+
.email("a@a.com")
342+
.templateId("template-id")
343+
.build());
344+
345+
assertThat(response).isEqualTo("some link");
346+
verify(apiProxy, times(1)).post(any(), any(), any());
347+
}
348+
}
349+
350+
@Test
351+
void testRevokeSSOConfigurationLinkForEmptyParams() {
352+
ServerCommonException thrown = assertThrows(ServerCommonException.class, () ->
353+
tenantService.revokeSSOConfigurationLink("", ""));
354+
assertNotNull(thrown);
355+
assertEquals("The tenantId argument is invalid", thrown.getMessage());
356+
}
357+
358+
@Test
359+
void testRevokeSSOConfigurationLinkForSuccess() {
360+
ApiProxy apiProxy = mock(ApiProxy.class);
361+
doReturn(void.class).when(apiProxy).post(any(), any(), any());
362+
try (MockedStatic<ApiProxyBuilder> mockedApiProxyBuilder = mockStatic(ApiProxyBuilder.class)) {
363+
mockedApiProxyBuilder.when(
364+
() -> ApiProxyBuilder.buildProxy(any(), any())).thenReturn(apiProxy);
365+
tenantService.revokeSSOConfigurationLink("tenant", "");
366+
verify(apiProxy, times(1)).post(any(), any(), any());
367+
}
368+
}
369+
370+
@Test
371+
void testRevokeSSOConfigurationLinkWithSSOIdForSuccess() {
372+
ApiProxy apiProxy = mock(ApiProxy.class);
373+
doReturn(void.class).when(apiProxy).post(any(), any(), any());
374+
try (MockedStatic<ApiProxyBuilder> mockedApiProxyBuilder = mockStatic(ApiProxyBuilder.class)) {
375+
mockedApiProxyBuilder.when(
376+
() -> ApiProxyBuilder.buildProxy(any(), any())).thenReturn(apiProxy);
377+
tenantService.revokeSSOConfigurationLink("tenant", "bla");
378+
verify(apiProxy, times(1)).post(any(), any(), any());
379+
}
380+
}
271381
}

0 commit comments

Comments
 (0)