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}