View Javadoc
1   /**
2    * This file Copyright (c) 2013-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.Security;
39  import info.magnolia.cms.security.SecuritySupport;
40  import info.magnolia.cms.security.User;
41  import info.magnolia.cms.security.UserManager;
42  import info.magnolia.commands.CommandsManager;
43  import info.magnolia.event.EventBus;
44  import info.magnolia.i18nsystem.SimpleTranslator;
45  import info.magnolia.jcr.util.NodeTypes;
46  import info.magnolia.jcr.util.NodeUtil;
47  import info.magnolia.objectfactory.Components;
48  import info.magnolia.ui.api.action.ActionExecutionException;
49  import info.magnolia.ui.api.context.UiContext;
50  import info.magnolia.ui.api.event.AdmincentralEventBus;
51  import info.magnolia.ui.api.overlay.ConfirmationCallback;
52  import info.magnolia.ui.framework.action.DeleteAction;
53  import info.magnolia.ui.framework.action.DeleteActionDefinition;
54  import info.magnolia.ui.vaadin.integration.jcr.JcrItemAdapter;
55  import info.magnolia.ui.vaadin.overlay.MessageStyleTypeEnum;
56  
57  import java.util.ArrayList;
58  import java.util.Collection;
59  import java.util.List;
60  
61  import javax.inject.Inject;
62  import javax.inject.Named;
63  import javax.jcr.Node;
64  import javax.jcr.RepositoryException;
65  
66  import org.slf4j.Logger;
67  import org.slf4j.LoggerFactory;
68  
69  /**
70   * Abstract common supertype for {@link DeleteGroupAction} and {@link DeleteRoleAction}.
71   *
72   * @param <D> the action definition type, must extend the {@link DeleteActionDefinition} class.
73   */
74  public abstract class AbstractDeleteGroupOrRoleAction<D extends DeleteActionDefinition> extends DeleteAction {
75  
76      private static final Logger log = LoggerFactory.getLogger(AbstractDeleteGroupOrRoleAction.class);
77  
78      private final JcrItemAdapter item;
79      private final SecuritySupport securitySupport;
80  
81      @Inject
82      public AbstractDeleteGroupOrRoleAction(D definition, JcrItemAdapter item, CommandsManager commandsManager, @Named(AdmincentralEventBus.NAME) EventBus eventBus, UiContext uiContext, SimpleTranslator i18n, SecuritySupport securitySupport) {
83          super(definition, item, commandsManager, eventBus, uiContext, i18n);
84          this.item = item;
85          this.securitySupport = securitySupport;
86      }
87  
88      /**
89       * @deprecated since 5.3.6 instead of use {@link #AbstractDeleteGroupOrRoleAction(info.magnolia.ui.framework.action.DeleteActionDefinition, info.magnolia.ui.vaadin.integration.jcr.JcrItemAdapter, info.magnolia.commands.CommandsManager, info.magnolia.event.EventBus, info.magnolia.ui.api.context.UiContext, info.magnolia.i18nsystem.SimpleTranslator, info.magnolia.cms.security.SecuritySupport)}
90       */
91      @Deprecated
92      public AbstractDeleteGroupOrRoleAction(D definition, JcrItemAdapter item, CommandsManager commandsManager, @Named(AdmincentralEventBus.NAME) EventBus eventBus, UiContext uiContext, SimpleTranslator i18n) {
93          this(definition, item, commandsManager, eventBus, uiContext, i18n, Security.getSecuritySupport());
94      }
95  
96      /**
97       * @deprecated since 5.2.2 instead of use {@link #AbstractDeleteGroupOrRoleAction(info.magnolia.ui.framework.action.DeleteActionDefinition, info.magnolia.ui.vaadin.integration.jcr.JcrItemAdapter, info.magnolia.commands.CommandsManager, info.magnolia.event.EventBus, info.magnolia.ui.api.context.UiContext, info.magnolia.i18nsystem.SimpleTranslator, info.magnolia.cms.security.SecuritySupport)}
98       */
99      @Deprecated
100     public AbstractDeleteGroupOrRoleAction(D definition, JcrItemAdapter item, @Named(AdmincentralEventBus.NAME) EventBus eventBus, UiContext uiContext, SimpleTranslator i18n) {
101         this(definition, item, Components.getComponent(CommandsManager.class), eventBus, uiContext, i18n, Security.getSecuritySupport());
102     }
103 
104     /**
105      * @deprecated since 5.2.2 instead of use {@link #getCurrentItem()}
106      */
107     @Deprecated
108     public JcrItemAdapter getItem() {
109         return this.item;
110     }
111 
112     /**
113      * @return the base for the error message shown to the user in case the item is already assigned; the list of users/groups the item is assigned to is added;
114      */
115     protected abstract String getBaseErrorMessage();
116 
117     /**
118      * @return the message to be shown to the user in case the verification ({@link #getAssignedUsersAndGroups()} method) fails.
119      */
120     protected abstract String getVerificationErrorMessage();
121 
122     /**
123      * Gets a collection of group or role names (according to where it is implemented) assigned to the user.
124      */
125     protected abstract Collection<String> getGroupsOrRoles(User user);
126 
127     /**
128      * Gets a collection of group or role names (according to where it is implemented) assigned to the group.
129      */
130     protected abstract Collection<String> getGroupsOrRoles(Group group);
131 
132     /**
133      * @return Collection of users that have the group or role to delete assigned to
134      */
135     protected abstract Collection<String> getUsersWithGroupOrRoleToDelete(String groupOrRoleName);
136 
137     /**
138      * @return Collection of groups that have the group or role to delete assigned to
139      */
140     protected abstract Collection<String> getGroupsWithGroupOrRoleToDelete(String groupOrRoleName);
141 
142     /**
143      * @deprecated since 5.2.2 instead of use {@link #onPreExecute()}
144      */
145     @Deprecated
146     protected void executeAfterConfirmation() {
147         log.warn("This method was deprecated. Use #onPreExecute() method instead.");
148     }
149 
150     @Override
151     protected void onPreExecute() throws Exception {
152 
153         List<String> assignedTo;
154         try {
155             assignedTo = getAssignedUsersAndGroups();
156         } catch (RepositoryException e) {
157             throw new RepositoryException("Cannot get the users/groups the group or role is assigned to.", e);
158         }
159         if (assignedTo != null && !assignedTo.isEmpty()) {
160             removeDependencies();
161         }
162         super.onPreExecute();
163     }
164 
165     protected abstract String getConfirmationDialogTitle ();
166 
167     protected abstract String getConfirmationDialogBody ();
168 
169     protected abstract String getConfirmationDialogProceedLabel ();
170 
171     protected abstract String getConfirmationDialogCancelLabel ();
172 
173 
174     @Override
175     public void execute() throws ActionExecutionException {
176         try {
177             executeOnConfirmation();
178         } catch (RepositoryException e) {
179             throw new ActionExecutionException(getVerificationErrorMessage() + e.getMessage());
180         }
181     }
182 
183     private String getConfirmationDialogStatement() throws RepositoryException {
184         final List<String> assignedTo = new ArrayList<String>();
185         StringBuilder confirmMessage = new StringBuilder("<ul>");
186         for (JcrItemAdapter item : (List<JcrItemAdapter>) getSortedItems(getItemComparator())) {
187             final List<String> assignedToItem = new ArrayList<String>();
188             try {
189                 List<String> dependenciesList = getAssignedUsersAndGroups(item.getJcrItem().getName());
190                 if (!dependenciesList.isEmpty()) {
191                     confirmMessage.append("<li>");
192                     confirmMessage.append(item.getJcrItem().getName());
193                     confirmMessage.append("</li>");
194                     assignedToItem.addAll(dependenciesList);
195                 }
196             } catch (RepositoryException e) {
197                 throw new RepositoryException("Cannot get the users/groups the group or role is assigned to.", e);
198             }
199             confirmMessage.append(formatUserAndGroupList(assignedToItem));
200             assignedTo.addAll(assignedToItem);
201         }
202         confirmMessage.append("</ul>");
203         return !assignedTo.isEmpty() ? confirmMessage.toString() : "";
204     }
205 
206     private void executeOnConfirmation() throws RepositoryException {
207         final String message = getConfirmationDialogStatement();
208         getUiContext().openConfirmation(MessageStyleTypeEnum.WARNING,
209                 getConfirmationDialogTitle(),
210                 (!message.isEmpty() ? "<br />" + getI18n().translate("security-app.delete.confirmationDialog.body.label", message) + "<br />" : "") + getConfirmationDialogBody(),
211                 getConfirmationDialogProceedLabel(),
212                 getConfirmationDialogCancelLabel(),
213                 true,
214                 new ConfirmationCallback() {
215                     @Override
216                     public void onCancel() {
217                         // do nothing
218                     }
219 
220                     @Override
221                     public void onSuccess() {
222                         try {
223                             AbstractDeleteGroupOrRoleAction.super.execute();
224                         } catch (Exception e) {
225                             onError(e);
226                         }
227                     }
228                 });
229     }
230 
231     /**
232      * @return the list of user- and group-names this item (group or role) is directly assigned to.
233      */
234     private List<String> getAssignedUsersAndGroups() throws RepositoryException {
235         return getAssignedUsersAndGroups(getCurrentItem().getJcrItem().getName());
236     }
237 
238     private List<String> getAssignedUsersAndGroups(final String itemName) throws RepositoryException {
239         List<String> assignedTo = new ArrayList<String>();
240 
241         final String translatedUserString = getI18n().translate("security.delete.userIdentifier");
242         // users
243         for (String user : getUsersWithGroupOrRoleToDelete(itemName)) {
244             assignedTo.add(translatedUserString + ":" + user);
245         }
246         // groups
247         final String translatedGroupString = getI18n().translate("security.delete.groupIdentifier");
248         for (String group : getGroupsWithGroupOrRoleToDelete(itemName)) {
249             assignedTo.add(translatedGroupString + ":" + group);
250         }
251 
252         return assignedTo;
253     }
254 
255     private static String formatUserAndGroupList(Collection<String> usersAndGroups) {
256         StringBuilder message = new StringBuilder("<ul>");
257         for (String name : usersAndGroups) {
258             message.append("<li>").append(name).append("</li>");
259         }
260         message.append("</ul>");
261         return message.toString();
262     }
263 
264     private void removeDependencies() throws RepositoryException, ActionExecutionException {
265         final String groupOrRoleName = getCurrentItem().getJcrItem().getName();
266         final UserManager userManager = securitySupport.getUserManager();
267         final GroupManager groupManager = securitySupport.getGroupManager();
268         // users
269         for (String user : getUsersWithGroupOrRoleToDelete(groupOrRoleName)) {
270             if (getCurrentItem().isNode()) {
271                 if (NodeUtil.isNodeType((Node) getCurrentItem().getJcrItem(), NodeTypes.Group.NAME)) {
272                     userManager.removeGroup(userManager.getUser(user), groupOrRoleName);
273                 }
274                 if (NodeUtil.isNodeType((Node) getCurrentItem().getJcrItem(), NodeTypes.Role.NAME)) {
275                     userManager.removeRole(userManager.getUser(user), groupOrRoleName);
276                 }
277             }
278         }
279         // groups
280         for (String group : getGroupsWithGroupOrRoleToDelete(groupOrRoleName)) {
281             if (getCurrentItem().isNode()) {
282                 if (NodeUtil.isNodeType((Node) getCurrentItem().getJcrItem(), NodeTypes.Group.NAME)) {
283                     groupManager.removeGroup(groupManager.getGroup(group), groupOrRoleName);
284                 }
285                 if (NodeUtil.isNodeType((Node) getCurrentItem().getJcrItem(), NodeTypes.Role.NAME)) {
286                     groupManager.removeRole(groupManager.getGroup(group), groupOrRoleName);
287                 }
288             }
289         }
290     }
291 
292     protected SecuritySupport getSecuritySupport() {
293         return securitySupport;
294     }
295 }