summary refs log tree commit diff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--changelog.d/12212.feature1
-rw-r--r--docs/sample_config.yaml3
-rw-r--r--docs/templates.md7
-rw-r--r--synapse/config/oidc.py3
-rw-r--r--synapse/handlers/oidc.py6
-rw-r--r--synapse/util/templates.py5
6 files changed, 23 insertions, 2 deletions
diff --git a/changelog.d/12212.feature b/changelog.d/12212.feature
new file mode 100644
index 0000000000..fe337ff990
--- /dev/null
+++ b/changelog.d/12212.feature
@@ -0,0 +1 @@
+Add a new Jinja2 template filter to extract the local part of an email address.
diff --git a/docs/sample_config.yaml b/docs/sample_config.yaml
index ef25a3175f..d634fd8ff5 100644
--- a/docs/sample_config.yaml
+++ b/docs/sample_config.yaml
@@ -1948,7 +1948,8 @@ saml2_config:
 #             localpart_template: Jinja2 template for the localpart of the MXID.
 #                 If this is not set, the user will be prompted to choose their
 #                 own username (see the documentation for the
-#                 'sso_auth_account_details.html' template).
+#                 'sso_auth_account_details.html' template). This template can
+#                 use the 'localpart_from_email' filter.
 #
 #             confirm_localpart: Whether to prompt the user to validate (or
 #                 change) the generated localpart (see the documentation for the
diff --git a/docs/templates.md b/docs/templates.md
index b251d05cb9..f87692a453 100644
--- a/docs/templates.md
+++ b/docs/templates.md
@@ -36,6 +36,13 @@ Turns a `mxc://` URL for media content into an HTTP(S) one using the homeserver'
 
 Example: `message.sender_avatar_url|mxc_to_http(32,32)`
 
+```python
+localpart_from_email(address: str) -> str
+```
+
+Returns the local part of an email address (e.g. `alice` in `alice@example.com`).
+
+Example: `user.email_address|localpart_from_email`
 
 ## Email templates
 
diff --git a/synapse/config/oidc.py b/synapse/config/oidc.py
index fc95912d9b..5d571651cb 100644
--- a/synapse/config/oidc.py
+++ b/synapse/config/oidc.py
@@ -183,7 +183,8 @@ class OIDCConfig(Config):
         #             localpart_template: Jinja2 template for the localpart of the MXID.
         #                 If this is not set, the user will be prompted to choose their
         #                 own username (see the documentation for the
-        #                 'sso_auth_account_details.html' template).
+        #                 'sso_auth_account_details.html' template). This template can
+        #                 use the 'localpart_from_email' filter.
         #
         #             confirm_localpart: Whether to prompt the user to validate (or
         #                 change) the generated localpart (see the documentation for the
diff --git a/synapse/handlers/oidc.py b/synapse/handlers/oidc.py
index d98659edc7..724b9cfcb4 100644
--- a/synapse/handlers/oidc.py
+++ b/synapse/handlers/oidc.py
@@ -45,6 +45,7 @@ from synapse.types import JsonDict, UserID, map_username_to_mxid_localpart
 from synapse.util import Clock, json_decoder
 from synapse.util.caches.cached_call import RetryOnExceptionCachedCall
 from synapse.util.macaroons import get_value_from_macaroon, satisfy_expiry
+from synapse.util.templates import _localpart_from_email_filter
 
 if TYPE_CHECKING:
     from synapse.server import HomeServer
@@ -1308,6 +1309,11 @@ def jinja_finalize(thing: Any) -> Any:
 
 
 env = Environment(finalize=jinja_finalize)
+env.filters.update(
+    {
+        "localpart_from_email": _localpart_from_email_filter,
+    }
+)
 
 
 @attr.s(slots=True, frozen=True, auto_attribs=True)
diff --git a/synapse/util/templates.py b/synapse/util/templates.py
index 12941065ca..fb758b7180 100644
--- a/synapse/util/templates.py
+++ b/synapse/util/templates.py
@@ -64,6 +64,7 @@ def build_jinja_env(
         {
             "format_ts": _format_ts_filter,
             "mxc_to_http": _create_mxc_to_http_filter(config.server.public_baseurl),
+            "localpart_from_email": _localpart_from_email_filter,
         }
     )
 
@@ -112,3 +113,7 @@ def _create_mxc_to_http_filter(
 
 def _format_ts_filter(value: int, format: str) -> str:
     return time.strftime(format, time.localtime(value / 1000))
+
+
+def _localpart_from_email_filter(address: str) -> str:
+    return address.rsplit("@", 1)[0]