mirror of
https://github.com/matrix-org/dendrite.git
synced 2025-12-11 08:53:11 -06:00
Add tests for config parsing
This commit is contained in:
parent
063da2dc56
commit
5bed22fe79
|
|
@ -17,13 +17,11 @@ package config
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/base64"
|
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/matrix-org/gomatrixserverlib"
|
"github.com/matrix-org/gomatrixserverlib"
|
||||||
"golang.org/x/crypto/ed25519"
|
"golang.org/x/crypto/ed25519"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -32,7 +30,7 @@ import (
|
||||||
|
|
||||||
// Version is the current version of the config format.
|
// Version is the current version of the config format.
|
||||||
// This will change whenever we make breaking changes to the config format.
|
// This will change whenever we make breaking changes to the config format.
|
||||||
const Version = "0"
|
const Version = "v0"
|
||||||
|
|
||||||
// Dendrite contains all the config used by a dendrite process.
|
// Dendrite contains all the config used by a dendrite process.
|
||||||
type Dendrite struct {
|
type Dendrite struct {
|
||||||
|
|
@ -104,11 +102,11 @@ type Dendrite struct {
|
||||||
|
|
||||||
// Postgres Config
|
// Postgres Config
|
||||||
Database struct {
|
Database struct {
|
||||||
Media DataSource `yaml:"media"`
|
MediaServer DataSource `yaml:"media_server"`
|
||||||
Account DataSource `yaml:"account"`
|
Account DataSource `yaml:"account"`
|
||||||
ServerKeys DataSource `yaml:"server_keys"`
|
ServerKeys DataSource `yaml:"server_keys"`
|
||||||
Sync DataSource `yaml:"sync"`
|
SyncServer DataSource `yaml:"sync_server"`
|
||||||
Roomserver DataSource `yaml:"roomserver"`
|
RoomServer DataSource `yaml:"room_server"`
|
||||||
} `yaml:"database"`
|
} `yaml:"database"`
|
||||||
|
|
||||||
// The internal addresses the components will listen on.
|
// The internal addresses the components will listen on.
|
||||||
|
|
@ -118,7 +116,7 @@ type Dendrite struct {
|
||||||
ClientAPI Address `yaml:"client_api"`
|
ClientAPI Address `yaml:"client_api"`
|
||||||
FederationAPI Address `yaml:"federation_api"`
|
FederationAPI Address `yaml:"federation_api"`
|
||||||
SyncAPI Address `yaml:"sync_api"`
|
SyncAPI Address `yaml:"sync_api"`
|
||||||
Roomserver Address `yaml:"roomserver"`
|
RoomServer Address `yaml:"room_server"`
|
||||||
} `yaml:"listen"`
|
} `yaml:"listen"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -189,18 +187,19 @@ func loadConfig(
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.Matrix.KeyID, config.Matrix.PrivateKey, err = readKey(string(privateKeyData)); err != nil {
|
if config.Matrix.KeyID, config.Matrix.PrivateKey, err = readKeyPEM(privateKeyPath, privateKeyData); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, certPath := range config.Matrix.FederationCertificatePaths {
|
for _, certPath := range config.Matrix.FederationCertificatePaths {
|
||||||
pemData, err := readFile(absPath(configDirPath, certPath))
|
absCertPath := absPath(configDirPath, certPath)
|
||||||
|
pemData, err := readFile(absCertPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
fingerprint := fingerprintPEM(pemData)
|
fingerprint := fingerprintPEM(pemData)
|
||||||
if fingerprint == nil {
|
if fingerprint == nil {
|
||||||
return nil, fmt.Errorf("no certificate PEM data in %q", certPath)
|
return nil, fmt.Errorf("no certificate PEM data in %q", absCertPath)
|
||||||
}
|
}
|
||||||
config.Matrix.TLSFingerPrints = append(config.Matrix.TLSFingerPrints, *fingerprint)
|
config.Matrix.TLSFingerPrints = append(config.Matrix.TLSFingerPrints, *fingerprint)
|
||||||
}
|
}
|
||||||
|
|
@ -261,16 +260,16 @@ func (config *Dendrite) check() error {
|
||||||
checkNotEmpty("media.base_path", string(config.Media.BasePath))
|
checkNotEmpty("media.base_path", string(config.Media.BasePath))
|
||||||
checkNotEmpty("kafka.topics.input_room_event", string(config.Kafka.Topics.InputRoomEvent))
|
checkNotEmpty("kafka.topics.input_room_event", string(config.Kafka.Topics.InputRoomEvent))
|
||||||
checkNotEmpty("kafka.topics.output_room_event", string(config.Kafka.Topics.InputRoomEvent))
|
checkNotEmpty("kafka.topics.output_room_event", string(config.Kafka.Topics.InputRoomEvent))
|
||||||
checkNotEmpty("database.media", string(config.Database.Media))
|
checkNotEmpty("database.media_server", string(config.Database.MediaServer))
|
||||||
checkNotEmpty("database.account", string(config.Database.Account))
|
checkNotEmpty("database.account", string(config.Database.Account))
|
||||||
checkNotEmpty("database.server_keys", string(config.Database.ServerKeys))
|
checkNotEmpty("database.server_keys", string(config.Database.ServerKeys))
|
||||||
checkNotEmpty("database.sync", string(config.Database.Sync))
|
checkNotEmpty("database.sync_server", string(config.Database.SyncServer))
|
||||||
checkNotEmpty("database.roomserver", string(config.Database.Roomserver))
|
checkNotEmpty("database.room_server", string(config.Database.RoomServer))
|
||||||
checkNotEmpty("listen.media_api", string(config.Listen.MediaAPI))
|
checkNotEmpty("listen.media_api", string(config.Listen.MediaAPI))
|
||||||
checkNotEmpty("listen.client_api", string(config.Listen.ClientAPI))
|
checkNotEmpty("listen.client_api", string(config.Listen.ClientAPI))
|
||||||
checkNotEmpty("listen.federation_api", string(config.Listen.FederationAPI))
|
checkNotEmpty("listen.federation_api", string(config.Listen.FederationAPI))
|
||||||
checkNotEmpty("listen.sync_api", string(config.Listen.SyncAPI))
|
checkNotEmpty("listen.sync_api", string(config.Listen.SyncAPI))
|
||||||
checkNotEmpty("listen.roomserver", string(config.Listen.Roomserver))
|
checkNotEmpty("listen.room_server", string(config.Listen.RoomServer))
|
||||||
|
|
||||||
if problems != nil {
|
if problems != nil {
|
||||||
return Error{problems}
|
return Error{problems}
|
||||||
|
|
@ -286,38 +285,28 @@ func absPath(dir string, path Path) string {
|
||||||
return filepath.Join(dir, string(path))
|
return filepath.Join(dir, string(path))
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadPrivateKey(path string) (gomatrixserverlib.KeyID, ed25519.PrivateKey, error) {
|
func readKeyPEM(path string, data []byte) (gomatrixserverlib.KeyID, ed25519.PrivateKey, error) {
|
||||||
keyData, err := ioutil.ReadFile(path)
|
for {
|
||||||
if err != nil {
|
var keyBlock *pem.Block
|
||||||
return "", nil, err
|
keyBlock, data = pem.Decode(data)
|
||||||
|
if data == nil {
|
||||||
|
return "", nil, fmt.Errorf("no matrix private key PEM data in %q", path)
|
||||||
|
}
|
||||||
|
if keyBlock.Type == "MATRIX PRIVATE KEY" {
|
||||||
|
keyID := keyBlock.Headers["Key-ID"]
|
||||||
|
if keyID == "" {
|
||||||
|
return "", nil, fmt.Errorf("missing key ID in PEM data in %q", path)
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(keyID, "ed25519:") {
|
||||||
|
return "", nil, fmt.Errorf("key ID %q doesn't start with \"ed25519:\" in %q", keyID, path)
|
||||||
|
}
|
||||||
|
_, privKey, err := ed25519.GenerateKey(bytes.NewReader(keyBlock.Bytes))
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
return gomatrixserverlib.KeyID(keyID), privKey, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return readKey(string(keyData))
|
|
||||||
}
|
|
||||||
|
|
||||||
// readKey reads a server's private ed25519 key.
|
|
||||||
// Otherwise the key is the key ID and the base64 encoded private key
|
|
||||||
// separated by a single space character.
|
|
||||||
// E.g "ed25519:abcd ABCDEFGHIJKLMNOPabcdefghijklmnop01234567890"
|
|
||||||
func readKey(key string) (gomatrixserverlib.KeyID, ed25519.PrivateKey, error) {
|
|
||||||
var keyID gomatrixserverlib.KeyID
|
|
||||||
var seed io.Reader
|
|
||||||
// TODO: We should be reading this from a PEM formatted file instead of
|
|
||||||
// our own custom format.
|
|
||||||
parts := strings.SplitN(strings.TrimRight(key, "\n"), " ", 2)
|
|
||||||
keyID = gomatrixserverlib.KeyID(parts[0])
|
|
||||||
if len(parts) != 2 {
|
|
||||||
return "", nil, fmt.Errorf("Invalid server key: %q", key)
|
|
||||||
}
|
|
||||||
seedBytes, err := base64.RawStdEncoding.DecodeString(parts[1])
|
|
||||||
if err != nil {
|
|
||||||
return "", nil, err
|
|
||||||
}
|
|
||||||
seed = bytes.NewReader(seedBytes)
|
|
||||||
_, privKey, err := ed25519.GenerateKey(seed)
|
|
||||||
if err != nil {
|
|
||||||
return "", nil, err
|
|
||||||
}
|
|
||||||
return keyID, privKey, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func fingerprintPEM(data []byte) *gomatrixserverlib.TLSFingerprint {
|
func fingerprintPEM(data []byte) *gomatrixserverlib.TLSFingerprint {
|
||||||
|
|
|
||||||
131
src/github.com/matrix-org/dendrite/common/config/config_test.go
Normal file
131
src/github.com/matrix-org/dendrite/common/config/config_test.go
Normal file
|
|
@ -0,0 +1,131 @@
|
||||||
|
// Copyright 2017 Vector Creations Ltd
|
||||||
|
//
|
||||||
|
// 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 config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLoadConfigRelative(t *testing.T) {
|
||||||
|
_, err := loadConfig("/my/config/dir", []byte(testConfig),
|
||||||
|
mockReadFile{
|
||||||
|
"/my/config/dir/matrix_key.pem": testKey,
|
||||||
|
"/my/config/dir/tls_cert.pem": testCert,
|
||||||
|
}.readFile,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
t.Error("failed to load config:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const testConfig = `
|
||||||
|
version: v0
|
||||||
|
matrix:
|
||||||
|
server_name: localhost
|
||||||
|
private_key: matrix_key.pem
|
||||||
|
federation_certificates: [tls_cert.pem]
|
||||||
|
media:
|
||||||
|
base_path: media_store
|
||||||
|
kafka:
|
||||||
|
addresses: ["localhost:9092"]
|
||||||
|
topics:
|
||||||
|
input_room_event: input.room
|
||||||
|
output_room_event: output.room
|
||||||
|
database:
|
||||||
|
media_server: "postgresql:///media_server"
|
||||||
|
account: "postgresql:///account"
|
||||||
|
server_keys: "postgresql:///server_keys"
|
||||||
|
sync_server: "postgresql:///sync_server"
|
||||||
|
room_server: "postgresql:///room_server"
|
||||||
|
listen:
|
||||||
|
room_server: "localhost:7770"
|
||||||
|
client_api: "localhost:7771"
|
||||||
|
federation_api: "localhost:7772"
|
||||||
|
sync_api: "localhost:7773"
|
||||||
|
media_api: "localhost:7774"
|
||||||
|
`
|
||||||
|
|
||||||
|
type mockReadFile map[string]string
|
||||||
|
|
||||||
|
func (m mockReadFile) readFile(path string) ([]byte, error) {
|
||||||
|
data, ok := m[path]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("no such file %q", path)
|
||||||
|
}
|
||||||
|
return []byte(data), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadKey(t *testing.T) {
|
||||||
|
keyID, _, err := readKeyPEM("path/to/key", []byte(testKey))
|
||||||
|
if err != nil {
|
||||||
|
t.Error("failed to load private key:", err)
|
||||||
|
}
|
||||||
|
wantKeyID := "ed25519:c8NsuQ"
|
||||||
|
if wantKeyID != string(keyID) {
|
||||||
|
t.Errorf("wanted key ID to be %q, got %q", wantKeyID, keyID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const testKey = `
|
||||||
|
-----BEGIN MATRIX PRIVATE KEY-----
|
||||||
|
Key-ID: ed25519:c8NsuQ
|
||||||
|
7KRZiZ2sTyRR8uqqUjRwczuwRXXkUMYIUHq4Mc3t4bE=
|
||||||
|
-----END MATRIX PRIVATE KEY-----
|
||||||
|
`
|
||||||
|
|
||||||
|
func TestFingerprintPEM(t *testing.T) {
|
||||||
|
got := fingerprintPEM([]byte(testCert))
|
||||||
|
if got == nil {
|
||||||
|
t.Error("failed to calculate fingerprint")
|
||||||
|
}
|
||||||
|
if string(got.SHA256) != testCertFingerprint {
|
||||||
|
t.Errorf("bad fingerprint: wanted %q got %q", got, testCertFingerprint)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const testCertFingerprint = "56.\\SPQxE\xd4\x95\xfb\xf6\xd5\x04\x91\xcb/\x07\xb1^\x88\x08\xe3\xc1p\xdfY\x04\x19w\xcb"
|
||||||
|
|
||||||
|
const testCert = `
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIE0zCCArugAwIBAgIJAPype3u24LJeMA0GCSqGSIb3DQEBCwUAMAAwHhcNMTcw
|
||||||
|
NjEzMTQyODU4WhcNMTgwNjEzMTQyODU4WjAAMIICIjANBgkqhkiG9w0BAQEFAAOC
|
||||||
|
Ag8AMIICCgKCAgEA3vNSr7lCh/alxPFqairp/PYohwdsqPvOD7zf7dJCNhy0gbdC
|
||||||
|
9/APwIbPAPL9nU+o9ud1ACNCKBCQin/9LnI5vd5pa/Ne+mmRADDLB/BBBoywSJWG
|
||||||
|
NSfKJ9n3XY1bjgtqi53uUh+RDdQ7sXudDqCUxiiJZmS7oqK/mp88XXAgCbuXUY29
|
||||||
|
GmzbbDz37vntuSxDgUOnJ8uPSvRp5YPKogA3JwW1SyrlLt4Z30CQ6nH3Y2Q5SVfJ
|
||||||
|
NIQyMrnwyjA9bCdXezv1cLXoTYn7U9BRyzXTZeXs3y3ldnRfISXN35CU04Az1F8j
|
||||||
|
lfj7nXMEqI/qAj/qhxZ8nVBB+rpNOZy9RJko3O+G5Qa/EvzkQYV1rW4TM2Yme88A
|
||||||
|
QyJspoV/0bXk6gG987PonK2Uk5djxSULhnGVIqswydyH0Nzb+slRp2bSoWbaNlee
|
||||||
|
+6TIeiyTQYc055pCHOp22gtLrC5LQGchksi02St2ZzRHdnlfqCJ8S9sS7x3trzds
|
||||||
|
cYueg1sGI+O8szpQ3eUM7OhJOBrx6OlR7+QYnQg1wr/V+JAz1qcyTC1URcwfeqtg
|
||||||
|
QjxFdBD9LfCtfK+AO51H9ugtsPJqOh33PmvfvUBEM05OHCA0lNaWJHROGpm4T4cc
|
||||||
|
YQI9JQk/0lB7itF1qK5RG74qgKdjkBkfZxi0OqkUgHk6YHtJlKfET8zfrtcCAwEA
|
||||||
|
AaNQME4wHQYDVR0OBBYEFGwb0NgH0Zr7Ga23njEJ85Ozf8M9MB8GA1UdIwQYMBaA
|
||||||
|
FGwb0NgH0Zr7Ga23njEJ85Ozf8M9MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEL
|
||||||
|
BQADggIBAKU3RHXggbq/pLhGinU5q/9QT0TB/0bBnF1wNFkKQC0FrNJ+ZnBNmusy
|
||||||
|
oqOn7DEohBCCDxT0kgOC05gLEsGLkSXlVyqCsPFfycCFhtu1QzSRtQNRxB3pW3Wq
|
||||||
|
4/RFVYv0PGBjVBKxImQlEmXJWEDwemGKqDQZPtqR/FTHTbJcaT0xQr5+1oG6lawt
|
||||||
|
I/2cW6GQ0kYW/Szps8FgNdSNgVqCjjNIzBYbWhRWMx/63qD1ReUbY7/Yw9KKT8nK
|
||||||
|
zXERpbTM9k+Pnm0g9Gep+9HJ1dBFJeuTPugKeSeyqg2OJbENw1hxGs/HjBXw7580
|
||||||
|
ioiMn/kMj6Tg/f3HCfKrdHHBFQw0/fJW6o17QImYIpPOPzc5RjXBrCJWb34kxqEd
|
||||||
|
NQdKgejWiV/LlVsguIF8hVZH2kRzvoyypkVUtSUYGmjvA5UXoORQZfJ+b41llq1B
|
||||||
|
GcSF6iaVbAFKnsUyyr1i9uHz/6Muqflphv/SfZxGheIn5u3PnhXrzDagvItjw0NS
|
||||||
|
n0Xq64k7fc42HXJpF8CGBkSaIhtlzcruO+vqR80B9r62+D0V7VmHOnP135MT6noU
|
||||||
|
8F0JQfEtP+I8NII5jHSF/khzSgP5g80LS9tEc2ILnIHK1StkInAoRQQ+/HsQsgbz
|
||||||
|
ANAf5kxmMsM0zlN2hkxl0H6o7wKlBSw3RI3cjfilXiMWRPJrzlc4
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
`
|
||||||
Loading…
Reference in a new issue