View Javadoc
1   /*******************************************************************************
2    * Copyright 2012 Internet2
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   ******************************************************************************/
16  /*
17   * @author mchyzer $Id: WsRestResponseContentType.java,v 1.8 2009-11-20 07:15:38 mchyzer Exp $
18   */
19  package edu.internet2.middleware.grouper.ws.rest.contentType;
20  
21  import java.io.Writer;
22  import java.util.Map;
23  
24  import org.apache.commons.logging.Log;
25  import org.apache.commons.logging.LogFactory;
26  
27  import com.thoughtworks.xstream.XStream;
28  import com.thoughtworks.xstream.io.json.JettisonMappedXmlDriver;
29  import com.thoughtworks.xstream.io.xml.CompactWriter;
30  import com.thoughtworks.xstream.io.xml.XppDriver;
31  import com.thoughtworks.xstream.mapper.MapperWrapper;
32  
33  import edu.internet2.middleware.grouper.misc.GrouperVersion;
34  import edu.internet2.middleware.grouper.util.GrouperUtil;
35  import edu.internet2.middleware.grouper.ws.GrouperWsConfig;
36  import edu.internet2.middleware.grouper.ws.coresoap.WsSubject;
37  import edu.internet2.middleware.grouper.ws.rest.GrouperRestInvalidRequest;
38  import edu.internet2.middleware.grouper.ws.rest.WsRestClassLookup;
39  import edu.internet2.middleware.grouper.ws.rest.json.JsonConverter;
40  import edu.internet2.middleware.grouper.ws.util.GrouperServiceUtils;
41  import edu.internet2.middleware.grouper.ws.util.GrouperWsVersionUtils;
42  
43  /**
44   * possible content types by grouper ws rest
45   */
46  public enum WsRestResponseContentType {
47  
48    /** xml content type */
49    xml {
50  
51      /**
52       * get the content type
53       * @return the http content type
54       */
55      @Override
56      public String getContentType() {
57        return GrouperWsConfig.retrieveConfig().propertyValueString("ws.restResponseContentType.xml", "application/xml");
58      }
59  
60      /**
61       * write a string representation to an outputstream
62       * @param object to write to output
63       * @param writer to write to (e.g. back to http client)
64       */
65      @Override
66      public void writeString(Object object, Writer writer) {
67        XStream xstream = xstream(false);
68        //dont indent
69        xstream.marshal(object, new CompactWriter(writer));
70      }
71  
72      /**
73       * parse a string to an object
74       * @param input
75       * @return the object
76       */
77      @Override
78      public Object parseString(String input) {
79        return WsRestRequestContentType.xml.parseString(input, new StringBuilder());
80      }
81  
82    },
83    /** json content type */
84    json {
85  
86      /**
87       * get the content type
88       * @return the http content type
89       */
90      @Override
91      public String getContentType() {
92        return GrouperWsConfig.retrieveConfig().propertyValueString("ws.restResponseContentType.json", "application/json");
93      }
94  
95      /**
96       * write a string representation to an outputstream
97       * @param object to write to output
98       * @param writer to write to (e.g. back to http client)
99       */
100     @Override
101     public void writeString(Object object, Writer writer) {
102       JsonConverter jsonConverter = WsRestRequestContentType.jsonConverter();
103       try {
104         jsonConverter.convertToJson(object, writer);
105       } catch (RuntimeException re) {
106         LOG.error("Error converting json object with converter: " 
107             + GrouperUtil.className(jsonConverter) + ", " + GrouperUtil.className(object));
108         throw new RuntimeException("Error converting json object with converter: " + GrouperUtil.className(jsonConverter)
109             + ", " + GrouperUtil.className(object), re);
110       }
111     }
112 
113     /**
114      * parse a string to an object
115      * @param input
116      * @return the object
117      */
118     @Override
119     public Object parseString(String input) {
120       return WsRestRequestContentType.json.parseString(input, new StringBuilder());
121     }
122 
123   };
124 
125   /**
126    * write a string representation to an outputstream
127    * @param object to write to output
128    * @param writer to write to (e.g. back to http client)
129    */
130   public abstract void writeString(Object object, Writer writer);
131 
132   /** logger */
133   private static final Log LOG = GrouperUtil.getLog(WsRestResponseContentType.class);
134 
135   /**
136    * parse a string to an object
137    * @param input
138    * @return the object
139    */
140   public abstract Object parseString(String input);
141 
142   /**
143    * get the content type
144    * @return the http content type
145    */
146   public abstract String getContentType();
147 
148   /**
149    * setup an xstream object for input/output
150    * @param isJson driver for json 
151    * @return the xstream object
152    */
153   public static XStream xstream(boolean isJson) {
154     //note new JsonHierarchicalStreamDriver() doesnt work
155     XStream xstream = null;
156     
157     boolean ignoreExtraneousFields = GrouperWsConfig.retrieveConfig().propertyValueBoolean("ws.ignoreExtraneousXmlFieldsRest", false);
158     
159     if (ignoreExtraneousFields) {
160       xstream = new XStream(isJson ? new JettisonMappedXmlDriver() : new XppDriver()) {
161 
162         /**
163          * 
164          * @see edu.internet2.middleware.grouperClientExt.com.thoughtworks.xstream.XStream#wrapMapper(edu.internet2.middleware.grouperClientExt.com.thoughtworks.xstream.mapper.MapperWrapper)
165          */
166         @Override
167         protected MapperWrapper wrapMapper(MapperWrapper next) {
168           return new MapperWrapper(next) {
169   
170             /**
171              * 
172              * @see edu.internet2.middleware.grouperClientExt.com.thoughtworks.xstream.mapper.MapperWrapper#shouldSerializeMember(java.lang.Class, java.lang.String)
173              */
174             @SuppressWarnings("unchecked")
175             @Override
176             public boolean shouldSerializeMember(Class definedIn, String fieldName) {
177               boolean definedInNotObject = definedIn != Object.class;
178               if (definedInNotObject) {
179                 return super.shouldSerializeMember(definedIn, fieldName);
180               }
181 
182               LOG.info("Cant find field: " + fieldName);
183               return false;
184             }
185 
186           };
187         }
188       };
189 
190     } else {
191       xstream = isJson ? new XStream(new JettisonMappedXmlDriver()) : new XStream();
192     }
193 
194     //see if omitting fields
195     String fieldsToOmit = GrouperWsConfig.retrieveConfig().propertyValueString("ws.omitXmlPropertiesRest");
196     if (!GrouperUtil.isBlank(fieldsToOmit)) {
197       String[] fieldsToOmitList = GrouperUtil.splitTrim(fieldsToOmit, ",");
198       for (String fieldToOmit: fieldsToOmitList) {
199         if (!GrouperUtil.isBlank(fieldToOmit)) {
200           try {
201             int dotIndex = fieldToOmit.lastIndexOf('.');
202             String className = fieldToOmit.substring(0, dotIndex);
203             String propertyName = fieldToOmit.substring(dotIndex+1, fieldToOmit.length());
204             Class<?> theClass = GrouperUtil.forName(className);
205             xstream.omitField(theClass, propertyName);
206           } catch (Exception e) {
207             throw new RuntimeException("Problem with ws.omitXmlPropertiesRest: " + fieldsToOmit + ", " + e.getMessage(), e);
208           }
209         }
210       }
211     }
212     GrouperVersion clientVersion = GrouperWsVersionUtils.retrieveCurrentClientVersion();
213     if (clientVersion != null && clientVersion.lessThanArg(GrouperVersion.valueOfIgnoreCase("v1_4_000"))) {
214       xstream.omitField(WsSubject.class, "identifierLookup");
215     }
216     //dont try to get fancy
217     //XStream.setupDefaultSecurity(xstream); //CVE-2013-7285, this is needed for XStream 1.4.10 due to regression bug #108
218     xstream.setMode(XStream.NO_REFERENCES);
219     xstream.autodetectAnnotations(true);
220     Map<String, Class<?>> aliasClassMap = WsRestClassLookup.getAliasClassMap();
221     for (String key : aliasClassMap.keySet()) {
222       xstream.alias(key, aliasClassMap.get(key));
223     }
224     //optional, whitelist all the WS classes for extra protection
225     xstream.allowTypes(aliasClassMap.values().toArray(new Class[0])); //CVE-2013-7285
226 
227     return xstream;
228   }
229 
230   /**
231    * do a case-insensitive matching
232    * 
233    * @param string
234    * @param exceptionOnNotFound true to throw exception on not found
235    * @return the enum or null or exception if not found
236    * @throws GrouperRestInvalidRequest if problem
237    */
238   public static WsRestResponseContentType valueOfIgnoreCase(String string,
239       boolean exceptionOnNotFound) throws GrouperRestInvalidRequest {
240     return GrouperServiceUtils.enumValueOfIgnoreCase(WsRestResponseContentType.class, 
241         string, exceptionOnNotFound);
242   }
243 
244 }