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

Require AppserviceRegistrationType #9548

Merged
merged 17 commits into from
Apr 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/9548.removal
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Make `/_matrix/client/r0/register` expect a type of `m.login.application_service` when an Application Service registers a user, to align with [the relevant spec](https://spec.matrix.org/unstable/application-service-api/#server-admin-style-permissions).
5 changes: 5 additions & 0 deletions synapse/api/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ class LoginType:
DUMMY = "m.login.dummy"


# This is used in the `type` parameter for /register when called by
# an appservice to register a new user.
APP_SERVICE_REGISTRATION_TYPE = "m.login.application_service"
Half-Shot marked this conversation as resolved.
Show resolved Hide resolved


class EventTypes:
Member = "m.room.member"
Create = "m.room.create"
Expand Down
23 changes: 16 additions & 7 deletions synapse/rest/client/v2_alpha/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
# 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.

import hmac
import logging
import random
Expand All @@ -22,7 +21,7 @@
import synapse
import synapse.api.auth
import synapse.types
from synapse.api.constants import LoginType
from synapse.api.constants import APP_SERVICE_REGISTRATION_TYPE, LoginType
from synapse.api.errors import (
Codes,
InteractiveAuthIncompleteError,
Expand Down Expand Up @@ -430,15 +429,20 @@ async def on_POST(self, request):
raise SynapseError(400, "Invalid username")
desired_username = body["username"]

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

# fork off as soon as possible for ASes which have completely
# different registration flows to normal users

# == Application Service Registration ==
if appservice:
if body.get("type") == APP_SERVICE_REGISTRATION_TYPE:
if not self.auth.has_access_token(request):
Half-Shot marked this conversation as resolved.
Show resolved Hide resolved
raise SynapseError(
400,
"Appservice token must be provided when using a type of m.login.application_service",
)

# Verify the AS
self.auth.get_appservice_by_req(request)

# Set the desired user according to the AS API (which uses the
# 'user' key not 'username'). Since this is a new addition, we'll
# fallback to 'username' if they gave one.
Expand All @@ -459,6 +463,11 @@ async def on_POST(self, request):
)

return 200, result
elif self.auth.has_access_token(request):
raise SynapseError(
400,
"An access token should not be provided on requests to /register (except if type is m.login.application_service)",
)

# == Normal User Registration == (everyone else)
if not self._registration_enabled:
Expand Down
31 changes: 27 additions & 4 deletions tests/rest/client/v2_alpha/test_register.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,14 @@
# 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.

import datetime
import json
import os

import pkg_resources

import synapse.rest.admin
from synapse.api.constants import LoginType
from synapse.api.constants import APP_SERVICE_REGISTRATION_TYPE, LoginType
from synapse.api.errors import Codes
from synapse.appservice import ApplicationService
from synapse.rest.client.v1 import login, logout
Expand Down Expand Up @@ -59,7 +58,9 @@ def test_POST_appservice_registration_valid(self):
)

self.hs.get_datastore().services_cache.append(appservice)
request_data = json.dumps({"username": "as_user_kermit"})
request_data = json.dumps(
{"username": "as_user_kermit", "type": APP_SERVICE_REGISTRATION_TYPE}
)

channel = self.make_request(
b"POST", self.url + b"?access_token=i_am_an_app_service", request_data
Expand All @@ -69,9 +70,31 @@ def test_POST_appservice_registration_valid(self):
det_data = {"user_id": user_id, "home_server": self.hs.hostname}
self.assertDictContainsSubset(det_data, channel.json_body)

def test_POST_appservice_registration_no_type(self):
as_token = "i_am_an_app_service"

appservice = ApplicationService(
as_token,
self.hs.config.server_name,
id="1234",
namespaces={"users": [{"regex": r"@as_user.*", "exclusive": True}]},
sender="@as:test",
)

self.hs.get_datastore().services_cache.append(appservice)
request_data = json.dumps({"username": "as_user_kermit"})

channel = self.make_request(
b"POST", self.url + b"?access_token=i_am_an_app_service", request_data
)

self.assertEquals(channel.result["code"], b"400", channel.result)

def test_POST_appservice_registration_invalid(self):
self.appservice = None # no application service exists
request_data = json.dumps({"username": "kermit"})
request_data = json.dumps(
{"username": "kermit", "type": APP_SERVICE_REGISTRATION_TYPE}
)
channel = self.make_request(
b"POST", self.url + b"?access_token=i_am_an_app_service", request_data
)
Expand Down
23 changes: 11 additions & 12 deletions tests/test_mau.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@

"""Tests REST events for /rooms paths."""

import json

from synapse.api.constants import LoginType
from synapse.api.constants import APP_SERVICE_REGISTRATION_TYPE, LoginType
from synapse.api.errors import Codes, HttpResponseException, SynapseError
from synapse.appservice import ApplicationService
from synapse.rest.client.v2_alpha import register, sync
Expand Down Expand Up @@ -113,7 +111,7 @@ def test_as_ignores_mau(self):
)
)

self.create_user("as_kermit4", token=as_token)
self.create_user("as_kermit4", token=as_token, appservice=True)

def test_allowed_after_a_month_mau(self):
# Create and sync so that the MAU counts get updated
Expand Down Expand Up @@ -232,14 +230,15 @@ def test_tracked_but_not_limited(self):
self.reactor.advance(100)
self.assertEqual(2, self.successResultOf(count))

def create_user(self, localpart, token=None):
request_data = json.dumps(
{
"username": localpart,
"password": "monkey",
"auth": {"type": LoginType.DUMMY},
}
)
def create_user(self, localpart, token=None, appservice=False):
request_data = {
"username": localpart,
"password": "monkey",
"auth": {"type": LoginType.DUMMY},
}

if appservice:
request_data["type"] = APP_SERVICE_REGISTRATION_TYPE

channel = self.make_request(
"POST",
Expand Down