package jnpf.dmsMapper; import jnpf.dmsDatabase.DmsUuidTypeHandler; import jnpf.dmsPermission.DmsUserFolderPermissionPrincipal; import jnpf.dmsPermission.DmsUserFolderPermissionTreeQuery; import org.apache.ibatis.builder.xml.XMLMapperBuilder; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.session.Configuration; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.io.IOException; import java.io.InputStream; import java.io.UncheckedIOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class DmsUserFolderPermissionTreeMapperSqlTest { private Configuration configuration; @BeforeEach void setUp() { configuration = new Configuration(); configuration.getTypeHandlerRegistry().register(UUID.class, DmsUuidTypeHandler.class); try (InputStream stream = getClass().getResourceAsStream( "/mapper/DmsUserFolderPermissionTreeMapper.xml")) { new XMLMapperBuilder(stream, configuration, "mapper/DmsUserFolderPermissionTreeMapper.xml", configuration.getSqlFragments()).parse(); } catch (IOException e) { throw new UncheckedIOException(e); } } @Test void resolvesActivePermissionsFromTheCurrentFolderAndItsAncestors() { String sql = sql(query()); assertTrue(sql.startsWith("WITH RECURSIVE input_principals")); assertTrue(sql.contains("jsonb_array_elements(CAST(? AS jsonb))")); assertTrue(sql.contains("p.resource_type = 'FOLDER'")); assertTrue(sql.contains("p.resource_scope IN ('SELF', 'SUBTREE')")); assertTrue(sql.contains("p.action_code IN ('READ', 'PRINT')")); assertTrue(sql.contains("p.revoked_at IS NULL")); assertTrue(sql.contains("p.valid_from <= CURRENT_TIMESTAMP")); assertTrue(sql.contains("CURRENT_TIMESTAMP < p.valid_to")); assertTrue(sql.contains("p.used_count < p.max_use_count")); assertTrue(sql.contains("FROM parent_ancestors child JOIN public.dms_folders parent")); assertTrue(sql.contains("parent.id = child.parent_id")); assertTrue(sql.contains("FROM navigation_ancestors child JOIN public.dms_folders parent")); assertTrue(sql.contains("permission.resource_scope = 'SUBTREE'")); assertTrue(sql.contains("permission.folder_id IN ( SELECT ancestor.id " + "FROM parent_ancestors ancestor )")); assertFalse(sql.contains("expanded_folder_permissions")); assertFalse(sql.contains("child.parent_id = parent.folder_id")); } @Test void matchesEverySupportedResolvedPrincipalKind() { String sql = sql(query()); assertTrue(sql.contains("p.principal_type = 'USER' AND ip.principal_kind = 'USER'")); assertTrue(sql.contains("p.principal_type = 'ORG' AND p.include_child_orgs = FALSE " + "AND ip.principal_kind = 'ORG_DIRECT'")); assertTrue(sql.contains("p.principal_type = 'ORG' AND p.include_child_orgs = TRUE " + "AND ip.principal_kind = 'ORG_ANCESTOR'")); assertTrue(sql.contains("p.principal_type = 'POSITION' " + "AND ip.principal_kind = 'POSITION'")); assertTrue(sql.contains("p.principal_type = 'ROLE' AND ip.principal_kind = 'ROLE'")); assertTrue(sql.contains("p.principal_type = 'GROUP' AND ip.principal_kind = 'GROUP'")); } @Test void bindsAllPrincipalsAsOneJsonPayload() { DmsUserFolderPermissionTreeQuery query = query(); BoundSql boundSql = boundSql(query); assertEquals(1, boundSql.getParameterMappings().stream() .filter(value -> "q.principalsJson".equals(value.getProperty())).count()); assertTrue(query.getPrincipalsJson().startsWith("[")); assertTrue(query.getPrincipalsJson().contains("\"userId\":\"user-1\"")); assertTrue(query.getPrincipalsJson().contains("\"principalKind\":\"ORG_DIRECT\"")); assertEquals(1, occurrences(normalize(boundSql.getSql()), "jsonb_array_elements(CAST(? AS jsonb))")); } @Test void largePrincipalCollectionStillUsesOneBindParameter() { DmsUserFolderPermissionTreeQuery query = query(); List principals = new ArrayList<>(); for (int index = 0; index < 25000; index++) { principals.add(principal("user-" + (index % 50), "GROUP", "group-" + index)); } query.setPrincipals(principals); BoundSql boundSql = boundSql(query); assertEquals(1, boundSql.getParameterMappings().stream() .filter(value -> "q.principalsJson".equals(value.getProperty())).count()); assertTrue(query.getPrincipalsJson().contains("\"principalId\":\"group-24999\"")); } @Test void pagesFoldersBeforeJoiningUserActionsAndUsesVisibleChildren() { DmsUserFolderPermissionTreeQuery query = query(); query.setParentId(UUID.fromString("018f0000-0000-7000-8000-000000000601")); query.setCursorSortCode(2048L); query.setCursorId(UUID.fromString("018f0000-0000-7000-8000-000000000602")); String sql = sql(query); assertTrue(sql.contains("folder.parent_id = ?")); assertTrue(sql.contains("folder.sort_code > ?")); assertTrue(sql.contains("folder.sort_code = ? AND folder.id > ?")); assertTrue(sql.contains("ORDER BY folder.sort_code, folder.id LIMIT ?")); assertTrue(sql.contains("FROM public.dms_folders child WHERE child.tenant_id = ? " + "AND child.parent_id = folder.id")); assertTrue(sql.contains("LEFT JOIN effective_page_permissions permission " + "ON permission.folder_id = folder.id")); assertTrue(sql.indexOf("LIMIT ?") < sql.indexOf("LEFT JOIN effective_page_permissions")); assertTrue(sql.endsWith("ORDER BY folder.sort_code, folder.id, " + "permission.user_id, permission.action_code")); } @Test void emptyPrincipalCollectionUsesAnEmptyJsonArray() { DmsUserFolderPermissionTreeQuery query = query(); query.setPrincipals(Collections.emptyList()); String sql = sql(query); assertEquals("[]", query.getPrincipalsJson()); assertTrue(sql.contains("jsonb_array_elements(CAST(? AS jsonb))")); assertFalse(sql.contains("VALUES")); } private DmsUserFolderPermissionTreeQuery query() { DmsUserFolderPermissionTreeQuery query = new DmsUserFolderPermissionTreeQuery(); query.setTenantId("tenant-1"); query.setLimit(21); query.setPrincipals(Arrays.asList( principal("user-1", "USER", "user-1"), principal("user-1", "ORG_DIRECT", "org-direct"), principal("user-1", "ORG_ANCESTOR", "org-parent"), principal("user-1", "POSITION", "position-1"), principal("user-2", "ROLE", "role-1"), principal("user-2", "GROUP", "group-1"))); return query; } private DmsUserFolderPermissionPrincipal principal(String userId, String kind, String id) { return new DmsUserFolderPermissionPrincipal(userId, kind, id); } private String sql(DmsUserFolderPermissionTreeQuery query) { return normalize(boundSql(query).getSql()); } private BoundSql boundSql(DmsUserFolderPermissionTreeQuery query) { MappedStatement statement = configuration.getMappedStatement( DmsUserFolderPermissionTreeMapper.class.getName() + ".selectTree"); Map parameters = new HashMap<>(); parameters.put("q", query); return statement.getBoundSql(parameters); } private String normalize(String sql) { return sql.replaceAll("\\s+", " ").trim(); } private int occurrences(String value, String token) { int result = 0; int offset = 0; while ((offset = value.indexOf(token, offset)) >= 0) { result++; offset += token.length(); } return result; } }