Skip to content

Commit

Permalink
Accept a user defined set of extra OpenId user info fields instead of…
Browse files Browse the repository at this point in the history
… a fixed, statically configured set. Extend the set of available fields by the user's avatar url.
  • Loading branch information
jkanefendt committed Sep 10, 2021
1 parent 486af13 commit 92e663c
Show file tree
Hide file tree
Showing 9 changed files with 64 additions and 46 deletions.
8 changes: 0 additions & 8 deletions docs/sample_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -679,14 +679,6 @@ templates:
#
#allow_device_name_lookup_over_federation: false

# Uncomment to include additional fields in the OpenID user info. Defaults to none.
# Currently available are 'name' (the user's display name) and 'room_powerlevels'
# (a mapping between the room id and the user's effective powerlevel)
#
#openid_userinfo_fields:
# - name
# - room_powerlevels


## Caching ##

Expand Down
6 changes: 6 additions & 0 deletions synapse/api/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,3 +249,9 @@ class GuestAccess:

class ReadReceiptEventFields:
MSC2285_HIDDEN = "org.matrix.msc2285.hidden"


class OpenIdUserInfoField:
DISPLAY_NAME = "display_name"
AVATAR_URL = "avatar_url"
ROOM_POWERLEVELS = "room_powerlevels"
20 changes: 0 additions & 20 deletions synapse/config/federation.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,6 @@ def read_config(self, config, **kwargs):
"allow_device_name_lookup_over_federation", True
)

openid_userinfo_fields = config.get("openid_userinfo_fields") or []
validate_config(
_OPENID_USERINFO_FIELDS_SCHEMA,
openid_userinfo_fields,
("openid_userinfo_fields",),
)
self.openid_userinfo_fields = set(openid_userinfo_fields)

def generate_config_section(self, config_dir_path, server_name, **kwargs):
return """\
## Federation ##
Expand Down Expand Up @@ -93,19 +85,7 @@ def generate_config_section(self, config_dir_path, server_name, **kwargs):
# on this homeserver. Defaults to 'true'.
#
#allow_device_name_lookup_over_federation: false
# Uncomment to include additional fields in the OpenID user info. Defaults to none.
# Currently available are 'name' (the user's display name) and 'room_powerlevels'
# (a mapping between the room id and the user's effective powerlevel)
#
#openid_userinfo_fields:
# - name
# - room_powerlevels
"""


_METRICS_FOR_DOMAINS_SCHEMA = {"type": "array", "items": {"type": "string"}}
_OPENID_USERINFO_FIELDS_SCHEMA = {
"type": "array",
"items": {"type": "string", "enum": ["name", "room_powerlevels"]},
}
4 changes: 3 additions & 1 deletion synapse/federation/federation_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -888,7 +888,9 @@ async def on_get_missing_events(
@log_function
async def on_openid_userinfo(self, token: str) -> Optional[str]:
ts_now_ms = self._clock.time_msec()
return await self.store.get_user_id_for_open_id_token(token, ts_now_ms)
return await self.store.get_user_id_and_userinfo_fields_for_open_id_token(
token, ts_now_ms
)

def _transaction_dict_from_pdus(self, pdu_list: List[EventBase]) -> JsonDict:
"""Returns a new Transaction containing the given PDUs suitable for
Expand Down
21 changes: 14 additions & 7 deletions synapse/federation/transport/server/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

from typing_extensions import Literal

from synapse.api.constants import EventTypes, Membership
from synapse.api.constants import EventTypes, Membership, OpenIdUserInfoField
from synapse.api.errors import FederationDeniedError, SynapseError
from synapse.federation.transport.server._base import (
Authenticator,
Expand Down Expand Up @@ -270,7 +270,7 @@ async def on_GET(
{"errcode": "M_MISSING_TOKEN", "error": "Access Token required"},
)

user_id = await self.handler.on_openid_userinfo(token)
user_id, userinfo_fields = await self.handler.on_openid_userinfo(token)

if user_id is None:
return (
Expand All @@ -283,14 +283,21 @@ async def on_GET(

userinfo = {"sub": user_id}

if self.hs.config.federation.openid_userinfo_fields:
if "name" in self.hs.config.federation.openid_userinfo_fields:
if userinfo_fields:
if OpenIdUserInfoField.DISPLAY_NAME in userinfo_fields:
localpart = UserID.from_string(user_id).localpart
userinfo[
"name"
OpenIdUserInfoField.DISPLAY_NAME
] = await self.hs.get_datastore().get_profile_displayname(localpart)
if "room_powerlevels" in self.hs.config.federation.openid_userinfo_fields:
userinfo["room_powerlevels"] = await self._get_powerlevels(user_id)
if OpenIdUserInfoField.AVATAR_URL in userinfo_fields:
localpart = UserID.from_string(user_id).localpart
userinfo[
OpenIdUserInfoField.AVATAR_URL
] = await self.hs.get_datastore().get_profile_avatar_url(localpart)
if OpenIdUserInfoField.ROOM_POWERLEVELS in userinfo_fields:
userinfo[
OpenIdUserInfoField.ROOM_POWERLEVELS
] = await self._get_powerlevels(user_id)

return 200, userinfo

Expand Down
28 changes: 23 additions & 5 deletions synapse/rest/client/openid.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
import logging
from typing import TYPE_CHECKING, Tuple

from synapse.api.errors import AuthError
from synapse.api.constants import OpenIdUserInfoField
from synapse.api.errors import AuthError, Codes, SynapseError
from synapse.http.server import HttpServer
from synapse.http.servlet import RestServlet, parse_json_object_from_request
from synapse.http.site import SynapseRequest
Expand Down Expand Up @@ -64,6 +65,12 @@ class IdTokenServlet(RestServlet):

EXPIRES_MS = 3600 * 1000

USERINFO_FIELDS = list(
item[1]
for item in OpenIdUserInfoField.__dict__.items()
if not item[0].startswith("_")
)

def __init__(self, hs: "HomeServer"):
super().__init__()
self.auth = hs.get_auth()
Expand All @@ -78,14 +85,25 @@ async def on_POST(
if user_id != requester.user.to_string():
raise AuthError(403, "Cannot request tokens for other users.")

# Parse the request body to make sure it's JSON, but ignore the contents
# for now.
parse_json_object_from_request(request)
json = parse_json_object_from_request(request, allow_empty_body=True)

userinfo_fields = None
if "userinfo_fields" in json:
userinfo_fields = json["userinfo_fields"]
for field in userinfo_fields:
if not field in self.USERINFO_FIELDS:
raise SynapseError(
400,
"Unknown userinfo field '" + field + "'",
Codes.INVALID_PARAM,
)

token = random_string(24)
ts_valid_until_ms = self.clock.time_msec() + self.EXPIRES_MS

await self.store.insert_open_id_token(token, ts_valid_until_ms, user_id)
await self.store.insert_open_id_token(
token, ts_valid_until_ms, user_id, userinfo_fields
)

return (
200,
Expand Down
16 changes: 12 additions & 4 deletions synapse/storage/databases/main/openid.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,27 @@

class OpenIdStore(SQLBaseStore):
async def insert_open_id_token(
self, token: str, ts_valid_until_ms: int, user_id: str
self, token: str, ts_valid_until_ms: int, user_id: str, userinfo_fields: list
) -> None:
await self.db_pool.simple_insert(
table="open_id_tokens",
values={
"token": token,
"ts_valid_until_ms": ts_valid_until_ms,
"user_id": user_id,
"userinfo_fields": (
",".join(set(userinfo_fields)) if userinfo_fields else None
),
},
desc="insert_open_id_token",
)

async def get_user_id_for_open_id_token(
async def get_user_id_and_userinfo_fields_for_open_id_token(
self, token: str, ts_now_ms: int
) -> Optional[str]:
def get_user_id_for_token_txn(txn):
sql = (
"SELECT user_id FROM open_id_tokens"
"SELECT user_id, userinfo_fields FROM open_id_tokens"
" WHERE token = ? AND ? <= ts_valid_until_ms"
)

Expand All @@ -32,7 +35,12 @@ def get_user_id_for_token_txn(txn):
if not rows:
return None
else:
return rows[0][0]
userinfo_fields = None
userinfo_fields_str = rows[0][1]
if userinfo_fields_str:
userinfo_fields = userinfo_fields_str.split(",")

return rows[0][0], userinfo_fields

return await self.db_pool.runInteraction(
"get_user_id_for_token", get_user_id_for_token_txn
Expand Down
6 changes: 5 additions & 1 deletion synapse/storage/schema/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

# When updating these values, please leave a short summary of the changes below.

SCHEMA_VERSION = 63
SCHEMA_VERSION = 64
"""Represents the expectations made by the codebase about the database schema
This should be incremented whenever the codebase changes its requirements on the
Expand All @@ -32,6 +32,10 @@
- The `public_room_list_stream` table is not written nor read to
(previously, it was written and read to, but not for any significant purpose).
https://github.com/matrix-org/synapse/pull/10565
Changes in SCHEMA_VERSION = 64:
- Add column `userinfo_fields` to table `open_id_tokens`. The new column is used
to persist the additional open id token fields requested by the user.
"""


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE open_id_tokens ADD COLUMN userinfo_fields TEXT;

0 comments on commit 92e663c

Please sign in to comment.