diff --git a/INSTALL.md b/INSTALL.md index e5a4b8474..37a08a5c7 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -71,21 +71,14 @@ Dendrite requires a postgres database engine, version 9.5 or later. ### Crypto key generation -Generate the keys (unlike synapse, dendrite doesn't autogen yet): +Generate the keys: ```bash # Generate a self-signed SSL cert for federation: test -f server.key || openssl req -x509 -newkey rsa:4096 -keyout server.key -out server.crt -days 3650 -nodes -subj /CN=localhost # generate ed25519 signing key -test -f matrix_key.pem || python3 > matrix_key.pem < 0 { + return Err3PIDInUse + } + + return d.threepids.insertThreePID(txn, threepid, medium, localpart) + }) +} + +// RemoveThreePIDAssociation removes the association involving a given third-party +// identifier. +// If no association exists involving this third-party identifier, returns nothing. +// If there was a problem talking to the database, returns an error. +func (d *Database) RemoveThreePIDAssociation(threepid string, medium string) (err error) { + return d.threepids.deleteThreePID(threepid, medium) +} + +// GetLocalpartForThreePID looks up the localpart associated with a given third-party +// identifier. +// If no association involves the given third-party idenfitier, returns an empty +// string. +// Returns an error if there was a problem talking to the database. +func (d *Database) GetLocalpartForThreePID(threepid string, medium string) (localpart string, err error) { + return d.threepids.selectLocalpartForThreePID(nil, threepid, medium) +} + +// GetThreePIDsForLocalpart looks up the third-party identifiers associated with +// a given local user. +// If no association is known for this user, returns an empty slice. +// Returns an error if there was an issue talking to the database. +func (d *Database) GetThreePIDsForLocalpart(localpart string) (threepids []authtypes.ThreePID, err error) { + return d.threepids.selectThreePIDsForLocalpart(localpart) +} diff --git a/src/github.com/matrix-org/dendrite/clientapi/auth/storage/accounts/threepid_table.go b/src/github.com/matrix-org/dendrite/clientapi/auth/storage/accounts/threepid_table.go new file mode 100644 index 000000000..55bcee6bc --- /dev/null +++ b/src/github.com/matrix-org/dendrite/clientapi/auth/storage/accounts/threepid_table.go @@ -0,0 +1,120 @@ +// 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 accounts + +import ( + "database/sql" + + "github.com/matrix-org/dendrite/clientapi/auth/authtypes" +) + +const threepidSchema = ` +-- Stores data about third party identifiers +CREATE TABLE IF NOT EXISTS account_threepid ( + -- The third party identifier + threepid TEXT NOT NULL, + -- The 3PID medium + medium TEXT NOT NULL DEFAULT 'email', + -- The localpart of the Matrix user ID associated to this 3PID + localpart TEXT NOT NULL, + + PRIMARY KEY(threepid, medium) +); + +CREATE INDEX IF NOT EXISTS account_threepid_localpart ON account_threepid(localpart); +` + +const selectLocalpartForThreePIDSQL = "" + + "SELECT localpart FROM account_threepid WHERE threepid = $1 AND medium = $2" + +const selectThreePIDsForLocalpartSQL = "" + + "SELECT threepid, medium FROM account_threepid WHERE localpart = $1" + +const insertThreePIDSQL = "" + + "INSERT INTO account_threepid (threepid, medium, localpart) VALUES ($1, $2, $3)" + +const deleteThreePIDSQL = "" + + "DELETE FROM account_threepid WHERE threepid = $1 AND medium = $2" + +type threepidStatements struct { + selectLocalpartForThreePIDStmt *sql.Stmt + selectThreePIDsForLocalpartStmt *sql.Stmt + insertThreePIDStmt *sql.Stmt + deleteThreePIDStmt *sql.Stmt +} + +func (s *threepidStatements) prepare(db *sql.DB) (err error) { + _, err = db.Exec(threepidSchema) + if err != nil { + return + } + if s.selectLocalpartForThreePIDStmt, err = db.Prepare(selectLocalpartForThreePIDSQL); err != nil { + return + } + if s.selectThreePIDsForLocalpartStmt, err = db.Prepare(selectThreePIDsForLocalpartSQL); err != nil { + return + } + if s.insertThreePIDStmt, err = db.Prepare(insertThreePIDSQL); err != nil { + return + } + if s.deleteThreePIDStmt, err = db.Prepare(deleteThreePIDSQL); err != nil { + return + } + + return +} + +func (s *threepidStatements) selectLocalpartForThreePID(txn *sql.Tx, threepid string, medium string) (localpart string, err error) { + var stmt *sql.Stmt + if txn != nil { + stmt = txn.Stmt(s.selectLocalpartForThreePIDStmt) + } else { + stmt = s.selectLocalpartForThreePIDStmt + } + err = stmt.QueryRow(threepid, medium).Scan(&localpart) + if err == sql.ErrNoRows { + return "", nil + } + return +} + +func (s *threepidStatements) selectThreePIDsForLocalpart(localpart string) (threepids []authtypes.ThreePID, err error) { + rows, err := s.selectThreePIDsForLocalpartStmt.Query(localpart) + if err != nil { + return + } + + threepids = []authtypes.ThreePID{} + for rows.Next() { + var threepid string + var medium string + if err = rows.Scan(&threepid, &medium); err != nil { + return + } + threepids = append(threepids, authtypes.ThreePID{threepid, medium}) + } + + return +} + +func (s *threepidStatements) insertThreePID(txn *sql.Tx, threepid string, medium string, localpart string) (err error) { + _, err = txn.Stmt(s.insertThreePIDStmt).Exec(threepid, medium, localpart) + return +} + +func (s *threepidStatements) deleteThreePID(threepid string, medium string) (err error) { + _, err = s.deleteThreePIDStmt.Exec(threepid, medium) + return +} diff --git a/src/github.com/matrix-org/dendrite/clientapi/readers/threepid.go b/src/github.com/matrix-org/dendrite/clientapi/readers/threepid.go new file mode 100644 index 000000000..4b86108e2 --- /dev/null +++ b/src/github.com/matrix-org/dendrite/clientapi/readers/threepid.go @@ -0,0 +1,160 @@ +// 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 readers + +import ( + "net/http" + + "github.com/matrix-org/dendrite/clientapi/auth/authtypes" + "github.com/matrix-org/dendrite/clientapi/auth/storage/accounts" + "github.com/matrix-org/dendrite/clientapi/httputil" + "github.com/matrix-org/dendrite/clientapi/jsonerror" + "github.com/matrix-org/dendrite/clientapi/threepid" + + "github.com/matrix-org/gomatrixserverlib" + "github.com/matrix-org/util" +) + +type reqTokenResponse struct { + SID string `json:"sid"` +} + +type threePIDsResponse struct { + ThreePIDs []authtypes.ThreePID `json:"threepids"` +} + +// RequestEmailToken implements: +// POST /account/3pid/email/requestToken +// POST /register/email/requestToken +func RequestEmailToken(req *http.Request, accountDB *accounts.Database) util.JSONResponse { + var body threepid.EmailAssociationRequest + if reqErr := httputil.UnmarshalJSONRequest(req, &body); reqErr != nil { + return *reqErr + } + + var resp reqTokenResponse + var err error + + // Check if the 3PID is already in use locally + localpart, err := accountDB.GetLocalpartForThreePID(body.Email, "email") + if err != nil { + return httputil.LogThenError(req, err) + } + + if len(localpart) > 0 { + return util.JSONResponse{ + Code: 400, + JSON: jsonerror.MatrixError{ + ErrCode: "M_THREEPID_IN_USE", + Err: accounts.Err3PIDInUse.Error(), + }, + } + } + + resp.SID, err = threepid.CreateSession(body) + if err != nil { + return httputil.LogThenError(req, err) + } + + return util.JSONResponse{ + Code: 200, + JSON: resp, + } +} + +// CheckAndSave3PIDAssociation implements POST /account/3pid +func CheckAndSave3PIDAssociation( + req *http.Request, accountDB *accounts.Database, device *authtypes.Device, +) util.JSONResponse { + var body threepid.EmailAssociationCheckRequest + if reqErr := httputil.UnmarshalJSONRequest(req, &body); reqErr != nil { + return *reqErr + } + + // Check if the association has been validated + verified, address, medium, err := threepid.CheckAssociation(body.Creds) + if err != nil { + return httputil.LogThenError(req, err) + } + + if !verified { + return util.JSONResponse{ + Code: 400, + JSON: jsonerror.MatrixError{ + ErrCode: "M_THREEPID_AUTH_FAILED", + Err: "Failed to auth 3pid", + }, + } + } + + if body.Bind { + // Publish the association on the identity server if requested + if err = threepid.PublishAssociation(body.Creds, device.UserID); err != nil { + return httputil.LogThenError(req, err) + } + } + + // Save the association in the database + localpart, _, err := gomatrixserverlib.SplitID('@', device.UserID) + if err != nil { + return httputil.LogThenError(req, err) + } + + if err = accountDB.SaveThreePIDAssociation(address, localpart, medium); err != nil { + return httputil.LogThenError(req, err) + } + + return util.JSONResponse{ + Code: 200, + JSON: struct{}{}, + } +} + +// GetAssociated3PIDs implements GET /account/3pid +func GetAssociated3PIDs( + req *http.Request, accountDB *accounts.Database, device *authtypes.Device, +) util.JSONResponse { + localpart, _, err := gomatrixserverlib.SplitID('@', device.UserID) + if err != nil { + return httputil.LogThenError(req, err) + } + + threepids, err := accountDB.GetThreePIDsForLocalpart(localpart) + if err != nil { + return httputil.LogThenError(req, err) + } + + return util.JSONResponse{ + Code: 200, + JSON: threePIDsResponse{threepids}, + } +} + +// Forget3PID implements POST /account/3pid/delete +func Forget3PID(req *http.Request, accountDB *accounts.Database) util.JSONResponse { + var body authtypes.ThreePID + if reqErr := httputil.UnmarshalJSONRequest(req, &body); reqErr != nil { + return *reqErr + } + + if err := accountDB.RemoveThreePIDAssociation(body.Address, body.Medium); err != nil { + return httputil.LogThenError(req, err) + } + + return util.JSONResponse{ + Code: 200, + JSON: struct{}{}, + } +} diff --git a/src/github.com/matrix-org/dendrite/clientapi/routing/routing.go b/src/github.com/matrix-org/dendrite/clientapi/routing/routing.go index 7a1078f18..950202c58 100644 --- a/src/github.com/matrix-org/dendrite/clientapi/routing/routing.go +++ b/src/github.com/matrix-org/dendrite/clientapi/routing/routing.go @@ -234,15 +234,28 @@ func Setup( // PUT requests, so we need to allow this method r0mux.Handle("/account/3pid", - common.MakeAPI("account_3pid", func(req *http.Request) util.JSONResponse { - // TODO: Get 3pid data for user ID - res := json.RawMessage(`{"threepids":[]}`) - return util.JSONResponse{ - Code: 200, - JSON: &res, - } + common.MakeAuthAPI("account_3pid", deviceDB, func(req *http.Request, device *authtypes.Device) util.JSONResponse { + return readers.GetAssociated3PIDs(req, accountDB, device) }), - ) + ).Methods("GET") + + r0mux.Handle("/account/3pid", + common.MakeAuthAPI("account_3pid", deviceDB, func(req *http.Request, device *authtypes.Device) util.JSONResponse { + return readers.CheckAndSave3PIDAssociation(req, accountDB, device) + }), + ).Methods("POST", "OPTIONS") + + unstableMux.Handle("/account/3pid/delete", + common.MakeAuthAPI("account_3pid", deviceDB, func(req *http.Request, device *authtypes.Device) util.JSONResponse { + return readers.Forget3PID(req, accountDB) + }), + ).Methods("POST", "OPTIONS") + + r0mux.Handle("/{path:(?:account/3pid|register)}/email/requestToken", + common.MakeAPI("account_3pid_request_token", func(req *http.Request) util.JSONResponse { + return readers.RequestEmailToken(req, accountDB) + }), + ).Methods("POST", "OPTIONS") // Riot logs get flooded unless this is handled r0mux.Handle("/presence/{userID}/status", diff --git a/src/github.com/matrix-org/dendrite/clientapi/thirdpartyinvites/thirdpartyinvites.go b/src/github.com/matrix-org/dendrite/clientapi/threepid/invites.go similarity index 99% rename from src/github.com/matrix-org/dendrite/clientapi/thirdpartyinvites/thirdpartyinvites.go rename to src/github.com/matrix-org/dendrite/clientapi/threepid/invites.go index c998a6522..244e5f632 100644 --- a/src/github.com/matrix-org/dendrite/clientapi/thirdpartyinvites/thirdpartyinvites.go +++ b/src/github.com/matrix-org/dendrite/clientapi/threepid/invites.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package thirdpartyinvites +package threepid import ( "encoding/json" @@ -66,7 +66,7 @@ type idServerStoreInviteResponse struct { PublicKeys []common.PublicKey `json:"public_keys"` } -// CheckAndProcess analyses the body of an incoming membership request. +// CheckAndProcessInvite analyses the body of an incoming membership request. // If the fields relative to a third-party-invite are all supplied, lookups the // matching Matrix ID from the given identity server. If no Matrix ID is // associated to the given 3PID, asks the identity server to store the invite @@ -79,7 +79,7 @@ type idServerStoreInviteResponse struct { // must be processed as a non-3PID membership request. In the latter case, // fills the Matrix ID in the request body so a normal invite membership event // can be emitted. -func CheckAndProcess( +func CheckAndProcessInvite( req *http.Request, device *authtypes.Device, body *MembershipRequest, cfg config.Dendrite, queryAPI api.RoomserverQueryAPI, db *accounts.Database, producer *producers.RoomserverProducer, membership string, roomID string, diff --git a/src/github.com/matrix-org/dendrite/clientapi/threepid/threepid.go b/src/github.com/matrix-org/dendrite/clientapi/threepid/threepid.go new file mode 100644 index 000000000..2ec4599a6 --- /dev/null +++ b/src/github.com/matrix-org/dendrite/clientapi/threepid/threepid.go @@ -0,0 +1,156 @@ +// 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 threepid + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + "net/url" + "strconv" + "strings" +) + +// EmailAssociationRequest represents the request defined at https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-register-email-requesttoken +type EmailAssociationRequest struct { + IDServer string `json:"id_server"` + Secret string `json:"client_secret"` + Email string `json:"email"` + SendAttempt int `json:"send_attempt"` +} + +// EmailAssociationCheckRequest represents the request defined at https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-account-3pid +type EmailAssociationCheckRequest struct { + Creds Credentials `json:"threePidCreds"` + Bind bool `json:"bind"` +} + +// Credentials represents the "ThreePidCredentials" structure defined at https://matrix.org/docs/spec/client_server/r0.2.0.html#post-matrix-client-r0-account-3pid +type Credentials struct { + SID string `json:"sid"` + IDServer string `json:"id_server"` + Secret string `json:"client_secret"` +} + +// CreateSession creates a session on an identity server. +// Returns the session's ID. +// Returns an error if there was a problem sending the request or decoding the +// response, or if the identity server responded with a non-OK status. +func CreateSession(req EmailAssociationRequest) (string, error) { + // TODO: Check if the ID server is trusted + + // Create a session on the ID server + postURL := fmt.Sprintf("https://%s/_matrix/identity/api/v1/validate/email/requestToken", req.IDServer) + + data := url.Values{} + data.Add("client_secret", req.Secret) + data.Add("email", req.Email) + data.Add("send_attempt", strconv.Itoa(req.SendAttempt)) + + request, err := http.NewRequest("POST", postURL, strings.NewReader(data.Encode())) + if err != nil { + return "", err + } + request.Header.Add("Content-Type", "application/x-www-form-urlencoded") + + client := http.Client{} + resp, err := client.Do(request) + if err != nil { + return "", err + } + + // Error if the status isn't OK + if resp.StatusCode != http.StatusOK { + return "", fmt.Errorf("Could not create a session on the server %s", req.IDServer) + } + + // Extract the SID from the response and return it + var sid struct { + SID string `json:"sid"` + } + err = json.NewDecoder(resp.Body).Decode(&sid) + + return sid.SID, err +} + +// CheckAssociation checks the status of an ongoing association validation on an +// identity server. +// Returns a boolean set to true if the association has been validated, false if not. +// If the association has been validated, also returns the related third-party +// identifier and its medium. +// Returns an error if there was a problem sending the request or decoding the +// response, or if the identity server responded with a non-OK status. +func CheckAssociation(creds Credentials) (bool, string, string, error) { + // TODO: Check if the ID server is trusted + url := fmt.Sprintf("https://%s/_matrix/identity/api/v1/3pid/getValidated3pid?sid=%s&client_secret=%s", creds.IDServer, creds.SID, creds.Secret) + resp, err := http.Get(url) + if err != nil { + return false, "", "", err + } + + var respBody struct { + Medium string `json:"medium"` + ValidatedAt int64 `json:"validated_at"` + Address string `json:"address"` + ErrCode string `json:"errcode"` + Error string `json:"error"` + } + + if err = json.NewDecoder(resp.Body).Decode(&respBody); err != nil { + return false, "", "", err + } + + if respBody.ErrCode == "M_SESSION_NOT_VALIDATED" { + return false, "", "", nil + } else if len(respBody.ErrCode) > 0 { + return false, "", "", errors.New(respBody.Error) + } + + return true, respBody.Address, respBody.Medium, nil +} + +// PublishAssociation publishes a validated association between a third-party +// identifier and a Matrix ID. +// Returns an error if there was a problem sending the request or decoding the +// response, or if the identity server responded with a non-OK status. +func PublishAssociation(creds Credentials, userID string) error { + // TODO: Check if the ID server is trusted + postURL := fmt.Sprintf("https://%s/_matrix/identity/api/v1/3pid/bind", creds.IDServer) + + data := url.Values{} + data.Add("sid", creds.SID) + data.Add("client_secret", creds.Secret) + data.Add("mxid", userID) + + request, err := http.NewRequest("POST", postURL, strings.NewReader(data.Encode())) + if err != nil { + return err + } + request.Header.Add("Content-Type", "application/x-www-form-urlencoded") + + client := http.Client{} + resp, err := client.Do(request) + if err != nil { + return err + } + + // Error if the status isn't OK + if resp.StatusCode != http.StatusOK { + return fmt.Errorf("Could not publish the association on the server %s", creds.IDServer) + } + + return nil +} diff --git a/src/github.com/matrix-org/dendrite/clientapi/writers/membership.go b/src/github.com/matrix-org/dendrite/clientapi/writers/membership.go index d58230473..6dd52a003 100644 --- a/src/github.com/matrix-org/dendrite/clientapi/writers/membership.go +++ b/src/github.com/matrix-org/dendrite/clientapi/writers/membership.go @@ -23,7 +23,7 @@ import ( "github.com/matrix-org/dendrite/clientapi/httputil" "github.com/matrix-org/dendrite/clientapi/jsonerror" "github.com/matrix-org/dendrite/clientapi/producers" - "github.com/matrix-org/dendrite/clientapi/thirdpartyinvites" + "github.com/matrix-org/dendrite/clientapi/threepid" "github.com/matrix-org/dendrite/common" "github.com/matrix-org/dendrite/common/config" "github.com/matrix-org/dendrite/roomserver/api" @@ -39,12 +39,12 @@ func SendMembership( roomID string, membership string, cfg config.Dendrite, queryAPI api.RoomserverQueryAPI, producer *producers.RoomserverProducer, ) util.JSONResponse { - var body thirdpartyinvites.MembershipRequest + var body threepid.MembershipRequest if reqErr := httputil.UnmarshalJSONRequest(req, &body); reqErr != nil { return *reqErr } - if res := thirdpartyinvites.CheckAndProcess( + if res := threepid.CheckAndProcessInvite( req, device, &body, cfg, queryAPI, accountDB, producer, membership, roomID, ); res != nil { return *res @@ -129,7 +129,7 @@ func loadProfile(userID string, cfg config.Dendrite, accountDB *accounts.Databas // In the latter case, if there was an issue retrieving the user ID from the request body, // returns a JSONResponse with a corresponding error code and message. func getMembershipStateKey( - body thirdpartyinvites.MembershipRequest, device *authtypes.Device, membership string, + body threepid.MembershipRequest, device *authtypes.Device, membership string, ) (stateKey string, reason string, response *util.JSONResponse) { if membership == "ban" || membership == "unban" || membership == "kick" || membership == "invite" { // If we're in this case, the state key is contained in the request body, diff --git a/src/github.com/matrix-org/dendrite/cmd/dendrite-monolith-server/main.go b/src/github.com/matrix-org/dendrite/cmd/dendrite-monolith-server/main.go index 25b269b89..8cc1ccbb6 100644 --- a/src/github.com/matrix-org/dendrite/cmd/dendrite-monolith-server/main.go +++ b/src/github.com/matrix-org/dendrite/cmd/dendrite-monolith-server/main.go @@ -282,7 +282,7 @@ func (m *monolith) setupConsumers() { m.cfg, m.kafkaConsumer(), m.accountDB, m.queryAPI, ) if err = clientAPIConsumer.Start(); err != nil { - log.Panicf("startup: failed to start room server consumer") + log.Panicf("startup: failed to start room server consumer: %s", err) } syncAPIRoomConsumer := syncapi_consumers.NewOutputRoomEvent( diff --git a/src/github.com/matrix-org/dendrite/cmd/generate-keys/main.go b/src/github.com/matrix-org/dendrite/cmd/generate-keys/main.go new file mode 100644 index 000000000..b807c2673 --- /dev/null +++ b/src/github.com/matrix-org/dendrite/cmd/generate-keys/main.go @@ -0,0 +1,65 @@ +// 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 main + +import ( + "flag" + "fmt" + "log" + "os" + + "github.com/matrix-org/dendrite/common/test" +) + +const usage = `Usage: %s + +Generate key files which are required by dendrite. + +Arguments: + +` + +var ( + tlsCertFile = flag.String("tls-cert", "", "An X509 certificate file to generate for use for TLS") + tlsKeyFile = flag.String("tls-key", "", "An RSA private key file to generate for use for TLS") + privateKeyFile = flag.String("private-key", "", "An Ed25519 private key to generate for use for object signing") +) + +func main() { + flag.Usage = func() { + fmt.Fprintf(os.Stderr, usage, os.Args[0]) + flag.PrintDefaults() + } + + flag.Parse() + + if *tlsCertFile != "" || *tlsKeyFile != "" { + if *tlsCertFile == "" || *tlsKeyFile == "" { + log.Fatal("Zero or both of --tls-key and --tls-cert must be supplied") + } + if err := test.NewTLSKey(*tlsKeyFile, *tlsCertFile); err != nil { + panic(err) + } + fmt.Printf("Created TLS cert file: %s\n", *tlsCertFile) + fmt.Printf("Created TLS key file: %s\n", *tlsKeyFile) + } + + if *privateKeyFile != "" { + if err := test.NewMatrixKey(*privateKeyFile); err != nil { + panic(err) + } + fmt.Printf("Created private key file: %s\n", *privateKeyFile) + } +} diff --git a/src/github.com/matrix-org/dendrite/common/test/config.go b/src/github.com/matrix-org/dendrite/common/test/config.go index 0efba5dfb..1e5d08210 100644 --- a/src/github.com/matrix-org/dendrite/common/test/config.go +++ b/src/github.com/matrix-org/dendrite/common/test/config.go @@ -62,11 +62,11 @@ func MakeConfig(configDir, kafkaURI, database, host string, startPort int) (*con tlsKeyPath := filepath.Join(configDir, TLSCertFile) mediaBasePath := filepath.Join(configDir, MediaDir) - if err := newMatrixKey(serverKeyPath); err != nil { + if err := NewMatrixKey(serverKeyPath); err != nil { return nil, 0, err } - if err := newTLSKey(tlsKeyPath, tlsCertPath); err != nil { + if err := NewTLSKey(tlsKeyPath, tlsCertPath); err != nil { return nil, 0, err } @@ -119,8 +119,8 @@ func WriteConfig(cfg *config.Dendrite, configDir string) error { return nil } -// newMatrixKey generates a new ed25519 matrix server key and writes it to a file. -func newMatrixKey(matrixKeyPath string) error { +// NewMatrixKey generates a new ed25519 matrix server key and writes it to a file. +func NewMatrixKey(matrixKeyPath string) error { var data [35]byte if _, err := rand.Read(data[:]); err != nil { return err @@ -145,8 +145,8 @@ func newMatrixKey(matrixKeyPath string) error { const certificateDuration = time.Hour * 24 * 365 * 10 -// newTLSKey generates a new RSA TLS key and certificate and writes it to a file. -func newTLSKey(tlsKeyPath, tlsCertPath string) error { +// NewTLSKey generates a new RSA TLS key and certificate and writes it to a file. +func NewTLSKey(tlsKeyPath, tlsCertPath string) error { priv, err := rsa.GenerateKey(rand.Reader, 4096) if err != nil { return err