View Javadoc
1   /**
2    * This file Copyright (c) 2014-2018 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.security.app.action;
35  
36  import info.magnolia.cms.security.Group;
37  import info.magnolia.cms.security.GroupManager;
38  import info.magnolia.cms.security.SecuritySupport;
39  import info.magnolia.cms.security.User;
40  import info.magnolia.cms.security.UserManager;
41  import info.magnolia.commands.CommandsManager;
42  import info.magnolia.event.EventBus;
43  import info.magnolia.i18nsystem.SimpleTranslator;
44  import info.magnolia.jcr.util.NodeTypes;
45  import info.magnolia.jcr.util.NodeUtil;
46  import info.magnolia.jcr.util.NodeVisitor;
47  import info.magnolia.ui.api.action.ActionExecutionException;
48  import info.magnolia.ui.api.context.UiContext;
49  import info.magnolia.ui.api.event.AdmincentralEventBus;
50  import info.magnolia.ui.api.overlay.ConfirmationCallback;
51  import info.magnolia.ui.framework.action.DeleteAction;
52  import info.magnolia.ui.vaadin.integration.jcr.JcrItemAdapter;
53  import info.magnolia.ui.vaadin.overlay.MessageStyleTypeEnum;
54  
55  import java.util.ArrayList;
56  import java.util.Collection;
57  import java.util.HashMap;
58  import java.util.List;
59  import java.util.Map;
60  
61  import javax.inject.Named;
62  import javax.jcr.Node;
63  import javax.jcr.RepositoryException;
64  
65  import org.slf4j.Logger;
66  import org.slf4j.LoggerFactory;
67  
68  /**
69   * Action that will only delete a folder if sub nodes are not in use.
70   *
71   * @see DeleteFolderActionDefinition
72   */
73  public class DeleteFolderAction extends DeleteAction<DeleteFolderActionDefinition> {
74  
75      private static final Logger log = LoggerFactory.getLogger(DeleteFolderAction.class);
76  
77      private final SecuritySupport securitySupport;
78  
79      public DeleteFolderAction(DeleteFolderActionDefinition definition, JcrItemAdapter item, CommandsManager commandsManager, @Named(AdmincentralEventBus.NAME) EventBus eventBus, UiContext uiContext, SimpleTranslator i18n, SecuritySupport securitySupport) {
80          super(definition, item, commandsManager, eventBus, uiContext, i18n);
81          this.securitySupport = securitySupport;
82      }
83  
84      public DeleteFolderAction(DeleteFolderActionDefinition definition, List<JcrItemAdapter> items, CommandsManager commandsManager, @Named(AdmincentralEventBus.NAME) EventBus eventBus, UiContext uiContext, SimpleTranslator i18n, SecuritySupport securitySupport) {
85          super(definition, items, commandsManager, eventBus, uiContext, i18n);
86          this.securitySupport = securitySupport;
87      }
88  
89      @Override
90      public void execute() throws ActionExecutionException {
91          try {
92              executeOnConfirmation();
93          } catch (RepositoryException e) {
94              throw new ActionExecutionException(getVerificationErrorMessage() + e.getMessage());
95          }
96      }
97  
98      private String getConfirmationDialogStatement() throws RepositoryException {
99          StringBuilder confirmMessage = new StringBuilder("<ul>");
100         final Map<String, List<String>> assignedTo = new HashMap<>();
101         for (JcrItemAdapter item : getSortedItems(getItemComparator())) {
102             final Map<String, List<String>> assignedToItem = new HashMap<>();
103             try {
104                 Map<String, List<String>> dependenciesMap = getAssignedUsersAndGroupsMap(item);
105                 if (!dependenciesMap.isEmpty()) {
106                     confirmMessage.append("<li>");
107                     confirmMessage.append(item.getJcrItem().getName());
108                     confirmMessage.append("</li>");
109                     assignedToItem.putAll(dependenciesMap);
110                 }
111             } catch (RepositoryException e) {
112                 throw new RepositoryException("Cannot get the users/groups the group or role is assigned to.", e);
113             }
114             confirmMessage.append(formatUserAndGroupList(assignedToItem));
115             assignedTo.putAll(assignedToItem);
116         }
117         confirmMessage.append("</ul>");
118         return !assignedTo.isEmpty() ? confirmMessage.toString() : "";
119     }
120 
121     private void executeOnConfirmation() throws RepositoryException {
122         final String message = getConfirmationDialogStatement();
123         getUiContext().openConfirmation(MessageStyleTypeEnum.WARNING,
124                 getConfirmationDialogTitle(),
125                 (!message.isEmpty() ? "<br />" + getI18n().translate("security-app.delete.confirmationDialog.body.label", message) + "<br />" : "") + getConfirmationDialogBody(),
126                 getConfirmationDialogProceedLabel(),
127                 getConfirmationDialogCancelLabel(),
128                 true,
129                 new ConfirmationCallback() {
130                     @Override
131                     public void onCancel() {
132                         // do nothing
133                     }
134 
135                     @Override
136                     public void onSuccess() {
137                         try {
138                             DeleteFolderAction.super.execute();
139                         } catch (Exception e) {
140                             onError(e);
141                         }
142                     }
143                 });
144     }
145 
146     @Override
147     protected void onPreExecute() throws Exception {
148         super.onPreExecute();
149 
150         final Map<String, List<String>> assignedTo = getAssignedUsersAndGroupsMap();
151         if (!assignedTo.isEmpty()) {
152             if (getCurrentItem().isNode()) {
153                 Node folder = (Node) getCurrentItem().getJcrItem();
154 
155                 NodeUtil.visit(folder, new NodeVisitor() {
156                     @Override
157                     public void visit(Node node) throws RepositoryException {
158                         if (NodeUtil.isNodeType(node, NodeTypes.Role.NAME) || NodeUtil.isNodeType(node, NodeTypes.Group.NAME)) {
159                             try {
160                                 removeDependencies(node);
161                             } catch (Exception e) {
162                                 onError(e);
163                             }
164                         }
165                     }
166                 });
167             }
168         }
169     }
170 
171     /**
172      * @return the list of user- and group-names this item (group or role) is directly assigned to.
173      */
174     private List<String> getAssignedUsersAndGroups(Node node) throws RepositoryException {
175         List<String> assignedTo = new ArrayList<String>();
176 
177         final String groupOrRoleName = node.getName();
178 
179         final String translatedUserString = getI18n().translate("security.delete.userIdentifier");
180 
181         final String translatedGroupString = getI18n().translate("security.delete.groupIdentifier");
182 
183         if (NodeUtil.isNodeType(node, NodeTypes.Group.NAME)) {
184             // group - user, group - group
185             for (String user : securitySupport.getUserManager().getUsersWithGroup(groupOrRoleName)) {
186                 assignedTo.add(translatedUserString + ":" + user);
187             }
188             for (String group : securitySupport.getGroupManager().getGroupsWithGroup(groupOrRoleName)) {
189                 assignedTo.add(translatedGroupString + ":" + group);
190             }
191         } else if (NodeUtil.isNodeType(node, NodeTypes.Role.NAME)) {
192             // role - user, role - group
193             for (String user : securitySupport.getUserManager().getUsersWithRole(groupOrRoleName)) {
194                 assignedTo.add(translatedUserString + ":" + user);
195             }
196             for (String group : securitySupport.getGroupManager().getGroupsWithRole(groupOrRoleName)) {
197                 assignedTo.add(translatedGroupString + ":" + group);
198             }
199         }
200 
201         return assignedTo;
202     }
203 
204     protected String getVerificationErrorMessage() {
205         return getI18n().translate("security.delete.folder.cannotVerifyError");
206     }
207 
208     /**
209      * @deprecated since 5.3.6 - will be removed without replacement
210      */
211     @Deprecated
212     protected Collection<String> getGroupsOrRoles(User user) {
213         List<String> groupsAndRoles = new ArrayList<String>();
214         groupsAndRoles.addAll(user.getGroups());
215         groupsAndRoles.addAll(user.getRoles());
216         return groupsAndRoles;
217     }
218 
219     /**
220      * @deprecated since 5.3.6 - will be removed without replacement
221      */
222     @Deprecated
223     protected Collection<String> getGroupsOrRoles(Group group) {
224         List<String> groupsAndRoles = new ArrayList<String>();
225         groupsAndRoles.addAll(group.getGroups());
226         groupsAndRoles.addAll(group.getRoles());
227         return groupsAndRoles;
228     }
229 
230     /**
231      * @deprecated since 5.3.10 - use {@link #formatUserAndGroupList(Map<String, List<String>>)} instead.
232      */
233     @Deprecated
234     protected String getUserAndGroupListForErrorMessage(List<String> usersAndGroups) {
235         Map<String, List<String>> usersAndGroupsMap = new HashMap<String, List<String>>();
236         usersAndGroupsMap.put("dependencies", usersAndGroups);
237         return formatUserAndGroupList(usersAndGroupsMap);
238     }
239 
240     protected String formatUserAndGroupList(Map<String, List<String>> usersAndGroups) {
241         StringBuilder message = new StringBuilder("<ul>");
242         for (String key : usersAndGroups.keySet()) {
243             int i = 0;
244             message.append("<li>").append(key).append("</li>");
245             message.append("<ul>");
246             for (String name : usersAndGroups.get(key)) {
247                 message.append("<li>").append(name).append("</li>");
248                 if (i > 4) {
249                     message.append("<li>...</li>");
250                     break;
251                 }
252                 i++;
253             }
254             message.append("</ul>");
255         }
256         message.append("</ul>");
257         return message.toString();
258     }
259 
260     protected String getConfirmationDialogTitle() {
261         return getI18n().translate("security.folders.actions.confirmDeleteFolder.confirmationHeader");
262     }
263 
264     protected String getConfirmationDialogBody() {
265         return getI18n().translate("security.folders.actions.confirmDeleteFolder.confirmationMessage");
266     }
267 
268     protected String getConfirmationDialogProceedLabel() {
269         return getI18n().translate("security.folders.actions.confirmDeleteFolder.proceedLabel");
270     }
271 
272     protected String getConfirmationDialogCancelLabel() {
273         return getI18n().translate("security.folders.actions.confirmDeleteFolder.cancelLabel");
274     }
275 
276     protected String getBaseErrorMessage() {
277         return getI18n().translate("security.delete.folder.roleOrGroupInfolderStillInUse");
278     }
279 
280     private void removeDependencies(Node node) throws RepositoryException, ActionExecutionException {
281         final String groupOrRoleName = node.getName();
282         final UserManager userManager = securitySupport.getUserManager();
283         final GroupManager groupManager = securitySupport.getGroupManager();
284         if (NodeUtil.isNodeType(node, NodeTypes.Group.NAME)) {
285             // group - user, group - group
286             for (String user : securitySupport.getUserManager().getUsersWithGroup(groupOrRoleName)) {
287                 userManager.removeGroup(userManager.getUser(user), groupOrRoleName);
288             }
289             for (String group : securitySupport.getGroupManager().getGroupsWithGroup(groupOrRoleName)) {
290                 groupManager.removeGroup(groupManager.getGroup(group), groupOrRoleName);
291             }
292         } else if (NodeUtil.isNodeType(node, NodeTypes.Role.NAME)) {
293             // role - user, role - group
294             for (String user : securitySupport.getUserManager().getUsersWithRole(groupOrRoleName)) {
295                 userManager.removeRole(userManager.getUser(user), groupOrRoleName);
296             }
297             for (String group : securitySupport.getGroupManager().getGroupsWithRole(groupOrRoleName)) {
298                 groupManager.removeRole(groupManager.getGroup(group), groupOrRoleName);
299             }
300         }
301     }
302 
303     private Map<String, List<String>> getAssignedUsersAndGroupsMap() throws RepositoryException {
304         return getAssignedUsersAndGroupsMap(getCurrentItem());
305     }
306     private Map<String, List<String>> getAssignedUsersAndGroupsMap(JcrItemAdapter jcrItemAdapter) throws RepositoryException {
307         final Map<String, List<String>> assignedTo = new HashMap<>();
308         try {
309             if (jcrItemAdapter.isNode()) {
310                 Node folder = (Node) jcrItemAdapter.getJcrItem();
311 
312                 NodeUtil.visit(folder, new NodeVisitor() {
313                     @Override
314                     public void visit(Node node) throws RepositoryException {
315                         if (NodeUtil.isNodeType(node, NodeTypes.Role.NAME) || NodeUtil.isNodeType(node, NodeTypes.Group.NAME)) {
316                             List<String> assignedToItem = getAssignedUsersAndGroups(node);
317                             if (!assignedToItem.isEmpty()) {
318                                 assignedTo.put(node.getName(), assignedToItem);
319                             }
320                         }
321                     }
322                 });
323             }
324         } catch (RepositoryException e) {
325             throw new RepositoryException("Cannot get the users/groups the group or role is assigned to.", e);
326         }
327         return assignedTo;
328     }
329 }