Skip to content
This repository has been archived by the owner on Apr 26, 2024. It is now read-only.

Allow appservice users to /login #8320

Merged
merged 12 commits into from
Sep 18, 2020
1 change: 1 addition & 0 deletions changelog.d/8320.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add `uk.half-shot.unstable.login.appservice` login type to allow appservices to login.
1 change: 1 addition & 0 deletions synapse/api/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ class JoinRules:

class LoginType:
PASSWORD = "m.login.password"
APPSERVICE = "uk.half-shot.msc2778.login.application_service"
Half-Shot marked this conversation as resolved.
Show resolved Hide resolved
EMAIL_IDENTITY = "m.login.email.identity"
MSISDN = "m.login.msisdn"
RECAPTCHA = "m.login.recaptcha"
Expand Down
24 changes: 22 additions & 2 deletions synapse/rest/client/v1/login.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
import logging
from typing import Awaitable, Callable, Dict, Optional

from synapse.api.constants import LoginType
from synapse.api.errors import Codes, LoginError, SynapseError
from synapse.api.ratelimiting import Ratelimiter
from synapse.appservice import ApplicationService
from synapse.handlers.auth import (
convert_client_dict_legacy_fields_to_identifier,
login_id_phone_to_thirdparty,
Expand Down Expand Up @@ -61,6 +63,8 @@ def __init__(self, hs):
self.cas_enabled = hs.config.cas_enabled
self.oidc_enabled = hs.config.oidc_enabled

self.auth = hs.get_auth()

self.auth_handler = self.hs.get_auth_handler()
self.registration_handler = hs.get_registration_handler()
self.handlers = hs.get_handlers()
Expand Down Expand Up @@ -116,6 +120,11 @@ async def on_POST(self, request: SynapseRequest):
self._address_ratelimiter.ratelimit(request.getClientIP())

login_submission = parse_json_object_from_request(request)

appservice = None
if self.auth.has_access_token(request):
appservice = self.auth.get_appservice_by_req(request)

try:
if self.jwt_enabled and (
login_submission["type"] == LoginRestServlet.JWT_TYPE
Expand All @@ -125,7 +134,7 @@ async def on_POST(self, request: SynapseRequest):
elif login_submission["type"] == LoginRestServlet.TOKEN_TYPE:
result = await self._do_token_login(login_submission)
else:
result = await self._do_other_login(login_submission)
result = await self._do_other_login(login_submission, appservice)
except KeyError:
raise SynapseError(400, "Missing JSON keys.")

Expand All @@ -134,7 +143,9 @@ async def on_POST(self, request: SynapseRequest):
result["well_known"] = well_known_data
return 200, result

async def _do_other_login(self, login_submission: JsonDict) -> Dict[str, str]:
async def _do_other_login(
self, login_submission: JsonDict, appservice: Optional[ApplicationService]
) -> Dict[str, str]:
"""Handle non-token/saml/jwt logins

Args:
Expand Down Expand Up @@ -229,6 +240,15 @@ async def _do_other_login(self, login_submission: JsonDict) -> Dict[str, str]:
else:
qualified_user_id = UserID(identifier["user"], self.hs.hostname).to_string()

if login_submission["type"] == LoginType.APPSERVICE:
if appservice is None or not appservice.is_interested_in_user(
qualified_user_id
):
raise LoginError(403, "Invalid access_token", errcode=Codes.FORBIDDEN)

result = await self._complete_login(qualified_user_id, login_submission)
return result
Half-Shot marked this conversation as resolved.
Show resolved Hide resolved

# Check if we've hit the failed ratelimit (but don't update it)
self._failed_attempts_ratelimiter.ratelimit(
qualified_user_id.lower(), update=False
Expand Down