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.filters;
35
36 import info.magnolia.cms.beans.runtime.File;
37 import info.magnolia.cms.beans.runtime.FileProperties;
38 import info.magnolia.cms.core.AggregationState;
39 import info.magnolia.cms.core.version.VersionManager;
40 import info.magnolia.cms.core.version.VersionedNode;
41 import info.magnolia.context.WebContext;
42 import info.magnolia.jcr.util.NodeNameHelper;
43 import info.magnolia.jcr.util.NodeTypes;
44 import info.magnolia.objectfactory.Components;
45
46 import java.io.IOException;
47
48 import javax.inject.Inject;
49 import javax.inject.Provider;
50 import javax.jcr.AccessDeniedException;
51 import javax.jcr.Node;
52 import javax.jcr.PathNotFoundException;
53 import javax.jcr.RepositoryException;
54 import javax.jcr.Session;
55 import javax.jcr.version.VersionException;
56 import javax.jcr.version.VersionHistory;
57 import javax.servlet.FilterChain;
58 import javax.servlet.ServletException;
59 import javax.servlet.http.HttpServletRequest;
60 import javax.servlet.http.HttpServletResponse;
61
62 import org.apache.commons.lang3.StringUtils;
63 import org.apache.jackrabbit.JcrConstants;
64 import org.slf4j.Logger;
65 import org.slf4j.LoggerFactory;
66
67
68
69
70
71 public class AggregatorFilter extends AbstractMgnlFilter {
72 private static final Logger log = LoggerFactory.getLogger(AggregatorFilter.class);
73
74 private final String VERSION_NUMBER = "mgnlVersion";
75
76 private final Provider<WebContext> contextProvider;
77
78 private final VersionManager versionManager;
79
80 private final NodeNameHelper nodeNameHelper;
81
82 @Inject
83 public AggregatorFilter(Provider<WebContext> contextProvider, VersionManager versionManager, NodeNameHelper nodeNameHelper) {
84 this.contextProvider = contextProvider;
85 this.versionManager = versionManager;
86 this.nodeNameHelper = nodeNameHelper;
87 }
88
89
90
91
92 @Deprecated
93 public AggregatorFilter(Provider<WebContext> contextProvider, VersionManager versionManager) {
94 this(contextProvider, versionManager, Components.getComponent(NodeNameHelper.class));
95 }
96
97
98
99
100 @Deprecated
101 public AggregatorFilter() {
102 this(() -> Components.getComponent(WebContext.class), Components.getComponent(VersionManager.class), Components.getComponent(NodeNameHelper.class));
103 }
104
105 @Override
106 public void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
107
108 boolean success;
109 try {
110 success = collect();
111 } catch (AccessDeniedException e) {
112
113 log.debug(e.getMessage(), e);
114 if (!response.isCommitted()) {
115 response.setStatus(HttpServletResponse.SC_FORBIDDEN);
116 }
117
118 return;
119 } catch (RepositoryException e) {
120 log.error(e.getMessage(), e);
121 throw new ServletException(e.getMessage(), e);
122 }
123
124 if (!success) {
125 log.debug("Resource not found, redirecting request for [{}] to 404 URI", request.getRequestURI());
126
127 if (!response.isCommitted()) {
128 response.sendError(HttpServletResponse.SC_NOT_FOUND);
129 } else {
130 log.info("Unable to redirect to 404 page, response is already committed. URI was {}", request.getRequestURI());
131 }
132
133 return;
134 }
135 chain.doFilter(request, response);
136 }
137
138
139
140
141 protected boolean collect() throws RepositoryException {
142 final AggregationState aggregationState = contextProvider.get().getAggregationState();
143 final String handle = aggregationState.getHandle();
144 final String repository = aggregationState.getRepository();
145
146 final Session session = contextProvider.get().getJCRSession(repository);
147
148 Node requestedPage = null;
149 Node requestedData = null;
150 String templateName;
151
152 if (!isJcrPathValid(handle)) {
153 return false;
154 }
155
156 boolean nodeExists = session.nodeExists(handle);
157
158 if (nodeExists && !hasBinarySubnode(session, handle)) {
159 requestedPage = session.getNode(handle);
160
161
162 final String versionNumber = contextProvider.get().getAttribute(VERSION_NUMBER);
163 if (versionNumber != null) {
164 try {
165 VersionHistory versionHistory = versionManager.getVersionHistory(requestedPage);
166 if (versionHistory != null) {
167 requestedPage = new VersionedNode(versionHistory.getVersion(versionNumber), requestedPage);
168 }
169 } catch (VersionException e) {
170 log.warn("The version '{}' of the node '{}' doesn't exists, rendering current state.", versionNumber, requestedPage, e);
171 } catch (RepositoryException re) {
172 log.debug(re.getMessage(), re);
173 log.error("Unable to get versioned state {} of {}, rendering current state.", versionNumber, handle);
174 }
175 }
176
177 try {
178 templateName = NodeTypes.Renderable.getTemplate(requestedPage);
179 } catch (RepositoryException e) {
180 templateName = null;
181 }
182
183 if (StringUtils.isBlank(templateName)) {
184 log.error("No template configured for page [{}].", requestedPage);
185 }
186 } else {
187 if (nodeExists) {
188 requestedData = session.getNode(handle);
189 } else {
190
191 int lastIndexOfSlash = handle.lastIndexOf("/");
192
193 if (lastIndexOfSlash > 0) {
194
195 final String handleToUse = StringUtils.substringBeforeLast(handle, "/");
196
197 try {
198 requestedData = session.getNode(handleToUse);
199 aggregationState.setHandle(handleToUse);
200
201
202
203
204 } catch (PathNotFoundException e) {
205
206 return false;
207 } catch (RepositoryException e) {
208 log.debug(e.getMessage(), e);
209 return false;
210 }
211 }
212 }
213
214 if (requestedData != null) {
215 templateName = requestedData.hasProperty(FileProperties.PROPERTY_TEMPLATE) ? requestedData.getProperty(FileProperties.PROPERTY_TEMPLATE).getString() : StringUtils.EMPTY;
216 } else {
217 return false;
218 }
219 }
220
221
222 if (requestedPage != null) {
223 aggregationState.setMainContentNode(requestedPage);
224 aggregationState.setCurrentContentNode(requestedPage);
225 }
226 if (requestedData != null && isBinary(requestedData)) {
227 File file = new File(requestedData);
228 aggregationState.setFile(file);
229 }
230
231 aggregationState.setTemplateName(templateName);
232
233 return true;
234 }
235
236 private boolean isBinary(Node requestedData) throws RepositoryException {
237 return requestedData.isNodeType(NodeTypes.Resource.NAME) ||
238 (requestedData.hasProperty("jcr:frozenPrimaryType") && requestedData.getProperty("jcr:frozenPrimaryType").getValue().getString().equals(NodeTypes.Resource.NAME));
239 }
240
241 private boolean hasBinarySubnode(Session session, String nodePath) throws RepositoryException {
242 return session.itemExists(nodePath + (nodePath.endsWith("/") ? "" : "/") + JcrConstants.JCR_DATA);
243 }
244
245
246
247
248
249
250
251 private boolean isJcrPathValid(String handle) {
252 if (StringUtils.isBlank(handle) || StringUtils.equals(handle, "/")) {
253
254 return false;
255 }
256
257 String[] jcrNames = handle.replaceFirst("/", "").split("/");
258
259 for (String jcrName : jcrNames) {
260 if (!nodeNameHelper.isValidName(jcrName)) {
261 return false;
262 }
263 }
264 return true;
265 }
266
267 }