Fix interactive registration failing (#583)
* Fix interactive registration failing because of being confused with AS registration * Fix AS registration tests * Move AS registration handling to dedicated function and split the switch/case to avoid unnecessary condition * Ignore handleRegistrationFlow() for gocyclo and add some doc/comments on the codemain
parent
1a82e6bc58
commit
8ff136e595
|
@ -369,18 +369,11 @@ func UsernameMatchesExclusiveNamespaces(
|
||||||
// two requirements are met, no error will be returned.
|
// two requirements are met, no error will be returned.
|
||||||
func validateApplicationService(
|
func validateApplicationService(
|
||||||
cfg *config.Dendrite,
|
cfg *config.Dendrite,
|
||||||
req *http.Request,
|
|
||||||
username string,
|
username string,
|
||||||
|
accessToken string,
|
||||||
) (string, *util.JSONResponse) {
|
) (string, *util.JSONResponse) {
|
||||||
// Check if the token if the application service is valid with one we have
|
// Check if the token if the application service is valid with one we have
|
||||||
// registered in the config.
|
// registered in the config.
|
||||||
accessToken, err := auth.ExtractAccessToken(req)
|
|
||||||
if err != nil {
|
|
||||||
return "", &util.JSONResponse{
|
|
||||||
Code: http.StatusUnauthorized,
|
|
||||||
JSON: jsonerror.MissingToken(err.Error()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var matchedApplicationService *config.ApplicationService
|
var matchedApplicationService *config.ApplicationService
|
||||||
for _, appservice := range cfg.Derived.ApplicationServices {
|
for _, appservice := range cfg.Derived.ApplicationServices {
|
||||||
if appservice.ASToken == accessToken {
|
if appservice.ASToken == accessToken {
|
||||||
|
@ -497,6 +490,7 @@ func Register(
|
||||||
|
|
||||||
// handleRegistrationFlow will direct and complete registration flow stages
|
// handleRegistrationFlow will direct and complete registration flow stages
|
||||||
// that the client has requested.
|
// that the client has requested.
|
||||||
|
// nolint: gocyclo
|
||||||
func handleRegistrationFlow(
|
func handleRegistrationFlow(
|
||||||
req *http.Request,
|
req *http.Request,
|
||||||
r registerRequest,
|
r registerRequest,
|
||||||
|
@ -542,21 +536,29 @@ func handleRegistrationFlow(
|
||||||
// Add SharedSecret to the list of completed registration stages
|
// Add SharedSecret to the list of completed registration stages
|
||||||
sessions.AddCompletedStage(sessionID, authtypes.LoginTypeSharedSecret)
|
sessions.AddCompletedStage(sessionID, authtypes.LoginTypeSharedSecret)
|
||||||
|
|
||||||
case "", authtypes.LoginTypeApplicationService:
|
case "":
|
||||||
// not passing a Auth.Type is allowed for ApplicationServices. So assume that as well
|
// Extract the access token from the request, if there's one to extract
|
||||||
// Check application service register user request is valid.
|
// (which we can know by checking whether the error is nil or not).
|
||||||
// The application service's ID is returned if so.
|
accessToken, err := auth.ExtractAccessToken(req)
|
||||||
appserviceID, err := validateApplicationService(cfg, req, r.Username)
|
|
||||||
if err != nil {
|
// A missing auth type can mean either the registration is performed by
|
||||||
return *err
|
// an AS or the request is made as the first step of a registration
|
||||||
|
// using the User-Interactive Authentication API. This can be determined
|
||||||
|
// by whether the request contains an access token.
|
||||||
|
if err == nil {
|
||||||
|
return handleApplicationServiceRegistration(
|
||||||
|
accessToken, err, req, r, cfg, accountDB, deviceDB,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no error, application service was successfully validated.
|
case authtypes.LoginTypeApplicationService:
|
||||||
// Don't need to worry about appending to registration stages as
|
// Extract the access token from the request.
|
||||||
// application service registration is entirely separate.
|
accessToken, err := auth.ExtractAccessToken(req)
|
||||||
return completeRegistration(
|
// Let the AS registration handler handle the process from here. We
|
||||||
req.Context(), accountDB, deviceDB, r.Username, "", appserviceID,
|
// don't need a condition on that call since the registration is clearly
|
||||||
r.InhibitLogin, r.InitialDisplayName,
|
// stated as being AS-related.
|
||||||
|
return handleApplicationServiceRegistration(
|
||||||
|
accessToken, err, req, r, cfg, accountDB, deviceDB,
|
||||||
)
|
)
|
||||||
|
|
||||||
case authtypes.LoginTypeDummy:
|
case authtypes.LoginTypeDummy:
|
||||||
|
@ -578,6 +580,50 @@ func handleRegistrationFlow(
|
||||||
req, r, sessionID, cfg, accountDB, deviceDB)
|
req, r, sessionID, cfg, accountDB, deviceDB)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handleApplicationServiceRegistration handles the registration of an
|
||||||
|
// application service's user by validating the AS from its access token and
|
||||||
|
// registering the user. Its two first parameters must be the two return values
|
||||||
|
// of the auth.ExtractAccessToken function.
|
||||||
|
// Returns an error if the access token couldn't be extracted from the request
|
||||||
|
// at an earlier step of the registration workflow, or if the provided access
|
||||||
|
// token doesn't belong to a valid AS, or if there was an issue completing the
|
||||||
|
// registration process.
|
||||||
|
func handleApplicationServiceRegistration(
|
||||||
|
accessToken string,
|
||||||
|
tokenErr error,
|
||||||
|
req *http.Request,
|
||||||
|
r registerRequest,
|
||||||
|
cfg *config.Dendrite,
|
||||||
|
accountDB *accounts.Database,
|
||||||
|
deviceDB *devices.Database,
|
||||||
|
) util.JSONResponse {
|
||||||
|
// Check if we previously had issues extracting the access token from the
|
||||||
|
// request.
|
||||||
|
if tokenErr != nil {
|
||||||
|
return util.JSONResponse{
|
||||||
|
Code: http.StatusUnauthorized,
|
||||||
|
JSON: jsonerror.MissingToken(tokenErr.Error()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check application service register user request is valid.
|
||||||
|
// The application service's ID is returned if so.
|
||||||
|
appserviceID, err := validateApplicationService(
|
||||||
|
cfg, r.Username, accessToken,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return *err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no error, application service was successfully validated.
|
||||||
|
// Don't need to worry about appending to registration stages as
|
||||||
|
// application service registration is entirely separate.
|
||||||
|
return completeRegistration(
|
||||||
|
req.Context(), accountDB, deviceDB, r.Username, "", appserviceID,
|
||||||
|
r.InhibitLogin, r.InitialDisplayName,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// checkAndCompleteFlow checks if a given registration flow is completed given
|
// checkAndCompleteFlow checks if a given registration flow is completed given
|
||||||
// a set of allowed flows. If so, registration is completed, otherwise a
|
// a set of allowed flows. If so, registration is completed, otherwise a
|
||||||
// response with
|
// response with
|
||||||
|
|
|
@ -15,8 +15,6 @@
|
||||||
package routing
|
package routing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -186,47 +184,26 @@ func TestValidationOfApplicationServices(t *testing.T) {
|
||||||
fakeConfig.Derived.ApplicationServices = []config.ApplicationService{fakeApplicationService}
|
fakeConfig.Derived.ApplicationServices = []config.ApplicationService{fakeApplicationService}
|
||||||
|
|
||||||
// Access token is correct, user_id omitted so we are acting as SenderLocalpart
|
// Access token is correct, user_id omitted so we are acting as SenderLocalpart
|
||||||
URL, _ := url.Parse("http://localhost/register?access_token=1234")
|
asID, resp := validateApplicationService(&fakeConfig, fakeSenderLocalpart, "1234")
|
||||||
fakeHTTPRequest := http.Request{
|
|
||||||
Method: "POST",
|
|
||||||
URL: URL,
|
|
||||||
}
|
|
||||||
asID, resp := validateApplicationService(&fakeConfig, &fakeHTTPRequest, fakeSenderLocalpart)
|
|
||||||
if resp != nil || asID != fakeID {
|
if resp != nil || asID != fakeID {
|
||||||
t.Errorf("appservice should have validated and returned correct ID: %s", resp.JSON)
|
t.Errorf("appservice should have validated and returned correct ID: %s", resp.JSON)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Access token is incorrect, user_id omitted so we are acting as SenderLocalpart
|
// Access token is incorrect, user_id omitted so we are acting as SenderLocalpart
|
||||||
URL, _ = url.Parse("http://localhost/register?access_token=xxxx")
|
asID, resp = validateApplicationService(&fakeConfig, fakeSenderLocalpart, "xxxx")
|
||||||
fakeHTTPRequest = http.Request{
|
|
||||||
Method: "POST",
|
|
||||||
URL: URL,
|
|
||||||
}
|
|
||||||
asID, resp = validateApplicationService(&fakeConfig, &fakeHTTPRequest, fakeSenderLocalpart)
|
|
||||||
if resp == nil || asID == fakeID {
|
if resp == nil || asID == fakeID {
|
||||||
t.Errorf("access_token should have been marked as invalid")
|
t.Errorf("access_token should have been marked as invalid")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Access token is correct, acting as valid user_id
|
// Access token is correct, acting as valid user_id
|
||||||
URL, _ = url.Parse("http://localhost/register?access_token=1234&user_id=@_appservice_bob:localhost")
|
asID, resp = validateApplicationService(&fakeConfig, "_appservice_bob", "1234")
|
||||||
fakeHTTPRequest = http.Request{
|
|
||||||
Method: "POST",
|
|
||||||
URL: URL,
|
|
||||||
}
|
|
||||||
asID, resp = validateApplicationService(&fakeConfig, &fakeHTTPRequest, "_appservice_bob")
|
|
||||||
if resp != nil || asID != fakeID {
|
if resp != nil || asID != fakeID {
|
||||||
t.Errorf("access_token and user_id should've been valid: %s", resp.JSON)
|
t.Errorf("access_token and user_id should've been valid: %s", resp.JSON)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Access token is correct, acting as invalid user_id
|
// Access token is correct, acting as invalid user_id
|
||||||
URL, _ = url.Parse("http://localhost/register?access_token=1234&user_id=@_something_else:localhost")
|
asID, resp = validateApplicationService(&fakeConfig, "_something_else", "1234")
|
||||||
fakeHTTPRequest = http.Request{
|
|
||||||
Method: "POST",
|
|
||||||
URL: URL,
|
|
||||||
}
|
|
||||||
asID, resp = validateApplicationService(&fakeConfig, &fakeHTTPRequest, "_something_else")
|
|
||||||
if resp == nil || asID == fakeID {
|
if resp == nil || asID == fakeID {
|
||||||
t.Errorf("user_id should not have been valid: %s",
|
t.Errorf("user_id should not have been valid: @_something_else:localhost")
|
||||||
fakeHTTPRequest.URL.Query().Get("user_id"))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue