1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.syncope.core.persistence.jpa.dao;
20
21 import java.sql.Clob;
22 import java.sql.SQLException;
23 import java.time.OffsetDateTime;
24 import java.util.ArrayList;
25 import java.util.List;
26 import java.util.Objects;
27 import java.util.stream.Collectors;
28 import javax.persistence.Query;
29 import javax.persistence.TypedQuery;
30 import org.apache.commons.lang3.StringUtils;
31 import org.apache.syncope.common.lib.audit.AuditEntry;
32 import org.apache.syncope.common.lib.types.AuditElements;
33 import org.apache.syncope.core.persistence.api.dao.AuditConfDAO;
34 import org.apache.syncope.core.persistence.api.dao.search.OrderByClause;
35 import org.apache.syncope.core.persistence.api.entity.AuditConf;
36 import org.apache.syncope.core.persistence.jpa.entity.JPAAuditConf;
37 import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
38 import org.springframework.transaction.annotation.Transactional;
39
40 public class JPAAuditConfDAO extends AbstractDAO<AuditConf> implements AuditConfDAO {
41
42 protected static class MessageCriteriaBuilder {
43
44 protected final StringBuilder query = new StringBuilder();
45
46 protected String andIfNeeded() {
47 return query.length() == 0 ? " " : " AND ";
48 }
49
50 protected int setParameter(final List<Object> parameters, final Object parameter) {
51 parameters.add(parameter);
52 return parameters.size();
53 }
54
55 protected MessageCriteriaBuilder entityKey(final String entityKey) {
56 if (entityKey != null) {
57 query.append(andIfNeeded()).append(AUDIT_ENTRY_MESSAGE_COLUMN).
58 append(" LIKE '%key%").append(entityKey).append("%'");
59 }
60 return this;
61 }
62
63 public MessageCriteriaBuilder type(final AuditElements.EventCategoryType type) {
64 if (type != null) {
65 query.append(andIfNeeded()).append(AUDIT_ENTRY_MESSAGE_COLUMN).
66 append(" LIKE '%\"type\":\"").append(type.name()).append("\"%'");
67 }
68 return this;
69 }
70
71 public MessageCriteriaBuilder category(final String category) {
72 if (StringUtils.isNotBlank(category)) {
73 query.append(andIfNeeded()).append(AUDIT_ENTRY_MESSAGE_COLUMN).
74 append(" LIKE '%\"category\":\"").append(category).append("\"%'");
75 }
76 return this;
77 }
78
79 public MessageCriteriaBuilder subcategory(final String subcategory) {
80 if (StringUtils.isNotBlank(subcategory)) {
81 query.append(andIfNeeded()).append(AUDIT_ENTRY_MESSAGE_COLUMN).
82 append(" LIKE '%\"subcategory\":\"").append(subcategory).append("\"%'");
83 }
84 return this;
85 }
86
87 public MessageCriteriaBuilder events(final List<String> events) {
88 if (!events.isEmpty()) {
89 query.append(andIfNeeded()).append("( ").
90 append(events.stream().
91 map(event -> AUDIT_ENTRY_MESSAGE_COLUMN + " LIKE '%\"event\":\"" + event + "\"%'").
92 collect(Collectors.joining(" OR "))).
93 append(" )");
94 }
95 return this;
96 }
97
98 public MessageCriteriaBuilder result(final AuditElements.Result result) {
99 if (result != null) {
100 query.append(andIfNeeded()).append(AUDIT_ENTRY_MESSAGE_COLUMN).
101 append(" LIKE '%\"result\":\"").append(result.name()).append("\"%' ");
102 }
103 return this;
104 }
105
106 public MessageCriteriaBuilder before(final OffsetDateTime before, final List<Object> parameters) {
107 if (before != null) {
108 query.append(andIfNeeded()).append(AUDIT_ENTRY_EVENT_DATE_COLUMN).
109 append(" <= ?").append(setParameter(parameters, before));
110 }
111 return this;
112 }
113
114 public MessageCriteriaBuilder after(final OffsetDateTime after, final List<Object> parameters) {
115 if (after != null) {
116 query.append(andIfNeeded()).append(AUDIT_ENTRY_EVENT_DATE_COLUMN).
117 append(" >= ?").append(setParameter(parameters, after));
118 }
119 return this;
120 }
121
122 public String build() {
123 return query.toString();
124 }
125 }
126
127 @Override
128 public AuditConf find(final String key) {
129 return entityManager().find(JPAAuditConf.class, key);
130 }
131
132 @Override
133 public List<AuditConf> findAll() {
134 TypedQuery<AuditConf> query = entityManager().createQuery(
135 "SELECT e FROM " + JPAAuditConf.class.getSimpleName() + " e ", AuditConf.class);
136 return query.getResultList();
137 }
138
139 @Override
140 public AuditConf save(final AuditConf auditConf) {
141 return entityManager().merge(auditConf);
142 }
143
144 @Override
145 public void delete(final AuditConf auditConf) {
146 entityManager().remove(auditConf);
147 }
148
149 protected MessageCriteriaBuilder messageCriteriaBuilder(final String entityKey) {
150 return new MessageCriteriaBuilder().entityKey(entityKey);
151 }
152
153 protected void fillWithParameters(final Query query, final List<Object> parameters) {
154 for (int i = 0; i < parameters.size(); i++) {
155 if (parameters.get(i) instanceof Boolean) {
156 query.setParameter(i + 1, ((Boolean) parameters.get(i)) ? 1 : 0);
157 } else {
158 query.setParameter(i + 1, parameters.get(i));
159 }
160 }
161 }
162
163 @Override
164 public int countEntries(
165 final String entityKey,
166 final AuditElements.EventCategoryType type,
167 final String category,
168 final String subcategory,
169 final List<String> events,
170 final AuditElements.Result result,
171 final OffsetDateTime before,
172 final OffsetDateTime after) {
173
174 List<Object> parameters = new ArrayList<>();
175 String queryString = "SELECT COUNT(0)"
176 + " FROM " + AUDIT_ENTRY_TABLE
177 + " WHERE " + messageCriteriaBuilder(entityKey).
178 type(type).
179 category(category).
180 subcategory(subcategory).
181 result(result).
182 events(events).
183 before(before, parameters).
184 after(after, parameters).
185 build();
186 Query query = entityManager().createNativeQuery(queryString);
187 fillWithParameters(query, parameters);
188
189 return ((Number) query.getSingleResult()).intValue();
190 }
191
192 protected String select() {
193 return AUDIT_ENTRY_MESSAGE_COLUMN;
194 }
195
196 @Transactional(readOnly = true)
197 @Override
198 public List<AuditEntry> searchEntries(
199 final String entityKey,
200 final int page,
201 final int itemsPerPage,
202 final AuditElements.EventCategoryType type,
203 final String category,
204 final String subcategory,
205 final List<String> events,
206 final AuditElements.Result result,
207 final OffsetDateTime before,
208 final OffsetDateTime after,
209 final List<OrderByClause> orderBy) {
210
211 List<Object> parameters = new ArrayList<>();
212 String queryString = "SELECT " + select()
213 + " FROM " + AUDIT_ENTRY_TABLE
214 + " WHERE " + messageCriteriaBuilder(entityKey).
215 type(type).
216 category(category).
217 subcategory(subcategory).
218 result(result).
219 events(events).
220 before(before, parameters).
221 after(after, parameters).
222 build();
223 if (!orderBy.isEmpty()) {
224 queryString += " ORDER BY " + orderBy.stream().
225 map(clause -> clause.getField() + ' ' + clause.getDirection().name()).
226 collect(Collectors.joining(","));
227 }
228
229 Query query = entityManager().createNativeQuery(queryString);
230 fillWithParameters(query, parameters);
231 query.setFirstResult(itemsPerPage * (page <= 0 ? 0 : page - 1));
232 if (itemsPerPage >= 0) {
233 query.setMaxResults(itemsPerPage);
234 }
235
236 @SuppressWarnings("unchecked")
237 List<Object> entries = query.getResultList();
238 return entries.stream().map(row -> {
239 String value;
240 if (row instanceof Clob) {
241 Clob clob = (Clob) row;
242 try {
243 value = clob.getSubString(1, (int) clob.length());
244 } catch (SQLException e) {
245 LOG.error("Unexpected error reading Audit Entry for entity key {}", entityKey, e);
246 return null;
247 }
248 } else {
249 value = row.toString();
250 }
251 return POJOHelper.deserialize(value, AuditEntry.class);
252 }).filter(Objects::nonNull).collect(Collectors.toList());
253 }
254 }