Revision: 10139
Author:   jlaba...@google.com
Date:     Wed May  4 11:30:38 2011
Log: Adds user authentication to MobileWebApp. Tasks are now specific to each user. Authentication is done using a Filter, which was copied almost verbatim from the expenses sample. If a user is not logged in, they are forwarded to the login page, then redirected back to their last place in the app. If a user tries to access a task that they do not own, an error messge says that the task cannot be found.

You can view a demo at http://jlabanca-testing.appspot.com

This change also replaces the auto generated datastore index file with a manual datastore index file. There aren't any indexes yet, but we may add them eventually, and using an auto-generated file is asking for trouble because its easy enough to miss a possible index.

Review at http://gwt-code-reviews.appspot.com/1432801

Review by: rj...@google.com
http://code.google.com/p/google-web-toolkit/source/detail?r=10139

Added:
 /trunk/samples/mobilewebapp/src/main/com/google/gwt/sample/gaerequest
/trunk/samples/mobilewebapp/src/main/com/google/gwt/sample/gaerequest/GaeRequest.gwt.xml /trunk/samples/mobilewebapp/src/main/com/google/gwt/sample/gaerequest/client /trunk/samples/mobilewebapp/src/main/com/google/gwt/sample/gaerequest/client/GaeAuthRequestTransport.java /trunk/samples/mobilewebapp/src/main/com/google/gwt/sample/gaerequest/client/GaeAuthenticationFailureEvent.java /trunk/samples/mobilewebapp/src/main/com/google/gwt/sample/gaerequest/client/ReloadOnAuthenticationFailure.java /trunk/samples/mobilewebapp/src/main/com/google/gwt/sample/gaerequest/server /trunk/samples/mobilewebapp/src/main/com/google/gwt/sample/gaerequest/server/GaeAuthFilter.java /trunk/samples/mobilewebapp/src/main/com/google/gwt/sample/gaerequest/shared /trunk/samples/mobilewebapp/src/main/com/google/gwt/sample/gaerequest/shared/GaeHelper.java /trunk/samples/mobilewebapp/src/main/com/google/gwt/sample/mobilewebapp/server/domain/UserServiceWrapper.java
 /trunk/samples/mobilewebapp/war/WEB-INF/datastore-indexes.xml
Modified:
/trunk/samples/mobilewebapp/src/main/com/google/gwt/sample/mobilewebapp/MobileWebApp.gwt.xml /trunk/samples/mobilewebapp/src/main/com/google/gwt/sample/mobilewebapp/client/ClientFactoryImpl.java /trunk/samples/mobilewebapp/src/main/com/google/gwt/sample/mobilewebapp/client/MobileWebApp.java /trunk/samples/mobilewebapp/src/main/com/google/gwt/sample/mobilewebapp/server/domain/Task.java
 /trunk/samples/mobilewebapp/war/WEB-INF/web.xml

=======================================
--- /dev/null
+++ /trunk/samples/mobilewebapp/src/main/com/google/gwt/sample/gaerequest/GaeRequest.gwt.xml Wed May 4 11:30:38 2011
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 0.0.999//EN" "http://google-web-toolkit.googlecode.com/svn/tags/0.0.999/distro-source/core/src/gwt-module.dtd";>
+<module>
+  <inherits name='com.google.web.bindery.requestfactory.RequestFactory'/>
+
+  <source path='client'/>
+  <source path='shared'/>
+</module>
=======================================
--- /dev/null
+++ /trunk/samples/mobilewebapp/src/main/com/google/gwt/sample/gaerequest/client/GaeAuthRequestTransport.java Wed May 4 11:30:38 2011
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.sample.gaerequest.client;
+
+import com.google.gwt.event.shared.EventBus;
+import com.google.gwt.http.client.Request;
+import com.google.gwt.http.client.RequestCallback;
+import com.google.gwt.http.client.Response;
+import com.google.gwt.sample.gaerequest.shared.GaeHelper;
+import com.google.gwt.user.client.Window;
+import com.google.web.bindery.requestfactory.gwt.client.DefaultRequestTransport;
+import com.google.web.bindery.requestfactory.shared.ServerFailure;
+
+/**
+ * Extends DefaultRequestTransport to handle the authentication failures
+ * reported by {@link com.google.gwt.sample.gaerequest.server.GaeAuthFilter}.
+ */
+public class GaeAuthRequestTransport extends DefaultRequestTransport {
+  private final EventBus eventBus;
+
+  public GaeAuthRequestTransport(EventBus eventBus) {
+    this.eventBus = eventBus;
+  }
+
+  @Override
+ protected RequestCallback createRequestCallback(final TransportReceiver receiver) { + final RequestCallback superCallback = super.createRequestCallback(receiver);
+
+    return new RequestCallback() {
+      public void onError(Request request, Throwable exception) {
+        superCallback.onError(request, exception);
+      }
+
+      public void onResponseReceived(Request request, Response response) {
+        /*
+ * The GaeAuthFailure filter responds with Response.SC_UNAUTHORIZED and
+         * adds a "login" url header if the user is not logged in. When we
+ * receive that combo, post an event so that the app can handle things
+         * as it sees fit.
+         */
+
+        if (Response.SC_UNAUTHORIZED == response.getStatusCode()) {
+          String loginUrl = response.getHeader("login");
+          if (loginUrl != null) {
+            // Replace the redirect url placeholder with the current url.
+ loginUrl = loginUrl.replace(GaeHelper.REDIRECT_URL_TOKEN, Window.Location.getHref());
+
+            /*
+             * Hand the receiver a non-fatal callback, so that *
+ * com.google.web.bindery.requestfactory.shared.Receiver will not
+             * post a runtime exception.
+             */
+            receiver
+ .onTransportFailure(new ServerFailure("Unauthenticated user", null, null, false)); + eventBus.fireEvent(new GaeAuthenticationFailureEvent(loginUrl));
+            return;
+          }
+        }
+        superCallback.onResponseReceived(request, response);
+      }
+    };
+  }
+}
=======================================
--- /dev/null
+++ /trunk/samples/mobilewebapp/src/main/com/google/gwt/sample/gaerequest/client/GaeAuthenticationFailureEvent.java Wed May 4 11:30:38 2011
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.sample.gaerequest.client;
+
+import com.google.gwt.event.shared.EventBus;
+import com.google.gwt.event.shared.EventHandler;
+import com.google.gwt.event.shared.GwtEvent;
+import com.google.gwt.event.shared.HandlerRegistration;
+
+/**
+ * An event posted when an authentication failure is detected.
+ */
+public class GaeAuthenticationFailureEvent extends GwtEvent<GaeAuthenticationFailureEvent.Handler> {
+
+  /**
+   * Implemented by handlers of this type of event.
+   */
+  public interface Handler extends EventHandler {
+    /**
+     * Called when a {@link GaeAuthenticationFailureEvent} is fired.
+     *
+     * @param requestEvent a {@link GaeAuthenticationFailureEvent} instance
+     */
+    void onAuthFailure(GaeAuthenticationFailureEvent requestEvent);
+  }
+
+  private static final Type<Handler> TYPE = new Type<Handler>();
+
+  /**
+   * Register a {@link GaeAuthenticationFailureEvent.Handler} on an
+   * {@link EventBus}.
+   *
+   * @param eventBus the {@link EventBus}
+   * @param handler a {@link GaeAuthenticationFailureEvent.Handler}
+   * @return a {@link HandlerRegistration} instance
+   */
+  public static HandlerRegistration register(EventBus eventBus,
+      GaeAuthenticationFailureEvent.Handler handler) {
+    return eventBus.addHandler(TYPE, handler);
+  }
+
+  private final String loginUrl;
+
+  /**
+   * Constructs a new @{link RequestEvent}.
+   *
+   * @param loginUrl the url used to login
+   */
+  public GaeAuthenticationFailureEvent(String loginUrl) {
+    this.loginUrl = loginUrl;
+  }
+
+  @Override
+  public GwtEvent.Type<Handler> getAssociatedType() {
+    return TYPE;
+  }
+
+  /**
+   * Returns the URL the user can visit to reauthenticate.
+   *
+   * @return a {@link com.google.gwt.http.client.Response} instance
+   */
+  public String getLoginUrl() {
+    return loginUrl;
+  }
+
+  @Override
+  protected void dispatch(Handler handler) {
+    handler.onAuthFailure(this);
+  }
+}
=======================================
--- /dev/null
+++ /trunk/samples/mobilewebapp/src/main/com/google/gwt/sample/gaerequest/client/ReloadOnAuthenticationFailure.java Wed May 4 11:30:38 2011
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.gwt.sample.gaerequest.client;
+
+import com.google.gwt.event.shared.EventBus;
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.user.client.Window.Location;
+
+/**
+ * A minimal auth failure handler which takes the user a login page.
+ */
+public class ReloadOnAuthenticationFailure implements GaeAuthenticationFailureEvent.Handler {
+
+  public void onAuthFailure(GaeAuthenticationFailureEvent requestEvent) {
+    Location.replace(requestEvent.getLoginUrl());
+  }
+
+  public HandlerRegistration register(EventBus eventBus) {
+    return GaeAuthenticationFailureEvent.register(eventBus, this);
+  }
+}
=======================================
--- /dev/null
+++ /trunk/samples/mobilewebapp/src/main/com/google/gwt/sample/gaerequest/server/GaeAuthFilter.java Wed May 4 11:30:38 2011
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.sample.gaerequest.server;
+
+import com.google.appengine.api.users.UserService;
+import com.google.appengine.api.users.UserServiceFactory;
+import com.google.gwt.sample.gaerequest.shared.GaeHelper;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * A servlet filter that handles basic GAE user authentication.
+ */
+public class GaeAuthFilter implements Filter {
+
+  public void destroy() {
+  }
+
+ public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
+      FilterChain filterChain) throws IOException, ServletException {
+    UserService userService = UserServiceFactory.getUserService();
+    HttpServletRequest request = (HttpServletRequest) servletRequest;
+    HttpServletResponse response = (HttpServletResponse) servletResponse;
+
+    if (!userService.isUserLoggedIn()) {
+ response.setHeader("login", userService.createLoginURL(GaeHelper.REDIRECT_URL_TOKEN));
+      response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
+      return;
+    }
+
+    filterChain.doFilter(request, response);
+  }
+
+  public void init(FilterConfig config) {
+  }
+}
=======================================
--- /dev/null
+++ /trunk/samples/mobilewebapp/src/main/com/google/gwt/sample/gaerequest/shared/GaeHelper.java Wed May 4 11:30:38 2011
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.sample.gaerequest.shared;
+
+/**
+ * Helper class for sharing values on the server and client.
+ */
+public interface GaeHelper {
+
+  /**
+ * The placeholder token added to the login URL. The client replaces the token
+   * with the current href, which only the client knows.
+   */
+ /* Prefixed with http:// to ensure that GAE doesn't automatically prefix it. */
+  String REDIRECT_URL_TOKEN = "http%3A%2F%2FREDIRECTURL";
+}
=======================================
--- /dev/null
+++ /trunk/samples/mobilewebapp/src/main/com/google/gwt/sample/mobilewebapp/server/domain/UserServiceWrapper.java Wed May 4 11:30:38 2011
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.sample.mobilewebapp.server.domain;
+
+import com.google.appengine.api.users.User;
+import com.google.appengine.api.users.UserServiceFactory;
+
+/**
+ * Wrapper around the {@link com.google.appengine.api.users.UserService} GAE
+ * api.
+ */
+public class UserServiceWrapper {
+
+  public static UserServiceWrapper instance = new UserServiceWrapper();
+
+  public static UserServiceWrapper get() {
+    return instance;
+  }
+
+  private UserServiceWrapper() {
+  }
+
+  /**
+   * Get the current user.
+ * {@link com.google.gwt.sample.gaerequest.server.GaeAuthFilter} should ensure
+   * that the user is logged in, but we take precautions here as well.
+   *
+   * @return the current user
+   * @throws RuntimeException if the user is not logged in
+   */
+  public User getCurrentUser() {
+ User currentUser = UserServiceFactory.getUserService().getCurrentUser();
+    if (currentUser == null) {
+      throw new RuntimeException("User not logged in");
+    }
+    return currentUser;
+  }
+
+  /**
+   * Get the unique ID of the current user.
+   *
+   * @return a unique ID
+   * @throws RuntimeException if the user is not logged in
+   */
+  public String getCurrentUserId() {
+    return getCurrentUser().getUserId();
+  }
+}
=======================================
--- /dev/null
+++ /trunk/samples/mobilewebapp/war/WEB-INF/datastore-indexes.xml Wed May 4 11:30:38 2011
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<datastore-indexes
+  autoGenerate="false">
+
+</datastore-indexes>
+
=======================================
--- /trunk/samples/mobilewebapp/src/main/com/google/gwt/sample/mobilewebapp/MobileWebApp.gwt.xml Wed May 4 09:12:17 2011 +++ /trunk/samples/mobilewebapp/src/main/com/google/gwt/sample/mobilewebapp/MobileWebApp.gwt.xml Wed May 4 11:30:38 2011
@@ -8,6 +8,7 @@
   <inherits name='com.google.gwt.editor.Editor'/>
   <inherits name='com.google.gwt.sample.mobilewebapp.FormFactor'/>
   <inherits name='com.google.web.bindery.requestfactory.RequestFactory'/>
+  <inherits name='com.google.gwt.sample.gaerequest.GaeRequest'/>

   <!-- Specify the app entry point class.                         -->
<entry-point class='com.google.gwt.sample.mobilewebapp.client.MobileWebApp'/>
=======================================
--- /trunk/samples/mobilewebapp/src/main/com/google/gwt/sample/mobilewebapp/client/ClientFactoryImpl.java Tue May 3 10:43:13 2011 +++ /trunk/samples/mobilewebapp/src/main/com/google/gwt/sample/mobilewebapp/client/ClientFactoryImpl.java Wed May 4 11:30:38 2011
@@ -19,14 +19,15 @@
 import com.google.gwt.event.shared.EventBus;
 import com.google.gwt.event.shared.SimpleEventBus;
 import com.google.gwt.place.shared.PlaceController;
-import com.google.web.bindery.requestfactory.gwt.client.DefaultRequestTransport;
-import com.google.gwt.storage.client.Storage;
-import com.google.gwt.user.client.Window;
+import com.google.gwt.sample.gaerequest.client.GaeAuthRequestTransport;
 import com.google.gwt.sample.mobilewebapp.client.activity.TaskEditView;
 import com.google.gwt.sample.mobilewebapp.client.activity.TaskListView;
import com.google.gwt.sample.mobilewebapp.client.desktop.DesktopTaskEditView; import com.google.gwt.sample.mobilewebapp.client.desktop.DesktopTaskListView; import com.google.gwt.sample.mobilewebapp.client.desktop.MobileWebAppShellDesktop;
+import com.google.gwt.storage.client.Storage;
+import com.google.gwt.user.client.Window;
+import com.google.web.bindery.requestfactory.shared.RequestTransport;

 /**
* Default implementation of {@link ClientFactory}. Used by desktop version.
@@ -47,7 +48,7 @@
   private TaskListView taskListView;

   public ClientFactoryImpl() {
- DefaultRequestTransport requestTransport = new DefaultRequestTransport(); + RequestTransport requestTransport = new GaeAuthRequestTransport(eventBus);
     requestFactory = GWT.create(MobileWebAppRequestFactory.class);
     requestFactory.initialize(eventBus, requestTransport);

=======================================
--- /trunk/samples/mobilewebapp/src/main/com/google/gwt/sample/mobilewebapp/client/MobileWebApp.java Tue May 3 10:43:13 2011 +++ /trunk/samples/mobilewebapp/src/main/com/google/gwt/sample/mobilewebapp/client/MobileWebApp.java Wed May 4 11:30:38 2011
@@ -26,6 +26,7 @@
 import com.google.gwt.place.shared.PlaceHistoryHandler;
 import com.google.gwt.place.shared.PlaceHistoryHandler.DefaultHistorian;
 import com.google.gwt.place.shared.PlaceHistoryHandler.Historian;
+import com.google.gwt.sample.gaerequest.client.ReloadOnAuthenticationFailure; import com.google.gwt.sample.mobilewebapp.client.activity.AppActivityMapper; import com.google.gwt.sample.mobilewebapp.client.place.AppPlaceHistoryMapper;
 import com.google.gwt.sample.mobilewebapp.client.place.TaskListPlace;
@@ -53,6 +54,9 @@
     EventBus eventBus = clientFactory.getEventBus();
     PlaceController placeController = clientFactory.getPlaceController();

+    // Check for Authentication failures or mismatches
+    new ReloadOnAuthenticationFailure().register(eventBus);
+
     /*
* Add the main application shell to the RootLayoutPanel. The shell includes
      * the header bar at the top and a content area.
=======================================
--- /trunk/samples/mobilewebapp/src/main/com/google/gwt/sample/mobilewebapp/server/domain/Task.java Tue May 3 10:43:13 2011 +++ /trunk/samples/mobilewebapp/src/main/com/google/gwt/sample/mobilewebapp/server/domain/Task.java Wed May 4 11:30:38 2011
@@ -36,13 +36,14 @@
 public class Task {

   /**
-   * Find all tasks.
+   * Find all tasks for the current user.
    */
   @SuppressWarnings("unchecked")
   public static List<Task> findAllTasks() {
     EntityManager em = entityManager();
     try {
-      Query query = em.createQuery("select o from Task o");
+ Query query = em.createQuery("select o from Task o where o.userId=:userId"); + query.setParameter("userId", UserServiceWrapper.get().getCurrentUserId());
       List<Task> list = query.getResultList();

       /*
@@ -67,7 +68,7 @@
   }

   /**
-   * Find a {@link Task} by id.
+   * Find a {@link Task} by id for the current user.
    *
    * @param id the {@link Task} id
    * @return the associated {@link Task}, or null if not found
@@ -76,10 +77,14 @@
     if (id == null) {
       return null;
     }
+
     EntityManager em = entityManager();
     try {
       Task task = em.find(Task.class, id);
-      return task;
+ if (task != null && UserServiceWrapper.get().getCurrentUserId().equals(task.userId)) {
+        return task;
+      }
+      return null;
     } finally {
       em.close();
     }
@@ -135,6 +140,11 @@
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private Long id;

+  /**
+   * The unique ID of the user who owns this task.
+   */
+  private String userId;
+
   @Version
   @Column(name = "version")
   private Integer version;
@@ -187,7 +197,16 @@
   public void persist() {
     EntityManager em = entityManager();
     try {
-      em.persist(this);
+      // Set the user id if this is a new task.
+      String curUserId = UserServiceWrapper.get().getCurrentUserId();
+      if (userId == null) {
+        userId = curUserId;
+      }
+
+      // Verify the current user owns the task before updating it.
+      if (curUserId.equals(userId)) {
+        em.persist(this);
+      }
     } finally {
       em.close();
     }
@@ -200,7 +219,11 @@
     EntityManager em = entityManager();
     try {
       Task task = em.find(Task.class, this.id);
-      em.remove(task);
+
+      // Verify the current user owns the task before removing it.
+ if (UserServiceWrapper.get().getCurrentUserId().equals(task.userId)) {
+        em.remove(task);
+      }
     } finally {
       em.close();
     }
=======================================
--- /trunk/samples/mobilewebapp/war/WEB-INF/web.xml     Wed May  4 09:12:17 2011
+++ /trunk/samples/mobilewebapp/war/WEB-INF/web.xml     Wed May  4 11:30:38 2011
@@ -5,6 +5,24 @@

 <web-app>

+  <!--
+ Require login. We only require login for the MobileWebApp.html page (instead of *.html) because + we do not want to require login for the compiled GWT javascript files, which also end in .html.
+  -->
+  <security-constraint>
+    <display-name>
+      Redirect to the login page if needed before showing
+      the host html page.
+    </display-name>
+    <web-resource-collection>
+      <web-resource-name>Login required</web-resource-name>
+      <url-pattern>/MobileWebApp.html</url-pattern>
+    </web-resource-collection>
+    <auth-constraint>
+      <role-name>*</role-name>
+    </auth-constraint>
+  </security-constraint>
+
   <!-- RequestFactory -->
   <servlet>
     <servlet-name>requestFactoryServlet</servlet-name>
@@ -15,7 +33,20 @@
     <servlet-name>requestFactoryServlet</servlet-name>
     <url-pattern>/gwtRequest</url-pattern>
   </servlet-mapping>
-
+
+  <filter>
+    <filter-name>GaeAuthFilter</filter-name>
+    <description>
+      This filter demonstrates making GAE authentication
+      services visible to a RequestFactory client.
+    </description>
+ <filter-class>com.google.gwt.sample.gaerequest.server.GaeAuthFilter</filter-class>
+  </filter>
+  <filter-mapping>
+    <filter-name>GaeAuthFilter</filter-name>
+    <url-pattern>/gwtRequest/*</url-pattern>
+  </filter-mapping>
+
   <!-- Default page to serve -->
   <welcome-file-list>
     <welcome-file>MobileWebApp.html</welcome-file>

--
http://groups.google.com/group/Google-Web-Toolkit-Contributors

Reply via email to