mirror of
https://github.com/matrix-org/dendrite.git
synced 2025-12-21 05:43:09 -06:00
Add storage layer for postgres/sqlite
This commit is contained in:
parent
7daa3bf098
commit
17017eefc1
|
|
@ -17,6 +17,7 @@ package api
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type KeyInternalAPI interface {
|
type KeyInternalAPI interface {
|
||||||
|
|
@ -52,6 +53,12 @@ type OneTimeKeys struct {
|
||||||
KeyJSON map[string]json.RawMessage
|
KeyJSON map[string]json.RawMessage
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Split a key in KeyJSON into algorithm and key ID
|
||||||
|
func (k *OneTimeKeys) Split(keyIDWithAlgo string) (algo string, keyID string) {
|
||||||
|
segments := strings.Split(keyIDWithAlgo, ":")
|
||||||
|
return segments[0], segments[1]
|
||||||
|
}
|
||||||
|
|
||||||
// OneTimeKeysCount represents the counts of one-time keys for a single device
|
// OneTimeKeysCount represents the counts of one-time keys for a single device
|
||||||
type OneTimeKeysCount struct {
|
type OneTimeKeysCount struct {
|
||||||
// The user who owns this device
|
// The user who owns this device
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,12 @@ func (a *KeyInternalAPI) uploadOneTimeKeys(ctx context.Context, req *api.Perform
|
||||||
Error: fmt.Sprintf("%s device %s : failed to store one-time keys: %s", key.UserID, key.DeviceID, err.Error()),
|
Error: fmt.Sprintf("%s device %s : failed to store one-time keys: %s", key.UserID, key.DeviceID, err.Error()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
// collect counts
|
||||||
|
res.OneTimeKeyCounts = append(res.OneTimeKeyCounts, api.OneTimeKeysCount{
|
||||||
|
DeviceID: key.DeviceID,
|
||||||
|
UserID: key.UserID,
|
||||||
|
KeyCount: nil,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
97
keyserver/storage/postgres/device_keys_table.go
Normal file
97
keyserver/storage/postgres/device_keys_table.go
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
// 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"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/api"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/storage/tables"
|
||||||
|
)
|
||||||
|
|
||||||
|
var deviceKeysSchema = `
|
||||||
|
-- Stores device keys for users
|
||||||
|
CREATE TABLE IF NOT EXISTS keyserver_device_keys (
|
||||||
|
user_id TEXT NOT NULL,
|
||||||
|
device_id TEXT NOT NULL,
|
||||||
|
ts_added_secs BIGINT NOT NULL,
|
||||||
|
key_json TEXT NOT NULL,
|
||||||
|
-- Clobber based on tuple of user/device.
|
||||||
|
CONSTRAINT keyserver_device_keys_unique UNIQUE (user_id, device_id)
|
||||||
|
);
|
||||||
|
`
|
||||||
|
|
||||||
|
const upsertDeviceKeysSQL = "" +
|
||||||
|
"INSERT INTO keyserver_device_keys (user_id, device_id, ts_added_secs, key_json)" +
|
||||||
|
" VALUES ($1, $2, $3, $4)" +
|
||||||
|
" ON CONFLICT ON CONSTRAINT keyserver_device_keys_unique" +
|
||||||
|
" DO UPDATE SET key_json = $4"
|
||||||
|
|
||||||
|
const selectDeviceKeysSQL = "" +
|
||||||
|
"SELECT key_json FROM keyserver_device_keys WHERE user_id=$1 AND device_id=$2"
|
||||||
|
|
||||||
|
type deviceKeysStatements struct {
|
||||||
|
db *sql.DB
|
||||||
|
upsertDeviceKeysStmt *sql.Stmt
|
||||||
|
selectDeviceKeysStmt *sql.Stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPostgresDeviceKeysTable(db *sql.DB) (tables.DeviceKeys, error) {
|
||||||
|
s := &deviceKeysStatements{
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
|
_, err := db.Exec(deviceKeysSchema)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if s.upsertDeviceKeysStmt, err = db.Prepare(upsertDeviceKeysSQL); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if s.selectDeviceKeysStmt, err = db.Prepare(selectDeviceKeysSQL); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *deviceKeysStatements) SelectDeviceKeysJSON(ctx context.Context, keys []api.DeviceKeys) error {
|
||||||
|
for i, key := range keys {
|
||||||
|
var keyJSONStr string
|
||||||
|
err := s.selectDeviceKeysStmt.QueryRowContext(ctx, key.UserID, key.DeviceID).Scan(&keyJSONStr)
|
||||||
|
if err != nil && err != sql.ErrNoRows {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// this will be '' when there is no device
|
||||||
|
keys[i].KeyJSON = []byte(keyJSONStr)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *deviceKeysStatements) InsertDeviceKeys(ctx context.Context, keys []api.DeviceKeys) error {
|
||||||
|
now := time.Now().Unix()
|
||||||
|
return sqlutil.WithTransaction(s.db, func(txn *sql.Tx) error {
|
||||||
|
for _, key := range keys {
|
||||||
|
_, err := txn.Stmt(s.upsertDeviceKeysStmt).ExecContext(
|
||||||
|
ctx, key.UserID, key.DeviceID, now, string(key.KeyJSON),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
117
keyserver/storage/postgres/one_time_keys_table.go
Normal file
117
keyserver/storage/postgres/one_time_keys_table.go
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
// 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"
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/internal"
|
||||||
|
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/api"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/storage/tables"
|
||||||
|
)
|
||||||
|
|
||||||
|
var oneTimeKeysSchema = `
|
||||||
|
-- Stores one-time public keys for users
|
||||||
|
CREATE TABLE IF NOT EXISTS keyserver_one_time_keys (
|
||||||
|
user_id TEXT NOT NULL,
|
||||||
|
device_id TEXT NOT NULL,
|
||||||
|
key_id TEXT NOT NULL,
|
||||||
|
algorithm TEXT NOT NULL,
|
||||||
|
ts_added_secs BIGINT NOT NULL,
|
||||||
|
key_json TEXT NOT NULL,
|
||||||
|
-- Clobber based on 4-uple of user/device/key/algorithm.
|
||||||
|
CONSTRAINT keyserver_one_time_keys_unique UNIQUE (user_id, device_id, key_id, algorithm)
|
||||||
|
);
|
||||||
|
`
|
||||||
|
|
||||||
|
const upsertKeysSQL = "" +
|
||||||
|
"INSERT INTO keyserver_one_time_keys (user_id, device_id, key_id, algorithm, ts_added_secs, key_json)" +
|
||||||
|
" VALUES ($1, $2, $3, $4, $5, $6)" +
|
||||||
|
" ON CONFLICT ON CONSTRAINT keyserver_one_time_keys_unique" +
|
||||||
|
" DO UPDATE SET key_json = $6"
|
||||||
|
|
||||||
|
const selectKeysSQL = "" +
|
||||||
|
"SELECT key_id, algorithm, key_json FROM keyserver_one_time_keys WHERE user_id=$1 AND device_id=$2"
|
||||||
|
|
||||||
|
type oneTimeKeysStatements struct {
|
||||||
|
db *sql.DB
|
||||||
|
upsertKeysStmt *sql.Stmt
|
||||||
|
selectKeysStmt *sql.Stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPostgresOneTimeKeysTable(db *sql.DB) (tables.OneTimeKeys, error) {
|
||||||
|
s := &oneTimeKeysStatements{
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
|
_, err := db.Exec(oneTimeKeysSchema)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if s.upsertKeysStmt, err = db.Prepare(upsertKeysSQL); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if s.selectKeysStmt, err = db.Prepare(selectKeysSQL); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *oneTimeKeysStatements) SelectOneTimeKeys(ctx context.Context, userID, deviceID string, keyIDsWithAlgorithms []string) (map[string]json.RawMessage, error) {
|
||||||
|
rows, err := s.selectKeysStmt.QueryContext(ctx, userID, deviceID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer internal.CloseAndLogIfError(ctx, rows, "selectKeysStmt: rows.close() failed")
|
||||||
|
|
||||||
|
wantSet := make(map[string]bool, len(keyIDsWithAlgorithms))
|
||||||
|
for _, ka := range keyIDsWithAlgorithms {
|
||||||
|
wantSet[ka] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make(map[string]json.RawMessage)
|
||||||
|
for rows.Next() {
|
||||||
|
var keyID string
|
||||||
|
var algorithm string
|
||||||
|
var keyJSONStr string
|
||||||
|
if err := rows.Scan(&keyID, &algorithm, &keyJSONStr); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
keyIDWithAlgo := algorithm + ":" + keyID
|
||||||
|
if wantSet[keyIDWithAlgo] {
|
||||||
|
result[keyIDWithAlgo] = json.RawMessage(keyJSONStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, rows.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *oneTimeKeysStatements) InsertOneTimeKeys(ctx context.Context, keys api.OneTimeKeys) error {
|
||||||
|
now := time.Now().Unix()
|
||||||
|
return sqlutil.WithTransaction(s.db, func(txn *sql.Tx) error {
|
||||||
|
for keyIDWithAlgo, keyJSON := range keys.KeyJSON {
|
||||||
|
algo, keyID := keys.Split(keyIDWithAlgo)
|
||||||
|
_, err := txn.Stmt(s.upsertKeysStmt).ExecContext(
|
||||||
|
ctx, keys.UserID, keys.DeviceID, keyID, algo, now, string(keyJSON),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
42
keyserver/storage/postgres/storage.go
Normal file
42
keyserver/storage/postgres/storage.go
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
// 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 (
|
||||||
|
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/storage/shared"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewDatabase creates a new sync server database
|
||||||
|
func NewDatabase(dbDataSourceName string, dbProperties sqlutil.DbProperties) (*shared.Database, error) {
|
||||||
|
var err error
|
||||||
|
db, err := sqlutil.Open("postgres", dbDataSourceName, dbProperties)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
otk, err := NewPostgresOneTimeKeysTable(db)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dk, err := NewPostgresDeviceKeysTable(db)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &shared.Database{
|
||||||
|
DB: db,
|
||||||
|
OneTimeKeysTable: otk,
|
||||||
|
DeviceKeysTable: dk,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
46
keyserver/storage/shared/storage.go
Normal file
46
keyserver/storage/shared/storage.go
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
// 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 shared
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/api"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/storage/tables"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Database struct {
|
||||||
|
DB *sql.DB
|
||||||
|
OneTimeKeysTable tables.OneTimeKeys
|
||||||
|
DeviceKeysTable tables.DeviceKeys
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Database) ExistingOneTimeKeys(ctx context.Context, userID, deviceID string, keyIDsWithAlgorithms []string) (map[string]json.RawMessage, error) {
|
||||||
|
return d.OneTimeKeysTable.SelectOneTimeKeys(ctx, userID, deviceID, keyIDsWithAlgorithms)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Database) StoreOneTimeKeys(ctx context.Context, keys api.OneTimeKeys) error {
|
||||||
|
return d.OneTimeKeysTable.InsertOneTimeKeys(ctx, keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Database) DeviceKeysJSON(ctx context.Context, keys []api.DeviceKeys) error {
|
||||||
|
return d.DeviceKeysTable.SelectDeviceKeysJSON(ctx, keys)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Database) StoreDeviceKeys(ctx context.Context, keys []api.DeviceKeys) error {
|
||||||
|
return d.DeviceKeysTable.InsertDeviceKeys(ctx, keys)
|
||||||
|
}
|
||||||
97
keyserver/storage/sqlite3/device_keys_table.go
Normal file
97
keyserver/storage/sqlite3/device_keys_table.go
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
// 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 sqlite3
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/api"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/storage/tables"
|
||||||
|
)
|
||||||
|
|
||||||
|
var deviceKeysSchema = `
|
||||||
|
-- Stores device keys for users
|
||||||
|
CREATE TABLE IF NOT EXISTS keyserver_device_keys (
|
||||||
|
user_id TEXT NOT NULL,
|
||||||
|
device_id TEXT NOT NULL,
|
||||||
|
ts_added_secs BIGINT NOT NULL,
|
||||||
|
key_json TEXT NOT NULL,
|
||||||
|
-- Clobber based on tuple of user/device.
|
||||||
|
CONSTRAINT keyserver_device_keys_unique UNIQUE (user_id, device_id)
|
||||||
|
);
|
||||||
|
`
|
||||||
|
|
||||||
|
const upsertDeviceKeysSQL = "" +
|
||||||
|
"INSERT INTO keyserver_device_keys (user_id, device_id, ts_added_secs, key_json)" +
|
||||||
|
" VALUES ($1, $2, $3, $4)" +
|
||||||
|
" ON CONFLICT ON CONSTRAINT keyserver_device_keys_unique" +
|
||||||
|
" DO UPDATE SET key_json = $4"
|
||||||
|
|
||||||
|
const selectDeviceKeysSQL = "" +
|
||||||
|
"SELECT key_json FROM keyserver_device_keys WHERE user_id=$1 AND device_id=$2"
|
||||||
|
|
||||||
|
type deviceKeysStatements struct {
|
||||||
|
db *sql.DB
|
||||||
|
upsertDeviceKeysStmt *sql.Stmt
|
||||||
|
selectDeviceKeysStmt *sql.Stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSqliteDeviceKeysTable(db *sql.DB) (tables.DeviceKeys, error) {
|
||||||
|
s := &deviceKeysStatements{
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
|
_, err := db.Exec(deviceKeysSchema)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if s.upsertDeviceKeysStmt, err = db.Prepare(upsertDeviceKeysSQL); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if s.selectDeviceKeysStmt, err = db.Prepare(selectDeviceKeysSQL); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *deviceKeysStatements) SelectDeviceKeysJSON(ctx context.Context, keys []api.DeviceKeys) error {
|
||||||
|
for i, key := range keys {
|
||||||
|
var keyJSONStr string
|
||||||
|
err := s.selectDeviceKeysStmt.QueryRowContext(ctx, key.UserID, key.DeviceID).Scan(&keyJSONStr)
|
||||||
|
if err != nil && err != sql.ErrNoRows {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// this will be '' when there is no device
|
||||||
|
keys[i].KeyJSON = []byte(keyJSONStr)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *deviceKeysStatements) InsertDeviceKeys(ctx context.Context, keys []api.DeviceKeys) error {
|
||||||
|
now := time.Now().Unix()
|
||||||
|
return sqlutil.WithTransaction(s.db, func(txn *sql.Tx) error {
|
||||||
|
for _, key := range keys {
|
||||||
|
_, err := txn.Stmt(s.upsertDeviceKeysStmt).ExecContext(
|
||||||
|
ctx, key.UserID, key.DeviceID, now, string(key.KeyJSON),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
117
keyserver/storage/sqlite3/one_time_keys_table.go
Normal file
117
keyserver/storage/sqlite3/one_time_keys_table.go
Normal file
|
|
@ -0,0 +1,117 @@
|
||||||
|
// 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 sqlite3
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/internal"
|
||||||
|
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/api"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/storage/tables"
|
||||||
|
)
|
||||||
|
|
||||||
|
var oneTimeKeysSchema = `
|
||||||
|
-- Stores one-time public keys for users
|
||||||
|
CREATE TABLE IF NOT EXISTS keyserver_one_time_keys (
|
||||||
|
user_id TEXT NOT NULL,
|
||||||
|
device_id TEXT NOT NULL,
|
||||||
|
key_id TEXT NOT NULL,
|
||||||
|
algorithm TEXT NOT NULL,
|
||||||
|
ts_added_secs BIGINT NOT NULL,
|
||||||
|
key_json TEXT NOT NULL,
|
||||||
|
-- Clobber based on 4-uple of user/device/key/algorithm.
|
||||||
|
CONSTRAINT keyserver_one_time_keys_unique UNIQUE (user_id, device_id, key_id, algorithm)
|
||||||
|
);
|
||||||
|
`
|
||||||
|
|
||||||
|
const upsertKeysSQL = "" +
|
||||||
|
"INSERT INTO keyserver_one_time_keys (user_id, device_id, key_id, algorithm, ts_added_secs, key_json)" +
|
||||||
|
" VALUES ($1, $2, $3, $4, $5, $6)" +
|
||||||
|
" ON CONFLICT ON CONSTRAINT keyserver_one_time_keys_unique" +
|
||||||
|
" DO UPDATE SET key_json = $6"
|
||||||
|
|
||||||
|
const selectKeysSQL = "" +
|
||||||
|
"SELECT key_id, algorithm, key_json FROM keyserver_one_time_keys WHERE user_id=$1 AND device_id=$2"
|
||||||
|
|
||||||
|
type oneTimeKeysStatements struct {
|
||||||
|
db *sql.DB
|
||||||
|
upsertKeysStmt *sql.Stmt
|
||||||
|
selectKeysStmt *sql.Stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSqliteOneTimeKeysTable(db *sql.DB) (tables.OneTimeKeys, error) {
|
||||||
|
s := &oneTimeKeysStatements{
|
||||||
|
db: db,
|
||||||
|
}
|
||||||
|
_, err := db.Exec(oneTimeKeysSchema)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if s.upsertKeysStmt, err = db.Prepare(upsertKeysSQL); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if s.selectKeysStmt, err = db.Prepare(selectKeysSQL); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *oneTimeKeysStatements) SelectOneTimeKeys(ctx context.Context, userID, deviceID string, keyIDsWithAlgorithms []string) (map[string]json.RawMessage, error) {
|
||||||
|
rows, err := s.selectKeysStmt.QueryContext(ctx, userID, deviceID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer internal.CloseAndLogIfError(ctx, rows, "selectKeysStmt: rows.close() failed")
|
||||||
|
|
||||||
|
wantSet := make(map[string]bool, len(keyIDsWithAlgorithms))
|
||||||
|
for _, ka := range keyIDsWithAlgorithms {
|
||||||
|
wantSet[ka] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make(map[string]json.RawMessage)
|
||||||
|
for rows.Next() {
|
||||||
|
var keyID string
|
||||||
|
var algorithm string
|
||||||
|
var keyJSONStr string
|
||||||
|
if err := rows.Scan(&keyID, &algorithm, &keyJSONStr); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
keyIDWithAlgo := algorithm + ":" + keyID
|
||||||
|
if wantSet[keyIDWithAlgo] {
|
||||||
|
result[keyIDWithAlgo] = json.RawMessage(keyJSONStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, rows.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *oneTimeKeysStatements) InsertOneTimeKeys(ctx context.Context, keys api.OneTimeKeys) error {
|
||||||
|
now := time.Now().Unix()
|
||||||
|
return sqlutil.WithTransaction(s.db, func(txn *sql.Tx) error {
|
||||||
|
for keyIDWithAlgo, keyJSON := range keys.KeyJSON {
|
||||||
|
algo, keyID := keys.Split(keyIDWithAlgo)
|
||||||
|
_, err := txn.Stmt(s.upsertKeysStmt).ExecContext(
|
||||||
|
ctx, keys.UserID, keys.DeviceID, keyID, algo, now, string(keyJSON),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
}
|
||||||
45
keyserver/storage/sqlite3/storage.go
Normal file
45
keyserver/storage/sqlite3/storage.go
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
// 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 sqlite3
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/storage/shared"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewDatabase(dataSourceName string) (*shared.Database, error) {
|
||||||
|
var err error
|
||||||
|
cs, err := sqlutil.ParseFileURI(dataSourceName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
db, err := sqlutil.Open(sqlutil.SQLiteDriverName(), cs, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
otk, err := NewSqliteOneTimeKeysTable(db)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
dk, err := NewSqliteDeviceKeysTable(db)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &shared.Database{
|
||||||
|
DB: db,
|
||||||
|
OneTimeKeysTable: otk,
|
||||||
|
DeviceKeysTable: dk,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
42
keyserver/storage/storage.go
Normal file
42
keyserver/storage/storage.go
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// +build !wasm
|
||||||
|
|
||||||
|
package storage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/storage/postgres"
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/storage/sqlite3"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewDatabase opens a new Postgres or Sqlite database (based on dataSourceName scheme)
|
||||||
|
// and sets postgres connection parameters
|
||||||
|
func NewDatabase(dataSourceName string, dbProperties sqlutil.DbProperties) (Database, error) {
|
||||||
|
uri, err := url.Parse(dataSourceName)
|
||||||
|
if err != nil {
|
||||||
|
return postgres.NewDatabase(dataSourceName, dbProperties)
|
||||||
|
}
|
||||||
|
switch uri.Scheme {
|
||||||
|
case "postgres":
|
||||||
|
return postgres.NewDatabase(dataSourceName, dbProperties)
|
||||||
|
case "file":
|
||||||
|
return sqlite3.NewDatabase(dataSourceName)
|
||||||
|
default:
|
||||||
|
return postgres.NewDatabase(dataSourceName, dbProperties)
|
||||||
|
}
|
||||||
|
}
|
||||||
41
keyserver/storage/storage_wasm.go
Normal file
41
keyserver/storage/storage_wasm.go
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
// 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 storage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/internal/sqlutil"
|
||||||
|
"github.com/matrix-org/dendrite/userapi/storage/accounts/sqlite3"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewDatabase(
|
||||||
|
dataSourceName string,
|
||||||
|
dbProperties sqlutil.DbProperties, // nolint:unparam
|
||||||
|
) (Database, error) {
|
||||||
|
uri, err := url.Parse(dataSourceName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Cannot use postgres implementation")
|
||||||
|
}
|
||||||
|
switch uri.Scheme {
|
||||||
|
case "postgres":
|
||||||
|
return nil, fmt.Errorf("Cannot use postgres implementation")
|
||||||
|
case "file":
|
||||||
|
return sqlite3.NewDatabase(dataSourceName)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("Cannot use postgres implementation")
|
||||||
|
}
|
||||||
|
}
|
||||||
32
keyserver/storage/tables/interface.go
Normal file
32
keyserver/storage/tables/interface.go
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
// 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 tables
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/matrix-org/dendrite/keyserver/api"
|
||||||
|
)
|
||||||
|
|
||||||
|
type OneTimeKeys interface {
|
||||||
|
SelectOneTimeKeys(ctx context.Context, userID, deviceID string, keyIDsWithAlgorithms []string) (map[string]json.RawMessage, error)
|
||||||
|
InsertOneTimeKeys(ctx context.Context, keys api.OneTimeKeys) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeviceKeys interface {
|
||||||
|
SelectDeviceKeysJSON(ctx context.Context, keys []api.DeviceKeys) error
|
||||||
|
InsertDeviceKeys(ctx context.Context, keys []api.DeviceKeys) error
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue