1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.forgerock.audit.json;
17
18 import static org.forgerock.json.JsonValue.field;
19 import static org.forgerock.json.JsonValue.json;
20 import static org.forgerock.json.JsonValue.object;
21
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.util.LinkedHashMap;
25 import java.util.Map;
26
27 import org.forgerock.audit.AuditException;
28 import org.forgerock.audit.AuditServiceBuilder;
29 import org.forgerock.audit.AuditServiceConfiguration;
30 import org.forgerock.audit.events.handlers.AuditEventHandler;
31 import org.forgerock.audit.events.handlers.EventHandlerConfiguration;
32 import org.forgerock.audit.util.JsonValueUtils;
33 import org.forgerock.json.JsonValue;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 import com.fasterxml.jackson.annotation.JsonPropertyDescription;
38 import com.fasterxml.jackson.databind.AnnotationIntrospector;
39 import com.fasterxml.jackson.databind.ObjectMapper;
40 import com.fasterxml.jackson.databind.introspect.Annotated;
41 import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
42 import com.fasterxml.jackson.module.jsonSchema.JsonSchema;
43 import com.fasterxml.jackson.module.jsonSchema.factories.SchemaFactoryWrapper;
44
45
46
47
48
49 public final class AuditJsonConfig {
50 private static final Logger LOGGER = LoggerFactory.getLogger(AuditJsonConfig.class);
51
52
53 private static final String NAME_FIELD = "name";
54
55 private static final String CLASS_FIELD = "class";
56
57 private static final String CONFIG_FIELD = "config";
58
59 private static final String EVENTS_FIELD = "events";
60
61
62 private static final ObjectMapper MAPPER = new ObjectMapper();
63
64 private static final AnnotationIntrospector DEFAULT_ANNOTATION_INTROSPECTOR = new JacksonAnnotationIntrospector();
65 private static final AnnotationIntrospector HELP_APPENDER_ANNOTATION_INTROSPECTOR =
66 new HelpAppenderAnnotationIntrospector();
67
68 private AuditJsonConfig() {
69
70 }
71
72
73
74
75
76
77
78
79
80
81 public static JsonValue getJson(InputStream input) throws AuditException {
82 if (input == null) {
83 throw new AuditException("Input stream is null");
84 }
85 try {
86 Object val = MAPPER.readValue(input, LinkedHashMap.class);
87 return new JsonValue(val);
88 } catch (IOException e) {
89 throw new AuditException(String.format("Unable to retrieve json value from json input stream"), e);
90 }
91 }
92
93
94
95
96
97
98
99
100
101
102 public static AuditServiceConfiguration parseAuditServiceConfiguration(InputStream input) throws AuditException {
103 try {
104 return MAPPER.readValue(input, AuditServiceConfiguration.class);
105 } catch (IOException e) {
106 throw new AuditException(String.format("Unable to retrieve class %s from json input stream",
107 AuditServiceConfiguration.class), e);
108 }
109 }
110
111
112
113
114
115
116
117
118
119
120 public static AuditServiceConfiguration parseAuditServiceConfiguration(String json) throws AuditException {
121 if (json == null) {
122 return new AuditServiceConfiguration();
123 }
124 try {
125 return MAPPER.readValue(json, AuditServiceConfiguration.class);
126 } catch (IOException e) {
127 throw new AuditException(String.format("Unable to retrieve class %s from json: %s",
128 AuditServiceConfiguration.class, json), e);
129 }
130 }
131
132
133
134
135
136
137
138
139
140
141 public static AuditServiceConfiguration parseAuditServiceConfiguration(JsonValue json) throws AuditException {
142 return parseAuditServiceConfiguration(JsonValueUtils.extractValueAsString(json, "/"));
143 }
144
145
146
147
148
149
150
151
152
153
154
155
156 public static void registerHandlerToService(JsonValue jsonConfig, AuditServiceBuilder auditServiceBuilder)
157 throws AuditException {
158 registerHandlerToService(jsonConfig, auditServiceBuilder, auditServiceBuilder.getClass().getClassLoader());
159 }
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174 public static void registerHandlerToService(JsonValue jsonConfig,
175 AuditServiceBuilder auditServiceBuilder, ClassLoader classLoader) throws AuditException {
176 String name = getHandlerName(jsonConfig);
177 Class<? extends AuditEventHandler> handlerClass = getAuditEventHandlerClass(name, jsonConfig, classLoader);
178 Class<? extends EventHandlerConfiguration> configClass =
179 getAuditEventHandlerConfigurationClass(name, handlerClass, classLoader);
180 EventHandlerConfiguration configuration = parseAuditEventHandlerConfiguration(configClass, jsonConfig);
181 auditServiceBuilder.withAuditEventHandler(handlerClass, configuration);
182 }
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199 private static String getHandlerName(JsonValue jsonConfig) throws AuditException {
200 String name = jsonConfig.get(CONFIG_FIELD).get(NAME_FIELD).asString();
201 if (name == null) {
202 throw new AuditException(String.format("No name is defined for the provided audit handler. "
203 + "You must define a 'name' property in the configuration."));
204 }
205 return name;
206 }
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222 @SuppressWarnings("unchecked")
223 private static Class<? extends AuditEventHandler> getAuditEventHandlerClass(
224 String handlerName, JsonValue jsonConfig, ClassLoader classLoader) throws AuditException {
225
226
227 String className = jsonConfig.get(CLASS_FIELD).asString();
228 if (className == null) {
229 String errorMessage = String.format("No class is defined for the audit handler %s. "
230 + "You must define a 'class' property in the configuration.", handlerName);
231 throw new AuditException(errorMessage);
232 }
233 try {
234 return (Class<? extends AuditEventHandler>) Class.forName(className, true, classLoader);
235 } catch (ClassNotFoundException e) {
236 String errorMessage = String.format("Invalid class is defined for the audit handler %s.", handlerName);
237 throw new AuditException(errorMessage, e);
238 }
239 }
240
241 @SuppressWarnings("unchecked")
242 private static Class<? extends EventHandlerConfiguration> getAuditEventHandlerConfigurationClass(
243 String handlerName, Class<? extends AuditEventHandler> handlerClass, ClassLoader classLoader)
244 throws AuditException {
245 String className = handlerClass.getName() + "Configuration";
246 try {
247 return (Class<? extends EventHandlerConfiguration>) Class.forName(className, true, classLoader);
248 } catch (ClassNotFoundException e) {
249 String errorMessage = String.format("Unable to locate configuration class %s for the audit handler %s.",
250 className, handlerName);
251 throw new AuditException(errorMessage, e);
252 }
253 }
254
255
256
257
258
259
260
261
262
263
264
265
266
267 public static <C extends EventHandlerConfiguration> C parseAuditEventHandlerConfiguration(
268 Class<C> clazz, JsonValue jsonConfig) throws AuditException {
269 C configuration = null;
270 JsonValue conf = jsonConfig.get(CONFIG_FIELD);
271 if (conf != null) {
272 configuration = MAPPER.convertValue(conf.getObject(), clazz);
273 }
274 return configuration;
275 }
276
277
278
279
280
281
282
283
284
285 public static JsonValue getAuditEventHandlerConfigurationSchema(final String className,
286 final ClassLoader classLoader) throws AuditException {
287 final Class<? extends EventHandlerConfiguration> eventHandlerConfiguration =
288 getAuditEventHandlerConfigurationClass(
289 className,
290 getAuditEventHandlerClass(
291 className,
292 json(object(field("class", className))),
293 classLoader),
294 classLoader);
295 try {
296 MAPPER.setAnnotationIntrospector(HELP_APPENDER_ANNOTATION_INTROSPECTOR);
297 SchemaFactoryWrapper visitor = new SchemaFactoryWrapper();
298 MAPPER.acceptJsonFormatVisitor(MAPPER.constructType(eventHandlerConfiguration), visitor);
299 JsonSchema jsonSchema = visitor.finalSchema();
300 final JsonValue schema = json(MAPPER.readValue(MAPPER.writeValueAsString(jsonSchema), Map.class));
301 MAPPER.setAnnotationIntrospector(DEFAULT_ANNOTATION_INTROSPECTOR);
302 return schema;
303 } catch (IOException e) {
304 final String error = String.format("Unable to parse configuration class schema for configuration class %s",
305 eventHandlerConfiguration.getName());
306 LOGGER.error(error, e);
307 throw new AuditException(error, e);
308 }
309 }
310
311
312
313
314
315 private static class HelpAppenderAnnotationIntrospector extends JacksonAnnotationIntrospector {
316 @Override
317 public String findPropertyDescription(Annotated ann) {
318 JsonPropertyDescription desc = _findAnnotation(ann, JsonPropertyDescription.class);
319 return (desc == null) ? null : desc.value().concat(".help");
320 }
321 }
322
323 }