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
129 } else {
130 roleNode = roleItem.getJcrItem();
131 String existingRoleName = roleNode.getName();
132
133 if (!StringUtils.equals(existingRoleName, newRoleName)) {
134 String pathBefore = roleNode.getPath();
135 NodeUtil.renameNode(roleNode, newRoleName);
136 roleNode.setProperty("name", newRoleName);
137 UsersWorkspaceUtil.updateAcls(roleNode, pathBefore);
138 }
139 }
140
141 roleNode = roleItem.applyChanges();
142
143 if (roleNode.hasNode("acl_userroles/0")) {
144 Node entryNode = roleNode.getNode("acl_userroles/0");
145 entryNode.setProperty(WorkspaceAccessFieldFactory.INTERMEDIARY_FORMAT_PROPERTY_NAME, "true");
146 entryNode.setProperty(WorkspaceAccessFieldFactory.ACCESS_TYPE_PROPERTY_NAME, AccessControlList.ACCESS_TYPE_NODE);
147 entryNode.getSession().save();
148 }
149
150 for (Node aclNode : NodeUtil.getNodes(roleNode)) {
151
152 if (aclNode.getName().startsWith("acl_") && !aclNode.getName().equals("acl_uri")) {
153
154 AccessControlList acl = new AccessControlList();
155
156 for (Node entryNode : NodeUtil.getNodes(aclNode)) {
157
158 if (entryNode.hasProperty(WorkspaceAccessFieldFactory.INTERMEDIARY_FORMAT_PROPERTY_NAME)) {
159 String path = entryNode.getProperty(AccessControlList.PATH_PROPERTY_NAME).getString();
160 long accessType = entryNode.getProperty(WorkspaceAccessFieldFactory.ACCESS_TYPE_PROPERTY_NAME).getLong();
161 long permissions = entryNode.getProperty(AccessControlList.PERMISSIONS_PROPERTY_NAME).getLong();
162
163 path = stripWildcardsFromPath(path);
164
165 if (StringUtils.isNotBlank(path)) {
166 acl.addEntry(new AccessControlList.Entry(permissions, accessType, path));
167 }
168 }
169 entryNode.remove();
170 }
171
172 aclNode.setProperty(WorkspaceAccessFieldFactory.INTERMEDIARY_FORMAT_PROPERTY_NAME, (Value) null);
173 acl.saveEntries(aclNode);
174 }
175 }
176
177 roleNode.getSession().save();
178
179 } catch (final Exception e) {
180 throw new ActionExecutionException(e);
181 }
182 }
183
184 private JcrNodeAdapter convertNewNodeAdapterForUpdating(JcrNewNodeAdapter newNodeAdapter, Node node) throws RepositoryException {
185
186 JcrNodeAdapter adapter = new JcrNodeAdapter(node);
187
188 for (Object propertyId : newNodeAdapter.getItemPropertyIds()) {
189 Property property = adapter.getItemProperty(propertyId);
190 if (property == null) {
191 adapter.addItemProperty(propertyId, newNodeAdapter.getItemProperty(propertyId));
192 } else {
193 property.setValue(newNodeAdapter.getItemProperty(propertyId).getValue());
194 }
195 }
196
197 adapter.getChildren().clear();
198 for (AbstractJcrNodeAdapter child : newNodeAdapter.getChildren().values()) {
199
200 if (child instanceof JcrNewNodeAdapter) {
201 if (node.hasNode(child.getNodeName())) {
202 if (child.getNodeName().startsWith("acl_")) {
203 child = convertNewNodeAdapterForUpdating((JcrNewNodeAdapter) child, node.getNode(child.getNodeName()));
204 adapter.addChild(child);
205 } else {
206 child.setNodeName(getUniqueNodeNameForChild(child.getParent()));
207 child.setParent(adapter);
208 child.setItemId(adapter.getItemId());
209 }
210 } else {
211 child.setParent(adapter);
212 child.setItemId(adapter.getItemId());
213 }
214 }
215 adapter.addChild(child);
216 }
217
218 return adapter;
219 }
220
221 private String getUniqueNodeNameForChild(AbstractJcrNodeAdapter parentItem) throws RepositoryException {
222
223
224
225 Node parentNode = null;
226 if (!(parentItem instanceof JcrNewNodeAdapter)) {
227 parentNode = parentItem.getJcrItem();
228 }
229
230 int newNodeName = 0;
231 while (true) {
232 if (parentItem.getChild(String.valueOf(newNodeName)) != null) {
233 newNodeName++;
234 continue;
235 }
236 if (parentNode != null && parentNode.hasNode(String.valueOf(newNodeName))) {
237 newNodeName++;
238 continue;
239 }
240 break;
241 }
242
243 return String.valueOf(newNodeName);
244 }
245
246
247
248
249
250
251 private boolean validateAccessControlLists(JcrNodeAdapter roleItem) throws ActionExecutionException {
252
253 if (MgnlContext.getUser().hasRole(AccessDefinition.DEFAULT_SUPERUSER_ROLE)) {
254 return true;
255 }
256
257 try {
258 if (roleItem instanceof JcrNewNodeAdapter) {
259 Node parentNode = roleItem.getJcrItem();
260
261
262 parentNode.getSession().checkPermission(parentNode.getPath(), Session.ACTION_ADD_NODE);
263 }
264
265 for (AbstractJcrNodeAdapter aclItem : roleItem.getChildren().values()) {
266
267 String aclNodeName = aclItem.getNodeName();
268
269 if (aclNodeName.startsWith("acl_")) {
270
271 if (aclItem.getItemProperty(WorkspaceAccessFieldFactory.INTERMEDIARY_FORMAT_PROPERTY_NAME) != null) {
272
273
274
275 for (AbstractJcrNodeAdapter entryItem : aclItem.getChildren().values()) {
276
277 String path = (String) entryItem.getItemProperty(AccessControlList.PATH_PROPERTY_NAME).getValue();
278 long accessType = (Long) entryItem.getItemProperty(WorkspaceAccessFieldFactory.ACCESS_TYPE_PROPERTY_NAME).getValue();
279 long permissions = (Long) entryItem.getItemProperty(AccessControlList.PERMISSIONS_PROPERTY_NAME).getValue();
280
281 String workspaceName = StringUtils.replace(aclItem.getNodeName(), "acl_", "");
282
283 if (!isCurrentUserEntitledToGrantRights(workspaceName, path, accessType, permissions)) {
284 throw new ActionExecutionException("Access violation: could not create role. Have you the necessary grants to create such a role?");
285 }
286 }
287 } else if (aclNodeName.equals("acl_uri")) {
288
289
290
291 for (AbstractJcrNodeAdapter entryItem : aclItem.getChildren().values()) {
292
293 String path = (String) entryItem.getItemProperty(AccessControlList.PATH_PROPERTY_NAME).getValue();
294 long permissions = (Long) entryItem.getItemProperty(AccessControlList.PERMISSIONS_PROPERTY_NAME).getValue();
295
296 if (!isCurrentUserEntitledToGrantUriRights(path, permissions)) {
297 throw new ActionExecutionException("Access violation: could not create role. Have you the necessary grants to create such a role?");
298 }
299 }
300 }
301 }
302 }
303
304 return true;
305
306 } catch (AccessControlException e) {
307 throw new ActionExecutionException(e);
308 } catch (RepositoryException e) {
309 throw new ActionExecutionException(e);
310 }
311 }
312
313
314
315
316
317 private boolean isCurrentUserEntitledToGrantRights(String workspaceName, String path, long accessType, long permissions) throws RepositoryException {
318
319 if (MgnlContext.getUser().hasRole(AccessDefinition.DEFAULT_SUPERUSER_ROLE)) {
320 return true;
321 }
322
323
324 if (permissions == Permission.NONE) {
325 permissions = Permission.READ;
326 }
327
328 ACL acl = PrincipalUtil.findAccessControlList(MgnlContext.getSubject(), workspaceName);
329 if (acl == null) {
330 return false;
331 }
332
333 Permission ownPermissions = findBestMatchingPermissions(acl.getList(), stripWildcardsFromPath(path));
334 if (ownPermissions == null) {
335 return false;
336 }
337
338 boolean recursive = (accessType & AccessControlList.ACCESS_TYPE_CHILDREN) != 0;
339
340 if (recursive && !ownPermissions.getPattern().getPatternString().endsWith("/*")) {
341 return false;
342 }
343
344 return granted(ownPermissions, permissions);
345 }
346
347
348
349
350
351 private boolean isCurrentUserEntitledToGrantUriRights(String path, long permissions) throws RepositoryException {
352
353 if (MgnlContext.getUser().hasRole(AccessDefinition.DEFAULT_SUPERUSER_ROLE)) {
354 return true;
355 }
356
357
358 if (permissions == Permission.NONE) {
359 permissions = Permission.READ;
360 }
361
362 ACL acl = PrincipalUtil.findAccessControlList(MgnlContext.getSubject(), "uri");
363 if (acl == null) {
364 return false;
365 }
366
367 boolean recursive = path.endsWith("*");
368
369 Permission ownPermissions = findBestMatchingPermissions(acl.getList(), stripWildcardsFromPath(path));
370 if (ownPermissions == null) {
371 return false;
372 }
373
374 if (recursive && !ownPermissions.getPattern().getPatternString().endsWith("*")) {
375 return false;
376 }
377
378 return granted(ownPermissions, permissions);
379 }
380
381 private String stripWildcardsFromPath(String path) {
382 path = StringUtils.stripEnd(path, "/*");
383 if (StringUtils.isBlank(path)) {
384 path = "/";
385 }
386 return path;
387 }
388
389 private boolean granted(Permission permissionsGranted, long permissionsNeeded) {
390 return (permissionsGranted.getPermissions() & permissionsNeeded) == permissionsNeeded;
391 }
392
393 private Permission findBestMatchingPermissions(List<Permission> permissions, String path) {
394 if (permissions == null) {
395 return null;
396 }
397 Permission bestMatch = null;
398 long permission = 0;
399 int patternLength = 0;
400 ArrayList<Permission> temp = new ArrayList<Permission>();
401 temp.addAll(permissions);
402 for (Permission p : temp) {
403 if (p.match(path)) {
404 int l = p.getPattern().getLength();
405 if (patternLength == l && (permission < p.getPermissions())) {
406 permission = p.getPermissions();
407 bestMatch = p;
408 } else if (patternLength < l) {
409 patternLength = l;
410 permission = p.getPermissions();
411 bestMatch = p;
412 }
413 }
414 }
415 return bestMatch;
416 }
417 }