Use AS sender_localpart when they omit user_id

This commit is contained in:
Andrew Morgan 2018-06-08 14:06:23 +01:00
parent 78440083df
commit fc3b32c269

View file

@ -24,6 +24,7 @@ import (
"net/http" "net/http"
"strings" "strings"
"github.com/matrix-org/dendrite/appservice/types"
"github.com/matrix-org/dendrite/clientapi/auth/authtypes" "github.com/matrix-org/dendrite/clientapi/auth/authtypes"
"github.com/matrix-org/dendrite/clientapi/httputil" "github.com/matrix-org/dendrite/clientapi/httputil"
"github.com/matrix-org/dendrite/clientapi/jsonerror" "github.com/matrix-org/dendrite/clientapi/jsonerror"
@ -48,26 +49,32 @@ type AccountDatabase interface {
GetAccountByLocalpart(ctx context.Context, localpart string) (*authtypes.Account, error) GetAccountByLocalpart(ctx context.Context, localpart string) (*authtypes.Account, error)
} }
// Data contains information required to authenticate a request.
type Data struct {
AccountDB AccountDatabase
DeviceDB DeviceDatabase
// AppServices is the list of all registered AS
AppServices []config.ApplicationService
}
// VerifyUserFromRequest authenticates the HTTP request, // VerifyUserFromRequest authenticates the HTTP request,
// on success returns UserID of the requester. // on success returns Device of the requester.
// Finds local user or an application service user. // Finds local user or an application service user.
// Note: For an AS user, AS dummy device is returned.
// On failure returns an JSON error response which can be sent to the client. // On failure returns an JSON error response which can be sent to the client.
func VerifyUserFromRequest( func VerifyUserFromRequest(
req *http.Request, accountDB AccountDatabase, deviceDB DeviceDatabase, req *http.Request, data Data,
applicationServices []config.ApplicationService, ) (*authtypes.Device, *util.JSONResponse) {
) (string, *util.JSONResponse) {
// Try to find local user from device database // Try to find local user from device database
dev, devErr := VerifyAccessToken(req, deviceDB) dev, devErr := verifyAccessToken(req, data.DeviceDB)
if devErr == nil { if devErr == nil {
return dev.UserID, nil return dev, nil
} }
// Try to find the Application Service user // Try to find the Application Service user
token, err := extractAccessToken(req) token, err := extractAccessToken(req)
if err != nil { if err != nil {
return "", &util.JSONResponse{ return nil, &util.JSONResponse{
Code: http.StatusUnauthorized, Code: http.StatusUnauthorized,
JSON: jsonerror.MissingToken(err.Error()), JSON: jsonerror.MissingToken(err.Error()),
} }
@ -75,7 +82,7 @@ func VerifyUserFromRequest(
// Search for app service with given access_token // Search for app service with given access_token
var appService *config.ApplicationService var appService *config.ApplicationService
for _, as := range applicationServices { for _, as := range data.AppServices {
if as.ASToken == token { if as.ASToken == token {
appService = &as appService = &as
break break
@ -84,39 +91,57 @@ func VerifyUserFromRequest(
if appService != nil { if appService != nil {
userID := req.URL.Query().Get("user_id") userID := req.URL.Query().Get("user_id")
localpart, err := userutil.ParseUsernameParam(userID, nil) var localpart string
if userID == "" {
if err != nil { // If no user_id was supplied, assume they are masquerading as the
return "", &util.JSONResponse{ // `sender_localpart` user from the application service registration config
Code: http.StatusBadRequest, // file
JSON: jsonerror.InvalidUsername(err.Error()), localpart = appService.SenderLocalpart
} else {
// Verify given userID
localpart, err = userutil.ParseUsernameParam(userID, nil)
if err != nil {
return nil, &util.JSONResponse{
Code: http.StatusBadRequest,
JSON: jsonerror.InvalidUsername(err.Error()),
}
} }
} }
// Verify that the user is registered // Verify that the user is registered
account, accountErr := accountDB.GetAccountByLocalpart(req.Context(), localpart) account, err := data.AccountDB.GetAccountByLocalpart(req.Context(), localpart)
// Verify that account exists & appServiceID matches // Verify that account exists & appServiceID matches
if accountErr == nil && account.AppServiceID == appService.ID { if err == nil && account.AppServiceID == appService.ID {
return userID, nil // Create a dummy device for AS user
dev := authtypes.Device{
// Use AS dummy device ID
ID: types.AppServiceDeviceID,
// User the AS is masquerading as.
UserID: userID,
// AS dummy device has AS's token.
AccessToken: token,
}
return &dev, nil
} }
return "", &util.JSONResponse{ return nil, &util.JSONResponse{
Code: http.StatusForbidden, Code: http.StatusForbidden,
JSON: jsonerror.Forbidden("Application service has not registered this user"), JSON: jsonerror.Forbidden("Application service has not registered this user"),
} }
} }
return "", &util.JSONResponse{ return nil, &util.JSONResponse{
Code: http.StatusUnauthorized, Code: http.StatusUnauthorized,
JSON: jsonerror.UnknownToken("Unrecognized access token"), JSON: jsonerror.UnknownToken("Unrecognized access token"),
} }
} }
// VerifyAccessToken verifies that an access token was supplied in the given HTTP request // verifyAccessToken verifies that an access token was supplied in the given HTTP request
// and returns the device it corresponds to. Returns resErr (an error response which can be // and returns the device 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. // sent to the client) if the token is invalid or there was a problem querying the database.
func VerifyAccessToken(req *http.Request, deviceDB DeviceDatabase) (device *authtypes.Device, resErr *util.JSONResponse) { func verifyAccessToken(req *http.Request, deviceDB DeviceDatabase) (device *authtypes.Device, resErr *util.JSONResponse) {
token, err := extractAccessToken(req) token, err := extractAccessToken(req)
if err != nil { if err != nil {
resErr = &util.JSONResponse{ resErr = &util.JSONResponse{