Skip to content

Commit

Permalink
Improve [Matrix] badge generation (#2527)
Browse files Browse the repository at this point in the history
Fixes #2524 

This PR addresses the issues expressed in #2524, in that it:

* checks if a server has advertised a FQDN it can be reached at and if that FQDN hosts Matrix's client APIs
* uses room aliases instead of room IDs, in order to avoid a badge being impossible to generate if the server that created the room leaves it

This includes a breaking change to the badge endpoint.
  • Loading branch information
babolivier authored and paulmelnikow committed Dec 20, 2018
1 parent 2fe61d2 commit da5ca74
Show file tree
Hide file tree
Showing 2 changed files with 339 additions and 42 deletions.
122 changes: 97 additions & 25 deletions services/matrix/matrix.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,20 @@

const Joi = require('joi')
const BaseJsonService = require('../base-json')
const errors = require('../errors')

const queryParamSchema = Joi.object({
server_fqdn: Joi.string().hostname(),
}).required()

const matrixRegisterSchema = Joi.object({
access_token: Joi.string().required(),
}).required()

const matrixAliasLookupSchema = Joi.object({
room_id: Joi.string().required(),
})

const matrixStateSchema = Joi.array()
.items(
Joi.object({
Expand All @@ -31,15 +40,36 @@ const documentation = `
<ul>
<li>Select the desired room inside the Riot.im client</li>
<li>Click on the room settings button (gear icon) located near the top right of the client</li>
<li>Scroll to the very bottom of the settings page and look under the <code>Advanced</code> tab</li>
<li>You should see the <code>Internal room ID</code> with your rooms ID next to it (ex: <code>!ltIjvaLydYAWZyihee:matrix.org</code>)</li>
<li>Replace the IDs <code>:</code> with <code>/</code></li>
<li>The final badge URL should look something like this <code>/matrix/!ltIjvaLydYAWZyihee/matrix.org.svg</code></li>
<li>Scroll to the very bottom of the settings page and look under the <code>Addresses</code> section</li>
<li>You should see one or more <code>room addresses (or aliases)</code>, which can be easily identified with their starting hash (<code>#</code>) character (ex: <code>#twim:matrix.org</code>)</li>
<li>If there is no address for this room, add one under <code>Local addresses for this room</code></li>
<li>Remove the starting hash character (<code>#</code>)</li>
<li>The final badge URL should look something like this <code>/matrix/twim:matrix.org.svg</code></li>
</ul>
</br>
Some Matrix homeservers don't hold a server name matching where they live (e.g. if the homeserver <code>example.com</code> that created the room alias <code>#mysuperroom:example.com</code> lives at <code>matrix.example.com</code>).
</br>
If that is the case of the homeserver that created the room alias used for generating the badge, you will need to add the server's FQDN (fully qualified domain name) as a query parameter.
</br>
The final badge URL should then look something like this <code>/matrix/mysuperroom:example.com.svg?server_fqdn=matrix.example.com</code>.
</p>
`

module.exports = class Matrix extends BaseJsonService {
async retrieveAccessToken({ host }) {
let auth
try {
auth = await this.registerAccount({ host, guest: true })
} catch (e) {
if (e.prettyMessage === 'guests not allowed') {
// attempt fallback method
auth = await this.registerAccount({ host, guest: false })
} else throw e
}

return auth.access_token
}

async registerAccount({ host, guest }) {
return this._requestJson({
url: `https://${host}/_matrix/client/r0/register`,
Expand All @@ -59,27 +89,59 @@ module.exports = class Matrix extends BaseJsonService {
errorMessages: {
401: 'auth failed',
403: 'guests not allowed',
429: 'rate limited by rooms host',
429: 'rate limited by remote server',
},
})
}

async fetch({ host, roomId }) {
let auth
try {
auth = await this.registerAccount({ host, guest: true })
} catch (e) {
if (e.prettyMessage === 'guests not allowed') {
// attempt fallback method
auth = await this.registerAccount({ host, guest: false })
} else throw e
async lookupRoomAlias({ host, roomAlias, accessToken }) {
return this._requestJson({
url: `https://${host}/_matrix/client/r0/directory/room/${encodeURIComponent(
`#${roomAlias}`
)}`,
schema: matrixAliasLookupSchema,
options: {
qs: {
access_token: accessToken,
},
},
errorMessages: {
401: 'bad auth token',
404: 'room not found',
429: 'rate limited by remote server',
},
})
}

async fetch({ roomAlias, serverFQDN }) {
let host
if (serverFQDN === undefined) {
const splitAlias = roomAlias.split(':')
// A room alias can either be in the form #localpart:server or
// #localpart:server:port.
switch (splitAlias.length) {
case 2:
host = splitAlias[1]
break
case 3:
host = `${splitAlias[1]}:${splitAlias[2]}`
break
default:
throw new errors.InvalidParameter({ prettyMessage: 'invalid alias' })
}
} else {
host = serverFQDN
}
const accessToken = await this.retrieveAccessToken({ host })
const lookup = await this.lookupRoomAlias({ host, roomAlias, accessToken })
const data = await this._requestJson({
url: `https://${host}/_matrix/client/r0/rooms/${roomId}/state`,
url: `https://${host}/_matrix/client/r0/rooms/${encodeURIComponent(
lookup.room_id
)}/state`,
schema: matrixStateSchema,
options: {
qs: {
access_token: auth.access_token,
access_token: accessToken,
},
},
errorMessages: {
Expand Down Expand Up @@ -109,11 +171,12 @@ module.exports = class Matrix extends BaseJsonService {
}
}

async handle({ roomId, host, authServer }) {
const members = await this.fetch({
host,
roomId: `${roomId}:${host}`,
})
async handle({ roomAlias }, queryParams) {
const { server_fqdn: serverFQDN } = this.constructor._validateQueryParams(
queryParams,
queryParamSchema
)
const members = await this.fetch({ roomAlias, serverFQDN })
return this.constructor.render({ members })
}

Expand All @@ -128,17 +191,26 @@ module.exports = class Matrix extends BaseJsonService {
static get route() {
return {
base: 'matrix',
format: '([^/]+)/([^/]+)',
capture: ['roomId', 'host'],
format: '([^/]+)',
capture: ['roomAlias'],
queryParams: ['server_fqdn'],
}
}

static get examples() {
return [
{
title: 'Matrix',
exampleUrl: '!ltIjvaLydYAWZyihee/matrix.org',
pattern: ':roomId/:host',
namedParams: { roomAlias: 'twim:matrix.org' },
pattern: ':roomAlias',
staticExample: this.render({ members: 42 }),
documentation,
},
{
title: 'Matrix',
namedParams: { roomAlias: 'twim:matrix.org' },
queryParams: { server_fqdn: 'matrix.org' },
pattern: ':roomAlias',
staticExample: this.render({ members: 42 }),
documentation,
},
Expand Down
Loading

0 comments on commit da5ca74

Please sign in to comment.