# Buggy code
async def on_POST(self, request, room_identifier):
    requester = await self.auth.get_user_by_req(request)
    await assert_user_is_admin(self.auth, requester.user)
    content = parse_json_object_from_request(request, allow_empty_body=True)

    # Resolve to a room ID, if necessary.
    if RoomID.is_valid(room_identifier):
        room_id = room_identifier
    elif RoomAlias.is_valid(room_identifier):
        room_alias = RoomAlias.from_string(room_identifier)
        room_id, _ = await self.room_member_handler.lookup_room_alias(room_alias)
        room_id = room_id.to_string()
    else:
        raise SynapseError(
            400, "%s was not legal room ID or room alias" % (room_identifier,)
        )

    # Which user to grant room admin rights to.
    user_to_add = content.get("user_id", requester.user.to_string())

    # Figure out which local users currently have power in the room, if any.
    room_state = await self.state_handler.get_current_state(room_id)
    if not room_state:
        raise SynapseError(400, "Server not in room")

    create_event = room_state[(EventTypes.Create, "")]
    power_levels = room_state.get((EventTypes.PowerLevels, ""))

    if power_levels is not None:
        # We pick the local user with the highest power.
        user_power = power_levels.content.get("users", {})
        admin_users = [user_id for user_id in user_power if self.is_mine_id(user_id)]
        admin_users.sort(key=lambda user: user_power[user])

        if not admin_users:
            raise SynapseError(400, "No local admin user in room")

        admin_user_id = None

        for admin_user in reversed(admin_users):
            if room_state.get((EventTypes.Member, admin_user)):
                admin_user_id = admin_user
                break

        if not admin_user_id:
            raise SynapseError(
                400,
                "No local admin user in room",
            )

        pl_content = power_levels.content
    else:
        # If there is no power level events then the creator has rights.
        pl_content = {}
        admin_user_id = create_event.sender
        if not self.is_mine_id(admin_user_id):
            raise SynapseError(
                400,
                "No local admin user in room",
            )

    # Grant the user power equal to the room admin by attempting to send an
    # updated power level event.
    new_pl_content = dict(pl_content)
    new_pl_content["users"] = dict(pl_content.get("users", {}))
    new_pl_content["users"][user_to_add] = new_pl_content["users"][admin_user_id]

    fake_requester = create_requester(
        admin_user_id,
        authenticated_entity=requester.authenticated_entity,
    )

    try:
        await self.event_creation_handler.create_and_send_nonmember_event(
            fake_requester,
            event_dict={
                "content": new_pl_content,
                "sender": admin_user_id,
                "type": EventTypes.PowerLevels,
                "state_key": "",
                "room_id": room_id,
            },
        )
    except AuthError:
        # The admin user we found turned out not to have enough power.
        raise SynapseError(
            400, "No local admin user in room with power to update power levels."
        )

    # Now we check if the user we're granting admin rights to is already in
    # the room. If not and it's not a public room we invite them.
    member_event = room_state.get((EventTypes.Member, user_to_add))
    is_joined = False
    if member_event:
        is_joined = member_event.content["membership"] in (
            Membership.JOIN,
            Membership.INVITE,
        )

    if is_joined:
        return 200, {}

    join_rules = room_state.get((EventTypes.JoinRules, ""))
    is_public = False
    if join_rules:
        is_public = join_rules.content.get("join_rule") == JoinRules.PUBLIC

    if is_public:
        return 200, {}

    await self.room_member_handler.update_membership(
        fake_requester,
        target=UserID.from_string(user_to_add),
        room_id=room_id,
        action=Membership.INVITE,
    )

    return 200, {}


# Traceback (ValueError)
2021-02-26 14:01:23,554 - synapse.http.server - 94 - ERROR - POST-320 - Failed handle request via 'JoinRoomAliasServlet': <XForwardedForRequest at 0x7feef12ec358 method='POST' uri='/_synapse/admin/v1/join/%23test%3Aexemple.test.com' clientproto='HTTP/1.1' site='8008'>
Traceback (most recent call last):
File "/opt/synapse2/env/lib/python3.6/site-packages/twisted/internet/defer.py", line 1416, in _inlineCallbacks
result = result.throwExceptionIntoGenerator(g)
File "/opt/synapse2/env/lib/python3.6/site-packages/twisted/python/failure.py", line 512, in throwExceptionIntoGenerator
return g.throw(self.type, self.value, self.tb)
File "/opt/synapse2/env/lib/python3.6/site-packages/synapse/storage/databases/main/event_federation.py", line 634, in get_latest_event_ids_in_room
desc="get_latest_event_ids_in_room",
File "/opt/synapse2/env/lib/python3.6/site-packages/synapse/storage/database.py", line 1453, in simple_select_onecol
db_autocommit=True,
File "/opt/synapse2/env/lib/python3.6/site-packages/synapse/storage/database.py", line 676, in runInteraction
**kwargs,
File "/opt/synapse2/env/lib/python3.6/site-packages/synapse/storage/database.py", line 752, in runWithConnection
self._db_pool.runWithConnection(inner_func, *args, **kwargs)
File "/opt/synapse2/env/lib/python3.6/site-packages/twisted/python/threadpool.py", line 250, in inContext
result = inContext.theWork()
File "/opt/synapse2/env/lib/python3.6/site-packages/twisted/python/threadpool.py", line 266, in <lambda>
inContext.theWork = lambda: context.call(ctx, func, *args, **kw)
File "/opt/synapse2/env/lib/python3.6/site-packages/twisted/python/context.py", line 122, in callWithContext
return self.currentContext().callWithContext(ctx, func, *args, **kw)
File "/opt/synapse2/env/lib/python3.6/site-packages/twisted/python/context.py", line 85, in callWithContext
return func(*args,**kw)
File "/opt/synapse2/env/lib/python3.6/site-packages/twisted/enterprise/adbapi.py", line 306, in _runWithConnection
compat.reraise(excValue, excTraceback)
File "/opt/synapse2/env/lib/python3.6/site-packages/twisted/python/compat.py", line 464, in reraise
raise exception.with_traceback(traceback)
File "/opt/synapse2/env/lib/python3.6/site-packages/twisted/enterprise/adbapi.py", line 297, in _runWithConnection
result = func(conn, *args, **kw)
File "/opt/synapse2/env/lib/python3.6/site-packages/synapse/storage/database.py", line 746, in inner_func
return func(db_conn, *args, **kwargs)
File "/opt/synapse2/env/lib/python3.6/site-packages/synapse/storage/database.py", line 540, in new_transaction
r = func(cursor, *args, **kwargs)
File "/opt/synapse2/env/lib/python3.6/site-packages/synapse/storage/database.py", line 1422, in simple_select_onecol_txn
txn.execute(sql, list(keyvalues.values()))
File "/opt/synapse2/env/lib/python3.6/site-packages/synapse/storage/database.py", line 295, in execute
self._do_execute(self.txn.execute, sql, *args)
File "/opt/synapse2/env/lib/python3.6/site-packages/synapse/storage/database.py", line 321, in _do_execute
return func(sql, *args)
File "/opt/synapse2/env/lib/python3.6/site-packages/psycopg2/extensions.py", line 121, in getquoted
pobjs = [adapt(o) for o in self._seq]
File "/opt/synapse2/env/lib/python3.6/site-packages/synapse/types.py", line 220, in __iter__
raise ValueError("Attempted to iterate a %s" % (type(self).__name__,))
ValueError: Attempted to iterate a RoomID

# Fixed code
async def on_POST(
    self, request: SynapseRequest, room_identifier: str
) -> Tuple[int, JsonDict]:
    requester = await self.auth.get_user_by_req(request)
    await assert_user_is_admin(self.auth, requester.user)
    content = parse_json_object_from_request(request, allow_empty_body=True)

    room_id, _ = await self.resolve_room_id(room_identifier)

    # Which user to grant room admin rights to.
    user_to_add = content.get("user_id", requester.user.to_string())

    # Figure out which local users currently have power in the room, if any.
    room_state = await self.state_handler.get_current_state(room_id)
    if not room_state:
        raise SynapseError(400, "Server not in room")

    create_event = room_state[(EventTypes.Create, "")]
    power_levels = room_state.get((EventTypes.PowerLevels, ""))

    if power_levels is not None:
        # We pick the local user with the highest power.
        user_power = power_levels.content.get("users", {})
        admin_users = [user_id for user_id in user_power if self.is_mine_id(user_id)]
        admin_users.sort(key=lambda user: user_power[user])

        if not admin_users:
            raise SynapseError(400, "No local admin user in room")

        admin_user_id = None

        for admin_user in reversed(admin_users):
            if room_state.get((EventTypes.Member, admin_user)):
                admin_user_id = admin_user
                break

        if not admin_user_id:
            raise SynapseError(
                400,
                "No local admin user in room",
            )

        pl_content = power_levels.content
    else:
        # If there is no power level events then the creator has rights.
        pl_content = {}
        admin_user_id = create_event.sender
        if not self.is_mine_id(admin_user_id):
            raise SynapseError(
                400,
                "No local admin user in room",
            )

    # Grant the user power equal to the room admin by attempting to send an
    # updated power level event.
    new_pl_content = dict(pl_content)
    new_pl_content["users"] = dict(pl_content.get("users", {}))
    new_pl_content["users"][user_to_add] = new_pl_content["users"][admin_user_id]

    fake_requester = create_requester(
        admin_user_id,
        authenticated_entity=requester.authenticated_entity,
    )

    try:
        await self.event_creation_handler.create_and_send_nonmember_event(
            fake_requester,
            event_dict={
                "content": new_pl_content,
                "sender": admin_user_id,
                "type": EventTypes.PowerLevels,
                "state_key": "",
                "room_id": room_id,
            },
        )
    except AuthError:
        # The admin user we found turned out not to have enough power.
        raise SynapseError(
            400, "No local admin user in room with power to update power levels."
        )

    # Now we check if the user we're granting admin rights to is already in
    # the room. If not and it's not a public room we invite them.
    member_event = room_state.get((EventTypes.Member, user_to_add))
    is_joined = False
    if member_event:
        is_joined = member_event.content["membership"] in (
            Membership.JOIN,
            Membership.INVITE,
        )

    if is_joined:
        return 200, {}

    join_rules = room_state.get((EventTypes.JoinRules, ""))
    is_public = False
    if join_rules:
        is_public = join_rules.content.get("join_rule") == JoinRules.PUBLIC

    if is_public:
        return 200, {}

    await self.room_member_handler.update_membership(
        fake_requester,
        target=UserID.from_string(user_to_add),
        room_id=room_id,
        action=Membership.INVITE,
    )

    return 200, {}