summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/docs-add-version-picker.yaml90
-rw-r--r--.github/workflows/docs.yaml3
-rw-r--r--book.toml7
-rw-r--r--changelog.d/16533.doc1
-rw-r--r--changelog.d/16726.misc1
-rw-r--r--changelog.d/16737.doc1
-rw-r--r--changelog.d/16740.bugfix1
-rw-r--r--docs/usage/configuration/config_documentation.md18
-rw-r--r--docs/website_files/README.md5
-rw-r--r--docs/website_files/theme/index.hbs12
-rw-r--r--docs/website_files/version-picker.css78
-rw-r--r--docs/website_files/version-picker.js127
-rw-r--r--docs/website_files/version.js1
-rwxr-xr-xsynapse/_scripts/generate_signing_key.py13
-rw-r--r--synapse/config/key.py8
-rw-r--r--synapse/handlers/room.py4
-rw-r--r--synapse/rest/__init__.py2
-rw-r--r--synapse/rest/client/auth_issuer.py63
-rw-r--r--synapse/rest/client/directory.py10
-rw-r--r--tests/rest/client/test_auth_issuer.py59
20 files changed, 488 insertions, 16 deletions
diff --git a/.github/workflows/docs-add-version-picker.yaml b/.github/workflows/docs-add-version-picker.yaml
new file mode 100644

index 0000000000..717d5c85d3 --- /dev/null +++ b/.github/workflows/docs-add-version-picker.yaml
@@ -0,0 +1,90 @@ +name: Add Version Picker (RUN ONCE) + +on: + workflow_dispatch: + +jobs: + add-version-picker: + name: Add Version Picker + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Configure Git + run: | + git config user.email "action@synapse.bot.com" + git config user.name "Action Bot" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Setup mdbook + uses: peaceiris/actions-mdbook@adeb05db28a0c0004681db83893d56c0388ea9ea # v1.2.0 + with: + mdbook-version: '0.4.17' + + - name: Copy files to release branches + run: | + for version in "v1.98" "v1.97" "v1.96" "v1.95" "v1.94" "v1.93" "v1.92" "v1.91" "v1.90" "v1.89" "v1.88" "v1.87" "v1.86" "v1.85" "v1.84" "v1.83" "v1.82" "v1.81" "v1.80" "v1.79" "v1.78" "v1.77" "v1.76" "v1.75" "v1.74" "v1.73" "v1.72" "v1.71" "v1.70" "v1.69" "v1.68" "v1.67" "v1.66" "v1.65" "v1.64" "v1.63" "v1.62" "v1.61" "v1.60" "v1.59" "v1.58" "v1.57" "v1.56" "v1.55" "v1.54" "v1.53" "v1.52" "v1.51" "v1.50" "v1.49" "v1.48" "v1.47" "v1.46" "v1.45" "v1.44" "v1.43" "v1.42" "v1.41" "v1.40" "v1.39" "v1.38" "v1.37" + do + git fetch + git checkout -b release-$version origin/release-$version + + git checkout develop -- ./book.toml + git checkout develop -- ./docs/website_files/version-picker.js + git checkout develop -- ./docs/website_files/version-picker.css + git checkout develop -- ./docs/website_files/README.md + + echo "window.SYNAPSE_VERSION = '$version';" > ./docs/website_files/version.js + + # Adding version-picker element to index.hbs + awk '/<button id="search-toggle" class="icon-button" type="button" title="Search. \(Shortkey: s\)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">/{ + print; getline; print; getline; print; getline; print; + print "\ + <div class=\"version-picker\">\n\ + <div class=\"dropdown\">\n\ + <div class=\"select\">\n\ + <span></span>\n\ + <i class=\"fa fa-chevron-down\"></i>\n\ + </div>\n\ + <input type=\"hidden\" name=\"version\">\n\ + <ul class=\"dropdown-menu\">\n\ + <!-- Versions will be added dynamically in version-picker.js -->\n\ + </ul>\n\ + </div>\n\ + </div>\ + "; + next + } 1' ./docs/website_files/theme/index.hbs > output.html && mv output.html ./docs/website_files/theme/index.hbs + + git add ./book.toml ./docs/website_files/version-picker.js ./docs/website_files/version-picker.css ./docs/website_files/version.js ./docs/website_files/README.md ./docs/website_files/theme/index.hbs + git commit -m "Version picker added for $version docs" + git push + done + + - name: Build docs for Github Pages + run: | + git fetch + git branch gh-pages origin/gh-pages + + for version in "v1.98" "v1.97" "v1.96" "v1.95" "v1.94" "v1.93" "v1.92" "v1.91" "v1.90" "v1.89" "v1.88" "v1.87" "v1.86" "v1.85" "v1.84" "v1.83" "v1.82" "v1.81" "v1.80" "v1.79" "v1.78" "v1.77" "v1.76" "v1.75" "v1.74" "v1.73" "v1.72" "v1.71" "v1.70" "v1.69" "v1.68" "v1.67" "v1.66" "v1.65" "v1.64" "v1.63" "v1.62" "v1.61" "v1.60" "v1.59" "v1.58" "v1.57" "v1.56" "v1.55" "v1.54" "v1.53" "v1.52" "v1.51" "v1.50" "v1.49" "v1.48" "v1.47" "v1.46" "v1.45" "v1.44" "v1.43" "v1.42" "v1.41" "v1.40" "v1.39" "v1.38" "v1.37" + do + git checkout release-$version + + mdbook build && cp book/welcome_and_overview.html book/index.html + mkdir ver-temp && cp -r book/* ver-temp/ + rm -r ./book + + git checkout gh-pages + rm -r $version + mv ver-temp $version + + git add ./$version + git commit -m "Version picker deployed for $version docs to Github Pages" + done + + - name: Push to gh-pages + run: | + git checkout gh-pages + git status + git push \ No newline at end of file diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml
index 31b9dbe3fe..6b9135a627 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml
@@ -60,6 +60,9 @@ jobs: with: mdbook-version: '0.4.17' + - name: Set version of docs + run: echo 'window.SYNAPSE_VERSION = "${{ needs.pre.outputs.branch-version }}";' > ./docs/website_files/version.js + - name: Setup python uses: actions/setup-python@v4 with: diff --git a/book.toml b/book.toml
index ed3f6151e0..977a8450bc 100644 --- a/book.toml +++ b/book.toml
@@ -34,8 +34,13 @@ additional-css = [ "docs/website_files/table-of-contents.css", "docs/website_files/remove-nav-buttons.css", "docs/website_files/indent-section-headers.css", + "docs/website_files/version-picker.css", +] +additional-js = [ + "docs/website_files/table-of-contents.js", + "docs/website_files/version-picker.js", + "docs/website_files/version.js", ] -additional-js = ["docs/website_files/table-of-contents.js"] theme = "docs/website_files/theme" [preprocessor.schema_versions] diff --git a/changelog.d/16533.doc b/changelog.d/16533.doc new file mode 100644
index 0000000000..ae23a8a578 --- /dev/null +++ b/changelog.d/16533.doc
@@ -0,0 +1 @@ +Added version picker for Synapse documentation. Contributed by @Dmytro27Ind. \ No newline at end of file diff --git a/changelog.d/16726.misc b/changelog.d/16726.misc new file mode 100644
index 0000000000..bac312465c --- /dev/null +++ b/changelog.d/16726.misc
@@ -0,0 +1 @@ +Update the implementation of [MSC2965](https://github.com/matrix-org/matrix-spec-proposals/pull/2965) (OIDC Provider discovery). diff --git a/changelog.d/16737.doc b/changelog.d/16737.doc new file mode 100644
index 0000000000..26035b73ec --- /dev/null +++ b/changelog.d/16737.doc
@@ -0,0 +1 @@ +Clarify that `password_config.enabled: "only_for_reauth"` does not allow new logins to be created using password auth. \ No newline at end of file diff --git a/changelog.d/16740.bugfix b/changelog.d/16740.bugfix new file mode 100644
index 0000000000..21551516e2 --- /dev/null +++ b/changelog.d/16740.bugfix
@@ -0,0 +1 @@ +Fix a long-standing bug where the signing keys generated by Synapse were world-readable. Contributed by Fabian Klemp. diff --git a/docs/usage/configuration/config_documentation.md b/docs/usage/configuration/config_documentation.md
index 425ec75542..dc92cc2992 100644 --- a/docs/usage/configuration/config_documentation.md +++ b/docs/usage/configuration/config_documentation.md
@@ -495,10 +495,10 @@ Unix socket support (_Added in Synapse 1.89.0_): * **Note**: The use of both `path` and `port` options for the same `listener` is not compatible. * The `x_forwarded` option defaults to true when using Unix sockets and can be omitted. - * Other options that would not make sense to use with a UNIX socket, such as + * Other options that would not make sense to use with a UNIX socket, such as `bind_addresses` and `tls` will be ignored and can be removed. * `mode`: The file permissions to set on the UNIX socket. Defaults to `666` -* **Note:** Must be set as `type: http` (does not support `metrics` and `manhole`). +* **Note:** Must be set as `type: http` (does not support `metrics` and `manhole`). Also make sure that `metrics` is not included in `resources` -> `names` @@ -2932,7 +2932,7 @@ access tokens via a query parameter. Example configuration: ```yaml -use_appservice_legacy_authorization: true +use_appservice_legacy_authorization: true ``` --- @@ -3613,7 +3613,7 @@ This setting has the following sub-options: * `enabled`: Defaults to true. Set to false to disable password authentication. Set to `only_for_reauth` to allow users with existing passwords to use them - to log in and reauthenticate, whilst preventing new users from setting passwords. + to reauthenticate (not log in), whilst preventing new users from setting passwords. * `localdb_enabled`: Set to false to disable authentication against the local password database. This is ignored if `enabled` is false, and is only useful if you have other `password_providers`. Defaults to true. @@ -3865,7 +3865,7 @@ This setting is an optional list of 0 or more rules. By default, no list is provided, meaning that all alias creations are permitted. Otherwise, requests to create aliases are matched against each rule in order. -The first rule that matches decides if the request is allowed or denied. If no +The first rule that matches decides if the request is allowed or denied. If no rule matches, the request is denied. In particular, this means that configuring an empty list of rules will deny every alias creation request. @@ -3877,7 +3877,7 @@ Each rule is a YAML object containing four fields, each of which is an optional * `action`: either `allow` or `deny`. What to do with the request if the rule matches. Defaults to `allow`. Each of the glob patterns is optional, defaulting to `*` ("match anything"). -Note that the patterns match against fully qualified IDs, e.g. against +Note that the patterns match against fully qualified IDs, e.g. against `@alice:example.com`, `#room:example.com` and `!abcdefghijk:example.com` instead of `alice`, `room` and `abcedgghijk`. @@ -3914,7 +3914,7 @@ alias_creation_rules: alias_creation_rules: - user_id: "@bad_user:example.com" action: deny - + - action: allow ``` @@ -3992,7 +3992,7 @@ room_list_publication_rules: room_list_publication_rules: - user_id: "@bad_user:example.com" action: deny - + - action: allow ``` @@ -4408,7 +4408,7 @@ must be declared, in the same way as the [`listeners` option](#listeners) in the shared config. Workers declared in [`stream_writers`](#stream_writers) and [`instance_map`](#instance_map) - will need to include a `replication` listener here, in order to accept internal HTTP + will need to include a `replication` listener here, in order to accept internal HTTP requests from other workers. Example configuration: diff --git a/docs/website_files/README.md b/docs/website_files/README.md
index 04d191479b..bc51c4865e 100644 --- a/docs/website_files/README.md +++ b/docs/website_files/README.md
@@ -24,6 +24,11 @@ Finally, we also stylise the chapter titles in the left sidebar by indenting the slightly so that they are more visually distinguishable from the section headers (the bold titles). This is done through the `indent-section-headers.css` file. +In addition to these modifications, we have added a version picker to the documentation. +Users can switch between documentations for different versions of Synapse. +This functionality was implemented through the `version-picker.js` and +`version-picker.css` files. + More information can be found in mdbook's official documentation for [injecting page JS/CSS](https://rust-lang.github.io/mdBook/format/config.html) and diff --git a/docs/website_files/theme/index.hbs b/docs/website_files/theme/index.hbs
index 3b7a5b6163..f60a7e96aa 100644 --- a/docs/website_files/theme/index.hbs +++ b/docs/website_files/theme/index.hbs
@@ -131,6 +131,18 @@ <i class="fa fa-search"></i> </button> {{/if}} + <div class="version-picker"> + <div class="dropdown"> + <div class="select"> + <span></span> + <i class="fa fa-chevron-down"></i> + </div> + <input type="hidden" name="version"> + <ul class="dropdown-menu"> + <!-- Versions will be added dynamically in version-picker.js --> + </ul> + </div> + </div> </div> <h1 class="menu-title">{{ book_title }}</h1> diff --git a/docs/website_files/version-picker.css b/docs/website_files/version-picker.css new file mode 100644
index 0000000000..28e5d5219a --- /dev/null +++ b/docs/website_files/version-picker.css
@@ -0,0 +1,78 @@ +.version-picker { + display: flex; + align-items: center; +} + +.version-picker .dropdown { + width: 130px; + max-height: 29px; + margin-left: 10px; + display: inline-block; + border-radius: 4px; + border: 1px solid var(--theme-popup-border); + position: relative; + font-size: 13px; + color: var(--fg); + height: 100%; + text-align: left; +} +.version-picker .dropdown .select { + cursor: pointer; + display: block; + padding: 5px 2px 5px 15px; +} +.version-picker .dropdown .select > i { + font-size: 10px; + color: var(--fg); + cursor: pointer; + float: right; + line-height: 20px !important; +} +.version-picker .dropdown:hover { + border: 1px solid var(--theme-popup-border); +} +.version-picker .dropdown:active { + background-color: var(--theme-popup-bg); +} +.version-picker .dropdown.active:hover, +.version-picker .dropdown.active { + border: 1px solid var(--theme-popup-border); + border-radius: 2px 2px 0 0; + background-color: var(--theme-popup-bg); +} +.version-picker .dropdown.active .select > i { + transform: rotate(-180deg); +} +.version-picker .dropdown .dropdown-menu { + position: absolute; + background-color: var(--theme-popup-bg); + width: 100%; + left: -1px; + right: 1px; + margin-top: 1px; + border: 1px solid var(--theme-popup-border); + border-radius: 0 0 4px 4px; + overflow: hidden; + display: none; + max-height: 300px; + overflow-y: auto; + z-index: 9; +} +.version-picker .dropdown .dropdown-menu li { + font-size: 12px; + padding: 6px 20px; + cursor: pointer; +} +.version-picker .dropdown .dropdown-menu { + padding: 0; + list-style: none; +} +.version-picker .dropdown .dropdown-menu li:hover { + background-color: var(--theme-hover); +} +.version-picker .dropdown .dropdown-menu li.active::before { + display: inline-block; + content: "✓"; + margin-inline-start: -14px; + width: 14px; +} \ No newline at end of file diff --git a/docs/website_files/version-picker.js b/docs/website_files/version-picker.js new file mode 100644
index 0000000000..bb35a7d896 --- /dev/null +++ b/docs/website_files/version-picker.js
@@ -0,0 +1,127 @@ + +const dropdown = document.querySelector('.version-picker .dropdown'); +const dropdownMenu = dropdown.querySelector('.dropdown-menu'); + +fetchVersions(dropdown, dropdownMenu).then(() => { + initializeVersionDropdown(dropdown, dropdownMenu); +}); + +/** + * Initialize the dropdown functionality for version selection. + * + * @param {Element} dropdown - The dropdown element. + * @param {Element} dropdownMenu - The dropdown menu element. + */ +function initializeVersionDropdown(dropdown, dropdownMenu) { + // Toggle the dropdown menu on click + dropdown.addEventListener('click', function () { + this.setAttribute('tabindex', 1); + this.classList.toggle('active'); + dropdownMenu.style.display = (dropdownMenu.style.display === 'block') ? 'none' : 'block'; + }); + + // Remove the 'active' class and hide the dropdown menu on focusout + dropdown.addEventListener('focusout', function () { + this.classList.remove('active'); + dropdownMenu.style.display = 'none'; + }); + + // Handle item selection within the dropdown menu + const dropdownMenuItems = dropdownMenu.querySelectorAll('li'); + dropdownMenuItems.forEach(function (item) { + item.addEventListener('click', function () { + dropdownMenuItems.forEach(function (item) { + item.classList.remove('active'); + }); + this.classList.add('active'); + dropdown.querySelector('span').textContent = this.textContent; + dropdown.querySelector('input').value = this.getAttribute('id'); + + window.location.href = changeVersion(window.location.href, this.textContent); + }); + }); +}; + +/** + * This function fetches the available versions from a GitHub repository + * and inserts them into the version picker. + * + * @param {Element} dropdown - The dropdown element. + * @param {Element} dropdownMenu - The dropdown menu element. + * @returns {Promise<Array<string>>} A promise that resolves with an array of available versions. + */ +function fetchVersions(dropdown, dropdownMenu) { + return new Promise((resolve, reject) => { + window.addEventListener("load", () => { + + fetch("https://api.github.com/repos/matrix-org/synapse/git/trees/gh-pages", { + cache: "force-cache", + }).then(res => + res.json() + ).then(resObject => { + const excluded = ['dev-docs', 'v1.91.0', 'v1.80.0', 'v1.69.0']; + const tree = resObject.tree.filter(item => item.type === "tree" && !excluded.includes(item.path)); + const versions = tree.map(item => item.path).sort(sortVersions); + + // Create a list of <li> items for versions + versions.forEach((version) => { + const li = document.createElement("li"); + li.textContent = version; + li.id = version; + + if (window.SYNAPSE_VERSION === version) { + li.classList.add('active'); + dropdown.querySelector('span').textContent = version; + dropdown.querySelector('input').value = version; + } + + dropdownMenu.appendChild(li); + }); + + resolve(versions); + + }).catch(ex => { + console.error("Failed to fetch version data", ex); + reject(ex); + }) + }); + }); +} + +/** + * Custom sorting function to sort an array of version strings. + * + * @param {string} a - The first version string to compare. + * @param {string} b - The second version string to compare. + * @returns {number} - A negative number if a should come before b, a positive number if b should come before a, or 0 if they are equal. + */ +function sortVersions(a, b) { + // Put 'develop' and 'latest' at the top + if (a === 'develop' || a === 'latest') return -1; + if (b === 'develop' || b === 'latest') return 1; + + const versionA = (a.match(/v\d+(\.\d+)+/) || [])[0]; + const versionB = (b.match(/v\d+(\.\d+)+/) || [])[0]; + + return versionB.localeCompare(versionA); +} + +/** + * Change the version in a URL path. + * + * @param {string} url - The original URL to be modified. + * @param {string} newVersion - The new version to replace the existing version in the URL. + * @returns {string} The updated URL with the new version. + */ +function changeVersion(url, newVersion) { + const parsedURL = new URL(url); + const pathSegments = parsedURL.pathname.split('/'); + + // Modify the version + pathSegments[2] = newVersion; + + // Reconstruct the URL + parsedURL.pathname = pathSegments.join('/'); + + return parsedURL.href; +} \ No newline at end of file diff --git a/docs/website_files/version.js b/docs/website_files/version.js new file mode 100644
index 0000000000..9065afcdbf --- /dev/null +++ b/docs/website_files/version.js
@@ -0,0 +1 @@ +window.SYNAPSE_VERSION = "latest"; \ No newline at end of file diff --git a/synapse/_scripts/generate_signing_key.py b/synapse/_scripts/generate_signing_key.py
index 3f8f5da75f..581b991505 100755 --- a/synapse/_scripts/generate_signing_key.py +++ b/synapse/_scripts/generate_signing_key.py
@@ -13,6 +13,7 @@ # See the License for the specific language governing permissions and # limitations under the License. import argparse +import os import sys from signedjson.key import generate_signing_key, write_signing_keys @@ -26,15 +27,21 @@ def main() -> None: parser.add_argument( "-o", "--output_file", - type=argparse.FileType("w"), - default=sys.stdout, + type=str, + default="-", help="Where to write the output to", ) args = parser.parse_args() key_id = "a_" + random_string(4) key = (generate_signing_key(key_id),) - write_signing_keys(args.output_file, key) + if args.output_file == "-": + write_signing_keys(sys.stdout, key) + else: + with open( + args.output_file, "w", opener=lambda p, f: os.open(p, f, mode=0o640) + ) as signing_key_file: + write_signing_keys(signing_key_file, key) if __name__ == "__main__": diff --git a/synapse/config/key.py b/synapse/config/key.py
index f3dc4df695..1920498cd1 100644 --- a/synapse/config/key.py +++ b/synapse/config/key.py
@@ -263,7 +263,9 @@ class KeyConfig(Config): if not self.path_exists(signing_key_path): print("Generating signing key file %s" % (signing_key_path,)) - with open(signing_key_path, "w") as signing_key_file: + with open( + signing_key_path, "w", opener=lambda p, f: os.open(p, f, mode=0o640) + ) as signing_key_file: key_id = "a_" + random_string(4) write_signing_keys(signing_key_file, (generate_signing_key(key_id),)) else: @@ -274,7 +276,9 @@ class KeyConfig(Config): key = decode_signing_key_base64( NACL_ED25519, key_id, signing_keys.split("\n")[0] ) - with open(signing_key_path, "w") as signing_key_file: + with open( + signing_key_path, "w", opener=lambda p, f: os.open(p, f, mode=0o640) + ) as signing_key_file: write_signing_keys(signing_key_file, (key,)) diff --git a/synapse/handlers/room.py b/synapse/handlers/room.py
index 2823ca6f0d..c391ab8f4a 100644 --- a/synapse/handlers/room.py +++ b/synapse/handlers/room.py
@@ -871,7 +871,9 @@ class RoomCreationHandler: # The spec says rooms should default to private visibility if # `visibility` is not specified. - visibility = config.get("visibility", "private") + #visibility = config.get("visibility", "private") + # temporarily block publishing rooms to directory - patch date 12/12/23 + visibility = "private" is_public = visibility == "public" self._validate_room_config(config, visibility) diff --git a/synapse/rest/__init__.py b/synapse/rest/__init__.py
index 1be9c47c61..53b8c319a6 100644 --- a/synapse/rest/__init__.py +++ b/synapse/rest/__init__.py
@@ -22,6 +22,7 @@ from synapse.rest.client import ( account_validity, appservice_ping, auth, + auth_issuer, capabilities, devices, directory, @@ -148,3 +149,4 @@ class ClientRestResource(JsonResource): mutual_rooms.register_servlets(hs, client_resource) login_token_request.register_servlets(hs, client_resource) rendezvous.register_servlets(hs, client_resource) + auth_issuer.register_servlets(hs, client_resource) diff --git a/synapse/rest/client/auth_issuer.py b/synapse/rest/client/auth_issuer.py new file mode 100644
index 0000000000..77b9720956 --- /dev/null +++ b/synapse/rest/client/auth_issuer.py
@@ -0,0 +1,63 @@ +# Copyright 2023 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# 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 logging +import typing +from typing import Tuple + +from synapse.api.errors import Codes, SynapseError +from synapse.http.server import HttpServer +from synapse.http.servlet import RestServlet +from synapse.http.site import SynapseRequest +from synapse.rest.client._base import client_patterns +from synapse.types import JsonDict + +if typing.TYPE_CHECKING: + from synapse.server import HomeServer + + +logger = logging.getLogger(__name__) + + +class AuthIssuerServlet(RestServlet): + """ + Advertises what OpenID Connect issuer clients should use to authorise users. + """ + + PATTERNS = client_patterns( + "/org.matrix.msc2965/auth_issuer$", + unstable=True, + releases=(), + ) + + def __init__(self, hs: "HomeServer"): + super().__init__() + self._config = hs.config + + async def on_GET(self, request: SynapseRequest) -> Tuple[int, JsonDict]: + if self._config.experimental.msc3861.enabled: + return 200, {"issuer": self._config.experimental.msc3861.issuer} + else: + # Wouldn't expect this to be reached: the servelet shouldn't have been + # registered. Still, fail gracefully if we are registered for some reason. + raise SynapseError( + 404, + "OIDC discovery has not been configured on this homeserver", + Codes.NOT_FOUND, + ) + + +def register_servlets(hs: "HomeServer", http_server: HttpServer) -> None: + # We use the MSC3861 values as they are used by multiple MSCs + if hs.config.experimental.msc3861.enabled: + AuthIssuerServlet(hs).register(http_server) diff --git a/synapse/rest/client/directory.py b/synapse/rest/client/directory.py
index 3534c3c259..0d16758f85 100644 --- a/synapse/rest/client/directory.py +++ b/synapse/rest/client/directory.py
@@ -159,6 +159,16 @@ class ClientDirectoryListServer(RestServlet): content = parse_and_validate_json_object_from_request(request, self.PutBody) + # temporarily block publishing rooms to public directory for non-admins + # patch date 12/12/23 + if content.visibility == "public": + is_admin = await self.is_server_admin(requester) + if not is_admin: + raise AuthError( + 403, + "Publishing rooms to the room list is temporarily disabled.", + ) + await self.directory_handler.edit_published_room_list( requester, room_id, content.visibility ) diff --git a/tests/rest/client/test_auth_issuer.py b/tests/rest/client/test_auth_issuer.py new file mode 100644
index 0000000000..964baeec32 --- /dev/null +++ b/tests/rest/client/test_auth_issuer.py
@@ -0,0 +1,59 @@ +# Copyright 2023 The Matrix.org Foundation C.I.C. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# 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. +from http import HTTPStatus + +from synapse.rest.client import auth_issuer + +from tests.unittest import HomeserverTestCase, override_config, skip_unless +from tests.utils import HAS_AUTHLIB + +ISSUER = "https://account.example.com/" + + +class AuthIssuerTestCase(HomeserverTestCase): + servlets = [ + auth_issuer.register_servlets, + ] + + def test_returns_404_when_msc3861_disabled(self) -> None: + # Make an unauthenticated request for the discovery info. + channel = self.make_request( + "GET", + "/_matrix/client/unstable/org.matrix.msc2965/auth_issuer", + ) + self.assertEqual(channel.code, HTTPStatus.NOT_FOUND) + + @skip_unless(HAS_AUTHLIB, "requires authlib") + @override_config( + { + "disable_registration": True, + "experimental_features": { + "msc3861": { + "enabled": True, + "issuer": ISSUER, + "client_id": "David Lister", + "client_auth_method": "client_secret_post", + "client_secret": "Who shot Mister Burns?", + } + }, + } + ) + def test_returns_issuer_when_oidc_enabled(self) -> None: + # Make an unauthenticated request for the discovery info. + channel = self.make_request( + "GET", + "/_matrix/client/unstable/org.matrix.msc2965/auth_issuer", + ) + self.assertEqual(channel.code, HTTPStatus.OK) + self.assertEqual(channel.json_body, {"issuer": ISSUER})