Google Cloud Endpoints - apiKeyRequired not working when endpoint has trailing slash - google-app-engine

I have an endpoint in Google Cloud Endpoints Frameworks for App Engine (Java). The endpoint is restricted to require an API key like this:
#ApiMethod(name = "echo", path = "echo", apiKeyRequired = AnnotationBoolean.TRUE, httpMethod = ApiMethod.HttpMethod.GET)
Which is working. However if I add a trailing slash when making the call, the endpoint returns data without an api key requirement.
I have tried to restrict api access globally in the api definition, like this:
#Api(
name = "myapi",
version = "v1",
apiKeyRequired = AnnotationBoolean.TRUE,
This however does not seem to work. I have regenerated the openapi.json and redeployed both the openapi.js and the app engine app, and the endpoint is still accessible if it has a trailing slash, but not without.
Does anyone know how I can prevent this? Any insight is much appreciated.

I was unable to solve this within Google Endpoints, so I utilized tuckey's urlrewrite to remove the trailing slashes in a filter
web.xml
<filter>
<filter-name>UrlRewriteFilter</filter-name>
<filter-class>org.tuckey.web.filters.urlrewrite.UrlRewriteFilter</filter-class>
<init-param>
<param-name>confPath</param-name>
<param-value>/WEB-INF/urlrewrite.xml</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>UrlRewriteFilter</filter-name>
<url-pattern>/_ah/api/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>
urlrewrite.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE urlrewrite PUBLIC "-//tuckey.org//DTD UrlRewrite 3.1//EN" "http://www.tuckey.org/res/dtds/urlrewrite3.1.dtd">
<urlrewrite>
<rule match-type="regex">
<note>Remove trailing slash</note>
<from>^(.*)/$</from>
<to type="redirect">$1</to>
</rule>
</urlrewrite>
more info:
http://www.tuckey.org/urlrewrite/manual/4.0/index.html
Note: as of yet, it is not redirecting properly. I'm continuing to work on that, and will post updates, but now at least I'm getting a 404 for the version with the trailing slash, rather than the response data without api key, which satisfies my security needs at the moment

Related

Mapping two servlets in web.xml where one URL pattern is substring of the other

I have a web application where I would like to tie a JSP to address http://host:port/status and a servlet to addresses like http://host:port/status/.... Is this possible? According to this article it should be possible ("container prefers an exact path match over a wildcard path match") at least for some containers and the Java Servlet Specification contains similar examples (albeit without wildcard, on p. 12-123 in April 2013 version), but if I try the following in web.xml it appears as if the JSP is never called and all requests (also to http://host:port/status) are routed to the servlet. My JSP and servlet are hosted on Google App Engine.
<servlet>
<servlet-name>Status</servlet-name>
<jsp-file>/Status.jsp</jsp-file>
</servlet>
<servlet-mapping>
<servlet-name>Status</servlet-name>
<url-pattern>/status</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>StatusUpload</servlet-name>
<servlet-class>com.example.StatusUploadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>StatusUpload</servlet-name>
<url-pattern>/status/*</url-pattern>
</servlet-mapping>
Instead of mapping the same url to two different JSPs/Servlets in web.xml you can use a URL Rewriting filter like Tuckey UrlRewriteFilter which uses a configuration file urlrewrite.xml which would also be placed in WEB-INF. It uses regex in the rules.
These two rules should do what you want:
<rule>
<from>^/status$</from>
<to>/Status.jsp</to>
</rule>
<rule>
<from>^/status/(.*)$</from>
<to>/StatusUpload/?param=$1</to>
</rule>
Then in WEB-INF you would not map the JSP anymore but would map the Servlet to StatusUpload. When the user goes to /status/postfix the URL Rewriting filter will forward to the servlet (with the postfix part passed as a parameter) in the backend without the address the user sees in the address bar changing.

Servlet-Filter is not honoured for welcome file

I am using a Filter do generate dynamicly content to be visible for webcrawlers (https://developers.google.com/webmasters/ajax-crawling/docs/specification). This filter is working fine if the incoming url contains a path (http://www.unclestock.com/app.jsp#!s=GOOG). If the incoming url contains just my domain (and a fragment), say http://www.unclestock.com#!s=GOOG, the welcome file (app.jsp) is returned, but the filter is not honnoured.
My web.xml contains the following filter map:
<filter-mapping>
<filter-name>crawler</filter-name>
<url-pattern>/app.jsp</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>app.jsp</welcome-file>
</welcome-file-list>
I have tried to use an index.html welcome file instead, which redirects to app.jsp. The filter is then executed. However, this does not solve my problem: A client side redirect is not followed by the crawlers (which is the idea), and with server side redirect, I would loose my url fragment (which I also need).
Do you see any alternative solution?
I'm using Google Appengine.
I solved it by using a welcome servlet which does a RequestDispatcher forward. Note that the dispatcher FORWARD must be added to the filter-mapping in order to have the filter working during the foward.
web.xml:
<filter-mapping>
<filter-name>crawler</filter-name>
<url-pattern>*.jsp</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
<welcome-file-list>
<welcome-file>welcome</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>welcome</servlet-name>
<servlet-class>Welcome</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>welcome</servlet-name>
<url-pattern>/welcome</url-pattern>
</servlet-mapping>
Welcome.java:
public class Welcome extends RemoteServiceServlet {
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
RequestDispatcher rd = req.getRequestDispatcher("app.jsp");
rd.forward(req, resp);
}
}

redirect through web.xml in google-app-engine

After migrating to the new HRD store, I want to redirect requests to my old application to the new HRD application. I know I should let the Google migration tool make an alias, but since I migrate an intermediate copy of my app (because of the nightmares that migrating the database causes) that is not an option.
My plan was to use a servlet that does a HTTP 301 (HttpServletResponse.SC_MOVED_PERMANENTLY) redirect, and use a servlet-mapping with /* in web.xml.
This works locally, but on the real app-engine, it doesn't. It seems like there is no URL pattern that the app-engine correctly recognizes. So far I have:
<servlet-mapping>
<servlet-name>RedirectToHRD</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>RedirectToHRD</servlet-name>
<url-pattern>/*</url-pattern>
<url-pattern>/*/*</url-pattern>
<url-pattern>/*/*/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>RedirectToHRD</servlet-name>
<url-pattern>*.jsp</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>RedirectToHRD</servlet-name>
<url-pattern>index.jsp</url-pattern>
</servlet-mapping>
I know it looks crazy, but I was desperate. Only '/' and 'index.jsp' get redirected, through the RedirectToHRD servlet. For other pages (JSP or anything else) this does not work. The log file just happily indicates that the pages get served.
Can anyone tell me what is happening?
Edit:
I did what Peter Knego kindly suggested below, and made a filter. Now my web.xml has:
<filter-mapping>
<filter-name>RedirectToHRDFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
This still does not work on the 'real' appengine, and like the earlier attempt it does work locally.
My filter has the following method:
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain filterChain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)req;
HttpServletResponse response = (HttpServletResponse)resp;
response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);
String redirectURL = "http://fit20app-hrd.appspot.com"+request.getRequestURI();
if (request.getQueryString() != null) {
redirectURL += "?"+request.getQueryString();
}
response.setHeader("Location", redirectURL);
}
Now I am thinking that this filter may be broken, even though it works locally. On Google's servers it works for / and /index.jsp, but not for anything else.
This is solved, see comment below.
You can not have multiple <url-pattern> inside a <servlet-mapping>. Instead create multiple <servlet-mapping> with each having one <url-pattern> element.
Also, since you are trying to redirect everything you should use a servlet filter instead.

web.xml configuration for restlet filter

Is there any special configuration needed in web.xml for using org.restlet.routing.Filter? I have edited it as(same as javax.servlet.Filter):
<filter>
<filter-name>Filter</filter-name>
<filter-class>*myfilterclassname*</filter-class>
</filter>
<filter-mapping>
<filter-name>Filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Its not working & the response is "Source not available". Any more changes needed in web.xml?
A RESTLET filter is not a ServletFilter and cannot be used directly in a web.xml.
Filter are part of the routers package in RESTLET which are specialized Restlets that can have other Restlets attached to them and that can automatically delegate calls based on a URI template.

My controller/servlet with "/" mapping isn't overriding "Hello App Engine!"

I'm working with Spring MVC on google app engine and even though I've gotten a basic hello world example working, I can't get my servlet to show up when I use the request mapping of "/". Even when I specify "/" as my request mapping in my controller, I keep getting the "Hello App Engine!" page with a link to my project. I've already pulled the welcome-file declaration out of my web xml.
Basically...
package my.package.for.spring.stuff.controllers;
import ....;
// It doesn't seem to make a difference if
// I have this reqeustmapping or not...
#Controller
public class MainController {
// If I change mapping to "/main" and then go to
// localhost:8888/main then everything works as expected
#RequestMapping("/")
public String HelloWorld() {
return "MyView";
}
}
is still going to the "Hello App Engine!" page. Also, here is my web.xml...
<?xml version="1.0" encoding="utf-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<servlet>
<servlet-name>SpringAppEngine</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringAppEngine</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
And then here is my spring xml...
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="my.package.for.spring.stuff" />
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/views/main/" p:suffix=".jsp" />
</beans>
Why is the app engine generated welcome file still showing up even though I'm declaring the root mapping in my controller? I know my setup should be right because when I change the requestmapping, everything works as expected.
The servlet 3.0 spec says:
A string containing only the ’/’ character indicates the "default"
servlet of the application.
And it also says:
By default all applications will have index.htm(l) and index.jsp in
the list of welcome-file-list. The descriptor may to be used to
override these default settings.
So, I guess that the container considers that the implicit index.html welcome file is an exact match, which takes precedence over the default servlet mapped to /.
The solution is to delete the index.html file or, probably, to define an explicit empty welcome file list in the descriptor.
Apparently pulling the index.html mapping out of the web.xml isn't enough, you actually have to delete the index.html. If someone can post an answer explaining why I'll still upvote and accept.
This post helped me and I think I can expand on why removing the file fixes the problem. According the GAE, any file in the war directory (except JSPs and anything in WEB-INF) gets a mapping to that file name. This implicit mapping appears to supercede any servlet rules in web.xml:
https://developers.google.com/appengine/docs/java/gettingstarted/staticfiles
Once you remove or rename index.html, your servlet rule for "/" is used instead of the implicit mapping for the static file.

Resources