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