From 5552e1f3a848e786451096e5569793982d4329d3 Mon Sep 17 00:00:00 2001 From: Kegsay Date: Tue, 7 Mar 2017 13:43:32 +0000 Subject: [PATCH] Extract access tokens from HTTP requests (#15) --- .../dendrite/clientapi/auth/auth.go | 55 +++++++++++++++++++ .../dendrite/clientapi/jsonerror/jsonerror.go | 19 ++++++- .../dendrite/clientapi/readers/sync.go | 8 ++- .../dendrite/clientapi/writers/sendmessage.go | 12 +++- 4 files changed, 91 insertions(+), 3 deletions(-) create mode 100644 src/github.com/matrix-org/dendrite/clientapi/auth/auth.go diff --git a/src/github.com/matrix-org/dendrite/clientapi/auth/auth.go b/src/github.com/matrix-org/dendrite/clientapi/auth/auth.go new file mode 100644 index 00000000..e3d8e6fa --- /dev/null +++ b/src/github.com/matrix-org/dendrite/clientapi/auth/auth.go @@ -0,0 +1,55 @@ +package auth + +import ( + "fmt" + "net/http" + "strings" + + "github.com/matrix-org/dendrite/clientapi/jsonerror" + "github.com/matrix-org/util" +) + +// VerifyAccessToken verifies that an access token was supplied in the given HTTP request +// and returns the user ID it corresponds to. Returns resErr (an error response which can be +// sent to the client) if the token is invalid or there was a problem querying the database. +func VerifyAccessToken(req *http.Request) (userID string, resErr *util.JSONResponse) { + token, tokenErr := extractAccessToken(req) + if tokenErr != nil { + resErr = &util.JSONResponse{ + Code: 401, + JSON: jsonerror.MissingToken(tokenErr.Error()), + } + return + } + if token == "fail" { + res := util.ErrorResponse(fmt.Errorf("Fatal error")) + resErr = &res + } + // TODO: Check the token against the database + return +} + +// extractAccessToken from a request, or return an error detailing what went wrong. The +// error message MUST be human-readable and comprehensible to the client. +func extractAccessToken(req *http.Request) (string, error) { + // cf https://github.com/matrix-org/synapse/blob/v0.19.2/synapse/api/auth.py#L631 + authBearer := req.Header.Get("Authorization") + queryToken := req.URL.Query().Get("access_token") + if authBearer != "" && queryToken != "" { + return "", fmt.Errorf("mixing Authorization headers and access_token query parameters") + } + + if queryToken != "" { + return queryToken, nil + } + + if authBearer != "" { + parts := strings.SplitN(authBearer, " ", 2) + if len(parts) != 2 || parts[0] != "Bearer" { + return "", fmt.Errorf("invalid Authorization header") + } + return parts[1], nil + } + + return "", fmt.Errorf("missing access token") +} diff --git a/src/github.com/matrix-org/dendrite/clientapi/jsonerror/jsonerror.go b/src/github.com/matrix-org/dendrite/clientapi/jsonerror/jsonerror.go index a0111197..ea64896d 100644 --- a/src/github.com/matrix-org/dendrite/clientapi/jsonerror/jsonerror.go +++ b/src/github.com/matrix-org/dendrite/clientapi/jsonerror/jsonerror.go @@ -1,6 +1,9 @@ package jsonerror -import "fmt" +import ( + "fmt" + "github.com/matrix-org/util" +) // MatrixError represents the "standard error response" in Matrix. // http://matrix.org/docs/spec/client_server/r0.2.0.html#api-standards @@ -13,6 +16,20 @@ func (e *MatrixError) Error() string { return fmt.Sprintf("%s: %s", e.ErrCode, e.Err) } +// InternalServerError returns a 500 Internal Server Error in a matrix-compliant +// format. +func InternalServerError() util.JSONResponse { + return util.JSONResponse{ + Code: 500, + JSON: Unknown("Internal Server Error"), + } +} + +// Unknown is an unexpected error +func Unknown(msg string) *MatrixError { + return &MatrixError{"M_UNKNOWN", msg} +} + // Forbidden is an error when the client tries to access a resource // they are not allowed to access. func Forbidden(msg string) *MatrixError { diff --git a/src/github.com/matrix-org/dendrite/clientapi/readers/sync.go b/src/github.com/matrix-org/dendrite/clientapi/readers/sync.go index 5f4516fb..c4b3a247 100644 --- a/src/github.com/matrix-org/dendrite/clientapi/readers/sync.go +++ b/src/github.com/matrix-org/dendrite/clientapi/readers/sync.go @@ -3,12 +3,18 @@ package readers import ( "net/http" + "github.com/matrix-org/dendrite/clientapi/auth" "github.com/matrix-org/util" ) // Sync implements /sync func Sync(req *http.Request) util.JSONResponse { logger := util.GetLogger(req.Context()) - logger.Info("Doing stuff...") + userID, resErr := auth.VerifyAccessToken(req) + if resErr != nil { + return *resErr + } + + logger.WithField("userID", userID).Info("Doing stuff...") return util.MessageResponse(404, "Not implemented yet") } diff --git a/src/github.com/matrix-org/dendrite/clientapi/writers/sendmessage.go b/src/github.com/matrix-org/dendrite/clientapi/writers/sendmessage.go index ae4103da..bbb432ec 100644 --- a/src/github.com/matrix-org/dendrite/clientapi/writers/sendmessage.go +++ b/src/github.com/matrix-org/dendrite/clientapi/writers/sendmessage.go @@ -3,12 +3,22 @@ package writers import ( "net/http" + log "github.com/Sirupsen/logrus" + "github.com/matrix-org/dendrite/clientapi/auth" "github.com/matrix-org/util" ) // SendMessage implements /rooms/{roomID}/send/{eventType} func SendMessage(req *http.Request, roomID, eventType string) util.JSONResponse { logger := util.GetLogger(req.Context()) - logger.WithField("roomID", roomID).WithField("eventType", eventType).Info("Doing stuff...") + userID, resErr := auth.VerifyAccessToken(req) + if resErr != nil { + return *resErr + } + logger.WithFields(log.Fields{ + "roomID": roomID, + "eventType": eventType, + "userID": userID, + }).Info("Doing stuff...") return util.MessageResponse(404, "Not implemented yet") }