diff --git a/schema/01_initial.sql b/schema/01_initial.sql index c7acbfb..94409c0 100644 --- a/schema/01_initial.sql +++ b/schema/01_initial.sql @@ -124,3 +124,63 @@ create table if not exists outputgroup_outputs( output_id integer not null references outputs(id), unique(outputgroups_id, output_id) ); + +-- create history functionality +create table if not exists history( + transaction text not null, + created_at timestamptz not null default now(), + created_by text not null, + type text not null, + action text not null, + old_entity jsonb default '{}', + new_entity jsonb default '{}' +); +comment on table history is 'History is the log of all changes happening in DIM.'; +comment on column history.transaction is 'The transaction ID is the same for all changes originating in the same request.'; +comment on column history.created_by is 'The username is not a foreign key to keep the history around'; +comment on column history.type is 'Type represents the table that originated the change.'; +comment on column history.action is 'The action can be one of insert, update or delete.'; + +create or replace function record() returns trigger as $$ +begin + insert into history(transaction, created_by, type, action, old_entity, new_entity) + values ( + current_setting('dim.transaction'), + current_setting('dim.username'), + TG_TABLE_NAME, TG_OP, + row_to_json(OLD), + row_to_json(NEW) + ); + if TG_OP = 'DELETE' then + return OLD; + else + return NEW; + end if; +end;$$ language plpgsql; + +-- drop existing triggers +drop trigger if exists trg_containers on containers; +drop trigger if exists trg_ips on ips; +drop trigger if exists trg_layer3domains on layer3domains; +drop trigger if exists trg_outputgroup_outputs on outputgroup_outputs; +drop trigger if exists trg_outputgroups on outputgroups; +drop trigger if exists trg_outputs on outputs; +drop trigger if exists trg_pools on pools; +drop trigger if exists trg_records on records; +drop trigger if exists trg_zones on zones; +drop trigger if exists trg_zoneviews on zoneviews; +drop trigger if exists trg_zoneviews_outputgroups on zoneviews_outputgroups; + +-- recreate history triggers +create trigger trg_containers before insert or update or delete on containers for each row execute function record(); +create trigger trg_ips before insert or update or delete on ips for each row execute function record(); +create trigger trg_layer3domains before insert or update or delete on layer3domains for each row execute function record(); +create trigger trg_outputgroup_outputs before insert or update or delete on outputgroup_outputs for each row execute function record(); +create trigger trg_outputgroups before insert or update or delete on outputgroups for each row execute function record(); +create trigger trg_outputs before insert or update or delete on outputs for each row execute function record(); +create trigger trg_pools before insert or update or delete on pools for each row execute function record(); +create trigger trg_records before insert or update or delete on records for each row execute function record(); +create trigger trg_zones before insert or update or delete on zones for each row execute function record(); +create trigger trg_zoneviews before insert or update or delete on zoneviews for each row execute function record(); +create trigger trg_zoneviews_outputgroups before insert or update or delete on zoneviews_outputgroups for each row execute function record(); + diff --git a/server.go b/server.go index 057a71d..551fcdb 100644 --- a/server.go +++ b/server.go @@ -93,9 +93,10 @@ func (s *Server) Handle(w http.ResponseWriter, r *http.Request) { return } c := &Context{ - id: id, - req: r, - w: w, + id: id, + req: r, + w: w, + username: "unknown", } req := Request{} @@ -116,6 +117,19 @@ func (s *Server) Handle(w http.ResponseWriter, r *http.Request) { defer tx.Rollback() // make a rollback, just in case something goes wrong c.tx = tx + // set username for transaction + // TODO check username to be ASCII and nothing else + _, err = c.tx.Exec( + fmt.Sprintf(`set local dim.username to '%s'; set local dim.transaction = '%s'`, c.username, c.id), + ) + if err != nil { + c.Logf(LevelError, "could not set transaction username: %s", err) + w.WriteHeader(http.StatusInternalServerError) + res.AddMessage(LevelFatal, "could not create transaction") + c.render(res) + return + } + dec := json.NewDecoder(r.Body) defer r.Body.Close() if err := dec.Decode(&req); err != nil {