1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.audit;
18
19 import org.apache.logging.log4j.LogManager;
20 import org.apache.logging.log4j.Logger;
21 import org.apache.logging.log4j.ThreadContext;
22 import org.apache.logging.log4j.audit.catalog.CatalogManager;
23 import org.apache.logging.log4j.audit.exception.AuditException;
24 import org.apache.logging.log4j.catalog.api.Attribute;
25 import org.apache.logging.log4j.catalog.api.Constraint;
26 import org.apache.logging.log4j.catalog.api.Event;
27 import org.apache.logging.log4j.catalog.api.EventAttribute;
28 import org.apache.logging.log4j.catalog.api.plugins.ConstraintPlugins;
29 import org.apache.logging.log4j.message.StructuredDataMessage;
30
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Set;
34
35 import static org.apache.logging.log4j.catalog.api.constant.Constants.*;
36
37
38
39
40 public abstract class AbstractEventLogger {
41
42 private static final Logger logger = LogManager.getLogger(AbstractEventLogger.class);
43
44 private static final int DEFAULT_MAX_LENGTH = 32;
45
46 private static ConstraintPlugins constraintPlugins = ConstraintPlugins.getInstance();
47
48 public CatalogManager catalogManager;
49
50 private static final AuditExceptionHandler DEFAULT_EXCEPTION_HANDLER = (message, ex) -> {
51 throw new AuditException("Error logging event " + message.getId().getName(), ex);
52 };
53
54 private static final AuditExceptionHandler NOOP_EXCEPTION_HANDLER = (message, ex) -> {
55 };
56
57 private AuditExceptionHandler defaultAuditExceptionHandler = DEFAULT_EXCEPTION_HANDLER;
58
59 private final int maxLength;
60
61 protected AbstractEventLogger() {
62 maxLength = DEFAULT_MAX_LENGTH;
63 }
64
65 protected AbstractEventLogger(int maxLength) {
66 this.maxLength = maxLength;
67 }
68
69 public void setCatalogManager(CatalogManager catalogManager) {
70 this.catalogManager = catalogManager;
71 }
72
73 public List<String> getAttributeNames(String eventId) {
74 return catalogManager.getAttributeNames(eventId);
75 }
76
77 public void setDefaultAuditExceptionHandler(AuditExceptionHandler auditExceptionHandler) {
78 defaultAuditExceptionHandler = auditExceptionHandler == null ? NOOP_EXCEPTION_HANDLER : auditExceptionHandler;
79 }
80
81 public void logEvent(String eventName, Map<String, String> attributes) {
82 Event event = catalogManager.getEvent(eventName);
83 if (event == null) {
84 throw new AuditException("Unable to locate definition of audit event " + eventName);
85 }
86 logEvent(eventName, attributes, event, defaultAuditExceptionHandler);
87 }
88
89 public void logEvent(String eventName, String catalogId, Map<String, String> attributes) {
90 Event event = catalogManager.getEvent(eventName, catalogId);
91 if (event == null) {
92 throw new AuditException("Unable to locate definition of audit event " + eventName);
93 }
94 logEvent(eventName, attributes, event, defaultAuditExceptionHandler);
95 }
96
97 public void logEvent(String eventName, Map<String, String> attributes, AuditExceptionHandler exceptionHandler) {
98 Event event = catalogManager.getEvent(eventName);
99
100 if (event == null) {
101 throw new AuditException("Unable to locate definition of audit event " + eventName);
102 }
103 logEvent(eventName, attributes, event, exceptionHandler);
104 }
105
106 protected abstract void logEvent(StructuredDataMessage message);
107
108 private void logEvent(String eventName, Map<String, String> attributes, Event event,
109 AuditExceptionHandler exceptionHandler) {
110 AuditMessage msg = new AuditMessage(eventName, maxLength);
111
112 StringBuilder missingAttributes = new StringBuilder();
113 StringBuilder errors = new StringBuilder();
114
115 for (EventAttribute eventAttribute : event.getAttributes()) {
116 Attribute attr = catalogManager.getAttribute(eventAttribute.getName(), event.getCatalogId());
117 if ((!attr.isRequestContext() && (attr.isRequired()) ||
118 (eventAttribute.isRequired() != null && eventAttribute.isRequired()))) {
119 String name = attr.getName();
120 if (!attributes.containsKey(name)) {
121 if (missingAttributes.length() > 0) {
122 missingAttributes.append(", ");
123 }
124 missingAttributes.append(name);
125 } else {
126 if (attr.getConstraints() != null && attr.getConstraints().size() > 0) {
127 Constraint[] constraints = attr.getConstraints().toArray(new Constraint[attr.getConstraints().size()]);
128 validateConstraints(false, constraints, name, attributes.get(name), errors);
129 }
130 }
131 }
132 }
133 Map<String, Attribute> attributeMap = catalogManager.getAttributes(eventName, event.getCatalogId());
134 for (String name : attributes.keySet()) {
135 if (!attributeMap.containsKey(name) && !name.equals("completionStatus")) {
136 if (errors.length() > 0) {
137 errors.append("\n");
138 }
139 errors.append("Attribute ").append(name).append(" is not defined for ").append(eventName);
140 }
141 }
142 if (missingAttributes.length() > 0) {
143 if (errors.length() > 0) {
144 errors.append("\n");
145 }
146 errors.append("Event ").append(eventName).append(" is missing required attribute(s) ").append(missingAttributes.toString());
147 }
148 if (errors.length() > 0) {
149 throw new AuditException(errors.toString());
150 }
151 List<String> attributeNames = catalogManager.getAttributeNames(eventName, event.getCatalogId());
152 StringBuilder buf = new StringBuilder();
153 for (String attribute : attributes.keySet()) {
154 if (!attributeNames.contains(attribute)) {
155 if (buf.length() > 0) {
156 buf.append(", ");
157 }
158 buf.append(attribute);
159 }
160 }
161 if (buf.length() > 0) {
162 throw new AuditException("Event " + eventName + " contains invalid attribute(s) " + buf.toString());
163 }
164
165 List<String> reqCtxAttrs = catalogManager.getRequiredContextAttributes(eventName, event.getCatalogId());
166
167 if (reqCtxAttrs != null) {
168 StringBuilder sb = new StringBuilder();
169 for (String attr : reqCtxAttrs) {
170 if (!ThreadContext.containsKey(attr)) {
171 if (sb.length() > 0) {
172 sb.append(", ");
173 }
174 sb.append(attr);
175 }
176 }
177 if (sb.length() > 0) {
178 throw new IllegalStateException("Event " + msg.getId().getName() +
179 " is missing required RequestContextMapping values for " + sb.toString());
180 }
181 }
182 Map<String, Attribute> reqCtxAttributes = catalogManager.getRequestContextAttributes();
183 for (Map.Entry<String, String> entry : ThreadContext.getImmutableContext().entrySet()) {
184 Attribute attribute = reqCtxAttributes.get(entry.getKey());
185 if (attribute == null) {
186 continue;
187 }
188 Set<Constraint> constraintList = attribute.getConstraints();
189 if (constraintList != null && constraintList.size() > 0) {
190 Constraint[] constraints =
191 attribute.getConstraints().toArray(new Constraint[attribute.getConstraints().size()]);
192 validateConstraints(true, constraints, entry.getKey(), ThreadContext.get(entry.getKey()), errors);
193 }
194 }
195 if (errors.length() > 0) {
196 throw new AuditException("Event " + eventName + " has incorrect data in the Thread Context: " + errors.toString());
197 }
198 msg.putAll(attributes);
199 try {
200 logEvent(msg);
201 } catch (Throwable ex) {
202 if (exceptionHandler == null) {
203 defaultAuditExceptionHandler.handleException(msg, ex);
204 } else {
205 exceptionHandler.handleException(msg, ex);
206 }
207 }
208 }
209
210 private static void validateConstraints(boolean isRequestContext, Constraint[] constraints, String name,
211 String value, StringBuilder errors) {
212 for (Constraint constraint : constraints) {
213 constraintPlugins.validateConstraint(isRequestContext, constraint.getConstraintType().getName(), name, value,
214 constraint.getValue(), errors);
215 }
216 }
217 }