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.cms.security;
35
36 import static info.magnolia.cms.security.SecurityConstants.NODE_ROLES;
37 import info.magnolia.cms.core.Path;
38 import info.magnolia.cms.security.auth.ACL;
39 import info.magnolia.cms.util.SimpleUrlPattern;
40 import info.magnolia.cms.util.UrlPattern;
41 import info.magnolia.context.MgnlContext;
42 import info.magnolia.jcr.util.NodeTypes;
43 import info.magnolia.module.InstallContextImpl;
44 import info.magnolia.module.InstallStatus;
45 import info.magnolia.objectfactory.Components;
46 import info.magnolia.repository.RepositoryConstants;
47
48 import java.util.ArrayList;
49 import java.util.Collection;
50 import java.util.Collections;
51 import java.util.HashMap;
52 import java.util.LinkedList;
53 import java.util.List;
54 import java.util.Map;
55
56 import javax.jcr.ItemNotFoundException;
57 import javax.jcr.Node;
58 import javax.jcr.NodeIterator;
59 import javax.jcr.PathNotFoundException;
60 import javax.jcr.Property;
61 import javax.jcr.PropertyIterator;
62 import javax.jcr.RepositoryException;
63 import javax.jcr.Session;
64 import javax.jcr.ValueFormatException;
65 import javax.jcr.query.Query;
66
67 import org.apache.commons.lang.StringUtils;
68 import org.apache.jackrabbit.commons.iterator.FilteringNodeIterator;
69 import org.apache.jackrabbit.commons.predicate.NodeTypePredicate;
70 import org.slf4j.Logger;
71 import org.slf4j.LoggerFactory;
72
73
74
75
76 public abstract class RepositoryBackedSecurityManager {
77
78 private static final Logger log = LoggerFactory.getLogger(RepositoryBackedSecurityManager.class);
79
80 public boolean hasAny(final String principalName, final String resourceName, final String resourceTypeName) {
81 long start = System.currentTimeMillis();
82 try {
83 final String sessionName = (StringUtils.equalsIgnoreCase(resourceTypeName, NODE_ROLES)) ? RepositoryConstants.USER_ROLES : RepositoryConstants.USER_GROUPS;
84
85
86
87 final Collection<String> groupsOrRoles = MgnlContext.doInSystemContext(new JCRSessionOp<Collection<String>>(getRepositoryName()) {
88
89 @Override
90 public Collection<String> exec(Session session) throws RepositoryException {
91 List<String> list = new ArrayList<String>();
92 Node principal = findPrincipalNode(principalName, session);
93 if(principal == null) {
94 log.debug("No User '"+principalName+"' found in repository");
95 return list;
96 }
97 Node groupsOrRoles = principal.getNode(resourceTypeName);
98
99 for (PropertyIterator props = groupsOrRoles.getProperties(); props.hasNext();) {
100 Property property = props.nextProperty();
101 try {
102
103 list.add(property.getString());
104 } catch (ItemNotFoundException e) {
105 log.debug("Role [{}] does not exist in the {} repository", resourceName, resourceTypeName);
106 } catch (IllegalArgumentException e) {
107 log.debug("{} has invalid value", property.getPath());
108 } catch (ValueFormatException e) {
109 log.debug("{} has invalid value", property.getPath());
110 }
111 }
112 return list;
113 }
114 });
115
116
117
118 return MgnlContext.doInSystemContext(new JCRSessionOp<Boolean>(sessionName) {
119
120 @Override
121 public Boolean exec(Session session) throws RepositoryException {
122 for (String groupOrRole : groupsOrRoles) {
123
124 try {
125 if (session.getNodeByIdentifier(groupOrRole).getName().equalsIgnoreCase(resourceName)) {
126 return true;
127 }
128 } catch (RepositoryException e) {
129 log.debug("Role [{}] does not exist in the ROLES repository", resourceName);
130 }
131 }
132 return false;
133 }});
134
135 } catch (RepositoryException e) {
136
137 log.debug(e.getMessage(), e);
138 } finally {
139 log.debug("checked {} for {} in {}ms.", new Object[] {resourceName, resourceTypeName, (System.currentTimeMillis() - start)});
140 }
141 return false;
142 }
143
144
145
146
147
148
149
150
151 protected void add(final String principalName, final String resourceName, final String resourceTypeName) throws PrincipalNotFoundException {
152 try {
153 final String nodeID = getLinkedResourceId(resourceName, resourceTypeName);
154
155 if (!hasAny(principalName, resourceName, resourceTypeName)) {
156 Session session = MgnlContext.getJCRSession(getRepositoryName());
157 Node principalNode = findPrincipalNode(principalName, session);
158 if (principalNode == null) {
159 throw new PrincipalNotFoundException("Principal " + principalName + " of type " + resourceTypeName + " was not found.");
160 }
161 if (!principalNode.hasNode(resourceTypeName)) {
162 principalNode.addNode(resourceTypeName, NodeTypes.ContentNode.NAME);
163 }
164 Node node = principalNode.getNode(resourceTypeName);
165
166
167 String newName = Path.getUniqueLabel(session, node.getPath(), "0");
168 node.setProperty(newName, nodeID);
169 session.save();
170 }
171 }
172 catch (RepositoryException e) {
173 log.error("failed to add " + resourceTypeName + " "+ resourceName + " to [" + principalName + "]", e);
174 }
175 }
176
177 private String getLinkedResourceId(final String resourceName, final String resourceTypeName) throws AccessDeniedException {
178 final String nodeID;
179 if (StringUtils.equalsIgnoreCase(resourceTypeName, NODE_ROLES)) {
180 Role role = SecuritySupport.Factory.getInstance().getRoleManager().getRole(resourceName);
181 if (role == null) {
182 log.warn("Invalid role requested: {}", resourceName);
183 nodeID = null;
184 }
185 else {
186 nodeID = role.getId();
187 }
188 }
189 else {
190 Group group = SecuritySupport.Factory.getInstance().getGroupManager().getGroup(resourceName);
191 if (group == null) {
192 log.warn("Invalid group requested: {}", resourceName);
193 nodeID = null;
194 }
195 else {
196 nodeID = group.getId();
197 }
198 }
199 return nodeID;
200 }
201
202 protected String getResourceName(final String resourceId) {
203 try {
204 return MgnlContext.getJCRSession(getRepositoryName()).getNodeByIdentifier(resourceId).getName();
205 } catch (ItemNotFoundException e) {
206
207 return null;
208 }
209 catch (RepositoryException e) {
210 log.error(e.getMessage(), e);
211 }
212 return null;
213 }
214
215
216
217
218
219
220
221
222 protected void remove(final String principalName, final String resourceName, final String resourceTypeName) throws PrincipalNotFoundException {
223 try {
224 final String nodeID = getLinkedResourceId(resourceName, resourceTypeName);
225
226 if (hasAny(principalName, resourceName, resourceTypeName)) {
227 Session session = MgnlContext.getJCRSession(getRepositoryName());
228 Node principalNode = findPrincipalNode(principalName, session);
229 if (!principalNode.hasNode(resourceTypeName)) {
230 throw new PrincipalNotFoundException("Principal " + principalName + " of type " + resourceTypeName + " was not found.");
231 }
232 Node node = principalNode.getNode(resourceTypeName);
233 for (PropertyIterator iter = node.getProperties(); iter.hasNext();) {
234 Property nodeData = iter.nextProperty();
235
236 try {
237 if (nodeData.getString().equals(nodeID)) {
238 nodeData.remove();
239 session.save();
240
241 }
242 } catch (IllegalArgumentException e) {
243 log.debug("{} has invalid value", nodeData.getPath());
244 } catch (ValueFormatException e) {
245 log.debug("{} has invalid value", nodeData.getPath());
246 }
247 }
248 }
249 }
250 catch (RepositoryException e) {
251 log.error("failed to remove " + resourceTypeName + " "+ resourceName + " from [" + principalName + "]", e);
252 }
253 }
254
255 protected abstract String getRepositoryName();
256
257 protected abstract Node findPrincipalNode(String principalName, Session session) throws RepositoryException;
258
259 protected Node findPrincipalNode(String principalName, Session session, String primaryNodeType) throws RepositoryException {
260 return findPrincipalNode(principalName, session, primaryNodeType, null);
261 }
262
263
264
265
266
267
268 protected Node findPrincipalNode(String principalName, Session session, String primaryNodeType, Node startNode) throws RepositoryException {
269 final boolean isInstallationPhase = InstallStatus.inProgress.equals(Components.getComponent(InstallContextImpl.class).getStatus());
270 final long start = System.currentTimeMillis();
271
272 final Node principalNode = isInstallationPhase ? findPrincipalNodeByTraversal(principalName, session, primaryNodeType, startNode) : findPrincipalNodeByQuery(principalName, session, primaryNodeType, startNode);
273 log.debug("Retrieving node took {}ms (isInstallationPhase: {}): path = {}", System.currentTimeMillis() - start, isInstallationPhase, principalNode == null ? "<null>" : principalNode.getPath());
274
275 return principalNode;
276 }
277
278
279
280
281 Node findPrincipalNodeByQuery(String principalName, Session session, String primaryNodeType, Node startNode) throws RepositoryException {
282 final Node root = startNode == null ? session.getRootNode() : startNode;
283
284 final StringBuilder builder = new StringBuilder("select * from [").append(primaryNodeType).append("] where name() = '").append(principalName).append("'");
285
286 if (!"/".equals(root.getPath())) {
287 builder.append(" and isdescendantnode(['").append(root.getPath()).append("'])");
288 }
289
290 final String queryString = builder.toString();
291 log.debug("Executing query \"{}\".", queryString);
292
293 final Query query = session.getWorkspace().getQueryManager().createQuery(queryString, Query.JCR_SQL2);
294 final NodeIterator iterator = query.execute().getNodes();
295 final Node user = iterator.hasNext() ? iterator.nextNode() : null;
296 if (iterator.hasNext()) {
297 log.error("Query found more than one node of type \"{}\" with name \"{}\" under the root path \"{}\".", primaryNodeType, principalName, root.getPath());
298 }
299 return user;
300 }
301
302
303
304
305 Node findPrincipalNodeByTraversal(String principalName, Session session, String primaryNodeType, Node startNode) throws RepositoryException {
306 final Node root = startNode == null ? session.getRootNode() : startNode;
307 log.debug("Traversing to find nodes of type \"{}\" with name \"{}\" under the root path \"{}\".", primaryNodeType, principalName, root.getPath());
308
309 final LinkedList<Node> nodes = new LinkedList<Node>();
310
311 for (NodeIterator iterator = root.getNodes(); iterator.hasNext(); ) {
312 final Node node = iterator.nextNode();
313 if (!node.getName().startsWith(NodeTypes.JCR_PREFIX) && !node.getName().startsWith(NodeTypes.REP_PREFIX)) {
314 nodes.add(node);
315 }
316 }
317
318 Node principalNode = null;
319 while (!nodes.isEmpty()) {
320 final Node node = nodes.removeFirst();
321
322 if (node.getName().equals(principalName) && node.getPrimaryNodeType().getName().equals(primaryNodeType)) {
323 if (principalNode != null) {
324 log.error("Traversal found more than one node of type \"{}\" with name \"{}\" under the root path \"{}\".", primaryNodeType, principalName, root.getPath());
325 break;
326 }
327 principalNode = node;
328 }
329
330 int i = 0;
331 for (NodeIterator iterator = node.getNodes(); iterator.hasNext(); ) {
332 nodes.add(i++, iterator.nextNode());
333 }
334 }
335 return principalNode;
336 }
337
338 public Map<String, ACL> getACLs(final String principalName) {
339 return MgnlContext.doInSystemContext(new SilentSessionOp<Map<String,ACL>>(getRepositoryName()) {
340 @Override
341 public Map<String, ACL> doExec(Session session) throws Throwable {
342 Node node = findPrincipalNode(principalName, session);
343 if(node == null){
344 return Collections.emptyMap();
345 }
346 return getACLs(node);
347 }});
348 }
349
350 protected Map<String, ACL> getACLs(Node node) throws RepositoryException, ValueFormatException, PathNotFoundException {
351 Map<String, ACL> principalList = new HashMap<String, ACL>();
352 NodeIterator it = new FilteringNodeIterator(node.getNodes(), new NodeTypePredicate(NodeTypes.ContentNode.NAME, true));
353 while (it.hasNext()) {
354 Node aclEntry = it.nextNode();
355 if (!aclEntry.getName().startsWith("acl")) {
356 continue;
357 }
358 String name = StringUtils.substringAfter(aclEntry.getName(), "acl_");
359
360 List<Permission> permissionList = new ArrayList<Permission>();
361
362 NodeIterator permissionIterator = new FilteringNodeIterator(aclEntry.getNodes(), new NodeTypePredicate(NodeTypes.ContentNode.NAME, true));
363 while (permissionIterator.hasNext()) {
364 Node map = permissionIterator.nextNode();
365 String path = map.getProperty("path").getString();
366 UrlPattern p = new SimpleUrlPattern(path);
367 Permission permission = new PermissionImpl();
368 permission.setPattern(p);
369 permission.setPermissions(map.getProperty("permissions").getLong());
370 permissionList.add(permission);
371 }
372
373 ACL acl;
374
375
376 if (principalList.containsKey(name)) {
377 acl = principalList.get(name);
378 permissionList.addAll(acl.getList());
379 }
380 acl = new ACLImpl(name, permissionList);
381 principalList.put(name, acl);
382
383 }
384 return principalList;
385 }
386
387 }