Merge pull request #9496 from vector-im/travis/guests/block-ui
Validate default homeserver config before loading the apppull/9719/head
						commit
						886828caa8
					
				
							
								
								
									
										40
									
								
								README.md
								
								
								
								
							
							
						
						
									
										40
									
								
								README.md
								
								
								
								
							| 
						 | 
				
			
			@ -109,25 +109,31 @@ You can configure the app by copying `config.sample.json` to
 | 
			
		|||
 | 
			
		||||
For a good example, see https://riot.im/develop/config.json.
 | 
			
		||||
 | 
			
		||||
1. `default_server_name` sets the default server name to use for authentication.
 | 
			
		||||
   This will trigger Riot to ask
 | 
			
		||||
   `https://<server_name>/.well-known/matrix/client` for the homeserver and
 | 
			
		||||
   identity server URLs to use. This is the recommended approach for setting a
 | 
			
		||||
   default server. However, it is also possible to use the following to directly
 | 
			
		||||
   configure each of the URLs:
 | 
			
		||||
   * `default_hs_url` sets the default homeserver URL.
 | 
			
		||||
   * `default_is_url` sets the default identity server URL (this is the server used
 | 
			
		||||
      for verifying third party identifiers like email addresses). If this is blank,
 | 
			
		||||
      registering with an email address, adding an email address to your account,
 | 
			
		||||
      or inviting users via email address will not work.  Matrix identity servers are
 | 
			
		||||
      very simple web services which map third party identifiers (currently only email
 | 
			
		||||
      addresses) to matrix IDs: see http://matrix.org/docs/spec/identity_service/unstable.html
 | 
			
		||||
      for more details.  Currently the only public matrix identity servers are https://matrix.org
 | 
			
		||||
      and https://vector.im.  In the future, identity servers will be decentralised.
 | 
			
		||||
   * Riot will report an error if you accidentally configure both `default_server_name` _and_ `default_hs_url` since it's unclear which should take priority.
 | 
			
		||||
1. `default_server_config` sets the default homeserver and identity server URL for
 | 
			
		||||
   Riot to use. The object is the same as returned by [https://<server_name>/.well-known/matrix/client](https://matrix.org/docs/spec/client_server/latest.html#get-well-known-matrix-client),
 | 
			
		||||
   with added support for a `server_name` under the `m.homeserver` section to display
 | 
			
		||||
   a custom homeserver name. Alternatively, the config can contain a `default_server_name`
 | 
			
		||||
   instead which is where Riot will go to get that same object - see the `.well-known`
 | 
			
		||||
   link above for more information. Note that the `default_server_name` is used to get
 | 
			
		||||
   a complete server configuration whereas the `server_name` in the `default_server_config`
 | 
			
		||||
   is for display purposes only.
 | 
			
		||||
   * *Note*: The URLs can also be individually specified as `default_hs_url` and 
 | 
			
		||||
     `default_is_url`, however these are deprecated. They are maintained for backwards
 | 
			
		||||
     compatibility with older configurations. `default_is_url` is respected only
 | 
			
		||||
     if `default_hs_url` is used.
 | 
			
		||||
   * The identity server is used for verifying third party identifiers like emails
 | 
			
		||||
     and phone numbers. It is not used to store your password or account information.
 | 
			
		||||
     If not provided, the identity server defaults to vector.im unless `disable_identity_server`
 | 
			
		||||
     is set to true in the config. Currently the only two public identity servers
 | 
			
		||||
     are https://matrix.org and https://vector.im, however in future identity servers
 | 
			
		||||
     will be decentralised.
 | 
			
		||||
   * Riot will fail to load if a mix of `default_server_config`, `default_server_name`, or
 | 
			
		||||
     `default_hs_url` is specified. When multiple sources are specified, it is unclear
 | 
			
		||||
     which should take priority and therefore the application cannot continue.
 | 
			
		||||
1. `features`: Lookup of optional features that may be `enable`d, `disable`d, or exposed to the user
 | 
			
		||||
   in the `labs` section of settings.  The available optional experimental features vary from
 | 
			
		||||
   release to release.
 | 
			
		||||
   release to release. Some of the available features are described in the Labs Feature section
 | 
			
		||||
   of this README.
 | 
			
		||||
1. `brand`: String to pass to your homeserver when configuring email notifications, to let the
 | 
			
		||||
   homeserver know what email template to use when talking to you.
 | 
			
		||||
1. `branding`: Configures various branding and logo details, such as:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,14 @@
 | 
			
		|||
{
 | 
			
		||||
    "default_hs_url": "https://matrix.org",
 | 
			
		||||
    "default_is_url": "https://vector.im",
 | 
			
		||||
    "default_server_config": {
 | 
			
		||||
        "m.homeserver": {
 | 
			
		||||
            "base_url": "https://matrix.org",
 | 
			
		||||
            "server_name": "matrix.org"
 | 
			
		||||
        },
 | 
			
		||||
        "m.identity_server": {
 | 
			
		||||
            "base_url": "https://vector.im"
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
    "disable_identity_server": false,
 | 
			
		||||
    "disable_custom_urls": false,
 | 
			
		||||
    "disable_guests": false,
 | 
			
		||||
    "disable_login_language_selector": false,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,7 @@
 | 
			
		|||
{
 | 
			
		||||
    "Unexpected error preparing the app. See console for details.": "Unexpected error preparing the app. See console for details.",
 | 
			
		||||
    "Invalid configuration: can only specify one of default_server_config, default_server_name, or default_hs_url.": "Invalid configuration: can only specify one of default_server_config, default_server_name, or default_hs_url.",
 | 
			
		||||
    "Invalid configuration: no default server specified.": "Invalid configuration: no default server specified.",
 | 
			
		||||
    "Riot Desktop on %(platformName)s": "Riot Desktop on %(platformName)s",
 | 
			
		||||
    "Unknown device": "Unknown device",
 | 
			
		||||
    "%(appName)s via %(browserName)s on %(osName)s": "%(appName)s via %(browserName)s on %(osName)s",
 | 
			
		||||
| 
						 | 
				
			
			@ -15,7 +18,5 @@
 | 
			
		|||
    "Need help?": "Need help?",
 | 
			
		||||
    "Chat with Riot Bot": "Chat with Riot Bot",
 | 
			
		||||
    "Explore rooms": "Explore rooms",
 | 
			
		||||
    "Room Directory": "Room Directory",
 | 
			
		||||
    "Search the room directory": "Search the room directory",
 | 
			
		||||
    "Get started with some tips from Riot Bot!": "Get started with some tips from Riot Bot!"
 | 
			
		||||
    "Room Directory": "Room Directory"
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,6 +45,9 @@ import VectorConferenceHandler from 'matrix-react-sdk/lib/VectorConferenceHandle
 | 
			
		|||
import Promise from 'bluebird';
 | 
			
		||||
import request from 'browser-request';
 | 
			
		||||
import * as languageHandler from 'matrix-react-sdk/lib/languageHandler';
 | 
			
		||||
import {_t, _td, newTranslatableError} from 'matrix-react-sdk/lib/languageHandler';
 | 
			
		||||
import AutoDiscoveryUtils from 'matrix-react-sdk/lib/utils/AutoDiscoveryUtils';
 | 
			
		||||
import {AutoDiscovery} from "matrix-js-sdk/lib/autodiscovery";
 | 
			
		||||
 | 
			
		||||
import url from 'url';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -341,22 +344,37 @@ async function loadApp() {
 | 
			
		|||
        const platform = PlatformPeg.get();
 | 
			
		||||
        platform.startUpdater();
 | 
			
		||||
 | 
			
		||||
        const MatrixChat = sdk.getComponent('structures.MatrixChat');
 | 
			
		||||
        window.matrixChat = ReactDOM.render(
 | 
			
		||||
            <MatrixChat
 | 
			
		||||
                onNewScreen={onNewScreen}
 | 
			
		||||
                makeRegistrationUrl={makeRegistrationUrl}
 | 
			
		||||
                ConferenceHandler={VectorConferenceHandler}
 | 
			
		||||
                config={configJson}
 | 
			
		||||
                realQueryParams={params}
 | 
			
		||||
                startingFragmentQueryParams={fragparts.params}
 | 
			
		||||
                enableGuest={!configJson.disable_guests}
 | 
			
		||||
                onTokenLoginCompleted={onTokenLoginCompleted}
 | 
			
		||||
                initialScreenAfterLogin={getScreenFromLocation(window.location)}
 | 
			
		||||
                defaultDeviceDisplayName={platform.getDefaultDeviceDisplayName()}
 | 
			
		||||
            />,
 | 
			
		||||
            document.getElementById('matrixchat'),
 | 
			
		||||
        );
 | 
			
		||||
        // Don't bother loading the app until the config is verified
 | 
			
		||||
        verifyServerConfig().then((newConfig) => {
 | 
			
		||||
            const MatrixChat = sdk.getComponent('structures.MatrixChat');
 | 
			
		||||
            window.matrixChat = ReactDOM.render(
 | 
			
		||||
                <MatrixChat
 | 
			
		||||
                    onNewScreen={onNewScreen}
 | 
			
		||||
                    makeRegistrationUrl={makeRegistrationUrl}
 | 
			
		||||
                    ConferenceHandler={VectorConferenceHandler}
 | 
			
		||||
                    config={newConfig}
 | 
			
		||||
                    realQueryParams={params}
 | 
			
		||||
                    startingFragmentQueryParams={fragparts.params}
 | 
			
		||||
                    enableGuest={!configJson.disable_guests}
 | 
			
		||||
                    onTokenLoginCompleted={onTokenLoginCompleted}
 | 
			
		||||
                    initialScreenAfterLogin={getScreenFromLocation(window.location)}
 | 
			
		||||
                    defaultDeviceDisplayName={platform.getDefaultDeviceDisplayName()}
 | 
			
		||||
                />,
 | 
			
		||||
                document.getElementById('matrixchat'),
 | 
			
		||||
            );
 | 
			
		||||
        }).catch(err => {
 | 
			
		||||
            console.error(err);
 | 
			
		||||
 | 
			
		||||
            const errorMessage = err.translatedMessage
 | 
			
		||||
                || _t("Unexpected error preparing the app. See console for details.");
 | 
			
		||||
 | 
			
		||||
            // Like the compatibility page, AWOOOOOGA at the user
 | 
			
		||||
            const GenericErrorPage = sdk.getComponent("structures.GenericErrorPage");
 | 
			
		||||
            window.matrixChat = ReactDOM.render(
 | 
			
		||||
                <GenericErrorPage message={errorMessage} />,
 | 
			
		||||
                document.getElementById('matrixchat'),
 | 
			
		||||
            );
 | 
			
		||||
        });
 | 
			
		||||
    } else {
 | 
			
		||||
        console.error("Browser is missing required features.");
 | 
			
		||||
        // take to a different landing page to AWOOOOOGA at the user
 | 
			
		||||
| 
						 | 
				
			
			@ -428,4 +446,66 @@ async function loadLanguage() {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function verifyServerConfig() {
 | 
			
		||||
    console.log("Verifying homeserver configuration");
 | 
			
		||||
 | 
			
		||||
    // TODO: TravisR - Handle query string arguments for hs_url and is_url
 | 
			
		||||
    // We probably don't want to handle them unless the user is logged out though?
 | 
			
		||||
 | 
			
		||||
    const config = SdkConfig.get();
 | 
			
		||||
    let wkConfig = config['default_server_config']; // overwritten later under some conditions
 | 
			
		||||
    const serverName = config['default_server_name'];
 | 
			
		||||
    const hsUrl = config['default_hs_url'];
 | 
			
		||||
    const isUrl = config['default_is_url'];
 | 
			
		||||
 | 
			
		||||
    const incompatibleOptions = [wkConfig, serverName, hsUrl].filter(i => !!i);
 | 
			
		||||
    if (incompatibleOptions.length > 1) {
 | 
			
		||||
        throw newTranslatableError(_td(
 | 
			
		||||
            "Invalid configuration: can only specify one of default_server_config, default_server_name, " +
 | 
			
		||||
            "or default_hs_url.",
 | 
			
		||||
        ));
 | 
			
		||||
    }
 | 
			
		||||
    if (incompatibleOptions.length < 1) {
 | 
			
		||||
        throw newTranslatableError(_td("Invalid configuration: no default server specified."));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (hsUrl) {
 | 
			
		||||
        console.log("Config uses a default_hs_url - constructing a default_server_config using this information");
 | 
			
		||||
 | 
			
		||||
        wkConfig = {
 | 
			
		||||
            "m.homeserver": {
 | 
			
		||||
                "base_url": hsUrl,
 | 
			
		||||
            },
 | 
			
		||||
        };
 | 
			
		||||
        if (isUrl) {
 | 
			
		||||
            wkConfig["m.identity_server"] = {
 | 
			
		||||
                "base_url": isUrl,
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let result = null;
 | 
			
		||||
 | 
			
		||||
    if (wkConfig) {
 | 
			
		||||
        console.log("Config uses a default_server_config - validating object");
 | 
			
		||||
        result = await AutoDiscovery.fromDiscoveryConfig(wkConfig);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (serverName) {
 | 
			
		||||
        console.log("Config uses a default_server_name - doing .well-known lookup");
 | 
			
		||||
        result = await AutoDiscovery.findClientConfig(serverName);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const validatedConfig = AutoDiscoveryUtils.buildValidatedConfigFromDiscovery(serverName, result);
 | 
			
		||||
 | 
			
		||||
    // Just in case we ever have to debug this
 | 
			
		||||
    console.log("Using homeserver config:", validatedConfig);
 | 
			
		||||
 | 
			
		||||
    // Add the newly built config to the actual config for use by the app
 | 
			
		||||
    console.log("Updating SdkConfig with validated discovery information");
 | 
			
		||||
    SdkConfig.add({"validated_server_config": validatedConfig});
 | 
			
		||||
 | 
			
		||||
    return SdkConfig.get();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
loadApp();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,6 +37,8 @@ const ReactDOM = require('react-dom');
 | 
			
		|||
const ReactTestUtils = require('react-addons-test-utils');
 | 
			
		||||
const expect = require('expect');
 | 
			
		||||
import Promise from 'bluebird';
 | 
			
		||||
import {makeType} from "matrix-react-sdk/lib/utils/TypeUtils";
 | 
			
		||||
import {ValidatedServerConfig} from "matrix-react-sdk/lib/utils/AutoDiscoveryUtils";
 | 
			
		||||
 | 
			
		||||
const test_utils = require('../test-utils');
 | 
			
		||||
const MockHttpBackend = require('matrix-mock-request');
 | 
			
		||||
| 
						 | 
				
			
			@ -96,8 +98,20 @@ describe('joining a room', function() {
 | 
			
		|||
 | 
			
		||||
            PlatformPeg.set(new WebPlatform());
 | 
			
		||||
 | 
			
		||||
            const config = {
 | 
			
		||||
                validated_server_config: makeType(ValidatedServerConfig, {
 | 
			
		||||
                    hsUrl: HS_URL,
 | 
			
		||||
                    hsName: "TEST_ENVIRONMENT",
 | 
			
		||||
                    hsNameIsDifferent: false, // yes, we lie
 | 
			
		||||
                    isUrl: IS_URL,
 | 
			
		||||
                    identityEnabled: true,
 | 
			
		||||
                }),
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            const mc = (
 | 
			
		||||
                <MatrixChat config={{}}
 | 
			
		||||
                <MatrixChat
 | 
			
		||||
                    config={config}
 | 
			
		||||
                    serverConfig={config.validated_server_config}
 | 
			
		||||
                    makeRegistrationUrl={()=>{throw new Error("unimplemented");}}
 | 
			
		||||
                    initialScreenAfterLogin={{
 | 
			
		||||
                        screen: 'directory',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -39,6 +39,8 @@ import dis from 'matrix-react-sdk/lib/dispatcher';
 | 
			
		|||
import * as test_utils from '../test-utils';
 | 
			
		||||
import MockHttpBackend from 'matrix-mock-request';
 | 
			
		||||
import {parseQs, parseQsFromFragment} from '../../src/vector/url_utils';
 | 
			
		||||
import {makeType} from "matrix-react-sdk/lib/utils/TypeUtils";
 | 
			
		||||
import {ValidatedServerConfig} from "matrix-react-sdk/lib/utils/AutoDiscoveryUtils";
 | 
			
		||||
 | 
			
		||||
const DEFAULT_HS_URL='http://my_server';
 | 
			
		||||
const DEFAULT_IS_URL='http://my_is';
 | 
			
		||||
| 
						 | 
				
			
			@ -146,6 +148,13 @@ describe('loading:', function() {
 | 
			
		|||
        const config = Object.assign({
 | 
			
		||||
            default_hs_url: DEFAULT_HS_URL,
 | 
			
		||||
            default_is_url: DEFAULT_IS_URL,
 | 
			
		||||
            validated_server_config: makeType(ValidatedServerConfig, {
 | 
			
		||||
                hsUrl: DEFAULT_HS_URL,
 | 
			
		||||
                hsName: "TEST_ENVIRONMENT",
 | 
			
		||||
                hsNameIsDifferent: false, // yes, we lie
 | 
			
		||||
                isUrl: DEFAULT_IS_URL,
 | 
			
		||||
                identityEnabled: true,
 | 
			
		||||
            }),
 | 
			
		||||
            embeddedPages: {
 | 
			
		||||
                homeUrl: 'data:text/html;charset=utf-8;base64,PGh0bWw+PC9odG1sPg==',
 | 
			
		||||
            },
 | 
			
		||||
| 
						 | 
				
			
			@ -160,6 +169,7 @@ describe('loading:', function() {
 | 
			
		|||
                <MatrixChat
 | 
			
		||||
                    onNewScreen={onNewScreen}
 | 
			
		||||
                    config={config}
 | 
			
		||||
                    serverConfig={config.validated_server_config}
 | 
			
		||||
                    realQueryParams={params}
 | 
			
		||||
                    startingFragmentQueryParams={fragParts.params}
 | 
			
		||||
                    enableGuest={true}
 | 
			
		||||
| 
						 | 
				
			
			@ -616,11 +626,21 @@ describe('loading:', function() {
 | 
			
		|||
 | 
			
		||||
    // check that we have a Login component, send a 'user:pass' login,
 | 
			
		||||
    // and await the HTTP requests.
 | 
			
		||||
    function completeLogin(matrixChat) {
 | 
			
		||||
    async function completeLogin(matrixChat) {
 | 
			
		||||
        // we expect a single <Login> component
 | 
			
		||||
        const login = ReactTestUtils.findRenderedComponentWithType(
 | 
			
		||||
            matrixChat, sdk.getComponent('structures.auth.Login'));
 | 
			
		||||
 | 
			
		||||
        // When we switch to the login component, it'll hit the login endpoint
 | 
			
		||||
        // for proof of life and to get flows. We'll only give it one option.
 | 
			
		||||
        httpBackend.when('GET', '/login')
 | 
			
		||||
            .respond(200, {"flows": [{"type": "m.login.password"}]});
 | 
			
		||||
        httpBackend.flush(); // We already would have tried the GET /login request
 | 
			
		||||
 | 
			
		||||
        // Give the component some time to finish processing the login flows before
 | 
			
		||||
        // continuing.
 | 
			
		||||
        await Promise.delay(100);
 | 
			
		||||
 | 
			
		||||
        httpBackend.when('POST', '/login').check(function(req) {
 | 
			
		||||
            expect(req.data.type).toEqual('m.login.password');
 | 
			
		||||
            expect(req.data.identifier.type).toEqual('m.id.user');
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue