/** * Get the number of readable resources fulfilling the provided detailed query. The detailed query * is a part of the overall query which allows to filter by single columns. * * @param pMetaDataManager The MetaDataManager used to query for the resources. * @param pDetailedQuery The detailed query, e.g. <i>name LIKE %John D%</i> * @param pContext The context used to authorize the access. Attention: User context needed. * System context is not applicable here! * @return The number of readable resources. * @throws UnauthorizedAccessAttemptException If the context is not authorized to access the * method or any resource. */ @SecuredMethod(roleRequired = Role.GUEST) public final int getReadableResourceCount( IMetaDataManager pMetaDataManager, String pDetailedQuery, @Context IAuthorizationContext pContext) throws UnauthorizedAccessAttemptException { StringBuilder query = new StringBuilder(); String domain = SecurableEntityHelper.getSecurableResourceDomain(clazz); String uniqueField = SecurableEntityHelper.getDomainUniqueFieldName(clazz); query.append("SELECT COUNT(f) FROM FilterHelper f,").append(tableName).append(" o WHERE "); query.append("f.userId='").append(pContext.getUserId().getStringRepresentation()); query.append("' AND "); // add group information only if not admin context is used (role ADMINISTRATOR or group // SYS_ADMIN) if (!AuthorizationUtil.isAdminContext(pContext)) { query .append("f.groupId='") .append(pContext.getGroupId().getStringRepresentation()) .append("' AND "); } query .append("f.domainId='") .append(domain) .append("' AND f.roleAllowed>=") .append(Role.GUEST.ordinal()) .append(" AND f.domainUniqueId=o.") .append(uniqueField); if (pDetailedQuery != null) { query.append(" AND ").append(pDetailedQuery); } logger.debug("Executing query for readable resources: {}", query); return ((Number) pMetaDataManager.findSingleResult(query.toString())).intValue(); }
/** * Get a single readable resources with a known unique id fulfilling also the provided detailed * query. The detailed query is a part of the overall query which allows to filter by single * columns. This method is intended to be used for a fast query to check accessability for a known * resource. * * @param pMetaDataManager The MetaDataManager used to query for the resources. * @param pUniqueId The unique id of the resource. * @param pDetailedQuery The detailed query, e.g. <i>name LIKE %John D%</i> * @param pContext The context used to authorize the access. Attention: User context needed. * System context is not applicable here! * @return A list of readable resources. * @throws UnauthorizedAccessAttemptException If the context is not authorized to access the * method or any resource. */ @SecuredMethod(roleRequired = Role.GUEST) public final C getReadableResource( IMetaDataManager pMetaDataManager, String pDetailedQuery, String pUniqueId, @Context IAuthorizationContext pContext) throws UnauthorizedAccessAttemptException { StringBuilder query = new StringBuilder(); String domain = SecurableEntityHelper.getSecurableResourceDomain(clazz); String uniqueField = SecurableEntityHelper.getDomainUniqueFieldName(clazz); query.append("SELECT f.domainUniqueId FROM FilterHelper f WHERE "); query.append("f.userId='").append(pContext.getUserId().getStringRepresentation()); query.append("' AND "); // add group information only if not admin context is used (role ADMINISTRATOR or group // SYS_ADMIN) if (!AuthorizationUtil.isAdminContext(pContext)) { query .append("f.groupId='") .append(pContext.getGroupId().getStringRepresentation()) .append("' AND "); } query .append("f.domainId='") .append(domain) .append("' AND ") .append("f.domainUniqueId='") .append(pUniqueId) .append("' AND f.roleAllowed>=") .append(Role.GUEST.ordinal()); Object fetchGraph = pMetaDataManager.getProperties().get(MetaDataManagerJpa.JAVAX_PERSISTENCE_FETCHGRAPH); pMetaDataManager.removeProperty(MetaDataManagerJpa.JAVAX_PERSISTENCE_FETCHGRAPH); logger.debug("Obtaining list of readable unique ids."); List<String> uniqueIds = pMetaDataManager.findResultList(query.toString(), String.class); if (fetchGraph != null) { logger.debug("Re-adding previously assigned fetch graph property."); pMetaDataManager.addProperty(MetaDataManagerJpa.JAVAX_PERSISTENCE_FETCHGRAPH, fetchGraph); } String uniqueId; if (uniqueIds.isEmpty()) { logger.warn("No domain unique ids found. Returning null."); return null; } if (uniqueIds.size() > 1) { logger.warn("Multiple domain unique ids found. Using first element {}", uniqueIds.get(0)); } uniqueId = uniqueIds.get(0); query = new StringBuilder(); query.append("SELECT o FROM ").append(tableName).append(" o WHERE "); query.append("o.").append(uniqueField).append("=?1"); if (pDetailedQuery != null) { query.append(" AND ").append(pDetailedQuery); } C resutl = pMetaDataManager.findSingleResult(query.toString(), new Object[] {uniqueId}, clazz); return resutl; /*StringBuilder query = new StringBuilder(); String domain = SecurableEntityHelper.getSecurableResourceDomain(clazz); String uniqueField = SecurableEntityHelper.getDomainUniqueFieldName(clazz); query.append("SELECT o FROM FilterHelper f, "). append(tableName).append(" o WHERE "); query.append("f.userId='").append(pContext.getUserId().getStringRepresentation()); query.append("' AND "); query.append("f.groupId='").append(pContext.getGroupId().getStringRepresentation()).append("' AND ") .append("f.domainId='").append(domain). append("' AND f.roleAllowed>=").append(Role.GUEST.ordinal()). append(" AND f.domainUniqueId=o.").append(uniqueField); if (pDetailedQuery != null) { query.append(" AND ").append(pDetailedQuery); } logger.debug("Executing query for accessible resources: {}", query); return pMetaDataManager.findResultList(query.toString(), clazz, pFirstResult, pMaxResults);*/ }
/** * Get a list of readable resources fulfilling the provided detailed query. The detailed query is * a part of the overall query which allows to filter by single columns. The internal workflow * covers two queries: In a first query all resource ids of the appropriate resource type are * obtained. In a second query this list is used to query for the according resources. If the * number of requested results is larger than 1.000 or if a pDetailedQuery is not 'null', the * resource ids are slit into blocks of 1.000 elements and are used in subsequent queries as long * as the requested number of results is not reached. This is done for memory reasons. The result * list will start at index pFirstResult and will contain max. pMaxResults elements. * * <p>For databases holding a huge number of resources (40K+) or requested results (>100) this * methods could have a poor performance. For fast checks of single resources, {@link * #getReadableResources(edu.kit.dama.mdm.core.IMetaDataManager, int, int, * edu.kit.dama.authorization.entities.IAuthorizationContext)} should be used. * * @param pMetaDataManager The MetaDataManager used to query for the resources. * @param pFirstResult The index of the first result. * @param pMaxResults The max. number of returned results. * @param pDetailedQuery The detailed query. The object queried for is adressed by 'o', e.g. * <i>o.name LIKE %John D%</i> * @param pContext The context used to authorize the access. Attention: User context needed. * System context is not applicable here! * @return A list of readable resources. * @throws UnauthorizedAccessAttemptException If the context is not authorized to access the * method or any resource. */ @SecuredMethod(roleRequired = Role.GUEST) public final List<C> getReadableResources( IMetaDataManager pMetaDataManager, String pDetailedQuery, int pFirstResult, int pMaxResults, @Context IAuthorizationContext pContext) throws UnauthorizedAccessAttemptException { StringBuilder query = new StringBuilder(); String domain = SecurableEntityHelper.getSecurableResourceDomain(clazz); String uniqueField = SecurableEntityHelper.getDomainUniqueFieldName(clazz); logger.debug("Querying for domain unique ids of readable resources."); query.append("SELECT f.domainUniqueId FROM FilterHelper f WHERE "); query.append("f.userId='").append(pContext.getUserId().getStringRepresentation()); query.append("' AND "); // add group information only if not admin context is used (role ADMINISTRATOR or group // SYS_ADMIN) if (!AuthorizationUtil.isAdminContext(pContext)) { query .append("f.groupId='") .append(pContext.getGroupId().getStringRepresentation()) .append("' AND "); } query .append("f.domainId='") .append(domain) .append("' AND f.roleAllowed>=") .append(Role.GUEST.ordinal()); logger.debug("Removing fetch graph property for FilterHelper query."); Object fetchGraph = pMetaDataManager.getProperties().get(MetaDataManagerJpa.JAVAX_PERSISTENCE_FETCHGRAPH); pMetaDataManager.removeProperty(MetaDataManagerJpa.JAVAX_PERSISTENCE_FETCHGRAPH); logger.debug("Obtaining list of readable unique ids."); // At this point ALL possible resource ids are fetched, which kills performance for large // repository instances, but is also necessary. // Reasons are: as later on the detailed query may filter out resources that are accessible in // principle. // In that case, additional resource ids would have to be loaded in order to be able to achieve // pMaxResults. The second reason is, // that single resources might be invalid, thus 100 resource ids might be retrieved but only 99 // entities are returned which might confuse the user. List<String> uniqueIds = pMetaDataManager.findResultList(query.toString(), String.class); if (fetchGraph != null) { logger.debug("Re-adding previously assigned fetch graph property."); pMetaDataManager.addProperty(MetaDataManagerJpa.JAVAX_PERSISTENCE_FETCHGRAPH, fetchGraph); } if (uniqueIds.isEmpty()) { logger.warn("No domain unique ids found. Returning empty resources list."); return new ArrayList<>(); } logger.info("{} domain unique id(s) found. Querying for resources.", uniqueIds.size()); List<C> results = new ArrayList(); // split query to subqueries int elementCount = uniqueIds.size(); int startIndex = 0; // Read results in blocks...the max. block size is 1000 elements per query. // If the number of max. results is smaller than 1000 and no detailed query is provided, // the max. number of results is used as block size as this should already be the exact number // of required results. If a detailed query is provided, there is a probability of filtered // entries. // Therefor, the max. block size will be used to reduce the number of single database queries. // However, due to the check for the current number of results this will be the only query // if the first query returns 1000 results, pFirstResult is 0 and pMaxResults is LEQ 1000. int stepSize = (pMaxResults < 1000 && pDetailedQuery == null) ? pMaxResults : 1000; while (elementCount > 0) { List<String> subList = uniqueIds.subList(startIndex, Math.min(uniqueIds.size(), startIndex + stepSize)); query = new StringBuilder(); query.append("SELECT o FROM ").append(tableName).append(" o WHERE "); query.append("o.").append(uniqueField).append(" IN ?1"); if (pDetailedQuery != null) { query.append(" AND ").append(pDetailedQuery); } List<C> subResults = pMetaDataManager.findResultList(query.toString(), new Object[] {subList}, clazz); results.addAll(subResults); if (results.size() >= pFirstResult + pMaxResults) { break; } elementCount -= subList.size(); startIndex += subList.size(); } return results.subList(pFirstResult, Math.min(results.size(), pFirstResult + pMaxResults)); /*StringBuilder query = new StringBuilder(); String domain = SecurableEntityHelper.getSecurableResourceDomain(clazz); String uniqueField = SecurableEntityHelper.getDomainUniqueFieldName(clazz); query.append("SELECT o FROM FilterHelper f, "). append(tableName).append(" o WHERE "); query.append("f.userId='").append(pContext.getUserId().getStringRepresentation()); query.append("' AND "); query.append("f.groupId='").append(pContext.getGroupId().getStringRepresentation()).append("' AND ") .append("f.domainId='").append(domain). append("' AND f.roleAllowed>=").append(Role.GUEST.ordinal()). append(" AND f.domainUniqueId=o.").append(uniqueField); if (pDetailedQuery != null) { query.append(" AND ").append(pDetailedQuery); } logger.debug("Executing query for accessible resources: {}", query); return pMetaDataManager.findResultList(query.toString(), clazz, pFirstResult, pMaxResults);*/ }