diff --git a/src/github.com/matrix-org/dendrite/clientapi/auth/authtypes/device.go b/src/github.com/matrix-org/dendrite/clientapi/auth/authtypes/device.go index 4feceada0..d9f2a4e06 100644 --- a/src/github.com/matrix-org/dendrite/clientapi/auth/authtypes/device.go +++ b/src/github.com/matrix-org/dendrite/clientapi/auth/authtypes/device.go @@ -16,8 +16,9 @@ package authtypes // Device represents a client's device (mobile, web, etc) type Device struct { - ID string - UserID string + UserID string + // The access_token granted to this device. + // This uniquely identifies the device from all other devices and clients. AccessToken string // TODO: display name, last used timestamp, keys, etc } diff --git a/src/github.com/matrix-org/dendrite/clientapi/auth/storage/devices/devices_table.go b/src/github.com/matrix-org/dendrite/clientapi/auth/storage/devices/devices_table.go new file mode 100644 index 000000000..81417a8be --- /dev/null +++ b/src/github.com/matrix-org/dendrite/clientapi/auth/storage/devices/devices_table.go @@ -0,0 +1,95 @@ +// 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 devices + +import ( + "database/sql" + "fmt" + "time" + + "github.com/matrix-org/dendrite/clientapi/auth/authtypes" + "github.com/matrix-org/gomatrixserverlib" +) + +const devicesSchema = ` +-- Stores data about devices. +CREATE TABLE IF NOT EXISTS devices ( + -- The access token granted to this device. This has to be the primary key + -- so we can distinguish which device is making a given request. + access_token TEXT NOT NULL PRIMARY KEY, + -- The Matrix user ID localpart for this device + localpart TEXT NOT NULL, + -- When this devices was first recognised on the network, as a unix timestamp (ms resolution). + created_ts BIGINT NOT NULL + -- TODO: device keys, device display names, last used ts and IP address?, token restrictions (if 3rd-party OAuth app) +); +` + +const insertDeviceSQL = "" + + "INSERT INTO devices(access_token, localpart, created_ts) VALUES ($1, $2, $3)" + +const selectDeviceByTokenSQL = "" + + "SELECT localpart FROM devices WHERE access_token = $1" + +// TODO: List devices, delete device API + +type devicesStatements struct { + insertDeviceStmt *sql.Stmt + selectDeviceByTokenStmt *sql.Stmt + serverName gomatrixserverlib.ServerName +} + +func (s *devicesStatements) prepare(db *sql.DB, server gomatrixserverlib.ServerName) (err error) { + _, err = db.Exec(devicesSchema) + if err != nil { + return + } + if s.insertDeviceStmt, err = db.Prepare(insertDeviceSQL); err != nil { + return + } + if s.selectDeviceByTokenStmt, err = db.Prepare(selectDeviceByTokenSQL); err != nil { + return + } + s.serverName = server + return +} + +// insertDevice creates a new device. Returns an error if a device with the same access token already exists. +// Returns the device on success. +func (s *devicesStatements) insertDevice(localpart, accessToken string) (dev *authtypes.Device, err error) { + createdTimeMS := time.Now().UnixNano() / 1000000 + if _, err = s.insertDeviceStmt.Exec(accessToken, localpart, createdTimeMS); err == nil { + dev = &authtypes.Device{ + UserID: makeUserID(localpart, s.serverName), + AccessToken: accessToken, + } + } + return +} + +func (s *devicesStatements) selectDeviceByToken(accessToken string) (*authtypes.Device, error) { + var dev authtypes.Device + var localpart string + err := s.selectDeviceByTokenStmt.QueryRow(accessToken).Scan(&localpart) + if err != nil { + dev.UserID = makeUserID(localpart, s.serverName) + dev.AccessToken = accessToken + } + return &dev, err +} + +func makeUserID(localpart string, server gomatrixserverlib.ServerName) string { + return fmt.Sprintf("@%s:%s", localpart, string(server)) +} diff --git a/src/github.com/matrix-org/dendrite/clientapi/auth/storage/devices/storage.go b/src/github.com/matrix-org/dendrite/clientapi/auth/storage/devices/storage.go index ef5fe495d..f3a4ac951 100644 --- a/src/github.com/matrix-org/dendrite/clientapi/auth/storage/devices/storage.go +++ b/src/github.com/matrix-org/dendrite/clientapi/auth/storage/devices/storage.go @@ -15,17 +15,30 @@ package devices import ( + "database/sql" + "github.com/matrix-org/dendrite/clientapi/auth/authtypes" + "github.com/matrix-org/gomatrixserverlib" ) // Database represents a device database. type Database struct { - // TODO + db *sql.DB + devices devicesStatements } // NewDatabase creates a new device database -func NewDatabase(dataSource string) (*Database, error) { - return &Database{}, nil +func NewDatabase(dataSourceName string, serverName gomatrixserverlib.ServerName) (*Database, error) { + var db *sql.DB + var err error + if db, err = sql.Open("postgres", dataSourceName); err != nil { + return nil, err + } + d := devicesStatements{} + if err = d.prepare(db, serverName); err != nil { + return nil, err + } + return &Database{db, d}, nil } // GetDeviceByAccessToken returns the device matching the given access token. diff --git a/src/github.com/matrix-org/dendrite/cmd/dendrite-client-api-server/main.go b/src/github.com/matrix-org/dendrite/cmd/dendrite-client-api-server/main.go index c75ca3235..e0a6616f2 100644 --- a/src/github.com/matrix-org/dendrite/cmd/dendrite-client-api-server/main.go +++ b/src/github.com/matrix-org/dendrite/cmd/dendrite-client-api-server/main.go @@ -86,7 +86,7 @@ func main() { if err != nil { log.Panicf("Failed to setup account database(%s): %s", accountDataSource, err.Error()) } - deviceDB, err := devices.NewDatabase(accountDataSource) + deviceDB, err := devices.NewDatabase(accountDataSource, serverName) if err != nil { log.Panicf("Failed to setup device database(%s): %s", accountDataSource, err.Error()) } diff --git a/src/github.com/matrix-org/dendrite/cmd/dendrite-sync-api-server/main.go b/src/github.com/matrix-org/dendrite/cmd/dendrite-sync-api-server/main.go index 090c0d88c..638eba3cd 100644 --- a/src/github.com/matrix-org/dendrite/cmd/dendrite-sync-api-server/main.go +++ b/src/github.com/matrix-org/dendrite/cmd/dendrite-sync-api-server/main.go @@ -46,6 +46,9 @@ func loadConfig(configPath string) (*config.Sync, error) { return nil, err } // check required fields + if cfg.ServerName == "" { + log.Fatalf("'server_name' must be supplied in %s", configPath) + } return &cfg, nil } @@ -74,7 +77,7 @@ func main() { } // TODO: DO NOT USE THIS DATA SOURCE (it's the sync one, not devices!) - deviceDB, err := devices.NewDatabase(cfg.DataSource) + deviceDB, err := devices.NewDatabase(cfg.DataSource, cfg.ServerName) if err != nil { log.Panicf("startup: failed to create device database with data source %s : %s", cfg.DataSource, err) } diff --git a/src/github.com/matrix-org/dendrite/cmd/syncserver-integration-tests/main.go b/src/github.com/matrix-org/dendrite/cmd/syncserver-integration-tests/main.go index b9c9caf1a..fb1fdfcb1 100644 --- a/src/github.com/matrix-org/dendrite/cmd/syncserver-integration-tests/main.go +++ b/src/github.com/matrix-org/dendrite/cmd/syncserver-integration-tests/main.go @@ -83,6 +83,7 @@ func getLastRequestError() error { var syncServerConfigFileContents = (`consumer_uris: ["` + kafkaURI + `"] roomserver_topic: "` + inputTopic + `" database: "` + testDatabase + `" +server_name: "localhost" `) func defaulting(value, defaultValue string) string { diff --git a/src/github.com/matrix-org/dendrite/syncapi/config/config.go b/src/github.com/matrix-org/dendrite/syncapi/config/config.go index ecfc5eaf7..433a7a2b6 100644 --- a/src/github.com/matrix-org/dendrite/syncapi/config/config.go +++ b/src/github.com/matrix-org/dendrite/syncapi/config/config.go @@ -14,6 +14,10 @@ package config +import ( + "github.com/matrix-org/gomatrixserverlib" +) + // Sync contains the config information necessary to spin up a sync-server process. type Sync struct { // The topic for events which are written by the room server output log. @@ -22,4 +26,6 @@ type Sync struct { KafkaConsumerURIs []string `yaml:"consumer_uris"` // The postgres connection config for connecting to the database e.g a postgres:// URI DataSource string `yaml:"database"` + // The server_name of the running process e.g "localhost" + ServerName gomatrixserverlib.ServerName `yaml:"server_name"` }