mirror of
https://github.com/matrix-org/dendrite
synced 2024-11-20 16:42:05 +01:00
Merge branch 'master' into babolivier/3pid-to-membership
This commit is contained in:
commit
f207917eb0
12 changed files with 614 additions and 32 deletions
11
INSTALL.md
11
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 <<EOF
|
||||
import base64;
|
||||
r = lambda n: base64.b64encode(open("/dev/urandom", "rb").read(n)).decode("utf8");
|
||||
print("-----BEGIN MATRIX PRIVATE KEY-----")
|
||||
print("Key-ID:", "ed25519:" + r(3).rstrip("="))
|
||||
print(r(32))
|
||||
print("-----END MATRIX PRIVATE KEY-----")
|
||||
EOF
|
||||
test -f matrix_key.pem || ./bin/generate-keys -private-key matrix_key.pem
|
||||
```
|
||||
|
||||
### Configuration
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
// 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 authtypes
|
||||
|
||||
// ThreePID represents a third-party identifier
|
||||
type ThreePID struct {
|
||||
Address string `json:"address"`
|
||||
Medium string `json:"medium"`
|
||||
}
|
|
@ -16,6 +16,7 @@ package accounts
|
|||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
|
||||
"github.com/matrix-org/dendrite/clientapi/auth/authtypes"
|
||||
"github.com/matrix-org/dendrite/common"
|
||||
|
@ -33,6 +34,7 @@ type Database struct {
|
|||
profiles profilesStatements
|
||||
memberships membershipStatements
|
||||
accountDatas accountDataStatements
|
||||
threepids threepidStatements
|
||||
serverName gomatrixserverlib.ServerName
|
||||
}
|
||||
|
||||
|
@ -63,7 +65,11 @@ func NewDatabase(dataSourceName string, serverName gomatrixserverlib.ServerName)
|
|||
if err = ac.prepare(db); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Database{db, partitions, a, p, m, ac, serverName}, nil
|
||||
t := threepidStatements{}
|
||||
if err = t.prepare(db); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Database{db, partitions, a, p, m, ac, t, serverName}, nil
|
||||
}
|
||||
|
||||
// GetAccountByPassword returns the account associated with the given localpart and password.
|
||||
|
@ -233,3 +239,51 @@ func hashPassword(plaintext string) (hash string, err error) {
|
|||
hashBytes, err := bcrypt.GenerateFromPassword([]byte(plaintext), bcrypt.DefaultCost)
|
||||
return string(hashBytes), err
|
||||
}
|
||||
|
||||
// Err3PIDInUse is the error returned when trying to save an association involving
|
||||
// a third-party identifier which is already associated to a local user.
|
||||
var Err3PIDInUse = errors.New("This third-party identifier is already in use")
|
||||
|
||||
// SaveThreePIDAssociation saves the association between a third party identifier
|
||||
// and a local Matrix user (identified by the user's ID's local part).
|
||||
// If the third-party identifier is already part of an association, returns Err3PIDInUse.
|
||||
// Returns an error if there was a problem talking to the database.
|
||||
func (d *Database) SaveThreePIDAssociation(threepid string, localpart string, medium string) (err error) {
|
||||
return common.WithTransaction(d.db, func(txn *sql.Tx) error {
|
||||
user, err := d.threepids.selectLocalpartForThreePID(txn, threepid, medium)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(user) > 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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
160
src/github.com/matrix-org/dendrite/clientapi/readers/threepid.go
Normal file
160
src/github.com/matrix-org/dendrite/clientapi/readers/threepid.go
Normal file
|
@ -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{}{},
|
||||
}
|
||||
}
|
|
@ -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",
|
||||
|
|
|
@ -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,
|
|
@ -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
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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(
|
||||
|
|
65
src/github.com/matrix-org/dendrite/cmd/generate-keys/main.go
Normal file
65
src/github.com/matrix-org/dendrite/cmd/generate-keys/main.go
Normal file
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue