-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathAuditLog.java
242 lines (212 loc) · 8.05 KB
/
AuditLog.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
/*
* Made with all the love in the world
* by scireum in Remshalden, Germany
*
* Copyright by scireum GmbH
* http://www.scireum.de - [email protected]
*/
package sirius.biz.protocol;
import sirius.db.es.Elastic;
import sirius.kernel.Sirius;
import sirius.kernel.di.Initializable;
import sirius.kernel.di.std.Part;
import sirius.kernel.di.std.Register;
import sirius.kernel.health.Exceptions;
import sirius.kernel.health.Log;
import sirius.kernel.nls.NLS;
import sirius.web.http.WebContext;
import sirius.web.security.UserContext;
import sirius.web.security.UserInfo;
import javax.annotation.CheckReturnValue;
import javax.annotation.Nullable;
import java.time.LocalDate;
import java.time.LocalDateTime;
/**
* Inserts entries into the security log of the system.
*/
@Register(classes = {AuditLog.class, Initializable.class})
public class AuditLog implements Initializable {
@Part
private Elastic elastic;
private boolean enabled;
private static final Log LOG = Log.get("audit");
@Override
public void initialize() throws Exception {
enabled = Sirius.isFrameworkEnabled(Protocols.FRAMEWORK_PROTOCOLS);
}
/**
* Fluent builder for audit logs.
*/
public class AuditLogBuilder {
private final AuditLogEntry entry = new AuditLogEntry();
protected AuditLogBuilder(String message, boolean negative) {
entry.setTimestamp(LocalDateTime.now());
entry.setDate(LocalDate.now());
entry.setNegative(negative);
entry.setMessage(message);
entry.setIp(getCurrentIP());
}
private String getCurrentIP() {
WebContext webContext = WebContext.getCurrent();
if (webContext.isValid()) {
return webContext.getRemoteIP().getHostAddress();
}
return null;
}
/**
* Creates the entry for the current user in {@link UserContext#getCurrentUser()}.
*
* @return the builder for fluent method calls
*/
@CheckReturnValue
public AuditLogBuilder forCurrentUser() {
UserInfo userInfo = UserContext.getCurrentUser();
return forUser(userInfo.getUserId(), userInfo.getProtocolUsername()).forTenant(userInfo.getTenantId(),
userInfo.getTenantName());
}
/**
* Creates the entry for the given user.
*
* @param userId the ID of the user
* @param name the name of the user
* @return the builder for fluent method calls
*/
@CheckReturnValue
public AuditLogBuilder forUser(@Nullable String userId, @Nullable String name) {
entry.setUser(userId);
entry.setUserName(name);
return this;
}
/**
* Marks the entry as caused by the current user in {@link UserContext#getCurrentUser()}.
*
* @return the builder for fluent method calls
*/
@CheckReturnValue
public AuditLogBuilder causedByCurrentUser() {
UserInfo userInfo = UserContext.getCurrentUser();
return causedByUser(userInfo.getUserId(), userInfo.getProtocolUsername());
}
/**
* Marks the entry as caused by the given user.
*
* @param userId the ID of the user
* @param userName the name of the user
* @return the builder for fluent method calls
*/
@CheckReturnValue
public AuditLogBuilder causedByUser(@Nullable String userId, @Nullable String userName) {
entry.setCausedByUser(userId);
entry.setCausedByUserName(userName);
return this;
}
/**
* Creates the entry for the given tenant.
*
* @param tenantId the ID of the tenant
* @param tenantName the name of the tenant
* @return the builder for fluent method calls
*/
@CheckReturnValue
public AuditLogBuilder forTenant(@Nullable String tenantId, @Nullable String tenantName) {
entry.setTenant(tenantId);
entry.setTenantName(tenantName);
return this;
}
/**
* Marks this entry as {@link AuditLogEntry#HIDDEN}.
* <p>
* Such entries are not visible to the user but only to the system tenant.
*
* @return the builder for fluent method calls
*/
@CheckReturnValue
public AuditLogBuilder hideFromUser() {
entry.setHidden(true);
return this;
}
/**
* Writes the entry into the log table.
*/
public void log() {
try {
if (!enabled
|| elastic == null
|| !elastic.getReadyFuture().isCompleted()
|| Sirius.isStartedAsTest()) {
return;
}
if (!canSkip()) {
elastic.update(entry);
logToSyslog();
}
} catch (Exception exception) {
Exceptions.ignore(exception);
}
}
private boolean canSkip() {
if (entry.isNegative()) {
return false;
}
return elastic.select(AuditLogEntry.class)
.eq(AuditLogEntry.DATE, entry.getDate())
.eq(AuditLogEntry.CAUSED_BY_USER, entry.getCausedByUser())
.eq(AuditLogEntry.USER, entry.getUser())
.eq(AuditLogEntry.TENANT, entry.getTenant())
.eq(AuditLogEntry.IP, entry.getIp())
.eq(AuditLogEntry.MESSAGE, entry.getMessage())
.exists();
}
protected void logToSyslog() {
if (entry.isNegative()) {
LOG.WARN("%s (%s) caused for %s (%s) of %s (%s): %s (%s) - IP: %s, Timestamp: %s",
entry.getCausedByUserName(),
entry.getCausedByUser(),
entry.getUserName(),
entry.getUser(),
entry.getTenantName(),
entry.getTenant(),
entry.getMessage(),
NLS.get(entry.getMessage(), NLS.getDefaultLanguage()),
entry.getIp(),
NLS.toUserString(entry.getTimestamp(), NLS.getDefaultLanguage()));
} else {
LOG.INFO("%s (%s) caused for %s (%s) of %s (%s): %s (%s) - IP: %s, Timestamp: %s",
entry.getCausedByUserName(),
entry.getCausedByUser(),
entry.getUserName(),
entry.getUser(),
entry.getTenantName(),
entry.getTenant(),
entry.getMessage(),
NLS.get(entry.getMessage(), NLS.getDefaultLanguage()),
entry.getIp(),
NLS.toUserString(entry.getTimestamp(), NLS.getDefaultLanguage()));
}
}
}
/**
* Prepares a "neutral" entry to be inserted into the log.
* <p>
* Note that a builder is returned and eventually {@link AuditLogBuilder#log()} has to be called.
*
* @param messageKey the message key (i18n key) to log
* @return the builder used to supply further info
*/
@CheckReturnValue
public AuditLogBuilder neutral(String messageKey) {
return new AuditLogBuilder(messageKey, false);
}
/**
* Prepares a "negative" entry to be inserted into the log.
* <p>
* Note that a builder is returned and eventually {@link AuditLogBuilder#log()} has to be called.
*
* @param messageKey the message key (i18n key) to log
* @return the builder used to supply further info
*/
@CheckReturnValue
public AuditLogBuilder negative(String messageKey) {
return new AuditLogBuilder(messageKey, true);
}
}