1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.security.app.dialog.action;
35
36 import static info.magnolia.cms.security.operations.AccessDefinition.DEFAULT_SUPERUSER_ROLE;
37
38 import info.magnolia.cms.core.Path;
39 import info.magnolia.cms.security.Role;
40 import info.magnolia.cms.security.RoleManager;
41 import info.magnolia.cms.security.SecuritySupport;
42 import info.magnolia.context.MgnlContext;
43 import info.magnolia.jcr.util.NodeTypes;
44 import info.magnolia.jcr.util.NodeUtil;
45 import info.magnolia.objectfactory.Components;
46 import info.magnolia.security.app.dialog.field.AccessControlList;
47 import info.magnolia.security.app.util.UsersWorkspaceUtil;
48 import info.magnolia.ui.dialog.action.SaveDialogAction;
49 import info.magnolia.ui.dialog.action.SaveDialogActionDefinition;
50 import info.magnolia.ui.api.action.ActionExecutionException;
51 import info.magnolia.ui.form.EditorCallback;
52 import info.magnolia.ui.form.EditorValidator;
53 import info.magnolia.ui.vaadin.integration.jcr.JcrNewNodeAdapter;
54 import info.magnolia.ui.vaadin.integration.jcr.JcrNodeAdapter;
55 import info.magnolia.ui.vaadin.integration.jcr.ModelConstants;
56
57 import java.lang.reflect.Field;
58 import java.security.AccessControlException;
59 import java.util.Collection;
60 import java.util.HashMap;
61 import java.util.Map;
62 import java.util.Map.Entry;
63
64 import javax.jcr.Node;
65 import javax.jcr.RepositoryException;
66 import javax.jcr.Session;
67
68 import org.apache.commons.lang3.StringUtils;
69 import org.slf4j.Logger;
70 import org.slf4j.LoggerFactory;
71
72 import com.google.common.base.Predicate;
73 import com.google.common.collect.Collections2;
74 import com.google.common.collect.ImmutableList;
75 import com.vaadin.data.Item;
76 import com.vaadin.data.Property;
77
78
79
80
81
82
83
84
85
86
87
88
89
90 public class SaveRoleDialogAction extends SaveDialogAction {
91
92 private static final Logger log = LoggerFactory.getLogger(SaveRoleDialogAction.class);
93 private final SecuritySupport securitySupport;
94
95 public SaveRoleDialogAction(SaveDialogActionDefinition definition, Item item, EditorValidator validator, EditorCallback callback, SecuritySupport securitySupport) {
96 super(definition, item, validator, callback);
97 this.securitySupport = securitySupport;
98 }
99
100
101
102
103 @Deprecated
104 public SaveRoleDialogAction(SaveDialogActionDefinition definition, Item item, EditorValidator validator, EditorCallback callback) {
105 this(definition, item, validator, callback, Components.getComponent(SecuritySupport.class));
106 }
107
108 @Override
109 public void execute() throws ActionExecutionException {
110 final JcrNodeAdapter nodeAdapter = (JcrNodeAdapter) item;
111
112 if (validateForm() && validateNewRolePermission(nodeAdapter)) {
113 createOrUpdateRole(nodeAdapter);
114 callback.onSuccess(getDefinition().getName());
115 }
116 }
117
118
119
120
121 protected boolean validateForm() {
122 validator.showValidation(false);
123 boolean isValid = validator.isValid();
124 validator.showValidation(!isValid);
125 if (!isValid) {
126 log.info("Validation error(s) occurred. No save performed.");
127 }
128 return isValid;
129 }
130
131 private boolean validateNewRolePermission(JcrNodeAdapter roleItem) throws ActionExecutionException {
132 if (MgnlContext.getUser().hasRole(DEFAULT_SUPERUSER_ROLE)) {
133 return true;
134 }
135
136
137 try {
138 if (roleItem instanceof JcrNewNodeAdapter) {
139 Node parentNode = roleItem.getJcrItem();
140 parentNode.getSession().checkPermission(parentNode.getPath(), Session.ACTION_ADD_NODE);
141 }
142 return true;
143 } catch (AccessControlException | RepositoryException e) {
144 throw new ActionExecutionException(e);
145 }
146 }
147
148 private void createOrUpdateRole(JcrNodeAdapter roleItem) throws ActionExecutionException {
149 try {
150
151 final RoleManager roleManager = securitySupport.getRoleManager();
152 final String newRoleName = Path.getValidatedLabel((String) roleItem.getItemProperty(ModelConstants.JCR_NAME).getValue());
153
154 Node roleNode;
155
156
157 Map<String, AccessControlList<AccessControlList.Entry>> aclsProperties = removeTransientAclProperties(roleItem);
158
159 if (roleItem instanceof JcrNewNodeAdapter) {
160
161 Node parentNode = roleItem.getJcrItem();
162 String parentPath = parentNode.getPath();
163
164 Role role = roleManager.createRole(parentPath, newRoleName);
165 roleNode = parentNode.getNode(role.getName());
166
167 JcrNodeAdapter newRoleItem = convertNewNodeAdapterForUpdating((JcrNewNodeAdapter) roleItem, roleNode, newRoleName);
168 roleNode = newRoleItem.applyChanges();
169
170 try {
171 Field f = roleItem.getClass().getDeclaredField("appliedChanges");
172 f.setAccessible(true);
173 f.setBoolean(roleItem, true);
174 f.setAccessible(false);
175 roleItem.setItemId(newRoleItem.getItemId());
176 } catch (IllegalAccessException | NoSuchFieldException e) {
177 log.warn("Unable to set new JcrItemId for adapter {}", roleItem, e);
178 }
179
180 updateAcls(roleNode, aclsProperties, false);
181
182 } else {
183
184 String existingRoleName = roleItem.getJcrItem().getName();
185 String pathBefore = roleItem.getJcrItem().getPath();
186
187 roleNode = roleItem.applyChanges();
188
189
190 updateAcls(roleNode, aclsProperties, true);
191
192 if (!StringUtils.equals(existingRoleName, newRoleName)) {
193 UsersWorkspaceUtil.updateAcls(roleNode, pathBefore);
194 }
195 }
196
197 roleNode.getSession().save();
198 } catch (final RepositoryException e) {
199 throw new ActionExecutionException(e);
200 }
201 }
202
203 private void updateAcls(Node roleNode, Map<String, AccessControlList<AccessControlList.Entry>> acls, boolean removeOldEntries) throws RepositoryException {
204 for (Entry<String, AccessControlList<AccessControlList.Entry>> aclEntry : acls.entrySet()) {
205 String aclNodeName = aclEntry.getKey();
206 AccessControlList<AccessControlList.Entry> acl = aclEntry.getValue();
207
208 Node aclNode;
209 if (roleNode.hasNode(aclNodeName)) {
210 aclNode = roleNode.getNode(aclNodeName);
211 if (removeOldEntries) {
212
213 for (Node entryNode : NodeUtil.getNodes(aclNode)) {
214 entryNode.remove();
215 }
216 }
217 } else {
218 aclNode = roleNode.addNode(aclNodeName, NodeTypes.ContentNode.NAME);
219 }
220
221 acl.saveEntries(aclNode);
222
223
224 if (!aclNode.hasNodes()) {
225 aclNode.remove();
226 }
227 }
228 }
229
230 private JcrNodeAdapter convertNewNodeAdapterForUpdating(JcrNewNodeAdapter newNodeAdapter, Node node, String newRoleName) throws RepositoryException {
231
232 JcrNodeAdapter adapter = new JcrNodeAdapter(node);
233
234 for (Object propertyId : newNodeAdapter.getItemPropertyIds()) {
235 Property property = adapter.getItemProperty(propertyId);
236 if (property == null) {
237 adapter.addItemProperty(propertyId, newNodeAdapter.getItemProperty(propertyId));
238 } else if (ModelConstants.JCR_NAME.equals(propertyId) && newRoleName != null) {
239 property.setValue(node.getName());
240 } else {
241 property.setValue(newNodeAdapter.getItemProperty(propertyId).getValue());
242 }
243 }
244 return adapter;
245 }
246
247
248
249
250 private Map<String, AccessControlList<AccessControlList.Entry>> removeTransientAclProperties(final JcrNodeAdapter roleItem) {
251
252 Collection<?> propertyIds = ImmutableList.copyOf(roleItem.getItemPropertyIds());
253 Collection<?> aclPropertyIds = Collections2.filter(propertyIds, new Predicate<Object>() {
254 @Override
255 public boolean apply(Object propertyId) {
256 return propertyId instanceof String && ((String) propertyId).startsWith("acl_");
257 }
258 });
259
260 Map<String, AccessControlList<AccessControlList.Entry>> acls = new HashMap<>();
261 for (Object aclPropertyId : aclPropertyIds) {
262 AccessControlList<AccessControlList.Entry> acl = (AccessControlList<AccessControlList.Entry>) roleItem.getItemProperty(aclPropertyId).getValue();
263 acls.put(aclPropertyId.toString(), acl);
264 roleItem.removeItemProperty(aclPropertyId);
265 }
266
267 return acls;
268 }
269 }