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 info.magnolia.cms.security.Permission;
37 import info.magnolia.cms.security.PrincipalUtil;
38 import info.magnolia.cms.security.Role;
39 import info.magnolia.cms.security.RoleManager;
40 import info.magnolia.cms.security.SecuritySupport;
41 import info.magnolia.cms.security.auth.ACL;
42 import info.magnolia.cms.security.operations.AccessDefinition;
43 import info.magnolia.context.MgnlContext;
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.dialog.field.WorkspaceAccessFieldFactory;
48 import info.magnolia.security.app.util.UsersWorkspaceUtil;
49 import info.magnolia.ui.admincentral.dialog.action.SaveDialogAction;
50 import info.magnolia.ui.admincentral.dialog.action.SaveDialogActionDefinition;
51 import info.magnolia.ui.api.action.ActionExecutionException;
52 import info.magnolia.ui.form.EditorCallback;
53 import info.magnolia.ui.form.EditorValidator;
54 import info.magnolia.ui.vaadin.integration.jcr.AbstractJcrNodeAdapter;
55 import info.magnolia.ui.vaadin.integration.jcr.JcrNewNodeAdapter;
56 import info.magnolia.ui.vaadin.integration.jcr.JcrNodeAdapter;
57 import info.magnolia.ui.vaadin.integration.jcr.ModelConstants;
58
59 import java.security.AccessControlException;
60 import java.util.ArrayList;
61 import java.util.List;
62
63 import javax.jcr.Node;
64 import javax.jcr.RepositoryException;
65 import javax.jcr.Session;
66 import javax.jcr.Value;
67
68 import org.apache.commons.lang3.StringUtils;
69
70 import com.vaadin.data.Item;
71 import com.vaadin.data.Property;
72
73
74
75
76 public class SaveRoleDialogAction extends SaveDialogAction {
77
78 private final SecuritySupport securitySupport;
79
80 public SaveRoleDialogAction(SaveDialogActionDefinition definition, Item item, EditorValidator validator, EditorCallback callback, SecuritySupport securitySupport) {
81 super(definition, item, validator, callback);
82 this.securitySupport = securitySupport;
83 }
84
85
86
87
88 public SaveRoleDialogAction(SaveDialogActionDefinition definition, Item item, EditorValidator validator, EditorCallback callback) {
89 this(definition, item, validator, callback, Components.getComponent(SecuritySupport.class));
90 }
91
92 @Override
93 public void execute() throws ActionExecutionException {
94
95 final JcrNodeAdapter nodeAdapter = (JcrNodeAdapter) item;
96
97
98 validator.showValidation(true);
99 if (validator.isValid() && validateAccessControlLists(nodeAdapter)) {
100 createOrUpdateRole(nodeAdapter);
101 callback.onSuccess(getDefinition().getName());
102
103 } else {
104
105 }
106 }
107
108 private void createOrUpdateRole(JcrNodeAdapter roleItem) throws ActionExecutionException {
109 try {
110
111 final RoleManager roleManager = securitySupport.getRoleManager();
112
113 final String newRoleName = (String) roleItem.getItemProperty(ModelConstants.JCR_NAME).getValue();
114
115 Role role;
116 Node roleNode;
117 if (roleItem instanceof JcrNewNodeAdapter) {
118
119
120 Node parentNode = roleItem.getJcrItem();
121 String parentPath = parentNode.getPath();
122
123 role = roleManager.createRole(parentPath, newRoleName);
124 roleNode = parentNode.getNode(role.getName());
125
126
127 roleItem = convertNewNodeAdapterForUpdating((JcrNewNodeAdapter) roleItem, roleNode);
128 roleNode = roleItem.applyChanges();
129 } else {
130
131 String existingRoleName = roleItem.getJcrItem().getName();
132 String pathBefore = roleItem.getJcrItem().getPath();
133
134 roleNode = roleItem.applyChanges();
135 if (!StringUtils.equals(existingRoleName, newRoleName)) {
136 NodeUtil.renameNode(roleNode, newRoleName);
137 roleNode.setProperty("name", newRoleName);
138 UsersWorkspaceUtil.updateAcls(roleNode, pathBefore);
139 }
140 }
141
142 if (roleNode.hasNode("acl_userroles/0")) {
143 Node entryNode = roleNode.getNode("acl_userroles/0");
144 entryNode.setProperty(WorkspaceAccessFieldFactory.INTERMEDIARY_FORMAT_PROPERTY_NAME, "true");
145 entryNode.setProperty(WorkspaceAccessFieldFactory.ACCESS_TYPE_PROPERTY_NAME, AccessControlList.ACCESS_TYPE_NODE);
146 entryNode.getSession().save();
147 }
148
149 for (Node aclNode : NodeUtil.getNodes(roleNode)) {
150
151 if (aclNode.getName().startsWith("acl_") && !aclNode.getName().equals("acl_uri")) {
152
153 AccessControlList acl = new AccessControlList();
154
155 for (Node entryNode : NodeUtil.getNodes(aclNode)) {
156
157 if (entryNode.hasProperty(WorkspaceAccessFieldFactory.INTERMEDIARY_FORMAT_PROPERTY_NAME)) {
158 String path = entryNode.getProperty(AccessControlList.PATH_PROPERTY_NAME).getString();
159 long accessType = entryNode.getProperty(WorkspaceAccessFieldFactory.ACCESS_TYPE_PROPERTY_NAME).getLong();
160 long permissions = entryNode.getProperty(AccessControlList.PERMISSIONS_PROPERTY_NAME).getLong();
161
162 path = stripWildcardsFromPath(path);
163
164 if (StringUtils.isNotBlank(path)) {
165 acl.addEntry(new AccessControlList.Entry(permissions, accessType, path));
166 }
167 }
168 entryNode.remove();
169 }
170
171 aclNode.setProperty(WorkspaceAccessFieldFactory.INTERMEDIARY_FORMAT_PROPERTY_NAME, (Value) null);
172 acl.saveEntries(aclNode);
173 }
174 }
175
176 roleNode.getSession().save();
177 } catch (final Exception e) {
178 throw new ActionExecutionException(e);
179 }
180 }
181
182 private JcrNodeAdapter convertNewNodeAdapterForUpdating(JcrNewNodeAdapter newNodeAdapter, Node node) throws RepositoryException {
183
184 JcrNodeAdapter adapter = new JcrNodeAdapter(node);
185
186 for (Object propertyId : newNodeAdapter.getItemPropertyIds()) {
187 Property property = adapter.getItemProperty(propertyId);
188 if (property == null) {
189 adapter.addItemProperty(propertyId, newNodeAdapter.getItemProperty(propertyId));
190 } else {
191 property.setValue(newNodeAdapter.getItemProperty(propertyId).getValue());
192 }
193 }
194
195 adapter.getChildren().clear();
196 for (AbstractJcrNodeAdapter child : newNodeAdapter.getChildren().values()) {
197
198 if (child instanceof JcrNewNodeAdapter) {
199 if (node.hasNode(child.getNodeName())) {
200 if (child.getNodeName().startsWith("acl_")) {
201 child = convertNewNodeAdapterForUpdating((JcrNewNodeAdapter) child, node.getNode(child.getNodeName()));
202 adapter.addChild(child);
203 } else {
204 child.setNodeName(getUniqueNodeNameForChild(child.getParent()));
205 child.setParent(adapter);
206 child.setItemId(adapter.getItemId());
207 }
208 } else {
209 child.setParent(adapter);
210 child.setItemId(adapter.getItemId());
211 }
212 }
213 adapter.addChild(child);
214 }
215
216 return adapter;
217 }
218
219 private String getUniqueNodeNameForChild(AbstractJcrNodeAdapter parentItem) throws RepositoryException {
220
221
222
223 Node parentNode = null;
224 if (!(parentItem instanceof JcrNewNodeAdapter)) {
225 parentNode = parentItem.getJcrItem();
226 }
227
228 int newNodeName = 0;
229 while (true) {
230 if (parentItem.getChild(String.valueOf(newNodeName)) != null) {
231 newNodeName++;
232 continue;
233 }
234 if (parentNode != null && parentNode.hasNode(String.valueOf(newNodeName))) {
235 newNodeName++;
236 continue;
237 }
238 break;
239 }
240
241 return String.valueOf(newNodeName);
242 }
243
244
245
246
247
248
249 private boolean validateAccessControlLists(JcrNodeAdapter roleItem) throws ActionExecutionException {
250
251 if (MgnlContext.getUser().hasRole(AccessDefinition.DEFAULT_SUPERUSER_ROLE)) {
252 return true;
253 }
254
255 try {
256 if (roleItem instanceof JcrNewNodeAdapter) {
257 Node parentNode = roleItem.getJcrItem();
258
259
260 parentNode.getSession().checkPermission(parentNode.getPath(), Session.ACTION_ADD_NODE);
261 }
262
263 for (AbstractJcrNodeAdapter aclItem : roleItem.getChildren().values()) {
264
265 String aclNodeName = aclItem.getNodeName();
266
267 if (aclNodeName.startsWith("acl_")) {
268
269 if (aclItem.getItemProperty(WorkspaceAccessFieldFactory.INTERMEDIARY_FORMAT_PROPERTY_NAME) != null) {
270
271
272
273 for (AbstractJcrNodeAdapter entryItem : aclItem.getChildren().values()) {
274
275 String path = (String) entryItem.getItemProperty(AccessControlList.PATH_PROPERTY_NAME).getValue();
276 long accessType = (Long) entryItem.getItemProperty(WorkspaceAccessFieldFactory.ACCESS_TYPE_PROPERTY_NAME).getValue();
277 long permissions = (Long) entryItem.getItemProperty(AccessControlList.PERMISSIONS_PROPERTY_NAME).getValue();
278
279 String workspaceName = StringUtils.replace(aclItem.getNodeName(), "acl_", "");
280
281 if (!isCurrentUserEntitledToGrantRights(workspaceName, path, accessType, permissions)) {
282 throw new ActionExecutionException("Access violation: could not create role. Have you the necessary grants to create such a role?");
283 }
284 }
285 } else if (aclNodeName.equals("acl_uri")) {
286
287
288
289 for (AbstractJcrNodeAdapter entryItem : aclItem.getChildren().values()) {
290
291 String path = (String) entryItem.getItemProperty(AccessControlList.PATH_PROPERTY_NAME).getValue();
292 long permissions = (Long) entryItem.getItemProperty(AccessControlList.PERMISSIONS_PROPERTY_NAME).getValue();
293
294 if (!isCurrentUserEntitledToGrantUriRights(path, permissions)) {
295 throw new ActionExecutionException("Access violation: could not create role. Have you the necessary grants to create such a role?");
296 }
297 }
298 }
299 }
300 }
301
302 return true;
303
304 } catch (AccessControlException e) {
305 throw new ActionExecutionException(e);
306 } catch (RepositoryException e) {
307 throw new ActionExecutionException(e);
308 }
309 }
310
311
312
313
314
315 private boolean isCurrentUserEntitledToGrantRights(String workspaceName, String path, long accessType, long permissions) throws RepositoryException {
316
317 if (MgnlContext.getUser().hasRole(AccessDefinition.DEFAULT_SUPERUSER_ROLE)) {
318 return true;
319 }
320
321
322 if (permissions == Permission.NONE) {
323 permissions = Permission.READ;
324 }
325
326 ACL acl = PrincipalUtil.findAccessControlList(MgnlContext.getSubject(), workspaceName);
327 if (acl == null) {
328 return false;
329 }
330
331 Permission ownPermissions = findBestMatchingPermissions(acl.getList(), stripWildcardsFromPath(path));
332 if (ownPermissions == null) {
333 return false;
334 }
335
336 boolean recursive = (accessType & AccessControlList.ACCESS_TYPE_CHILDREN) != 0;
337
338 if (recursive && !ownPermissions.getPattern().getPatternString().endsWith("/*")) {
339 return false;
340 }
341
342 return granted(ownPermissions, permissions);
343 }
344
345
346
347
348
349 private boolean isCurrentUserEntitledToGrantUriRights(String path, long permissions) throws RepositoryException {
350
351 if (MgnlContext.getUser().hasRole(AccessDefinition.DEFAULT_SUPERUSER_ROLE)) {
352 return true;
353 }
354
355
356 if (permissions == Permission.NONE) {
357 permissions = Permission.READ;
358 }
359
360 ACL acl = PrincipalUtil.findAccessControlList(MgnlContext.getSubject(), "uri");
361 if (acl == null) {
362 return false;
363 }
364
365 boolean recursive = path.endsWith("*");
366
367 Permission ownPermissions = findBestMatchingPermissions(acl.getList(), stripWildcardsFromPath(path));
368 if (ownPermissions == null) {
369 return false;
370 }
371
372 if (recursive && !ownPermissions.getPattern().getPatternString().endsWith("*")) {
373 return false;
374 }
375
376 return granted(ownPermissions, permissions);
377 }
378
379 private String stripWildcardsFromPath(String path) {
380 path = StringUtils.stripEnd(path, "/*");
381 if (StringUtils.isBlank(path)) {
382 path = "/";
383 }
384 return path;
385 }
386
387 private boolean granted(Permission permissionsGranted, long permissionsNeeded) {
388 return (permissionsGranted.getPermissions() & permissionsNeeded) == permissionsNeeded;
389 }
390
391 private Permission findBestMatchingPermissions(List<Permission> permissions, String path) {
392 if (permissions == null) {
393 return null;
394 }
395 Permission bestMatch = null;
396 long permission = 0;
397 int patternLength = 0;
398 ArrayList<Permission> temp = new ArrayList<Permission>();
399 temp.addAll(permissions);
400 for (Permission p : temp) {
401 if (p.match(path)) {
402 int l = p.getPattern().getLength();
403 if (patternLength == l && (permission < p.getPermissions())) {
404 permission = p.getPermissions();
405 bestMatch = p;
406 } else if (patternLength < l) {
407 patternLength = l;
408 permission = p.getPermissions();
409 bestMatch = p;
410 }
411 }
412 }
413 return bestMatch;
414 }
415 }