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