All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.cedarsolutions.wiring.gwt.rpc.XsrfRpcProxyCreator Maven / Gradle / Ivy

There is a newer version: 5.8.4
Show newest version
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *
 *              C E D A R
 *          S O L U T I O N S       "Software done right."
 *           S O F T W A R E
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *
 * Copyright (c) 2013-2014 Kenneth J. Pronovici.
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the Apache License, Version 2.0.
 * See LICENSE for more information about the licensing terms.
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *
 * Author   : Kenneth J. Pronovici 
 * Language : Java 6
 * Project  : Common Java Functionality
 *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
package com.cedarsolutions.wiring.gwt.rpc;

import com.cedarsolutions.client.gwt.rpc.proxy.XsrfRpcProxy;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.JParameter;
import com.google.gwt.core.ext.typeinfo.JType;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.user.client.rpc.impl.RemoteServiceProxy;
import com.google.gwt.user.rebind.SourceWriter;
import com.google.gwt.user.rebind.rpc.ProxyCreator;
import com.google.gwt.user.rebind.rpc.SerializableTypeOracle;
import com.google.gwt.user.server.rpc.NoXsrfProtect;
import com.google.gwt.user.server.rpc.XsrfProtect;

/**
 * Create a customized remote service proxy that knows how to make CSRF/XSRF-protected requests.
 *
 * 

* When you request an RPC interface with GWT.create(MyRemoteService.class), * GWT normally does some magic to implement an asynchronous proxy over your * remote service interface. Below, we generate customized code that knows how * to call the CSRF/XSRF token service before making the actual RPC call. That * way, RPC clients don't need to know anything about the way the service is * actually invoked — it's all controlled by annotations. *

* *

* The customization is conceptually very simple: for every relevant remote procedure * call, we want to insert a different RPC call that happens first. The first RPC * call invokes the standard GWT XSRF/CSRF token service and gets a cryptographic * token that's needed by the real RPC call. Once we have the token, the real RPC * call is made as usual. The implementation isn't quite that simple, but hopefully * it won't be too brittle as time goes on. *

* * @see GWT RPC XSRF protection * @author Kenneth J. Pronovici */ public class XsrfRpcProxyCreator extends ProxyCreator { /** Configured type. */ protected JClassType type; /** Number of methods that have been XSRF protected so far. */ protected int handled; /** Instantiates a new proxy creator. */ public XsrfRpcProxyCreator(JClassType type) { super(type); this.type = type; this.handled = 0; } /** Return our customized proxy supertype. */ @Override protected Class getProxySupertype() { return XsrfRpcProxy.class; } /** Get the configured type. */ public JClassType getType() { return this.type; } /** Generates the client's asynchronous proxy method and related utility code. */ @Override protected void generateProxyMethod(SourceWriter w, SerializableTypeOracle serializableTypeOracle, TypeOracle typeOracle, JMethod syncMethod, JMethod asyncMethod) { if (!isMethodXsrfProtected(syncMethod)) { super.generateProxyMethod(w, serializableTypeOracle, typeOracle, syncMethod, asyncMethod); } else { String sequence = String.valueOf(++this.handled); generateInterfaceRpcMethod(w, asyncMethod, sequence); generateClientRpcMethod(w, serializableTypeOracle, typeOracle, syncMethod, asyncMethod); generateTokenCallback(w, asyncMethod, sequence); } } /** * Checks if specified method is XSRF protected at the class or method level. * *

* This is roughly equivalent to com.google.gwt.user.server.Util.isMethodXsrfProtected(), * but it doesn't deal with method return types. No application-level RPC calls should * return RPC tokens, anyway. *

* * @param method Method to check * * @return True if the method is protected, false otherwise. */ protected static boolean isMethodXsrfProtected(JMethod method) { if (method.isAnnotationPresent(XsrfProtect.class)) { // Method is protected if it's annotated with @XsrfProtect return true; } else if (method.isAnnotationPresent(NoXsrfProtect.class)) { // Method is not protected if it's annotated as @NoXsrfProtect return false; } else { // Otherwise, method is protected if its class is annotated with @XsrfProtect return method.getEnclosingType().isAnnotationPresent(XsrfProtect.class); } } /** * Create the method that matches the RPC's interface. * *

* Given an RPC method "createExchange", we want to create a proxy * method that looks like this: *

* *
     *    public void createExchange(java.lang.String name, com.google.gwt.user.client.rpc.AsyncCallback callback) {
     *       AbstractTokenCallback tokenCallback = new _TokenCallback_createExchange(name, callback);
     *       com.cedarsolutions.client.gwt.rpc.IXsrfTokenRpcAsync xsrfTokenRpc = getXsrfTokenRpc();
     *       xsrfTokenRpc.generateXsrfToken(tokenCallback);
     *    }
     * 
* *

* The proxy method calls out to the token service before invoking * the actual RPC in the callback's onSuccess() method. *

* *

* The sequence parameter is incorporated into the generated callback * name so we don't have problems if a single RPC overloads a method name. *

*/ protected void generateInterfaceRpcMethod(SourceWriter w, JMethod asyncMethod, String sequence) { String asyncReturnType = asyncMethod.getReturnType().getErasedType().getQualifiedSourceName(); String asyncMethodName = asyncMethod.getName(); JParameter[] asyncParams = asyncMethod.getParameters(); String callbackClassName = "_TokenCallback_" + asyncMethodName + "_" + sequence; w.println(); w.print("public "); w.print(asyncReturnType); w.print(" "); w.print(asyncMethodName + "("); writeMethodParameters(w, asyncParams); w.println(") {"); w.indent(); w.print("AbstractTokenCallback tokenCallback = new "); w.print(callbackClassName); w.print("("); writeMethodArguments(w, asyncParams); w.println(");"); w.println("com.cedarsolutions.client.gwt.rpc.IXsrfTokenRpcAsync xsrfTokenRpc = getXsrfTokenRpc();"); w.println("xsrfTokenRpc.generateXsrfToken(tokenCallback);"); w.outdent(); w.println("}"); } /** * Create the method that invokes the client service. * *

* The goal here is to use exactly the same implementation as the parent * class (to hopefully future-proof this implementation as Google enhances * the RPC mechanism), but to change the method name so that it can be * called as expected from the token callback. *

*/ protected void generateClientRpcMethod(SourceWriter w, SerializableTypeOracle serializableTypeOracle, TypeOracle typeOracle, JMethod syncMethod, JMethod asyncMethod) { JType asyncReturnType = asyncMethod.getReturnType().getErasedType(); String origSignature = "public " + asyncReturnType.getQualifiedSourceName() + " " + asyncMethod.getName(); String newSignature = "public " + asyncReturnType.getQualifiedSourceName() + " _realRpcMethod_" + asyncMethod.getName(); SourceWriter sourceWriter = new StringSourceWriter(); super.generateProxyMethod(sourceWriter, serializableTypeOracle, typeOracle, syncMethod, asyncMethod); String method = sourceWriter.toString().replaceFirst(origSignature, newSignature); w.print(method); } /** * Generate the token callback class referenced by the interface method. * *

* Given an RPC method "createExchange", we want to create a token callback * that looks like this: *

* *
     *    public class _TokenCallback_createExchange extends AbstractTokenCallback {
     *       private java.lang.String name;
     *       private com.google.gwt.user.client.rpc.AsyncCallback callback;
     *
     *       public _TokenCallback_createExchange(java.lang.String name, com.google.gwt.user.client.rpc.AsyncCallback callback) {
     *          super(callback);
     *          this.name = name;
     *          this.callback = callback;
     *       }
     *
     *       @Override
     *       public void invokeRpcMethod() {
     *          _realRpcMethod_createExchange(name, callback);
     *       }
     *    }
     * 
* *

* The sequence parameter is incorporated into the generated callback * name so we don't have problems if a single RPC overloads a method name. *

*/ protected void generateTokenCallback(SourceWriter w, JMethod asyncMethod, String sequence) { String asyncMethodName = asyncMethod.getName(); JParameter[] asyncParams = asyncMethod.getParameters(); JParameter callbackParam = asyncParams[asyncParams.length - 1]; // callback is assumed to be the last parameter String realMethodName = "_realRpcMethod_" + asyncMethodName; String callbackClassName = "_TokenCallback_" + asyncMethodName + "_" + sequence; w.println(); w.print("public class "); w.print(callbackClassName); w.println(" extends AbstractTokenCallback {"); w.indent(); writePrivateVariables(w, asyncParams); w.println(); w.print("public "); w.print(callbackClassName); w.print("("); writeMethodParameters(w, asyncParams); w.println(") {"); w.indent(); w.print("super("); w.print(callbackParam.getName()); w.println(");"); writeVariableAssignments(w, asyncParams); w.outdent(); w.println("}"); w.println(); w.println("@Override"); w.println("public void invokeRpcMethod() {"); w.indent(); w.print(realMethodName); w.print("("); writeMethodArguments(w, asyncParams); w.println(");"); w.outdent(); w.println("}"); w.outdent(); w.println("}"); } /** Write a list of parameters as if they are in a method declaration. */ protected void writeMethodParameters(SourceWriter w, JParameter[] params) { boolean needsComma = false; for (JParameter param : params) { if (needsComma) { w.print(", "); } else { needsComma = true; } w.print(param.getType().getErasedType().getQualifiedSourceName()); w.print(" "); w.print(param.getName()); } } /** Write a list of parameters as if they are being passed into a method call. */ protected void writeMethodArguments(SourceWriter w, JParameter[] params) { boolean needsComma = false; for (JParameter param : params) { if (needsComma) { w.print(", "); } else { needsComma = true; } w.print(param.getName()); } } /** Write a list of parameters as if they are private variables in a class. */ protected void writePrivateVariables(SourceWriter w, JParameter[] params) { for (JParameter param : params) { w.print("private "); w.print(param.getType().getErasedType().getQualifiedSourceName()); w.print(" "); w.print(param.getName()); w.println(";"); } } /** Write a list of parameters as if they are variable assignments. */ protected void writeVariableAssignments(SourceWriter w, JParameter[] params) { for (JParameter param : params) { w.print("this."); w.print(param.getName()); w.print(" = "); w.print(param.getName()); w.println(";"); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy