From 7ce94121ad33f5607ced17b18b8e99efdfa2bd70 Mon Sep 17 00:00:00 2001 From: Anant Prakash Date: Wed, 14 Mar 2018 01:45:02 +0530 Subject: [PATCH] Add whoami endpoint Signed-off-by: Anant Prakash --- .../dendrite/clientapi/auth/auth.go | 21 +++---- .../clientapi/auth/storage/devices/storage.go | 5 +- .../dendrite/clientapi/routing/login.go | 13 +++- .../dendrite/clientapi/routing/routing.go | 6 ++ .../dendrite/clientapi/routing/whoami.go | 60 +++++++++++++++++++ .../userutil/userutil.go} | 30 ++++------ 6 files changed, 102 insertions(+), 33 deletions(-) create mode 100644 src/github.com/matrix-org/dendrite/clientapi/routing/whoami.go rename src/github.com/matrix-org/dendrite/{common/username_utils.go => clientapi/userutil/userutil.go} (55%) diff --git a/src/github.com/matrix-org/dendrite/clientapi/auth/auth.go b/src/github.com/matrix-org/dendrite/clientapi/auth/auth.go index 31c097d65..7f4e16a94 100644 --- a/src/github.com/matrix-org/dendrite/clientapi/auth/auth.go +++ b/src/github.com/matrix-org/dendrite/clientapi/auth/auth.go @@ -25,11 +25,9 @@ import ( "strings" "github.com/matrix-org/dendrite/clientapi/auth/authtypes" - "github.com/matrix-org/dendrite/clientapi/auth/storage/accounts" - "github.com/matrix-org/dendrite/clientapi/auth/storage/devices" "github.com/matrix-org/dendrite/clientapi/httputil" "github.com/matrix-org/dendrite/clientapi/jsonerror" - "github.com/matrix-org/dendrite/common" + "github.com/matrix-org/dendrite/clientapi/userutil" "github.com/matrix-org/dendrite/common/config" "github.com/matrix-org/util" ) @@ -38,22 +36,25 @@ import ( // 32 bytes => 256 bits var tokenByteLength = 32 -// The length of generated device IDs -var deviceIDByteLength = 6 - // DeviceDatabase represents a device database. type DeviceDatabase interface { // Look up the device matching the given access token. GetDeviceByAccessToken(ctx context.Context, token string) (*authtypes.Device, error) } +// AccountDatabase represents a account database. +type AccountDatabase interface { + // Look up the account matching the given localpart. + GetAccountByLocalpart(ctx context.Context, localpart string) (*authtypes.Account, error) +} + // VerifyUserFromRequest authenticates the HTTP request, // on success returns UserID of the requester. // Finds local user or an application service user. // On failure returns an JSON error response which can be sent to the client. func VerifyUserFromRequest( - req *http.Request, accountDB *accounts.Database, deviceDB *devices.Database, - cfg *config.Dendrite, + req *http.Request, accountDB AccountDatabase, deviceDB DeviceDatabase, + applicationServices []config.ApplicationService, ) (string, *util.JSONResponse) { // Try to find local user from device database dev, devErr := VerifyAccessToken(req, deviceDB) @@ -74,7 +75,7 @@ func VerifyUserFromRequest( // Search for app service with given access_token var appService *config.ApplicationService - for _, as := range cfg.Derived.ApplicationServices { + for _, as := range applicationServices { if as.ASToken == token { appService = &as break @@ -83,7 +84,7 @@ func VerifyUserFromRequest( if appService != nil { userID := req.URL.Query().Get("user_id") - localpart, err := common.GetLocalpartFromUsername(userID) + localpart, err := userutil.GetLocalpartFromUsername(userID) if err != nil { return "", &util.JSONResponse{ 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 017a28bda..7683a427e 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 @@ -25,6 +25,9 @@ import ( "github.com/matrix-org/gomatrixserverlib" ) +// The length of generated device IDs +var deviceIDByteLength = 6 + // Database represents a device database. type Database struct { db *sql.DB @@ -94,7 +97,7 @@ func (d *Database) CreateDevice( // We cap this at going round 5 times to ensure we don't spin forever var newDeviceID string for i := 1; i <= 5; i++ { - newDeviceID, returnErr = GenerateDeviceID() + newDeviceID, returnErr = generateDeviceID() if returnErr != nil { return } diff --git a/src/github.com/matrix-org/dendrite/clientapi/routing/login.go b/src/github.com/matrix-org/dendrite/clientapi/routing/login.go index 3f35bff7e..e53d90f07 100644 --- a/src/github.com/matrix-org/dendrite/clientapi/routing/login.go +++ b/src/github.com/matrix-org/dendrite/clientapi/routing/login.go @@ -15,6 +15,7 @@ package routing import ( + "errors" "net/http" "github.com/matrix-org/dendrite/clientapi/auth" @@ -22,7 +23,7 @@ import ( "github.com/matrix-org/dendrite/clientapi/auth/storage/devices" "github.com/matrix-org/dendrite/clientapi/httputil" "github.com/matrix-org/dendrite/clientapi/jsonerror" - "github.com/matrix-org/dendrite/common" + "github.com/matrix-org/dendrite/clientapi/userutil" "github.com/matrix-org/dendrite/common/config" "github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/util" @@ -82,9 +83,15 @@ func Login( util.GetLogger(req.Context()).WithField("user", r.User).Info("Processing login request") - localpart, err = common.GetLocalpartFromUsername(r.User) + localpart, err := userutil.GetLocalpartFromUsername(r.User) if err != nil { - return &util.JSONResponse{ + // Check that domain matches this server. error should reflect a mismatch. + domain, domainErr := userutil.GetDomainFromUserID(r.User) + + if domainErr == nil && domain != cfg.Matrix.ServerName { + err = errors.New("User ID not ours") + } + return util.JSONResponse{ Code: http.StatusBadRequest, JSON: jsonerror.InvalidUsername(err.Error()), } 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 1a0252511..39a0ba86b 100644 --- a/src/github.com/matrix-org/dendrite/clientapi/routing/routing.go +++ b/src/github.com/matrix-org/dendrite/clientapi/routing/routing.go @@ -389,4 +389,10 @@ func Setup( }} }), ).Methods(http.MethodGet, http.MethodOptions) + + r0mux.Handle("/account/whoami", + common.MakeExternalAPI("whoami", func(req *http.Request) util.JSONResponse { + return Whoami(req, accountDB, deviceDB, &cfg) + }), + ).Methods(http.MethodGet, http.MethodOptions) } diff --git a/src/github.com/matrix-org/dendrite/clientapi/routing/whoami.go b/src/github.com/matrix-org/dendrite/clientapi/routing/whoami.go new file mode 100644 index 000000000..85695f53c --- /dev/null +++ b/src/github.com/matrix-org/dendrite/clientapi/routing/whoami.go @@ -0,0 +1,60 @@ +// Copyright 2018 New Vector 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 routing + +import ( + "net/http" + + "github.com/matrix-org/dendrite/clientapi/auth" + "github.com/matrix-org/dendrite/clientapi/auth/storage/accounts" + "github.com/matrix-org/dendrite/clientapi/auth/storage/devices" + "github.com/matrix-org/dendrite/clientapi/jsonerror" + "github.com/matrix-org/dendrite/common/config" + "github.com/matrix-org/util" +) + +// whoamiResponse represents an response for a `whoami` request +type whoamiResponse struct { + UserID string `json:"user_id"` +} + +// Whoami implements `/account/whoami` which enables client to query owner of the HTTP request. +// https://matrix.org/docs/spec/client_server/r0.3.0.html#get-matrix-client-r0-account-whoami +func Whoami( + req *http.Request, accountDB *accounts.Database, deviceDB *devices.Database, + cfg *config.Dendrite, +) util.JSONResponse { + + if req.Method == http.MethodGet { + user, err := auth.VerifyUserFromRequest(req, accountDB, deviceDB, cfg.Derived.ApplicationServices) + + if err != nil { + return *err + } + util.GetLogger(req.Context()).WithField("user", user).Info("Processing whoami request") + + return util.JSONResponse{ + Code: http.StatusOK, + JSON: whoamiResponse{ + UserID: user, + }, + } + } + + return util.JSONResponse{ + Code: http.StatusMethodNotAllowed, + JSON: jsonerror.NotFound("Bad method"), + } +} diff --git a/src/github.com/matrix-org/dendrite/common/username_utils.go b/src/github.com/matrix-org/dendrite/clientapi/userutil/userutil.go similarity index 55% rename from src/github.com/matrix-org/dendrite/common/username_utils.go rename to src/github.com/matrix-org/dendrite/clientapi/userutil/userutil.go index 05ddc673e..d15b05d4a 100644 --- a/src/github.com/matrix-org/dendrite/common/username_utils.go +++ b/src/github.com/matrix-org/dendrite/clientapi/userutil/userutil.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package common +package userutil import ( "errors" @@ -21,36 +21,28 @@ import ( "github.com/matrix-org/gomatrixserverlib" ) -// GetLocalpartDomainFromUserID extracts localpart & domain of server from userID. -// Returns error in case of invalid username. -func GetLocalpartDomainFromUserID(userID string, -) (string, gomatrixserverlib.ServerName, error) { - localpart, domain, err := gomatrixserverlib.SplitID('@', userID) +// GetDomainFromUserID extracts the domain of server from userID. +// Returns error in case of invalid userID. +func GetDomainFromUserID(userID string, +) (gomatrixserverlib.ServerName, error) { + _, domain, err := gomatrixserverlib.SplitID('@', userID) - if err != nil { - return localpart, domain, err - } - - return localpart, domain, nil + return domain, err } -// GetLocalpartFromUsername extracts localpart from userID -// userID can either be a user ID or just the localpart. +// GetLocalpartFromUsername extracts localpart from username. +// username can either be a user ID or just the localpart. // Returns error in case of invalid username. -func GetLocalpartFromUsername(userID string, -) (string, error) { +func GetLocalpartFromUsername(userID string) (string, error) { localpart := userID if strings.HasPrefix(userID, "@") { - lp, domain, err := GetLocalpartDomainFromUserid(userID) + lp, _, err := gomatrixserverlib.SplitID('@', userID) if err != nil { return "", errors.New("Invalid username") } - if domain != cfg.Matrix.ServerName { - return "", errors.New("User ID not ours") - } localpart = lp } return localpart, nil