1 unstable release
0.1.0 | Dec 17, 2020 |
---|
#33 in #audit
26KB
191 lines
sql-audit
Tracking changes is the whole goal here. When someone inserts, updates, or deletes a record, those changes should be traced and attributable for the user. This serves the dual purposes of auditing who makes changes and providing a way to revert them.
Limitations
- Only Postgres is currently supported, only version 10.12 is tested.
- To make it easy to track changes against a specific record, the primary key of that record is recorded separately from the rest of the data. Currently, this requires that all audited tables have a primary key column called
pk
which is typeinteger
. There are plans to make this more flexible. - Your audit will only be as safe as the permissions on your database! Make sure only a secure audit account has access to the audit schema/table/trigger. The trigger will execute with the permissions of the user that creates it.
- Only tables in the
public
schema of the connected database will be audited.
How To
-
cargo install sql-audit-cli
-
Create an
audit.toml
with content like this:database = "postgres://username:password@host/database" exclude_tables = ["test_2"] # Tables to not audit
DO NOT CHECK THIS FILE IN as it obviously contains database credentials of the secure audit account. Future versions should make this config easier (e.g. prompting for credentials).
-
Run
sql-audit
from the command line in the directory containing this file.
How it Works
- A new schema called
sql_audit
will be created in the database with the tableaudit
. - A function will be created called
sql_audit_trigger
. - All tables in the
public
schema of the connected database will be audited except those listed in theexclude_tables
list inaudit.toml
. - All "audited" tables (defined above) will get a
TRIGGER
onINSERT
,UPDATE
, andDELETE
. Those triggers insert data into theaudit
table created for each row with the following columns:- id: a surrogate key
- ts: the time of the change
- table_name: the table that the change took place on
- pk: the pk of the row which the trigger ran on
- operation: "INSERT", "UPDATE", "DELETE" depending on the action that triggered the trigger.
- db_user: The database user which made the change
- app_user: An optional string for storing data about a logged in user (to the app) that made the change. More details in features section.
- request_id: An optional arbitrary string representing a specific web request. More details in features.
- new_val: A JSON representation of the entire new row (only populated on insert or update).
- new_val: A JSON representation of the entire old row (only populated on update or delete).
Features
Store app_user
This setting only lasts for the duration of a transaction.
The point of app_user is to be able to track which users make changes at the application level instead of the database level. Often, an application will use a single set of database credentials regardless of which user is making a web request. Recording the application-level user is therefore often more useful than the database user alone. However, this can be set by whoever is making the query, so only trust this value as much as you trust the db_user
.
Using this is purely at the discretion of the consuming application. Set it by calling sql_audit::set_local_app_user
from Rust or using SELECT set_config('sql_audit.app_user', $1, true)
where $1 is whatever you want to store here. Using a param and binding is strongly recommended since the user name / email is usually some sort of user-provided input. You don't want to run arbitrary SQL with the permissions of the audit user!
Store request_id
This setting only lasts for the duration of a transaction.
This field is here for tracing purposes. When a user makes a web request, you will often log some unique request ID for tracking bugs/changes/whatever. You can also store that unique ID here to track database changes back to a specific web request (or the other way around).
Using this is purely at the discretion of the consuming application. Set it by calling sql_audit::set_local_app_user
from Rust or using SELECT set_config('sql_audit.request_id', $1, true)
where $1 is whatever you want to store here.
Dependencies
~28–42MB
~799K SLoC