1 // Copyright (C) 2019-2020 Internet Systems Consortium, Inc. ("ISC")
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #ifndef AUDIT_ENTRY_H
8 #define AUDIT_ENTRY_H
9 
10 #include <boost/date_time/posix_time/posix_time.hpp>
11 #include <boost/multi_index/composite_key.hpp>
12 #include <boost/multi_index_container.hpp>
13 #include <boost/multi_index/hashed_index.hpp>
14 #include <boost/multi_index/mem_fun.hpp>
15 #include <boost/multi_index/ordered_index.hpp>
16 #include <boost/shared_ptr.hpp>
17 #include <cstdint>
18 #include <string>
19 
20 namespace isc {
21 namespace db {
22 
23 class AuditEntry;
24 
25 /// @brief Pointer to the @c AuditEntry object.
26 typedef boost::shared_ptr<AuditEntry> AuditEntryPtr;
27 
28 /// @brief Represents a single entry in the audit table.
29 ///
30 /// The audit tables are used in the databases to track incremental
31 /// changes, e.g. configuration changes applied via the configuration
32 /// backend. The backend can query for the entries in the audit table
33 /// to identify the SQL table in which the records were modified, the
34 /// identifiers of the modified objects, type of the modifications,
35 /// time of the modifications and optional log messages associated
36 /// with the modifications.
37 ///
38 /// The server should remember the most recent modification time out of
39 /// all audit entries it has fetched. During the next attempt to fetch
40 /// the most recent modifications in the database it will query for all
41 /// entries with later modification time than stored. That way the
42 /// server queries only for the audit entries it hasn't fetched yet.
43 /// In the case two (or more) successive audit entries have the same
44 /// modification time the strictly increasing modification id is used.
45 ///
46 /// When the modification type of the entry is set to
47 /// @c AuditEntry::ModificationType::DELETE, the corresponding
48 /// configuration entry is already gone from the database. For example:
49 /// when a subnet with ID of 123 is deleted from the dhcp4_subnet
50 /// table, the audit entry similar to this will be stored in the audit
51 /// table:
52 ///
53 /// - object_type: "dhcp4_subnet"
54 /// - object_id: 123
55 /// - modification_type: 3 (DELETE)
56 /// - modification_time: "2019-01-15 15:45:23"
57 /// - revision id: 234
58 /// - log_message: "DHCPv4 subnet 123 deleted"
59 ///
60 /// The subnet is instantly removed from the dhcp4_subnet table. When
61 /// the server finds such entry in the audit table, it removes the
62 /// subnet 123 from its (in-memory) configuration. There is no need
63 /// make additional queries to fetch updated data from the dhcp4_subnet
64 /// table, unless there are also audit entries indicating that some
65 /// new subnets have been added or some subnets have been updated.
66 class AuditEntry {
67 public:
68 
69     /// @brief Types of the modifications.
70     ///
71     /// The numbers representing those modification types correspond
72     /// to the values representing them in the database.
73     enum class ModificationType : uint8_t {
74         CREATE = 0,
75         UPDATE = 1,
76         DELETE = 2
77     };
78 
79     /// @brief Constructor using explicit modification time and id.
80     ///
81     /// @param object_type name of the table where data was modified.
82     /// @param object_id identifier of the modified record in this table.
83     /// @param modification_type type of the modification, e.g. DELETE.
84     /// @param modification_time time of modification for that record.
85     /// @param revision_id identifier of the revision.
86     /// @param log_message optional log message associated with the
87     /// modification.
88     AuditEntry(const std::string& object_type,
89                const uint64_t object_id,
90                const ModificationType& modification_type,
91                const boost::posix_time::ptime& modification_time,
92                const uint64_t revision_id,
93                const std::string& log_message);
94 
95     /// @brief Constructor using default modification time.
96     ///
97     /// @param object_type name of the table where data was modified.
98     /// @param object_id identifier of the modified record in this table.
99     /// @param modification_type type of the modification, e.g. DELETE.
100     /// @param revision_id identifier of the revision.
101     /// @param log_message optional log message associated with the
102     /// modification.
103     AuditEntry(const std::string& object_type,
104                const uint64_t object_id,
105                const ModificationType& modification_type,
106                const uint64_t revision_id,
107                const std::string& log_message);
108 
109     /// @brief Factory function creating an instance of @c AuditEntry.
110     ///
111     /// This function should be used to create an instance of the audit
112     /// entry within a hooks library in cases when the library may be
113     /// unloaded before the object is destroyed. This ensures that the
114     /// ownership of the object by the Kea process is retained.
115     ///
116     /// @param object_type name of the table where data was modified.
117     /// @param object_id identifier of the modified record in this table.
118     /// @param modification_type type of the modification, e.g. DELETE.
119     /// @param modification_time time of modification for that record.
120     /// @param revision_id identifier of the revision.
121     /// @param log_message optional log message associated with the
122     /// modification.
123     ///
124     /// @return Pointer to the @c AuditEntry instance.
125     static AuditEntryPtr create(const std::string& object_type,
126                                 const uint64_t object_id,
127                                 const ModificationType& modification_type,
128                                 const boost::posix_time::ptime& modification_time,
129                                 const uint64_t revision_id,
130                                 const std::string& log_message);
131 
132     /// @brief Factory function creating an instance of @c AuditEntry.
133     ///
134     /// This function should be used to create an instance of the audit
135     /// entry within a hooks library in cases when the library may be
136     /// unloaded before the object is destroyed. This ensures that the
137     /// ownership of the object by the Kea process is retained.
138     ///
139     /// @param object_type name of the table where data was modified.
140     /// @param object_id identifier of the modified record in this table.
141     /// @param modification_type type of the modification, e.g. DELETE.
142     /// @param revision_id identifier of the revision.
143     /// @param log_message optional log message associated with the
144     /// modification.
145     ///
146     /// @return Pointer to the @c AuditEntry instance.
147     static AuditEntryPtr create(const std::string& object_type,
148                                 const uint64_t object_id,
149                                 const ModificationType& modification_type,
150                                 const uint64_t revision_id,
151                                 const std::string& log_message);
152 
153     /// @brief Returns object type.
154     ///
155     /// @return Name of the table in which the modification is present.
getObjectType()156     std::string getObjectType() const {
157         return (object_type_);
158     }
159 
160     /// @brief Returns object id.
161     ///
162     /// @return Identifier of the added, updated or deleted object.
getObjectId()163     uint64_t getObjectId() const {
164         return (object_id_);
165     }
166 
167     /// @brief Returns modification type.
168     ///
169     /// @return Type of the modification, e.g. DELETE.
getModificationType()170     ModificationType getModificationType() const {
171         return (modification_type_);
172     }
173 
174     /// @brief Returns modification time.
175     ///
176     /// @return Modification time of the corresponding record.
getModificationTime()177     boost::posix_time::ptime getModificationTime() const {
178         return (modification_time_);
179     }
180 
181     /// @brief Returns revision id.
182     ///
183     /// The revision id is used when two audit entries have the same
184     /// modification time.
185     ///
186     /// @return Identifier of the revision.
getRevisionId()187     uint64_t getRevisionId() const {
188         return (revision_id_);
189     }
190 
191     /// @brief Returns log message.
192     ///
193     /// @return Optional log message corresponding to the changes.
getLogMessage()194     std::string getLogMessage() const {
195         return (log_message_);
196     }
197 
198 private:
199 
200     /// @brief Validates the values specified for the audit entry.
201     ///
202     /// @throw BadValue if the object type is empty or if the
203     /// modification time is not a date time value.
204     void validate() const;
205 
206     /// @brief Object type.
207     std::string object_type_;
208 
209     /// @brief Object id.
210     uint64_t object_id_;
211 
212     /// @brief Modification type.
213     ModificationType modification_type_;
214 
215     /// @brief Modification time.
216     boost::posix_time::ptime modification_time_;
217 
218     /// @brief Revision id.
219     ///
220     /// The revision id is used when two audit entries have the same
221     /// modification time.
222     uint64_t revision_id_;
223 
224     /// @brief Log message.
225     std::string log_message_;
226 };
227 
228 /// @brief Tag used to access index by object type.
229 struct AuditEntryObjectTypeTag { };
230 
231 /// @brief Tag used to access index by modification time.
232 struct AuditEntryModificationTimeIdTag { };
233 
234 /// @brief Tag used to access index by object id.
235 struct AuditEntryObjectIdTag { };
236 
237 /// @brief Multi index container holding @c AuditEntry instances.
238 ///
239 /// This container provides indexes to access the audit entries
240 /// by object type and modification time.
241 typedef boost::multi_index_container<
242     // The container holds pointers to @c AuditEntry objects.
243     AuditEntryPtr,
244     // First index allows for accessing by the object type.
245     boost::multi_index::indexed_by<
246         boost::multi_index::ordered_non_unique<
247             boost::multi_index::tag<AuditEntryObjectTypeTag>,
248             boost::multi_index::composite_key<
249                 AuditEntry,
250                 boost::multi_index::const_mem_fun<
251                     AuditEntry,
252                     std::string,
253                     &AuditEntry::getObjectType
254                 >,
255                 boost::multi_index::const_mem_fun<
256                     AuditEntry,
257                     AuditEntry::ModificationType,
258                     &AuditEntry::getModificationType
259                 >
260             >
261         >,
262 
263         // Second index allows for accessing by the modification time and id.
264         boost::multi_index::ordered_non_unique<
265             boost::multi_index::tag<AuditEntryModificationTimeIdTag>,
266             boost::multi_index::composite_key<
267                 AuditEntry,
268                 boost::multi_index::const_mem_fun<
269                     AuditEntry,
270                     boost::posix_time::ptime,
271                     &AuditEntry::getModificationTime
272                 >,
273                 boost::multi_index::const_mem_fun<
274                     AuditEntry,
275                     uint64_t,
276                     &AuditEntry::getRevisionId
277                 >
278             >
279         >,
280 
281         // Third index allows for accessing by the object id.
282         boost::multi_index::hashed_non_unique<
283             boost::multi_index::tag<AuditEntryObjectIdTag>,
284             boost::multi_index::const_mem_fun<
285                 AuditEntry,
286                 uint64_t,
287                 &AuditEntry::getObjectId
288             >
289         >
290     >
291 > AuditEntryCollection;
292 
293 //// @brief Pointer to the @c AuditEntryCollection object.
294 typedef boost::shared_ptr<AuditEntryCollection> AuditEntryCollectionPtr;
295 
296 } // end of namespace isc::db
297 } // end of namespace isc
298 
299 #endif
300