-
-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement client API routes for 3PID handling (#205)
* Create package for handling 3pid processes and move invite processing there * Add database table and functions for tracking 3PIDs * Add structures and functions to interact with an ID server * Add handlers for 3PIDs management * Fix 3PIDs retrieval sending null if no 3PID known for a user * Include medium in database requests and function calls * Publish an association if it has been validated and requested * Add TODO markers for tursted ID server check * Use a structure instead of a map to represent a 3PID
- Loading branch information
1 parent
8c2e627
commit 960af3d
Showing
8 changed files
with
540 additions
and
16 deletions.
There are no files selected for viewing
21 changes: 21 additions & 0 deletions
21
src/github.com/matrix-org/dendrite/clientapi/auth/authtypes/threepid.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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"` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
120 changes: 120 additions & 0 deletions
120
src/github.com/matrix-org/dendrite/clientapi/auth/storage/accounts/threepid_table.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 changes: 160 additions & 0 deletions
160
src/github.com/matrix-org/dendrite/clientapi/readers/threepid.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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{}{}, | ||
} | ||
} |
Oops, something went wrong.