Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tenant level roles #97

Merged
merged 2 commits into from
Feb 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -987,6 +987,8 @@ String name = "My Role";
String description = "Optional description to briefly explain what this role allows.";
List<String> permissionNames = Arrays.asList("My Updated Permission");

// In case roles are on tenant scope, use the overloaded functions that has the tenantId parameter

try {
rs.create(name, description, permissionNames);
} catch (DescopeException de) {
Expand Down
1 change: 1 addition & 0 deletions src/main/java/com/descope/model/roles/Role.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
@AllArgsConstructor
public class Role {
private String name;
private String tenantId;
private String description;
private List<String> permissionNames;
private Long createdTime;
Expand Down
11 changes: 8 additions & 3 deletions src/main/java/com/descope/sdk/mgmt/RolesService.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,18 @@

public interface RolesService {

void create(String name, String description, List<String> permissionNames)
throws DescopeException;
void create(String name, String description, List<String> permissionNames) throws DescopeException;

void create(String name, String tenantId, String description, List<String> permissionNames) throws DescopeException;

void update(String name, String newName, String description, List<String> permissionNames)
void update(String name, String newName, String description, List<String> permissionNames) throws DescopeException;

void update(String name, String tenantId, String newName, String description, List<String> permissionNames)
throws DescopeException;

void delete(String name) throws DescopeException;

void delete(String name, String tenantId) throws DescopeException;

RoleResponse loadAll() throws DescopeException;
}
34 changes: 21 additions & 13 deletions src/main/java/com/descope/sdk/mgmt/impl/RolesServiceImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,17 @@ class RolesServiceImpl extends ManagementsBase implements RolesService {
}

@Override
public void create(String name, String description, List<String> permissionNames)
public void create(String name, String description, List<String> permissionNames) throws DescopeException {
this.create(name, "", description, permissionNames);
}

@Override
public void create(String name, String tenantId, String description, List<String> permissionNames)
throws DescopeException {
if (StringUtils.isBlank(name)) {
throw ServerCommonException.invalidArgument("Name");
}
Map<String, Object> request = mapOf("name", name, "description", description);
Map<String, Object> request = mapOf("name", name, "description", description, "tenantId", tenantId);
if (permissionNames != null) {
request.put("permissionNames", permissionNames);
}
Expand All @@ -39,32 +44,35 @@ public void create(String name, String description, List<String> permissionNames
@Override
public void update(String name, String newName, String description, List<String> permissionNames)
throws DescopeException {
this.update(name, "", newName, description, permissionNames);
}

@Override
public void update(String name, String tenantId, String newName, String description, List<String> permissionNames)
throws DescopeException {
if (StringUtils.isBlank(name)) {
throw ServerCommonException.invalidArgument("Name");
}
if (StringUtils.isBlank(newName)) {
throw ServerCommonException.invalidArgument("NewName");
}
Map<String, Object> request =
mapOf(
"name",
name,
"newName",
newName,
"description",
description,
"permissionNames",
permissionNames);
Map<String, Object> request = mapOf("name", name, "newName", newName, "description", description, "permissionNames",
permissionNames, "tenantId", tenantId);
ApiProxy apiProxy = getApiProxy();
apiProxy.post(getUri(MANAGEMENT_ROLES_UPDATE_LINK), request, Void.class);
}

@Override
public void delete(String name) throws DescopeException {
this.delete(name, "");
}

@Override
public void delete(String name, String tenantId) throws DescopeException {
if (StringUtils.isBlank(name)) {
throw ServerCommonException.invalidArgument("Name");
}
Map<String, String> request = mapOf("name", name);
Map<String, String> request = mapOf("name", name, "tenantId", tenantId);
ApiProxy apiProxy = getApiProxy();
apiProxy.post(getUri(MANAGEMENT_ROLES_DELETE_LINK), request, Void.class);
}
Expand Down
11 changes: 3 additions & 8 deletions src/main/java/com/descope/utils/UriUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,8 @@ public static Map<String, List<String>> splitQuery(URL url) {
return Collections.emptyMap();
}

return Arrays.stream(url.getQuery().split("&"))
.map(UriUtils::splitQueryParameter)
.collect(Collectors.groupingBy(
SimpleImmutableEntry::getKey,
LinkedHashMap::new,
return Arrays.stream(url.getQuery().split("&")).map(UriUtils::splitQueryParameter)
.collect(Collectors.groupingBy(SimpleImmutableEntry::getKey, LinkedHashMap::new,
Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
}

Expand All @@ -59,8 +56,6 @@ public static SimpleImmutableEntry<String, String> splitQueryParameter(String it
final int idx = it.indexOf("=");
final String key = idx > 0 ? it.substring(0, idx) : it;
final String value = idx > 0 && it.length() > idx + 1 ? it.substring(idx + 1) : null;
return new SimpleImmutableEntry<>(
URLDecoder.decode(key, "UTF-8"),
URLDecoder.decode(value, "UTF-8"));
return new SimpleImmutableEntry<>(URLDecoder.decode(key, "UTF-8"), URLDecoder.decode(value, "UTF-8"));
}
}
101 changes: 67 additions & 34 deletions src/test/java/com/descope/sdk/mgmt/impl/RolesServiceImplTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
Expand All @@ -23,6 +24,7 @@
import com.descope.sdk.TestUtils;
import com.descope.sdk.mgmt.PermissionService;
import com.descope.sdk.mgmt.RolesService;
import com.descope.sdk.mgmt.TenantService;
import java.util.Arrays;
import java.util.List;
import org.assertj.core.api.Assertions;
Expand All @@ -34,32 +36,26 @@
class RolesServiceImplTest {

private final List<String> mockPermissionNames = Arrays.asList("permission1", "permission2");
private final List<Role> mockRole =
Arrays.asList(
Role.builder()
.name("someName")
.permissionNames(mockPermissionNames)
.description("someDesc")
.createdTime(1245667L)
.build());
private final List<Role> mockRole = Arrays.asList(Role.builder().name("someName").permissionNames(mockPermissionNames)
.description("someDesc").createdTime(1245667L).build());
private final RoleResponse mockRoleResponse = new RoleResponse(mockRole);
private RolesService rolesService;
private PermissionService permissionService;
private TenantService tenantService;

@BeforeEach
void setUp() {
Client client = TestUtils.getClient();
ManagementServices mgmtServices = ManagementServiceBuilder.buildServices(client);
this.rolesService = mgmtServices.getRolesService();
this.permissionService = mgmtServices.getPermissionService();
this.tenantService = mgmtServices.getTenantService();
}

@Test
void testRolesForEmptyName() {
ServerCommonException thrown =
assertThrows(
ServerCommonException.class,
() -> rolesService.create("", "someDesc", mockPermissionNames));
ServerCommonException thrown = assertThrows(ServerCommonException.class,
() -> rolesService.create("", "someDesc", mockPermissionNames));
assertNotNull(thrown);
assertEquals("The Name argument is invalid", thrown.getMessage());
}
Expand All @@ -69,19 +65,16 @@ void testRolesCreateSuccess() {
ApiProxy apiProxy = mock(ApiProxy.class);
doReturn(Void.class).when(apiProxy).post(any(), any(), any());
try (MockedStatic<ApiProxyBuilder> mockedApiProxyBuilder = mockStatic(ApiProxyBuilder.class)) {
mockedApiProxyBuilder.when(
() -> ApiProxyBuilder.buildProxy(any(), any())).thenReturn(apiProxy);
mockedApiProxyBuilder.when(() -> ApiProxyBuilder.buildProxy(any(), any())).thenReturn(apiProxy);
rolesService.create("krishna", "", mockPermissionNames);
verify(apiProxy, times(1)).post(any(), any(), any());
}
}

@Test
void testUpdateForEmptyName() {
ServerCommonException thrown =
assertThrows(
ServerCommonException.class,
() -> rolesService.update("", "", "", mockPermissionNames));
ServerCommonException thrown = assertThrows(ServerCommonException.class,
() -> rolesService.update("", "", "", mockPermissionNames));
assertNotNull(thrown);
assertEquals("The Name argument is invalid", thrown.getMessage());
}
Expand All @@ -91,27 +84,23 @@ void testUpdateForSuccess() {
ApiProxy apiProxy = mock(ApiProxy.class);
doReturn(void.class).when(apiProxy).post(any(), any(), any());
try (MockedStatic<ApiProxyBuilder> mockedApiProxyBuilder = mockStatic(ApiProxyBuilder.class)) {
mockedApiProxyBuilder.when(
() -> ApiProxyBuilder.buildProxy(any(), any())).thenReturn(apiProxy);
mockedApiProxyBuilder.when(() -> ApiProxyBuilder.buildProxy(any(), any())).thenReturn(apiProxy);
rolesService.update("Test", "name", "10", mockPermissionNames);
verify(apiProxy, times(1)).post(any(), any(), any());
}
}

@Test
void testUpdateForEmptyNewName() {
ServerCommonException thrown =
assertThrows(
ServerCommonException.class,
() -> rolesService.update("krishna", "", "", mockPermissionNames));
ServerCommonException thrown = assertThrows(ServerCommonException.class,
() -> rolesService.update("krishna", "", "", mockPermissionNames));
assertNotNull(thrown);
assertEquals("The NewName argument is invalid", thrown.getMessage());
}

@Test
void testDeleteForEmptyName() {
ServerCommonException thrown =
assertThrows(ServerCommonException.class, () -> rolesService.delete(""));
ServerCommonException thrown = assertThrows(ServerCommonException.class, () -> rolesService.delete(""));
assertNotNull(thrown);
assertEquals("The Name argument is invalid", thrown.getMessage());
}
Expand All @@ -121,8 +110,7 @@ void testDeleteForSuccess() {
ApiProxy apiProxy = mock(ApiProxy.class);
doReturn(Void.class).when(apiProxy).post(any(), any(), any());
try (MockedStatic<ApiProxyBuilder> mockedApiProxyBuilder = mockStatic(ApiProxyBuilder.class)) {
mockedApiProxyBuilder.when(
() -> ApiProxyBuilder.buildProxy(any(), any())).thenReturn(apiProxy);
mockedApiProxyBuilder.when(() -> ApiProxyBuilder.buildProxy(any(), any())).thenReturn(apiProxy);
rolesService.delete("someName");
verify(apiProxy, times(1)).post(any(), any(), any());
}
Expand All @@ -133,17 +121,14 @@ void testLoadAllForSuccess() {
ApiProxy apiProxy = mock(ApiProxy.class);
doReturn(mockRoleResponse).when(apiProxy).get(any(), any());
try (MockedStatic<ApiProxyBuilder> mockedApiProxyBuilder = mockStatic(ApiProxyBuilder.class)) {
mockedApiProxyBuilder.when(
() -> ApiProxyBuilder.buildProxy(any(), any())).thenReturn(apiProxy);
mockedApiProxyBuilder.when(() -> ApiProxyBuilder.buildProxy(any(), any())).thenReturn(apiProxy);
RoleResponse response = rolesService.loadAll();
Assertions.assertThat(response.getRoles().size()).isEqualTo(1);
Assertions.assertThat(response.getRoles().get(0).getName()).isEqualTo("someName");
Assertions.assertThat(response.getRoles().get(0).getDescription()).isEqualTo("someDesc");
Assertions.assertThat(response.getRoles().get(0).getPermissionNames().size()).isEqualTo(2);
Assertions.assertThat(response.getRoles().get(0).getPermissionNames().get(0))
.isEqualTo("permission1");
Assertions.assertThat(response.getRoles().get(0).getPermissionNames().get(1))
.isEqualTo("permission2");
Assertions.assertThat(response.getRoles().get(0).getPermissionNames().get(0)).isEqualTo("permission1");
Assertions.assertThat(response.getRoles().get(0).getPermissionNames().get(1)).isEqualTo("permission2");
}
}

Expand Down Expand Up @@ -182,4 +167,52 @@ void testFunctionalFullCycle() {
permissionService.delete(p2);
rolesService.delete(r1 + "1");
}

@RetryingTest(value = 3, suspendForMs = 30000, onExceptions = RateLimitExceededException.class)
void testFunctionalFullCycleWithTenantId() {
String p1 = TestUtils.getRandomName("pt-").substring(0, 20);
String p2 = TestUtils.getRandomName("pt-").substring(0, 20);
String r1 = TestUtils.getRandomName("rt-").substring(0, 20);
permissionService.create(p1, "p1");
permissionService.create(p2, "p2");
String tid = tenantService.create(TestUtils.getRandomName("tnfr-").substring(0, 20), null);
rolesService.create(r1, tid, "ttt", Arrays.asList(p1, p2));
RoleResponse roles = rolesService.loadAll();
assertThat(roles.getRoles()).isNotEmpty();
boolean found = false;
for (Role r : roles.getRoles()) {
if (r.getName().equals(r1)) {
found = true;
assertEquals("ttt", r.getDescription());
assertEquals(tid, r.getTenantId());
assertThat(r.getPermissionNames()).contains(p1, p2);
}
}
assertTrue(found);
rolesService.update(r1, tid, r1 + "1", "zzz", Arrays.asList(p1));
roles = rolesService.loadAll();
assertThat(roles.getRoles()).isNotEmpty();
found = false;
for (Role r : roles.getRoles()) {
if (r.getName().equals(r1 + "1")) {
found = true;
assertEquals("zzz", r.getDescription());
assertEquals(tid, r.getTenantId());
assertThat(r.getPermissionNames()).containsExactly(p1);
}
}
assertTrue(found);
permissionService.delete(p1);
permissionService.delete(p2);
rolesService.delete(r1 + "1", tid);
found = false;
roles = rolesService.loadAll();
for (Role r : roles.getRoles()) {
if (r.getName().equals(r1 + "1") && r.getTenantId().equals(tid)) {
found = true;
}
}
assertFalse(found);
tenantService.delete(tid);
}
}
Loading