001/******************************************************************************* 002 * Copyright 2017 The MIT Internet Trust Consortium 003 * 004 * Portions copyright 2011-2013 The MITRE Corporation 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 *******************************************************************************/ 018/** 019 * 020 */ 021package org.mitre.util; 022 023import java.io.IOException; 024import java.util.ArrayList; 025import java.util.Date; 026import java.util.HashMap; 027import java.util.HashSet; 028import java.util.List; 029import java.util.Map; 030import java.util.Set; 031 032import org.mitre.oauth2.model.PKCEAlgorithm; 033import org.slf4j.Logger; 034import org.slf4j.LoggerFactory; 035 036import com.google.common.collect.Lists; 037import com.google.common.collect.Sets; 038import com.google.gson.Gson; 039import com.google.gson.JsonElement; 040import com.google.gson.JsonNull; 041import com.google.gson.JsonObject; 042import com.google.gson.JsonSyntaxException; 043import com.google.gson.reflect.TypeToken; 044import com.google.gson.stream.JsonReader; 045import com.google.gson.stream.JsonWriter; 046import com.nimbusds.jose.EncryptionMethod; 047import com.nimbusds.jose.JWEAlgorithm; 048import com.nimbusds.jose.JWSAlgorithm; 049 050/** 051 * A collection of null-safe converters from common classes and JSON elements, using GSON. 052 * 053 * @author jricher 054 * 055 */ 056@SuppressWarnings(value = {"rawtypes", "unchecked"}) 057public class JsonUtils { 058 059 /** 060 * Logger for this class 061 */ 062 private static final Logger logger = LoggerFactory.getLogger(JsonUtils.class); 063 064 private static Gson gson = new Gson(); 065 066 /** 067 * Translate a set of strings to a JSON array, empty array returned as null 068 * @param value 069 * @return 070 */ 071 public static JsonElement getAsArray(Set<String> value) { 072 return getAsArray(value, false); 073 } 074 075 076 /** 077 * Translate a set of strings to a JSON array, optionally preserving the empty array. Otherwise (default) empty array is returned as null. 078 * @param value 079 * @param preserveEmpty 080 * @return 081 */ 082 public static JsonElement getAsArray(Set<String> value, boolean preserveEmpty) { 083 if (!preserveEmpty && value != null && value.isEmpty()) { 084 // if we're not preserving empty arrays and the value is empty, return null 085 return JsonNull.INSTANCE; 086 } else { 087 return gson.toJsonTree(value, new TypeToken<Set<String>>(){}.getType()); 088 } 089 } 090 091 /** 092 * Gets the value of the given member (expressed as integer seconds since epoch) as a Date 093 */ 094 public static Date getAsDate(JsonObject o, String member) { 095 if (o.has(member)) { 096 JsonElement e = o.get(member); 097 if (e != null && e.isJsonPrimitive()) { 098 return new Date(e.getAsInt() * 1000L); 099 } else { 100 return null; 101 } 102 } else { 103 return null; 104 } 105 } 106 107 /** 108 * Gets the value of the given member as a JWE Algorithm, null if it doesn't exist 109 */ 110 public static JWEAlgorithm getAsJweAlgorithm(JsonObject o, String member) { 111 String s = getAsString(o, member); 112 if (s != null) { 113 return JWEAlgorithm.parse(s); 114 } else { 115 return null; 116 } 117 } 118 119 /** 120 * Gets the value of the given member as a JWE Encryption Method, null if it doesn't exist 121 */ 122 public static EncryptionMethod getAsJweEncryptionMethod(JsonObject o, String member) { 123 String s = getAsString(o, member); 124 if (s != null) { 125 return EncryptionMethod.parse(s); 126 } else { 127 return null; 128 } 129 } 130 131 /** 132 * Gets the value of the given member as a JWS Algorithm, null if it doesn't exist 133 */ 134 public static JWSAlgorithm getAsJwsAlgorithm(JsonObject o, String member) { 135 String s = getAsString(o, member); 136 if (s != null) { 137 return JWSAlgorithm.parse(s); 138 } else { 139 return null; 140 } 141 } 142 143 /** 144 * Gets the value of the given member as a PKCE Algorithm, null if it doesn't exist 145 * @param o 146 * @param member 147 * @return 148 */ 149 public static PKCEAlgorithm getAsPkceAlgorithm(JsonObject o, String member) { 150 String s = getAsString(o, member); 151 if (s != null) { 152 return PKCEAlgorithm.parse(s); 153 } else { 154 return null; 155 } 156 } 157 158 /** 159 * Gets the value of the given member as a string, null if it doesn't exist 160 */ 161 public static String getAsString(JsonObject o, String member) { 162 if (o.has(member)) { 163 JsonElement e = o.get(member); 164 if (e != null && e.isJsonPrimitive()) { 165 return e.getAsString(); 166 } else { 167 return null; 168 } 169 } else { 170 return null; 171 } 172 } 173 174 /** 175 * Gets the value of the given member as a boolean, null if it doesn't exist 176 */ 177 public static Boolean getAsBoolean(JsonObject o, String member) { 178 if (o.has(member)) { 179 JsonElement e = o.get(member); 180 if (e != null && e.isJsonPrimitive()) { 181 return e.getAsBoolean(); 182 } else { 183 return null; 184 } 185 } else { 186 return null; 187 } 188 } 189 190 /** 191 * Gets the value of the given member as a Long, null if it doesn't exist 192 */ 193 public static Long getAsLong(JsonObject o, String member) { 194 if (o.has(member)) { 195 JsonElement e = o.get(member); 196 if (e != null && e.isJsonPrimitive()) { 197 return e.getAsLong(); 198 } else { 199 return null; 200 } 201 } else { 202 return null; 203 } 204 } 205 206 /** 207 * Gets the value of the given given member as a set of strings, null if it doesn't exist 208 */ 209 public static Set<String> getAsStringSet(JsonObject o, String member) throws JsonSyntaxException { 210 if (o.has(member)) { 211 if (o.get(member).isJsonArray()) { 212 return gson.fromJson(o.get(member), new TypeToken<Set<String>>(){}.getType()); 213 } else { 214 return Sets.newHashSet(o.get(member).getAsString()); 215 } 216 } else { 217 return null; 218 } 219 } 220 221 /** 222 * Gets the value of the given given member as a set of strings, null if it doesn't exist 223 */ 224 public static List<String> getAsStringList(JsonObject o, String member) throws JsonSyntaxException { 225 if (o.has(member)) { 226 if (o.get(member).isJsonArray()) { 227 return gson.fromJson(o.get(member), new TypeToken<List<String>>(){}.getType()); 228 } else { 229 return Lists.newArrayList(o.get(member).getAsString()); 230 } 231 } else { 232 return null; 233 } 234 } 235 236 /** 237 * Gets the value of the given member as a list of JWS Algorithms, null if it doesn't exist 238 */ 239 public static List<JWSAlgorithm> getAsJwsAlgorithmList(JsonObject o, String member) { 240 List<String> strings = getAsStringList(o, member); 241 if (strings != null) { 242 List<JWSAlgorithm> algs = new ArrayList<>(); 243 for (String alg : strings) { 244 algs.add(JWSAlgorithm.parse(alg)); 245 } 246 return algs; 247 } else { 248 return null; 249 } 250 } 251 252 /** 253 * Gets the value of the given member as a list of JWS Algorithms, null if it doesn't exist 254 */ 255 public static List<JWEAlgorithm> getAsJweAlgorithmList(JsonObject o, String member) { 256 List<String> strings = getAsStringList(o, member); 257 if (strings != null) { 258 List<JWEAlgorithm> algs = new ArrayList<>(); 259 for (String alg : strings) { 260 algs.add(JWEAlgorithm.parse(alg)); 261 } 262 return algs; 263 } else { 264 return null; 265 } 266 } 267 268 /** 269 * Gets the value of the given member as a list of JWS Algorithms, null if it doesn't exist 270 */ 271 public static List<EncryptionMethod> getAsEncryptionMethodList(JsonObject o, String member) { 272 List<String> strings = getAsStringList(o, member); 273 if (strings != null) { 274 List<EncryptionMethod> algs = new ArrayList<>(); 275 for (String alg : strings) { 276 algs.add(EncryptionMethod.parse(alg)); 277 } 278 return algs; 279 } else { 280 return null; 281 } 282 } 283 284 public static Map readMap(JsonReader reader) throws IOException { 285 Map map = new HashMap<>(); 286 reader.beginObject(); 287 while(reader.hasNext()) { 288 String name = reader.nextName(); 289 Object value = null; 290 switch(reader.peek()) { 291 case STRING: 292 value = reader.nextString(); 293 break; 294 case BOOLEAN: 295 value = reader.nextBoolean(); 296 break; 297 case NUMBER: 298 value = reader.nextLong(); 299 break; 300 default: 301 logger.debug("Found unexpected entry"); 302 reader.skipValue(); 303 continue; 304 } 305 map.put(name, value); 306 } 307 reader.endObject(); 308 return map; 309 } 310 311 public static Set readSet(JsonReader reader) throws IOException { 312 Set arraySet = null; 313 reader.beginArray(); 314 switch (reader.peek()) { 315 case STRING: 316 arraySet = new HashSet<>(); 317 while (reader.hasNext()) { 318 arraySet.add(reader.nextString()); 319 } 320 break; 321 case NUMBER: 322 arraySet = new HashSet<>(); 323 while (reader.hasNext()) { 324 arraySet.add(reader.nextLong()); 325 } 326 break; 327 default: 328 arraySet = new HashSet(); 329 break; 330 } 331 reader.endArray(); 332 return arraySet; 333 } 334 335 public static void writeNullSafeArray(JsonWriter writer, Set<String> items) throws IOException { 336 if (items != null) { 337 writer.beginArray(); 338 for (String s : items) { 339 writer.value(s); 340 } 341 writer.endArray(); 342 } else { 343 writer.nullValue(); 344 } 345 } 346 347}