dendrite/roomserver/storage/postgres/redactions_table.go
Kegsay 52c77cb4b4
Implement core redaction logic (#1185)
* Implement core redaction logic

- Add a new `redactions_table.go` which tracks the mapping of
  the redaction event ID and the redacted event ID
- Mark redactions as 'validated' when we have both events.
- When redactions are validated, add `unsigned.redacted_because`
  and modify the `eventJSON` accordingly.

Note: We currently do NOT redact the event content - it's gated
behind a feature flag - until we have tested redactions a bit more.

* Linting
2020-07-06 17:49:15 +01:00

121 lines
4.1 KiB
Go

// Copyright 2020 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package postgres
import (
"context"
"database/sql"
"github.com/matrix-org/dendrite/internal/sqlutil"
"github.com/matrix-org/dendrite/roomserver/storage/shared"
"github.com/matrix-org/dendrite/roomserver/storage/tables"
)
const redactionsSchema = `
-- Stores information about the redacted state of events.
-- We need to track redactions rather than blindly updating the event JSON table on receipt of a redaction
-- because we might receive the redaction BEFORE we receive the event which it redacts (think backfill).
CREATE TABLE IF NOT EXISTS roomserver_redactions (
redaction_event_id TEXT PRIMARY KEY,
redacts_event_id TEXT NOT NULL,
-- Initially FALSE, set to TRUE when the redaction has been validated according to rooms v3+ spec
-- https://matrix.org/docs/spec/rooms/v3#authorization-rules-for-events
validated BOOLEAN NOT NULL
);
CREATE INDEX IF NOT EXISTS roomserver_redactions_redacts_event_id ON roomserver_redactions(redacts_event_id);
`
const insertRedactionSQL = "" +
"INSERT INTO roomserver_redactions (redaction_event_id, redacts_event_id, validated)" +
" VALUES ($1, $2, $3)"
const selectRedactedEventSQL = "" +
"SELECT redaction_event_id, redacts_event_id, validated FROM roomserver_redactions" +
" WHERE redaction_event_id = $1"
const selectRedactionEventSQL = "" +
"SELECT redaction_event_id, redacts_event_id, validated FROM roomserver_redactions" +
" WHERE redacts_event_id = $1"
const markRedactionValidatedSQL = "" +
" UPDATE roomserver_redactions SET validated = $2 WHERE redaction_event_id = $1"
type redactionStatements struct {
insertRedactionStmt *sql.Stmt
selectRedactedEventStmt *sql.Stmt
selectRedactionEventStmt *sql.Stmt
markRedactionValidatedStmt *sql.Stmt
}
func NewPostgresRedactionsTable(db *sql.DB) (tables.Redactions, error) {
s := &redactionStatements{}
_, err := db.Exec(redactionsSchema)
if err != nil {
return nil, err
}
return s, shared.StatementList{
{&s.insertRedactionStmt, insertRedactionSQL},
{&s.selectRedactedEventStmt, selectRedactedEventSQL},
{&s.selectRedactionEventStmt, selectRedactionEventSQL},
{&s.markRedactionValidatedStmt, markRedactionValidatedSQL},
}.Prepare(db)
}
func (s *redactionStatements) InsertRedaction(
ctx context.Context, txn *sql.Tx, info tables.RedactionInfo,
) error {
stmt := sqlutil.TxStmt(txn, s.insertRedactionStmt)
_, err := stmt.ExecContext(ctx, info.RedactionEventID, info.RedactsEventID, info.Validated)
return err
}
func (s *redactionStatements) SelectRedactedEvent(
ctx context.Context, txn *sql.Tx, redactionEventID string,
) (info *tables.RedactionInfo, err error) {
info = &tables.RedactionInfo{}
stmt := sqlutil.TxStmt(txn, s.selectRedactedEventStmt)
err = stmt.QueryRowContext(ctx, redactionEventID).Scan(
&info.RedactionEventID, &info.RedactsEventID, &info.Validated,
)
if err == sql.ErrNoRows {
err = nil
info = nil
}
return
}
func (s *redactionStatements) SelectRedactionEvent(
ctx context.Context, txn *sql.Tx, redactedEventID string,
) (info *tables.RedactionInfo, err error) {
info = &tables.RedactionInfo{}
stmt := sqlutil.TxStmt(txn, s.selectRedactionEventStmt)
err = stmt.QueryRowContext(ctx, redactedEventID).Scan(
&info.RedactionEventID, &info.RedactsEventID, &info.Validated,
)
if err == sql.ErrNoRows {
err = nil
info = nil
}
return
}
func (s *redactionStatements) MarkRedactionValidated(
ctx context.Context, txn *sql.Tx, redactionEventID string, validated bool,
) error {
stmt := sqlutil.TxStmt(txn, s.markRedactionValidatedStmt)
_, err := stmt.ExecContext(ctx, redactionEventID, validated)
return err
}