001/* 002 * Copyright 2016 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.ArrayList; 019import java.util.Date; 020import java.util.HashMap; 021import java.util.HashSet; 022import java.util.LinkedList; 023import java.util.List; 024import java.util.Map; 025import java.util.Optional; 026import java.util.Set; 027import java.util.function.Function; 028import java.util.stream.Collectors; 029 030import javax.jcr.Node; 031import javax.jcr.RepositoryException; 032 033import org.apache.avalon.framework.component.Component; 034import org.apache.avalon.framework.service.ServiceException; 035import org.apache.avalon.framework.service.ServiceManager; 036import org.apache.avalon.framework.service.Serviceable; 037import org.apache.commons.lang3.StringUtils; 038import org.apache.jackrabbit.util.Text; 039 040import org.ametys.cms.FilterNameHelper; 041import org.ametys.core.group.GroupIdentity; 042import org.ametys.core.group.GroupManager; 043import org.ametys.core.right.RightManager; 044import org.ametys.core.right.RightManager.RightResult; 045import org.ametys.core.ui.Callable; 046import org.ametys.core.user.CurrentUserProvider; 047import org.ametys.core.user.UserIdentity; 048import org.ametys.core.util.DateUtils; 049import org.ametys.core.util.LambdaUtils; 050import org.ametys.plugins.core.user.UserHelper; 051import org.ametys.plugins.queriesdirectory.Query.QueryProfile; 052import org.ametys.plugins.queriesdirectory.Query.Visibility; 053import org.ametys.plugins.repository.AmetysObject; 054import org.ametys.plugins.repository.AmetysObjectIterable; 055import org.ametys.plugins.repository.AmetysObjectResolver; 056import org.ametys.plugins.repository.AmetysRepositoryException; 057import org.ametys.plugins.repository.ModifiableTraversableAmetysObject; 058import org.ametys.plugins.repository.MovableAmetysObject; 059import org.ametys.plugins.repository.UnknownAmetysObjectException; 060import org.ametys.runtime.plugin.component.AbstractLogEnabled; 061 062import com.google.common.base.Predicates; 063import com.google.common.collect.ImmutableMap; 064 065/** 066 * DAO for manipulating queries 067 */ 068public class QueryDAO extends AbstractLogEnabled implements Serviceable, Component 069{ 070 /** The Avalon role */ 071 public static final String ROLE = QueryDAO.class.getName(); 072 073 /** The alias id of the root {@link QueryContainer} */ 074 public static final String ROOT_QUERY_CONTAINER_ID = "root"; 075 076 private static final String __PLUGIN_NODE_NAME = "queriesdirectory"; 077 078 /** The current user provider */ 079 protected CurrentUserProvider _userProvider; 080 /** The group manager */ 081 protected GroupManager _groupManager; 082 083 /** The Ametys object resolver */ 084 private AmetysObjectResolver _resolver; 085 086 private UserHelper _userHelper; 087 088 private RightManager _rightManager; 089 090 @Override 091 public void service(ServiceManager serviceManager) throws ServiceException 092 { 093 _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE); 094 _userProvider = (CurrentUserProvider) serviceManager.lookup(CurrentUserProvider.ROLE); 095 _groupManager = (GroupManager) serviceManager.lookup(GroupManager.ROLE); 096 _userHelper = (UserHelper) serviceManager.lookup(UserHelper.ROLE); 097 _rightManager = (RightManager) serviceManager.lookup(RightManager.ROLE); 098 } 099 100 /** 101 * Get the root plugin storage object. 102 * @return the root plugin storage object. 103 * @throws AmetysRepositoryException if a repository error occurs. 104 */ 105 public QueryContainer getQueriesRootNode() throws AmetysRepositoryException 106 { 107 try 108 { 109 return _getOrCreateRootNode(); 110 } 111 catch (AmetysRepositoryException e) 112 { 113 throw new AmetysRepositoryException("Unable to get the queries root node", e); 114 } 115 } 116 117 private QueryContainer _getOrCreateRootNode() throws AmetysRepositoryException 118 { 119 ModifiableTraversableAmetysObject pluginsNode = _resolver.resolveByPath("/ametys:plugins"); 120 121 ModifiableTraversableAmetysObject pluginNode = (ModifiableTraversableAmetysObject) _getOrCreateNode(pluginsNode, __PLUGIN_NODE_NAME, "ametys:unstructured"); 122 123 return (QueryContainer) _getOrCreateNode(pluginNode, "ametys:queries", QueryContainerFactory.QUERY_CONTAINER_NODETYPE); 124 } 125 126 private static AmetysObject _getOrCreateNode(ModifiableTraversableAmetysObject parentNode, String nodeName, String nodeType) throws AmetysRepositoryException 127 { 128 AmetysObject definitionsNode; 129 if (parentNode.hasChild(nodeName)) 130 { 131 definitionsNode = parentNode.getChild(nodeName); 132 } 133 else 134 { 135 definitionsNode = parentNode.createChild(nodeName, nodeType); 136 parentNode.saveChanges(); 137 } 138 return definitionsNode; 139 } 140 141 /** 142 * Get queries' properties 143 * @param queryIds The ids of queries to retrieve 144 * @return The queries' properties 145 */ 146 @Callable 147 public Map<String, Object> getQueriesProperties(List<String> queryIds) 148 { 149 Map<String, Object> result = new HashMap<>(); 150 151 List<Map<String, Object>> queries = new LinkedList<>(); 152 List<Map<String, Object>> notAllowedQueries = new LinkedList<>(); 153 Set<String> unknownQueries = new HashSet<>(); 154 155 156 for (String id : queryIds) 157 { 158 try 159 { 160 Query query = _resolver.resolveById(id); 161 162 if (_hasRight(query)) 163 { 164 queries.add(getQueryProperties(query)); 165 } 166 else 167 { 168 notAllowedQueries.add(getQueryProperties(query)); 169 } 170 } 171 catch (UnknownAmetysObjectException e) 172 { 173 unknownQueries.add(id); 174 } 175 } 176 177 result.put("queries", queries); 178 result.put("unknownQueries", unknownQueries); 179 result.put("unknownQueries", unknownQueries); 180 181 return result; 182 } 183 184 /** 185 * Get the query properties 186 * @param query The query 187 * @return The query properties 188 */ 189 public Map<String, Object> getQueryProperties (Query query) 190 { 191 Map<String, Object> infos = new HashMap<>(); 192 193 List<String> fullPath = new ArrayList<>(); 194 fullPath.add(query.getTitle()); 195 196 AmetysObject node = query.getParent(); 197 while (node instanceof QueryContainer 198 && node.getParent() instanceof QueryContainer) // The parent must also be a container to avoid the root 199 { 200 fullPath.add(0, node.getName()); 201 node = node.getParent(); 202 } 203 204 205 infos.put("isQuery", true); 206 infos.put("id", query.getId()); 207 infos.put("title", query.getTitle()); 208 infos.put("fullPath", String.join(" > ", fullPath)); 209 infos.put("type", query.getType()); 210 infos.put("description", query.getDescription()); 211 infos.put("author", _userHelper.user2json(query.getAuthor())); 212 infos.put("visibility", query.getVisibility().toString()); 213 infos.put("content", query.getContent()); 214 infos.put("lastModificationDate", DateUtils.dateToString(query.getLastModificationDate())); 215 216 boolean isAdministrator = isAdministrator(); 217 infos.put("isAdministrator", isAdministrator); 218 219 UserIdentity currentUser = _userProvider.getUser(); 220 infos.put("canRead", isAdministrator || query.canRead(currentUser)); 221 infos.put("canWrite", isAdministrator || query.canWrite(currentUser)); 222 223 return infos; 224 } 225 226 /** 227 * Gets the ids of the path elements of a query or query container, i.e. the parent ids. 228 * <br>For instance, if the query path is 'a/b/c', then the result list will be ["id-of-a", "id-of-b", "id-of-c"] 229 * @param queryId The id of the query 230 * @return the ids of the path elements of a query 231 */ 232 @Callable 233 public List<String> getIdsOfPath(String queryId) 234 { 235 AmetysObject queryOrQueryContainer = _resolver.resolveById(queryId); 236 QueryContainer queriesRootNode = getQueriesRootNode(); 237 238 if (!(queryOrQueryContainer instanceof Query) && !(queryOrQueryContainer instanceof QueryContainer)) 239 { 240 throw new IllegalArgumentException("The given id is not a query nor a query container"); 241 } 242 243 List<String> pathElements = new ArrayList<>(); 244 QueryContainer current = queryOrQueryContainer.getParent(); 245 while (!queriesRootNode.equals(current)) 246 { 247 pathElements.add(0, current.getId()); 248 current = current.getParent(); 249 } 250 251 return pathElements; 252 } 253 254 /** 255 * Determines if the current user has right to handle {@link QueryContainer}s 256 * @return <code>true</code> if the current user has right to handle {@link QueryContainer}s 257 */ 258 @Callable 259 public boolean hasRightOnContainers() 260 { 261 return _rightManager.currentUserHasRight("QueriesDirectory_Rights_Containers", "/cms") == RightResult.RIGHT_ALLOW; 262 } 263 264 /** 265 * Get the query container properties 266 * @param id The query container id. Can be {@link #ROOT_QUERY_CONTAINER_ID} for the root container. 267 * @return The query container properties 268 */ 269 @Callable 270 public Map<String, Object> getQueryContainerProperties(String id) 271 { 272 return getQueryContainerProperties(_getQueryContainer(id)); 273 } 274 275 /** 276 * Get the query container properties 277 * @param queryContainer The query container 278 * @return The query container properties 279 */ 280 public Map<String, Object> getQueryContainerProperties(QueryContainer queryContainer) 281 { 282 Map<String, Object> infos = new HashMap<>(); 283 284 infos.put("isQuery", false); 285 infos.put("id", queryContainer.getId()); 286 infos.put("title", queryContainer.getName()); 287 288 return infos; 289 } 290 291 /** 292 * Creates a new {@link Query} 293 * @param title The title of the query 294 * @param desc The description of the query 295 * @param type The type of the query 296 * @param content The content of the query 297 * @param parentId The id of the parent of the query. Use {@link #ROOT_QUERY_CONTAINER_ID} for the root container. 298 * @return A result map 299 */ 300 @Callable 301 public Map<String, Object> createQuery(String title, String desc, String type, String content, String parentId) 302 { 303 Map<String, Object> results = new HashMap<>(); 304 305 QueryContainer queriesNode = _getQueryContainer(parentId); 306 307 String name = FilterNameHelper.filterName(title); 308 309 // Find unique name 310 String uniqueName = name; 311 int index = 2; 312 while (queriesNode.hasChild(uniqueName)) 313 { 314 uniqueName = name + "-" + (index++); 315 } 316 317 Query query = queriesNode.createChild(uniqueName, QueryFactory.QUERY_NODETYPE); 318 query.setTitle(title); 319 query.setDescription(desc); 320 query.setAuthor(_userProvider.getUser()); 321 query.setType(type); 322 query.setContent(content); 323 query.setVisibility(Visibility.PRIVATE); 324 query.setLastModificationDate(new Date()); 325 326 queriesNode.saveChanges(); 327 328 results.put("id", query.getId()); 329 330 return results; 331 } 332 333 /** 334 * Creates a new {@link QueryContainer} 335 * @param parentId The id of the parent. Use {@link #ROOT_QUERY_CONTAINER_ID} for the root container. 336 * @param name The desired name for the new {@link QueryContainer} 337 * @return A result map 338 */ 339 @Callable(right = "QueriesDirectory_Rights_Containers", context = "/cms") 340 public Map<String, Object> createQueryContainer(String parentId, String name) 341 { 342 Map<String, Object> results = new HashMap<>(); 343 344 QueryContainer parent = _getQueryContainer(parentId); 345 346 int index = 2; 347 String legalName = Text.escapeIllegalJcrChars(name); 348 String realName = legalName; 349 while (parent.hasChild(realName)) 350 { 351 realName = legalName + " (" + index + ")"; 352 index++; 353 } 354 355 QueryContainer createdChild = parent.createChild(realName, QueryContainerFactory.QUERY_CONTAINER_NODETYPE); 356 parent.saveChanges(); 357 358 results.put("id", createdChild.getId()); 359 results.put("name", realName); 360 361 return results; 362 } 363 364 /** 365 * Edits a {@link Query} 366 * @param id The id of the query 367 * @param title The title of the query 368 * @param desc The description of the query 369 * @return A result map 370 */ 371 @Callable 372 public Map<String, Object> updateQuery(String id, String title, String desc) 373 { 374 Map<String, Object> results = new HashMap<>(); 375 376 Query query = _resolver.resolveById(id); 377 378 if (query.canWrite(_userProvider.getUser())) 379 { 380 query.setTitle(title); 381 query.setDescription(desc); 382 query.saveChanges(); 383 } 384 else 385 { 386 results.put("message", "not-allowed"); 387 } 388 389 results.put("id", query.getId()); 390 391 return results; 392 } 393 394 /** 395 * Renames a {@link QueryContainer} 396 * @param id The id of the query container 397 * @param newName The new name of the container 398 * @return A result map 399 */ 400 @Callable(right = "QueriesDirectory_Rights_Containers", context = "/cms") 401 public Map<String, Object> renameQueryContainer(String id, String newName) 402 { 403 Map<String, Object> results = new HashMap<>(); 404 405 QueryContainer query = _resolver.resolveById(id); 406 407 String legalName = Text.escapeIllegalJcrChars(newName); 408 Node node = query.getNode(); 409 try 410 { 411 node.getSession().move(node.getPath(), node.getParent().getPath() + '/' + legalName); 412 node.getSession().save(); 413 414 results.put("id", id); 415 results.put("newName", legalName); 416 } 417 catch (RepositoryException e) 418 { 419 getLogger().warn("Query container renaming failed.", e); 420 results.put("message", "cannot-rename"); 421 } 422 423 return results; 424 } 425 426 /** 427 * Moves a {@link Query} 428 * @param id The id of the query 429 * @param newParentId The id of the new parent container of the query. Use {@link #ROOT_QUERY_CONTAINER_ID} for the root container. 430 * @return A result map 431 */ 432 @Callable 433 public Map<String, Object> moveQuery(String id, String newParentId) 434 { 435 Map<String, Object> results = new HashMap<>(); 436 Query query = _resolver.resolveById(id); 437 438 if (query.canWrite(_userProvider.getUser()) 439 || hasRightOnContainers()) 440 { 441 _move(query, newParentId, results); 442 } 443 else 444 { 445 results.put("message", "not-allowed"); 446 } 447 448 results.put("id", query.getId()); 449 return results; 450 } 451 452 /** 453 * Moves a {@link QueryContainer} 454 * @param id The id of the query container 455 * @param newParentId The id of the new parent container of the query container. Use {@link #ROOT_QUERY_CONTAINER_ID} for the root container. 456 * @return A result map 457 */ 458 @Callable(right = "QueriesDirectory_Rights_Containers", context = "/cms") 459 public Map<String, Object> moveQueryContainer(String id, String newParentId) 460 { 461 Map<String, Object> results = new HashMap<>(); 462 QueryContainer queryContainer = _resolver.resolveById(id); 463 464 _move(queryContainer, newParentId, results); 465 466 results.put("id", queryContainer.getId()); 467 results.put("name", queryContainer.getName()); 468 return results; 469 } 470 471 private void _move(MovableAmetysObject obj, String newParentId, Map<String, Object> results) 472 { 473 QueryContainer newParent = _getQueryContainer(newParentId); 474 if (obj.canMoveTo(newParent)) 475 { 476 try 477 { 478 obj.moveTo(newParent, false); 479 } 480 catch (AmetysRepositoryException e) 481 { 482 getLogger().warn("Query moving failed.", e); 483 results.put("message", "cannot-move"); 484 } 485 } 486 else 487 { 488 results.put("message", "cannot-move"); 489 } 490 } 491 492 private QueryContainer _getQueryContainer(String id) 493 { 494 QueryContainer container; 495 if (ROOT_QUERY_CONTAINER_ID.equals(id)) 496 { 497 container = getQueriesRootNode(); 498 } 499 else 500 { 501 container = _resolver.resolveById(id); 502 } 503 return container; 504 } 505 506 /** 507 * Saves a {@link Query} 508 * @param id The id of the query 509 * @param type The type of the query 510 * @param content The content of the query 511 * @return A result map 512 */ 513 @Callable 514 public Map<String, Object> saveQuery(String id, String type, String content) 515 { 516 Map<String, Object> results = new HashMap<>(); 517 518 Query query = _resolver.resolveById(id); 519 520 if (query.canWrite(_userProvider.getUser())) 521 { 522 query.setType(type); 523 query.setContent(content); 524 query.saveChanges(); 525 } 526 else 527 { 528 results.put("message", "not-allowed"); 529 } 530 531 results.put("id", query.getId()); 532 533 return results; 534 } 535 536 /** 537 * Deletes {@link Query}(ies) 538 * @param ids The ids of the queries to delete 539 * @return A result map 540 */ 541 @Callable 542 public Map<String, Object> deleteQuery(List<String> ids) 543 { 544 Map<String, Object> results = new HashMap<>(); 545 546 List<String> deletedQueries = new ArrayList<>(); 547 List<String> unknownQueries = new ArrayList<>(); 548 List<String> notallowedQueries = new ArrayList<>(); 549 550 for (String id : ids) 551 { 552 try 553 { 554 Query query = _resolver.resolveById(id); 555 556 UserIdentity author = query.getAuthor(); 557 if (author != null && author.equals(_userProvider.getUser())) 558 { 559 query.remove(); 560 query.saveChanges(); 561 deletedQueries.add(id); 562 } 563 else 564 { 565 notallowedQueries.add(query.getTitle()); 566 } 567 } 568 catch (UnknownAmetysObjectException e) 569 { 570 unknownQueries.add(id); 571 getLogger().error("Unable to delete query. The query of id '" + id + " doesn't exist", e); 572 } 573 } 574 575 results.put("deletedQueries", deletedQueries); 576 results.put("notallowedQueries", notallowedQueries); 577 results.put("unknownQueries", unknownQueries); 578 579 return results; 580 } 581 582 /** 583 * Can the current user delete all the {@link QueryContainer}s from the list ? 584 * @param ids The {@link QueryContainer} ids 585 * @return <code>true</code> if he can 586 */ 587 protected boolean canDeleteAllQueryContainers(List<String> ids) 588 { 589 return canDeleteQueryContainers(ids).values() 590 .stream() 591 .allMatch(Boolean.TRUE::equals); 592 } 593 594 /** 595 * Determines if the current user can delete the given {@link QueryContainer}s 596 * @param ids The {@link QueryContainer} ids 597 * @return A map with <code>true</code> for each id if the current user can delete the associated {@link QueryContainer} 598 */ 599 @Callable 600 public Map<String, Boolean> canDeleteQueryContainers(List<String> ids) 601 { 602 return ids.stream() 603 .collect(Collectors.toMap( 604 Function.identity(), 605 LambdaUtils.wrap(this::_canDeleteQueryContainer))); 606 } 607 608 private boolean _canDeleteQueryContainer(String id) 609 { 610 if (ROOT_QUERY_CONTAINER_ID.equals(id)) 611 { 612 return false; 613 } 614 else 615 { 616 UserIdentity user = _userProvider.getUser(); 617 Set<GroupIdentity> userGroups = _groupManager.getUserGroups(user); 618 QueryContainer container = _resolver.resolveById(id); 619 620 org.ametys.plugins.queriesdirectory.QueryHelper.Visibility visibility = org.ametys.plugins.queriesdirectory.QueryHelper.Visibility.of(user, userGroups); 621 AmetysObjectIterable<Query> queriesInWriteAccess = getChildQueriesInWriteAccess(container, false, visibility, Optional.empty()); 622 AmetysObjectIterable<Query> allQueries = getChildQueriesForAdministrator(container, false, Optional.empty()); 623 return allQueries.getSize() == queriesInWriteAccess.getSize(); // if different collections, then it means the current user has not the write access on at least one query 624 } 625 } 626 627 /** 628 * Determines if application must warn before deleting the given {@link QueryContainer}s 629 * @param ids The {@link QueryContainer} ids 630 * @return <code>true</code> if application must warn 631 */ 632 @Callable 633 public boolean mustWarnBeforeDeletion(List<String> ids) 634 { 635 return ids.stream() 636 .anyMatch(LambdaUtils.wrapPredicate(this::_mustWarnBeforeDeletion)); 637 } 638 639 private boolean _mustWarnBeforeDeletion(String id) 640 { 641 QueryContainer container = _resolver.resolveById(id); 642 AmetysObjectIterable<Query> allQueries = getChildQueriesForAdministrator(container, false, Optional.empty()); 643 return _containsNotOwnQueries(allQueries) || _containsSharedQueries(allQueries); 644 } 645 646 private boolean _containsNotOwnQueries(AmetysObjectIterable<Query> allQueries) 647 { 648 UserIdentity currentUser = _userProvider.getUser(); 649 return allQueries.stream() 650 .map(Query::getAuthor) 651 .anyMatch(Predicates.not(currentUser::equals)); 652 } 653 654 private boolean _containsSharedQueries(AmetysObjectIterable<Query> allQueries) 655 { 656 return allQueries.stream() 657 .map(Query::getVisibility) 658 .anyMatch(Predicates.not(Visibility.PRIVATE::equals)); 659 } 660 661 662 /** 663 * Deletes {@link QueryContainer}(s) 664 * @param ids The ids of the query containers to delete 665 * @return A result map 666 */ 667 @Callable(right = "QueriesDirectory_Rights_Containers", context = "/cms") 668 public Map<String, Object> deleteQueryContainer(List<String> ids) 669 { 670 Map<String, Object> results = new HashMap<>(); 671 672 List<Map<String, Object>> allDeleted = new ArrayList<>(); 673 List<Map<String, Object>> allUnknown = new ArrayList<>(); 674 675 if (!canDeleteAllQueryContainers(ids)) 676 { 677 results.put("message", "not-allowed"); 678 return results; 679 } 680 681 for (String id : ids) 682 { 683 try 684 { 685 QueryContainer container = _resolver.resolveById(id); 686 String name = container.getName(); 687 container.remove(); 688 container.saveChanges(); 689 Map<String, Object> deleted = ImmutableMap.of("id", id, "name", name); 690 allDeleted.add(deleted); 691 } 692 catch (UnknownAmetysObjectException e) 693 { 694 Map<String, Object> unknown = ImmutableMap.of("id", id, "name", id); 695 allUnknown.add(unknown); 696 getLogger().error("Unable to delete query container. The container of id '" + id + " doesn't exist", e); 697 } 698 } 699 700 results.put("deleted", allDeleted); 701 results.put("unknown", allUnknown); 702 703 return results; 704 } 705 706 /** 707 * Changes the visibility of a {@link Query} 708 * @param queryId The id of the query 709 * @param visibilityStr The new visibility 710 * @return A result map 711 */ 712 @Callable 713 public Map<String, Object> changeVisibility(String queryId, String visibilityStr) 714 { 715 Map<String, Object> results = new HashMap<>(); 716 717 // Parameter checks. 718 Query query = null; 719 if (StringUtils.isNotEmpty(queryId)) 720 { 721 query = _resolver.resolveById(queryId); 722 } 723 else 724 { 725 throw new IllegalArgumentException("Mandatory query id parameter is missing."); 726 } 727 728 Visibility visibility = Visibility.valueOf(visibilityStr.toUpperCase()); 729 730 UserIdentity author = query.getAuthor(); 731 if (author != null && author.equals(_userProvider.getUser())) 732 { 733 query.setVisibility(visibility); 734 query.saveChanges(); 735 } 736 else 737 { 738 results.put("message", "not-allowed"); 739 } 740 741 return results; 742 } 743 744 /** 745 * Assign rights to the given users on the given query 746 * @param queryId The query id 747 * @param profileId The profile id 748 * @param users The users to grant 749 * @return A result map 750 */ 751 @Callable 752 public Map<String, Object> addGrantedUsers(String queryId, String profileId, List<Map<String, String>> users) 753 { 754 return _assignRights(queryId, profileId, "users", users, null); 755 } 756 757 /** 758 * Assign rigths to the given groups on the given query 759 * @param queryId The query id 760 * @param profileId The profile id 761 * @param groups The groups to grant 762 * @return A result map 763 */ 764 @Callable 765 public Map<String, Object> addGrantedGroups(String queryId, String profileId, List<Map<String, String>> groups) 766 { 767 return _assignRights(queryId, profileId, "groups", null, groups); 768 } 769 770 private Map<String, Object> _assignRights(String queryId, String profileId, String type, List<Map<String, String>> users, List<Map<String, String>> groups) 771 { 772 HashMap<String, Object> results = new HashMap<>(); 773 774 // Parameter checks. 775 Query query = null; 776 if (StringUtils.isNotEmpty(queryId)) 777 { 778 query = _resolver.resolveById(queryId); 779 } 780 else 781 { 782 throw new IllegalArgumentException("Mandatory query id parameter is missing."); 783 } 784 785 if (!"users".equals(type) && !"groups".equals(type)) 786 { 787 throw new IllegalArgumentException("Unexpected type parameter : " + type); 788 } 789 790 if (!"read_access".equals(profileId) && !"write_access".equals(profileId)) 791 { 792 throw new IllegalArgumentException("Unexpected profile identifier : " + profileId); 793 } 794 795 UserIdentity author = query.getAuthor(); 796 if (author != null && author.equals(_userProvider.getUser())) 797 { 798 QueryProfile profile = QueryProfile.valueOf(profileId.toUpperCase()); 799 800 // Set new values 801 if ("users".equals(type)) 802 { 803 Set<UserIdentity> allEntries = query.getGrantedUsers(profile); 804 805 for (Map<String, String> user : users) 806 { 807 UserIdentity userIdentity = new UserIdentity(user.get("login"), user.get("populationId")); 808 if (!allEntries.contains(userIdentity)) 809 { 810 allEntries.add(userIdentity); 811 } 812 } 813 814 query.setGrantedUsers(profile, allEntries); 815 } 816 else 817 { 818 Set<GroupIdentity> allEntries = query.getGrantedGroups(profile); 819 820 for (Map<String, String> group : groups) 821 { 822 GroupIdentity groupIdentity = new GroupIdentity(group.get("id"), group.get("groupDirectory")); 823 if (!allEntries.contains(groupIdentity)) 824 { 825 allEntries.add(groupIdentity); 826 } 827 } 828 829 query.setGrantedGroups(profile, allEntries); 830 } 831 832 // Save 833 query.saveChanges(); 834 } 835 else 836 { 837 results.put("message", "not-allowed"); 838 } 839 840 return results; 841 } 842 843 /** 844 * Remove rights to the given users on the given query 845 * @param queryId The query id 846 * @param profileId The profile id 847 * @param users The users to remove 848 * @param groups The groups to remove 849 * @return A result map 850 */ 851 @Callable 852 public Map<String, Object> removeAssignment(String queryId, String profileId, List<Map<String, String>> users, List<Map<String, String>> groups) 853 { 854 HashMap<String, Object> results = new HashMap<>(); 855 856 // Parameter checks. 857 Query query = null; 858 if (StringUtils.isNotEmpty(queryId)) 859 { 860 query = _resolver.resolveById(queryId); 861 } 862 else 863 { 864 throw new IllegalArgumentException("Mandatory query id parameter is missing."); 865 } 866 if (!"read_access".equals(profileId) && !"write_access".equals(profileId)) 867 { 868 throw new IllegalArgumentException("Unexpected profile identifier : " + profileId); 869 } 870 871 UserIdentity author = query.getAuthor(); 872 if (author != null && author.equals(_userProvider.getUser())) 873 { 874 QueryProfile profile = QueryProfile.valueOf(profileId.toUpperCase()); 875 876 if (!users.isEmpty()) 877 { 878 Set<UserIdentity> grantedUsers = query.getGrantedUsers(profile); 879 for (Map<String, String> user : users) 880 { 881 UserIdentity userIdentity = new UserIdentity(user.get("login"), user.get("populationId")); 882 grantedUsers.remove(userIdentity); 883 } 884 885 query.setGrantedUsers(profile, grantedUsers); 886 } 887 888 if (!groups.isEmpty()) 889 { 890 Set<GroupIdentity> grantedGroups = query.getGrantedGroups(profile); 891 for (Map<String, String> group : groups) 892 { 893 GroupIdentity groupIdentity = new GroupIdentity(group.get("id"), group.get("groupDirectory")); 894 grantedGroups.remove(groupIdentity); 895 } 896 897 query.setGrantedGroups(profile, grantedGroups); 898 } 899 900 // Save 901 query.saveChanges(); 902 } 903 else 904 { 905 results.put("message", "not-allowed"); 906 } 907 908 return results; 909 } 910 911 /** 912 * Determines if the current user is administrator of queries 913 * @return <code>true</code> if current user is administrator 914 */ 915 public boolean isAdministrator() 916 { 917 return _rightManager.hasRight(_userProvider.getUser(), "QueriesDirectory_Rights_Admin", "/cms") == RightResult.RIGHT_ALLOW; 918 } 919 920 /** 921 * Test if the current user has the right required to access the query 922 * @param query The query 923 * @return true if the user has the right needed, false otherwise. 924 */ 925 protected boolean _hasRight(Query query) 926 { 927 UserIdentity user = _userProvider.getUser(); 928 return query.canRead(user) || isAdministrator(); 929 } 930 931 /** 932 * Gets all queries for administrator for given parent 933 * @param parent The {@link QueryContainer}, defining the context from which getting children 934 * @param onlyDirect <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 935 * @param type The query type 936 * @return all queries for administrator for given parent 937 */ 938 public AmetysObjectIterable<Query> getChildQueriesForAdministrator(QueryContainer parent, boolean onlyDirect, Optional<String> type) 939 { 940 return _resolver.query(QueryHelper.getXPathForQueriesForAdministrator(parent, onlyDirect, type)); 941 } 942 943 /** 944 * Gets all queries in READ access for given parent 945 * @param parent The {@link QueryContainer}, defining the context from which getting children 946 * @param onlyDirect <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 947 * @param visibility The user and its groups for checking visibility 948 * @param type The query type 949 * @return all queries in READ access for given parent 950 */ 951 public AmetysObjectIterable<Query> getChildQueriesInReadAccess(QueryContainer parent, boolean onlyDirect, QueryHelper.Visibility visibility, Optional<String> type) 952 { 953 return _resolver.query(QueryHelper.getXPathForQueriesInReadAccess(parent, onlyDirect, visibility, type)); 954 } 955 956 /** 957 * Gets all queries in WRITE access for given parent 958 * @param parent The {@link QueryContainer}, defining the context from which getting children 959 * @param onlyDirect <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 960 * @param visibility The user and its groups for checking visibility 961 * @param type The query type 962 * @return all queries in WRITE access for given parent 963 */ 964 public AmetysObjectIterable<Query> getChildQueriesInWriteAccess(QueryContainer parent, boolean onlyDirect, QueryHelper.Visibility visibility, Optional<String> type) 965 { 966 return _resolver.query(QueryHelper.getXPathForQueriesInWriteAccess(parent, onlyDirect, visibility, type)); 967 } 968 969 /** 970 * Gets all query containers for given parent 971 * @param parent The {@link QueryContainer}, defining the context from which getting children 972 * @return all query containers for given parent 973 */ 974 public AmetysObjectIterable<QueryContainer> getChildQueryContainers(QueryContainer parent) 975 { 976 return _resolver.query(QueryHelper.getXPathForQueryContainers(parent)); 977 } 978}