mirror of
https://mau.dev/maunium/synapse.git
synced 2024-11-04 21:58:54 +01:00
Move and refactor LoginRestServlet helper methods (#8182)
This is split out from https://github.com/matrix-org/synapse/pull/7438, which had gotten rather large. `LoginRestServlet` has a couple helper methods, `login_submission_legacy_convert` and `login_id_thirdparty_from_phone`. They're primarily used for converting legacy user login submissions to "identifier" dicts ([see spec](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-login)). Identifying information such as usernames or 3PID information used to be top-level in the login body. They're now supposed to be put inside an [identifier](https://matrix.org/docs/spec/client_server/r0.6.1#identifier-types) parameter instead. #7438's purpose is to allow using the new identifier parameter during User-Interactive Authentication, which is currently handled in AuthHandler. That's why I've moved these helper methods there. I also moved the refactoring of these method from #7438 as they're relevant.
This commit is contained in:
parent
e00816ad98
commit
2c2e649be2
3 changed files with 94 additions and 55 deletions
1
changelog.d/8182.misc
Normal file
1
changelog.d/8182.misc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
Refactor some of `LoginRestServlet`'s helper methods, and move them to `AuthHandler` for easier reuse.
|
|
@ -42,8 +42,9 @@ from synapse.http.site import SynapseRequest
|
||||||
from synapse.logging.context import defer_to_thread
|
from synapse.logging.context import defer_to_thread
|
||||||
from synapse.metrics.background_process_metrics import run_as_background_process
|
from synapse.metrics.background_process_metrics import run_as_background_process
|
||||||
from synapse.module_api import ModuleApi
|
from synapse.module_api import ModuleApi
|
||||||
from synapse.types import Requester, UserID
|
from synapse.types import JsonDict, Requester, UserID
|
||||||
from synapse.util import stringutils as stringutils
|
from synapse.util import stringutils as stringutils
|
||||||
|
from synapse.util.msisdn import phone_number_to_msisdn
|
||||||
from synapse.util.threepids import canonicalise_email
|
from synapse.util.threepids import canonicalise_email
|
||||||
|
|
||||||
from ._base import BaseHandler
|
from ._base import BaseHandler
|
||||||
|
@ -51,6 +52,91 @@ from ._base import BaseHandler
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def convert_client_dict_legacy_fields_to_identifier(
|
||||||
|
submission: JsonDict,
|
||||||
|
) -> Dict[str, str]:
|
||||||
|
"""
|
||||||
|
Convert a legacy-formatted login submission to an identifier dict.
|
||||||
|
|
||||||
|
Legacy login submissions (used in both login and user-interactive authentication)
|
||||||
|
provide user-identifying information at the top-level instead.
|
||||||
|
|
||||||
|
These are now deprecated and replaced with identifiers:
|
||||||
|
https://matrix.org/docs/spec/client_server/r0.6.1#identifier-types
|
||||||
|
|
||||||
|
Args:
|
||||||
|
submission: The client dict to convert
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The matching identifier dict
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
SynapseError: If the format of the client dict is invalid
|
||||||
|
"""
|
||||||
|
identifier = submission.get("identifier", {})
|
||||||
|
|
||||||
|
# Generate an m.id.user identifier if "user" parameter is present
|
||||||
|
user = submission.get("user")
|
||||||
|
if user:
|
||||||
|
identifier = {"type": "m.id.user", "user": user}
|
||||||
|
|
||||||
|
# Generate an m.id.thirdparty identifier if "medium" and "address" parameters are present
|
||||||
|
medium = submission.get("medium")
|
||||||
|
address = submission.get("address")
|
||||||
|
if medium and address:
|
||||||
|
identifier = {
|
||||||
|
"type": "m.id.thirdparty",
|
||||||
|
"medium": medium,
|
||||||
|
"address": address,
|
||||||
|
}
|
||||||
|
|
||||||
|
# We've converted valid, legacy login submissions to an identifier. If the
|
||||||
|
# submission still doesn't have an identifier, it's invalid
|
||||||
|
if not identifier:
|
||||||
|
raise SynapseError(400, "Invalid login submission", Codes.INVALID_PARAM)
|
||||||
|
|
||||||
|
# Ensure the identifier has a type
|
||||||
|
if "type" not in identifier:
|
||||||
|
raise SynapseError(
|
||||||
|
400, "'identifier' dict has no key 'type'", errcode=Codes.MISSING_PARAM,
|
||||||
|
)
|
||||||
|
|
||||||
|
return identifier
|
||||||
|
|
||||||
|
|
||||||
|
def login_id_phone_to_thirdparty(identifier: JsonDict) -> Dict[str, str]:
|
||||||
|
"""
|
||||||
|
Convert a phone login identifier type to a generic threepid identifier.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
identifier: Login identifier dict of type 'm.id.phone'
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
An equivalent m.id.thirdparty identifier dict
|
||||||
|
"""
|
||||||
|
if "country" not in identifier or (
|
||||||
|
# The specification requires a "phone" field, while Synapse used to require a "number"
|
||||||
|
# field. Accept both for backwards compatibility.
|
||||||
|
"phone" not in identifier
|
||||||
|
and "number" not in identifier
|
||||||
|
):
|
||||||
|
raise SynapseError(
|
||||||
|
400, "Invalid phone-type identifier", errcode=Codes.INVALID_PARAM
|
||||||
|
)
|
||||||
|
|
||||||
|
# Accept both "phone" and "number" as valid keys in m.id.phone
|
||||||
|
phone_number = identifier.get("phone", identifier["number"])
|
||||||
|
|
||||||
|
# Convert user-provided phone number to a consistent representation
|
||||||
|
msisdn = phone_number_to_msisdn(identifier["country"], phone_number)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"type": "m.id.thirdparty",
|
||||||
|
"medium": "msisdn",
|
||||||
|
"address": msisdn,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class AuthHandler(BaseHandler):
|
class AuthHandler(BaseHandler):
|
||||||
SESSION_EXPIRE_MS = 48 * 60 * 60 * 1000
|
SESSION_EXPIRE_MS = 48 * 60 * 60 * 1000
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,10 @@ from typing import Awaitable, Callable, Dict, Optional
|
||||||
|
|
||||||
from synapse.api.errors import Codes, LoginError, SynapseError
|
from synapse.api.errors import Codes, LoginError, SynapseError
|
||||||
from synapse.api.ratelimiting import Ratelimiter
|
from synapse.api.ratelimiting import Ratelimiter
|
||||||
|
from synapse.handlers.auth import (
|
||||||
|
convert_client_dict_legacy_fields_to_identifier,
|
||||||
|
login_id_phone_to_thirdparty,
|
||||||
|
)
|
||||||
from synapse.http.server import finish_request
|
from synapse.http.server import finish_request
|
||||||
from synapse.http.servlet import (
|
from synapse.http.servlet import (
|
||||||
RestServlet,
|
RestServlet,
|
||||||
|
@ -28,56 +32,11 @@ from synapse.http.site import SynapseRequest
|
||||||
from synapse.rest.client.v2_alpha._base import client_patterns
|
from synapse.rest.client.v2_alpha._base import client_patterns
|
||||||
from synapse.rest.well_known import WellKnownBuilder
|
from synapse.rest.well_known import WellKnownBuilder
|
||||||
from synapse.types import JsonDict, UserID
|
from synapse.types import JsonDict, UserID
|
||||||
from synapse.util.msisdn import phone_number_to_msisdn
|
|
||||||
from synapse.util.threepids import canonicalise_email
|
from synapse.util.threepids import canonicalise_email
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def login_submission_legacy_convert(submission):
|
|
||||||
"""
|
|
||||||
If the input login submission is an old style object
|
|
||||||
(ie. with top-level user / medium / address) convert it
|
|
||||||
to a typed object.
|
|
||||||
"""
|
|
||||||
if "user" in submission:
|
|
||||||
submission["identifier"] = {"type": "m.id.user", "user": submission["user"]}
|
|
||||||
del submission["user"]
|
|
||||||
|
|
||||||
if "medium" in submission and "address" in submission:
|
|
||||||
submission["identifier"] = {
|
|
||||||
"type": "m.id.thirdparty",
|
|
||||||
"medium": submission["medium"],
|
|
||||||
"address": submission["address"],
|
|
||||||
}
|
|
||||||
del submission["medium"]
|
|
||||||
del submission["address"]
|
|
||||||
|
|
||||||
|
|
||||||
def login_id_thirdparty_from_phone(identifier):
|
|
||||||
"""
|
|
||||||
Convert a phone login identifier type to a generic threepid identifier
|
|
||||||
Args:
|
|
||||||
identifier(dict): Login identifier dict of type 'm.id.phone'
|
|
||||||
|
|
||||||
Returns: Login identifier dict of type 'm.id.threepid'
|
|
||||||
"""
|
|
||||||
if "country" not in identifier or (
|
|
||||||
# The specification requires a "phone" field, while Synapse used to require a "number"
|
|
||||||
# field. Accept both for backwards compatibility.
|
|
||||||
"phone" not in identifier
|
|
||||||
and "number" not in identifier
|
|
||||||
):
|
|
||||||
raise SynapseError(400, "Invalid phone-type identifier")
|
|
||||||
|
|
||||||
# Accept both "phone" and "number" as valid keys in m.id.phone
|
|
||||||
phone_number = identifier.get("phone", identifier["number"])
|
|
||||||
|
|
||||||
msisdn = phone_number_to_msisdn(identifier["country"], phone_number)
|
|
||||||
|
|
||||||
return {"type": "m.id.thirdparty", "medium": "msisdn", "address": msisdn}
|
|
||||||
|
|
||||||
|
|
||||||
class LoginRestServlet(RestServlet):
|
class LoginRestServlet(RestServlet):
|
||||||
PATTERNS = client_patterns("/login$", v1=True)
|
PATTERNS = client_patterns("/login$", v1=True)
|
||||||
CAS_TYPE = "m.login.cas"
|
CAS_TYPE = "m.login.cas"
|
||||||
|
@ -194,18 +153,11 @@ class LoginRestServlet(RestServlet):
|
||||||
login_submission.get("address"),
|
login_submission.get("address"),
|
||||||
login_submission.get("user"),
|
login_submission.get("user"),
|
||||||
)
|
)
|
||||||
login_submission_legacy_convert(login_submission)
|
identifier = convert_client_dict_legacy_fields_to_identifier(login_submission)
|
||||||
|
|
||||||
if "identifier" not in login_submission:
|
|
||||||
raise SynapseError(400, "Missing param: identifier")
|
|
||||||
|
|
||||||
identifier = login_submission["identifier"]
|
|
||||||
if "type" not in identifier:
|
|
||||||
raise SynapseError(400, "Login identifier has no type")
|
|
||||||
|
|
||||||
# convert phone type identifiers to generic threepids
|
# convert phone type identifiers to generic threepids
|
||||||
if identifier["type"] == "m.id.phone":
|
if identifier["type"] == "m.id.phone":
|
||||||
identifier = login_id_thirdparty_from_phone(identifier)
|
identifier = login_id_phone_to_thirdparty(identifier)
|
||||||
|
|
||||||
# convert threepid identifiers to user IDs
|
# convert threepid identifiers to user IDs
|
||||||
if identifier["type"] == "m.id.thirdparty":
|
if identifier["type"] == "m.id.thirdparty":
|
||||||
|
|
Loading…
Reference in a new issue