Merge pull request #3154 from matrix-org/travis/spec/knock-knock-whos-there
Add knocking to the specpull/3166/head
commit
315f36cf38
@ -0,0 +1 @@
|
||||
Add support for knocking, as per [MSC2403](https://github.com/matrix-org/matrix-doc/pull/2403).
|
@ -0,0 +1 @@
|
||||
Add `/knock` endpoint as per [MSC2403](https://github.com/matrix-org/matrix-doc/pull/2403).
|
@ -0,0 +1 @@
|
||||
Add support for knocking, as per [MSC2403](https://github.com/matrix-org/matrix-doc/pull/2403).
|
@ -0,0 +1 @@
|
||||
Add `/make_knock` and `/send_knock` endpoints as per [MSC2403](https://github.com/matrix-org/matrix-doc/pull/2403).
|
@ -0,0 +1,52 @@
|
||||
---
|
||||
title: Room Version 7
|
||||
type: docs
|
||||
weight: 60
|
||||
---
|
||||
|
||||
This room version builds on [version 6](/rooms/v6) to introduce knocking
|
||||
as a possible join rule and membership state.
|
||||
|
||||
## Client considerations
|
||||
|
||||
This is the first room version to support knocking completely. As such,
|
||||
users will not be able to knock on rooms which are not based off v7.
|
||||
|
||||
## Server implementation components
|
||||
|
||||
{{% boxes/warning %}}
|
||||
The information contained in this section is strictly for server
|
||||
implementors. Applications which use the Client-Server API are generally
|
||||
unaffected by the intricacies contained here. The section above
|
||||
regarding client considerations is the resource that Client-Server API
|
||||
use cases should reference.
|
||||
{{% /boxes/warning %}}
|
||||
|
||||
Room version 7 adds new authorization rules for events to support knocking.
|
||||
[Room version 6](/rooms/v6) has details of other authorization rule changes,
|
||||
as do the versions v6 is based upon.
|
||||
|
||||
For checks perfomed upon `m.room.member` events, the following conditions
|
||||
are added in context:
|
||||
|
||||
If type is `m.room.member`:
|
||||
|
||||
...
|
||||
|
||||
* If `membership` is `ban`:
|
||||
|
||||
...
|
||||
|
||||
* If `membership` is `knock`:
|
||||
|
||||
i. If the `join_rule` is anything other than `knock`, reject.
|
||||
|
||||
ii. If `sender` does not match `state_key`, reject.
|
||||
|
||||
iii. If the `sender`'s current membership is not `ban`, `invite`, or `join`, allow.
|
||||
|
||||
iv. Otherwise, reject.
|
||||
|
||||
...
|
||||
|
||||
The remaining rules are the same as in [room version 6](/rooms/v6#authorization-rules-for-events).
|
@ -0,0 +1,124 @@
|
||||
# Copyright 2021 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.
|
||||
swagger: '2.0'
|
||||
info:
|
||||
title: "Matrix Client-Server Room Knocking API"
|
||||
version: "1.0.0"
|
||||
host: localhost:8008
|
||||
schemes:
|
||||
- https
|
||||
- http
|
||||
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
|
||||
consumes:
|
||||
- application/json
|
||||
produces:
|
||||
- application/json
|
||||
securityDefinitions:
|
||||
$ref: definitions/security.yaml
|
||||
paths:
|
||||
"/knock/{roomIdOrAlias}":
|
||||
post:
|
||||
summary: Knock on a room, requesting permission to join.
|
||||
description: |-
|
||||
*Note that this API takes either a room ID or alias, unlike other membership APIs.*
|
||||
|
||||
This API "knocks" on the room to ask for permission to join, if the user
|
||||
is allowed to knock on the room. Acceptance of the knock happens out of
|
||||
band from this API, meaning that the client will have to watch for updates
|
||||
regarding the acceptance/rejection of the knock.
|
||||
|
||||
If the room history settings allow, the user will still be able to see
|
||||
history of the room while being in the "knock" state. The user will have
|
||||
to accept the invitation to join the room (acceptance of knock) to see
|
||||
messages reliably. See the `/join` endpoints for more information about
|
||||
history visibility to the user.
|
||||
|
||||
The knock will appear as an entry in the response of the
|
||||
[`/sync`](/client-server-api/#get_matrixclientr0sync) API.
|
||||
operationId: knockRoom
|
||||
security:
|
||||
- accessToken: []
|
||||
parameters:
|
||||
- in: path
|
||||
type: string
|
||||
name: roomIdOrAlias
|
||||
description: The room identifier or alias to knock upon.
|
||||
required: true
|
||||
x-example: "#monkeys:matrix.org"
|
||||
- in: query
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
name: server_name
|
||||
description: |-
|
||||
The servers to attempt to knock on the room through. One of the servers
|
||||
must be participating in the room.
|
||||
x-example: ["matrix.org", "elsewhere.ca"]
|
||||
- in: body
|
||||
name: body
|
||||
required: true
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
reason:
|
||||
type: string
|
||||
description: |-
|
||||
Optional reason to be included as the `reason` on the subsequent
|
||||
membership event.
|
||||
example: "Looking for support"
|
||||
responses:
|
||||
200:
|
||||
description: |-
|
||||
The room has been knocked upon.
|
||||
|
||||
The knocked room ID must be returned in the `room_id` field.
|
||||
examples:
|
||||
application/json: {
|
||||
"room_id": "!d41d8cd:matrix.org"
|
||||
}
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
room_id:
|
||||
type: string
|
||||
description: The knocked room ID.
|
||||
required: ["room_id"]
|
||||
403:
|
||||
description: |-
|
||||
You do not have permission to knock on the room. A meaningful `errcode`
|
||||
and description error text will be returned. Example reasons for rejection are:
|
||||
|
||||
- The room is not set up for knocking.
|
||||
- The user has been banned from the room.
|
||||
examples:
|
||||
application/json: {
|
||||
"errcode": "M_FORBIDDEN", "error": "You are not allowed to knock on this room."
|
||||
}
|
||||
schema:
|
||||
"$ref": "definitions/errors/error.yaml"
|
||||
404:
|
||||
description: |-
|
||||
The room could not be found or resolved to a room ID.
|
||||
examples:
|
||||
application/json: {
|
||||
"errcode": "M_NOT_FOUND", "error": "That room does not appear to exist."
|
||||
}
|
||||
schema:
|
||||
"$ref": "definitions/errors/error.yaml"
|
||||
429:
|
||||
description: This request was rate-limited.
|
||||
schema:
|
||||
"$ref": "definitions/errors/rate_limited.yaml"
|
||||
tags:
|
||||
- Room membership
|
@ -0,0 +1,322 @@
|
||||
# Copyright 2021 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.
|
||||
|
||||
swagger: '2.0'
|
||||
info:
|
||||
title: "Matrix Federation Knock Room API"
|
||||
version: "1.0.0"
|
||||
host: localhost:8448
|
||||
schemes:
|
||||
- https
|
||||
basePath: /_matrix/federation/v1
|
||||
consumes:
|
||||
- application/json
|
||||
produces:
|
||||
- application/json
|
||||
securityDefinitions:
|
||||
$ref: definitions/security.yaml
|
||||
paths:
|
||||
"/make_knock/{roomId}/{userId}":
|
||||
get:
|
||||
summary: Get information required to make a knock event for a room.
|
||||
description: |-
|
||||
Asks the receiving server to return information that the sending
|
||||
server will need to prepare a knock event for the room.
|
||||
operationId: makeKnock
|
||||
security:
|
||||
- signedRequest: []
|
||||
parameters:
|
||||
- in: path
|
||||
name: roomId
|
||||
type: string
|
||||
description: The room ID that is about to be knocked.
|
||||
required: true
|
||||
x-example: "!abc123:example.org"
|
||||
- in: path
|
||||
name: userId
|
||||
type: string
|
||||
description: The user ID the knock event will be for.
|
||||
required: true
|
||||
x-example: "@someone:example.org"
|
||||
- in: query
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
name: ver
|
||||
description: |-
|
||||
The room versions the sending server has support for.
|
||||
required: true # knocking was supported in v7
|
||||
x-example: ["1", "7"]
|
||||
responses:
|
||||
200:
|
||||
description: |-
|
||||
A template to be used for the rest of the [Knocking Rooms](/server-server-api/#knocking-rooms)
|
||||
handshake. Note that events have a different format depending on room version - check the
|
||||
[room version specification](/#room-versions) for precise event formats. **The response body
|
||||
here describes the common event fields in more detail and may be missing other
|
||||
required fields for a PDU.**
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
room_version:
|
||||
type: string
|
||||
description: |-
|
||||
The version of the room where the server is trying to knock.
|
||||
example: "7"
|
||||
event:
|
||||
description: |-
|
||||
An unsigned template event. Note that events have a different format
|
||||
depending on the room version - check the [room version specification](/#room-versions)
|
||||
for precise event formats.
|
||||
type: object
|
||||
title: Event Template
|
||||
properties:
|
||||
sender:
|
||||
type: string
|
||||
description: The user ID of the knocking member.
|
||||
example: "@someone:example.org"
|
||||
origin:
|
||||
type: string
|
||||
description: The name of the resident homeserver.
|
||||
example: "matrix.org"
|
||||
origin_server_ts:
|
||||
type: integer
|
||||
format: int64
|
||||
description: A timestamp added by the resident homeserver.
|
||||
example: 1234567890
|
||||
type:
|
||||
type: string
|
||||
description: The value `m.room.member`.
|
||||
example: "m.room.member"
|
||||
state_key:
|
||||
type: string
|
||||
description: The user ID of the knocking member.
|
||||
example: "@someone:example.org"
|
||||
content:
|
||||
type: object
|
||||
title: Membership Event Content
|
||||
description: The content of the event.
|
||||
example: {"membership": "knock"}
|
||||
properties:
|
||||
membership:
|
||||
type: string
|
||||
description: The value `knock`.
|
||||
example: "knock"
|
||||
required: ['membership']
|
||||
required:
|
||||
- state_key
|
||||
- origin
|
||||
- origin_server_ts
|
||||
- type
|
||||
- content
|
||||
- sender
|
||||
required:
|
||||
- room_version # knocking was added in v7
|
||||
- event
|
||||
examples:
|
||||
application/json: {
|
||||
"room_version": "7",
|
||||
"event": {
|
||||
"$ref": "examples/minimal_pdu.json",
|
||||
"type": "m.room.member",
|
||||
"state_key": "@someone:example.org",
|
||||
"origin": "example.org",
|
||||
"origin_server_ts": 1549041175876,
|
||||
"sender": "@someone:example.org",
|
||||
"content": {
|
||||
"membership": "knock"
|
||||
}
|
||||
}
|
||||
}
|
||||
400:
|
||||
description: |-
|
||||
The request is invalid or the room the server is attempting
|
||||
to knock upon has a version that is not listed in the `ver`
|
||||
parameters.
|
||||
|
||||
The error should be passed through to clients so that they
|
||||
may give better feedback to users.
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: "../client-server/definitions/errors/error.yaml"
|
||||
- type: object
|
||||
properties:
|
||||
room_version:
|
||||
type: string
|
||||
description: |-
|
||||
The version of the room. Required if the `errcode`
|
||||
is `M_INCOMPATIBLE_ROOM_VERSION`.
|
||||
examples:
|
||||
application/json: {
|
||||
"errcode": "M_INCOMPATIBLE_ROOM_VERSION",
|
||||
"error": "Your homeserver does not support the features required to knock on this room",
|
||||
"room_version": "7"
|
||||
}
|
||||
404:
|
||||
description: |-
|
||||
The room that the knocking server is attempting to knock upon is unknown
|
||||
to the receiving server.
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: "../client-server/definitions/errors/error.yaml"
|
||||
examples:
|
||||
application/json: {
|
||||
"errcode": "M_NOT_FOUND",
|
||||
"error": "Unknown room",
|
||||
}
|
||||
403:
|
||||
description: |-
|
||||
The knocking server or user is not permitted to knock on the room, such as when the
|
||||
server/user is banned or the room is not set up for receiving knocks.
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: "../client-server/definitions/errors/error.yaml"
|
||||
examples:
|
||||
application/json: {
|
||||
"errcode": "M_FORBIDDEN",
|
||||
"error": "You are not permitted to knock on this room",
|
||||
}
|
||||
|
||||
"/send_knock/{roomId}/{eventId}":
|
||||
put:
|
||||
summary: Submit a signed knock event to a resident server.
|
||||
description: |-
|
||||
Submits a signed knock event to the resident server for it to
|
||||
accept into the room's graph. Note that events have
|
||||
a different format depending on the room version - check
|
||||
the [room version specification](/#room-versions) for precise event formats.
|
||||
**The request and response body here describe the common
|
||||
event fields in more detail and may be missing other required
|
||||
fields for a PDU.**
|
||||
operationId: sendKnock
|
||||
security:
|
||||
- signedRequest: []
|
||||
parameters:
|
||||
- in: path
|
||||
name: roomId
|
||||
type: string
|
||||
description: The room ID that is about to be knocked upon.
|
||||
required: true
|
||||
x-example: "!abc123:example.org"
|
||||
- in: path
|
||||
name: eventId
|
||||
type: string
|
||||
description: The event ID for the knock event.
|
||||
required: true
|
||||
x-example: "$abc123:example.org"
|
||||
- in: body
|
||||
name: body
|
||||
type: object
|
||||
required: true
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
sender:
|
||||
type: string
|
||||
description: The user ID of the knocking member.
|
||||
example: "@someone:example.org"
|
||||
origin:
|
||||
type: string
|
||||
description: The name of the knocking homeserver.
|
||||
example: "matrix.org"
|
||||
origin_server_ts:
|
||||
type: integer
|
||||
format: int64
|
||||
description: A timestamp added by the knocking homeserver.
|
||||
example: 1234567890
|
||||
type:
|
||||
type: string
|
||||
description: The value `m.room.member`.
|
||||
example: "m.room.member"
|
||||
state_key:
|
||||
type: string
|
||||
description: The user ID of the knocking member.
|
||||
example: "@someone:example.org"
|
||||
content:
|
||||
type: object
|
||||
title: Membership Event Content
|
||||
description: The content of the event.
|
||||
example: {"membership": "knock"}
|
||||
properties:
|
||||
membership:
|
||||
type: string
|
||||
description: The value `knock`.
|
||||
example: "knock"
|
||||
required: ['membership']
|
||||
required:
|
||||
- state_key
|
||||
- sender
|
||||
- origin
|
||||
- origin_server_ts
|
||||
- type
|
||||
- content
|
||||
example: {
|
||||
"$ref": "examples/minimal_pdu.json",
|
||||
"type": "m.room.member",
|
||||
"state_key": "@someone:example.org",
|
||||
"origin": "example.org",
|
||||
"origin_server_ts": 1549041175876,
|
||||
"sender": "@someone:example.org",
|
||||
"content": {
|
||||
"membership": "knock"
|
||||
}
|
||||
}
|
||||
responses:
|
||||
200:
|
||||
description: |-
|
||||
Information about the room to pass along to clients regarding the
|
||||
knock.
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
knock_room_state:
|
||||
type: array
|
||||
items:
|
||||
$ref: "../../event-schemas/schema/stripped_state.yaml"
|
||||
description: |-
|
||||
A list of simplified events to help the initiator of the knock identify
|
||||
the room. The recommended events to include are the join rules, canonical
|
||||
alias, avatar, name, and encryption state of the room.
|
||||
example:
|
||||
$ref: "../../event-schemas/examples/knock_room_state.json"
|
||||
required: ['knock_room_state']
|
||||
examples:
|
||||
application/json: {
|
||||
"knock_room_state": {"$ref": "../../event-schemas/examples/knock_room_state.json"}
|
||||
}
|
||||
404:
|
||||
description: |-
|
||||
The room that the knocking server is attempting to knock upon is unknown
|
||||
to the receiving server.
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: "../client-server/definitions/errors/error.yaml"
|
||||
examples:
|
||||
application/json: {
|
||||
"errcode": "M_NOT_FOUND",
|
||||
"error": "Unknown room",
|
||||
}
|
||||
403:
|
||||
description: |-
|
||||
The knocking server or user is not permitted to knock on the room, such as when the
|
||||
server/user is banned or the room is not set up for receiving knocks.
|
||||
schema:
|
||||
allOf:
|
||||
- $ref: "../client-server/definitions/errors/error.yaml"
|
||||
examples:
|
||||
application/json: {
|
||||
"errcode": "M_FORBIDDEN",
|
||||
"error": "You are not permitted to knock on this room",
|
||||
}
|
||||
|
@ -0,0 +1,18 @@
|
||||
[
|
||||
{
|
||||
"type": "m.room.name",
|
||||
"sender": "@bob:example.org",
|
||||
"state_key": "",
|
||||
"content": {
|
||||
"name": "Example Room"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "m.room.join_rules",
|
||||
"sender": "@bob:example.org",
|
||||
"state_key": "",
|
||||
"content": {
|
||||
"join_rule": "knock"
|
||||
}
|
||||
}
|
||||
]
|
@ -0,0 +1,15 @@
|
||||
{
|
||||
"$ref": "m.room.member.yaml",
|
||||
"content": {
|
||||
"membership": "knock",
|
||||
"avatar_url": "mxc://example.org/SEsfnsuifSDFSSEF",
|
||||
"displayname": "Alice Margatroid",
|
||||
"reason": "Looking for support"
|
||||
},
|
||||
"unsigned": {
|
||||
"age": 1234,
|
||||
"knock_room_state": {
|
||||
"$ref": "knock_room_state.json"
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
# Spec diagrams
|
||||
|
||||
Non-ascii diagrams for the spec can be placed here for reference in the actual spec.
|
||||
Please include source material so the diagram can be recreated by a future editor.
|
||||
|
||||
https://www.diagrams.net/ is a great ([open source](https://github.com/jgraph/drawio))
|
||||
tool for these sorts of things - include your `.drawio` file next to your diagram.
|
||||
|
||||
Suggested settings for diagrams.net:
|
||||
* Export as PNG.
|
||||
* 100% size.
|
||||
* `20` for a border width.
|
||||
* No transparent background, shadow, or grid.
|
||||
* Include a copy of the diagram.
|
||||
|
||||
To reference a diagram, use the absolute path when compiled. For example,
|
||||
`![membership-flow-diagram](/diagrams/membership.png)`
|
@ -0,0 +1 @@
|
||||
<mxfile host="app.diagrams.net" modified="2021-04-28T19:35:50.494Z" agent="5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.85 Safari/537.36" etag="-IOh23FjJiPnGlGWLseU" version="14.6.6" type="device"><diagram id="4a_pTli-mcEMNPq0ciXK" name="Page-1">3Vvdb6M4EP9rIt09NMIGA3ncZtvbh73TSn243X1ZOYmT0BIcOaZJ9q8/E0z4cggEgulVqoqHsbHHv/nwjDsyp5vDXwxv13/TBfFH0FgcRubnEYTAAa74E1GOMWViopiwYt5CMqWEF+83kURDUkNvQXY5Rk6pz71tnjinQUDmPEfDjNF9nm1J/fxXt3hFSoSXOfbL1H+9BV/HVBc6Kf0L8Vbr5MvAnsRvNjhhlivZrfGC7jMk82lkThmlPH7aHKbEj4SXyCXu93zh7XlijAS8TodvBvr96euj/xL83G+Dn2gaTvgDkMO8Yz+UKxYjvFIvkJPmx0QSZCEEI5uU8TVd0QD7Tyn1kdEwWJDoc4ZopTxfKd0KIhDEV8L5Ue4yDjkVpDXf+PJt/M3oQxfXKEk7GrI5qVqYxApmK8Ir+OB5JwSECd0Qzo6iHyM+5t57fh5YYml15pNdPzGGjxmGrZAf32VG/hYRBINUCziRmJBKYdqFrWvGLx7iGSStzFJS0gkOTaBhqaDhEyykogEb5ODx75nnH9FQY4hk8/NBDn1qHJNGIGTwPcMZtX9kX6b9Tq2kY4dIhKgmFC01FCUGjDEwHDMeqhk6S3Cy3DycoDUZ25kfx8qPGM9cDpLCrinqLVD4rG1Wot4CqIo/j/q0dzIdulzuCC/06EYzSorhBe8eL6tFHvT7teB52eITWvbCXaqM3zthnByqQXcRI9ApSBjJ9j51XcCQtHXGbSV8KgzlpNdYVHaFETEoG0VO6/nNm799cH8DO1JysRduOyW/P/4hVG3qDGuJGRK/YIxRxjOA29wC6NEt1ASMWQmYB2NsI6ulW+gBMSXAKEPMRvZySQP+jDeeH23OVIjbI5E5+Yfs72NMLUu7MXVVehcGujUvF5HVDMhAVu1kr34Uz2xnqWsrWautNks7rdrkQekLQoVDiqldX5R+KrY8hreMVhfOfG8+grYv5vI4E/KwV9FTxPOLhT7Z6XVp8Caf5ug66lg1VavtobsdKsq6Jca5EMH3sduXNq7HYKT2xqFr0QgaeigCnEEGr05W0426mq4tqVEXMBei1340vZzAUmevBuVGzWKuT7sbVUadGu1lO53RFnfCSWdG1rVdq52dPddnCgkjB+WHiFdVSv31arCNjx3F5RPW1ccjLXBraaLrIcuy6yGrs4yDsrCly82nHrtRhHdLbksHhK7mqFxogrsYLGTVg1XTWgUo1CqQWV2hs41Kfn21CqhMwA/Ae9+SNGp6tM3Fal+I/064N8cdK0lHZycRzN+iIU2BbVr5ohqYoIEAVXkuG0qlqGWV4eOitrpeJUy7A4ZffjAHFQzcEBf+L6B0NUpwEGoZJfRgpspJhbeAKozSoJMK5zBKW1IhuRXT8SWKvouC+m9YJEF6RpBPB06YMEknl1k8FP8R0JOmiN8N2cwI+7NC4qChxMsmpl1JqXCGVKXCoELY9r2EbSpzEdIAxMmIU+MBz4QwB5qQKJSVaiebu/E8r+Fmmyw0oAFJSPGsHV1JkKtxjuvCm3JuTQP0wt08YFUfPM+3EtT8Gi/Jle2S/pjrVug3O3ZmIY7ZvGNI171OejXcmvRy4ISTwoETXcmkwEr+ewBaNNNr+DF7+s8M5tN/</diagram></mxfile>
|
Binary file not shown.
After Width: | Height: | Size: 28 KiB |
Loading…
Reference in New Issue