1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.forgerock.audit.handlers.syslog;
18
19 import static org.forgerock.audit.util.ResourceExceptionsUtil.adapt;
20 import static org.forgerock.audit.util.ResourceExceptionsUtil.notSupported;
21 import static org.forgerock.json.resource.Responses.newResourceResponse;
22
23 import java.net.InetSocketAddress;
24 import javax.inject.Inject;
25
26 import org.forgerock.audit.Audit;
27 import org.forgerock.audit.events.EventTopicsMetaData;
28 import org.forgerock.audit.events.handlers.AuditEventHandlerBase;
29 import org.forgerock.audit.providers.DefaultLocalHostNameProvider;
30 import org.forgerock.audit.providers.LocalHostNameProvider;
31 import org.forgerock.audit.providers.ProductInfoProvider;
32 import org.forgerock.json.JsonValue;
33 import org.forgerock.json.resource.BadRequestException;
34 import org.forgerock.json.resource.InternalServerErrorException;
35 import org.forgerock.json.resource.NotSupportedException;
36 import org.forgerock.json.resource.QueryRequest;
37 import org.forgerock.json.resource.QueryResourceHandler;
38 import org.forgerock.json.resource.QueryResponse;
39 import org.forgerock.json.resource.ResourceException;
40 import org.forgerock.json.resource.ResourceResponse;
41 import org.forgerock.services.context.Context;
42 import org.forgerock.util.Reject;
43 import org.forgerock.util.promise.Promise;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47
48
49
50
51 public class SyslogAuditEventHandler extends AuditEventHandlerBase {
52
53 private static final Logger logger = LoggerFactory.getLogger(SyslogAuditEventHandler.class);
54
55 private final SyslogPublisher publisher;
56 private final SyslogFormatter formatter;
57
58
59
60
61
62
63
64
65
66
67
68
69
70 @Inject
71 public SyslogAuditEventHandler(
72 final SyslogAuditEventHandlerConfiguration configuration,
73 final EventTopicsMetaData eventTopicsMetaData,
74 @Audit final ProductInfoProvider productInfoProvider,
75 @Audit final LocalHostNameProvider localHostNameProvider) {
76
77 super(configuration.getName(), eventTopicsMetaData, configuration.getTopics(), configuration.isEnabled());
78 Reject.ifNull(configuration.getProtocol(),
79 "Syslog transport 'protocol' of TCP or UDP is required");
80 Reject.ifNull(configuration.getHost(),
81 "Syslog destination server 'host' is required");
82 Reject.ifTrue(configuration.getPort() < 0 || configuration.getPort() > 65535,
83 "Syslog destination server 'port' between 0 and 65535 is required");
84 Reject.ifNull(configuration.getFacility(),
85 "Syslog 'facility' is required");
86 Reject.ifTrue(configuration.getProtocol() == TransportProtocol.TCP && configuration.getConnectTimeout() == 0,
87 "Syslog 'connectTimeout' is required for TCP connections");
88
89 InetSocketAddress socketAddress = new InetSocketAddress(configuration.getHost(), configuration.getPort());
90 this.publisher = configuration.getProtocol().getPublisher(socketAddress, configuration);
91 this.formatter = new SyslogFormatter(
92 eventTopicsMetaData,
93 configuration,
94 getLocalHostNameProvider(localHostNameProvider),
95 getProductNameProvider(productInfoProvider));
96
97 logger.debug("Successfully configured Syslog audit event handler.");
98 }
99
100 private ProductInfoProvider getProductNameProvider(ProductInfoProvider productInfoProvider) {
101 if (productInfoProvider != null) {
102 return productInfoProvider;
103 } else {
104 logger.debug("No {} provided; using default.", ProductInfoProvider.class.getSimpleName());
105 return new DefaultProductInfoProvider();
106 }
107 }
108
109 private LocalHostNameProvider getLocalHostNameProvider(LocalHostNameProvider localHostNameProvider) {
110 if (localHostNameProvider != null) {
111 return localHostNameProvider;
112 } else {
113 logger.debug("No {} provided; using default.", LocalHostNameProvider.class.getSimpleName());
114 return new DefaultLocalHostNameProvider();
115 }
116 }
117
118
119 @Override
120 public void startup() {
121
122 }
123
124
125
126
127 @Override
128 public void shutdown() {
129 synchronized (publisher) {
130 publisher.close();
131 }
132 }
133
134 @Override
135 public Promise<ResourceResponse, ResourceException> publishEvent(Context context, String topic, JsonValue event) {
136
137 try {
138 final String syslogMessage = formatAsSyslogMessage(topic, event);
139 synchronized (publisher) {
140 publisher.publishMessage(syslogMessage);
141 }
142
143 return newResourceResponse(
144 event.get(ResourceResponse.FIELD_CONTENT_ID).asString(),
145 null,
146 event.clone()).asPromise();
147
148 } catch (Exception ex) {
149 return adapt(ex).asPromise();
150 }
151 }
152
153 private String formatAsSyslogMessage(String topic, JsonValue auditEvent) throws ResourceException {
154 if (!formatter.canFormat(topic)) {
155 throw new InternalServerErrorException("Unable to format " + topic + " audit event");
156 }
157 try {
158 return formatter.format(topic, auditEvent);
159 } catch (Exception ex) {
160 throw new BadRequestException(ex);
161 }
162 }
163
164 @Override
165 public Promise<QueryResponse, ResourceException> queryEvents(
166 Context context,
167 String topic,
168 QueryRequest queryRequest,
169 QueryResourceHandler queryResourceHandler) {
170 return notSupported(queryRequest).asPromise();
171 }
172
173 @Override
174 public Promise<ResourceResponse, ResourceException> readEvent(Context context, String topic, String resourceId) {
175 return new NotSupportedException("query operations are not supported").asPromise();
176 }
177
178
179
180
181 private static class DefaultProductInfoProvider implements ProductInfoProvider {
182
183 @Override
184 public String getProductName() {
185 return null;
186 }
187 }
188 }