001/* 002 * Copyright 2013 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.plugins.queriesdirectory; 017 018import java.util.Optional; 019import java.util.Set; 020 021import javax.jcr.RepositoryException; 022 023import org.apache.jackrabbit.util.ISO9075; 024 025import org.ametys.core.group.GroupIdentity; 026import org.ametys.core.user.UserIdentity; 027import org.ametys.plugins.repository.AmetysRepositoryException; 028 029/** 030 * Helper for manipulating {@link Query} 031 * 032 */ 033public final class QueryHelper 034{ 035 private QueryHelper() 036 { 037 // Private 038 } 039 040 /** 041 * Creates the XPath query to get all query containers 042 * @param queryContainer The {@link QueryContainer}, defining the context from which getting children 043 * @return The XPath query 044 */ 045 static String getXPathForQueryContainers(QueryContainer queryContainer) 046 { 047 return _getXPathQuery(queryContainer, true, ObjectToReturn.QUERY_CONTAINER, Optional.empty(), Optional.empty(), null); 048 } 049 050 /** 051 * Creates the XPath query to get all queries for administrator 052 * @param queryContainer The {@link QueryContainer}, defining the context from which getting children 053 * @param onlyDirectChildren <code>true</code> in order to have only direct child queries from parent path, <code>false</code> otherwise to have all queries at any level underneath the parent path 054 * @param type The query type 055 * @return The XPath query 056 */ 057 static String getXPathForQueriesForAdministrator(QueryContainer queryContainer, boolean onlyDirectChildren, Optional<String> type) 058 { 059 return _getXPathQuery(queryContainer, onlyDirectChildren, ObjectToReturn.QUERY, Optional.empty(), type, null); 060 } 061 062 /** 063 * Creates the XPath query to get all queries in READ access 064 * @param queryContainer The {@link QueryContainer}, defining the context from which getting children 065 * @param onlyDirectChildren <code>true</code> in order to have only direct child queries from parent path, <code>false</code> otherwise to have all queries at any level underneath the parent path 066 * @param visibility The user and its groups for checking visibility 067 * @param type The query type 068 * @return The XPath query 069 */ 070 static String getXPathForQueriesInReadAccess(QueryContainer queryContainer, boolean onlyDirectChildren, Visibility visibility, Optional<String> type) 071 { 072 return _getXPathQuery(queryContainer, onlyDirectChildren, ObjectToReturn.QUERY, Optional.of(visibility), type, true); 073 } 074 075 /** 076 * Creates the XPath query to get all queries in WRITE access 077 * @param queryContainer The {@link QueryContainer}, defining the context from which getting children 078 * @param onlyDirectChildren <code>true</code> in order to have only direct child queries from parent path, <code>false</code> otherwise to have all queries at any level underneath the parent path 079 * @param visibility The user and its groups for checking visibility 080 * @param type The query type 081 * @return The XPath query 082 */ 083 static String getXPathForQueriesInWriteAccess(QueryContainer queryContainer, boolean onlyDirectChildren, Visibility visibility, Optional<String> type) 084 { 085 return _getXPathQuery(queryContainer, onlyDirectChildren, ObjectToReturn.QUERY, Optional.of(visibility), type, false); 086 } 087 088 private static StringBuilder _getParentPath(QueryContainer queryContainer) 089 { 090 try 091 { 092 StringBuilder parentPath = new StringBuilder("/jcr:root") 093 .append(ISO9075.encodePath(queryContainer.getNode().getPath())); 094 return parentPath; 095 } 096 catch (RepositoryException e) 097 { 098 throw new AmetysRepositoryException(e); 099 } 100 } 101 102 private static String _getXPathQuery(QueryContainer queryContainer, boolean onlyDirectChildren, ObjectToReturn objectToReturn, Optional<Visibility> visibility, Optional<String> type, Boolean readAccess) 103 { 104 StringBuilder parentPath = _getParentPath(queryContainer); 105 106 final String slashOrDoubleSlash = onlyDirectChildren ? "/" : "//"; 107 StringBuilder query = parentPath 108 .append(slashOrDoubleSlash) 109 .append("element(*, ") 110 .append(objectToReturn.toNodetype()) 111 .append(")"); 112 113 if (visibility.isPresent() || type.isPresent()) 114 { 115 query.append('['); 116 } 117 118 type.ifPresent(t -> query.append("@").append(Query.TYPE).append("='").append(t).append("'")); 119 120 if (visibility.isPresent()) 121 { 122 type.ifPresent(t -> query.append(" and (")); 123 124 _appendPublicPredicate(query); 125 126 UserIdentity user = visibility.get().getUser(); 127 String login = user.getLogin(); 128 String populationId = user.getPopulationId(); 129 _appendPrivatePredicate(query, login, populationId); 130 131 Set<GroupIdentity> groups = visibility.get().getGroups(); 132 _appendSharedPredicate(query, login, populationId, groups, readAccess); 133 134 type.ifPresent(t -> query.append(')')); 135 } 136 137 138 if (visibility.isPresent() || type.isPresent()) 139 { 140 query.append(']'); 141 } 142 143 return query.toString(); 144 } 145 146 private static void _appendPublicPredicate(StringBuilder query) 147 { 148 query.append("(@").append(Query.VISIBILITY).append("='").append(Query.Visibility.PUBLIC.name()) 149 .append("') or "); 150 } 151 152 private static void _appendPrivatePredicate(StringBuilder query, String login, String populationId) 153 { 154 query.append("(@").append(Query.VISIBILITY).append("='").append(Query.Visibility.PRIVATE.name()) 155 .append("' and "); 156 _appendUserCondition(query, login, populationId); 157 query.append(") or "); 158 } 159 160 private static void _appendSharedPredicate(StringBuilder query, String login, String populationId, Set<GroupIdentity> groups, boolean readAccess) 161 { 162 query.append("(@").append(Query.VISIBILITY).append("='").append(Query.Visibility.SHARED.name()) 163 .append("' and ("); 164 query.append("("); 165 _appendUserCondition(query, login, populationId); 166 query.append(") or "); 167 168 if (readAccess) 169 { 170 _appendUserAccessCondition(query, login, populationId, Query.PROFILE_READ_ACCESS); 171 query.append(" or "); 172 } 173 _appendUserAccessCondition(query, login, populationId, Query.PROFILE_WRITE_ACCESS); 174 175 _appendGroupsCondition(query, groups, readAccess); 176 query.append("))"); 177 } 178 179 private static void _appendUserCondition(StringBuilder query, String login, String populationId) 180 { 181 query.append('@').append(Query.AUTHOR).append("/ametys:login='").append(login) 182 .append("' and @").append(Query.AUTHOR).append("/ametys:population='").append(populationId) 183 .append('\''); 184 } 185 186 private static void _appendGroupsCondition(StringBuilder query, Set<GroupIdentity> groups, boolean readAccess) 187 { 188 for (GroupIdentity group : groups) 189 { 190 String groupId = group.getId(); 191 String groupDirectory = group.getDirectoryId(); 192 193 if (readAccess) 194 { 195 query.append(" or "); 196 _appendGroupAccessCondition(query, groupId, groupDirectory, Query.PROFILE_READ_ACCESS); 197 } 198 query.append(" or "); 199 _appendGroupAccessCondition(query, groupId, groupDirectory, Query.PROFILE_WRITE_ACCESS); 200 } 201 } 202 203 private static void _appendUserAccessCondition(StringBuilder query, String login, String populationId, String accessNodeName) 204 { 205 query.append("(").append(accessNodeName).append("/").append(Query.USERS).append("/ametys:login='").append(login) 206 .append("' and ").append(accessNodeName).append("/").append(Query.USERS).append("/ametys:population='").append(populationId) 207 .append("')"); 208 } 209 210 private static void _appendGroupAccessCondition(StringBuilder query, String groupId, String groupDirectory, String accessNodeName) 211 { 212 query.append("(").append(accessNodeName).append("/").append(Query.GROUPS).append("/ametys:groupId='").append(groupId) 213 .append("' and ").append(accessNodeName).append("/").append(Query.GROUPS).append("/ametys:groupDirectory='").append(groupDirectory) 214 .append("')"); 215 } 216 217 /** 218 * The user and its groups for checking visibility 219 */ 220 public static final class Visibility 221 { 222 private UserIdentity _user; 223 private Set<GroupIdentity> _groups; 224 225 private Visibility(UserIdentity user, Set<GroupIdentity> groups) 226 { 227 _user = user; 228 _groups = groups; 229 } 230 231 /** 232 * Creates a new {@link Visibility} 233 * @param user The user 234 * @param groups The user groups 235 * @return the {@link Visibility} wrapper 236 */ 237 public static Visibility of(UserIdentity user, Set<GroupIdentity> groups) 238 { 239 return new Visibility(user, groups); 240 } 241 242 UserIdentity getUser() 243 { 244 return _user; 245 } 246 247 Set<GroupIdentity> getGroups() 248 { 249 return _groups; 250 } 251 } 252 253 private static enum ObjectToReturn 254 { 255 QUERY, 256 QUERY_CONTAINER; 257 258 private String _nodetype; 259 260 static 261 { 262 QUERY._nodetype = QueryFactory.QUERY_NODETYPE; 263 QUERY_CONTAINER._nodetype = QueryContainerFactory.QUERY_CONTAINER_NODETYPE; 264 } 265 266 String toNodetype() 267 { 268 return _nodetype; 269 } 270 } 271}