1 package org
.argeo
.cms
.internal
.kernel
;
3 import static javax
.jcr
.Property
.JCR_DESCRIPTION
;
4 import static javax
.jcr
.Property
.JCR_TITLE
;
5 import static org
.argeo
.cms
.CmsTypes
.CMS_IMAGE
;
7 import java
.io
.IOException
;
8 import java
.io
.PrintWriter
;
9 import java
.security
.PrivilegedExceptionAction
;
10 import java
.security
.cert
.X509Certificate
;
11 import java
.util
.Enumeration
;
13 import javax
.jcr
.Node
;
14 import javax
.jcr
.NodeIterator
;
15 import javax
.jcr
.Repository
;
16 import javax
.jcr
.RepositoryException
;
17 import javax
.jcr
.Session
;
18 import javax
.security
.auth
.Subject
;
19 import javax
.servlet
.FilterChain
;
20 import javax
.servlet
.ServletException
;
21 import javax
.servlet
.http
.HttpServlet
;
22 import javax
.servlet
.http
.HttpServletRequest
;
23 import javax
.servlet
.http
.HttpServletResponse
;
24 import javax
.servlet
.http
.HttpSession
;
26 import org
.apache
.commons
.logging
.Log
;
27 import org
.apache
.commons
.logging
.LogFactory
;
28 import org
.argeo
.cms
.CmsException
;
29 import org
.argeo
.cms
.util
.CmsUtils
;
30 import org
.argeo
.jcr
.ArgeoJcrConstants
;
31 import org
.argeo
.jcr
.JcrUtils
;
32 import org
.eclipse
.equinox
.http
.servlet
.ExtendedHttpService
;
35 * Intercepts and enriches http access, mainly focusing on security and
38 class NodeHttp
implements KernelConstants
, ArgeoJcrConstants
{
39 private final static Log log
= LogFactory
.getLog(NodeHttp
.class);
42 // private final RootFilter rootFilter;
44 // private final DoSFilter dosFilter;
45 // private final QoSFilter qosFilter;
47 private Repository repository
;
49 NodeHttp(ExtendedHttpService httpService
, NodeRepository node
) {
50 this.repository
= node
;
51 // rootFilter = new RootFilter();
52 // dosFilter = new CustomDosFilter();
53 // qosFilter = new QoSFilter();
56 httpService
.registerServlet("/!", new LinkServlet(repository
),
58 } catch (Exception e
) {
59 throw new CmsException("Cannot register filters", e
);
63 public void destroy() {
66 static class LinkServlet
extends HttpServlet
{
67 private static final long serialVersionUID
= 3749990143146845708L;
68 private final Repository repository
;
70 public LinkServlet(Repository repository
) {
71 this.repository
= repository
;
75 protected void service(HttpServletRequest request
,
76 HttpServletResponse response
) throws ServletException
,
78 String path
= request
.getPathInfo();
79 String userAgent
= request
.getHeader("User-Agent").toLowerCase();
80 boolean isBot
= false;
81 boolean isCompatibleBrowser
= false;
82 if (userAgent
.contains("bot") || userAgent
.contains("facebook")
83 || userAgent
.contains("twitter")) {
85 } else if (userAgent
.contains("webkit")
86 || userAgent
.contains("gecko")
87 || userAgent
.contains("firefox")
88 || userAgent
.contains("msie")
89 || userAgent
.contains("chrome")
90 || userAgent
.contains("chromium")
91 || userAgent
.contains("opera")
92 || userAgent
.contains("browser")) {
93 isCompatibleBrowser
= true;
97 log
.warn("# BOT " + request
.getHeader("User-Agent"));
98 canonicalAnswer(request
, response
, path
);
102 if (isCompatibleBrowser
&& log
.isTraceEnabled())
103 log
.trace("# BWS " + request
.getHeader("User-Agent"));
104 redirectTo(response
, "/#" + path
);
107 private void redirectTo(HttpServletResponse response
, String location
) {
108 response
.setHeader("Location", location
);
109 response
.setStatus(HttpServletResponse
.SC_FOUND
);
112 // private boolean canonicalAnswerNeededBy(HttpServletRequest request) {
113 // String userAgent = request.getHeader("User-Agent").toLowerCase();
114 // return userAgent.startsWith("facebookexternalhit/");
117 /** For bots which don't understand RWT. */
118 private void canonicalAnswer(HttpServletRequest request
,
119 HttpServletResponse response
, String path
) {
120 Session session
= null;
122 PrintWriter writer
= response
.getWriter();
123 session
= Subject
.doAs(KernelUtils
.anonymousLogin(),
124 new PrivilegedExceptionAction
<Session
>() {
127 public Session
run() throws Exception
{
128 return repository
.login();
132 Node node
= session
.getNode(path
);
133 String title
= node
.hasProperty(JCR_TITLE
) ? node
.getProperty(
134 JCR_TITLE
).getString() : node
.getName();
135 String desc
= node
.hasProperty(JCR_DESCRIPTION
) ? node
136 .getProperty(JCR_DESCRIPTION
).getString() : null;
137 String url
= CmsUtils
.getCanonicalUrl(node
, request
);
138 String imgUrl
= null;
139 for (NodeIterator it
= node
.getNodes(); it
.hasNext();) {
140 Node child
= it
.nextNode();
141 if (child
.isNodeType(CMS_IMAGE
))
142 imgUrl
= CmsUtils
.getDataUrl(child
, request
);
144 StringBuilder buf
= new StringBuilder();
145 buf
.append("<html>");
146 buf
.append("<head>");
147 writeMeta(buf
, "og:title", title
);
148 writeMeta(buf
, "og:type", "website");
149 writeMeta(buf
, "og:url", url
);
151 writeMeta(buf
, "og:description", desc
);
153 writeMeta(buf
, "og:image", imgUrl
);
154 buf
.append("</head>");
155 buf
.append("<body>");
157 "<p><b>!! This page is meant for indexing robots, not for real people,"
158 + " visit <a href='/#").append(path
)
159 .append("'>").append(title
)
160 .append("</a> instead.</b></p>");
161 writeCanonical(buf
, node
);
162 buf
.append("</body>");
163 buf
.append("</html>");
164 writer
.print(buf
.toString());
166 } catch (Exception e
) {
167 throw new CmsException("Cannot write canonical answer", e
);
169 JcrUtils
.logoutQuietly(session
);
173 private void writeMeta(StringBuilder buf
, String tag
, String value
) {
174 buf
.append("<meta property='").append(tag
).append("' content='")
175 .append(value
).append("'/>");
178 private void writeCanonical(StringBuilder buf
, Node node
)
179 throws RepositoryException
{
181 if (node
.hasProperty(JCR_TITLE
))
183 .append(node
.getProperty(JCR_TITLE
).getString())
185 if (node
.hasProperty(JCR_DESCRIPTION
))
187 .append(node
.getProperty(JCR_DESCRIPTION
).getString())
189 NodeIterator children
= node
.getNodes();
190 while (children
.hasNext()) {
191 writeCanonical(buf
, children
.nextNode());
193 buf
.append("</div>");
197 /** Intercepts all requests. Authenticates. */
198 class RootFilter
extends HttpFilter
{
201 public void doFilter(HttpSession httpSession
,
202 HttpServletRequest request
, HttpServletResponse response
,
203 FilterChain filterChain
) throws IOException
, ServletException
{
204 if (log
.isTraceEnabled()) {
205 log
.trace(request
.getRequestURL().append(
206 request
.getQueryString() != null ?
"?"
207 + request
.getQueryString() : ""));
211 String servletPath
= request
.getServletPath();
213 // client certificate
214 X509Certificate clientCert
= extractCertificate(request
);
215 if (clientCert
!= null) {
217 // if (log.isDebugEnabled())
218 // log.debug(clientCert.getSubjectX500Principal().getName());
222 if (servletPath
.startsWith(PATH_DATA
)) {
223 filterChain
.doFilter(request
, response
);
227 // skip /ui (workbench) for the time being
228 if (servletPath
.startsWith(PATH_WORKBENCH
)) {
229 filterChain
.doFilter(request
, response
);
233 // redirect long RWT paths to anchor
234 String path
= request
.getRequestURI().substring(
235 servletPath
.length());
236 int pathLength
= path
.length();
237 if (pathLength
!= 0 && (path
.charAt(0) == '/')
238 && !servletPath
.endsWith("rwt-resources")
239 && !path
.startsWith(KernelConstants
.PATH_WORKBENCH
)
240 && path
.lastIndexOf('/') != 0) {
241 String newLocation
= request
.getServletPath() + "#" + path
;
242 response
.setHeader("Location", newLocation
);
243 response
.setStatus(HttpServletResponse
.SC_FOUND
);
248 filterChain
.doFilter(request
, response
);
252 private void logRequest(HttpServletRequest request
) {
253 log
.debug("contextPath=" + request
.getContextPath());
254 log
.debug("servletPath=" + request
.getServletPath());
255 log
.debug("requestURI=" + request
.getRequestURI());
256 log
.debug("queryString=" + request
.getQueryString());
257 StringBuilder buf
= new StringBuilder();
259 Enumeration
<String
> en
= request
.getHeaderNames();
260 while (en
.hasMoreElements()) {
261 String header
= en
.nextElement();
262 Enumeration
<String
> values
= request
.getHeaders(header
);
263 while (values
.hasMoreElements())
264 buf
.append(" " + header
+ ": " + values
.nextElement());
269 Enumeration
<String
> an
= request
.getAttributeNames();
270 while (an
.hasMoreElements()) {
271 String attr
= an
.nextElement();
272 Object value
= request
.getAttribute(attr
);
273 buf
.append(" " + attr
+ ": " + value
);
276 log
.debug("\n" + buf
);
279 private X509Certificate
extractCertificate(HttpServletRequest req
) {
280 X509Certificate
[] certs
= (X509Certificate
[]) req
281 .getAttribute("javax.servlet.request.X509Certificate");
282 if (null != certs
&& certs
.length
> 0) {
288 // class CustomDosFilter extends DoSFilter {
290 // protected String extractUserId(ServletRequest request) {
291 // HttpSession httpSession = ((HttpServletRequest) request)
293 // if (isSessionAuthenticated(httpSession)) {
294 // String userId = ((SecurityContext) httpSession
295 // .getAttribute(SPRING_SECURITY_CONTEXT_KEY))
296 // .getAuthentication().getName();
299 // return super.extractUserId(request);